Fix handling of EFI with big memory maps.

* grub-core/loader/i386/linux.c (GRUB_LINUX_CL_OFFSET): Removed.
	(real_mode_mem): Likewise.
	(real_mode_target): Likewise.
	(real_mode_pages): Likewise.
	(prot_mode_pages): Likewise.
	(linux_params): New var.
	(linux_cmdline): Likewise.
	(free_pages): Don't set real mode variables.
	(allocate_pages): Don't allocate real mode memory.
	(grub_e820_add_region): Remove the limit.
	(grub_linux_boot): Allocate and copy real mode memory.
	(grub_linux_unload): Free linux_cmdline.
	(grub_cmd_linux): Use temporary storage for parameters.
	(grub_cmd_initrd): Likewise.
	* include/grub/i386/linux.h (GRUB_E820_MAX_ENTRY): Removed.
	(linux_kernel_params): Make it 1K big.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2012-05-30 14:56:53 +02:00
parent ddb7355862
commit a4e5ca80d9
3 changed files with 120 additions and 106 deletions

View file

@ -1,3 +1,24 @@
2012-05-30 Vladimir Serbinenko <phcoder@gmail.com>
Fix handling of EFI with big memory maps.
* grub-core/loader/i386/linux.c (GRUB_LINUX_CL_OFFSET): Removed.
(real_mode_mem): Likewise.
(real_mode_target): Likewise.
(real_mode_pages): Likewise.
(prot_mode_pages): Likewise.
(linux_params): New var.
(linux_cmdline): Likewise.
(free_pages): Don't set real mode variables.
(allocate_pages): Don't allocate real mode memory.
(grub_e820_add_region): Remove the limit.
(grub_linux_boot): Allocate and copy real mode memory.
(grub_linux_unload): Free linux_cmdline.
(grub_cmd_linux): Use temporary storage for parameters.
(grub_cmd_initrd): Likewise.
* include/grub/i386/linux.h (GRUB_E820_MAX_ENTRY): Removed.
(linux_kernel_params): Make it 1K big.
2012-05-30 Vladimir Serbinenko <phcoder@gmail.com> 2012-05-30 Vladimir Serbinenko <phcoder@gmail.com>
* Makefile.util.def: Remove -Wno-format. * Makefile.util.def: Remove -Wno-format.

View file

