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/gzio.h>
32 #include <grub/command.h>
33 #include <grub/misc.h>
34 #include <grub/extcmd.h>
36 #include <grub/i18n.h>
38 struct grub_xnu_devtree_key
*grub_xnu_devtree_root
= 0;
39 static int driverspackagenum
= 0;
40 static int driversnum
= 0;
41 int grub_xnu_is_64bit
= 0;
43 void *grub_xnu_heap_start
= 0;
44 grub_size_t grub_xnu_heap_size
= 0;
46 /* Allocate heap by 32MB-blocks. */
47 #define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
50 grub_xnu_register_memory (char *prefix
, int *suffix
,
51 void *addr
, grub_size_t size
);
53 grub_xnu_heap_malloc (int size
)
56 int oldblknum
, newblknum
;
58 /* The page after the heap is used for stack. Ensure it's usable. */
59 if (grub_xnu_heap_size
)
60 oldblknum
= (grub_xnu_heap_size
+ GRUB_XNU_PAGESIZE
61 + GRUB_XNU_HEAP_ALLOC_BLOCK
- 1) / GRUB_XNU_HEAP_ALLOC_BLOCK
;
64 newblknum
= (grub_xnu_heap_size
+ size
+ GRUB_XNU_PAGESIZE
65 + GRUB_XNU_HEAP_ALLOC_BLOCK
- 1) / GRUB_XNU_HEAP_ALLOC_BLOCK
;
66 if (oldblknum
!= newblknum
)
68 /* FIXME: instruct realloc to allocate at 1MB if possible once
69 advanced mm is ready. */
71 = XNU_RELOCATOR (realloc
) (grub_xnu_heap_start
,
73 * GRUB_XNU_HEAP_ALLOC_BLOCK
);
74 if (!grub_xnu_heap_start
)
78 val
= (grub_uint8_t
*) grub_xnu_heap_start
+ grub_xnu_heap_size
;
79 grub_xnu_heap_size
+= size
;
80 grub_dprintf ("xnu", "val=%p\n", val
);
84 /* Make sure next block of the heap will be aligned.
85 Please notice: aligned are pointers AFTER relocation
86 and not the current ones. */
88 grub_xnu_align_heap (int align
)
90 int align_overhead
= align
- grub_xnu_heap_size
% align
;
91 if (align_overhead
== align
)
93 if (! grub_xnu_heap_malloc (align_overhead
))
98 /* Free subtree pointed by CUR. */
100 grub_xnu_free_devtree (struct grub_xnu_devtree_key
*cur
)
102 struct grub_xnu_devtree_key
*d
;
105 grub_free (cur
->name
);
106 if (cur
->datasize
== -1)
107 grub_xnu_free_devtree (cur
->first_child
);
109 grub_free (cur
->data
);
116 /* Compute the size of device tree in xnu format. */
118 grub_xnu_writetree_get_size (struct grub_xnu_devtree_key
*start
, char *name
)
121 struct grub_xnu_devtree_key
*cur
;
124 ret
= 2 * sizeof (grub_uint32_t
);
127 ret
+= 32 + sizeof (grub_uint32_t
)
128 + grub_strlen (name
) + 4
129 - (grub_strlen (name
) % 4);
131 for (cur
= start
; cur
; cur
= cur
->next
)
132 if (cur
->datasize
!= -1)
136 align_overhead
= 4 - (cur
->datasize
% 4);
137 if (align_overhead
== 4)
139 ret
+= 32 + sizeof (grub_uint32_t
) + cur
->datasize
+ align_overhead
;
142 ret
+= grub_xnu_writetree_get_size (cur
->first_child
, cur
->name
);
146 /* Write devtree in XNU format at curptr assuming the head is named NAME.*/
148 grub_xnu_writetree_toheap_real (void *curptr
,
149 struct grub_xnu_devtree_key
*start
, char *name
)
151 struct grub_xnu_devtree_key
*cur
;
152 int nkeys
= 0, nvals
= 0;
153 for (cur
= start
; cur
; cur
= cur
->next
)
155 if (cur
->datasize
== -1)
163 *((grub_uint32_t
*) curptr
) = nvals
;
164 curptr
= ((grub_uint32_t
*) curptr
) + 1;
165 *((grub_uint32_t
*) curptr
) = nkeys
;
166 curptr
= ((grub_uint32_t
*) curptr
) + 1;
168 /* First comes "name" value. */
169 grub_memset (curptr
, 0, 32);
170 grub_memcpy (curptr
, "name", 4);
171 curptr
= ((grub_uint8_t
*) curptr
) + 32;
172 *((grub_uint32_t
*)curptr
) = grub_strlen (name
) + 1;
173 curptr
= ((grub_uint32_t
*) curptr
) + 1;
174 grub_memcpy (curptr
, name
, grub_strlen (name
));
175 curptr
= ((grub_uint8_t
*) curptr
) + grub_strlen (name
);
176 grub_memset (curptr
, 0, 4 - (grub_strlen (name
) % 4));
177 curptr
= ((grub_uint8_t
*) curptr
) + (4 - (grub_strlen (name
) % 4));
179 /* Then the other values. */
180 for (cur
= start
; cur
; cur
= cur
->next
)
181 if (cur
->datasize
!= -1)
185 align_overhead
= 4 - (cur
->datasize
% 4);
186 if (align_overhead
== 4)
188 grub_memset (curptr
, 0, 32);
189 grub_strncpy (curptr
, cur
->name
, 31);
190 curptr
= ((grub_uint8_t
*) curptr
) + 32;
191 *((grub_uint32_t
*) curptr
) = cur
->datasize
;
192 curptr
= ((grub_uint32_t
*) curptr
) + 1;
193 grub_memcpy (curptr
, cur
->data
, cur
->datasize
);
194 curptr
= ((grub_uint8_t
*) curptr
) + cur
->datasize
;
195 grub_memset (curptr
, 0, align_overhead
);
196 curptr
= ((grub_uint8_t
*) curptr
) + align_overhead
;
199 /* And then the keys. Recursively use this function. */
200 for (cur
= start
; cur
; cur
= cur
->next
)
201 if (cur
->datasize
== -1)
202 if (!(curptr
= grub_xnu_writetree_toheap_real (curptr
,
210 grub_xnu_writetree_toheap (void **start
, grub_size_t
*size
)
212 struct grub_xnu_devtree_key
*chosen
;
213 struct grub_xnu_devtree_key
*memorymap
;
214 struct grub_xnu_devtree_key
*driverkey
;
215 struct grub_xnu_extdesc
*extdesc
;
218 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
222 /* Device tree itself is in the memory map of device tree. */
223 /* Create a dummy value in memory-map. */
224 chosen
= grub_xnu_create_key (&grub_xnu_devtree_root
, "chosen");
227 memorymap
= grub_xnu_create_key (&(chosen
->first_child
), "memory-map");
231 driverkey
= (struct grub_xnu_devtree_key
*) grub_malloc (sizeof (*driverkey
));
233 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't write device tree");
234 driverkey
->name
= grub_strdup ("DeviceTree");
235 if (! driverkey
->name
)
236 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't write device tree");
237 driverkey
->datasize
= sizeof (*extdesc
);
238 driverkey
->next
= memorymap
->first_child
;
239 memorymap
->first_child
= driverkey
;
240 driverkey
->data
= extdesc
241 = (struct grub_xnu_extdesc
*) grub_malloc (sizeof (*extdesc
));
242 if (! driverkey
->data
)
243 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't write device tree");
245 /* Allocate the space based on the size with dummy value. */
246 *size
= grub_xnu_writetree_get_size (grub_xnu_devtree_root
, "/");
247 *start
= grub_xnu_heap_malloc (*size
+ GRUB_XNU_PAGESIZE
248 - *size
% GRUB_XNU_PAGESIZE
);
250 /* Put real data in the dummy. */
251 extdesc
->addr
= (grub_uint8_t
*) *start
- (grub_uint8_t
*) grub_xnu_heap_start
252 + grub_xnu_heap_will_be_at
;
253 extdesc
->size
= (grub_uint32_t
) *size
;
255 /* Write the tree to heap. */
256 grub_xnu_writetree_toheap_real (*start
, grub_xnu_devtree_root
, "/");
257 return GRUB_ERR_NONE
;
260 /* Find a key or value in parent key. */
261 struct grub_xnu_devtree_key
*
262 grub_xnu_find_key (struct grub_xnu_devtree_key
*parent
, char *name
)
264 struct grub_xnu_devtree_key
*cur
;
265 for (cur
= parent
; cur
; cur
= cur
->next
)
266 if (grub_strcmp (cur
->name
, name
) == 0)
271 struct grub_xnu_devtree_key
*
272 grub_xnu_create_key (struct grub_xnu_devtree_key
**parent
, char *name
)
274 struct grub_xnu_devtree_key
*ret
;
275 ret
= grub_xnu_find_key (*parent
, name
);
278 ret
= (struct grub_xnu_devtree_key
*) grub_zalloc (sizeof (*ret
));
281 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't create key %s", name
);
284 ret
->name
= grub_strdup (name
);
288 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't create key %s", name
);
297 struct grub_xnu_devtree_key
*
298 grub_xnu_create_value (struct grub_xnu_devtree_key
**parent
, char *name
)
300 struct grub_xnu_devtree_key
*ret
;
301 ret
= grub_xnu_find_key (*parent
, name
);
304 if (ret
->datasize
== -1)
305 grub_xnu_free_devtree (ret
->first_child
);
306 else if (ret
->datasize
)
307 grub_free (ret
->data
);
312 ret
= (struct grub_xnu_devtree_key
*) grub_zalloc (sizeof (*ret
));
315 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't create value %s", name
);
318 ret
->name
= grub_strdup (name
);
322 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't create value %s", name
);
331 grub_xnu_unload (void)
333 grub_cpu_xnu_unload ();
335 grub_xnu_free_devtree (grub_xnu_devtree_root
);
336 grub_xnu_devtree_root
= 0;
338 /* Free loaded image. */
340 driverspackagenum
= 0;
341 grub_free (grub_xnu_heap_start
);
342 grub_xnu_heap_start
= 0;
343 grub_xnu_heap_size
= 0;
345 return GRUB_ERR_NONE
;
349 grub_cmd_xnu_kernel (grub_command_t cmd
__attribute__ ((unused
)),
350 int argc
, char *args
[])
354 grub_uint32_t startcode
, endcode
;
356 char *ptr
, *loadaddr
;
359 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
363 macho
= grub_macho_open (args
[0]);
366 if (! grub_macho_contains_macho32 (macho
))
368 grub_macho_close (macho
);
369 return grub_error (GRUB_ERR_BAD_OS
,
370 "kernel doesn't contain suitable 32-bit architecture");
373 err
= grub_macho_size32 (macho
, &startcode
, &endcode
, GRUB_MACHO_NOBSS
);
376 grub_macho_close (macho
);
381 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
382 (unsigned long) endcode
, (unsigned long) startcode
);
384 loadaddr
= grub_xnu_heap_malloc (endcode
- startcode
);
385 grub_xnu_heap_will_be_at
= startcode
;
389 grub_macho_close (macho
);
391 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
392 "not enough memory to load kernel");
396 err
= grub_macho_load32 (macho
, loadaddr
- startcode
, GRUB_MACHO_NOBSS
);
399 grub_macho_close (macho
);
404 grub_xnu_entry_point
= grub_macho_get_entry_point32 (macho
);
405 if (! grub_xnu_entry_point
)
407 grub_macho_close (macho
);
409 return grub_error (GRUB_ERR_BAD_OS
, "couldn't find entry point");
412 grub_macho_close (macho
);
414 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
421 /* Copy parameters to kernel command line. */
422 ptr
= grub_xnu_cmdline
;
423 for (i
= 1; i
< argc
; i
++)
425 if (ptr
+ grub_strlen (args
[i
]) + 1
426 >= grub_xnu_cmdline
+ sizeof (grub_xnu_cmdline
))
428 grub_memcpy (ptr
, args
[i
], grub_strlen (args
[i
]));
429 ptr
+= grub_strlen (args
[i
]);
434 /* Replace last space by '\0'. */
435 if (ptr
!= grub_xnu_cmdline
)
438 grub_loader_set (grub_xnu_boot
, grub_xnu_unload
, 0);
441 grub_xnu_is_64bit
= 0;
447 grub_cmd_xnu_kernel64 (grub_command_t cmd
__attribute__ ((unused
)),
448 int argc
, char *args
[])
452 grub_uint64_t startcode
, endcode
;
454 char *ptr
, *loadaddr
;
457 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
461 macho
= grub_macho_open (args
[0]);
464 if (! grub_macho_contains_macho64 (macho
))
466 grub_macho_close (macho
);
467 return grub_error (GRUB_ERR_BAD_OS
,
468 "kernel doesn't contain suitable 64-bit architecture");
471 err
= grub_macho_size64 (macho
, &startcode
, &endcode
, GRUB_MACHO_NOBSS
);
474 grub_macho_close (macho
);
479 startcode
&= 0x0fffffff;
480 endcode
&= 0x0fffffff;
482 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
483 (unsigned long) endcode
, (unsigned long) startcode
);
485 loadaddr
= grub_xnu_heap_malloc (endcode
- startcode
);
486 grub_xnu_heap_will_be_at
= startcode
;
490 grub_macho_close (macho
);
492 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
493 "not enough memory to load kernel");
497 err
= grub_macho_load64 (macho
, loadaddr
- startcode
, GRUB_MACHO_NOBSS
);
500 grub_macho_close (macho
);
505 grub_xnu_entry_point
= grub_macho_get_entry_point64 (macho
) & 0x0fffffff;
506 if (! grub_xnu_entry_point
)
508 grub_macho_close (macho
);
510 return grub_error (GRUB_ERR_BAD_OS
, "couldn't find entry point");
513 grub_macho_close (macho
);
515 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
522 /* Copy parameters to kernel command line. */
523 ptr
= grub_xnu_cmdline
;
524 for (i
= 1; i
< argc
; i
++)
526 if (ptr
+ grub_strlen (args
[i
]) + 1
527 >= grub_xnu_cmdline
+ sizeof (grub_xnu_cmdline
))
529 grub_memcpy (ptr
, args
[i
], grub_strlen (args
[i
]));
530 ptr
+= grub_strlen (args
[i
]);
535 /* Replace last space by '\0'. */
536 if (ptr
!= grub_xnu_cmdline
)
539 grub_loader_set (grub_xnu_boot
, grub_xnu_unload
, 0);
542 grub_xnu_is_64bit
= 1;
547 /* Register a memory in a memory map under name PREFIXSUFFIX
548 and increment SUFFIX. */
550 grub_xnu_register_memory (char *prefix
, int *suffix
,
551 void *addr
, grub_size_t size
)
553 struct grub_xnu_devtree_key
*chosen
;
554 struct grub_xnu_devtree_key
*memorymap
;
555 struct grub_xnu_devtree_key
*driverkey
;
556 struct grub_xnu_extdesc
*extdesc
;
558 if (! grub_xnu_heap_size
)
559 return grub_error (GRUB_ERR_BAD_OS
, "no xnu kernel loaded");
561 chosen
= grub_xnu_create_key (&grub_xnu_devtree_root
, "chosen");
564 memorymap
= grub_xnu_create_key (&(chosen
->first_child
), "memory-map");
568 driverkey
= (struct grub_xnu_devtree_key
*) grub_malloc (sizeof (*driverkey
));
570 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't register memory");
573 driverkey
->name
= grub_xasprintf ("%s%d", prefix
, (*suffix
)++);
574 if (!driverkey
->name
)
575 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't register memory");
578 driverkey
->name
= grub_strdup (prefix
);
579 if (! driverkey
->name
)
580 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't register extension");
581 driverkey
->datasize
= sizeof (*extdesc
);
582 driverkey
->next
= memorymap
->first_child
;
583 memorymap
->first_child
= driverkey
;
584 driverkey
->data
= extdesc
585 = (struct grub_xnu_extdesc
*) grub_malloc (sizeof (*extdesc
));
586 if (! driverkey
->data
)
587 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "can't register extension");
588 extdesc
->addr
= grub_xnu_heap_will_be_at
+
589 ((grub_uint8_t
*) addr
- (grub_uint8_t
*) grub_xnu_heap_start
);
590 extdesc
->size
= (grub_uint32_t
) size
;
591 return GRUB_ERR_NONE
;
595 get_name_ptr (char *name
)
598 /* Skip Info.plist. */
599 p2
= grub_strrchr (p
, '/');
606 p2
= grub_strrchr (p
, '/');
611 if (grub_memcmp (p2
, "/Contents/", sizeof ("/Contents/") - 1) != 0)
616 p2
= grub_strrchr (p
, '/');
624 grub_xnu_load_driver (char *infoplistname
, grub_file_t binaryfile
)
628 grub_file_t infoplist
;
629 struct grub_xnu_extheader
*exthead
;
630 int neededspace
= sizeof (*exthead
);
632 grub_size_t infoplistsize
= 0, machosize
= 0;
633 char *name
, *nameend
;
636 name
= get_name_ptr (infoplistname
);
637 nameend
= grub_strchr (name
, '/');
640 namelen
= nameend
- name
;
642 namelen
= grub_strlen (name
);
644 neededspace
+= namelen
+ 1;
646 if (! grub_xnu_heap_size
)
647 return grub_error (GRUB_ERR_BAD_OS
, "no xnu kernel loaded");
649 /* Compute the needed space. */
652 macho
= grub_macho_file (binaryfile
);
653 if (! macho
|| ! grub_macho_contains_macho32 (macho
))
656 grub_macho_close (macho
);
657 return grub_error (GRUB_ERR_BAD_OS
,
658 "extension doesn't contain suitable architecture");
660 if (grub_xnu_is_64bit
)
661 machosize
= grub_macho_filesize64 (macho
);
663 machosize
= grub_macho_filesize32 (macho
);
664 neededspace
+= machosize
;
670 infoplist
= grub_gzfile_open (infoplistname
, 1);
673 grub_errno
= GRUB_ERR_NONE
;
676 infoplistsize
= grub_file_size (infoplist
);
677 neededspace
+= infoplistsize
+ 1;
682 /* Allocate the space. */
683 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
686 buf
= grub_xnu_heap_malloc (neededspace
);
688 exthead
= (struct grub_xnu_extheader
*) buf
;
689 grub_memset (exthead
, 0, sizeof (*exthead
));
690 buf
+= sizeof (*exthead
);
692 /* Load the binary. */
695 exthead
->binaryaddr
= (buf
- (grub_uint8_t
*) grub_xnu_heap_start
)
696 + grub_xnu_heap_will_be_at
;
697 exthead
->binarysize
= machosize
;
698 if (grub_xnu_is_64bit
)
699 err
= grub_macho_readfile64 (macho
, buf
);
701 err
= grub_macho_readfile32 (macho
, buf
);
704 grub_macho_close (macho
);
707 grub_macho_close (macho
);
710 grub_errno
= GRUB_ERR_NONE
;
712 /* Load the plist. */
715 exthead
->infoplistaddr
= (buf
- (grub_uint8_t
*) grub_xnu_heap_start
)
716 + grub_xnu_heap_will_be_at
;
717 exthead
->infoplistsize
= infoplistsize
+ 1;
718 if (grub_file_read (infoplist
, buf
, infoplistsize
)
719 != (grub_ssize_t
) (infoplistsize
))
721 grub_file_close (infoplist
);
723 return grub_error (GRUB_ERR_BAD_OS
, "couldn't read file %s: ",
726 grub_file_close (infoplist
);
727 buf
[infoplistsize
] = 0;
728 buf
+= infoplistsize
+ 1;
730 grub_errno
= GRUB_ERR_NONE
;
732 exthead
->nameaddr
= (buf
- (grub_uint8_t
*) grub_xnu_heap_start
)
733 + grub_xnu_heap_will_be_at
;
734 exthead
->namesize
= namelen
+ 1;
735 grub_memcpy (buf
, name
, namelen
);
739 /* Announce to kernel */
740 return grub_xnu_register_memory ("Driver-", &driversnum
, exthead
,
746 grub_cmd_xnu_mkext (grub_command_t cmd
__attribute__ ((unused
)),
747 int argc
, char *args
[])
752 grub_off_t readoff
= 0;
753 grub_ssize_t readlen
= -1;
754 struct grub_macho_fat_header head
;
755 struct grub_macho_fat_arch
*archs
;
759 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
761 if (! grub_xnu_heap_size
)
762 return grub_error (GRUB_ERR_BAD_OS
, "no xnu kernel loaded");
764 file
= grub_gzfile_open (args
[0], 1);
766 return grub_error (GRUB_ERR_FILE_NOT_FOUND
,
767 "couldn't load driver package");
769 /* Sometimes caches are fat binary. Errgh. */
770 if (grub_file_read (file
, &head
, sizeof (head
))
771 != (grub_ssize_t
) (sizeof (head
)))
773 /* I don't know the internal structure of package but
774 can hardly imagine a valid package shorter than 20 bytes. */
775 grub_file_close (file
);
777 return grub_error (GRUB_ERR_BAD_OS
, "couldn't read file %s", args
[0]);
780 /* Find the corresponding architecture. */
781 if (grub_be_to_cpu32 (head
.magic
) == GRUB_MACHO_FAT_MAGIC
)
783 narchs
= grub_be_to_cpu32 (head
.nfat_arch
);
784 archs
= grub_malloc (sizeof (struct grub_macho_fat_arch
) * narchs
);
787 grub_file_close (file
);
789 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
790 "couldn't read file %s", args
[0]);
793 if (grub_file_read (file
, archs
,
794 sizeof (struct grub_macho_fat_arch
) * narchs
)
795 != (grub_ssize_t
) sizeof(struct grub_macho_fat_arch
) * narchs
)
799 return grub_error (GRUB_ERR_READ_ERROR
, "cannot read fat header");
801 for (i
= 0; i
< narchs
; i
++)
803 if (!grub_xnu_is_64bit
&& GRUB_MACHO_CPUTYPE_IS_HOST32
804 (grub_be_to_cpu32 (archs
[i
].cputype
)))
806 readoff
= grub_be_to_cpu32 (archs
[i
].offset
);
807 readlen
= grub_be_to_cpu32 (archs
[i
].size
);
809 if (grub_xnu_is_64bit
&& GRUB_MACHO_CPUTYPE_IS_HOST64
810 (grub_be_to_cpu32 (archs
[i
].cputype
)))
812 readoff
= grub_be_to_cpu32 (archs
[i
].offset
);
813 readlen
= grub_be_to_cpu32 (archs
[i
].size
);
820 /* It's a flat file. Some sane people still exist. */
822 readlen
= grub_file_size (file
);
827 grub_file_close (file
);
828 return grub_error (GRUB_ERR_BAD_OS
, "no suitable architecture is found");
831 /* Allocate space. */
832 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
835 grub_file_close (file
);
839 loadto
= grub_xnu_heap_malloc (readlen
);
842 grub_file_close (file
);
847 grub_file_seek (file
, readoff
);
848 if (grub_file_read (file
, loadto
, readlen
) != (grub_ssize_t
) (readlen
))
850 grub_file_close (file
);
852 return grub_error (GRUB_ERR_BAD_OS
, "couldn't read file %s", args
[0]);
854 grub_file_close (file
);
856 /* Pass it to kernel. */
857 return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum
,
862 grub_cmd_xnu_ramdisk (grub_command_t cmd
__attribute__ ((unused
)),
863 int argc
, char *args
[])
871 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
873 if (! grub_xnu_heap_size
)
874 return grub_error (GRUB_ERR_BAD_OS
, "no xnu kernel loaded");
876 file
= grub_gzfile_open (args
[0], 1);
878 return grub_error (GRUB_ERR_FILE_NOT_FOUND
,
879 "couldn't load ramdisk");
881 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
885 size
= grub_file_size (file
);
887 loadto
= grub_xnu_heap_malloc (size
);
890 if (grub_file_read (file
, loadto
, size
)
891 != (grub_ssize_t
) (size
))
893 grub_file_close (file
);
895 return grub_error (GRUB_ERR_BAD_OS
, "couldn't read file %s", args
[0]);
897 return grub_xnu_register_memory ("RAMDisk", 0, loadto
, size
);
900 /* Returns true if the kext should be loaded according to plist
901 and osbundlereq. Also fill BINNAME. */
903 grub_xnu_check_os_bundle_required (char *plistname
, char *osbundlereq
,
907 char *buf
= 0, *tagstart
= 0, *ptr1
= 0, *keyptr
= 0;
908 char *stringptr
= 0, *ptr2
= 0;
912 int osbundlekeyfound
= 0, binnamekeyfound
= 0;
916 file
= grub_gzfile_open (plistname
, 1);
919 grub_file_close (file
);
921 grub_error (GRUB_ERR_BAD_OS
, "couldn't read file %s", plistname
);
925 size
= grub_file_size (file
);
926 buf
= grub_malloc (size
);
929 grub_file_close (file
);
931 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "couldn't read file %s", plistname
);
934 if (grub_file_read (file
, buf
, size
) != (grub_ssize_t
) (size
))
936 grub_file_close (file
);
938 grub_error (GRUB_ERR_BAD_OS
, "couldn't read file %s", plistname
);
941 grub_file_close (file
);
943 /* Set the return value for the case when no OSBundleRequired tag is found. */
945 ret
= grub_strword (osbundlereq
, "all") || grub_strword (osbundlereq
, "-");
949 /* Parse plist. It's quite dirty and inextensible but does its job. */
950 for (ptr1
= buf
; ptr1
< buf
+ size
; ptr1
++)
956 if (keyptr
&& depth
== 4
957 && grub_strcmp (keyptr
, "OSBundleRequired") == 0)
958 osbundlekeyfound
= 1;
959 if (keyptr
&& depth
== 4 &&
960 grub_strcmp (keyptr
, "CFBundleExecutable") == 0)
962 if (stringptr
&& osbundlekeyfound
&& osbundlereq
&& depth
== 4)
964 for (ptr2
= stringptr
; *ptr2
; ptr2
++)
965 *ptr2
= grub_tolower (*ptr2
);
966 ret
= grub_strword (osbundlereq
, stringptr
)
967 || grub_strword (osbundlereq
, "all");
969 if (stringptr
&& binnamekeyfound
&& binname
&& depth
== 4)
972 grub_free (*binname
);
973 *binname
= grub_strdup (stringptr
);
984 grub_error (GRUB_ERR_BAD_OS
, "can't parse %s", plistname
);
988 if (tagstart
[1] == '?' || ptr1
[-1] == '/')
990 osbundlekeyfound
= 0;
994 if (depth
== 3 && grub_strcmp (tagstart
+ 1, "key") == 0)
996 if (depth
== 3 && grub_strcmp (tagstart
+ 1, "string") == 0)
997 stringptr
= ptr1
+ 1;
998 else if (grub_strcmp (tagstart
+ 1, "/key") != 0)
1000 osbundlekeyfound
= 0;
1001 binnamekeyfound
= 0;
1005 if (tagstart
[1] == '/')
1016 /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
1018 grub_xnu_scan_dir_for_kexts (char *dirname
, char *osbundlerequired
,
1026 auto int load_hook (const char *filename
,
1027 const struct grub_dirhook_info
*info
);
1028 int load_hook (const char *filename
, const struct grub_dirhook_info
*info
)
1033 if (filename
[0] == '.')
1036 if (grub_strlen (filename
) < 5 ||
1037 grub_memcmp (filename
+ grub_strlen (filename
) - 5, ".kext", 5) != 0)
1041 = grub_malloc (grub_strlen (dirname
) + grub_strlen (filename
) + 2);
1043 /* It's a .kext. Try to load it. */
1046 grub_strcpy (newdirname
, dirname
);
1047 newdirname
[grub_strlen (newdirname
) + 1] = 0;
1048 newdirname
[grub_strlen (newdirname
)] = '/';
1049 grub_strcpy (newdirname
+ grub_strlen (newdirname
), filename
);
1050 grub_xnu_load_kext_from_dir (newdirname
, osbundlerequired
,
1052 if (grub_errno
== GRUB_ERR_BAD_OS
)
1053 grub_errno
= GRUB_ERR_NONE
;
1054 grub_free (newdirname
);
1059 if (! grub_xnu_heap_size
)
1060 return grub_error (GRUB_ERR_BAD_OS
, "no xnu kernel loaded");
1062 device_name
= grub_file_get_device_name (dirname
);
1063 dev
= grub_device_open (device_name
);
1066 fs
= grub_fs_probe (dev
);
1067 path
= grub_strchr (dirname
, ')');
1074 (fs
->dir
) (dev
, path
, load_hook
);
1075 grub_device_close (dev
);
1077 grub_free (device_name
);
1079 return GRUB_ERR_NONE
;
1082 /* Load extension DIRNAME. (extensions are directories in xnu) */
1084 grub_xnu_load_kext_from_dir (char *dirname
, char *osbundlerequired
,
1088 char *plistname
= 0;
1096 grub_file_t binfile
;
1098 auto int load_hook (const char *filename
,
1099 const struct grub_dirhook_info
*info
);
1101 int load_hook (const char *filename
, const struct grub_dirhook_info
*info
)
1103 if (grub_strlen (filename
) > 15)
1105 grub_strcpy (newdirname
+ grub_strlen (dirname
) + 1, filename
);
1107 /* If the kext contains directory "Contents" all real stuff is in
1109 if (info
->dir
&& grub_strcasecmp (filename
, "Contents") == 0)
1110 grub_xnu_load_kext_from_dir (newdirname
, osbundlerequired
,
1113 /* Directory "Plugins" contains nested kexts. */
1114 if (info
->dir
&& grub_strcasecmp (filename
, "Plugins") == 0)
1115 grub_xnu_scan_dir_for_kexts (newdirname
, osbundlerequired
,
1118 /* Directory "MacOS" contains executable, otherwise executable is
1120 if (info
->dir
&& grub_strcasecmp (filename
, "MacOS") == 0)
1123 /* Info.plist is the file which governs our future actions. */
1124 if (! info
->dir
&& grub_strcasecmp (filename
, "Info.plist") == 0
1126 plistname
= grub_strdup (newdirname
);
1130 newdirname
= grub_malloc (grub_strlen (dirname
) + 20);
1132 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "couldn't allocate buffer");
1133 grub_strcpy (newdirname
, dirname
);
1134 newdirname
[grub_strlen (dirname
)] = '/';
1135 newdirname
[grub_strlen (dirname
) + 1] = 0;
1136 device_name
= grub_file_get_device_name (dirname
);
1137 dev
= grub_device_open (device_name
);
1140 fs
= grub_fs_probe (dev
);
1141 path
= grub_strchr (dirname
, ')');
1147 newpath
= grub_strchr (newdirname
, ')');
1149 newpath
= newdirname
;
1153 /* Look at the directory. */
1155 (fs
->dir
) (dev
, path
, load_hook
);
1157 if (plistname
&& grub_xnu_check_os_bundle_required
1158 (plistname
, osbundlerequired
, &binsuffix
))
1162 /* Open the binary. */
1163 char *binname
= grub_malloc (grub_strlen (dirname
)
1164 + grub_strlen (binsuffix
)
1165 + sizeof ("/MacOS/"));
1166 grub_strcpy (binname
, dirname
);
1168 grub_strcpy (binname
+ grub_strlen (binname
), "/MacOS/");
1170 grub_strcpy (binname
+ grub_strlen (binname
), "/");
1171 grub_strcpy (binname
+ grub_strlen (binname
), binsuffix
);
1172 grub_dprintf ("xnu", "%s:%s\n", plistname
, binname
);
1173 binfile
= grub_gzfile_open (binname
, 1);
1175 grub_errno
= GRUB_ERR_NONE
;
1177 /* Load the extension. */
1178 grub_xnu_load_driver (plistname
, binfile
);
1179 grub_free (binname
);
1180 grub_free (binsuffix
);
1184 grub_dprintf ("xnu", "%s:0\n", plistname
);
1185 grub_xnu_load_driver (plistname
, 0);
1188 grub_free (plistname
);
1189 grub_device_close (dev
);
1191 grub_free (device_name
);
1193 return GRUB_ERR_NONE
;
1197 static int locked
=0;
1198 static grub_dl_t my_mod
;
1200 /* Load the kext. */
1202 grub_cmd_xnu_kext (grub_command_t cmd
__attribute__ ((unused
)),
1203 int argc
, char *args
[])
1205 grub_file_t binfile
= 0;
1208 /* User explicitly specified plist and binary. */
1209 if (grub_strcmp (args
[1], "-") != 0)
1211 binfile
= grub_gzfile_open (args
[1], 1);
1214 grub_error (GRUB_ERR_BAD_OS
, "can't open file");
1215 return GRUB_ERR_NONE
;
1218 return grub_xnu_load_driver (grub_strcmp (args
[0], "-") ? args
[0] : 0,
1222 /* load kext normally. */
1224 return grub_xnu_load_kext_from_dir (args
[0], 0, 10);
1226 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
1229 /* Load a directory containing kexts. */
1231 grub_cmd_xnu_kextdir (grub_command_t cmd
__attribute__ ((unused
)),
1232 int argc
, char *args
[])
1234 if (argc
!= 1 && argc
!= 2)
1235 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "directory name required");
1238 return grub_xnu_scan_dir_for_kexts (args
[0],
1239 "console,root,local-root,network-root",
1243 char *osbundlerequired
= grub_strdup (args
[1]), *ptr
;
1245 if (! osbundlerequired
)
1246 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
1247 "couldn't allocate string temporary space");
1248 for (ptr
= osbundlerequired
; *ptr
; ptr
++)
1249 *ptr
= grub_tolower (*ptr
);
1250 err
= grub_xnu_scan_dir_for_kexts (args
[0], osbundlerequired
, 10);
1251 grub_free (osbundlerequired
);
1259 if (c
>= '0' && c
<= '9')
1261 if (c
>= 'a' && c
<= 'z')
1262 return c
- 'a' + 10;
1263 if (c
>= 'A' && c
<= 'Z')
1264 return c
- 'A' + 10;
1269 unescape (char *name
, char *curdot
, char *nextdot
, int *len
)
1273 for (ptr
= curdot
; ptr
< nextdot
;)
1274 if (ptr
+ 2 < nextdot
&& *ptr
== '%')
1276 *dptr
= (hextoval (ptr
[1]) << 4) | (hextoval (ptr
[2]));
1290 grub_xnu_fill_devicetree (void)
1292 auto int iterate_env (struct grub_env_var
*var
);
1293 int iterate_env (struct grub_env_var
*var
)
1295 char *nextdot
= 0, *curdot
;
1296 struct grub_xnu_devtree_key
**curkey
= &grub_xnu_devtree_root
;
1297 struct grub_xnu_devtree_key
*curvalue
;
1298 char *name
= 0, *data
;
1301 if (grub_memcmp (var
->name
, "XNU.DeviceTree.",
1302 sizeof ("XNU.DeviceTree.") - 1) != 0)
1305 curdot
= var
->name
+ sizeof ("XNU.DeviceTree.") - 1;
1306 nextdot
= grub_strchr (curdot
, '.');
1311 name
= grub_realloc (name
, nextdot
- curdot
+ 1);
1316 unescape (name
, curdot
, nextdot
, &len
);
1319 curkey
= &(grub_xnu_create_key (curkey
, name
)->first_child
);
1322 nextdot
= grub_strchr (nextdot
, '.');
1327 nextdot
= curdot
+ grub_strlen (curdot
) + 1;
1329 name
= grub_realloc (name
, nextdot
- curdot
+ 1);
1334 unescape (name
, curdot
, nextdot
, &len
);
1337 curvalue
= grub_xnu_create_value (curkey
, name
);
1340 data
= grub_malloc (grub_strlen (var
->value
) + 1);
1344 unescape (data
, var
->value
, var
->value
+ grub_strlen (var
->value
),
1346 curvalue
->datasize
= len
;
1347 curvalue
->data
= data
;
1352 grub_env_iterate (iterate_env
);
1357 struct grub_video_bitmap
*grub_xnu_bitmap
= 0;
1358 grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode
;
1360 /* Option array indices. */
1361 #define XNU_SPLASH_CMD_ARGINDEX_MODE 0
1363 static const struct grub_arg_option xnu_splash_cmd_options
[] =
1365 {"mode", 'm', 0, "Background image mode.", "stretch|normal",
1371 grub_cmd_xnu_splash (grub_extcmd_t cmd
,
1372 int argc
, char *args
[])
1376 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
1378 if (cmd
->state
[XNU_SPLASH_CMD_ARGINDEX_MODE
].set
&&
1379 grub_strcmp (cmd
->state
[XNU_SPLASH_CMD_ARGINDEX_MODE
].arg
,
1381 grub_xnu_bitmap_mode
= GRUB_XNU_BITMAP_STRETCH
;
1383 grub_xnu_bitmap_mode
= GRUB_XNU_BITMAP_CENTER
;
1385 err
= grub_video_bitmap_load (&grub_xnu_bitmap
, args
[0]);
1387 grub_xnu_bitmap
= 0;
1395 grub_cmd_xnu_resume (grub_command_t cmd
__attribute__ ((unused
)),
1396 int argc
, char *args
[])
1399 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
1401 return grub_xnu_resume (args
[0]);
1409 grub_dl_ref (my_mod
);
1417 grub_dl_unref (my_mod
);
1421 static grub_command_t cmd_kernel64
, cmd_kernel
, cmd_mkext
, cmd_kext
;
1422 static grub_command_t cmd_kextdir
, cmd_ramdisk
, cmd_resume
;
1423 static grub_extcmd_t cmd_splash
;
1427 cmd_kernel
= grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel
, 0,
1428 N_("Load XNU image."));
1429 cmd_kernel64
= grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64
,
1430 0, N_("Load 64-bit XNU image."));
1431 cmd_mkext
= grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext
, 0,
1432 N_("Load XNU extension package."));
1433 cmd_kext
= grub_register_command ("xnu_kext", grub_cmd_xnu_kext
, 0,
1434 N_("Load XNU extension."));
1435 cmd_kextdir
= grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir
,
1436 N_("DIRECTORY [OSBundleRequired]"),
1437 N_("Load XNU extension directory."));
1438 cmd_ramdisk
= grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk
, 0,
1439 "Load XNU ramdisk. "
1440 "It will be seen as md0.");
1441 cmd_splash
= grub_register_extcmd ("xnu_splash",
1442 grub_cmd_xnu_splash
,
1443 GRUB_COMMAND_FLAG_BOTH
, 0,
1444 N_("Load a splash image for XNU."),
1445 xnu_splash_cmd_options
);
1448 cmd_resume
= grub_register_command ("xnu_resume", grub_cmd_xnu_resume
,
1449 0, N_("Load XNU hibernate image."));
1452 grub_cpu_xnu_init ();
1460 grub_unregister_command (cmd_resume
);
1462 grub_unregister_command (cmd_mkext
);
1463 grub_unregister_command (cmd_kext
);
1464 grub_unregister_command (cmd_kextdir
);
1465 grub_unregister_command (cmd_ramdisk
);
1466 grub_unregister_command (cmd_kernel
);
1467 grub_unregister_extcmd (cmd_splash
);
1468 grub_unregister_command (cmd_kernel64
);
1470 grub_cpu_xnu_fini ();