]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Fix handling of EFI with big memory maps.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 30 May 2012 12:56:53 +0000 (14:56 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 30 May 2012 12:56:53 +0000 (14:56 +0200)
* 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.

ChangeLog
grub-core/loader/i386/linux.c
include/grub/i386/linux.h

index 2ef7663f4b3add61d030f0cb061713e2e74f88c0..0623e6834fe139dae1a56fadc7b40e32f255c9d9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        * Makefile.util.def: Remove -Wno-format.
index 4c382b114092e14292e669427a2b282a63183fd6..1d53be24700275273a4f23edbfd20592ce3844dd 100644 (file)
@@ -59,25 +59,21 @@ GRUB_MOD_LICENSE ("GPLv3+");
 #define ACCEPTS_PURE_TEXT 1
 #endif
 
-#define GRUB_LINUX_CL_OFFSET           0x1000
-
 static grub_dl_t my_mod;
 
 static grub_size_t linux_mem_size;
 static int loaded;
-static void *real_mode_mem;
-static grub_addr_t real_mode_target;
 static void *prot_mode_mem;
 static grub_addr_t prot_mode_target;
 static void *initrd_mem;
 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_uint32_t initrd_pages;
 static struct grub_relocator *relocator = NULL;
 static void *efi_mmap_buf;
 static grub_size_t maximal_cmdline_size;
+static struct linux_kernel_params linux_params;
+static char *linux_cmdline;
 #ifdef GRUB_MACHINE_EFI
 static grub_efi_uintn_t efi_mmap_size;
 #else
@@ -183,8 +179,8 @@ free_pages (void)
 {
   grub_relocator_unload (relocator);
   relocator = NULL;
-  real_mode_mem = prot_mode_mem = initrd_mem = 0;
-  real_mode_target = prot_mode_target = initrd_mem_target = 0;
+  prot_mode_mem = initrd_mem = 0;
+  prot_mode_target = initrd_mem_target = 0;
 }
 
 /* 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_uint64_t prefered_address)
 {
-  grub_size_t real_size, mmap_size;
   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);
-  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.  */
   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
      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;
     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);
   }
 
-  grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_target = %lx, real_mode_pages = %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",
+  grub_dprintf ("linux", "prot_mode_mem = %lx, prot_mode_target = %lx, prot_size = %x\n",
                 (unsigned long) prot_mode_mem, (unsigned long) prot_mode_target,
-               (unsigned) prot_mode_pages);
+               (unsigned) prot_size);
   return GRUB_ERR_NONE;
 
  fail:
@@ -335,12 +257,6 @@ grub_e820_add_region (struct grub_e820_mmap *e820_map, int *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) &&
       (e820_map[n - 1].type == type))
       e820_map[n - 1].size += size;
@@ -462,9 +378,85 @@ grub_linux_boot (void)
   const char *modevar;
   char *tmp;
   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 = 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
   {
     const char *bootpath;
@@ -482,10 +474,10 @@ grub_linux_boot (void)
   grub_dprintf ("linux", "code32_start = %x\n",
                (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);
-  int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, 
-                            grub_memory_type_t type)
+  int NESTED_FUNC_ATTR hook_fill (grub_uint64_t addr, grub_uint64_t size, 
+                                 grub_memory_type_t type)
     {
       grub_uint32_t e820_type;
       switch (type)
@@ -517,7 +509,7 @@ grub_linux_boot (void)
     }
 
   e820_num = 0;
-  if (grub_mmap_iterate (hook))
+  if (grub_mmap_iterate (hook_fill))
     return grub_errno;
   params->mmap_size = e820_num;
 
@@ -652,6 +644,8 @@ grub_linux_unload (void)
 {
   grub_dl_unref (my_mod);
   loaded = 0;
+  grub_free (linux_cmdline);
+  linux_cmdline = 0;
   return GRUB_ERR_NONE;
 }
 
@@ -781,16 +775,16 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
                      preffered_address))
     goto fail;
 
-  params = (struct linux_kernel_params *) real_mode_mem;
-  grub_memset (params, 0, GRUB_LINUX_CL_OFFSET + maximal_cmdline_size);
+  params = (struct linux_kernel_params *) &linux_params;
+  grub_memset (params, 0, sizeof (*params));
   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->kernel_alignment = (1 << align);
   params->ps_mouse = params->padding10 =  0;
 
-  len = 0x400 - sizeof (lh);
-  if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
+  len = sizeof (*params) - sizeof (lh);
+  if (grub_file_read (file, (char *) params + sizeof (lh), len) != len)
     {
       if (!grub_errno)
        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_offset = 0x1000;
 
-  params->cmd_line_ptr = real_mode_target + 0x1000;
   params->ramdisk_image = 0;
   params->ramdisk_size = 0;
 
@@ -978,10 +971,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
       }
 
   /* Create kernel command line.  */
-  grub_memcpy ((char *)real_mode_mem + GRUB_LINUX_CL_OFFSET, LINUX_IMAGE,
-             sizeof (LINUX_IMAGE));
+  linux_cmdline = grub_zalloc (maximal_cmdline_size + 1);
+  if (!linux_cmdline)
+    goto fail;
+  grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
   grub_create_loader_cmdline (argc, argv,
-                             (char *)real_mode_mem + GRUB_LINUX_CL_OFFSET
+                             linux_cmdline
                              + sizeof (LINUX_IMAGE) - 1,
                              maximal_cmdline_size
                              - (sizeof (LINUX_IMAGE) - 1));
@@ -1054,7 +1049,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
 
   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.  */
   if (grub_le_to_cpu16 (lh->version) >= 0x0203)
index 8dde9c227721a1afdbb2d04152e4013d2eaffc30..38e8e97582603391bd7ab2e18438ecd4e4db7e8c 100644 (file)
@@ -69,8 +69,6 @@
 #define GRUB_E820_NVS        4
 #define GRUB_E820_BADRAM     5
 
-#define GRUB_E820_MAX_ENTRY  128
-
 struct grub_e820_mmap
 {
   grub_uint64_t addr;
@@ -298,7 +296,7 @@ struct linux_kernel_params
   grub_uint32_t payload_length;
   grub_uint64_t setup_data;
   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));
 #endif /* ! ASM_FILE */