On Wed, Feb 4, 2015 at 7:25 PM, Dave Young <dyoung@xxxxxxxxxx> wrote: >> After this patch, could use patched grub2-x86_64.efi to place >> kernel/boot_params/cmd_line/initrd all above 4G and execute the kernel >> above 4G. > > Can you share the grub2 patch for testing? Please check attached 5 patches. last one is for debug purpose. You need to apply them on top of git://git.savannah.gnu.org/grub.git plus http://pkgs.fedoraproject.org/cgit/grub2.git/plain/0091-Add-support-for-linuxefi.patch
Subject: [PATCH] update setup_header and boot_params to 2.12 the one that is used by kernel v3.8... --- include/grub/i386/linux.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) Index: grub/include/grub/i386/linux.h =================================================================== --- grub.orig/include/grub/i386/linux.h +++ grub/include/grub/i386/linux.h @@ -130,7 +130,7 @@ struct linux_kernel_header grub_uint32_t kernel_alignment; grub_uint8_t relocatable; grub_uint8_t min_alignment; - grub_uint8_t pad[2]; + grub_uint16_t xloadflags; grub_uint32_t cmdline_size; grub_uint32_t hardware_subarch; grub_uint64_t hardware_subarch_data; @@ -221,7 +221,11 @@ struct linux_kernel_params grub_uint32_t ofw_cif_handler; /* b8 */ grub_uint32_t ofw_idt; /* bc */ - grub_uint8_t padding7[0x1b8 - 0xc0]; + grub_uint32_t ext_ramdisk_image; /* c0 */ + grub_uint32_t ext_ramdisk_size; /* c4 */ + grub_uint32_t ext_cmd_line_ptr; /* c8 */ + + grub_uint8_t padding7[0x1b8 - 0xcc]; union { @@ -300,14 +304,18 @@ struct linux_kernel_params grub_uint32_t initrd_addr_max; /* Maximum initrd address */ grub_uint32_t kernel_alignment; /* Alignment of the kernel */ grub_uint8_t relocatable_kernel; /* Is the kernel relocatable */ - grub_uint8_t pad1[3]; + grub_uint8_t min_alignment; + grub_uint16_t xloadflags; grub_uint32_t cmdline_size; /* Size of the kernel command line */ grub_uint32_t hardware_subarch; grub_uint64_t hardware_subarch_data; grub_uint32_t payload_offset; grub_uint32_t payload_length; grub_uint64_t setup_data; - grub_uint8_t pad2[120]; /* 258 */ + grub_uint64_t pref_address; + grub_uint32_t init_size; + grub_uint32_t handover_offset; + grub_uint8_t pad2[104]; /* 248 */ struct grub_e820_mmap e820_map[(0x400 - 0x2d0) / 20]; /* 2d0 */ } GRUB_PACKED;
Subject: [PATCH] x86, efi: add allocate_pages_high The allocate_pages_max one does not return above 4G. translate the version in linux kernel. --- grub-core/kern/efi/mm.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ include/grub/efi/efi.h | 3 + 2 files changed, 147 insertions(+) Index: grub/grub-core/kern/efi/mm.c =================================================================== --- grub.orig/grub-core/kern/efi/mm.c +++ grub/grub-core/kern/efi/mm.c @@ -557,3 +557,147 @@ grub_efi_mm_init (void) grub_efi_free_pages ((grub_addr_t) memory_map, 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); } + +#define EFI_PAGE_SIZE 0x1000 +static grub_efi_status_t grub2_efi_get_memory_map(grub_efi_uintn_t *map_size, + grub_efi_memory_descriptor_t **map, + grub_efi_uintn_t *key_ptr, + grub_efi_uintn_t *desc_size, + grub_efi_uint32_t *desc_ver) +{ + grub_efi_memory_descriptor_t *m = NULL; + grub_efi_status_t status; + grub_efi_uintn_t key; + grub_efi_uint32_t desc_version; + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + + *map_size = 0; + *desc_size = 0; + key = 0; + status = efi_call_5 (b->get_memory_map, map_size, NULL, &key, + desc_size, &desc_version); + + if (status != GRUB_EFI_BUFFER_TOO_SMALL) + return GRUB_EFI_LOAD_ERROR; + + /* + * Add an additional efi_memory_desc_t because we're doing an + * allocation which may be in a new descriptor region. + */ + *map_size += *desc_size; + status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA, + *map_size, (void **)&m); + if (status != GRUB_EFI_SUCCESS) + goto fail; + + status = efi_call_5 (b->get_memory_map, map_size, m, &key, + desc_size, &desc_version); + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + efi_call_1 (b->free_pool, m); + return GRUB_EFI_LOAD_ERROR; + } + + if (status != GRUB_EFI_SUCCESS) + efi_call_1 (b->free_pool, m); + + if (key_ptr && status == GRUB_EFI_SUCCESS) + *key_ptr = key; + if (desc_ver && status == GRUB_EFI_SUCCESS) + *desc_ver = desc_version; + +fail: + *map = m; + return status; +} + +/* + * Allocate at the highest possible address that is not above 'max'. + */ +void *grub2_efi_allocate_pages_high(grub_efi_physical_address_t max, + grub_efi_uint64_t pages, grub_efi_uint64_t align) +{ + grub_efi_boot_services_t *b; + grub_efi_uintn_t map_size, desc_size; + grub_efi_memory_descriptor_t *map= NULL; + grub_efi_status_t status; + grub_uint64_t max_addr = 0; + grub_uint64_t addr = 0; + grub_efi_uintn_t i; + grub_uint64_t size = PAGES_TO_BYTES(pages); + + /* Obtain descriptors for available memory. */ + status = grub2_efi_get_memory_map (&map_size, &map, 0, &desc_size, 0); + if (status != GRUB_EFI_SUCCESS) + goto fail; + + if (align < EFI_PAGE_SIZE) + align = EFI_PAGE_SIZE; + + b = grub_efi_system_table->boot_services; + +again: + for (i = 0; i < map_size / desc_size; i++) + { + grub_efi_memory_descriptor_t *desc; + unsigned long m = (unsigned long)map; + grub_uint64_t start, end; + + desc = (grub_efi_memory_descriptor_t *)(m + (i * desc_size)); + if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY) + continue; + + if (desc->num_pages < pages) + continue; + + start = desc->physical_start; + end = start + PAGES_TO_BYTES(desc->num_pages); + + if ((start + size) > end || (start + size) > max) + continue; + + if (end - size > max) + end = max; + + if ((((end - size)/align)*align) < start) + continue; + + start = (((end - size)/align)*align); + + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. + */ + if (start == 0x0) + continue; + + if (start > max_addr) + max_addr = start; + } + + if (!max_addr) + addr = 0; + else + { + status = efi_call_4 (b->allocate_pages, + GRUB_EFI_ALLOCATE_ADDRESS, GRUB_EFI_LOADER_DATA, + pages, &max_addr); + if (status != GRUB_EFI_SUCCESS) + { + max = max_addr; + max_addr = 0; + goto again; + } + + addr = max_addr; + } + + /* Release the memory maps. */ + efi_call_1 (b->free_pool, map); + +fail: + return (void *)((grub_addr_t) addr); +} + Index: grub/include/grub/efi/efi.h =================================================================== --- grub.orig/include/grub/efi/efi.h +++ grub/include/grub/efi/efi.h @@ -43,6 +43,9 @@ EXPORT_FUNC(grub_efi_allocate_pages) (gr void * EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max, grub_efi_uintn_t pages); +void * +EXPORT_FUNC(grub2_efi_allocate_pages_high) (grub_efi_physical_address_t max, + grub_efi_uint64_t pages, grub_efi_uint64_t align); void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); int
Subject: [PATCH] x86, efi: make initrd load high if it is supported for kernel after v3.9 and later. --- grub-core/loader/i386/efi/linux.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) Index: grub/grub-core/loader/i386/efi/linux.c =================================================================== --- grub.orig/grub-core/loader/i386/efi/linux.c +++ grub/grub-core/loader/i386/efi/linux.c @@ -108,6 +108,8 @@ static grub_err_t grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { + grub_uint64_t addr_max = 0x3fffffff; + int load_high = 0; grub_file_t *files = 0; int i, nfiles = 0; grub_size_t size = 0; @@ -139,7 +141,14 @@ grub_cmd_initrd (grub_command_t cmd __at size += ALIGN_UP (grub_file_size (files[i]), 4); } - initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); + if (params->version > grub_cpu_to_le16 (0x020b) && + params->xloadflags & (1<<1)) /* XLF_CAN_BE_LOADED_ABOVE_4G */ + { + addr_max = -1UL; + load_high = 1; + } + + initrd_mem = grub2_efi_allocate_pages_high (addr_max, BYTES_TO_PAGES(size), 4096); if (!initrd_mem) { @@ -147,8 +156,13 @@ grub_cmd_initrd (grub_command_t cmd __at goto fail; } - params->ramdisk_size = size; + params->ramdisk_size = (grub_uint32_t) size; params->ramdisk_image = (grub_uint32_t)(grub_uint64_t) initrd_mem; + if ( load_high ) + { + params->ext_ramdisk_image = (grub_uint64_t) initrd_mem >> 32; + params->ext_ramdisk_size = size >> 32; + } ptr = initrd_mem; @@ -167,8 +181,6 @@ grub_cmd_initrd (grub_command_t cmd __at ptr += ALIGN_UP_OVERHEAD (cursize, 4); } - params->ramdisk_size = size; - fail: for (i = 0; i < nfiles; i++) grub_file_close (files[i]);
Subject: [PATCH] x86, efi: make kernel/boot_params/cmdline could be above 4G if supported for kernel that support ext_code32_start. Also reduce kernel reading to one time only. --- grub-core/loader/i386/efi/linux.c | 146 +++++++++++++++++++++++--------------- include/grub/i386/linux.h | 4 - 2 files changed, 93 insertions(+), 57 deletions(-) Index: grub/grub-core/loader/i386/efi/linux.c =================================================================== --- grub.orig/grub-core/loader/i386/efi/linux.c +++ grub/grub-core/loader/i386/efi/linux.c @@ -192,14 +192,27 @@ grub_cmd_initrd (grub_command_t cmd __at return grub_errno; } +static void copy_setup_header(unsigned char *param, unsigned char *h) +{ + unsigned long setup_header_size = h[0x201] + 0x202 - 0x1f1; + + /* only copy setup_header */ + if (setup_header_size > 0x7f) + setup_header_size = 0x7f; + memcpy(param + 0x1f1, h + 0x1f1, setup_header_size); +} + static grub_err_t grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { + grub_uint64_t addr_max = 0x3fffffff; + int load_high = 0; grub_file_t file = 0; struct linux_kernel_header lh; - grub_ssize_t len, start, filelen; - void *kernel; + grub_ssize_t start, filelen; + void *kernel = NULL; + int kernel_high = 0; grub_dl_ref (my_mod); @@ -213,77 +226,96 @@ grub_cmd_linux (grub_command_t cmd __att if (! file) goto fail; - filelen = grub_file_size (file); + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } - kernel = grub_malloc(filelen); + if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) + { + grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); + goto fail; + } - if (!kernel) + if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); goto fail; } - if (grub_file_read (file, kernel, filelen) != filelen) + if (lh.version < grub_cpu_to_le16 (0x020b)) { - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); + grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); goto fail; } - if (! grub_linuxefi_secure_validate (kernel, filelen)) + if (!lh.handover_offset) { - grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); - grub_free (kernel); + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); goto fail; } - grub_file_seek (file, 0); + if (lh.version > grub_cpu_to_le16 (0x020d) && + lh.xloadflags & (1<<1)) /* XLF_CAN_BE_LOADED_ABOVE_4G */ + { + addr_max = -1UL; + load_high = 1; + } - grub_free(kernel); + filelen = grub_file_size (file); - params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); + kernel = grub_malloc(filelen); - if (! params) + if (!kernel && !load_high) { - grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); goto fail; } - memset (params, 0, 16384); - - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + if (!kernel) { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; + kernel = grub2_efi_allocate_pages_high (addr_max, BYTES_TO_PAGES(filelen), 4096); + kernel_high = 1; } - if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) + if (!kernel) { - grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); goto fail; } - if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + grub_file_seek (file, 0); + if (grub_file_read (file, kernel, filelen) != filelen) { - grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); goto fail; } + grub_file_close(file); + file = 0; - if (lh.version < grub_cpu_to_le16 (0x020b)) + if (! grub_linuxefi_secure_validate (kernel, filelen)) { - grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); + grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); goto fail; } - if (!lh.handover_offset) + params = grub2_efi_allocate_pages_high (addr_max, BYTES_TO_PAGES(16384), 4096); + + if (! params) { - grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); goto fail; } - linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, - BYTES_TO_PAGES(lh.cmdline_size + 1)); + memset (params, 0, 16384); + copy_setup_header((unsigned char *) params, (unsigned char *) &lh); + params->type_of_loader = 0x21; + + linux_cmdline = grub2_efi_allocate_pages_high (addr_max, + BYTES_TO_PAGES(lh.cmdline_size + 1), 4096); if (!linux_cmdline) { @@ -296,19 +328,20 @@ grub_cmd_linux (grub_command_t cmd __att linux_cmdline + sizeof (LINUX_IMAGE) - 1, lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1)); - lh.cmd_line_ptr = (grub_uint32_t)(grub_uint64_t)linux_cmdline; + params->cmd_line_ptr = (grub_uint32_t)(grub_uint64_t) linux_cmdline; + if ( load_high ) + params->ext_cmd_line_ptr = (grub_uint64_t) linux_cmdline >> 32; handover_offset = lh.handover_offset; - start = (lh.setup_sects + 1) * 512; - len = grub_file_size(file) - start; - - kernel_mem = grub_efi_allocate_pages(lh.pref_address, + kernel_mem = NULL; + if ( !load_high ) + kernel_mem = grub_efi_allocate_pages(lh.pref_address, BYTES_TO_PAGES(lh.init_size)); if (!kernel_mem) - kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, - BYTES_TO_PAGES(lh.init_size)); + kernel_mem = grub2_efi_allocate_pages_high (addr_max, + BYTES_TO_PAGES(lh.init_size), lh.kernel_alignment); if (!kernel_mem) { @@ -316,31 +349,32 @@ grub_cmd_linux (grub_command_t cmd __att goto fail; } - if (grub_file_seek (file, start) == (grub_off_t) -1) - { - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; - } + kernel_size = lh.init_size; - if (grub_file_read (file, kernel_mem, len) != len && !grub_errno) - { - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - } + start = (lh.setup_sects + 1) * 512; + memcpy(kernel_mem, (unsigned char *)kernel + start, filelen - start); if (grub_errno == GRUB_ERR_NONE) { grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); loaded = 1; - lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; + params->code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; + if ( load_high ) + { + params->ext_code32_start = (grub_uint64_t) kernel_mem >> 32; + /* don't not relocate down in kernel eboot.c::efi_main() */ + params->pref_address = (grub_uint64_t) kernel_mem; + } } - memcpy(params, &lh, 2 * 512); - - params->type_of_loader = 0x21; - fail: + if (kernel) + { + if (!kernel_high) + grub_free(kernel); + else + grub_efi_free_pages((grub_efi_physical_address_t)kernel, BYTES_TO_PAGES(filelen)); + } if (file) grub_file_close (file); Index: grub/include/grub/i386/linux.h =================================================================== --- grub.orig/include/grub/i386/linux.h +++ grub/include/grub/i386/linux.h @@ -140,6 +140,7 @@ struct linux_kernel_header grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; + grub_uint32_t ext_code32_start; } GRUB_PACKED; /* Boot parameters for Linux based on 2.6.12. This is used by the setup @@ -315,7 +316,8 @@ struct linux_kernel_params grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; - grub_uint8_t pad2[104]; /* 248 */ + grub_uint32_t ext_code32_start; + grub_uint8_t pad2[100]; /* 24c */ struct grub_e820_mmap e820_map[(0x400 - 0x2d0) / 20]; /* 2d0 */ } GRUB_PACKED;
Subject: [PATCH] x86, efi: enable debug print out for debug purpose --- grub-core/loader/i386/efi/linux.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) Index: grub/grub-core/loader/i386/efi/linux.c =================================================================== --- grub.orig/grub-core/loader/i386/efi/linux.c +++ grub/grub-core/loader/i386/efi/linux.c @@ -29,6 +29,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); +#define DEBUG_PRINT 1 + static grub_dl_t my_mod; static int loaded; static void *kernel_mem; @@ -166,6 +168,12 @@ grub_cmd_initrd (grub_command_t cmd __at ptr = initrd_mem; +#ifdef DEBUG_PRINT + /* before initrd read */ + grub_printf("initrd: [%lx,%lx]\n", + (unsigned long)initrd_mem, (unsigned long)initrd_mem + size - 1); +#endif + for (i = 0; i < nfiles; i++) { grub_ssize_t cursize = grub_file_size (files[i]); @@ -181,6 +189,11 @@ grub_cmd_initrd (grub_command_t cmd __at ptr += ALIGN_UP_OVERHEAD (cursize, 4); } +#ifdef DEBUG_PRINT + /* after initrd read */ + grub_printf("initrd: read %d file%sdone\n", nfiles, nfiles==1 ? " ": "s "); +#endif + fail: for (i = 0; i < nfiles; i++) grub_file_close (files[i]); @@ -296,6 +309,11 @@ grub_cmd_linux (grub_command_t cmd __att grub_file_close(file); file = 0; +#ifdef DEBUG_PRINT + /* after kernel read */ + grub_printf("kernel: read done\n"); +#endif + if (! grub_linuxefi_secure_validate (kernel, filelen)) { grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); @@ -314,6 +332,12 @@ grub_cmd_linux (grub_command_t cmd __att copy_setup_header((unsigned char *) params, (unsigned char *) &lh); params->type_of_loader = 0x21; +#ifdef DEBUG_PRINT + /* after params */ + grub_printf("params: [%lx,%lx]\n", + (unsigned long)params, (unsigned long)params + 16384 - 1); +#endif + linux_cmdline = grub2_efi_allocate_pages_high (addr_max, BYTES_TO_PAGES(lh.cmdline_size + 1), 4096); @@ -332,6 +356,12 @@ grub_cmd_linux (grub_command_t cmd __att if ( load_high ) params->ext_cmd_line_ptr = (grub_uint64_t) linux_cmdline >> 32; +#ifdef DEBUG_PRINT + /* after cmdline */ + grub_printf("cmdline: [%lx,%lx]\n", (unsigned long)linux_cmdline, + (unsigned long)linux_cmdline + lh.cmdline_size - 1); +#endif + handover_offset = lh.handover_offset; kernel_mem = NULL; @@ -351,6 +381,12 @@ grub_cmd_linux (grub_command_t cmd __att kernel_size = lh.init_size; +#ifdef DEBUG_PRINT + /* after kernel alloc */ + grub_printf("kernel: [%lx,%lx]\n", (unsigned long)kernel_mem, + (unsigned long)kernel_mem + lh.init_size - 1); +#endif + start = (lh.setup_sects + 1) * 512; memcpy(kernel_mem, (unsigned char *)kernel + start, filelen - start);