Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / loader / xnu.c
blobed3fc72a739bec6f4ea6ee7314572c08a2607673
1 /* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the
2 time he spent testing this
3 */
4 /*
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>
23 #include <grub/xnu.h>
24 #include <grub/cpu/xnu.h>
25 #include <grub/mm.h>
26 #include <grub/dl.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>
34 #include <grub/env.h>
35 #include <grub/i18n.h>
37 GRUB_MOD_LICENSE ("GPLv3+");
39 #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
40 #include <grub/autoefi.h>
41 #endif
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;
53 static grub_err_t
54 grub_xnu_register_memory (const char *prefix, int *suffix,
55 grub_addr_t addr, grub_size_t size);
56 grub_err_t
57 grub_xnu_heap_malloc (int size, void **src, grub_addr_t *target)
59 grub_err_t err;
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);
65 if (err)
66 return err;
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);
72 return GRUB_ERR_NONE;
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. */
78 grub_err_t
79 grub_xnu_align_heap (int align)
81 grub_xnu_heap_size
82 = ALIGN_UP (grub_xnu_heap_target_start+ grub_xnu_heap_size, align)
83 - grub_xnu_heap_target_start;
84 return GRUB_ERR_NONE;
87 /* Free subtree pointed by CUR. */
88 void
89 grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
91 struct grub_xnu_devtree_key *d;
92 while (cur)
94 grub_free (cur->name);
95 if (cur->datasize == -1)
96 grub_xnu_free_devtree (cur->first_child);
97 else if (cur->data)
98 grub_free (cur->data);
99 d = cur->next;
100 grub_free (cur);
101 cur = d;
105 /* Compute the size of device tree in xnu format. */
106 static grub_size_t
107 grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start,
108 const char *name)
110 grub_size_t ret;
111 struct grub_xnu_devtree_key *cur;
113 /* Key header. */
114 ret = 2 * sizeof (grub_uint32_t);
116 /* "name" value. */
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)
124 int align_overhead;
126 align_overhead = 4 - (cur->datasize % 4);
127 if (align_overhead == 4)
128 align_overhead = 0;
129 ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
131 else
132 ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
133 return ret;
136 /* Write devtree in XNU format at curptr assuming the head is named NAME.*/
137 static void *
138 grub_xnu_writetree_toheap_real (void *curptr,
139 struct grub_xnu_devtree_key *start,
140 const char *name)
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)
147 nkeys++;
148 else
149 nvals++;
151 /* For the name. */
152 nvals++;
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)
174 int align_overhead;
176 align_overhead = 4 - (cur->datasize % 4);
177 if (align_overhead == 4)
178 align_overhead = 0;
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,
195 cur->first_child,
196 cur->name);
197 if (!curptr)
198 return 0;
200 return curptr;
203 grub_err_t
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;
210 grub_err_t err;
211 void *src;
213 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
214 if (err)
215 return err;
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");
220 if (! chosen)
221 return grub_errno;
222 memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
223 if (! memorymap)
224 return grub_errno;
226 driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
227 if (! driverkey)
228 return grub_errno;
229 driverkey->name = grub_strdup ("DeviceTree");
230 if (! driverkey->name)
231 return grub_errno;
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)
238 return grub_errno;
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),
243 &src, target);
244 if (err)
245 return err;
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)
263 return cur;
264 return 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);
272 if (ret)
273 return ret;
274 ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
275 if (! ret)
276 return 0;
277 ret->name = grub_strdup (name);
278 if (! ret->name)
280 grub_free (ret);
281 return 0;
283 ret->datasize = -1;
284 ret->next = *parent;
285 *parent = ret;
286 return ret;
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);
294 if (ret)
296 if (ret->datasize == -1)
297 grub_xnu_free_devtree (ret->first_child);
298 else if (ret->datasize)
299 grub_free (ret->data);
300 ret->datasize = 0;
301 ret->data = 0;
302 return ret;
304 ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
305 if (! ret)
306 return 0;
307 ret->name = grub_strdup (name);
308 if (! ret->name)
310 grub_free (ret);
311 return 0;
313 ret->next = *parent;
314 *parent = ret;
315 return ret;
318 static grub_err_t
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. */
327 driversnum = 0;
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;
333 grub_xnu_unlock ();
334 return GRUB_ERR_NONE;
337 static grub_err_t
338 grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
339 int argc, char *args[])
341 grub_err_t err;
342 grub_macho_t macho;
343 grub_uint32_t startcode, endcode;
344 int i;
345 char *ptr;
346 void *loadaddr;
347 grub_addr_t loadaddr_target;
349 if (argc < 1)
350 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
352 grub_xnu_unload ();
354 macho = grub_macho_open (args[0], 0);
355 if (! macho)
356 return grub_errno;
358 err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
359 args[0]);
360 if (err)
362 grub_macho_close (macho);
363 grub_xnu_unload ();
364 return err;
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)
372 return grub_errno;
373 grub_xnu_heap_target_start = startcode;
374 err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr,
375 &loadaddr_target);
377 if (err)
379 grub_macho_close (macho);
380 grub_xnu_unload ();
381 return err;
384 /* Load kernel. */
385 err = grub_macho_load32 (macho, args[0], (char *) loadaddr - startcode,
386 GRUB_MACHO_NOBSS, &grub_xnu_darwin_version);
387 if (err)
389 grub_macho_close (macho);
390 grub_xnu_unload ();
391 return err;
394 grub_xnu_entry_point = grub_macho_get_entry_point32 (macho, args[0]);
395 if (! grub_xnu_entry_point)
397 grub_macho_close (macho);
398 grub_xnu_unload ();
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);
405 if (err)
407 grub_xnu_unload ();
408 return err;
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))
417 break;
418 grub_memcpy (ptr, args[i], grub_strlen (args[i]));
419 ptr += grub_strlen (args[i]);
420 *ptr = ' ';
421 ptr++;
424 /* Replace last space by '\0'. */
425 if (ptr != grub_xnu_cmdline)
426 *(ptr - 1) = 0;
428 #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
429 err = grub_efiemu_autocore ();
430 if (err)
431 return err;
432 #endif
434 grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
436 grub_xnu_lock ();
437 grub_xnu_is_64bit = 0;
439 return 0;
442 static grub_err_t
443 grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)),
444 int argc, char *args[])
446 grub_err_t err;
447 grub_macho_t macho;
448 grub_uint64_t startcode, endcode;
449 int i;
450 char *ptr;
451 void *loadaddr;
452 grub_addr_t loadaddr_target;
454 if (argc < 1)
455 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
457 grub_xnu_unload ();
459 macho = grub_macho_open (args[0], 1);
460 if (! macho)
461 return grub_errno;
463 err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
464 args[0]);
465 if (err)
467 grub_macho_close (macho);
468 grub_xnu_unload ();
469 return err;
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)
480 return grub_errno;
481 grub_xnu_heap_target_start = startcode;
482 err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr,
483 &loadaddr_target);
485 if (err)
487 grub_macho_close (macho);
488 grub_xnu_unload ();
489 return err;
492 /* Load kernel. */
493 err = grub_macho_load64 (macho, args[0], (char *) loadaddr - startcode,
494 GRUB_MACHO_NOBSS, &grub_xnu_darwin_version);
495 if (err)
497 grub_macho_close (macho);
498 grub_xnu_unload ();
499 return err;
502 grub_xnu_entry_point = grub_macho_get_entry_point64 (macho, args[0])
503 & 0x0fffffff;
504 if (! grub_xnu_entry_point)
506 grub_macho_close (macho);
507 grub_xnu_unload ();
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);
514 if (err)
516 grub_xnu_unload ();
517 return err;
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))
526 break;
527 grub_memcpy (ptr, args[i], grub_strlen (args[i]));
528 ptr += grub_strlen (args[i]);
529 *ptr = ' ';
530 ptr++;
533 /* Replace last space by '\0'. */
534 if (ptr != grub_xnu_cmdline)
535 *(ptr - 1) = 0;
537 #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
538 err = grub_efiemu_autocore ();
539 if (err)
540 return err;
541 #endif
543 grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
545 grub_xnu_lock ();
546 grub_xnu_is_64bit = 1;
548 return 0;
551 /* Register a memory in a memory map under name PREFIXSUFFIX
552 and increment SUFFIX. */
553 static grub_err_t
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");
566 if (! chosen)
567 return grub_errno;
568 memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
569 if (! memorymap)
570 return grub_errno;
572 driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
573 if (! driverkey)
574 return grub_errno;
575 if (suffix)
576 driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++);
577 else
578 driverkey->name = grub_strdup (prefix);
579 if (!driverkey->name)
581 grub_free (driverkey);
582 return grub_errno;
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);
592 return grub_errno;
594 memorymap->first_child = driverkey;
595 extdesc->addr = addr;
596 extdesc->size = (grub_uint32_t) size;
597 return GRUB_ERR_NONE;
600 static inline char *
601 get_name_ptr (char *name)
603 char *p = name, *p2;
604 /* Skip Info.plist. */
605 p2 = grub_strrchr (p, '/');
606 if (!p2)
607 return name;
608 if (p2 == name)
609 return name + 1;
610 p = p2 - 1;
612 p2 = grub_strrchr (p, '/');
613 if (!p2)
614 return name;
615 if (p2 == name)
616 return name + 1;
617 if (grub_memcmp (p2, "/Contents/", sizeof ("/Contents/") - 1) != 0)
618 return p2 + 1;
620 p = p2 - 1;
622 p2 = grub_strrchr (p, '/');
623 if (!p2)
624 return name;
625 return p2 + 1;
628 /* Load .kext. */
629 static grub_err_t
630 grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile,
631 const char *filename)
633 grub_macho_t macho;
634 grub_err_t err;
635 grub_file_t infoplist;
636 struct grub_xnu_extheader *exthead;
637 int neededspace = sizeof (*exthead);
638 grub_uint8_t *buf;
639 void *buf0;
640 grub_addr_t buf_target;
641 grub_size_t infoplistsize = 0, machosize = 0;
642 char *name, *nameend;
643 int namelen;
645 name = get_name_ptr (infoplistname);
646 nameend = grub_strchr (name, '/');
648 if (nameend)
649 namelen = nameend - name;
650 else
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. */
659 if (binaryfile)
661 macho = grub_macho_file (binaryfile, filename, grub_xnu_is_64bit);
662 if (!macho)
663 grub_file_close (binaryfile);
664 else
666 if (grub_xnu_is_64bit)
667 machosize = grub_macho_filesize64 (macho);
668 else
669 machosize = grub_macho_filesize32 (macho);
671 neededspace += machosize;
673 else
674 macho = 0;
676 if (infoplistname)
677 infoplist = grub_file_open (infoplistname);
678 else
679 infoplist = 0;
680 grub_errno = GRUB_ERR_NONE;
681 if (infoplist)
683 infoplistsize = grub_file_size (infoplist);
684 neededspace += infoplistsize + 1;
686 else
687 infoplistsize = 0;
689 /* Allocate the space. */
690 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
691 if (err)
692 return err;
693 err = grub_xnu_heap_malloc (neededspace, &buf0, &buf_target);
694 if (err)
695 return err;
696 buf = buf0;
698 exthead = (struct grub_xnu_extheader *) buf;
699 grub_memset (exthead, 0, sizeof (*exthead));
700 buf += sizeof (*exthead);
702 /* Load the binary. */
703 if (macho)
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);
709 else
710 err = grub_macho_readfile32 (macho, filename, buf);
711 if (err)
713 grub_macho_close (macho);
714 return err;
716 grub_macho_close (macho);
717 buf += machosize;
719 grub_errno = GRUB_ERR_NONE;
721 /* Load the plist. */
722 if (infoplist)
724 exthead->infoplistaddr = buf_target + (buf - (grub_uint8_t *) buf0);
725 exthead->infoplistsize = infoplistsize + 1;
726 if (grub_file_read (infoplist, buf, infoplistsize)
727 != (grub_ssize_t) (infoplistsize))
729 grub_file_close (infoplist);
730 if (!grub_errno)
731 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
732 infoplistname);
733 return grub_errno;
735 grub_file_close (infoplist);
736 buf[infoplistsize] = 0;
737 buf += infoplistsize + 1;
739 grub_errno = GRUB_ERR_NONE;
741 exthead->nameaddr = (buf - (grub_uint8_t *) buf0) + buf_target;
742 exthead->namesize = namelen + 1;
743 grub_memcpy (buf, name, namelen);
744 buf[namelen] = 0;
745 buf += namelen + 1;
747 /* Announce to kernel */
748 return grub_xnu_register_memory ("Driver-", &driversnum, buf_target,
749 neededspace);
752 /* Load mkext. */
753 static grub_err_t
754 grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
755 int argc, char *args[])
757 grub_file_t file;
758 void *loadto;
759 grub_addr_t loadto_target;
760 grub_err_t err;
761 grub_off_t readoff = 0;
762 grub_ssize_t readlen = -1;
763 struct grub_macho_fat_header head;
764 struct grub_macho_fat_arch *archs;
765 int narchs, i;
767 if (argc != 1)
768 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
770 if (! grub_xnu_heap_size)
771 return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
773 file = grub_file_open (args[0]);
774 if (! file)
775 return grub_errno;
777 /* Sometimes caches are fat binary. Errgh. */
778 if (grub_file_read (file, &head, sizeof (head))
779 != (grub_ssize_t) (sizeof (head)))
781 /* I don't know the internal structure of package but
782 can hardly imagine a valid package shorter than 20 bytes. */
783 grub_file_close (file);
784 if (!grub_errno)
785 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
786 return grub_errno;
789 /* Find the corresponding architecture. */
790 if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
792 narchs = grub_be_to_cpu32 (head.nfat_arch);
793 archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
794 if (! archs)
796 grub_file_close (file);
797 return grub_errno;
800 if (grub_file_read (file, archs,
801 sizeof (struct grub_macho_fat_arch) * narchs)
802 != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
804 grub_free (archs);
805 if (!grub_errno)
806 grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file %s"),
807 args[0]);
808 return grub_errno;
810 for (i = 0; i < narchs; i++)
812 if (!grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST32
813 (grub_be_to_cpu32 (archs[i].cputype)))
815 readoff = grub_be_to_cpu32 (archs[i].offset);
816 readlen = grub_be_to_cpu32 (archs[i].size);
818 if (grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64
819 (grub_be_to_cpu32 (archs[i].cputype)))
821 readoff = grub_be_to_cpu32 (archs[i].offset);
822 readlen = grub_be_to_cpu32 (archs[i].size);
825 grub_free (archs);
827 else
829 /* It's a flat file. Some sane people still exist. */
830 readoff = 0;
831 readlen = grub_file_size (file);
834 if (readlen == -1)
836 grub_file_close (file);
837 return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
840 /* Allocate space. */
841 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
842 if (err)
844 grub_file_close (file);
845 return err;
848 err = grub_xnu_heap_malloc (readlen, &loadto, &loadto_target);
849 if (err)
851 grub_file_close (file);
852 return err;
855 /* Read the file. */
856 grub_file_seek (file, readoff);
857 if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
859 grub_file_close (file);
860 if (!grub_errno)
861 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
862 return grub_errno;
864 grub_file_close (file);
866 /* Pass it to kernel. */
867 return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
868 loadto_target, readlen);
871 static grub_err_t
872 grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
873 int argc, char *args[])
875 grub_file_t file;
876 void *loadto;
877 grub_addr_t loadto_target;
878 grub_err_t err;
879 grub_size_t size;
881 if (argc != 1)
882 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
884 if (! grub_xnu_heap_size)
885 return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
887 file = grub_file_open (args[0]);
888 if (! file)
889 return grub_errno;
891 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
892 if (err)
893 return err;
895 size = grub_file_size (file);
897 err = grub_xnu_heap_malloc (size, &loadto, &loadto_target);
898 if (err)
899 return err;
900 if (grub_file_read (file, loadto, size) != (grub_ssize_t) (size))
902 grub_file_close (file);
903 if (!grub_errno)
904 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
905 return grub_errno;
907 return grub_xnu_register_memory ("RAMDisk", 0, loadto_target, size);
910 /* Returns true if the kext should be loaded according to plist
911 and osbundlereq. Also fill BINNAME. */
912 static int
913 grub_xnu_check_os_bundle_required (char *plistname,
914 const char *osbundlereq,
915 char **binname)
917 grub_file_t file;
918 char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
919 char *stringptr = 0, *ptr2 = 0;
920 grub_size_t size;
921 int depth = 0;
922 int ret;
923 int osbundlekeyfound = 0, binnamekeyfound = 0;
924 if (binname)
925 *binname = 0;
927 file = grub_file_open (plistname);
928 if (! file)
929 return 0;
931 size = grub_file_size (file);
932 buf = grub_malloc (size);
933 if (! buf)
935 grub_file_close (file);
936 return 0;
938 if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
940 grub_file_close (file);
941 if (!grub_errno)
942 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), plistname);
943 return 0;
945 grub_file_close (file);
947 /* Set the return value for the case when no OSBundleRequired tag is found. */
948 if (osbundlereq)
949 ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
950 else
951 ret = 1;
953 /* Parse plist. It's quite dirty and inextensible but does its job. */
954 for (ptr1 = buf; ptr1 < buf + size; ptr1++)
955 switch (*ptr1)
957 case '<':
958 tagstart = ptr1;
959 *ptr1 = 0;
960 if (keyptr && depth == 4
961 && grub_strcmp (keyptr, "OSBundleRequired") == 0)
962 osbundlekeyfound = 1;
963 if (keyptr && depth == 4 &&
964 grub_strcmp (keyptr, "CFBundleExecutable") == 0)
965 binnamekeyfound = 1;
966 if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
968 for (ptr2 = stringptr; *ptr2; ptr2++)
969 *ptr2 = grub_tolower (*ptr2);
970 ret = grub_strword (osbundlereq, stringptr)
971 || grub_strword (osbundlereq, "all");
973 if (stringptr && binnamekeyfound && binname && depth == 4)
975 if (*binname)
976 grub_free (*binname);
977 *binname = grub_strdup (stringptr);
980 *ptr1 = '<';
981 keyptr = 0;
982 stringptr = 0;
983 break;
984 case '>':
985 if (! tagstart)
987 grub_free (buf);
988 grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
989 return 0;
991 *ptr1 = 0;
992 if (tagstart[1] == '?' || ptr1[-1] == '/')
994 osbundlekeyfound = 0;
995 *ptr1 = '>';
996 break;
998 if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
999 keyptr = ptr1 + 1;
1000 if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
1001 stringptr = ptr1 + 1;
1002 else if (grub_strcmp (tagstart + 1, "/key") != 0)
1004 osbundlekeyfound = 0;
1005 binnamekeyfound = 0;
1007 *ptr1 = '>';
1009 if (tagstart[1] == '/')
1010 depth--;
1011 else
1012 depth++;
1013 break;
1015 grub_free (buf);
1017 return ret;
1020 /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
1021 grub_err_t
1022 grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired,
1023 int maxrecursion)
1025 grub_device_t dev;
1026 char *device_name;
1027 grub_fs_t fs;
1028 const char *path;
1030 auto int load_hook (const char *filename,
1031 const struct grub_dirhook_info *info);
1032 int load_hook (const char *filename, const struct grub_dirhook_info *info)
1034 char *newdirname;
1035 if (! info->dir)
1036 return 0;
1037 if (filename[0] == '.')
1038 return 0;
1040 if (grub_strlen (filename) < 5 ||
1041 grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
1042 return 0;
1044 newdirname
1045 = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
1047 /* It's a .kext. Try to load it. */
1048 if (newdirname)
1050 grub_strcpy (newdirname, dirname);
1051 newdirname[grub_strlen (newdirname) + 1] = 0;
1052 newdirname[grub_strlen (newdirname)] = '/';
1053 grub_strcpy (newdirname + grub_strlen (newdirname), filename);
1054 grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
1055 maxrecursion);
1056 if (grub_errno == GRUB_ERR_BAD_OS)
1057 grub_errno = GRUB_ERR_NONE;
1058 grub_free (newdirname);
1060 return 0;
1063 if (! grub_xnu_heap_size)
1064 return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
1066 device_name = grub_file_get_device_name (dirname);
1067 dev = grub_device_open (device_name);
1068 if (dev)
1070 fs = grub_fs_probe (dev);
1071 path = grub_strchr (dirname, ')');
1072 if (! path)
1073 path = dirname;
1074 else
1075 path++;
1077 if (fs)
1078 (fs->dir) (dev, path, load_hook);
1079 grub_device_close (dev);
1081 grub_free (device_name);
1083 return GRUB_ERR_NONE;
1086 /* Load extension DIRNAME. (extensions are directories in xnu) */
1087 grub_err_t
1088 grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired,
1089 int maxrecursion)
1091 grub_device_t dev;
1092 char *plistname = 0;
1093 char *newdirname;
1094 char *newpath;
1095 char *device_name;
1096 grub_fs_t fs;
1097 const char *path;
1098 char *binsuffix;
1099 int usemacos = 0;
1100 grub_file_t binfile;
1102 auto int load_hook (const char *filename,
1103 const struct grub_dirhook_info *info);
1105 int load_hook (const char *filename, const struct grub_dirhook_info *info)
1107 if (grub_strlen (filename) > 15)
1108 return 0;
1109 grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
1111 /* If the kext contains directory "Contents" all real stuff is in
1112 this directory. */
1113 if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
1114 grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
1115 maxrecursion - 1);
1117 /* Directory "Plugins" contains nested kexts. */
1118 if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
1119 grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
1120 maxrecursion - 1);
1122 /* Directory "MacOS" contains executable, otherwise executable is
1123 on the top. */
1124 if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
1125 usemacos = 1;
1127 /* Info.plist is the file which governs our future actions. */
1128 if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
1129 && ! plistname)
1130 plistname = grub_strdup (newdirname);
1131 return 0;
1134 newdirname = grub_malloc (grub_strlen (dirname) + 20);
1135 if (! newdirname)
1136 return grub_errno;
1137 grub_strcpy (newdirname, dirname);
1138 newdirname[grub_strlen (dirname)] = '/';
1139 newdirname[grub_strlen (dirname) + 1] = 0;
1140 device_name = grub_file_get_device_name (dirname);
1141 dev = grub_device_open (device_name);
1142 if (dev)
1144 fs = grub_fs_probe (dev);
1145 path = grub_strchr (dirname, ')');
1146 if (! path)
1147 path = dirname;
1148 else
1149 path++;
1151 newpath = grub_strchr (newdirname, ')');
1152 if (! newpath)
1153 newpath = newdirname;
1154 else
1155 newpath++;
1157 /* Look at the directory. */
1158 if (fs)
1159 (fs->dir) (dev, path, load_hook);
1161 if (plistname && grub_xnu_check_os_bundle_required
1162 (plistname, osbundlerequired, &binsuffix))
1164 if (binsuffix)
1166 /* Open the binary. */
1167 char *binname = grub_malloc (grub_strlen (dirname)
1168 + grub_strlen (binsuffix)
1169 + sizeof ("/MacOS/"));
1170 grub_strcpy (binname, dirname);
1171 if (usemacos)
1172 grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
1173 else
1174 grub_strcpy (binname + grub_strlen (binname), "/");
1175 grub_strcpy (binname + grub_strlen (binname), binsuffix);
1176 grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
1177 binfile = grub_file_open (binname);
1178 if (! binfile)
1179 grub_errno = GRUB_ERR_NONE;
1181 /* Load the extension. */
1182 grub_xnu_load_driver (plistname, binfile,
1183 binname);
1184 grub_free (binname);
1185 grub_free (binsuffix);
1187 else
1189 grub_dprintf ("xnu", "%s:0\n", plistname);
1190 grub_xnu_load_driver (plistname, 0, 0);
1193 grub_free (plistname);
1194 grub_device_close (dev);
1196 grub_free (device_name);
1198 return GRUB_ERR_NONE;
1202 static int locked=0;
1203 static grub_dl_t my_mod;
1205 /* Load the kext. */
1206 static grub_err_t
1207 grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
1208 int argc, char *args[])
1210 grub_file_t binfile = 0;
1212 if (! grub_xnu_heap_size)
1213 return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
1215 if (argc == 2)
1217 /* User explicitly specified plist and binary. */
1218 if (grub_strcmp (args[1], "-") != 0)
1220 binfile = grub_file_open (args[1]);
1221 if (! binfile)
1222 return grub_errno;
1224 return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
1225 binfile, args[1]);
1228 /* load kext normally. */
1229 if (argc == 1)
1230 return grub_xnu_load_kext_from_dir (args[0], 0, 10);
1232 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
1235 /* Load a directory containing kexts. */
1236 static grub_err_t
1237 grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
1238 int argc, char *args[])
1240 if (argc != 1 && argc != 2)
1241 return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
1243 if (! grub_xnu_heap_size)
1244 return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
1246 if (argc == 1)
1247 return grub_xnu_scan_dir_for_kexts (args[0],
1248 "console,root,local-root,network-root",
1249 10);
1250 else
1252 char *osbundlerequired = grub_strdup (args[1]), *ptr;
1253 grub_err_t err;
1254 if (! osbundlerequired)
1255 return grub_errno;
1256 for (ptr = osbundlerequired; *ptr; ptr++)
1257 *ptr = grub_tolower (*ptr);
1258 err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
1259 grub_free (osbundlerequired);
1260 return err;
1264 static inline int
1265 hextoval (char c)
1267 if (c >= '0' && c <= '9')
1268 return c - '0';
1269 if (c >= 'a' && c <= 'z')
1270 return c - 'a' + 10;
1271 if (c >= 'A' && c <= 'Z')
1272 return c - 'A' + 10;
1273 return 0;
1276 static inline void
1277 unescape (char *name, char *curdot, char *nextdot, int *len)
1279 char *ptr, *dptr;
1280 dptr = name;
1281 for (ptr = curdot; ptr < nextdot;)
1282 if (ptr + 2 < nextdot && *ptr == '%')
1284 *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
1285 ptr += 3;
1286 dptr++;
1288 else
1290 *dptr = *ptr;
1291 ptr++;
1292 dptr++;
1294 *len = dptr - name;
1297 grub_err_t
1298 grub_xnu_fill_devicetree (void)
1300 auto int iterate_env (struct grub_env_var *var);
1301 int iterate_env (struct grub_env_var *var)
1303 char *nextdot = 0, *curdot;
1304 struct grub_xnu_devtree_key **curkey = &grub_xnu_devtree_root;
1305 struct grub_xnu_devtree_key *curvalue;
1306 char *name = 0, *data;
1307 int len;
1309 if (grub_memcmp (var->name, "XNU.DeviceTree.",
1310 sizeof ("XNU.DeviceTree.") - 1) != 0)
1311 return 0;
1313 curdot = var->name + sizeof ("XNU.DeviceTree.") - 1;
1314 nextdot = grub_strchr (curdot, '.');
1315 if (nextdot)
1316 nextdot++;
1317 while (nextdot)
1319 name = grub_realloc (name, nextdot - curdot + 1);
1321 if (!name)
1322 return 1;
1324 unescape (name, curdot, nextdot, &len);
1325 name[len - 1] = 0;
1327 curkey = &(grub_xnu_create_key (curkey, name)->first_child);
1329 curdot = nextdot;
1330 nextdot = grub_strchr (nextdot, '.');
1331 if (nextdot)
1332 nextdot++;
1335 nextdot = curdot + grub_strlen (curdot) + 1;
1337 name = grub_realloc (name, nextdot - curdot + 1);
1339 if (!name)
1340 return 1;
1342 unescape (name, curdot, nextdot, &len);
1343 name[len] = 0;
1345 curvalue = grub_xnu_create_value (curkey, name);
1346 grub_free (name);
1348 data = grub_malloc (grub_strlen (var->value) + 1);
1349 if (!data)
1350 return 1;
1352 unescape (data, var->value, var->value + grub_strlen (var->value),
1353 &len);
1354 curvalue->datasize = len;
1355 curvalue->data = data;
1357 return 0;
1360 grub_env_iterate (iterate_env);
1362 return grub_errno;
1365 struct grub_video_bitmap *grub_xnu_bitmap = 0;
1366 grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
1368 /* Option array indices. */
1369 #define XNU_SPLASH_CMD_ARGINDEX_MODE 0
1371 static const struct grub_arg_option xnu_splash_cmd_options[] =
1373 {"mode", 'm', 0, N_("Background image mode."), N_("stretch|normal"),
1374 ARG_TYPE_STRING},
1375 {0, 0, 0, 0, 0, 0}
1378 static grub_err_t
1379 grub_cmd_xnu_splash (grub_extcmd_context_t ctxt,
1380 int argc, char *args[])
1382 grub_err_t err;
1383 if (argc != 1)
1384 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
1386 if (! grub_xnu_heap_size)
1387 return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
1389 if (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set &&
1390 grub_strcmp (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg,
1391 "stretch") == 0)
1392 grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH;
1393 else
1394 grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER;
1396 err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
1397 if (err)
1398 grub_xnu_bitmap = 0;
1400 return err;
1404 #ifndef GRUB_MACHINE_EMU
1405 static grub_err_t
1406 grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
1407 int argc, char *args[])
1409 if (argc != 1)
1410 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
1412 return grub_xnu_resume (args[0]);
1414 #endif
1416 void
1417 grub_xnu_lock (void)
1419 if (!locked)
1420 grub_dl_ref (my_mod);
1421 locked = 1;
1424 void
1425 grub_xnu_unlock (void)
1427 if (locked)
1428 grub_dl_unref (my_mod);
1429 locked = 0;
1432 static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext;
1433 static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume;
1434 static grub_extcmd_t cmd_splash;
1436 GRUB_MOD_INIT(xnu)
1438 cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
1439 N_("Load XNU image."));
1440 cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
1441 0, N_("Load 64-bit XNU image."));
1442 cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
1443 N_("Load XNU extension package."));
1444 cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
1445 N_("Load XNU extension."));
1446 cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
1447 /* TRANSLATORS: OSBundleRequired is a
1448 variable name in xnu extensions
1449 manifests. It behaves mostly like
1450 GNU/Linux runlevels.
1452 N_("DIRECTORY [OSBundleRequired]"),
1453 /* TRANSLATORS: There are many extensions
1454 in extension directory. */
1455 N_("Load XNU extension directory."));
1456 cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
1457 /* TRANSLATORS: ramdisk here isn't identifier. It can be translated. */
1458 N_("Load XNU ramdisk. "
1459 "It will be available in OS as md0."));
1460 cmd_splash = grub_register_extcmd ("xnu_splash",
1461 grub_cmd_xnu_splash, 0, 0,
1462 N_("Load a splash image for XNU."),
1463 xnu_splash_cmd_options);
1465 #ifndef GRUB_MACHINE_EMU
1466 cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
1467 0, N_("Load an image of hibernated"
1468 " XNU."));
1469 #endif
1471 grub_cpu_xnu_init ();
1473 my_mod = mod;
1476 GRUB_MOD_FINI(xnu)
1478 #ifndef GRUB_MACHINE_EMU
1479 grub_unregister_command (cmd_resume);
1480 #endif
1481 grub_unregister_command (cmd_mkext);
1482 grub_unregister_command (cmd_kext);
1483 grub_unregister_command (cmd_kextdir);
1484 grub_unregister_command (cmd_ramdisk);
1485 grub_unregister_command (cmd_kernel);
1486 grub_unregister_extcmd (cmd_splash);
1487 grub_unregister_command (cmd_kernel64);
1489 grub_cpu_xnu_fini ();