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;