1 /* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the
2 time he spent testing this
5 * GRUB -- GRand Unified Bootloader
6 * Copyright (C) 2009 Free Software Foundation, Inc.
8 * GRUB is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * GRUB is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
22 #include <grub/file.h>
24 #include <grub/cpu/xnu.h>
27 #include <grub/loader.h>
28 #include <grub/machoload.h>
29 #include <grub/macho.h>
30 #include <grub/cpu/macho.h>
31 #include <grub/command.h>
32 #include <grub/misc.h>
33 #include <grub/extcmd.h>
35 #include <grub/i18n.h>
37 GRUB_MOD_LICENSE ("GPLv3+");
39 #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
40 #include <grub/autoefi.h>
43 struct grub_xnu_devtree_key
*grub_xnu_devtree_root
= 0;
44 static int driverspackagenum
= 0;
45 static int driversnum
= 0;
46 int grub_xnu_is_64bit
= 0;
47 int grub_xnu_darwin_version
= 0;
49 grub_addr_t grub_xnu_heap_target_start
= 0;
50 grub_size_t grub_xnu_heap_size
= 0;
51 struct grub_relocator
*grub_xnu_relocator
;
54 grub_xnu_register_memory (const char *prefix
, int *suffix
,
55 grub_addr_t addr
, grub_size_t size
);
57 grub_xnu_heap_malloc (int size
, void **src
, grub_addr_t
*target
)
60 grub_relocator_chunk_t ch
;
62 err
= grub_relocator_alloc_chunk_addr (grub_xnu_relocator
, &ch
,
63 grub_xnu_heap_target_start
64 + grub_xnu_heap_size
, size
);
68 *src
= get_virtual_current_address (ch
);
69 *target
= grub_xnu_heap_target_start
+ grub_xnu_heap_size
;
70 grub_xnu_heap_size
+= size
;
71 grub_dprintf ("xnu", "val=%p\n", *src
);
75 /* Make sure next block of the heap will be aligned.
76 Please notice: aligned are pointers AFTER relocation
77 and not the current ones. */
79 grub_xnu_align_heap (int align
)
82 = ALIGN_UP (grub_xnu_heap_target_start
+ grub_xnu_heap_size
, align
)
83 - grub_xnu_heap_target_start
;
87 /* Free subtree pointed by CUR. */
89 grub_xnu_free_devtree (struct grub_xnu_devtree_key
*cur
)
91 struct grub_xnu_devtree_key
*d
;
94 grub_free (cur
->name
);
95 if (cur
->datasize
== -1)
96 grub_xnu_free_devtree (cur
->first_child
);
98 grub_free (cur
->data
);
105 /* Compute the size of device tree in xnu format. */
107 grub_xnu_writetree_get_size (struct grub_xnu_devtree_key
*start
,
111 struct grub_xnu_devtree_key
*cur
;
114 ret
= 2 * sizeof (grub_uint32_t
);
117 ret
+= 32 + sizeof (grub_uint32_t
)
118 + grub_strlen (name
) + 4
119 - (grub_strlen (name
) % 4);
121 for (cur
= start
; cur
; cur
= cur
->next
)
122 if (cur
->datasize
!= -1)
126 align_overhead
= 4 - (cur
->datasize
% 4);
127 if (align_overhead
== 4)
129 ret
+= 32 + sizeof (grub_uint32_t
) + cur
->datasize
+ align_overhead
;
132 ret
+= grub_xnu_writetree_get_size (cur
->first_child
, cur
->name
);
136 /* Write devtree in XNU format at curptr assuming the head is named NAME.*/
138 grub_xnu_writetree_toheap_real (void *curptr
,
139 struct grub_xnu_devtree_key
*start
,
142 struct grub_xnu_devtree_key
*cur
;
143 int nkeys
= 0, nvals
= 0;
144 for (cur
= start
; cur
; cur
= cur
->next
)
146 if (cur
->datasize
== -1)
154 *((grub_uint32_t
*) curptr
) = nvals
;
155 curptr
= ((grub_uint32_t
*) curptr
) + 1;
156 *((grub_uint32_t
*) curptr
) = nkeys
;
157 curptr
= ((grub_uint32_t
*) curptr
) + 1;
159 /* First comes "name" value. */
160 grub_memset (curptr
, 0, 32);
161 grub_memcpy (curptr
, "name", 4);
162 curptr
= ((grub_uint8_t
*) curptr
) + 32;
163 *((grub_uint32_t
*)curptr
) = grub_strlen (name
) + 1;
164 curptr
= ((grub_uint32_t
*) curptr
) + 1;
165 grub_memcpy (curptr
, name
, grub_strlen (name
));
166 curptr
= ((grub_uint8_t
*) curptr
) + grub_strlen (name
);
167 grub_memset (curptr
, 0, 4 - (grub_strlen (name
) % 4));
168 curptr
= ((grub_uint8_t
*) curptr
) + (4 - (grub_strlen (name
) % 4));
170 /* Then the other values. */
171 for (cur
= start
; cur
; cur
= cur
->next
)
172 if (cur
->datasize
!= -1)
176 align_overhead
= 4 - (cur
->datasize
% 4);
177 if (align_overhead
== 4)
179 grub_memset (curptr
, 0, 32);
180 grub_strncpy (curptr
, cur
->name
, 31);
181 curptr
= ((grub_uint8_t
*) curptr
) + 32;
182 *((grub_uint32_t
*) curptr
) = cur
->datasize
;
183 curptr
= ((grub_uint32_t
*) curptr
) + 1;
184 grub_memcpy (curptr
, cur
->data
, cur
->datasize
);
185 curptr
= ((grub_uint8_t
*) curptr
) + cur
->datasize
;
186 grub_memset (curptr
, 0, align_overhead
);
187 curptr
= ((grub_uint8_t
*) curptr
) + align_overhead
;
190 /* And then the keys. Recursively use this function. */
191 for (cur
= start
; cur
; cur
= cur
->next
)
192 if (cur
->datasize
== -1)
194 curptr
= grub_xnu_writetree_toheap_real (curptr
,
204 grub_xnu_writetree_toheap (grub_addr_t
*target
, grub_size_t
*size
)
206 struct grub_xnu_devtree_key
*chosen
;
207 struct grub_xnu_devtree_key
*memorymap
;
208 struct grub_xnu_devtree_key
*driverkey
;
209 struct grub_xnu_extdesc
*extdesc
;
213 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
217 /* Device tree itself is in the memory map of device tree. */
218 /* Create a dummy value in memory-map. */
219 chosen
= grub_xnu_create_key (&grub_xnu_devtree_root
, "chosen");
222 memorymap
= grub_xnu_create_key (&(chosen
->first_child
), "memory-map");
226 driverkey
= (struct grub_xnu_devtree_key
*) grub_malloc (sizeof (*driverkey
));
229 driverkey
->name
= grub_strdup ("DeviceTree");
230 if (! driverkey
->name
)
232 driverkey
->datasize
= sizeof (*extdesc
);
233 driverkey
->next
= memorymap
->first_child
;
234 memorymap
->first_child
= driverkey
;
235 driverkey
->data
= extdesc
236 = (struct grub_xnu_extdesc
*) grub_malloc (sizeof (*extdesc
));
237 if (! driverkey
->data
)
240 /* Allocate the space based on the size with dummy value. */
241 *size
= grub_xnu_writetree_get_size (grub_xnu_devtree_root
, "/");
242 err
= grub_xnu_heap_malloc (ALIGN_UP (*size
+ 1, GRUB_XNU_PAGESIZE
),
247 /* Put real data in the dummy. */
248 extdesc
->addr
= *target
;
249 extdesc
->size
= (grub_uint32_t
) *size
;
251 /* Write the tree to heap. */
252 grub_xnu_writetree_toheap_real (src
, grub_xnu_devtree_root
, "/");
253 return GRUB_ERR_NONE
;
256 /* Find a key or value in parent key. */
257 struct grub_xnu_devtree_key
*
258 grub_xnu_find_key (struct grub_xnu_devtree_key
*parent
, const char *name
)
260 struct grub_xnu_devtree_key
*cur
;
261 for (cur
= parent
; cur
; cur
= cur
->next
)
262 if (grub_strcmp (cur
->name
, name
) == 0)
267 struct grub_xnu_devtree_key
*
268 grub_xnu_create_key (struct grub_xnu_devtree_key
**parent
, const char *name
)
270 struct grub_xnu_devtree_key
*ret
;
271 ret
= grub_xnu_find_key (*parent
, name
);
274 ret
= (struct grub_xnu_devtree_key
*) grub_zalloc (sizeof (*ret
));
277 ret
->name
= grub_strdup (name
);
289 struct grub_xnu_devtree_key
*
290 grub_xnu_create_value (struct grub_xnu_devtree_key
**parent
, const char *name
)
292 struct grub_xnu_devtree_key
*ret
;
293 ret
= grub_xnu_find_key (*parent
, name
);
296 if (ret
->datasize
== -1)
297 grub_xnu_free_devtree (ret
->first_child
);
298 else if (ret
->datasize
)
299 grub_free (ret
->data
);
304 ret
= (struct grub_xnu_devtree_key
*) grub_zalloc (sizeof (*ret
));
307 ret
->name
= grub_strdup (name
);
319 grub_xnu_unload (void)
321 grub_cpu_xnu_unload ();
323 grub_xnu_free_devtree (grub_xnu_devtree_root
);
324 grub_xnu_devtree_root
= 0;
326 /* Free loaded image. */
328 driverspackagenum
= 0;
329 grub_relocator_unload (grub_xnu_relocator
);
330 grub_xnu_relocator
= NULL
;
331 grub_xnu_heap_target_start
= 0;
332 grub_xnu_heap_size
= 0;
334 return GRUB_ERR_NONE
;
338 grub_cmd_xnu_kernel (grub_command_t cmd
__attribute__ ((unused
)),
339 int argc
, char *args
[])
343 grub_uint32_t startcode
, endcode
;
347 grub_addr_t loadaddr_target
;
350 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
354 macho
= grub_macho_open (args
[0], 0);
358 err
= grub_macho_size32 (macho
, &startcode
, &endcode
, GRUB_MACHO_NOBSS
,
362 grub_macho_close (macho
);
367 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
368 (unsigned long) endcode
, (unsigned long) startcode
);
370 grub_xnu_relocator
= grub_relocator_new ();
371 if (!grub_xnu_relocator
)
373 grub_xnu_heap_target_start
= startcode
;
374 err
= grub_xnu_heap_malloc (endcode
- startcode
, &loadaddr
,
379 grub_macho_close (macho
);
385 err
= grub_macho_load32 (macho
, args
[0], (char *) loadaddr
- startcode
,
386 GRUB_MACHO_NOBSS
, &grub_xnu_darwin_version
);
389 grub_macho_close (macho
);
394 grub_xnu_entry_point
= grub_macho_get_entry_point32 (macho
, args
[0]);
395 if (! grub_xnu_entry_point
)
397 grub_macho_close (macho
);
399 return grub_error (GRUB_ERR_BAD_OS
, "couldn't find entry point");
402 grub_macho_close (macho
);
404 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
411 /* Copy parameters to kernel command line. */
412 ptr
= grub_xnu_cmdline
;
413 for (i
= 1; i
< argc
; i
++)
415 if (ptr
+ grub_strlen (args
[i
]) + 1
416 >= grub_xnu_cmdline
+ sizeof (grub_xnu_cmdline
))
418 grub_memcpy (ptr
, args
[i
], grub_strlen (args
[i
]));
419 ptr
+= grub_strlen (args
[i
]);
424 /* Replace last space by '\0'. */
425 if (ptr
!= grub_xnu_cmdline
)
428 #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
429 err
= grub_efiemu_autocore ();
434 grub_loader_set (grub_xnu_boot
, grub_xnu_unload
, 0);
437 grub_xnu_is_64bit
= 0;
443 grub_cmd_xnu_kernel64 (grub_command_t cmd
__attribute__ ((unused
)),
444 int argc
, char *args
[])
448 grub_uint64_t startcode
, endcode
;
452 grub_addr_t loadaddr_target
;
455 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
459 macho
= grub_macho_open (args
[0], 1);
463 err
= grub_macho_size64 (macho
, &startcode
, &endcode
, GRUB_MACHO_NOBSS
,
467 grub_macho_close (macho
);
472 startcode
&= 0x0fffffff;
473 endcode
&= 0x0fffffff;
475 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
476 (unsigned long) endcode
, (unsigned long) startcode
);
478 grub_xnu_relocator
= grub_relocator_new ();
479 if (!grub_xnu_relocator
)
481 grub_xnu_heap_target_start
= startcode
;
482 err
= grub_xnu_heap_malloc (endcode
- startcode
, &loadaddr
,
487 grub_macho_close (macho
);
493 err
= grub_macho_load64 (macho
, args
[0], (char *) loadaddr
- startcode
,
494 GRUB_MACHO_NOBSS
, &grub_xnu_darwin_version
);
497 grub_macho_close (macho
);
502 grub_xnu_entry_point
= grub_macho_get_entry_point64 (macho
, args
[0])
504 if (! grub_xnu_entry_point
)
506 grub_macho_close (macho
);
508 return grub_error (GRUB_ERR_BAD_OS
, "couldn't find entry point");
511 grub_macho_close (macho
);
513 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
520 /* Copy parameters to kernel command line. */
521 ptr
= grub_xnu_cmdline
;
522 for (i
= 1; i
< argc
; i
++)
524 if (ptr
+ grub_strlen (args
[i
]) + 1
525 >= grub_xnu_cmdline
+ sizeof (grub_xnu_cmdline
))
527 grub_memcpy (ptr
, args
[i
], grub_strlen (args
[i
]));
528 ptr
+= grub_strlen (args
[i
]);
533 /* Replace last space by '\0'. */
534 if (ptr
!= grub_xnu_cmdline
)
537 #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
538 err
= grub_efiemu_autocore ();
543 grub_loader_set (grub_xnu_boot
, grub_xnu_unload
, 0);
546 grub_xnu_is_64bit
= 1;
551 /* Register a memory in a memory map under name PREFIXSUFFIX
552 and increment SUFFIX. */
554 grub_xnu_register_memory (const char *prefix
, int *suffix
,
555 grub_addr_t addr
, grub_size_t size
)
557 struct grub_xnu_devtree_key
*chosen
;
558 struct grub_xnu_devtree_key
*memorymap
;
559 struct grub_xnu_devtree_key
*driverkey
;
560 struct grub_xnu_extdesc
*extdesc
;
562 if (! grub_xnu_heap_size
)
563 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
565 chosen
= grub_xnu_create_key (&grub_xnu_devtree_root
, "chosen");
568 memorymap
= grub_xnu_create_key (&(chosen
->first_child
), "memory-map");
572 driverkey
= (struct grub_xnu_devtree_key
*) grub_malloc (sizeof (*driverkey
));
576 driverkey
->name
= grub_xasprintf ("%s%d", prefix
, (*suffix
)++);
578 driverkey
->name
= grub_strdup (prefix
);
579 if (!driverkey
->name
)
581 grub_free (driverkey
);
584 driverkey
->datasize
= sizeof (*extdesc
);
585 driverkey
->next
= memorymap
->first_child
;
586 driverkey
->data
= extdesc
587 = (struct grub_xnu_extdesc
*) grub_malloc (sizeof (*extdesc
));
588 if (! driverkey
->data
)
590 grub_free (driverkey
->name
);
591 grub_free (driverkey
);
594 memorymap
->first_child
= driverkey
;
595 extdesc
->addr
= addr
;
596 extdesc
->size
= (grub_uint32_t
) size
;
597 return GRUB_ERR_NONE
;
601 get_name_ptr (char *name
)
604 /* Skip Info.plist. */
605 p2
= grub_strrchr (p
, '/');
612 p2
= grub_strrchr (p
, '/');
617 if (grub_memcmp (p2
, "/Contents/", sizeof ("/Contents/") - 1) != 0)
622 p2
= grub_strrchr (p
, '/');
630 grub_xnu_load_driver (char *infoplistname
, grub_file_t binaryfile
,
631 const char *filename
)
635 grub_file_t infoplist
;
636 struct grub_xnu_extheader
*exthead
;
637 int neededspace
= sizeof (*exthead
);
640 grub_addr_t buf_target
;
641 grub_size_t infoplistsize
= 0, machosize
= 0;
642 char *name
, *nameend
;
645 name
= get_name_ptr (infoplistname
);
646 nameend
= grub_strchr (name
, '/');
649 namelen
= nameend
- name
;
651 namelen
= grub_strlen (name
);
653 neededspace
+= namelen
+ 1;
655 if (! grub_xnu_heap_size
)
656 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
658 /* Compute the needed space. */
661 macho
= grub_macho_file (binaryfile
, filename
, grub_xnu_is_64bit
);
663 grub_file_close (binaryfile
);
666 if (grub_xnu_is_64bit
)
667 machosize
= grub_macho_filesize64 (macho
);
669 machosize
= grub_macho_filesize32 (macho
);
671 neededspace
+= machosize
;
677 infoplist
= grub_file_open (infoplistname
);
680 grub_errno
= GRUB_ERR_NONE
;
683 infoplistsize
= grub_file_size (infoplist
);
684 neededspace
+= infoplistsize
+ 1;
689 /* Allocate the space. */
690 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
693 err
= grub_xnu_heap_malloc (neededspace
, &buf0
, &buf_target
);
698 exthead
= (struct grub_xnu_extheader
*) buf
;
699 grub_memset (exthead
, 0, sizeof (*exthead
));
700 buf
+= sizeof (*exthead
);
702 /* Load the binary. */
705 exthead
->binaryaddr
= buf_target
+ (buf
- (grub_uint8_t
*) buf0
);
706 exthead
->binarysize
= machosize
;
707 if (grub_xnu_is_64bit
)
708 err
= grub_macho_readfile64 (macho
, filename
, buf
);
710 err
= grub_macho_readfile32 (macho
, filename
, buf
);
713 grub_macho_close (macho
);
716 grub_errno
= GRUB_ERR_NONE
;
718 /* Load the plist. */
721 exthead
->infoplistaddr
= buf_target
+ (buf
- (grub_uint8_t
*) buf0
);
722 exthead
->infoplistsize
= infoplistsize
+ 1;
723 if (grub_file_read (infoplist
, buf
, infoplistsize
)
724 != (grub_ssize_t
) (infoplistsize
))
726 grub_file_close (infoplist
);
728 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"),
732 grub_file_close (infoplist
);
733 buf
[infoplistsize
] = 0;
734 buf
+= infoplistsize
+ 1;
736 grub_errno
= GRUB_ERR_NONE
;
738 exthead
->nameaddr
= (buf
- (grub_uint8_t
*) buf0
) + buf_target
;
739 exthead
->namesize
= namelen
+ 1;
740 grub_memcpy (buf
, name
, namelen
);
744 /* Announce to kernel */
745 return grub_xnu_register_memory ("Driver-", &driversnum
, buf_target
,
749 grub_macho_close (macho
);
755 grub_cmd_xnu_mkext (grub_command_t cmd
__attribute__ ((unused
)),
756 int argc
, char *args
[])
760 grub_addr_t loadto_target
;
762 grub_off_t readoff
= 0;
763 grub_ssize_t readlen
= -1;
764 struct grub_macho_fat_header head
;
765 struct grub_macho_fat_arch
*archs
;
769 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
771 if (! grub_xnu_heap_size
)
772 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
774 file
= grub_file_open (args
[0]);
778 /* Sometimes caches are fat binary. Errgh. */
779 if (grub_file_read (file
, &head
, sizeof (head
))
780 != (grub_ssize_t
) (sizeof (head
)))
782 /* I don't know the internal structure of package but
783 can hardly imagine a valid package shorter than 20 bytes. */
784 grub_file_close (file
);
786 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"), args
[0]);
790 /* Find the corresponding architecture. */
791 if (grub_be_to_cpu32 (head
.magic
) == GRUB_MACHO_FAT_MAGIC
)
793 narchs
= grub_be_to_cpu32 (head
.nfat_arch
);
794 archs
= grub_malloc (sizeof (struct grub_macho_fat_arch
) * narchs
);
797 grub_file_close (file
);
801 if (grub_file_read (file
, archs
,
802 sizeof (struct grub_macho_fat_arch
) * narchs
)
803 != (grub_ssize_t
) sizeof(struct grub_macho_fat_arch
) * narchs
)
807 grub_error (GRUB_ERR_READ_ERROR
, N_("premature end of file %s"),
811 for (i
= 0; i
< narchs
; i
++)
813 if (!grub_xnu_is_64bit
&& GRUB_MACHO_CPUTYPE_IS_HOST32
814 (grub_be_to_cpu32 (archs
[i
].cputype
)))
816 readoff
= grub_be_to_cpu32 (archs
[i
].offset
);
817 readlen
= grub_be_to_cpu32 (archs
[i
].size
);
819 if (grub_xnu_is_64bit
&& GRUB_MACHO_CPUTYPE_IS_HOST64
820 (grub_be_to_cpu32 (archs
[i
].cputype
)))
822 readoff
= grub_be_to_cpu32 (archs
[i
].offset
);
823 readlen
= grub_be_to_cpu32 (archs
[i
].size
);
830 /* It's a flat file. Some sane people still exist. */
832 readlen
= grub_file_size (file
);
837 grub_file_close (file
);
838 return grub_error (GRUB_ERR_BAD_OS
, "no suitable architecture is found");
841 /* Allocate space. */
842 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
845 grub_file_close (file
);
849 err
= grub_xnu_heap_malloc (readlen
, &loadto
, &loadto_target
);
852 grub_file_close (file
);
857 grub_file_seek (file
, readoff
);
858 if (grub_file_read (file
, loadto
, readlen
) != (grub_ssize_t
) (readlen
))
860 grub_file_close (file
);
862 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"), args
[0]);
865 grub_file_close (file
);
867 /* Pass it to kernel. */
868 return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum
,
869 loadto_target
, readlen
);
873 grub_cmd_xnu_ramdisk (grub_command_t cmd
__attribute__ ((unused
)),
874 int argc
, char *args
[])
878 grub_addr_t loadto_target
;
883 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
885 if (! grub_xnu_heap_size
)
886 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
888 file
= grub_file_open (args
[0]);
892 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
896 size
= grub_file_size (file
);
898 err
= grub_xnu_heap_malloc (size
, &loadto
, &loadto_target
);
901 if (grub_file_read (file
, loadto
, size
) != (grub_ssize_t
) (size
))
903 grub_file_close (file
);
905 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"), args
[0]);
908 return grub_xnu_register_memory ("RAMDisk", 0, loadto_target
, size
);
911 /* Returns true if the kext should be loaded according to plist
912 and osbundlereq. Also fill BINNAME. */
914 grub_xnu_check_os_bundle_required (char *plistname
,
915 const char *osbundlereq
,
919 char *buf
= 0, *tagstart
= 0, *ptr1
= 0, *keyptr
= 0;
920 char *stringptr
= 0, *ptr2
= 0;
924 int osbundlekeyfound
= 0, binnamekeyfound
= 0;
928 file
= grub_file_open (plistname
);
932 size
= grub_file_size (file
);
933 buf
= grub_malloc (size
);
936 grub_file_close (file
);
939 if (grub_file_read (file
, buf
, size
) != (grub_ssize_t
) (size
))
941 grub_file_close (file
);
943 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"), plistname
);
946 grub_file_close (file
);
948 /* Set the return value for the case when no OSBundleRequired tag is found. */
950 ret
= grub_strword (osbundlereq
, "all") || grub_strword (osbundlereq
, "-");
954 /* Parse plist. It's quite dirty and inextensible but does its job. */
955 for (ptr1
= buf
; ptr1
< buf
+ size
; ptr1
++)
961 if (keyptr
&& depth
== 4
962 && grub_strcmp (keyptr
, "OSBundleRequired") == 0)
963 osbundlekeyfound
= 1;
964 if (keyptr
&& depth
== 4 &&
965 grub_strcmp (keyptr
, "CFBundleExecutable") == 0)
967 if (stringptr
&& osbundlekeyfound
&& osbundlereq
&& depth
== 4)
969 for (ptr2
= stringptr
; *ptr2
; ptr2
++)
970 *ptr2
= grub_tolower (*ptr2
);
971 ret
= grub_strword (osbundlereq
, stringptr
)
972 || grub_strword (osbundlereq
, "all");
974 if (stringptr
&& binnamekeyfound
&& binname
&& depth
== 4)
977 grub_free (*binname
);
978 *binname
= grub_strdup (stringptr
);
989 grub_error (GRUB_ERR_BAD_OS
, "can't parse %s", plistname
);
993 if (tagstart
[1] == '?' || ptr1
[-1] == '/')
995 osbundlekeyfound
= 0;
999 if (depth
== 3 && grub_strcmp (tagstart
+ 1, "key") == 0)
1001 if (depth
== 3 && grub_strcmp (tagstart
+ 1, "string") == 0)
1002 stringptr
= ptr1
+ 1;
1003 else if (grub_strcmp (tagstart
+ 1, "/key") != 0)
1005 osbundlekeyfound
= 0;
1006 binnamekeyfound
= 0;
1010 if (tagstart
[1] == '/')
1021 /* Context for grub_xnu_scan_dir_for_kexts. */
1022 struct grub_xnu_scan_dir_for_kexts_ctx
1025 const char *osbundlerequired
;
1029 /* Helper for grub_xnu_scan_dir_for_kexts. */
1031 grub_xnu_scan_dir_for_kexts_load (const char *filename
,
1032 const struct grub_dirhook_info
*info
,
1035 struct grub_xnu_scan_dir_for_kexts_ctx
*ctx
= data
;
1040 if (filename
[0] == '.')
1043 if (grub_strlen (filename
) < 5 ||
1044 grub_memcmp (filename
+ grub_strlen (filename
) - 5, ".kext", 5) != 0)
1048 = grub_malloc (grub_strlen (ctx
->dirname
) + grub_strlen (filename
) + 2);
1050 /* It's a .kext. Try to load it. */
1053 grub_strcpy (newdirname
, ctx
->dirname
);
1054 newdirname
[grub_strlen (newdirname
) + 1] = 0;
1055 newdirname
[grub_strlen (newdirname
)] = '/';
1056 grub_strcpy (newdirname
+ grub_strlen (newdirname
), filename
);
1057 grub_xnu_load_kext_from_dir (newdirname
, ctx
->osbundlerequired
,
1059 if (grub_errno
== GRUB_ERR_BAD_OS
)
1060 grub_errno
= GRUB_ERR_NONE
;
1061 grub_free (newdirname
);
1066 /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
1068 grub_xnu_scan_dir_for_kexts (char *dirname
, const char *osbundlerequired
,
1071 struct grub_xnu_scan_dir_for_kexts_ctx ctx
= {
1073 .osbundlerequired
= osbundlerequired
,
1074 .maxrecursion
= maxrecursion
1081 if (! grub_xnu_heap_size
)
1082 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
1084 device_name
= grub_file_get_device_name (dirname
);
1085 dev
= grub_device_open (device_name
);
1088 fs
= grub_fs_probe (dev
);
1089 path
= grub_strchr (dirname
, ')');
1096 (fs
->dir
) (dev
, path
, grub_xnu_scan_dir_for_kexts_load
, &ctx
);
1097 grub_device_close (dev
);
1099 grub_free (device_name
);
1101 return GRUB_ERR_NONE
;
1104 /* Context for grub_xnu_load_kext_from_dir. */
1105 struct grub_xnu_load_kext_from_dir_ctx
1108 const char *osbundlerequired
;
1115 /* Helper for grub_xnu_load_kext_from_dir. */
1117 grub_xnu_load_kext_from_dir_load (const char *filename
,
1118 const struct grub_dirhook_info
*info
,
1121 struct grub_xnu_load_kext_from_dir_ctx
*ctx
= data
;
1123 if (grub_strlen (filename
) > 15)
1125 grub_strcpy (ctx
->newdirname
+ grub_strlen (ctx
->dirname
) + 1, filename
);
1127 /* If the kext contains directory "Contents" all real stuff is in
1129 if (info
->dir
&& grub_strcasecmp (filename
, "Contents") == 0)
1130 grub_xnu_load_kext_from_dir (ctx
->newdirname
, ctx
->osbundlerequired
,
1131 ctx
->maxrecursion
- 1);
1133 /* Directory "Plugins" contains nested kexts. */
1134 if (info
->dir
&& grub_strcasecmp (filename
, "Plugins") == 0)
1135 grub_xnu_scan_dir_for_kexts (ctx
->newdirname
, ctx
->osbundlerequired
,
1136 ctx
->maxrecursion
- 1);
1138 /* Directory "MacOS" contains executable, otherwise executable is
1140 if (info
->dir
&& grub_strcasecmp (filename
, "MacOS") == 0)
1143 /* Info.plist is the file which governs our future actions. */
1144 if (! info
->dir
&& grub_strcasecmp (filename
, "Info.plist") == 0
1145 && ! ctx
->plistname
)
1146 ctx
->plistname
= grub_strdup (ctx
->newdirname
);
1150 /* Load extension DIRNAME. (extensions are directories in xnu) */
1152 grub_xnu_load_kext_from_dir (char *dirname
, const char *osbundlerequired
,
1155 struct grub_xnu_load_kext_from_dir_ctx ctx
= {
1157 .osbundlerequired
= osbundlerequired
,
1158 .maxrecursion
= maxrecursion
,
1168 grub_file_t binfile
;
1170 ctx
.newdirname
= grub_malloc (grub_strlen (dirname
) + 20);
1171 if (! ctx
.newdirname
)
1173 grub_strcpy (ctx
.newdirname
, dirname
);
1174 ctx
.newdirname
[grub_strlen (dirname
)] = '/';
1175 ctx
.newdirname
[grub_strlen (dirname
) + 1] = 0;
1176 device_name
= grub_file_get_device_name (dirname
);
1177 dev
= grub_device_open (device_name
);
1180 fs
= grub_fs_probe (dev
);
1181 path
= grub_strchr (dirname
, ')');
1187 newpath
= grub_strchr (ctx
.newdirname
, ')');
1189 newpath
= ctx
.newdirname
;
1193 /* Look at the directory. */
1195 (fs
->dir
) (dev
, path
, grub_xnu_load_kext_from_dir_load
, &ctx
);
1197 if (ctx
.plistname
&& grub_xnu_check_os_bundle_required
1198 (ctx
.plistname
, osbundlerequired
, &binsuffix
))
1202 /* Open the binary. */
1203 char *binname
= grub_malloc (grub_strlen (dirname
)
1204 + grub_strlen (binsuffix
)
1205 + sizeof ("/MacOS/"));
1206 grub_strcpy (binname
, dirname
);
1208 grub_strcpy (binname
+ grub_strlen (binname
), "/MacOS/");
1210 grub_strcpy (binname
+ grub_strlen (binname
), "/");
1211 grub_strcpy (binname
+ grub_strlen (binname
), binsuffix
);
1212 grub_dprintf ("xnu", "%s:%s\n", ctx
.plistname
, binname
);
1213 binfile
= grub_file_open (binname
);
1215 grub_errno
= GRUB_ERR_NONE
;
1217 /* Load the extension. */
1218 grub_xnu_load_driver (ctx
.plistname
, binfile
,
1220 grub_free (binname
);
1221 grub_free (binsuffix
);
1225 grub_dprintf ("xnu", "%s:0\n", ctx
.plistname
);
1226 grub_xnu_load_driver (ctx
.plistname
, 0, 0);
1229 grub_free (ctx
.plistname
);
1230 grub_device_close (dev
);
1232 grub_free (device_name
);
1234 return GRUB_ERR_NONE
;
1238 static int locked
=0;
1239 static grub_dl_t my_mod
;
1241 /* Load the kext. */
1243 grub_cmd_xnu_kext (grub_command_t cmd
__attribute__ ((unused
)),
1244 int argc
, char *args
[])
1246 grub_file_t binfile
= 0;
1248 if (! grub_xnu_heap_size
)
1249 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
1253 /* User explicitly specified plist and binary. */
1254 if (grub_strcmp (args
[1], "-") != 0)
1256 binfile
= grub_file_open (args
[1]);
1260 return grub_xnu_load_driver (grub_strcmp (args
[0], "-") ? args
[0] : 0,
1264 /* load kext normally. */
1266 return grub_xnu_load_kext_from_dir (args
[0], 0, 10);
1268 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
1271 /* Load a directory containing kexts. */
1273 grub_cmd_xnu_kextdir (grub_command_t cmd
__attribute__ ((unused
)),
1274 int argc
, char *args
[])
1276 if (argc
!= 1 && argc
!= 2)
1277 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "directory name required");
1279 if (! grub_xnu_heap_size
)
1280 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
1283 return grub_xnu_scan_dir_for_kexts (args
[0],
1284 "console,root,local-root,network-root",
1288 char *osbundlerequired
= grub_strdup (args
[1]), *ptr
;
1290 if (! osbundlerequired
)
1292 for (ptr
= osbundlerequired
; *ptr
; ptr
++)
1293 *ptr
= grub_tolower (*ptr
);
1294 err
= grub_xnu_scan_dir_for_kexts (args
[0], osbundlerequired
, 10);
1295 grub_free (osbundlerequired
);
1303 if (c
>= '0' && c
<= '9')
1305 if (c
>= 'a' && c
<= 'z')
1306 return c
- 'a' + 10;
1307 if (c
>= 'A' && c
<= 'Z')
1308 return c
- 'A' + 10;
1313 unescape (char *name
, char *curdot
, char *nextdot
, int *len
)
1317 for (ptr
= curdot
; ptr
< nextdot
;)
1318 if (ptr
+ 2 < nextdot
&& *ptr
== '%')
1320 *dptr
= (hextoval (ptr
[1]) << 4) | (hextoval (ptr
[2]));
1334 grub_xnu_fill_devicetree (void)
1336 struct grub_env_var
*var
;
1337 FOR_SORTED_ENV (var
)
1339 char *nextdot
= 0, *curdot
;
1340 struct grub_xnu_devtree_key
**curkey
= &grub_xnu_devtree_root
;
1341 struct grub_xnu_devtree_key
*curvalue
;
1342 char *name
= 0, *data
;
1345 if (grub_memcmp (var
->name
, "XNU.DeviceTree.",
1346 sizeof ("XNU.DeviceTree.") - 1) != 0)
1349 curdot
= var
->name
+ sizeof ("XNU.DeviceTree.") - 1;
1350 nextdot
= grub_strchr (curdot
, '.');
1355 name
= grub_realloc (name
, nextdot
- curdot
+ 1);
1360 unescape (name
, curdot
, nextdot
, &len
);
1363 curkey
= &(grub_xnu_create_key (curkey
, name
)->first_child
);
1366 nextdot
= grub_strchr (nextdot
, '.');
1371 nextdot
= curdot
+ grub_strlen (curdot
) + 1;
1373 name
= grub_realloc (name
, nextdot
- curdot
+ 1);
1378 unescape (name
, curdot
, nextdot
, &len
);
1381 curvalue
= grub_xnu_create_value (curkey
, name
);
1386 data
= grub_malloc (grub_strlen (var
->value
) + 1);
1390 unescape (data
, var
->value
, var
->value
+ grub_strlen (var
->value
),
1392 curvalue
->datasize
= len
;
1393 curvalue
->data
= data
;
1399 struct grub_video_bitmap
*grub_xnu_bitmap
= 0;
1400 grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode
;
1402 /* Option array indices. */
1403 #define XNU_SPLASH_CMD_ARGINDEX_MODE 0
1405 static const struct grub_arg_option xnu_splash_cmd_options
[] =
1407 {"mode", 'm', 0, N_("Background image mode."), N_("stretch|normal"),
1413 grub_cmd_xnu_splash (grub_extcmd_context_t ctxt
,
1414 int argc
, char *args
[])
1418 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
1420 if (! grub_xnu_heap_size
)
1421 return grub_error (GRUB_ERR_BAD_OS
, N_("you need to load the kernel first"));
1423 if (ctxt
->state
[XNU_SPLASH_CMD_ARGINDEX_MODE
].set
&&
1424 grub_strcmp (ctxt
->state
[XNU_SPLASH_CMD_ARGINDEX_MODE
].arg
,
1426 grub_xnu_bitmap_mode
= GRUB_XNU_BITMAP_STRETCH
;
1428 grub_xnu_bitmap_mode
= GRUB_XNU_BITMAP_CENTER
;
1430 err
= grub_video_bitmap_load (&grub_xnu_bitmap
, args
[0]);
1432 grub_xnu_bitmap
= 0;
1438 #ifndef GRUB_MACHINE_EMU
1440 grub_cmd_xnu_resume (grub_command_t cmd
__attribute__ ((unused
)),
1441 int argc
, char *args
[])
1444 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
1446 return grub_xnu_resume (args
[0]);
1451 grub_xnu_lock (void)
1454 grub_dl_ref (my_mod
);
1459 grub_xnu_unlock (void)
1462 grub_dl_unref (my_mod
);
1466 static grub_command_t cmd_kernel64
, cmd_kernel
, cmd_mkext
, cmd_kext
;
1467 static grub_command_t cmd_kextdir
, cmd_ramdisk
, cmd_resume
;
1468 static grub_extcmd_t cmd_splash
;
1472 cmd_kernel
= grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel
, 0,
1473 N_("Load XNU image."));
1474 cmd_kernel64
= grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64
,
1475 0, N_("Load 64-bit XNU image."));
1476 cmd_mkext
= grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext
, 0,
1477 N_("Load XNU extension package."));
1478 cmd_kext
= grub_register_command ("xnu_kext", grub_cmd_xnu_kext
, 0,
1479 N_("Load XNU extension."));
1480 cmd_kextdir
= grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir
,
1481 /* TRANSLATORS: OSBundleRequired is a
1482 variable name in xnu extensions
1483 manifests. It behaves mostly like
1484 GNU/Linux runlevels.
1486 N_("DIRECTORY [OSBundleRequired]"),
1487 /* TRANSLATORS: There are many extensions
1488 in extension directory. */
1489 N_("Load XNU extension directory."));
1490 cmd_ramdisk
= grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk
, 0,
1491 /* TRANSLATORS: ramdisk here isn't identifier. It can be translated. */
1492 N_("Load XNU ramdisk. "
1493 "It will be available in OS as md0."));
1494 cmd_splash
= grub_register_extcmd ("xnu_splash",
1495 grub_cmd_xnu_splash
, 0, 0,
1496 N_("Load a splash image for XNU."),
1497 xnu_splash_cmd_options
);
1499 #ifndef GRUB_MACHINE_EMU
1500 cmd_resume
= grub_register_command ("xnu_resume", grub_cmd_xnu_resume
,
1501 0, N_("Load an image of hibernated"
1505 grub_cpu_xnu_init ();
1512 #ifndef GRUB_MACHINE_EMU
1513 grub_unregister_command (cmd_resume
);
1515 grub_unregister_command (cmd_mkext
);
1516 grub_unregister_command (cmd_kext
);
1517 grub_unregister_command (cmd_kextdir
);
1518 grub_unregister_command (cmd_ramdisk
);
1519 grub_unregister_command (cmd_kernel
);
1520 grub_unregister_extcmd (cmd_splash
);
1521 grub_unregister_command (cmd_kernel64
);
1523 grub_cpu_xnu_fini ();