| /* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the |
| time he spent testing this |
| */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2009 Free Software Foundation, Inc. |
| * |
| * GRUB is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * GRUB is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <grub/file.h> |
| #include <grub/xnu.h> |
| #include <grub/cpu/xnu.h> |
| #include <grub/mm.h> |
| #include <grub/dl.h> |
| #include <grub/loader.h> |
| #include <grub/machoload.h> |
| #include <grub/macho.h> |
| #include <grub/cpu/macho.h> |
| #include <grub/command.h> |
| #include <grub/misc.h> |
| #include <grub/extcmd.h> |
| #include <grub/env.h> |
| #include <grub/i18n.h> |
| #include <grub/verify.h> |
| #include <grub/efi/sb.h> |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| #if defined (__i386) && !defined (GRUB_MACHINE_EFI) |
| #include <grub/autoefi.h> |
| #endif |
| |
| struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0; |
| static int driverspackagenum = 0; |
| static int driversnum = 0; |
| int grub_xnu_is_64bit = 0; |
| int grub_xnu_darwin_version = 0; |
| |
| grub_addr_t grub_xnu_heap_target_start = 0; |
| grub_size_t grub_xnu_heap_size = 0; |
| struct grub_relocator *grub_xnu_relocator; |
| |
| static grub_err_t |
| grub_xnu_register_memory (const char *prefix, int *suffix, |
| grub_addr_t addr, grub_size_t size); |
| grub_err_t |
| grub_xnu_heap_malloc (int size, void **src, grub_addr_t *target) |
| { |
| grub_err_t err; |
| grub_relocator_chunk_t ch; |
| |
| err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, |
| grub_xnu_heap_target_start |
| + grub_xnu_heap_size, size); |
| if (err) |
| return err; |
| |
| *src = get_virtual_current_address (ch); |
| *target = grub_xnu_heap_target_start + grub_xnu_heap_size; |
| grub_xnu_heap_size += size; |
| grub_dprintf ("xnu", "val=%p\n", *src); |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Make sure next block of the heap will be aligned. |
| Please notice: aligned are pointers AFTER relocation |
| and not the current ones. */ |
| grub_err_t |
| grub_xnu_align_heap (int align) |
| { |
| grub_xnu_heap_size |
| = ALIGN_UP (grub_xnu_heap_target_start+ grub_xnu_heap_size, align) |
| - grub_xnu_heap_target_start; |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Free subtree pointed by CUR. */ |
| void |
| grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur) |
| { |
| struct grub_xnu_devtree_key *d; |
| while (cur) |
| { |
| grub_free (cur->name); |
| if (cur->datasize == -1) |
| grub_xnu_free_devtree (cur->first_child); |
| else if (cur->data) |
| grub_free (cur->data); |
| d = cur->next; |
| grub_free (cur); |
| cur = d; |
| } |
| } |
| |
| /* Compute the size of device tree in xnu format. */ |
| static grub_size_t |
| grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, |
| const char *name) |
| { |
| grub_size_t ret; |
| struct grub_xnu_devtree_key *cur; |
| |
| /* Key header. */ |
| ret = 2 * sizeof (grub_uint32_t); |
| |
| /* "name" value. */ |
| ret += 32 + sizeof (grub_uint32_t) |
| + grub_strlen (name) + 4 |
| - (grub_strlen (name) % 4); |
| |
| for (cur = start; cur; cur = cur->next) |
| if (cur->datasize != -1) |
| { |
| int align_overhead; |
| |
| align_overhead = 4 - (cur->datasize % 4); |
| if (align_overhead == 4) |
| align_overhead = 0; |
| ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead; |
| } |
| else |
| ret += grub_xnu_writetree_get_size (cur->first_child, cur->name); |
| return ret; |
| } |
| |
| /* Write devtree in XNU format at curptr assuming the head is named NAME.*/ |
| static void * |
| grub_xnu_writetree_toheap_real (void *curptr, |
| struct grub_xnu_devtree_key *start, |
| const char *name) |
| { |
| struct grub_xnu_devtree_key *cur; |
| int nkeys = 0, nvals = 0; |
| for (cur = start; cur; cur = cur->next) |
| { |
| if (cur->datasize == -1) |
| nkeys++; |
| else |
| nvals++; |
| } |
| /* For the name. */ |
| nvals++; |
| |
| *((grub_uint32_t *) curptr) = nvals; |
| curptr = ((grub_uint32_t *) curptr) + 1; |
| *((grub_uint32_t *) curptr) = nkeys; |
| curptr = ((grub_uint32_t *) curptr) + 1; |
| |
| /* First comes "name" value. */ |
| grub_memset (curptr, 0, 32); |
| grub_memcpy (curptr, "name", 4); |
| curptr = ((grub_uint8_t *) curptr) + 32; |
| *((grub_uint32_t *)curptr) = grub_strlen (name) + 1; |
| curptr = ((grub_uint32_t *) curptr) + 1; |
| grub_memcpy (curptr, name, grub_strlen (name)); |
| curptr = ((grub_uint8_t *) curptr) + grub_strlen (name); |
| grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4)); |
| curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4)); |
| |
| /* Then the other values. */ |
| for (cur = start; cur; cur = cur->next) |
| if (cur->datasize != -1) |
| { |
| int align_overhead; |
| |
| align_overhead = 4 - (cur->datasize % 4); |
| if (align_overhead == 4) |
| align_overhead = 0; |
| grub_memset (curptr, 0, 32); |
| grub_strncpy (curptr, cur->name, 31); |
| curptr = ((grub_uint8_t *) curptr) + 32; |
| *((grub_uint32_t *) curptr) = cur->datasize; |
| curptr = ((grub_uint32_t *) curptr) + 1; |
| grub_memcpy (curptr, cur->data, cur->datasize); |
| curptr = ((grub_uint8_t *) curptr) + cur->datasize; |
| grub_memset (curptr, 0, align_overhead); |
| curptr = ((grub_uint8_t *) curptr) + align_overhead; |
| } |
| |
| /* And then the keys. Recursively use this function. */ |
| for (cur = start; cur; cur = cur->next) |
| if (cur->datasize == -1) |
| { |
| curptr = grub_xnu_writetree_toheap_real (curptr, |
| cur->first_child, |
| cur->name); |
| if (!curptr) |
| return 0; |
| } |
| return curptr; |
| } |
| |
| grub_err_t |
| grub_xnu_writetree_toheap (grub_addr_t *target, grub_size_t *size) |
| { |
| struct grub_xnu_devtree_key *chosen; |
| struct grub_xnu_devtree_key *memorymap; |
| struct grub_xnu_devtree_key *driverkey; |
| struct grub_xnu_extdesc *extdesc; |
| grub_err_t err; |
| void *src; |
| |
| err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE); |
| if (err) |
| return err; |
| |
| /* Device tree itself is in the memory map of device tree. */ |
| /* Create a dummy value in memory-map. */ |
| chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen"); |
| if (! chosen) |
| return grub_errno; |
| memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map"); |
| if (! memorymap) |
| return grub_errno; |
| |
| driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey)); |
| if (! driverkey) |
| return grub_errno; |
| driverkey->name = grub_strdup ("DeviceTree"); |
| if (! driverkey->name) |
| return grub_errno; |
| driverkey->datasize = sizeof (*extdesc); |
| driverkey->next = memorymap->first_child; |
| memorymap->first_child = driverkey; |
| driverkey->data = extdesc |
| = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc)); |
| if (! driverkey->data) |
| return grub_errno; |
| |
| /* Allocate the space based on the size with dummy value. */ |
| *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/"); |
| err = grub_xnu_heap_malloc (ALIGN_UP (*size + 1, GRUB_XNU_PAGESIZE), |
| &src, target); |
| if (err) |
| return err; |
| |
| /* Put real data in the dummy. */ |
| extdesc->addr = *target; |
| extdesc->size = (grub_uint32_t) *size; |
| |
| /* Write the tree to heap. */ |
| grub_xnu_writetree_toheap_real (src, grub_xnu_devtree_root, "/"); |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Find a key or value in parent key. */ |
| struct grub_xnu_devtree_key * |
| grub_xnu_find_key (struct grub_xnu_devtree_key *parent, const char *name) |
| { |
| struct grub_xnu_devtree_key *cur; |
| for (cur = parent; cur; cur = cur->next) |
| if (grub_strcmp (cur->name, name) == 0) |
| return cur; |
| return 0; |
| } |
| |
| struct grub_xnu_devtree_key * |
| grub_xnu_create_key (struct grub_xnu_devtree_key **parent, const char *name) |
| { |
| struct grub_xnu_devtree_key *ret; |
| ret = grub_xnu_find_key (*parent, name); |
| if (ret) |
| return ret; |
| ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret)); |
| if (! ret) |
| return 0; |
| ret->name = grub_strdup (name); |
| if (! ret->name) |
| { |
| grub_free (ret); |
| return 0; |
| } |
| ret->datasize = -1; |
| ret->next = *parent; |
| *parent = ret; |
| return ret; |
| } |
| |
| struct grub_xnu_devtree_key * |
| grub_xnu_create_value (struct grub_xnu_devtree_key **parent, const char *name) |
| { |
| struct grub_xnu_devtree_key *ret; |
| ret = grub_xnu_find_key (*parent, name); |
| if (ret) |
| { |
| if (ret->datasize == -1) |
| grub_xnu_free_devtree (ret->first_child); |
| else if (ret->datasize) |
| grub_free (ret->data); |
| ret->datasize = 0; |
| ret->data = 0; |
| return ret; |
| } |
| ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret)); |
| if (! ret) |
| return 0; |
| ret->name = grub_strdup (name); |
| if (! ret->name) |
| { |
| grub_free (ret); |
| return 0; |
| } |
| ret->next = *parent; |
| *parent = ret; |
| return ret; |
| } |
| |
| static grub_err_t |
| grub_xnu_unload (void) |
| { |
| grub_cpu_xnu_unload (); |
| |
| grub_xnu_free_devtree (grub_xnu_devtree_root); |
| grub_xnu_devtree_root = 0; |
| |
| /* Free loaded image. */ |
| driversnum = 0; |
| driverspackagenum = 0; |
| grub_relocator_unload (grub_xnu_relocator); |
| grub_xnu_relocator = NULL; |
| grub_xnu_heap_target_start = 0; |
| grub_xnu_heap_size = 0; |
| grub_xnu_unlock (); |
| return GRUB_ERR_NONE; |
| } |
| |
| static grub_err_t |
| grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| grub_err_t err; |
| grub_macho_t macho; |
| grub_uint32_t startcode, endcode; |
| int i; |
| char *ptr; |
| void *loadaddr; |
| grub_addr_t loadaddr_target; |
| |
| if (argc < 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| grub_xnu_unload (); |
| |
| macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, 0); |
| if (! macho) |
| return grub_errno; |
| |
| err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS, |
| args[0]); |
| if (err) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n", |
| (unsigned long) endcode, (unsigned long) startcode); |
| |
| grub_xnu_relocator = grub_relocator_new (); |
| if (!grub_xnu_relocator) |
| return grub_errno; |
| grub_xnu_heap_target_start = startcode; |
| err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr, |
| &loadaddr_target); |
| |
| if (err) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| /* Load kernel. */ |
| err = grub_macho_load32 (macho, args[0], (char *) loadaddr - startcode, |
| GRUB_MACHO_NOBSS, &grub_xnu_darwin_version); |
| if (err) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| grub_xnu_entry_point = grub_macho_get_entry_point32 (macho, args[0]); |
| if (! grub_xnu_entry_point) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point"); |
| } |
| |
| grub_macho_close (macho); |
| |
| err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE); |
| if (err) |
| { |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| /* Copy parameters to kernel command line. */ |
| ptr = grub_xnu_cmdline; |
| for (i = 1; i < argc; i++) |
| { |
| if (ptr + grub_strlen (args[i]) + 1 |
| >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline)) |
| break; |
| grub_memcpy (ptr, args[i], grub_strlen (args[i])); |
| ptr += grub_strlen (args[i]); |
| *ptr = ' '; |
| ptr++; |
| } |
| |
| /* Replace last space by '\0'. */ |
| if (ptr != grub_xnu_cmdline) |
| *(ptr - 1) = 0; |
| |
| err = grub_verify_string (grub_xnu_cmdline, GRUB_VERIFY_KERNEL_CMDLINE); |
| if (err) |
| return err; |
| |
| #if defined (__i386) && !defined (GRUB_MACHINE_EFI) |
| err = grub_efiemu_autocore (); |
| if (err) |
| return err; |
| #endif |
| |
| grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0); |
| |
| grub_xnu_lock (); |
| grub_xnu_is_64bit = 0; |
| |
| return 0; |
| } |
| |
| static grub_err_t |
| grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| grub_err_t err; |
| grub_macho_t macho; |
| grub_uint64_t startcode, endcode; |
| int i; |
| char *ptr; |
| void *loadaddr; |
| grub_addr_t loadaddr_target; |
| |
| if (argc < 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| grub_xnu_unload (); |
| |
| macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, 1); |
| if (! macho) |
| return grub_errno; |
| |
| err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS, |
| args[0]); |
| if (err) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| startcode &= 0x0fffffff; |
| endcode &= 0x0fffffff; |
| |
| grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n", |
| (unsigned long) endcode, (unsigned long) startcode); |
| |
| grub_xnu_relocator = grub_relocator_new (); |
| if (!grub_xnu_relocator) |
| return grub_errno; |
| grub_xnu_heap_target_start = startcode; |
| err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr, |
| &loadaddr_target); |
| |
| if (err) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| /* Load kernel. */ |
| err = grub_macho_load64 (macho, args[0], (char *) loadaddr - startcode, |
| GRUB_MACHO_NOBSS, &grub_xnu_darwin_version); |
| if (err) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| grub_xnu_entry_point = grub_macho_get_entry_point64 (macho, args[0]) |
| & 0x0fffffff; |
| if (! grub_xnu_entry_point) |
| { |
| grub_macho_close (macho); |
| grub_xnu_unload (); |
| return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point"); |
| } |
| |
| grub_macho_close (macho); |
| |
| err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE); |
| if (err) |
| { |
| grub_xnu_unload (); |
| return err; |
| } |
| |
| /* Copy parameters to kernel command line. */ |
| ptr = grub_xnu_cmdline; |
| for (i = 1; i < argc; i++) |
| { |
| if (ptr + grub_strlen (args[i]) + 1 |
| >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline)) |
| break; |
| grub_memcpy (ptr, args[i], grub_strlen (args[i])); |
| ptr += grub_strlen (args[i]); |
| *ptr = ' '; |
| ptr++; |
| } |
| |
| /* Replace last space by '\0'. */ |
| if (ptr != grub_xnu_cmdline) |
| *(ptr - 1) = 0; |
| |
| err = grub_verify_string (grub_xnu_cmdline, GRUB_VERIFY_KERNEL_CMDLINE); |
| if (err) |
| return err; |
| |
| #if defined (__i386) && !defined (GRUB_MACHINE_EFI) |
| err = grub_efiemu_autocore (); |
| if (err) |
| return err; |
| #endif |
| |
| grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0); |
| |
| grub_xnu_lock (); |
| grub_xnu_is_64bit = 1; |
| |
| return 0; |
| } |
| |
| /* Register a memory in a memory map under name PREFIXSUFFIX |
| and increment SUFFIX. */ |
| static grub_err_t |
| grub_xnu_register_memory (const char *prefix, int *suffix, |
| grub_addr_t addr, grub_size_t size) |
| { |
| struct grub_xnu_devtree_key *chosen; |
| struct grub_xnu_devtree_key *memorymap; |
| struct grub_xnu_devtree_key *driverkey; |
| struct grub_xnu_extdesc *extdesc; |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen"); |
| if (! chosen) |
| return grub_errno; |
| memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map"); |
| if (! memorymap) |
| return grub_errno; |
| |
| driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey)); |
| if (! driverkey) |
| return grub_errno; |
| if (suffix) |
| driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++); |
| else |
| driverkey->name = grub_strdup (prefix); |
| if (!driverkey->name) |
| { |
| grub_free (driverkey); |
| return grub_errno; |
| } |
| driverkey->datasize = sizeof (*extdesc); |
| driverkey->next = memorymap->first_child; |
| driverkey->data = extdesc |
| = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc)); |
| if (! driverkey->data) |
| { |
| grub_free (driverkey->name); |
| grub_free (driverkey); |
| return grub_errno; |
| } |
| memorymap->first_child = driverkey; |
| extdesc->addr = addr; |
| extdesc->size = (grub_uint32_t) size; |
| return GRUB_ERR_NONE; |
| } |
| |
| static inline char * |
| get_name_ptr (char *name) |
| { |
| char *p = name, *p2; |
| /* Skip Info.plist. */ |
| p2 = grub_strrchr (p, '/'); |
| if (!p2) |
| return name; |
| if (p2 == name) |
| return name + 1; |
| p = p2 - 1; |
| |
| p2 = grub_strrchr (p, '/'); |
| if (!p2) |
| return name; |
| if (p2 == name) |
| return name + 1; |
| if (grub_memcmp (p2, "/Contents/", sizeof ("/Contents/") - 1) != 0) |
| return p2 + 1; |
| |
| p = p2 - 1; |
| |
| p2 = grub_strrchr (p, '/'); |
| if (!p2) |
| return name; |
| return p2 + 1; |
| } |
| |
| /* Load .kext. */ |
| static grub_err_t |
| grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile, |
| const char *filename) |
| { |
| grub_macho_t macho; |
| grub_err_t err; |
| grub_file_t infoplist; |
| struct grub_xnu_extheader *exthead; |
| int neededspace = sizeof (*exthead); |
| grub_uint8_t *buf; |
| void *buf0; |
| grub_addr_t buf_target; |
| grub_size_t infoplistsize = 0, machosize = 0; |
| char *name, *nameend; |
| int namelen; |
| |
| name = get_name_ptr (infoplistname); |
| nameend = grub_strchr (name, '/'); |
| |
| if (nameend) |
| namelen = nameend - name; |
| else |
| namelen = grub_strlen (name); |
| |
| neededspace += namelen + 1; |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| /* Compute the needed space. */ |
| if (binaryfile) |
| { |
| macho = grub_macho_file (binaryfile, filename, grub_xnu_is_64bit); |
| if (!macho) |
| grub_file_close (binaryfile); |
| else |
| { |
| if (grub_xnu_is_64bit) |
| machosize = grub_macho_filesize64 (macho); |
| else |
| machosize = grub_macho_filesize32 (macho); |
| } |
| neededspace += machosize; |
| } |
| else |
| macho = 0; |
| |
| if (infoplistname) |
| infoplist = grub_file_open (infoplistname, GRUB_FILE_TYPE_XNU_INFO_PLIST); |
| else |
| infoplist = 0; |
| grub_errno = GRUB_ERR_NONE; |
| if (infoplist) |
| { |
| infoplistsize = grub_file_size (infoplist); |
| neededspace += infoplistsize + 1; |
| } |
| else |
| infoplistsize = 0; |
| |
| /* Allocate the space. */ |
| err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE); |
| if (err) |
| goto fail; |
| err = grub_xnu_heap_malloc (neededspace, &buf0, &buf_target); |
| if (err) |
| goto fail; |
| buf = buf0; |
| |
| exthead = (struct grub_xnu_extheader *) buf; |
| grub_memset (exthead, 0, sizeof (*exthead)); |
| buf += sizeof (*exthead); |
| |
| /* Load the binary. */ |
| if (macho) |
| { |
| exthead->binaryaddr = buf_target + (buf - (grub_uint8_t *) buf0); |
| exthead->binarysize = machosize; |
| if (grub_xnu_is_64bit) |
| err = grub_macho_readfile64 (macho, filename, buf); |
| else |
| err = grub_macho_readfile32 (macho, filename, buf); |
| if (err) |
| goto fail; |
| grub_macho_close (macho); |
| buf += machosize; |
| } |
| grub_errno = GRUB_ERR_NONE; |
| |
| /* Load the plist. */ |
| if (infoplist) |
| { |
| exthead->infoplistaddr = buf_target + (buf - (grub_uint8_t *) buf0); |
| exthead->infoplistsize = infoplistsize + 1; |
| if (grub_file_read (infoplist, buf, infoplistsize) |
| != (grub_ssize_t) (infoplistsize)) |
| { |
| grub_file_close (infoplist); |
| if (!grub_errno) |
| grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), |
| infoplistname); |
| return grub_errno; |
| } |
| grub_file_close (infoplist); |
| buf[infoplistsize] = 0; |
| buf += infoplistsize + 1; |
| } |
| grub_errno = GRUB_ERR_NONE; |
| |
| exthead->nameaddr = (buf - (grub_uint8_t *) buf0) + buf_target; |
| exthead->namesize = namelen + 1; |
| grub_memcpy (buf, name, namelen); |
| buf[namelen] = 0; |
| buf += namelen + 1; |
| |
| /* Announce to kernel */ |
| return grub_xnu_register_memory ("Driver-", &driversnum, buf_target, |
| neededspace); |
| fail: |
| if (macho) |
| grub_macho_close (macho); |
| return err; |
| } |
| |
| /* Load mkext. */ |
| static grub_err_t |
| grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| grub_file_t file; |
| void *loadto; |
| grub_addr_t loadto_target; |
| grub_err_t err; |
| grub_off_t readoff = 0; |
| grub_ssize_t readlen = -1; |
| struct grub_macho_fat_header head; |
| struct grub_macho_fat_arch *archs; |
| int narchs, i; |
| |
| if (argc != 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_MKEXT); |
| if (! file) |
| return grub_errno; |
| |
| /* Sometimes caches are fat binary. Errgh. */ |
| if (grub_file_read (file, &head, sizeof (head)) |
| != (grub_ssize_t) (sizeof (head))) |
| { |
| /* I don't know the internal structure of package but |
| can hardly imagine a valid package shorter than 20 bytes. */ |
| grub_file_close (file); |
| if (!grub_errno) |
| grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]); |
| return grub_errno; |
| } |
| |
| /* Find the corresponding architecture. */ |
| if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC) |
| { |
| narchs = grub_be_to_cpu32 (head.nfat_arch); |
| archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs); |
| if (! archs) |
| { |
| grub_file_close (file); |
| return grub_errno; |
| |
| } |
| if (grub_file_read (file, archs, |
| sizeof (struct grub_macho_fat_arch) * narchs) |
| != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs) |
| { |
| grub_free (archs); |
| if (!grub_errno) |
| grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file %s"), |
| args[0]); |
| return grub_errno; |
| } |
| for (i = 0; i < narchs; i++) |
| { |
| if (!grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST32 |
| (grub_be_to_cpu32 (archs[i].cputype))) |
| { |
| readoff = grub_be_to_cpu32 (archs[i].offset); |
| readlen = grub_be_to_cpu32 (archs[i].size); |
| } |
| if (grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64 |
| (grub_be_to_cpu32 (archs[i].cputype))) |
| { |
| readoff = grub_be_to_cpu32 (archs[i].offset); |
| readlen = grub_be_to_cpu32 (archs[i].size); |
| } |
| } |
| grub_free (archs); |
| } |
| else |
| { |
| /* It's a flat file. Some sane people still exist. */ |
| readoff = 0; |
| readlen = grub_file_size (file); |
| } |
| |
| if (readlen == -1) |
| { |
| grub_file_close (file); |
| return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found"); |
| } |
| |
| /* Allocate space. */ |
| err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE); |
| if (err) |
| { |
| grub_file_close (file); |
| return err; |
| } |
| |
| err = grub_xnu_heap_malloc (readlen, &loadto, &loadto_target); |
| if (err) |
| { |
| grub_file_close (file); |
| return err; |
| } |
| |
| /* Read the file. */ |
| grub_file_seek (file, readoff); |
| if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen)) |
| { |
| grub_file_close (file); |
| if (!grub_errno) |
| grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]); |
| return grub_errno; |
| } |
| grub_file_close (file); |
| |
| /* Pass it to kernel. */ |
| return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum, |
| loadto_target, readlen); |
| } |
| |
| static grub_err_t |
| grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| grub_file_t file; |
| void *loadto; |
| grub_addr_t loadto_target; |
| grub_err_t err; |
| grub_size_t size; |
| |
| if (argc != 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_RAMDISK); |
| if (! file) |
| return grub_errno; |
| |
| err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE); |
| if (err) |
| return err; |
| |
| size = grub_file_size (file); |
| |
| err = grub_xnu_heap_malloc (size, &loadto, &loadto_target); |
| if (err) |
| return err; |
| if (grub_file_read (file, loadto, size) != (grub_ssize_t) (size)) |
| { |
| grub_file_close (file); |
| if (!grub_errno) |
| grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]); |
| return grub_errno; |
| } |
| return grub_xnu_register_memory ("RAMDisk", 0, loadto_target, size); |
| } |
| |
| /* Returns true if the kext should be loaded according to plist |
| and osbundlereq. Also fill BINNAME. */ |
| static int |
| grub_xnu_check_os_bundle_required (char *plistname, |
| const char *osbundlereq, |
| char **binname) |
| { |
| grub_file_t file; |
| char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0; |
| char *stringptr = 0, *ptr2 = 0; |
| grub_size_t size; |
| int depth = 0; |
| int ret; |
| int osbundlekeyfound = 0, binnamekeyfound = 0; |
| if (binname) |
| *binname = 0; |
| |
| file = grub_file_open (plistname, GRUB_FILE_TYPE_XNU_INFO_PLIST); |
| if (! file) |
| return 0; |
| |
| size = grub_file_size (file); |
| buf = grub_malloc (size); |
| if (! buf) |
| { |
| grub_file_close (file); |
| return 0; |
| } |
| if (grub_file_read (file, buf, size) != (grub_ssize_t) (size)) |
| { |
| grub_file_close (file); |
| if (!grub_errno) |
| grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), plistname); |
| return 0; |
| } |
| grub_file_close (file); |
| |
| /* Set the return value for the case when no OSBundleRequired tag is found. */ |
| if (osbundlereq) |
| ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-"); |
| else |
| ret = 1; |
| |
| /* Parse plist. It's quite dirty and inextensible but does its job. */ |
| for (ptr1 = buf; ptr1 < buf + size; ptr1++) |
| switch (*ptr1) |
| { |
| case '<': |
| tagstart = ptr1; |
| *ptr1 = 0; |
| if (keyptr && depth == 4 |
| && grub_strcmp (keyptr, "OSBundleRequired") == 0) |
| osbundlekeyfound = 1; |
| if (keyptr && depth == 4 && |
| grub_strcmp (keyptr, "CFBundleExecutable") == 0) |
| binnamekeyfound = 1; |
| if (stringptr && osbundlekeyfound && osbundlereq && depth == 4) |
| { |
| for (ptr2 = stringptr; *ptr2; ptr2++) |
| *ptr2 = grub_tolower (*ptr2); |
| ret = grub_strword (osbundlereq, stringptr) |
| || grub_strword (osbundlereq, "all"); |
| } |
| if (stringptr && binnamekeyfound && binname && depth == 4) |
| { |
| if (*binname) |
| grub_free (*binname); |
| *binname = grub_strdup (stringptr); |
| } |
| |
| *ptr1 = '<'; |
| keyptr = 0; |
| stringptr = 0; |
| break; |
| case '>': |
| if (! tagstart) |
| { |
| grub_free (buf); |
| grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname); |
| return 0; |
| } |
| *ptr1 = 0; |
| if (tagstart[1] == '?' || ptr1[-1] == '/') |
| { |
| osbundlekeyfound = 0; |
| *ptr1 = '>'; |
| break; |
| } |
| if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0) |
| keyptr = ptr1 + 1; |
| if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0) |
| stringptr = ptr1 + 1; |
| else if (grub_strcmp (tagstart + 1, "/key") != 0) |
| { |
| osbundlekeyfound = 0; |
| binnamekeyfound = 0; |
| } |
| *ptr1 = '>'; |
| |
| if (tagstart[1] == '/') |
| depth--; |
| else |
| depth++; |
| break; |
| } |
| grub_free (buf); |
| |
| return ret; |
| } |
| |
| /* Context for grub_xnu_scan_dir_for_kexts. */ |
| struct grub_xnu_scan_dir_for_kexts_ctx |
| { |
| char *dirname; |
| const char *osbundlerequired; |
| int maxrecursion; |
| }; |
| |
| /* Helper for grub_xnu_scan_dir_for_kexts. */ |
| static int |
| grub_xnu_scan_dir_for_kexts_load (const char *filename, |
| const struct grub_dirhook_info *info, |
| void *data) |
| { |
| struct grub_xnu_scan_dir_for_kexts_ctx *ctx = data; |
| char *newdirname; |
| |
| if (! info->dir) |
| return 0; |
| if (filename[0] == '.') |
| return 0; |
| |
| if (grub_strlen (filename) < 5 || |
| grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0) |
| return 0; |
| |
| newdirname |
| = grub_malloc (grub_strlen (ctx->dirname) + grub_strlen (filename) + 2); |
| |
| /* It's a .kext. Try to load it. */ |
| if (newdirname) |
| { |
| grub_strcpy (newdirname, ctx->dirname); |
| newdirname[grub_strlen (newdirname) + 1] = 0; |
| newdirname[grub_strlen (newdirname)] = '/'; |
| grub_strcpy (newdirname + grub_strlen (newdirname), filename); |
| grub_xnu_load_kext_from_dir (newdirname, ctx->osbundlerequired, |
| ctx->maxrecursion); |
| if (grub_errno == GRUB_ERR_BAD_OS) |
| grub_errno = GRUB_ERR_NONE; |
| grub_free (newdirname); |
| } |
| return 0; |
| } |
| |
| /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */ |
| grub_err_t |
| grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired, |
| int maxrecursion) |
| { |
| struct grub_xnu_scan_dir_for_kexts_ctx ctx = { |
| .dirname = dirname, |
| .osbundlerequired = osbundlerequired, |
| .maxrecursion = maxrecursion |
| }; |
| grub_device_t dev; |
| char *device_name; |
| grub_fs_t fs; |
| const char *path; |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| device_name = grub_file_get_device_name (dirname); |
| dev = grub_device_open (device_name); |
| if (dev) |
| { |
| fs = grub_fs_probe (dev); |
| path = grub_strchr (dirname, ')'); |
| if (! path) |
| path = dirname; |
| else |
| path++; |
| |
| if (fs) |
| (fs->dir) (dev, path, grub_xnu_scan_dir_for_kexts_load, &ctx); |
| grub_device_close (dev); |
| } |
| grub_free (device_name); |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Context for grub_xnu_load_kext_from_dir. */ |
| struct grub_xnu_load_kext_from_dir_ctx |
| { |
| char *dirname; |
| const char *osbundlerequired; |
| int maxrecursion; |
| char *plistname; |
| char *newdirname; |
| int usemacos; |
| }; |
| |
| /* Helper for grub_xnu_load_kext_from_dir. */ |
| static int |
| grub_xnu_load_kext_from_dir_load (const char *filename, |
| const struct grub_dirhook_info *info, |
| void *data) |
| { |
| struct grub_xnu_load_kext_from_dir_ctx *ctx = data; |
| |
| if (grub_strlen (filename) > 15) |
| return 0; |
| grub_strcpy (ctx->newdirname + grub_strlen (ctx->dirname) + 1, filename); |
| |
| /* If the kext contains directory "Contents" all real stuff is in |
| this directory. */ |
| if (info->dir && grub_strcasecmp (filename, "Contents") == 0) |
| grub_xnu_load_kext_from_dir (ctx->newdirname, ctx->osbundlerequired, |
| ctx->maxrecursion - 1); |
| |
| /* Directory "Plugins" contains nested kexts. */ |
| if (info->dir && grub_strcasecmp (filename, "Plugins") == 0) |
| grub_xnu_scan_dir_for_kexts (ctx->newdirname, ctx->osbundlerequired, |
| ctx->maxrecursion - 1); |
| |
| /* Directory "MacOS" contains executable, otherwise executable is |
| on the top. */ |
| if (info->dir && grub_strcasecmp (filename, "MacOS") == 0) |
| ctx->usemacos = 1; |
| |
| /* Info.plist is the file which governs our future actions. */ |
| if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0 |
| && ! ctx->plistname) |
| ctx->plistname = grub_strdup (ctx->newdirname); |
| return 0; |
| } |
| |
| /* Load extension DIRNAME. (extensions are directories in xnu) */ |
| grub_err_t |
| grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired, |
| int maxrecursion) |
| { |
| struct grub_xnu_load_kext_from_dir_ctx ctx = { |
| .dirname = dirname, |
| .osbundlerequired = osbundlerequired, |
| .maxrecursion = maxrecursion, |
| .plistname = 0, |
| .usemacos = 0 |
| }; |
| grub_device_t dev; |
| char *newpath; |
| char *device_name; |
| grub_fs_t fs; |
| const char *path; |
| char *binsuffix; |
| grub_file_t binfile; |
| |
| ctx.newdirname = grub_malloc (grub_strlen (dirname) + 20); |
| if (! ctx.newdirname) |
| return grub_errno; |
| grub_strcpy (ctx.newdirname, dirname); |
| ctx.newdirname[grub_strlen (dirname)] = '/'; |
| ctx.newdirname[grub_strlen (dirname) + 1] = 0; |
| device_name = grub_file_get_device_name (dirname); |
| dev = grub_device_open (device_name); |
| if (dev) |
| { |
| fs = grub_fs_probe (dev); |
| path = grub_strchr (dirname, ')'); |
| if (! path) |
| path = dirname; |
| else |
| path++; |
| |
| newpath = grub_strchr (ctx.newdirname, ')'); |
| if (! newpath) |
| newpath = ctx.newdirname; |
| else |
| newpath++; |
| |
| /* Look at the directory. */ |
| if (fs) |
| (fs->dir) (dev, path, grub_xnu_load_kext_from_dir_load, &ctx); |
| |
| if (ctx.plistname && grub_xnu_check_os_bundle_required |
| (ctx.plistname, osbundlerequired, &binsuffix)) |
| { |
| if (binsuffix) |
| { |
| /* Open the binary. */ |
| char *binname = grub_malloc (grub_strlen (dirname) |
| + grub_strlen (binsuffix) |
| + sizeof ("/MacOS/")); |
| grub_strcpy (binname, dirname); |
| if (ctx.usemacos) |
| grub_strcpy (binname + grub_strlen (binname), "/MacOS/"); |
| else |
| grub_strcpy (binname + grub_strlen (binname), "/"); |
| grub_strcpy (binname + grub_strlen (binname), binsuffix); |
| grub_dprintf ("xnu", "%s:%s\n", ctx.plistname, binname); |
| binfile = grub_file_open (binname, GRUB_FILE_TYPE_XNU_KEXT); |
| if (! binfile) |
| grub_errno = GRUB_ERR_NONE; |
| |
| /* Load the extension. */ |
| grub_xnu_load_driver (ctx.plistname, binfile, |
| binname); |
| grub_free (binname); |
| grub_free (binsuffix); |
| } |
| else |
| { |
| grub_dprintf ("xnu", "%s:0\n", ctx.plistname); |
| grub_xnu_load_driver (ctx.plistname, 0, 0); |
| } |
| } |
| grub_free (ctx.plistname); |
| grub_device_close (dev); |
| } |
| grub_free (device_name); |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| |
| static int locked=0; |
| static grub_dl_t my_mod; |
| |
| /* Load the kext. */ |
| static grub_err_t |
| grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| grub_file_t binfile = 0; |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| if (argc == 2) |
| { |
| /* User explicitly specified plist and binary. */ |
| if (grub_strcmp (args[1], "-") != 0) |
| { |
| binfile = grub_file_open (args[1], GRUB_FILE_TYPE_XNU_KEXT); |
| if (! binfile) |
| return grub_errno; |
| } |
| return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0, |
| binfile, args[1]); |
| } |
| |
| /* load kext normally. */ |
| if (argc == 1) |
| return grub_xnu_load_kext_from_dir (args[0], 0, 10); |
| |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| } |
| |
| /* Load a directory containing kexts. */ |
| static grub_err_t |
| grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| if (argc != 1 && argc != 2) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required"); |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| if (argc == 1) |
| return grub_xnu_scan_dir_for_kexts (args[0], |
| "console,root,local-root,network-root", |
| 10); |
| else |
| { |
| char *osbundlerequired = grub_strdup (args[1]), *ptr; |
| grub_err_t err; |
| if (! osbundlerequired) |
| return grub_errno; |
| for (ptr = osbundlerequired; *ptr; ptr++) |
| *ptr = grub_tolower (*ptr); |
| err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10); |
| grub_free (osbundlerequired); |
| return err; |
| } |
| } |
| |
| static inline int |
| hextoval (char c) |
| { |
| if (c >= '0' && c <= '9') |
| return c - '0'; |
| if (c >= 'a' && c <= 'z') |
| return c - 'a' + 10; |
| if (c >= 'A' && c <= 'Z') |
| return c - 'A' + 10; |
| return 0; |
| } |
| |
| static inline void |
| unescape (char *name, char *curdot, char *nextdot, int *len) |
| { |
| char *ptr, *dptr; |
| dptr = name; |
| for (ptr = curdot; ptr < nextdot;) |
| if (ptr + 2 < nextdot && *ptr == '%') |
| { |
| *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2])); |
| ptr += 3; |
| dptr++; |
| } |
| else |
| { |
| *dptr = *ptr; |
| ptr++; |
| dptr++; |
| } |
| *len = dptr - name; |
| } |
| |
| grub_err_t |
| grub_xnu_fill_devicetree (void) |
| { |
| struct grub_env_var *var; |
| FOR_SORTED_ENV (var) |
| { |
| char *nextdot = 0, *curdot; |
| struct grub_xnu_devtree_key **curkey = &grub_xnu_devtree_root; |
| struct grub_xnu_devtree_key *curvalue; |
| char *name = 0, *data; |
| int len; |
| |
| if (grub_memcmp (var->name, "XNU.DeviceTree.", |
| sizeof ("XNU.DeviceTree.") - 1) != 0) |
| continue; |
| |
| curdot = var->name + sizeof ("XNU.DeviceTree.") - 1; |
| nextdot = grub_strchr (curdot, '.'); |
| if (nextdot) |
| nextdot++; |
| while (nextdot) |
| { |
| name = grub_realloc (name, nextdot - curdot + 1); |
| |
| if (!name) |
| return grub_errno; |
| |
| unescape (name, curdot, nextdot, &len); |
| name[len - 1] = 0; |
| |
| curkey = &(grub_xnu_create_key (curkey, name)->first_child); |
| |
| curdot = nextdot; |
| nextdot = grub_strchr (nextdot, '.'); |
| if (nextdot) |
| nextdot++; |
| } |
| |
| nextdot = curdot + grub_strlen (curdot) + 1; |
| |
| name = grub_realloc (name, nextdot - curdot + 1); |
| |
| if (!name) |
| return grub_errno; |
| |
| unescape (name, curdot, nextdot, &len); |
| name[len] = 0; |
| |
| curvalue = grub_xnu_create_value (curkey, name); |
| if (!curvalue) |
| return grub_errno; |
| grub_free (name); |
| |
| data = grub_malloc (grub_strlen (var->value) + 1); |
| if (!data) |
| return grub_errno; |
| |
| unescape (data, var->value, var->value + grub_strlen (var->value), |
| &len); |
| curvalue->datasize = len; |
| curvalue->data = data; |
| } |
| |
| return grub_errno; |
| } |
| |
| struct grub_video_bitmap *grub_xnu_bitmap = 0; |
| grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode; |
| |
| /* Option array indices. */ |
| #define XNU_SPLASH_CMD_ARGINDEX_MODE 0 |
| |
| static const struct grub_arg_option xnu_splash_cmd_options[] = |
| { |
| {"mode", 'm', 0, N_("Background image mode."), N_("stretch|normal"), |
| ARG_TYPE_STRING}, |
| {0, 0, 0, 0, 0, 0} |
| }; |
| |
| static grub_err_t |
| grub_cmd_xnu_splash (grub_extcmd_context_t ctxt, |
| int argc, char *args[]) |
| { |
| grub_err_t err; |
| if (argc != 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| if (! grub_xnu_heap_size) |
| return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first")); |
| |
| if (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set && |
| grub_strcmp (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg, |
| "stretch") == 0) |
| grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH; |
| else |
| grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER; |
| |
| err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]); |
| if (err) |
| grub_xnu_bitmap = 0; |
| |
| return err; |
| } |
| |
| |
| #ifndef GRUB_MACHINE_EMU |
| static grub_err_t |
| grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| if (argc != 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| return grub_xnu_resume (args[0]); |
| } |
| #endif |
| |
| void |
| grub_xnu_lock (void) |
| { |
| if (!locked) |
| grub_dl_ref (my_mod); |
| locked = 1; |
| } |
| |
| void |
| grub_xnu_unlock (void) |
| { |
| if (locked) |
| grub_dl_unref (my_mod); |
| locked = 0; |
| } |
| |
| static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext; |
| static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume; |
| static grub_extcmd_t cmd_splash; |
| |
| GRUB_MOD_INIT(xnu) |
| { |
| if (grub_efi_secure_boot()) |
| return; |
| |
| cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0, |
| N_("Load XNU image.")); |
| cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64, |
| 0, N_("Load 64-bit XNU image.")); |
| cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0, |
| N_("Load XNU extension package.")); |
| cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0, |
| N_("Load XNU extension.")); |
| cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir, |
| /* TRANSLATORS: OSBundleRequired is a |
| variable name in xnu extensions |
| manifests. It behaves mostly like |
| GNU/Linux runlevels. |
| */ |
| N_("DIRECTORY [OSBundleRequired]"), |
| /* TRANSLATORS: There are many extensions |
| in extension directory. */ |
| N_("Load XNU extension directory.")); |
| cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0, |
| /* TRANSLATORS: ramdisk here isn't identifier. It can be translated. */ |
| N_("Load XNU ramdisk. " |
| "It will be available in OS as md0.")); |
| cmd_splash = grub_register_extcmd ("xnu_splash", |
| grub_cmd_xnu_splash, 0, 0, |
| N_("Load a splash image for XNU."), |
| xnu_splash_cmd_options); |
| |
| #ifndef GRUB_MACHINE_EMU |
| cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume, |
| 0, N_("Load an image of hibernated" |
| " XNU.")); |
| #endif |
| |
| grub_cpu_xnu_init (); |
| |
| my_mod = mod; |
| } |
| |
| GRUB_MOD_FINI(xnu) |
| { |
| if (grub_efi_secure_boot()) |
| return; |
| |
| #ifndef GRUB_MACHINE_EMU |
| grub_unregister_command (cmd_resume); |
| #endif |
| grub_unregister_command (cmd_mkext); |
| grub_unregister_command (cmd_kext); |
| grub_unregister_command (cmd_kextdir); |
| grub_unregister_command (cmd_ramdisk); |
| grub_unregister_command (cmd_kernel); |
| grub_unregister_extcmd (cmd_splash); |
| grub_unregister_command (cmd_kernel64); |
| |
| grub_cpu_xnu_fini (); |
| } |