diff --git a/ChangeLog b/ChangeLog index 542a8c611..dbdf4a1ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2012-02-27 Vladimir Serbinenko + + Support v2 xnu boot arguments. + + * grub-core/loader/i386/xnu.c (grub_cpu_xnu_fill_devicetree): + New argument fsbfreq_out. + (grub_xnu_set_video): Receive an argument grub_xnu_boot_params_common. + (grub_xnu_boot): Support v2 arguments. Disable PIC so that APIC can + be used. + * grub-core/loader/machoXX.c (grub_macho_load): New argument + darwin_version. + * grub-core/loader/xnu.c (grub_xnu_darwin_version): New variable. + * include/grub/i386/xnu.h (grub_xnu_boot_params_common): New struct. + (grub_xnu_boot_params): Rename to ... + (grub_xnu_boot_params_v1): ...this. Use grub_xnu_boot_params_common. + (grub_xnu_boot_params_v2): New struct. + 2012-02-27 Vladimir Serbinenko * grub-core/efiemu/prepare.c (grub_efiemu_crc): Add missing diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c index a94b6a48e..6bc090b94 100644 --- a/grub-core/loader/i386/xnu.c +++ b/grub-core/loader/i386/xnu.c @@ -33,6 +33,7 @@ #include #include #include +#include #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) @@ -623,8 +624,8 @@ grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)), /* Fill device tree. */ /* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */ -grub_err_t -grub_cpu_xnu_fill_devicetree (void) +static grub_err_t +grub_cpu_xnu_fill_devicetree (grub_uint64_t *fsbfreq_out) { struct grub_xnu_devtree_key *efikey; struct grub_xnu_devtree_key *cfgtablekey; @@ -702,13 +703,14 @@ grub_cpu_xnu_fill_devicetree (void) /* First see if user supplies the value. */ const char *fsbvar = grub_env_get ("fsb"); - if (! fsbvar) - *((grub_uint64_t *) curval->data) = 0; - else - *((grub_uint64_t *) curval->data) = readfrequency (fsbvar); + grub_uint64_t fsbfreq = 0; + if (fsbvar) + fsbfreq = readfrequency (fsbvar); /* Try autodetect. */ - if (! *((grub_uint64_t *) curval->data)) - *((grub_uint64_t *) curval->data) = guessfsb (); + if (! fsbfreq) + fsbfreq = guessfsb (); + *((grub_uint64_t *) curval->data) = fsbfreq; + *fsbfreq_out = fsbfreq; grub_dprintf ("xnu", "fsb autodetected as %llu\n", (unsigned long long) *((grub_uint64_t *) curval->data)); @@ -845,7 +847,7 @@ grub_xnu_boot_resume (void) /* Setup video for xnu. */ static grub_err_t -grub_xnu_set_video (struct grub_xnu_boot_params *params) +grub_xnu_set_video (struct grub_xnu_boot_params_common *params) { struct grub_video_mode_info mode_info; char *tmp; @@ -950,7 +952,8 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) grub_err_t grub_xnu_boot (void) { - struct grub_xnu_boot_params *bootparams; + union grub_xnu_boot_params_any *bootparams; + struct grub_xnu_boot_params_common *bootparams_common; void *bp_in; grub_addr_t bootparams_target; grub_err_t err; @@ -966,6 +969,9 @@ grub_xnu_boot (void) grub_size_t devtreelen; int i; struct grub_relocator32_state state; + grub_uint64_t fsbfreq; + int v2 = (grub_xnu_darwin_version >= 11); + grub_uint32_t efi_system_table = 0; err = grub_autoefi_prepare (); if (err) @@ -975,7 +981,7 @@ grub_xnu_boot (void) if (err) return err; - err = grub_cpu_xnu_fill_devicetree (); + err = grub_cpu_xnu_fill_devicetree (&fsbfreq); if (err) return err; @@ -995,7 +1001,8 @@ grub_xnu_boot (void) descriptor_size = 0; descriptor_version = 0; - grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point); + grub_dprintf ("xnu", "eip=%x, efi=%x\n", grub_xnu_entry_point, + (int) grub_autoefi_system_table); const char *debug = grub_env_get ("debug"); @@ -1012,20 +1019,29 @@ grub_xnu_boot (void) return err; bootparams = bp_in; + grub_memset (bootparams, 0, sizeof (*bootparams)); + if (v2) + { + bootparams_common = &bootparams->v2.common; + bootparams->v2.fsbfreq = fsbfreq; + } + else + bootparams_common = &bootparams->v1.common; + /* Set video. */ - err = grub_xnu_set_video (bootparams); + err = grub_xnu_set_video (bootparams_common); if (err != GRUB_ERR_NONE) { grub_print_error (); grub_errno = GRUB_ERR_NONE; grub_puts_ (N_("Booting in blind mode")); - bootparams->lfb_mode = 0; - bootparams->lfb_width = 0; - bootparams->lfb_height = 0; - bootparams->lfb_depth = 0; - bootparams->lfb_line_len = 0; - bootparams->lfb_base = 0; + bootparams_common->lfb_mode = 0; + bootparams_common->lfb_width = 0; + bootparams_common->lfb_height = 0; + bootparams_common->lfb_depth = 0; + bootparams_common->lfb_line_len = 0; + bootparams_common->lfb_base = 0; } if (grub_autoefi_get_memory_map (&memory_map_size, memory_map, @@ -1045,11 +1061,11 @@ grub_xnu_boot (void) if (err) return err; - grub_memcpy (bootparams->cmdline, grub_xnu_cmdline, - sizeof (bootparams->cmdline)); + grub_memcpy (bootparams_common->cmdline, grub_xnu_cmdline, + sizeof (bootparams_common->cmdline)); - bootparams->devtree = devtree_target; - bootparams->devtreelen = devtreelen; + bootparams_common->devtree = devtree_target; + bootparams_common->devtreelen = devtreelen; err = grub_autoefi_finish_boot_services (&memory_map_size, memory_map, &map_key, &descriptor_size, @@ -1057,7 +1073,10 @@ grub_xnu_boot (void) if (err) return err; - bootparams->efi_system_table = (grub_addr_t) grub_autoefi_system_table; + if (v2) + bootparams->v2.efi_system_table = (grub_addr_t) grub_autoefi_system_table; + else + bootparams->v1.efi_system_table = (grub_addr_t) grub_autoefi_system_table; firstruntimepage = (((grub_addr_t) grub_xnu_heap_target_start + grub_xnu_heap_size + GRUB_XNU_PAGESIZE - 1) @@ -1080,7 +1099,7 @@ grub_xnu_boot (void) <= (grub_addr_t) grub_autoefi_system_table && curdesc->physical_start + (curdesc->num_pages << 12) > (grub_addr_t) grub_autoefi_system_table) - bootparams->efi_system_table + efi_system_table = (grub_addr_t) grub_autoefi_system_table - curdesc->physical_start + curdesc->virtual_start; if (SIZEOF_OF_UINTN == 8 && grub_xnu_is_64bit) @@ -1090,24 +1109,33 @@ grub_xnu_boot (void) lastruntimepage = curruntimepage; - bootparams->efi_mmap = memory_map_target; - bootparams->efi_mmap_size = memory_map_size; - bootparams->efi_mem_desc_size = descriptor_size; - bootparams->efi_mem_desc_version = descriptor_version; + if (v2) + { + bootparams->v2.efi_uintnbits = SIZEOF_OF_UINTN * 8; + bootparams->v2.verminor = GRUB_XNU_BOOTARGSV2_VERMINOR; + bootparams->v2.vermajor = GRUB_XNU_BOOTARGSV2_VERMAJOR; + bootparams->v2.efi_system_table = efi_system_table; + } + else + { + bootparams->v1.efi_uintnbits = SIZEOF_OF_UINTN * 8; + bootparams->v1.verminor = GRUB_XNU_BOOTARGSV1_VERMINOR; + bootparams->v1.vermajor = GRUB_XNU_BOOTARGSV1_VERMAJOR; + bootparams->v1.efi_system_table = efi_system_table; + } - bootparams->heap_start = grub_xnu_heap_target_start; - bootparams->heap_size = grub_xnu_heap_size; - bootparams->efi_runtime_first_page = firstruntimepage; - - bootparams->efi_runtime_npages = lastruntimepage - firstruntimepage; - bootparams->efi_uintnbits = SIZEOF_OF_UINTN * 8; - - bootparams->verminor = GRUB_XNU_BOOTARGS_VERMINOR; - bootparams->vermajor = GRUB_XNU_BOOTARGS_VERMAJOR; + bootparams_common->efi_runtime_first_page = firstruntimepage; + bootparams_common->efi_runtime_npages = lastruntimepage - firstruntimepage; + bootparams_common->efi_mem_desc_size = descriptor_size; + bootparams_common->efi_mem_desc_version = descriptor_version; + bootparams_common->efi_mmap = memory_map_target; + bootparams_common->efi_mmap_size = memory_map_size; + bootparams_common->heap_start = grub_xnu_heap_target_start; + bootparams_common->heap_size = grub_xnu_heap_size; /* Parameters for asm helper. */ - grub_xnu_stack = bootparams->heap_start - + bootparams->heap_size + GRUB_XNU_PAGESIZE; + grub_xnu_stack = bootparams_common->heap_start + + bootparams_common->heap_size + GRUB_XNU_PAGESIZE; grub_xnu_arg1 = bootparams_target; grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size, @@ -1117,6 +1145,11 @@ grub_xnu_boot (void) state.eax = grub_xnu_arg1; state.esp = grub_xnu_stack; state.ebp = grub_xnu_stack; + + /* XNU uses only APIC. Disable PIC. */ + grub_outb (0xff, 0x21); + grub_outb (0xff, 0xa1); + return grub_relocator32_boot (grub_xnu_relocator, state); } diff --git a/grub-core/loader/machoXX.c b/grub-core/loader/machoXX.c index 58770ddf8..313c5baa7 100644 --- a/grub-core/loader/machoXX.c +++ b/grub-core/loader/machoXX.c @@ -164,7 +164,7 @@ SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start, /* Load every loadable segment into memory specified by `_load_hook'. */ grub_err_t SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename, - char *offset, int flags) + char *offset, int flags, int *darwin_version) { auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho, struct grub_macho_cmd *hdr0, @@ -201,6 +201,23 @@ SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename, return 1; } + if (darwin_version) + { + const char *ptr = offset + hdr->vmaddr; + const char *end = ptr + min (hdr->filesize, hdr->vmsize) + - (sizeof ("Darwin Kernel Version ") - 1); + for (; ptr < end; ptr++) + if (grub_memcmp (ptr, "Darwin Kernel Version ", + sizeof ("Darwin Kernel Version ") - 1) == 0) + { + ptr += sizeof ("Darwin Kernel Version ") - 1; + *darwin_version = 0; + end += (sizeof ("Darwin Kernel Version ") - 1); + while (ptr < end && grub_isdigit (*ptr)) + *darwin_version = (*ptr++ - '0') + *darwin_version * 10; + break; + } + } } if (hdr->filesize < hdr->vmsize) @@ -209,6 +226,9 @@ SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename, return 0; } + if (darwin_version) + *darwin_version = 0; + grub_macho_cmds_iterate (macho, do_load, 0); return grub_errno; diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c index 80dd1984e..88c17a582 100644 --- a/grub-core/loader/xnu.c +++ b/grub-core/loader/xnu.c @@ -44,6 +44,7 @@ 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; @@ -387,7 +388,7 @@ grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)), /* Load kernel. */ err = grub_macho_load32 (macho, args[0], (char *) loadaddr - startcode, - GRUB_MACHO_NOBSS); + GRUB_MACHO_NOBSS, &grub_xnu_darwin_version); if (err) { grub_macho_close (macho); @@ -500,7 +501,7 @@ grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)), /* Load kernel. */ err = grub_macho_load64 (macho, args[0], (char *) loadaddr - startcode, - GRUB_MACHO_NOBSS); + GRUB_MACHO_NOBSS, &grub_xnu_darwin_version); if (err) { grub_macho_close (macho); diff --git a/include/grub/i386/xnu.h b/include/grub/i386/xnu.h index 386c8b9e0..12b1e60bc 100644 --- a/include/grub/i386/xnu.h +++ b/include/grub/i386/xnu.h @@ -28,10 +28,8 @@ #define GRUB_XNU_PAGESIZE 4096 typedef grub_uint32_t grub_xnu_ptr_t; -struct grub_xnu_boot_params +struct grub_xnu_boot_params_common { - grub_uint16_t verminor; - grub_uint16_t vermajor; /* Command line passed to xnu. */ grub_uint8_t cmdline[1024]; @@ -59,17 +57,50 @@ struct grub_xnu_boot_params grub_xnu_ptr_t heap_start; /* Last used address by kernel or boot structures minus previous value. */ grub_uint32_t heap_size; - /* First memory page containing runtime code or data. */ grub_uint32_t efi_runtime_first_page; /* First memory page containing runtime code or data minus previous value. */ grub_uint32_t efi_runtime_npages; +} __attribute__ ((packed)); + +struct grub_xnu_boot_params_v1 +{ + grub_uint16_t verminor; + grub_uint16_t vermajor; + struct grub_xnu_boot_params_common common; + grub_uint32_t efi_system_table; /* Size of grub_efi_uintn_t in bits. */ grub_uint8_t efi_uintnbits; } __attribute__ ((packed)); -#define GRUB_XNU_BOOTARGS_VERMINOR 5 -#define GRUB_XNU_BOOTARGS_VERMAJOR 1 +#define GRUB_XNU_BOOTARGSV1_VERMINOR 5 +#define GRUB_XNU_BOOTARGSV1_VERMAJOR 1 + +struct grub_xnu_boot_params_v2 +{ + grub_uint16_t verminor; + grub_uint16_t vermajor; + + /* Size of grub_efi_uintn_t in bits. */ + grub_uint8_t efi_uintnbits; + grub_uint8_t unused[3]; + + struct grub_xnu_boot_params_common common; + + grub_uint64_t efi_runtime_first_page_virtual; + grub_uint32_t efi_system_table; + grub_uint32_t unused2[11]; + grub_uint64_t fsbfreq; + grub_uint32_t unused3[734]; +} __attribute__ ((packed)); +#define GRUB_XNU_BOOTARGSV2_VERMINOR 0 +#define GRUB_XNU_BOOTARGSV2_VERMAJOR 2 + +union grub_xnu_boot_params_any +{ + struct grub_xnu_boot_params_v1 v1; + struct grub_xnu_boot_params_v2 v2; +}; struct grub_xnu_devprop_header { @@ -114,5 +145,4 @@ extern grub_uint32_t grub_xnu_stack; extern grub_uint32_t grub_xnu_arg1; extern char grub_xnu_cmdline[1024]; grub_err_t grub_xnu_boot (void); -grub_err_t grub_cpu_xnu_fill_devicetree (void); #endif diff --git a/include/grub/machoload.h b/include/grub/machoload.h index 9ad509b6e..bb0374c88 100644 --- a/include/grub/machoload.h +++ b/include/grub/machoload.h @@ -58,9 +58,9 @@ grub_uint64_t grub_macho_get_entry_point64 (grub_macho_t macho); /* Ignore BSS segments when loading. */ #define GRUB_MACHO_NOBSS 0x1 grub_err_t grub_macho_load32 (grub_macho_t macho, const char *filename, - char *offset, int flags); + char *offset, int flags, int *darwin_version); grub_err_t grub_macho_load64 (grub_macho_t macho, const char *filename, - char *offset, int flags); + char *offset, int flags, int *darwin_version); /* Like filesize and file_read but take only 32-bit part for current architecture. */ diff --git a/include/grub/xnu.h b/include/grub/xnu.h index 2a096d803..9b17717a9 100644 --- a/include/grub/xnu.h +++ b/include/grub/xnu.h @@ -115,4 +115,5 @@ typedef enum {GRUB_XNU_BITMAP_CENTER, GRUB_XNU_BITMAP_STRETCH} extern grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode; extern int grub_xnu_is_64bit; extern grub_addr_t grub_xnu_heap_target_start; +extern int grub_xnu_darwin_version; #endif