]> git.proxmox.com Git - grub2.git/blame - loader/xnu.c
merge mainline into emu-mod
[grub2.git] / loader / xnu.c
CommitLineData
b39f9d20 1/* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the
bbee0f2b 2 time he spent testing this
3 */
4/*
5 * GRUB -- GRand Unified Bootloader
6 * Copyright (C) 2009 Free Software Foundation, Inc.
7 *
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.
12 *
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.
17 *
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/>.
20 */
21
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>
a9d407a8 34#include <grub/extcmd.h>
1d3c6f1d 35#include <grub/env.h>
809bbfeb 36#include <grub/i18n.h>
bbee0f2b 37
38struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
39static int driverspackagenum = 0;
40static int driversnum = 0;
8a10b2c6 41int grub_xnu_is_64bit = 0;
bbee0f2b 42
15919498
VS
43void *grub_xnu_heap_start = 0;
44grub_size_t grub_xnu_heap_size = 0;
bbee0f2b 45
46/* Allocate heap by 32MB-blocks. */
47#define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
48
49static grub_err_t
b39f9d20 50grub_xnu_register_memory (char *prefix, int *suffix,
bbee0f2b 51 void *addr, grub_size_t size);
52void *
53grub_xnu_heap_malloc (int size)
54{
55 void *val;
bbee0f2b 56 int oldblknum, newblknum;
57
58 /* The page after the heap is used for stack. Ensure it's usable. */
59 if (grub_xnu_heap_size)
b39f9d20 60 oldblknum = (grub_xnu_heap_size + GRUB_XNU_PAGESIZE
bbee0f2b 61 + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
62 else
63 oldblknum = 0;
b39f9d20 64 newblknum = (grub_xnu_heap_size + size + GRUB_XNU_PAGESIZE
bbee0f2b 65 + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
66 if (oldblknum != newblknum)
bbee0f2b 67 {
15919498
VS
68 /* FIXME: instruct realloc to allocate at 1MB if possible once
69 advanced mm is ready. */
70 grub_xnu_heap_start
71 = XNU_RELOCATOR (realloc) (grub_xnu_heap_start,
a2c1332b 72 newblknum
15919498
VS
73 * GRUB_XNU_HEAP_ALLOC_BLOCK);
74 if (!grub_xnu_heap_start)
75 return NULL;
bbee0f2b 76 }
bbee0f2b 77
15919498 78 val = (grub_uint8_t *) grub_xnu_heap_start + grub_xnu_heap_size;
bbee0f2b 79 grub_xnu_heap_size += size;
80 grub_dprintf ("xnu", "val=%p\n", val);
15919498 81 return val;
bbee0f2b 82}
83
b39f9d20 84/* Make sure next block of the heap will be aligned.
85 Please notice: aligned are pointers AFTER relocation
bbee0f2b 86 and not the current ones. */
87grub_err_t
88grub_xnu_align_heap (int align)
89{
90 int align_overhead = align - grub_xnu_heap_size % align;
91 if (align_overhead == align)
92 return GRUB_ERR_NONE;
93 if (! grub_xnu_heap_malloc (align_overhead))
94 return grub_errno;
95 return GRUB_ERR_NONE;
96}
97
98/* Free subtree pointed by CUR. */
99void
100grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
101{
102 struct grub_xnu_devtree_key *d;
103 while (cur)
104 {
105 grub_free (cur->name);
106 if (cur->datasize == -1)
107 grub_xnu_free_devtree (cur->first_child);
108 else if (cur->data)
109 grub_free (cur->data);
110 d = cur->next;
b39f9d20 111 grub_free (cur);
bbee0f2b 112 cur = d;
b39f9d20 113 }
bbee0f2b 114}
115
116/* Compute the size of device tree in xnu format. */
117static grub_size_t
118grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, char *name)
119{
120 grub_size_t ret;
121 struct grub_xnu_devtree_key *cur;
b39f9d20 122
bbee0f2b 123 /* Key header. */
124 ret = 2 * sizeof (grub_uint32_t);
125
126 /* "name" value. */
127 ret += 32 + sizeof (grub_uint32_t)
b39f9d20 128 + grub_strlen (name) + 4
bbee0f2b 129 - (grub_strlen (name) % 4);
130
131 for (cur = start; cur; cur = cur->next)
132 if (cur->datasize != -1)
133 {
134 int align_overhead;
b39f9d20 135
bbee0f2b 136 align_overhead = 4 - (cur->datasize % 4);
137 if (align_overhead == 4)
138 align_overhead = 0;
139 ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
140 }
141 else
142 ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
143 return ret;
144}
145
146/* Write devtree in XNU format at curptr assuming the head is named NAME.*/
147static void *
b39f9d20 148grub_xnu_writetree_toheap_real (void *curptr,
bbee0f2b 149 struct grub_xnu_devtree_key *start, char *name)
150{
151 struct grub_xnu_devtree_key *cur;
152 int nkeys = 0, nvals = 0;
153 for (cur = start; cur; cur = cur->next)
154 {
155 if (cur->datasize == -1)
156 nkeys++;
157 else
158 nvals++;
159 }
160 /* For the name. */
b39f9d20 161 nvals++;
162
bbee0f2b 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;
167
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));
178
179 /* Then the other values. */
180 for (cur = start; cur; cur = cur->next)
181 if (cur->datasize != -1)
182 {
183 int align_overhead;
b39f9d20 184
bbee0f2b 185 align_overhead = 4 - (cur->datasize % 4);
186 if (align_overhead == 4)
187 align_overhead = 0;
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;
197 }
b39f9d20 198
bbee0f2b 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,
b39f9d20 203 cur->first_child,
bbee0f2b 204 cur->name)))
205 return 0;
206 return curptr;
207}
208
209grub_err_t
210grub_xnu_writetree_toheap (void **start, grub_size_t *size)
211{
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;
216 grub_err_t err;
b39f9d20 217
bbee0f2b 218 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
219 if (err)
220 return err;
b39f9d20 221
bbee0f2b 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");
225 if (! chosen)
226 return grub_errno;
227 memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
228 if (! memorymap)
229 return grub_errno;
230
231 driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
232 if (! 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;
b39f9d20 240 driverkey->data = extdesc
bbee0f2b 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");
244
245 /* Allocate the space based on the size with dummy value. */
246 *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
b39f9d20 247 *start = grub_xnu_heap_malloc (*size + GRUB_XNU_PAGESIZE
bbee0f2b 248 - *size % GRUB_XNU_PAGESIZE);
249
250 /* Put real data in the dummy. */
15919498 251 extdesc->addr = (grub_uint8_t *) *start - (grub_uint8_t *) grub_xnu_heap_start
bbee0f2b 252 + grub_xnu_heap_will_be_at;
253 extdesc->size = (grub_uint32_t) *size;
254
255 /* Write the tree to heap. */
256 grub_xnu_writetree_toheap_real (*start, grub_xnu_devtree_root, "/");
257 return GRUB_ERR_NONE;
258}
259
260/* Find a key or value in parent key. */
261struct grub_xnu_devtree_key *
262grub_xnu_find_key (struct grub_xnu_devtree_key *parent, char *name)
263{
264 struct grub_xnu_devtree_key *cur;
265 for (cur = parent; cur; cur = cur->next)
266 if (grub_strcmp (cur->name, name) == 0)
267 return cur;
268 return 0;
269}
270
271struct grub_xnu_devtree_key *
272grub_xnu_create_key (struct grub_xnu_devtree_key **parent, char *name)
273{
274 struct grub_xnu_devtree_key *ret;
275 ret = grub_xnu_find_key (*parent, name);
276 if (ret)
277 return ret;
eab58da2 278 ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
bbee0f2b 279 if (! ret)
280 {
281 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
282 return 0;
283 }
284 ret->name = grub_strdup (name);
285 if (! ret->name)
286 {
287 grub_free (ret);
288 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
289 return 0;
290 }
291 ret->datasize = -1;
bbee0f2b 292 ret->next = *parent;
293 *parent = ret;
294 return ret;
295}
296
297struct grub_xnu_devtree_key *
298grub_xnu_create_value (struct grub_xnu_devtree_key **parent, char *name)
299{
300 struct grub_xnu_devtree_key *ret;
301 ret = grub_xnu_find_key (*parent, name);
302 if (ret)
303 {
304 if (ret->datasize == -1)
305 grub_xnu_free_devtree (ret->first_child);
306 else if (ret->datasize)
307 grub_free (ret->data);
308 ret->datasize = 0;
309 ret->data = 0;
310 return ret;
311 }
eab58da2 312 ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
bbee0f2b 313 if (! ret)
314 {
315 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
316 return 0;
317 }
318 ret->name = grub_strdup (name);
319 if (! ret->name)
320 {
321 grub_free (ret);
322 grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
323 return 0;
324 }
bbee0f2b 325 ret->next = *parent;
326 *parent = ret;
327 return ret;
328}
329
330static grub_err_t
331grub_xnu_unload (void)
332{
1d3c6f1d
VS
333 grub_cpu_xnu_unload ();
334
bbee0f2b 335 grub_xnu_free_devtree (grub_xnu_devtree_root);
336 grub_xnu_devtree_root = 0;
337
338 /* Free loaded image. */
339 driversnum = 0;
340 driverspackagenum = 0;
341 grub_free (grub_xnu_heap_start);
342 grub_xnu_heap_start = 0;
343 grub_xnu_heap_size = 0;
344 grub_xnu_unlock ();
345 return GRUB_ERR_NONE;
346}
347
348static grub_err_t
349grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
350 int argc, char *args[])
351{
352 grub_err_t err;
353 grub_macho_t macho;
72db7c22 354 grub_uint32_t startcode, endcode;
bbee0f2b 355 int i;
356 char *ptr, *loadaddr;
357
358 if (argc < 1)
359 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
360
361 grub_xnu_unload ();
362
363 macho = grub_macho_open (args[0]);
364 if (! macho)
365 return grub_errno;
366 if (! grub_macho_contains_macho32 (macho))
367 {
368 grub_macho_close (macho);
b39f9d20 369 return grub_error (GRUB_ERR_BAD_OS,
7fd0baee 370 "kernel doesn't contain suitable 32-bit architecture");
bbee0f2b 371 }
372
72db7c22 373 err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
bbee0f2b 374 if (err)
375 {
376 grub_macho_close (macho);
377 grub_xnu_unload ();
378 return err;
379 }
380
b39f9d20 381 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
bbee0f2b 382 (unsigned long) endcode, (unsigned long) startcode);
383
384 loadaddr = grub_xnu_heap_malloc (endcode - startcode);
385 grub_xnu_heap_will_be_at = startcode;
386
387 if (! loadaddr)
388 {
389 grub_macho_close (macho);
390 grub_xnu_unload ();
b39f9d20 391 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
bbee0f2b 392 "not enough memory to load kernel");
393 }
394
395 /* Load kernel. */
72db7c22 396 err = grub_macho_load32 (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
bbee0f2b 397 if (err)
398 {
399 grub_macho_close (macho);
400 grub_xnu_unload ();
401 return err;
402 }
403
72db7c22 404 grub_xnu_entry_point = grub_macho_get_entry_point32 (macho);
bbee0f2b 405 if (! grub_xnu_entry_point)
406 {
407 grub_macho_close (macho);
408 grub_xnu_unload ();
409 return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
410 }
411
412 grub_macho_close (macho);
413
414 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
415 if (err)
416 {
417 grub_xnu_unload ();
418 return err;
419 }
420
421 /* Copy parameters to kernel command line. */
422 ptr = grub_xnu_cmdline;
423 for (i = 1; i < argc; i++)
424 {
b39f9d20 425 if (ptr + grub_strlen (args[i]) + 1
bbee0f2b 426 >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
427 break;
428 grub_memcpy (ptr, args[i], grub_strlen (args[i]));
429 ptr += grub_strlen (args[i]);
430 *ptr = ' ';
431 ptr++;
432 }
433
434 /* Replace last space by '\0'. */
435 if (ptr != grub_xnu_cmdline)
b39f9d20 436 *(ptr - 1) = 0;
bbee0f2b 437
b39f9d20 438 grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
bbee0f2b 439
440 grub_xnu_lock ();
8a10b2c6 441 grub_xnu_is_64bit = 0;
72db7c22 442
443 return 0;
444}
445
446static grub_err_t
447grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)),
448 int argc, char *args[])
449{
450 grub_err_t err;
451 grub_macho_t macho;
452 grub_uint64_t startcode, endcode;
453 int i;
454 char *ptr, *loadaddr;
455
456 if (argc < 1)
457 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
458
459 grub_xnu_unload ();
460
461 macho = grub_macho_open (args[0]);
462 if (! macho)
463 return grub_errno;
464 if (! grub_macho_contains_macho64 (macho))
465 {
466 grub_macho_close (macho);
467 return grub_error (GRUB_ERR_BAD_OS,
7fd0baee 468 "kernel doesn't contain suitable 64-bit architecture");
72db7c22 469 }
470
471 err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
bbee0f2b 472 if (err)
72db7c22 473 {
474 grub_macho_close (macho);
475 grub_xnu_unload ();
476 return err;
477 }
478
479 startcode &= 0x0fffffff;
480 endcode &= 0x0fffffff;
481
482 grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
483 (unsigned long) endcode, (unsigned long) startcode);
484
485 loadaddr = grub_xnu_heap_malloc (endcode - startcode);
486 grub_xnu_heap_will_be_at = startcode;
487
488 if (! loadaddr)
489 {
490 grub_macho_close (macho);
491 grub_xnu_unload ();
492 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
493 "not enough memory to load kernel");
494 }
495
496 /* Load kernel. */
497 err = grub_macho_load64 (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
498 if (err)
499 {
500 grub_macho_close (macho);
501 grub_xnu_unload ();
502 return err;
503 }
504
505 grub_xnu_entry_point = grub_macho_get_entry_point64 (macho) & 0x0fffffff;
506 if (! grub_xnu_entry_point)
507 {
508 grub_macho_close (macho);
509 grub_xnu_unload ();
510 return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
511 }
512
513 grub_macho_close (macho);
514
515 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
516 if (err)
517 {
518 grub_xnu_unload ();
519 return err;
520 }
521
522 /* Copy parameters to kernel command line. */
523 ptr = grub_xnu_cmdline;
524 for (i = 1; i < argc; i++)
525 {
526 if (ptr + grub_strlen (args[i]) + 1
527 >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
528 break;
529 grub_memcpy (ptr, args[i], grub_strlen (args[i]));
530 ptr += grub_strlen (args[i]);
531 *ptr = ' ';
532 ptr++;
533 }
534
535 /* Replace last space by '\0'. */
536 if (ptr != grub_xnu_cmdline)
537 *(ptr - 1) = 0;
bbee0f2b 538
b39f9d20 539 grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
bbee0f2b 540
541 grub_xnu_lock ();
8a10b2c6 542 grub_xnu_is_64bit = 1;
72db7c22 543
bbee0f2b 544 return 0;
545}
546
b39f9d20 547/* Register a memory in a memory map under name PREFIXSUFFIX
bbee0f2b 548 and increment SUFFIX. */
549static grub_err_t
b39f9d20 550grub_xnu_register_memory (char *prefix, int *suffix,
bbee0f2b 551 void *addr, grub_size_t size)
552{
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;
557
558 if (! grub_xnu_heap_size)
559 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
560
561 chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
562 if (! chosen)
563 return grub_errno;
564 memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
565 if (! memorymap)
566 return grub_errno;
567
568 driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
569 if (! driverkey)
570 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
571 if (suffix)
572 {
61eb45ee 573 driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++);
bbee0f2b 574 if (!driverkey->name)
575 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
bbee0f2b 576 }
577 else
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;
b39f9d20 584 driverkey->data = extdesc
bbee0f2b 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");
b39f9d20 588 extdesc->addr = grub_xnu_heap_will_be_at +
bbee0f2b 589 ((grub_uint8_t *) addr - (grub_uint8_t *) grub_xnu_heap_start);
590 extdesc->size = (grub_uint32_t) size;
591 return GRUB_ERR_NONE;
592}
593
965632c1 594static inline char *
595get_name_ptr (char *name)
596{
597 char *p = name, *p2;
598 /* Skip Info.plist. */
599 p2 = grub_strrchr (p, '/');
600 if (!p2)
601 return name;
602 if (p2 == name)
603 return name + 1;
604 p = p2 - 1;
605
606 p2 = grub_strrchr (p, '/');
607 if (!p2)
608 return name;
609 if (p2 == name)
610 return name + 1;
611 if (grub_memcmp (p2, "/Contents/", sizeof ("/Contents/") - 1) != 0)
612 return p2 + 1;
613
614 p = p2 - 1;
615
616 p2 = grub_strrchr (p, '/');
617 if (!p2)
618 return name;
619 return p2 + 1;
620}
621
bbee0f2b 622/* Load .kext. */
623static grub_err_t
624grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
625{
626 grub_macho_t macho;
627 grub_err_t err;
628 grub_file_t infoplist;
629 struct grub_xnu_extheader *exthead;
630 int neededspace = sizeof (*exthead);
15919498 631 grub_uint8_t *buf;
bbee0f2b 632 grub_size_t infoplistsize = 0, machosize = 0;
965632c1 633 char *name, *nameend;
634 int namelen;
635
636 name = get_name_ptr (infoplistname);
637 nameend = grub_strchr (name, '/');
638
639 if (nameend)
640 namelen = nameend - name;
641 else
642 namelen = grub_strlen (name);
643
644 neededspace += namelen + 1;
bbee0f2b 645
646 if (! grub_xnu_heap_size)
647 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
b39f9d20 648
bbee0f2b 649 /* Compute the needed space. */
650 if (binaryfile)
651 {
652 macho = grub_macho_file (binaryfile);
653 if (! macho || ! grub_macho_contains_macho32 (macho))
654 {
655 if (macho)
656 grub_macho_close (macho);
b39f9d20 657 return grub_error (GRUB_ERR_BAD_OS,
7fd0baee 658 "extension doesn't contain suitable architecture");
bbee0f2b 659 }
8a10b2c6 660 if (grub_xnu_is_64bit)
72db7c22 661 machosize = grub_macho_filesize64 (macho);
662 else
663 machosize = grub_macho_filesize32 (macho);
bbee0f2b 664 neededspace += machosize;
665 }
666 else
667 macho = 0;
668
669 if (infoplistname)
670 infoplist = grub_gzfile_open (infoplistname, 1);
671 else
672 infoplist = 0;
673 grub_errno = GRUB_ERR_NONE;
674 if (infoplist)
675 {
676 infoplistsize = grub_file_size (infoplist);
677 neededspace += infoplistsize + 1;
678 }
679 else
680 infoplistsize = 0;
681
682 /* Allocate the space. */
683 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
684 if (err)
685 return err;
686 buf = grub_xnu_heap_malloc (neededspace);
687
688 exthead = (struct grub_xnu_extheader *) buf;
689 grub_memset (exthead, 0, sizeof (*exthead));
690 buf += sizeof (*exthead);
691
692 /* Load the binary. */
693 if (macho)
694 {
15919498 695 exthead->binaryaddr = (buf - (grub_uint8_t *) grub_xnu_heap_start)
bbee0f2b 696 + grub_xnu_heap_will_be_at;
697 exthead->binarysize = machosize;
8a10b2c6 698 if (grub_xnu_is_64bit)
72db7c22 699 err = grub_macho_readfile64 (macho, buf);
700 else
701 err = grub_macho_readfile32 (macho, buf);
702 if (err)
bbee0f2b 703 {
704 grub_macho_close (macho);
705 return err;
706 }
707 grub_macho_close (macho);
708 buf += machosize;
709 }
710 grub_errno = GRUB_ERR_NONE;
711
712 /* Load the plist. */
713 if (infoplist)
714 {
15919498 715 exthead->infoplistaddr = (buf - (grub_uint8_t *) grub_xnu_heap_start)
bbee0f2b 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))
720 {
721 grub_file_close (infoplist);
722 grub_error_push ();
7fd0baee 723 return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s: ",
bbee0f2b 724 infoplistname);
725 }
726 grub_file_close (infoplist);
727 buf[infoplistsize] = 0;
965632c1 728 buf += infoplistsize + 1;
bbee0f2b 729 }
730 grub_errno = GRUB_ERR_NONE;
731
7ea73643
VS
732 exthead->nameaddr = (buf - (grub_uint8_t *) grub_xnu_heap_start)
733 + grub_xnu_heap_will_be_at;
965632c1 734 exthead->namesize = namelen + 1;
735 grub_memcpy (buf, name, namelen);
736 buf[namelen] = 0;
737 buf += namelen + 1;
738
bbee0f2b 739 /* Announce to kernel */
740 return grub_xnu_register_memory ("Driver-", &driversnum, exthead,
b39f9d20 741 neededspace);
bbee0f2b 742}
743
744/* Load mkext. */
745static grub_err_t
746grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
747 int argc, char *args[])
748{
749 grub_file_t file;
750 void *loadto;
751 grub_err_t err;
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;
756 int narchs, i;
757
758 if (argc != 1)
759 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
760
761 if (! grub_xnu_heap_size)
762 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
763
764 file = grub_gzfile_open (args[0], 1);
765 if (! file)
b39f9d20 766 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
7fd0baee 767 "couldn't load driver package");
bbee0f2b 768
769 /* Sometimes caches are fat binary. Errgh. */
5c5215d5 770 if (grub_file_read (file, &head, sizeof (head))
bbee0f2b 771 != (grub_ssize_t) (sizeof (head)))
772 {
b39f9d20 773 /* I don't know the internal structure of package but
bbee0f2b 774 can hardly imagine a valid package shorter than 20 bytes. */
775 grub_file_close (file);
776 grub_error_push ();
7fd0baee 777 return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
bbee0f2b 778 }
779
780 /* Find the corresponding architecture. */
781 if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
782 {
783 narchs = grub_be_to_cpu32 (head.nfat_arch);
784 archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
785 if (! archs)
786 {
787 grub_file_close (file);
788 grub_error_push ();
b39f9d20 789 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
7fd0baee 790 "couldn't read file %s", args[0]);
b39f9d20 791
bbee0f2b 792 }
5c5215d5 793 if (grub_file_read (file, archs,
bbee0f2b 794 sizeof (struct grub_macho_fat_arch) * narchs)
795 != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
796 {
797 grub_free (archs);
798 grub_error_push ();
7fd0baee 799 return grub_error (GRUB_ERR_READ_ERROR, "cannot read fat header");
bbee0f2b 800 }
801 for (i = 0; i < narchs; i++)
802 {
8a10b2c6 803 if (!grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST32
72db7c22 804 (grub_be_to_cpu32 (archs[i].cputype)))
805 {
806 readoff = grub_be_to_cpu32 (archs[i].offset);
807 readlen = grub_be_to_cpu32 (archs[i].size);
808 }
8a10b2c6 809 if (grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64
bbee0f2b 810 (grub_be_to_cpu32 (archs[i].cputype)))
811 {
812 readoff = grub_be_to_cpu32 (archs[i].offset);
813 readlen = grub_be_to_cpu32 (archs[i].size);
814 }
815 }
816 grub_free (archs);
817 }
818 else
819 {
820 /* It's a flat file. Some sane people still exist. */
821 readoff = 0;
822 readlen = grub_file_size (file);
823 }
824
825 if (readlen == -1)
826 {
827 grub_file_close (file);
828 return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
829 }
830
831 /* Allocate space. */
832 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
833 if (err)
834 {
835 grub_file_close (file);
836 return err;
837 }
838
839 loadto = grub_xnu_heap_malloc (readlen);
840 if (! loadto)
841 {
842 grub_file_close (file);
843 return grub_errno;
844 }
845
846 /* Read the file. */
847 grub_file_seek (file, readoff);
848 if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
849 {
850 grub_file_close (file);
851 grub_error_push ();
7fd0baee 852 return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
bbee0f2b 853 }
854 grub_file_close (file);
855
856 /* Pass it to kernel. */
857 return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
b39f9d20 858 loadto, readlen);
bbee0f2b 859}
860
861static grub_err_t
862grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
863 int argc, char *args[])
864{
865 grub_file_t file;
866 void *loadto;
867 grub_err_t err;
868 grub_size_t size;
869
870 if (argc != 1)
871 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
872
873 if (! grub_xnu_heap_size)
874 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
875
876 file = grub_gzfile_open (args[0], 1);
877 if (! file)
b39f9d20 878 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
7fd0baee 879 "couldn't load ramdisk");
bbee0f2b 880
881 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
882 if (err)
883 return err;
884
885 size = grub_file_size (file);
b39f9d20 886
bbee0f2b 887 loadto = grub_xnu_heap_malloc (size);
888 if (! loadto)
889 return grub_errno;
890 if (grub_file_read (file, loadto, size)
891 != (grub_ssize_t) (size))
892 {
893 grub_file_close (file);
894 grub_error_push ();
7fd0baee 895 return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
bbee0f2b 896 }
b39f9d20 897 return grub_xnu_register_memory ("RAMDisk", 0, loadto, size);
bbee0f2b 898}
899
b39f9d20 900/* Returns true if the kext should be loaded according to plist
bbee0f2b 901 and osbundlereq. Also fill BINNAME. */
902static int
903grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
904 char **binname)
905{
906 grub_file_t file;
907 char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
908 char *stringptr = 0, *ptr2 = 0;
909 grub_size_t size;
910 int depth = 0;
911 int ret;
912 int osbundlekeyfound = 0, binnamekeyfound = 0;
913 if (binname)
914 *binname = 0;
915
916 file = grub_gzfile_open (plistname, 1);
917 if (! file)
918 {
b39f9d20 919 grub_file_close (file);
bbee0f2b 920 grub_error_push ();
7fd0baee 921 grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", plistname);
bbee0f2b 922 return 0;
923 }
924
925 size = grub_file_size (file);
926 buf = grub_malloc (size);
927 if (! buf)
928 {
b39f9d20 929 grub_file_close (file);
bbee0f2b 930 grub_error_push ();
7fd0baee 931 grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read file %s", plistname);
bbee0f2b 932 return 0;
933 }
934 if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
935 {
b39f9d20 936 grub_file_close (file);
bbee0f2b 937 grub_error_push ();
7fd0baee 938 grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", plistname);
bbee0f2b 939 return 0;
940 }
b39f9d20 941 grub_file_close (file);
bbee0f2b 942
943 /* Set the return value for the case when no OSBundleRequired tag is found. */
944 if (osbundlereq)
945 ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
946 else
947 ret = 1;
b39f9d20 948
bbee0f2b 949 /* Parse plist. It's quite dirty and inextensible but does its job. */
950 for (ptr1 = buf; ptr1 < buf + size; ptr1++)
951 switch (*ptr1)
952 {
953 case '<':
954 tagstart = ptr1;
955 *ptr1 = 0;
b39f9d20 956 if (keyptr && depth == 4
bbee0f2b 957 && grub_strcmp (keyptr, "OSBundleRequired") == 0)
958 osbundlekeyfound = 1;
b39f9d20 959 if (keyptr && depth == 4 &&
bbee0f2b 960 grub_strcmp (keyptr, "CFBundleExecutable") == 0)
961 binnamekeyfound = 1;
962 if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
963 {
964 for (ptr2 = stringptr; *ptr2; ptr2++)
965 *ptr2 = grub_tolower (*ptr2);
b39f9d20 966 ret = grub_strword (osbundlereq, stringptr)
bbee0f2b 967 || grub_strword (osbundlereq, "all");
968 }
969 if (stringptr && binnamekeyfound && binname && depth == 4)
970 {
971 if (*binname)
972 grub_free (*binname);
973 *binname = grub_strdup (stringptr);
974 }
975
976 *ptr1 = '<';
977 keyptr = 0;
978 stringptr = 0;
979 break;
980 case '>':
981 if (! tagstart)
982 {
983 grub_free (buf);
984 grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
985 return 0;
986 }
987 *ptr1 = 0;
988 if (tagstart[1] == '?' || ptr1[-1] == '/')
989 {
990 osbundlekeyfound = 0;
991 *ptr1 = '>';
992 break;
993 }
994 if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
995 keyptr = ptr1 + 1;
996 if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
b39f9d20 997 stringptr = ptr1 + 1;
bbee0f2b 998 else if (grub_strcmp (tagstart + 1, "/key") != 0)
999 {
1000 osbundlekeyfound = 0;
1001 binnamekeyfound = 0;
1002 }
1003 *ptr1 = '>';
b39f9d20 1004
bbee0f2b 1005 if (tagstart[1] == '/')
1006 depth--;
1007 else
1008 depth++;
1009 break;
1010 }
1011 grub_free (buf);
1012
b39f9d20 1013 return ret;
bbee0f2b 1014}
1015
1016/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
1017grub_err_t
b39f9d20 1018grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
bbee0f2b 1019 int maxrecursion)
1020{
1021 grub_device_t dev;
1022 char *device_name;
1023 grub_fs_t fs;
1024 const char *path;
1025
b39f9d20 1026 auto int load_hook (const char *filename,
bbee0f2b 1027 const struct grub_dirhook_info *info);
1028 int load_hook (const char *filename, const struct grub_dirhook_info *info)
1029 {
1030 char *newdirname;
1031 if (! info->dir)
1032 return 0;
1033 if (filename[0] == '.')
1034 return 0;
1035
1036 if (grub_strlen (filename) < 5 ||
1037 grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
1038 return 0;
1039
b39f9d20 1040 newdirname
bbee0f2b 1041 = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
1042
1043 /* It's a .kext. Try to load it. */
1044 if (newdirname)
1045 {
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);
b39f9d20 1050 grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
bbee0f2b 1051 maxrecursion);
1052 if (grub_errno == GRUB_ERR_BAD_OS)
1053 grub_errno = GRUB_ERR_NONE;
1054 grub_free (newdirname);
1055 }
1056 return 0;
1057 }
1058
1059 if (! grub_xnu_heap_size)
1060 return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
1061
1062 device_name = grub_file_get_device_name (dirname);
1063 dev = grub_device_open (device_name);
1064 if (dev)
1065 {
1066 fs = grub_fs_probe (dev);
1067 path = grub_strchr (dirname, ')');
1068 if (! path)
1069 path = dirname;
1070 else
1071 path++;
1072
1073 if (fs)
1074 (fs->dir) (dev, path, load_hook);
1075 grub_device_close (dev);
1076 }
1077 grub_free (device_name);
1078
1079 return GRUB_ERR_NONE;
1080}
1081
4241d2b1 1082/* Load extension DIRNAME. (extensions are directories in xnu) */
bbee0f2b 1083grub_err_t
b39f9d20 1084grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
bbee0f2b 1085 int maxrecursion)
1086{
1087 grub_device_t dev;
1088 char *plistname = 0;
1089 char *newdirname;
1090 char *newpath;
1091 char *device_name;
1092 grub_fs_t fs;
1093 const char *path;
1094 char *binsuffix;
1095 int usemacos = 0;
1096 grub_file_t binfile;
1097
b39f9d20 1098 auto int load_hook (const char *filename,
bbee0f2b 1099 const struct grub_dirhook_info *info);
1100
1101 int load_hook (const char *filename, const struct grub_dirhook_info *info)
1102 {
1103 if (grub_strlen (filename) > 15)
1104 return 0;
b39f9d20 1105 grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
bbee0f2b 1106
b39f9d20 1107 /* If the kext contains directory "Contents" all real stuff is in
bbee0f2b 1108 this directory. */
1109 if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
1110 grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
1111 maxrecursion - 1);
1112
1113 /* Directory "Plugins" contains nested kexts. */
1114 if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
b39f9d20 1115 grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
bbee0f2b 1116 maxrecursion - 1);
1117
b39f9d20 1118 /* Directory "MacOS" contains executable, otherwise executable is
bbee0f2b 1119 on the top. */
1120 if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
1121 usemacos = 1;
b39f9d20 1122
bbee0f2b 1123 /* Info.plist is the file which governs our future actions. */
b39f9d20 1124 if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
bbee0f2b 1125 && ! plistname)
1126 plistname = grub_strdup (newdirname);
1127 return 0;
1128 }
1129
1130 newdirname = grub_malloc (grub_strlen (dirname) + 20);
1131 if (! newdirname)
1132 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
1133 grub_strcpy (newdirname, dirname);
1134 newdirname[grub_strlen (dirname)] = '/';
b39f9d20 1135 newdirname[grub_strlen (dirname) + 1] = 0;
bbee0f2b 1136 device_name = grub_file_get_device_name (dirname);
1137 dev = grub_device_open (device_name);
1138 if (dev)
1139 {
1140 fs = grub_fs_probe (dev);
1141 path = grub_strchr (dirname, ')');
1142 if (! path)
1143 path = dirname;
1144 else
1145 path++;
1146
1147 newpath = grub_strchr (newdirname, ')');
1148 if (! newpath)
1149 newpath = newdirname;
1150 else
1151 newpath++;
b39f9d20 1152
bbee0f2b 1153 /* Look at the directory. */
1154 if (fs)
1155 (fs->dir) (dev, path, load_hook);
1156
b39f9d20 1157 if (plistname && grub_xnu_check_os_bundle_required
bbee0f2b 1158 (plistname, osbundlerequired, &binsuffix))
1159 {
1160 if (binsuffix)
1161 {
1162 /* Open the binary. */
b39f9d20 1163 char *binname = grub_malloc (grub_strlen (dirname)
1164 + grub_strlen (binsuffix)
bbee0f2b 1165 + sizeof ("/MacOS/"));
1166 grub_strcpy (binname, dirname);
1167 if (usemacos)
1168 grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
1169 else
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);
b39f9d20 1174 if (! binfile)
bbee0f2b 1175 grub_errno = GRUB_ERR_NONE;
1176
b39f9d20 1177 /* Load the extension. */
bbee0f2b 1178 grub_xnu_load_driver (plistname, binfile);
1179 grub_free (binname);
1180 grub_free (binsuffix);
1181 }
1182 else
1183 {
1184 grub_dprintf ("xnu", "%s:0\n", plistname);
1185 grub_xnu_load_driver (plistname, 0);
1186 }
1187 }
1188 grub_free (plistname);
1189 grub_device_close (dev);
1190 }
1191 grub_free (device_name);
1192
1193 return GRUB_ERR_NONE;
1194}
1195
bbee0f2b 1196
1197static int locked=0;
1198static grub_dl_t my_mod;
1199
1200/* Load the kext. */
1201static grub_err_t
1202grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
1203 int argc, char *args[])
1204{
1205 grub_file_t binfile = 0;
1206 if (argc == 2)
1207 {
4241d2b1 1208 /* User explicitly specified plist and binary. */
bbee0f2b 1209 if (grub_strcmp (args[1], "-") != 0)
1210 {
1211 binfile = grub_gzfile_open (args[1], 1);
1212 if (! binfile)
1213 {
1214 grub_error (GRUB_ERR_BAD_OS, "can't open file");
1215 return GRUB_ERR_NONE;
1216 }
1217 }
1218 return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
b39f9d20 1219 binfile);
bbee0f2b 1220 }
1221
1222 /* load kext normally. */
1223 if (argc == 1)
1224 return grub_xnu_load_kext_from_dir (args[0], 0, 10);
1225
1226 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
1227}
1228
1229/* Load a directory containing kexts. */
1230static grub_err_t
1231grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
1232 int argc, char *args[])
1233{
1234 if (argc != 1 && argc != 2)
1235 return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
1236
1237 if (argc == 1)
b39f9d20 1238 return grub_xnu_scan_dir_for_kexts (args[0],
bbee0f2b 1239 "console,root,local-root,network-root",
1240 10);
1241 else
1242 {
1243 char *osbundlerequired = grub_strdup (args[1]), *ptr;
1244 grub_err_t err;
1245 if (! osbundlerequired)
b39f9d20 1246 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
bbee0f2b 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);
1252 return err;
1253 }
1254}
1255
1d3c6f1d
VS
1256static inline int
1257hextoval (char c)
1258{
1259 if (c >= '0' && c <= '9')
1260 return c - '0';
1261 if (c >= 'a' && c <= 'z')
1262 return c - 'a' + 10;
1263 if (c >= 'A' && c <= 'Z')
1264 return c - 'A' + 10;
1265 return 0;
1266}
1267
1268static inline void
1269unescape (char *name, char *curdot, char *nextdot, int *len)
1270{
1271 char *ptr, *dptr;
1272 dptr = name;
1273 for (ptr = curdot; ptr < nextdot;)
1274 if (ptr + 2 < nextdot && *ptr == '%')
1275 {
1276 *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
1277 ptr += 3;
1278 dptr++;
1279 }
1280 else
1281 {
1282 *dptr = *ptr;
1283 ptr++;
1284 dptr++;
1285 }
1286 *len = dptr - name;
1287}
1288
1289grub_err_t
1290grub_xnu_fill_devicetree (void)
1291{
1292 auto int iterate_env (struct grub_env_var *var);
1293 int iterate_env (struct grub_env_var *var)
1294 {
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;
1299 int len;
1300
1301 if (grub_memcmp (var->name, "XNU.DeviceTree.",
1302 sizeof ("XNU.DeviceTree.") - 1) != 0)
1303 return 0;
1304
1305 curdot = var->name + sizeof ("XNU.DeviceTree.") - 1;
1306 nextdot = grub_strchr (curdot, '.');
1307 if (nextdot)
1308 nextdot++;
1309 while (nextdot)
1310 {
1311 name = grub_realloc (name, nextdot - curdot + 1);
1312
1313 if (!name)
1314 return 1;
1315
1316 unescape (name, curdot, nextdot, &len);
1317 name[len - 1] = 0;
1318
1319 curkey = &(grub_xnu_create_key (curkey, name)->first_child);
1320
1321 curdot = nextdot;
1322 nextdot = grub_strchr (nextdot, '.');
1323 if (nextdot)
1324 nextdot++;
1325 }
1326
1327 nextdot = curdot + grub_strlen (curdot) + 1;
1328
1329 name = grub_realloc (name, nextdot - curdot + 1);
a2c1332b 1330
1d3c6f1d
VS
1331 if (!name)
1332 return 1;
a2c1332b 1333
1d3c6f1d
VS
1334 unescape (name, curdot, nextdot, &len);
1335 name[len] = 0;
1336
1337 curvalue = grub_xnu_create_value (curkey, name);
1338 grub_free (name);
a2c1332b 1339
1d3c6f1d
VS
1340 data = grub_malloc (grub_strlen (var->value) + 1);
1341 if (!data)
1342 return 1;
a2c1332b 1343
1d3c6f1d
VS
1344 unescape (data, var->value, var->value + grub_strlen (var->value),
1345 &len);
1346 curvalue->datasize = len;
1347 curvalue->data = data;
1348
1349 return 0;
1350 }
1351
1352 grub_env_iterate (iterate_env);
1353
1354 return grub_errno;
1355}
1356
bbee0f2b 1357struct grub_video_bitmap *grub_xnu_bitmap = 0;
a9d407a8
VS
1358grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
1359
1360/* Option array indices. */
1361#define XNU_SPLASH_CMD_ARGINDEX_MODE 0
1362
1363static const struct grub_arg_option xnu_splash_cmd_options[] =
1364 {
1365 {"mode", 'm', 0, "Background image mode.", "stretch|normal",
1366 ARG_TYPE_STRING},
1367 {0, 0, 0, 0, 0, 0}
1368 };
bbee0f2b 1369
1370static grub_err_t
a9d407a8 1371grub_cmd_xnu_splash (grub_extcmd_t cmd,
bbee0f2b 1372 int argc, char *args[])
1373{
1374 grub_err_t err;
1375 if (argc != 1)
b39f9d20 1376 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
1377
a9d407a8
VS
1378 if (cmd->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set &&
1379 grub_strcmp (cmd->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg,
1380 "stretch") == 0)
1381 grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH;
1382 else
1383 grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER;
1384
bbee0f2b 1385 err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
1386 if (err)
1387 grub_xnu_bitmap = 0;
a9d407a8 1388
bbee0f2b 1389 return err;
1390}
1391
1392
2083672a 1393#ifndef GRUB_MACHINE_EMU
bbee0f2b 1394static grub_err_t
1395grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
1396 int argc, char *args[])
1397{
1398 if (argc != 1)
1399 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
b39f9d20 1400
bbee0f2b 1401 return grub_xnu_resume (args[0]);
1402}
1403#endif
1404
1405void
1406grub_xnu_lock ()
1407{
bbee0f2b 1408 if (!locked)
1409 grub_dl_ref (my_mod);
bbee0f2b 1410 locked = 1;
1411}
1412
1413void
1414grub_xnu_unlock ()
1415{
bbee0f2b 1416 if (locked)
1417 grub_dl_unref (my_mod);
bbee0f2b 1418 locked = 0;
1419}
1420
72db7c22 1421static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext;
a9d407a8
VS
1422static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume;
1423static grub_extcmd_t cmd_splash;
bbee0f2b 1424
1425GRUB_MOD_INIT(xnu)
1426{
bbee0f2b 1427 cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
809bbfeb 1428 N_("Load XNU image."));
72db7c22 1429 cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
809bbfeb 1430 0, N_("Load 64-bit XNU image."));
bbee0f2b 1431 cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
809bbfeb 1432 N_("Load XNU extension package."));
bbee0f2b 1433 cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
809bbfeb 1434 N_("Load XNU extension."));
b39f9d20 1435 cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
809bbfeb 1436 N_("DIRECTORY [OSBundleRequired]"),
1437 N_("Load XNU extension directory."));
bbee0f2b 1438 cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
1439 "Load XNU ramdisk. "
864ba2bb 1440 "It will be seen as md0.");
a9d407a8
VS
1441 cmd_splash = grub_register_extcmd ("xnu_splash",
1442 grub_cmd_xnu_splash,
1443 GRUB_COMMAND_FLAG_BOTH, 0,
1f534b69 1444 N_("Load a splash image for XNU."),
a9d407a8 1445 xnu_splash_cmd_options);
bbee0f2b 1446
2083672a 1447#ifndef GRUB_MACHINE_EMU
b39f9d20 1448 cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
809bbfeb 1449 0, N_("Load XNU hibernate image."));
bbee0f2b 1450#endif
1d3c6f1d
VS
1451
1452 grub_cpu_xnu_init ();
1453
1454 my_mod = mod;
bbee0f2b 1455}
1456
1457GRUB_MOD_FINI(xnu)
1458{
2083672a 1459#ifndef GRUB_MACHINE_EMU
bbee0f2b 1460 grub_unregister_command (cmd_resume);
1461#endif
1462 grub_unregister_command (cmd_mkext);
1463 grub_unregister_command (cmd_kext);
1464 grub_unregister_command (cmd_kextdir);
bbee0f2b 1465 grub_unregister_command (cmd_ramdisk);
1466 grub_unregister_command (cmd_kernel);
a9d407a8 1467 grub_unregister_extcmd (cmd_splash);
72db7c22 1468 grub_unregister_command (cmd_kernel64);
1d3c6f1d
VS
1469
1470 grub_cpu_xnu_fini ();
bbee0f2b 1471}