@ -59,25 +59,21 @@ GRUB_MOD_LICENSE ("GPLv3+");
#define ACCEPTS_PURE_TEXT 1 #define ACCEPTS_PURE_TEXT 1
#endif #endif
#define GRUB_LINUX_CL_OFFSET 0x1000
static grub_dl_t my_mod; static grub_dl_t my_mod;
static grub_size_t linux_mem_size; static grub_size_t linux_mem_size;
static int loaded; static int loaded;
static void *real_mode_mem;
static grub_addr_t real_mode_target;
static void *prot_mode_mem; static void *prot_mode_mem;
static grub_addr_t prot_mode_target; static grub_addr_t prot_mode_target;
static void *initrd_mem; static void *initrd_mem;
static grub_addr_t initrd_mem_target; static grub_addr_t initrd_mem_target;
static grub_uint32_t real_mode_pages;
static grub_uint32_t prot_mode_pages;
static grub_size_t prot_init_space; static grub_size_t prot_init_space;
static grub_uint32_t initrd_pages; static grub_uint32_t initrd_pages;
static struct grub_relocator *relocator = NULL; static struct grub_relocator *relocator = NULL;
static void *efi_mmap_buf; static void *efi_mmap_buf;
static grub_size_t maximal_cmdline_size; static grub_size_t maximal_cmdline_size;
static struct linux_kernel_params linux_params;
static char *linux_cmdline;
#ifdef GRUB_MACHINE_EFI #ifdef GRUB_MACHINE_EFI
static grub_efi_uintn_t efi_mmap_size; static grub_efi_uintn_t efi_mmap_size;
#else #else
@ -183,8 +179,8 @@ free_pages (void)
{ {
grub_relocator_unload (relocator); grub_relocator_unload (relocator);
relocator = NULL; relocator = NULL;
real_mode_mem = prot_mode_mem = initrd_mem = 0; prot_mode_mem = initrd_mem = 0;
real_mode_target = prot_mode_target = initrd_mem_target = 0; prot_mode_target = initrd_mem_target = 0;
} }
/* Allocate pages for the real mode code and the protected mode code /* Allocate pages for the real mode code and the protected mode code
@ -194,27 +190,9 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
grub_size_t min_align, int relocatable, grub_size_t min_align, int relocatable,
grub_uint64_t prefered_address) grub_uint64_t prefered_address)
{ {
grub_size_t real_size, mmap_size;
grub_err_t err; grub_err_t err;
/* Make sure that each size is aligned to a page boundary. */
real_size = GRUB_LINUX_CL_OFFSET + maximal_cmdline_size;
prot_size = page_align (prot_size); prot_size = page_align (prot_size);
mmap_size = find_mmap_size ();
#ifdef GRUB_MACHINE_EFI
efi_mmap_size = find_efi_mmap_size ();
if (efi_mmap_size == 0)
return grub_errno;
#endif
grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
(unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
/* Calculate the number of pages; Combine the real mode code with
the memory map buffer for simplicity. */
real_mode_pages = ((real_size + mmap_size + efi_mmap_size) >> 12);
prot_mode_pages = (prot_size >> 12);
/* Initialize the memory pointers with NULL for convenience. */ /* Initialize the memory pointers with NULL for convenience. */
free_pages (); free_pages ();
@ -229,59 +207,6 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
/* FIXME: Should request low memory from the heap when this feature is /* FIXME: Should request low memory from the heap when this feature is
implemented. */ implemented. */
auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
grub_memory_type_t);
int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
grub_memory_type_t type)
{
/* We must put real mode code in the traditional space. */
if (type == GRUB_MEMORY_AVAILABLE
&& addr <= 0x90000)
{
if (addr < 0x10000)
{
size += addr - 0x10000;
addr = 0x10000;
}
if (addr + size > 0x90000)
size = 0x90000 - addr;
if (real_size + mmap_size + efi_mmap_size > size)
return 0;
real_mode_target = ((addr + size) - (real_size + mmap_size + efi_mmap_size));
return 1;
}
return 0;
}
#ifdef GRUB_MACHINE_EFI
grub_efi_mmap_iterate (hook, 1);
if (! real_mode_target)
grub_efi_mmap_iterate (hook, 0);
#else
grub_mmap_iterate (hook);
#endif
if (! real_mode_target)
{
err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
goto fail;
}
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
real_mode_target,
(real_size + mmap_size
+ efi_mmap_size));
if (err)
goto fail;
real_mode_mem = get_virtual_current_address (ch);
}
efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size + mmap_size;
{ {
grub_relocator_chunk_t ch; grub_relocator_chunk_t ch;
if (relocatable) if (relocatable)
@ -315,12 +240,9 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
prot_mode_target = get_physical_target_address (ch); prot_mode_target = get_physical_target_address (ch);
} }
grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_target = %lx, real_mode_pages = %x\n", grub_dprintf ("linux", "prot_mode_mem = %lx, prot_mode_target = %lx, prot_size = %x\n",
(unsigned long) real_mode_mem, (unsigned long) real_mode_target,
(unsigned) real_mode_pages);
grub_dprintf ("linux", "prot_mode_mem = %lx, prot_mode_target = %lx, prot_mode_pages = %x\n",
(unsigned long) prot_mode_mem, (unsigned long) prot_mode_target, (unsigned long) prot_mode_mem, (unsigned long) prot_mode_target,
(unsigned) prot_mode_pages); (unsigned) prot_size);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
fail: fail:
@ -335,12 +257,6 @@ grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
{ {
int n = *e820_num; int n = *e820_num;
if (n >= GRUB_E820_MAX_ENTRY)
{
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Too many e820 memory map entries");
}
if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) && if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
(e820_map[n - 1].type == type)) (e820_map[n - 1].type == type))
e820_map[n - 1].size += size; e820_map[n - 1].size += size;
@ -462,9 +378,85 @@ grub_linux_boot (void)
const char *modevar; const char *modevar;
char *tmp; char *tmp;
struct grub_relocator32_state state; struct grub_relocator32_state state;
void *real_mode_mem;
grub_addr_t real_mode_target;
grub_size_t real_size, mmap_size;
grub_size_t cl_offset;
mmap_size = find_mmap_size ();
/* Make sure that each size is aligned to a page boundary. */
cl_offset = ALIGN_UP (mmap_size + sizeof (*params), 4096);
real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096);
#ifdef GRUB_MACHINE_EFI
efi_mmap_size = find_efi_mmap_size ();
if (efi_mmap_size == 0)
return grub_errno;
#endif
grub_dprintf ("linux", "real_size = %x, mmap_size = %x\n",
(unsigned) real_size, (unsigned) mmap_size);
auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
grub_memory_type_t);
int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
grub_memory_type_t type)
{
/* We must put real mode code in the traditional space. */
if (type == GRUB_MEMORY_AVAILABLE
&& addr <= 0x90000)
{
if (addr < 0x10000)
{
size += addr - 0x10000;
addr = 0x10000;
}
if (addr + size > 0x90000)
size = 0x90000 - addr;
if (real_size + efi_mmap_size > size)
return 0;
real_mode_target = ((addr + size) - (real_size + efi_mmap_size));
return 1;
}
return 0;
}
#ifdef GRUB_MACHINE_EFI
grub_efi_mmap_iterate (hook, 1);
if (! real_mode_target)
grub_efi_mmap_iterate (hook, 0);
#else
grub_mmap_iterate (hook);
#endif
if (! real_mode_target)
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
real_mode_target,
(real_size + efi_mmap_size));
if (err)
return err;
real_mode_mem = get_virtual_current_address (ch);
}
efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size;
grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_target = %lx, real_size = %x\n",
(unsigned long) real_mode_mem, (unsigned long) real_mode_target,
(unsigned) real_size);
params = real_mode_mem; params = real_mode_mem;
*params = linux_params;
params->cmd_line_ptr = real_mode_target + cl_offset;
grub_memcpy ((char *) params + cl_offset, linux_cmdline,
maximal_cmdline_size);
#ifdef GRUB_MACHINE_IEEE1275 #ifdef GRUB_MACHINE_IEEE1275
{ {
const char *bootpath; const char *bootpath;
@ -482,9 +474,9 @@ grub_linux_boot (void)
grub_dprintf ("linux", "code32_start = %x\n", grub_dprintf ("linux", "code32_start = %x\n",
(unsigned) params->code32_start); (unsigned) params->code32_start);
auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, auto int NESTED_FUNC_ATTR hook_fill (grub_uint64_t, grub_uint64_t,
grub_memory_type_t); grub_memory_type_t);
int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, int NESTED_FUNC_ATTR hook_fill (grub_uint64_t addr, grub_uint64_t size,
grub_memory_type_t type) grub_memory_type_t type)
{ {
grub_uint32_t e820_type; grub_uint32_t e820_type;
@ -517,7 +509,7 @@ grub_linux_boot (void)
} }
e820_num = 0; e820_num = 0;
if (grub_mmap_iterate (hook)) if (grub_mmap_iterate (hook_fill))
return grub_errno; return grub_errno;
params->mmap_size = e820_num; params->mmap_size = e820_num;
@ -652,6 +644,8 @@ grub_linux_unload (void)
{ {
grub_dl_unref (my_mod); grub_dl_unref (my_mod);
loaded = 0; loaded = 0;
grub_free (linux_cmdline);
linux_cmdline = 0;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
@ -781,16 +775,16 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
preffered_address)) preffered_address))
goto fail; goto fail;
params = (struct linux_kernel_params *) real_mode_mem; params = (struct linux_kernel_params *) &linux_params;
grub_memset (params, 0, GRUB_LINUX_CL_OFFSET + maximal_cmdline_size); grub_memset (params, 0, sizeof (*params));
grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1); grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
params->code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; params->code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
params->kernel_alignment = (1 << align); params->kernel_alignment = (1 << align);
params->ps_mouse = params->padding10 = 0; params->ps_mouse = params->padding10 = 0;
len = 0x400 - sizeof (lh); len = sizeof (*params) - sizeof (lh);
if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len) if (grub_file_read (file, (char *) params + sizeof (lh), len) != len)
{ {
if (!grub_errno) if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
@ -805,7 +799,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
params->cl_magic = GRUB_LINUX_CL_MAGIC; params->cl_magic = GRUB_LINUX_CL_MAGIC;
params->cl_offset = 0x1000; params->cl_offset = 0x1000;
params->cmd_line_ptr = real_mode_target + 0x1000;
params->ramdisk_image = 0; params->ramdisk_image = 0;
params->ramdisk_size = 0; params->ramdisk_size = 0;
@ -978,10 +971,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
} }
/* Create kernel command line. */ /* Create kernel command line. */
grub_memcpy ((char *)real_mode_mem + GRUB_LINUX_CL_OFFSET, LINUX_IMAGE, linux_cmdline = grub_zalloc (maximal_cmdline_size + 1);
sizeof (LINUX_IMAGE)); if (!linux_cmdline)
goto fail;
grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
grub_create_loader_cmdline (argc, argv, grub_create_loader_cmdline (argc, argv,
(char *)real_mode_mem + GRUB_LINUX_CL_OFFSET linux_cmdline
+ sizeof (LINUX_IMAGE) - 1, + sizeof (LINUX_IMAGE) - 1,
maximal_cmdline_size maximal_cmdline_size
- (sizeof (LINUX_IMAGE) - 1)); - (sizeof (LINUX_IMAGE) - 1));
@ -1054,7 +1049,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
initrd_pages = (page_align (size) >> 12); initrd_pages = (page_align (size) >> 12);
lh = (struct linux_kernel_header *) real_mode_mem; lh = (struct linux_kernel_header *) &linux_params;
/* Get the highest address available for the initrd. */ /* Get the highest address available for the initrd. */
if (grub_le_to_cpu16 (lh->version) >= 0x0203) if (grub_le_to_cpu16 (lh->version) >= 0x0203)

View file

@ -69,8 +69,6 @@
#define GRUB_E820_NVS 4 #define GRUB_E820_NVS 4
#define GRUB_E820_BADRAM 5 #define GRUB_E820_BADRAM 5
#define GRUB_E820_MAX_ENTRY 128
struct grub_e820_mmap struct grub_e820_mmap
{ {
grub_uint64_t addr; grub_uint64_t addr;
@ -298,7 +296,7 @@ struct linux_kernel_params
grub_uint32_t payload_length; grub_uint32_t payload_length;
grub_uint64_t setup_data; grub_uint64_t setup_data;
grub_uint8_t pad2[120]; /* 258 */ grub_uint8_t pad2[120]; /* 258 */
struct grub_e820_mmap e820_map[GRUB_E820_MAX_ENTRY]; /* 2d0 */ struct grub_e820_mmap e820_map[(0x400 - 0x2d0) / 20]; /* 2d0 */
} __attribute__ ((packed)); } __attribute__ ((packed));
#endif /* ! ASM_FILE */ #endif /* ! ASM_FILE */