style fix
[grub2/phcoder.git] / loader / xnu.c
blobb2c6c059ea0ffa3477d0d00f88f42f26fc167274
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/gzio.h>
32 #include <grub/command.h>
33 #include <grub/misc.h>
35 struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
36 static int driverspackagenum = 0;
37 static int driversnum = 0;
39 /* Allocate heap by 32MB-blocks. */
40 #define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
42 static grub_err_t
43 grub_xnu_register_memory (char *prefix, int *suffix,
44 void *addr, grub_size_t size);
45 void *
46 grub_xnu_heap_malloc (int size)
48 void *val;
50 #if 0
51 /* This way booting is faster but less reliable.
52 Once we have advanced mm second way will be as fast as this one. */
53 val = grub_xnu_heap_start = (char *) 0x100000;
54 #else
55 int oldblknum, newblknum;
57 /* The page after the heap is used for stack. Ensure it's usable. */
58 if (grub_xnu_heap_size)
59 oldblknum = (grub_xnu_heap_size + GRUB_XNU_PAGESIZE
60 + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
61 else
62 oldblknum = 0;
63 newblknum = (grub_xnu_heap_size + size + GRUB_XNU_PAGESIZE
64 + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
65 if (oldblknum != newblknum)
66 /* FIXME: instruct realloc to allocate at 1MB if possible once
67 advanced mm is ready. */
68 val = grub_realloc (grub_xnu_heap_start,
69 newblknum * GRUB_XNU_HEAP_ALLOC_BLOCK);
70 else
71 val = grub_xnu_heap_start;
72 if (! val)
74 grub_error (GRUB_ERR_OUT_OF_MEMORY,
75 "not enough space on xnu memory heap");
76 return 0;
78 grub_xnu_heap_start = val;
79 #endif
81 val = (char *) grub_xnu_heap_start + grub_xnu_heap_size;
82 grub_xnu_heap_size += size;
83 grub_dprintf ("xnu", "val=%p\n", val);
84 return (char *) val;
87 /* Make sure next block of the heap will be aligned.
88 Please notice: aligned are pointers AFTER relocation
89 and not the current ones. */
90 grub_err_t
91 grub_xnu_align_heap (int align)
93 int align_overhead = align - grub_xnu_heap_size % align;
94 if (align_overhead == align)
95 return GRUB_ERR_NONE;
96 if (! grub_xnu_heap_malloc (align_overhead))
97 return grub_errno;
98 return GRUB_ERR_NONE;
101 /* Free subtree pointed by CUR. */
102 void
103 grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
105 struct grub_xnu_devtree_key *d;
106 while (cur)
108 grub_free (cur->name);
109 if (cur->datasize == -1)
110 grub_xnu_free_devtree (cur->first_child);
111 else if (cur->data)
112 grub_free (cur->data);
113 d = cur->next;
114 grub_free (cur);
115 cur = d;
119 /* Compute the size of device tree in xnu format. */
120 static grub_size_t
121 grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, char *name)
123 grub_size_t ret;
124 struct grub_xnu_devtree_key *cur;
126 /* Key header. */
127 ret = 2 * sizeof (grub_uint32_t);
129 /* "name" value. */
130 ret += 32 + sizeof (grub_uint32_t)
131 + grub_strlen (name) + 4
132 - (grub_strlen (name) % 4);
134 for (cur = start; cur; cur = cur->next)
135 if (cur->datasize != -1)
137 int align_overhead;
139 align_overhead = 4 - (cur->datasize % 4);
140 if (align_overhead == 4)
141 align_overhead = 0;
142 ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
144 else
145 ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
146 return ret;
149 /* Write devtree in XNU format at curptr assuming the head is named NAME.*/
150 static void *
151 grub_xnu_writetree_toheap_real (void *curptr,
152 struct grub_xnu_devtree_key *start, char *name)
154 struct grub_xnu_devtree_key *cur;
155 int nkeys = 0, nvals = 0;
156 for (cur = start; cur; cur = cur->next)
158 if (cur->datasize == -1)
159 nkeys++;
160 else
161 nvals++;
163 /* For the name. */
164 nvals++;
166 *((grub_uint32_t *) curptr) = nvals;
167 curptr = ((grub_uint32_t *) curptr) + 1;
168 *((grub_uint32_t *) curptr) = nkeys;
169 curptr = ((grub_uint32_t *) curptr) + 1;
171 /* First comes "name" value. */
172 grub_memset (curptr, 0, 32);
173 grub_memcpy (curptr, "name", 4);
174 curptr = ((grub_uint8_t *) curptr) + 32;
175 *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
176 curptr = ((grub_uint32_t *) curptr) + 1;
177 grub_memcpy (curptr, name, grub_strlen (name));
178 curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
179 grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
180 curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
182 /* Then the other values. */
183 for (cur = start; cur; cur = cur->next)
184 if (cur->datasize != -1)
186 int align_overhead;
188 align_overhead = 4 - (cur->datasize % 4);
189 if (align_overhead == 4)
190 align_overhead = 0;
191 grub_memset (curptr, 0, 32);
192 grub_strncpy (curptr, cur->name, 31);
193 curptr = ((grub_uint8_t *) curptr) + 32;
194 *((grub_uint32_t *) curptr) = cur->datasize;
195 curptr = ((grub_uint32_t *) curptr) + 1;
196 grub_memcpy (curptr, cur->data, cur->datasize);
197 curptr = ((grub_uint8_t *) curptr) + cur->datasize;
198 grub_memset (curptr, 0, align_overhead);
199 curptr = ((grub_uint8_t *) curptr) + align_overhead;
202 /* And then the keys. Recursively use this function. */
203 for (cur = start; cur; cur = cur->next)
204 if (cur->datasize == -1)
205 if (!(curptr = grub_xnu_writetree_toheap_real (curptr,
206 cur->first_child,
207 cur->name)))
208 return 0;
209 return curptr;
212 grub_err_t
213 grub_xnu_writetree_toheap (void **start, grub_size_t *size)
215 struct grub_xnu_devtree_key *chosen;
216 struct grub_xnu_devtree_key *memorymap;
217 struct grub_xnu_devtree_key *driverkey;
218 struct grub_xnu_extdesc *extdesc;
219 grub_err_t err;
221 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
222 if (err)
223 return err;
225 /* Device tree itself is in the memory map of device tree. */
226 /* Create a dummy value in memory-map. */
227 chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
228 if (! chosen)
229 return grub_errno;
230 memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
231 if (! memorymap)
232 return grub_errno;
234 driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
235 if (! driverkey)
236 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
237 driverkey->name = grub_strdup ("DeviceTree");
238 if (! driverkey->name)
239 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
240 driverkey->datasize = sizeof (*extdesc);
241 driverkey->next = memorymap->first_child;
242 memorymap->first_child = driverkey;
243 driverkey->data = extdesc
244 = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
245 if (! driverkey->data)
246 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
248 /* Allocate the space based on the size with dummy value. */
249 *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
250 *start = grub_xnu_heap_malloc (*size + GRUB_XNU_PAGESIZE
251 - *size % GRUB_XNU_PAGESIZE);
253 /* Put real data in the dummy. */
254 extdesc->addr = (char *) *start - grub_xnu_heap_start
255 + grub_xnu_heap_will_be_at;
256 extdesc->size = (grub_uint32_t) *size;
258 /* Write the tree to heap. */
259 grub_xnu_writetree_toheap_real (*start, grub_xnu_devtree_root, "/");
260 return GRUB_ERR_NONE;
263 /* Find a key or value in parent key. */
264 struct grub_xnu_devtree_key *
265 grub_xnu_find_key (struct grub_xnu_devtree_key *parent, char *name)
267 struct grub_xnu_devtree_key *cur;
268 for (cur = parent; cur; cur = cur->next)
269 if (grub_strcmp (cur->name, name) == 0)
270 return cur;
271 return 0;
274 struct grub_xnu_devtree_key *
275 grub_xnu_create_key (struct grub_xnu_devtree_key **parent, char *name)
277 struct grub_xnu_devtree_key *ret;
278 ret = grub_xnu_find_key (*parent, name);
279 if (ret)
280 return ret;
281 ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
282 if (! ret)
284 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
285 return 0;
287 ret->name = grub_strdup (name);
288 if (! ret->name)
290 grub_free (ret);
291 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
292 return 0;
294 ret->datasize = -1;
295 ret->first_child = 0;
296 ret->next = *parent;
297 *parent = ret;
298 return ret;
301 struct grub_xnu_devtree_key *
302 grub_xnu_create_value (struct grub_xnu_devtree_key **parent, char *name)
304 struct grub_xnu_devtree_key *ret;
305 ret = grub_xnu_find_key (*parent, name);
306 if (ret)
308 if (ret->datasize == -1)
309 grub_xnu_free_devtree (ret->first_child);
310 else if (ret->datasize)
311 grub_free (ret->data);
312 ret->datasize = 0;
313 ret->data = 0;
314 return ret;
316 ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
317 if (! ret)
319 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
320 return 0;
322 ret->name = grub_strdup (name);
323 if (! ret->name)
325 grub_free (ret);
326 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
327 return 0;
329 ret->datasize = 0;
330 ret->data = 0;
331 ret->next = *parent;
332 *parent = ret;
333 return ret;
336 static grub_err_t
337 grub_xnu_unload (void)
339 grub_xnu_free_devtree (grub_xnu_devtree_root);
340 grub_xnu_devtree_root = 0;
342 /* Free loaded image. */
343 driversnum = 0;
344 driverspackagenum = 0;
345 grub_free (grub_xnu_heap_start);
346 grub_xnu_heap_start = 0;
347 grub_xnu_heap_size = 0;
348 grub_xnu_unlock ();
349 return GRUB_ERR_NONE;
352 static grub_err_t
353 grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
354 int argc, char *args[])
356 grub_err_t err;
357 grub_macho_t macho;
358 grub_addr_t startcode, endcode;
359 int i;
360 char *ptr, *loadaddr;
362 if (argc < 1)
363 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
365 grub_xnu_unload ();
367 macho = grub_macho_open (args[0]);
368 if (! macho)
369 return grub_errno;
370 if (! grub_macho_contains_macho32 (macho))
372 grub_macho_close (macho);
373 return grub_error (GRUB_ERR_BAD_OS,
374 "Kernel doesn't contain suitable architecture");
377 err = grub_macho32_size (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
378 if (err)
380 grub_macho_close (macho);
381 grub_xnu_unload ();
382 return err;
385 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
386 (unsigned long) endcode, (unsigned long) startcode);
388 loadaddr = grub_xnu_heap_malloc (endcode - startcode);
389 grub_xnu_heap_will_be_at = startcode;
391 if (! loadaddr)
393 grub_macho_close (macho);
394 grub_xnu_unload ();
395 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
396 "not enough memory to load kernel");
399 /* Load kernel. */
400 err = grub_macho32_load (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
401 if (err)
403 grub_macho_close (macho);
404 grub_xnu_unload ();
405 return err;
408 grub_xnu_entry_point = grub_macho32_get_entry_point (macho);
409 if (! grub_xnu_entry_point)
411 grub_macho_close (macho);
412 grub_xnu_unload ();
413 return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
416 grub_macho_close (macho);
418 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
419 if (err)
421 grub_xnu_unload ();
422 return err;
425 /* Copy parameters to kernel command line. */
426 ptr = grub_xnu_cmdline;
427 for (i = 1; i < argc; i++)
429 if (ptr + grub_strlen (args[i]) + 1
430 >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
431 break;
432 grub_memcpy (ptr, args[i], grub_strlen (args[i]));
433 ptr += grub_strlen (args[i]);
434 *ptr = ' ';
435 ptr++;
438 /* Replace last space by '\0'. */
439 if (ptr != grub_xnu_cmdline)
440 *(ptr - 1) = 0;
442 err = grub_cpu_xnu_fill_devicetree ();
443 if (err)
444 return err;
446 grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
448 grub_xnu_lock ();
449 return 0;
452 /* Register a memory in a memory map under name PREFIXSUFFIX
453 and increment SUFFIX. */
454 static grub_err_t
455 grub_xnu_register_memory (char *prefix, int *suffix,
456 void *addr, grub_size_t size)
458 struct grub_xnu_devtree_key *chosen;
459 struct grub_xnu_devtree_key *memorymap;
460 struct grub_xnu_devtree_key *driverkey;
461 struct grub_xnu_extdesc *extdesc;
463 if (! grub_xnu_heap_size)
464 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
466 chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
467 if (! chosen)
468 return grub_errno;
469 memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
470 if (! memorymap)
471 return grub_errno;
473 driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
474 if (! driverkey)
475 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
476 if (suffix)
478 driverkey->name = grub_malloc (grub_strlen (prefix) + 10);
479 if (!driverkey->name)
480 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
481 grub_sprintf (driverkey->name, "%s%d", prefix, (*suffix)++);
483 else
484 driverkey->name = grub_strdup (prefix);
485 if (! driverkey->name)
486 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
487 driverkey->datasize = sizeof (*extdesc);
488 driverkey->next = memorymap->first_child;
489 memorymap->first_child = driverkey;
490 driverkey->data = extdesc
491 = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
492 if (! driverkey->data)
493 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
494 extdesc->addr = grub_xnu_heap_will_be_at +
495 ((grub_uint8_t *) addr - (grub_uint8_t *) grub_xnu_heap_start);
496 extdesc->size = (grub_uint32_t) size;
497 return GRUB_ERR_NONE;
500 /* Load .kext. */
501 static grub_err_t
502 grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
504 grub_macho_t macho;
505 grub_err_t err;
506 grub_file_t infoplist;
507 struct grub_xnu_extheader *exthead;
508 int neededspace = sizeof (*exthead);
509 char *buf;
510 grub_size_t infoplistsize = 0, machosize = 0;
512 if (! grub_xnu_heap_size)
513 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
515 /* Compute the needed space. */
516 if (binaryfile)
518 macho = grub_macho_file (binaryfile);
519 if (! macho || ! grub_macho_contains_macho32 (macho))
521 if (macho)
522 grub_macho_close (macho);
523 return grub_error (GRUB_ERR_BAD_OS,
524 "Extension doesn't contain suitable architecture");
526 machosize = grub_macho32_filesize (macho);
527 neededspace += machosize;
529 else
530 macho = 0;
532 if (infoplistname)
533 infoplist = grub_gzfile_open (infoplistname, 1);
534 else
535 infoplist = 0;
536 grub_errno = GRUB_ERR_NONE;
537 if (infoplist)
539 infoplistsize = grub_file_size (infoplist);
540 neededspace += infoplistsize + 1;
542 else
543 infoplistsize = 0;
545 /* Allocate the space. */
546 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
547 if (err)
548 return err;
549 buf = grub_xnu_heap_malloc (neededspace);
551 exthead = (struct grub_xnu_extheader *) buf;
552 grub_memset (exthead, 0, sizeof (*exthead));
553 buf += sizeof (*exthead);
555 /* Load the binary. */
556 if (macho)
558 exthead->binaryaddr = (buf - grub_xnu_heap_start)
559 + grub_xnu_heap_will_be_at;
560 exthead->binarysize = machosize;
561 if ((err = grub_macho32_readfile (macho, buf)))
563 grub_macho_close (macho);
564 return err;
566 grub_macho_close (macho);
567 buf += machosize;
569 grub_errno = GRUB_ERR_NONE;
571 /* Load the plist. */
572 if (infoplist)
574 exthead->infoplistaddr = (buf - grub_xnu_heap_start)
575 + grub_xnu_heap_will_be_at;
576 exthead->infoplistsize = infoplistsize + 1;
577 if (grub_file_read (infoplist, buf, infoplistsize)
578 != (grub_ssize_t) (infoplistsize))
580 grub_file_close (infoplist);
581 grub_error_push ();
582 return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s: ",
583 infoplistname);
585 grub_file_close (infoplist);
586 buf[infoplistsize] = 0;
588 grub_errno = GRUB_ERR_NONE;
590 /* Announce to kernel */
591 return grub_xnu_register_memory ("Driver-", &driversnum, exthead,
592 neededspace);
595 /* Load mkext. */
596 static grub_err_t
597 grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
598 int argc, char *args[])
600 grub_file_t file;
601 void *loadto;
602 grub_err_t err;
603 grub_off_t readoff = 0;
604 grub_ssize_t readlen = -1;
605 struct grub_macho_fat_header head;
606 struct grub_macho_fat_arch *archs;
607 int narchs, i;
609 if (argc != 1)
610 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
612 if (! grub_xnu_heap_size)
613 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
615 file = grub_gzfile_open (args[0], 1);
616 if (! file)
617 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
618 "Couldn't load driver package");
620 /* Sometimes caches are fat binary. Errgh. */
621 if (grub_file_read (file, &head, sizeof (head))
622 != (grub_ssize_t) (sizeof (head)))
624 /* I don't know the internal structure of package but
625 can hardly imagine a valid package shorter than 20 bytes. */
626 grub_file_close (file);
627 grub_error_push ();
628 return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
631 /* Find the corresponding architecture. */
632 if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
634 narchs = grub_be_to_cpu32 (head.nfat_arch);
635 archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
636 if (! archs)
638 grub_file_close (file);
639 grub_error_push ();
640 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
641 "Couldn't read file %s", args[0]);
644 if (grub_file_read (file, archs,
645 sizeof (struct grub_macho_fat_arch) * narchs)
646 != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
648 grub_free (archs);
649 grub_error_push ();
650 return grub_error (GRUB_ERR_READ_ERROR, "Cannot read fat header.");
652 for (i = 0; i < narchs; i++)
654 if (GRUB_MACHO_CPUTYPE_IS_HOST32
655 (grub_be_to_cpu32 (archs[i].cputype)))
657 readoff = grub_be_to_cpu32 (archs[i].offset);
658 readlen = grub_be_to_cpu32 (archs[i].size);
661 grub_free (archs);
663 else
665 /* It's a flat file. Some sane people still exist. */
666 readoff = 0;
667 readlen = grub_file_size (file);
670 if (readlen == -1)
672 grub_file_close (file);
673 return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
676 /* Allocate space. */
677 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
678 if (err)
680 grub_file_close (file);
681 return err;
684 loadto = grub_xnu_heap_malloc (readlen);
685 if (! loadto)
687 grub_file_close (file);
688 return grub_errno;
691 /* Read the file. */
692 grub_file_seek (file, readoff);
693 if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
695 grub_file_close (file);
696 grub_error_push ();
697 return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
699 grub_file_close (file);
701 /* Pass it to kernel. */
702 return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
703 loadto, readlen);
706 static grub_err_t
707 grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
708 int argc, char *args[])
710 grub_file_t file;
711 void *loadto;
712 grub_err_t err;
713 grub_size_t size;
715 if (argc != 1)
716 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
718 if (! grub_xnu_heap_size)
719 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
721 file = grub_gzfile_open (args[0], 1);
722 if (! file)
723 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
724 "Couldn't load ramdisk");
726 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
727 if (err)
728 return err;
730 size = grub_file_size (file);
732 loadto = grub_xnu_heap_malloc (size);
733 if (! loadto)
734 return grub_errno;
735 if (grub_file_read (file, loadto, size)
736 != (grub_ssize_t) (size))
738 grub_file_close (file);
739 grub_error_push ();
740 return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
742 return grub_xnu_register_memory ("RAMDisk", 0, loadto, size);
745 /* Parse a devtree file. It uses the following format:
746 valuename:valuedata;
747 keyname{
748 contents
750 keyname, valuename and valuedata are in hex.
752 static char *
753 grub_xnu_parse_devtree (struct grub_xnu_devtree_key **parent,
754 char *start, char *end)
756 char *ptr, *ptr2;
757 char *name, *data;
758 int namelen, datalen, i;
759 for (ptr = start; ptr && ptr < end; )
761 if (grub_isspace (*ptr))
763 ptr++;
764 continue;
766 if (*ptr == '}')
767 return ptr + 1;
768 namelen = 0;
770 /* Parse the name. */
771 for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
772 || (*ptr2 >= '0' && *ptr2 <= '9')
773 || (*ptr2 >= 'a' && *ptr2 <= 'f')
774 || (*ptr2 >= 'A' && *ptr2 <= 'F'));
775 ptr2++)
776 if (! grub_isspace (*ptr2))
777 namelen++;
778 if (ptr2 == end)
779 return 0;
780 namelen /= 2;
781 name = grub_malloc (namelen + 1);
782 if (!name)
783 return 0;
784 for (i = 0; i < 2 * namelen; i++)
786 int hex = 0;
787 while (grub_isspace (*ptr))
788 ptr++;
789 if (*ptr >= '0' && *ptr <= '9')
790 hex = *ptr - '0';
791 if (*ptr >= 'a' && *ptr <= 'f')
792 hex = *ptr - 'a' + 10;
793 if (*ptr >= 'A' && *ptr <= 'F')
794 hex = *ptr - 'A' + 10;
796 if (i % 2 == 0)
797 name[i / 2] = hex << 4;
798 else
799 name[i / 2] |= hex;
800 ptr++;
802 name [namelen] = 0;
803 while (grub_isspace (*ptr))
804 ptr++;
806 /* If it describes a key recursively invoke the function. */
807 if (*ptr == '{')
809 struct grub_xnu_devtree_key *newkey
810 = grub_xnu_create_key (parent, name);
811 grub_free (name);
812 if (! newkey)
813 return 0;
814 ptr = grub_xnu_parse_devtree (&(newkey->first_child), ptr + 1, end);
815 continue;
818 /* Parse the data. */
819 if (*ptr != ':')
820 return 0;
821 ptr++;
822 datalen = 0;
823 for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
824 || (*ptr2 >= '0' && *ptr2 <= '9')
825 || (*ptr2 >= 'a' && *ptr2 <= 'f')
826 || (*ptr2 >= 'A' && *ptr2 <= 'F'));
827 ptr2++)
828 if (! grub_isspace (*ptr2))
829 datalen++;
830 if (ptr2 == end)
831 return 0;
832 datalen /= 2;
833 data = grub_malloc (datalen);
834 if (! data)
835 return 0;
836 for (i = 0; i < 2 * datalen; i++)
838 int hex = 0;
839 while (grub_isspace (*ptr))
840 ptr++;
841 if (*ptr >= '0' && *ptr <= '9')
842 hex = *ptr - '0';
843 if (*ptr >= 'a' && *ptr <= 'f')
844 hex = *ptr - 'a' + 10;
845 if (*ptr >= 'A' && *ptr <= 'F')
846 hex = *ptr - 'A' + 10;
848 if (i % 2 == 0)
849 data[i / 2] = hex << 4;
850 else
851 data[i / 2] |= hex;
852 ptr++;
854 while (ptr < end && grub_isspace (*ptr))
855 ptr++;
857 struct grub_xnu_devtree_key *newkey
858 = grub_xnu_create_value (parent, name);
859 grub_free (name);
860 if (! newkey)
861 return 0;
862 newkey->datasize = datalen;
863 newkey->data = data;
865 if (*ptr != ';')
866 return 0;
867 ptr++;
869 if (ptr >= end && *parent != grub_xnu_devtree_root)
870 return 0;
871 return ptr;
874 /* Returns true if the kext should be loaded according to plist
875 and osbundlereq. Also fill BINNAME. */
876 static int
877 grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
878 char **binname)
880 grub_file_t file;
881 char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
882 char *stringptr = 0, *ptr2 = 0;
883 grub_size_t size;
884 int depth = 0;
885 int ret;
886 int osbundlekeyfound = 0, binnamekeyfound = 0;
887 if (binname)
888 *binname = 0;
890 file = grub_gzfile_open (plistname, 1);
891 if (! file)
893 grub_file_close (file);
894 grub_error_push ();
895 grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
896 return 0;
899 size = grub_file_size (file);
900 buf = grub_malloc (size);
901 if (! buf)
903 grub_file_close (file);
904 grub_error_push ();
905 grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't read file %s", plistname);
906 return 0;
908 if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
910 grub_file_close (file);
911 grub_error_push ();
912 grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
913 return 0;
915 grub_file_close (file);
917 /* Set the return value for the case when no OSBundleRequired tag is found. */
918 if (osbundlereq)
919 ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
920 else
921 ret = 1;
923 /* Parse plist. It's quite dirty and inextensible but does its job. */
924 for (ptr1 = buf; ptr1 < buf + size; ptr1++)
925 switch (*ptr1)
927 case '<':
928 tagstart = ptr1;
929 *ptr1 = 0;
930 if (keyptr && depth == 4
931 && grub_strcmp (keyptr, "OSBundleRequired") == 0)
932 osbundlekeyfound = 1;
933 if (keyptr && depth == 4 &&
934 grub_strcmp (keyptr, "CFBundleExecutable") == 0)
935 binnamekeyfound = 1;
936 if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
938 for (ptr2 = stringptr; *ptr2; ptr2++)
939 *ptr2 = grub_tolower (*ptr2);
940 ret = grub_strword (osbundlereq, stringptr)
941 || grub_strword (osbundlereq, "all");
943 if (stringptr && binnamekeyfound && binname && depth == 4)
945 if (*binname)
946 grub_free (*binname);
947 *binname = grub_strdup (stringptr);
950 *ptr1 = '<';
951 keyptr = 0;
952 stringptr = 0;
953 break;
954 case '>':
955 if (! tagstart)
957 grub_free (buf);
958 grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
959 return 0;
961 *ptr1 = 0;
962 if (tagstart[1] == '?' || ptr1[-1] == '/')
964 osbundlekeyfound = 0;
965 *ptr1 = '>';
966 break;
968 if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
969 keyptr = ptr1 + 1;
970 if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
971 stringptr = ptr1 + 1;
972 else if (grub_strcmp (tagstart + 1, "/key") != 0)
974 osbundlekeyfound = 0;
975 binnamekeyfound = 0;
977 *ptr1 = '>';
979 if (tagstart[1] == '/')
980 depth--;
981 else
982 depth++;
983 break;
985 grub_free (buf);
987 return ret;
990 /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
991 grub_err_t
992 grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
993 int maxrecursion)
995 grub_device_t dev;
996 char *device_name;
997 grub_fs_t fs;
998 const char *path;
1000 auto int load_hook (const char *filename,
1001 const struct grub_dirhook_info *info);
1002 int load_hook (const char *filename, const struct grub_dirhook_info *info)
1004 char *newdirname;
1005 if (! info->dir)
1006 return 0;
1007 if (filename[0] == '.')
1008 return 0;
1010 if (grub_strlen (filename) < 5 ||
1011 grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
1012 return 0;
1014 newdirname
1015 = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
1017 /* It's a .kext. Try to load it. */
1018 if (newdirname)
1020 grub_strcpy (newdirname, dirname);
1021 newdirname[grub_strlen (newdirname) + 1] = 0;
1022 newdirname[grub_strlen (newdirname)] = '/';
1023 grub_strcpy (newdirname + grub_strlen (newdirname), filename);
1024 grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
1025 maxrecursion);
1026 if (grub_errno == GRUB_ERR_BAD_OS)
1027 grub_errno = GRUB_ERR_NONE;
1028 grub_free (newdirname);
1030 return 0;
1033 if (! grub_xnu_heap_size)
1034 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
1036 device_name = grub_file_get_device_name (dirname);
1037 dev = grub_device_open (device_name);
1038 if (dev)
1040 fs = grub_fs_probe (dev);
1041 path = grub_strchr (dirname, ')');
1042 if (! path)
1043 path = dirname;
1044 else
1045 path++;
1047 if (fs)
1048 (fs->dir) (dev, path, load_hook);
1049 grub_device_close (dev);
1051 grub_free (device_name);
1053 return GRUB_ERR_NONE;
1056 /* Load extension DIRNAME. (extensions are directories in xnu) */
1057 grub_err_t
1058 grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
1059 int maxrecursion)
1061 grub_device_t dev;
1062 char *plistname = 0;
1063 char *newdirname;
1064 char *newpath;
1065 char *device_name;
1066 grub_fs_t fs;
1067 const char *path;
1068 char *binsuffix;
1069 int usemacos = 0;
1070 grub_file_t binfile;
1072 auto int load_hook (const char *filename,
1073 const struct grub_dirhook_info *info);
1075 int load_hook (const char *filename, const struct grub_dirhook_info *info)
1077 if (grub_strlen (filename) > 15)
1078 return 0;
1079 grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
1081 /* If the kext contains directory "Contents" all real stuff is in
1082 this directory. */
1083 if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
1084 grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
1085 maxrecursion - 1);
1087 /* Directory "Plugins" contains nested kexts. */
1088 if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
1089 grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
1090 maxrecursion - 1);
1092 /* Directory "MacOS" contains executable, otherwise executable is
1093 on the top. */
1094 if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
1095 usemacos = 1;
1097 /* Info.plist is the file which governs our future actions. */
1098 if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
1099 && ! plistname)
1100 plistname = grub_strdup (newdirname);
1101 return 0;
1104 newdirname = grub_malloc (grub_strlen (dirname) + 20);
1105 if (! newdirname)
1106 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
1107 grub_strcpy (newdirname, dirname);
1108 newdirname[grub_strlen (dirname)] = '/';
1109 newdirname[grub_strlen (dirname) + 1] = 0;
1110 device_name = grub_file_get_device_name (dirname);
1111 dev = grub_device_open (device_name);
1112 if (dev)
1114 fs = grub_fs_probe (dev);
1115 path = grub_strchr (dirname, ')');
1116 if (! path)
1117 path = dirname;
1118 else
1119 path++;
1121 newpath = grub_strchr (newdirname, ')');
1122 if (! newpath)
1123 newpath = newdirname;
1124 else
1125 newpath++;
1127 /* Look at the directory. */
1128 if (fs)
1129 (fs->dir) (dev, path, load_hook);
1131 if (plistname && grub_xnu_check_os_bundle_required
1132 (plistname, osbundlerequired, &binsuffix))
1134 if (binsuffix)
1136 /* Open the binary. */
1137 char *binname = grub_malloc (grub_strlen (dirname)
1138 + grub_strlen (binsuffix)
1139 + sizeof ("/MacOS/"));
1140 grub_strcpy (binname, dirname);
1141 if (usemacos)
1142 grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
1143 else
1144 grub_strcpy (binname + grub_strlen (binname), "/");
1145 grub_strcpy (binname + grub_strlen (binname), binsuffix);
1146 grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
1147 binfile = grub_gzfile_open (binname, 1);
1148 if (! binfile)
1149 grub_errno = GRUB_ERR_NONE;
1151 /* Load the extension. */
1152 grub_xnu_load_driver (plistname, binfile);
1153 grub_free (binname);
1154 grub_free (binsuffix);
1156 else
1158 grub_dprintf ("xnu", "%s:0\n", plistname);
1159 grub_xnu_load_driver (plistname, 0);
1162 grub_free (plistname);
1163 grub_device_close (dev);
1165 grub_free (device_name);
1167 return GRUB_ERR_NONE;
1170 /* Load devtree file. */
1171 static grub_err_t
1172 grub_cmd_xnu_devtree (grub_command_t cmd __attribute__ ((unused)),
1173 int argc, char *args[])
1175 grub_file_t file;
1176 char *data, *endret;
1177 grub_size_t datalen;
1179 if (argc != 1)
1180 return grub_error (GRUB_ERR_BAD_ARGUMENT, "Filename required");
1182 if (! grub_xnu_heap_size)
1183 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
1185 /* Load the file. */
1186 file = grub_gzfile_open (args[0], 1);
1187 if (! file)
1188 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load device tree");
1189 datalen = grub_file_size (file);
1190 data = grub_malloc (datalen + 1);
1191 if (! data)
1193 grub_file_close (file);
1194 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
1195 "Could load device tree into memory");
1197 if (grub_file_read (file, data, datalen) != (grub_ssize_t) datalen)
1199 grub_file_close (file);
1200 grub_free (data);
1201 grub_error_push ();
1202 return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
1204 grub_file_close (file);
1205 data[datalen] = 0;
1207 /* Parse the file. */
1208 endret = grub_xnu_parse_devtree (&grub_xnu_devtree_root,
1209 data, data + datalen);
1210 grub_free (data);
1212 if (! endret)
1213 return grub_error (GRUB_ERR_BAD_OS, "Couldn't parse devtree");
1215 return GRUB_ERR_NONE;
1218 static int locked=0;
1219 static grub_dl_t my_mod;
1221 /* Load the kext. */
1222 static grub_err_t
1223 grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
1224 int argc, char *args[])
1226 grub_file_t binfile = 0;
1227 if (argc == 2)
1229 /* User explicitly specified plist and binary. */
1230 if (grub_strcmp (args[1], "-") != 0)
1232 binfile = grub_gzfile_open (args[1], 1);
1233 if (! binfile)
1235 grub_error (GRUB_ERR_BAD_OS, "can't open file");
1236 return GRUB_ERR_NONE;
1239 return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
1240 binfile);
1243 /* load kext normally. */
1244 if (argc == 1)
1245 return grub_xnu_load_kext_from_dir (args[0], 0, 10);
1247 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
1250 /* Load a directory containing kexts. */
1251 static grub_err_t
1252 grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
1253 int argc, char *args[])
1255 if (argc != 1 && argc != 2)
1256 return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
1258 if (argc == 1)
1259 return grub_xnu_scan_dir_for_kexts (args[0],
1260 "console,root,local-root,network-root",
1261 10);
1262 else
1264 char *osbundlerequired = grub_strdup (args[1]), *ptr;
1265 grub_err_t err;
1266 if (! osbundlerequired)
1267 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
1268 "couldn't allocate string temporary space");
1269 for (ptr = osbundlerequired; *ptr; ptr++)
1270 *ptr = grub_tolower (*ptr);
1271 err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
1272 grub_free (osbundlerequired);
1273 return err;
1277 struct grub_video_bitmap *grub_xnu_bitmap = 0;
1279 static grub_err_t
1280 grub_cmd_xnu_splash (grub_command_t cmd __attribute__ ((unused)),
1281 int argc, char *args[])
1283 grub_err_t err;
1284 if (argc != 1)
1285 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
1287 err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
1288 if (err)
1289 grub_xnu_bitmap = 0;
1290 return err;
1294 #ifndef GRUB_UTIL
1295 static grub_err_t
1296 grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
1297 int argc, char *args[])
1299 if (argc != 1)
1300 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
1302 return grub_xnu_resume (args[0]);
1304 #endif
1306 void
1307 grub_xnu_lock ()
1309 if (!locked)
1310 grub_dl_ref (my_mod);
1311 locked = 1;
1314 void
1315 grub_xnu_unlock ()
1317 if (locked)
1318 grub_dl_unref (my_mod);
1319 locked = 0;
1322 static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir,
1323 cmd_ramdisk, cmd_devtree, cmd_resume, cmd_splash;
1325 GRUB_MOD_INIT(xnu)
1327 cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
1328 "load a xnu kernel");
1329 cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
1330 "Load XNU extension package.");
1331 cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
1332 "Load XNU extension.");
1333 cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
1334 "xnu_kextdir DIRECTORY [OSBundleRequired]",
1335 "Load XNU extension directory");
1336 cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
1337 "Load XNU ramdisk. "
1338 "It will be seen as md0");
1339 cmd_devtree = grub_register_command ("xnu_devtree", grub_cmd_xnu_devtree, 0,
1340 "Load XNU devtree");
1341 cmd_splash = grub_register_command ("xnu_splash", grub_cmd_xnu_splash, 0,
1342 "Load a splash image for XNU");
1344 #ifndef GRUB_UTIL
1345 cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
1346 0, "Load XNU hibernate image.");
1347 #endif
1348 my_mod=mod;
1351 GRUB_MOD_FINI(xnu)
1353 #ifndef GRUB_UTIL
1354 grub_unregister_command (cmd_resume);
1355 #endif
1356 grub_unregister_command (cmd_mkext);
1357 grub_unregister_command (cmd_kext);
1358 grub_unregister_command (cmd_kextdir);
1359 grub_unregister_command (cmd_devtree);
1360 grub_unregister_command (cmd_ramdisk);
1361 grub_unregister_command (cmd_kernel);
1362 grub_unregister_command (cmd_splash);