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