File grub-0.97-better-get-memory-map-rhbz607213.patch of Package grub

From 3605b86b534c4bdc5ab5011b7ac1568bf986fa91 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Mon, 12 Jul 2010 16:45:47 -0400
Subject: [PATCH] Make the allocator for kernel memory slightly more sane...

Based on a patch from Stuart Hayes.
---
 efi/efimm.c               |   78 ++++++++++++++++++++----------------
 efi/grub/efi/efi.h        |    8 ++-
 efi/ia32/loader/linux.c   |   96 ++++++--------------------------------------
 efi/x86_64/loader/linux.c |   90 +++++-------------------------------------
 4 files changed, 71 insertions(+), 201 deletions(-)

diff --git a/efi/efimm.c b/efi/efimm.c
index 3b790d5..e21babd 100644
--- a/efi/efimm.c
+++ b/efi/efimm.c
@@ -32,9 +32,11 @@
 #define BYTES_TO_PAGES(bytes)	((bytes) >> 12)
 #define PAGES_TO_BYTES(pages)	((pages) << 12)
 
-/* The size of a memory map obtained from the firmware. This must be
-   a multiplier of 4KB.  */
-#define MEMORY_MAP_SIZE	0x2000
+/* Global variables used to store memory map, its size, and the number of
+ * pages allocated for the buffer. */
+void *mmap_buf;
+grub_efi_uintn_t mmap_size;
+grub_efi_uintn_t mmap_pages;
 
 /* Maintain the list of allocated pages.  */
 struct allocated_page
@@ -205,11 +207,14 @@ grub_efi_free_pages (grub_efi_physical_address_t address,
 }
 
 /* Get the memory map as defined in the EFI spec. Return 1 if successful,
-   return 0 if partial, or return -1 if an error occurs.  */
+   return 0 if partial, or return -1 if an error occurs.
+
+   This function will allocate memory for (global) mmap_buf if there isn't
+   already a buffer allocated, and will free & reallocate if it needs to
+   be larger. */
+
 int
-grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size,
-			 grub_efi_memory_descriptor_t *memory_map,
-			 grub_efi_uintn_t *map_key,
+grub_efi_get_memory_map (grub_efi_uintn_t *map_key,
 			 grub_efi_uintn_t *descriptor_size,
 			 grub_efi_uint32_t *descriptor_version)
 {
@@ -217,6 +222,7 @@ grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size,
   grub_efi_boot_services_t *b;
   grub_efi_uintn_t key;
   grub_efi_uint32_t version;
+  grub_efi_uintn_t tmp_mmap_size;
 
   /* Allow some parameters to be missing.  */
   if (! map_key)
@@ -224,16 +230,35 @@ grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size,
   if (! descriptor_version)
     descriptor_version = &version;
 
-  b = grub_efi_system_table->boot_services;
-  status = Call_Service_5 (b->get_memory_map,
-			      memory_map_size, memory_map, map_key,
+  while (1)
+    {
+      b = grub_efi_system_table->boot_services;
+      tmp_mmap_size = PAGES_TO_BYTES(mmap_pages);
+      status = Call_Service_5 (b->get_memory_map,
+			      &tmp_mmap_size, mmap_buf, map_key,
 			      descriptor_size, descriptor_version);
-  if (status == GRUB_EFI_SUCCESS)
-    return 1;
-  else if (status == GRUB_EFI_BUFFER_TOO_SMALL)
-    return 0;
-  else
-    return -1;
+      if (status == GRUB_EFI_SUCCESS)
+        {
+          mmap_size = tmp_mmap_size;
+          return 1;
+        }
+      else if (status != GRUB_EFI_BUFFER_TOO_SMALL)
+        return -1;
+
+      /* we need a larger buffer */
+      if (mmap_buf)
+        grub_efi_free_pages ((grub_addr_t) mmap_buf, mmap_pages);
+
+      /* get 1 more page than we need, just in case */
+      mmap_pages = BYTES_TO_PAGES(tmp_mmap_size + 4095) + 1;
+      mmap_buf = grub_efi_allocate_pages (0, mmap_pages);
+      if (! mmap_buf)
+        {
+          mmap_pages = 0;
+          grub_printf ("cannot allocate memory for memory map");
+          return -1;
+        }
+    }
 }
 
 #define MMAR_DESC_LENGTH	20
@@ -345,33 +370,16 @@ static void
 update_e820_map (struct e820_entry *e820_map,
 		 int *e820_nr_map)
 {
-  grub_efi_memory_descriptor_t *memory_map;
-  grub_efi_uintn_t map_size;
   grub_efi_uintn_t desc_size;
 
-  /* Prepare a memory region to store memory map.  */
-  memory_map = grub_efi_allocate_pages (0, BYTES_TO_PAGES (MEMORY_MAP_SIZE));
-  if (! memory_map)
-    {
-      grub_printf ("cannot allocate memory");
-      return;
-    }
-
-  /* Obtain descriptors for available memory.  */
-  map_size = MEMORY_MAP_SIZE;
-
-  if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0)
+  if (grub_efi_get_memory_map (0, &desc_size, 0) < 0)
     {
       grub_printf ("cannot get memory map");
       return;
     }
 
   e820_map_from_efi_map (e820_map, e820_nr_map,
-			 memory_map, desc_size, map_size);
-
-  /* Release the memory map.  */
-  grub_efi_free_pages ((grub_addr_t) memory_map,
-		       BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+			 mmap_buf, desc_size, mmap_size);
 }
 
 /* Simulated memory sizes. */
diff --git a/efi/grub/efi/efi.h b/efi/grub/efi/efi.h
index 858e37d..936759b 100644
--- a/efi/grub/efi/efi.h
+++ b/efi/grub/efi/efi.h
@@ -52,9 +52,7 @@ void
 grub_efi_free_pages (grub_efi_physical_address_t address,
 		     grub_efi_uintn_t pages);
 int
-grub_efi_get_memory_map (grub_efi_uintn_t * memory_map_size,
-			 grub_efi_memory_descriptor_t * memory_map,
-			 grub_efi_uintn_t * map_key,
+grub_efi_get_memory_map (grub_efi_uintn_t * map_key,
 			 grub_efi_uintn_t * descriptor_size,
 			 grub_efi_uint32_t * descriptor_version);
 grub_efi_loaded_image_t *grub_efi_get_loaded_image (grub_efi_handle_t
@@ -71,6 +69,10 @@ void grub_efi_fini (void);
 void grub_efi_set_prefix (void);
 
 /* Variables.  */
+extern void *mmap_buf;
+extern grub_efi_uintn_t mmap_size;
+extern grub_efi_uintn_t mmap_pages;
+
 extern grub_efi_system_table_t *grub_efi_system_table;
 extern grub_efi_handle_t grub_efi_image_handle;
 
diff --git a/efi/ia32/loader/linux.c b/efi/ia32/loader/linux.c
index 41f1ce6..eb6b5de 100644
--- a/efi/ia32/loader/linux.c
+++ b/efi/ia32/loader/linux.c
@@ -42,11 +42,9 @@ static int loaded;
 static void *real_mode_mem;
 static void *prot_mode_mem;
 static void *initrd_mem;
-static void *mmap_buf;
 static grub_efi_uintn_t real_mode_pages;
 static grub_efi_uintn_t prot_mode_pages;
 static grub_efi_uintn_t initrd_pages;
-static grub_efi_uintn_t mmap_pages;
 static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GRAPHICS_OUTPUT_GUID;
 
 static inline grub_size_t
@@ -55,45 +53,6 @@ page_align (grub_size_t size)
   return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
 }
 
-/* Find the optimal number of pages for the memory map. Is it better to
-   move this code to efimm.c?  */
-static grub_efi_uintn_t
-find_mmap_size (void)
-{
-  static grub_efi_uintn_t mmap_size = 0;
-
-  if (mmap_size != 0)
-    return mmap_size;
-
-  mmap_size = (1 << 12);
-  while (1)
-    {
-      int ret;
-      grub_efi_memory_descriptor_t *mmap;
-      grub_efi_uintn_t desc_size;
-
-      mmap = grub_malloc (mmap_size);
-      if (! mmap)
-	return 0;
-
-      ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
-      grub_free (mmap);
-
-      if (ret < 0)
-	grub_fatal ("cannot get memory map");
-      else if (ret > 0)
-	break;
-
-      mmap_size += (1 << 12);
-    }
-
-  /* Increase the size a bit for safety, because GRUB allocates more on
-     later, and EFI itself may allocate more.  */
-  mmap_size += (1 << 11);
-
-  return page_align (mmap_size);
-}
-
 static void
 free_pages (void)
 {
@@ -128,15 +87,13 @@ static int
 allocate_pages (grub_size_t real_size, grub_size_t prot_size)
 {
   grub_efi_uintn_t desc_size;
-  grub_efi_memory_descriptor_t *mmap, *mmap_end;
-  grub_efi_uintn_t mmap_size, tmp_mmap_size;
+  grub_efi_memory_descriptor_t *mmap_end;
   grub_efi_memory_descriptor_t *desc;
   grub_efi_physical_address_t addr;
 
   /* Make sure that each size is aligned to a page boundary.  */
   real_size = page_align (real_size + SECTOR_SIZE);
   prot_size = page_align (prot_size);
-  mmap_size = find_mmap_size ();
 
   grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
 		(unsigned int) real_size, (unsigned int) prot_size,
@@ -146,30 +103,19 @@ allocate_pages (grub_size_t real_size, grub_size_t prot_size)
      the memory map buffer for simplicity.  */
   real_mode_pages = (real_size >> 12);
   prot_mode_pages = (prot_size >> 12);
-  mmap_pages = (mmap_size >> 12);
 
   /* Initialize the memory pointers with NULL for convenience.  */
   real_mode_mem = 0;
   prot_mode_mem = 0;
-  mmap_buf = 0;
-
-  /* Read the memory map temporarily, to find free space.  */
-  mmap = grub_malloc (mmap_size);
-  if (! mmap)
-    {
-      errnum = ERR_UNRECOGNIZED;
-      return 0;
-    }
 
-  tmp_mmap_size = mmap_size;
-  if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
+  if (grub_efi_get_memory_map (0, &desc_size, 0) <= 0)
     grub_fatal ("cannot get memory map");
 
   addr = 0;
-  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
+  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
   /* First, find free pages for the real mode code
      and the memory map buffer.  */
-  for (desc = mmap;
+  for (desc = mmap_buf;
        desc < mmap_end;
        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
     {
@@ -183,7 +129,7 @@ allocate_pages (grub_size_t real_size, grub_size_t prot_size)
           grub_dprintf ("linux", "physical_start = %x, physical_end = %x\n",
                         (unsigned) desc->physical_start,
                         (unsigned) physical_end);
-          addr = physical_end - real_size - mmap_size;
+          addr = physical_end - real_size;
           if (addr < 0x10000)
             continue;
 
@@ -205,29 +151,15 @@ allocate_pages (grub_size_t real_size, grub_size_t prot_size)
       goto fail;
     }
 
-  mmap_buf = grub_efi_allocate_pages (0, mmap_pages);
-  if (! mmap_buf)
-    {
-      grub_printf("cannot allocate efi mmap pages");
-      errnum = ERR_WONT_FIT;
-      goto fail;
-    }
-
   /* Next, find free pages for the protected mode code.  */
   /* XXX what happens if anything is using this address?  */
   prot_mode_mem = grub_efi_allocate_pages (0x100000, prot_mode_pages);
   if (! prot_mode_mem)
-    {
-      errnum = ERR_WONT_FIT;
-      grub_printf ("cannot allocate protected mode pages");
-      goto fail;
-    }
+	grub_fatal("Cannot allocate pages for VMLINUZ");
 
-  grub_free (mmap);
   return 1;
 
  fail:
-  grub_free (mmap);
   free_pages ();
   return 0;
 }
@@ -274,7 +206,6 @@ big_linux_boot (void)
 {
   struct linux_kernel_params *params;
   struct grub_linux_kernel_header *lh;
-  grub_efi_uintn_t mmap_size;
   grub_efi_uintn_t map_key;
   grub_efi_uintn_t desc_size;
   grub_efi_uint32_t desc_version;
@@ -285,9 +216,7 @@ big_linux_boot (void)
 
   graphics_set_kernel_params (params);
 
-  mmap_size = find_mmap_size ();
-  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
-			       &desc_size, &desc_version) <= 0)
+  if (grub_efi_get_memory_map (&map_key, &desc_size, &desc_version) <= 0)
     grub_fatal ("cannot get memory map");
 
   /* Pass e820 memmap. */
@@ -599,10 +528,12 @@ grub_load_initrd (char *initrd)
   grub_ssize_t size;
   grub_addr_t addr_min, addr_max;
   grub_addr_t addr;
-  grub_efi_uintn_t mmap_size;
+  grub_efi_uintn_t map_key;
+  grub_efi_memory_descriptor_t *mmap_end;
   grub_efi_memory_descriptor_t *desc;
   grub_efi_memory_descriptor_t tdesc;
   grub_efi_uintn_t desc_size;
+  grub_efi_uint32_t desc_version;
   struct linux_kernel_params *params;
 
   if (initrd == NULL)
@@ -644,14 +575,13 @@ grub_load_initrd (char *initrd)
   grub_dprintf(__func__, "prot_mode_mem=%p prot_mode_pages=%lu\n", prot_mode_mem, prot_mode_pages);
 
   /* Find the highest address to put the initrd.  */
-  mmap_size = find_mmap_size ();
-  grub_dprintf(__func__, "addr_min: 0x%lx addr_max: 0x%lx mmap_size: %lu\n", addr_min, addr_max, mmap_size);
-  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0) <= 0)
+  if (grub_efi_get_memory_map (&map_key, &desc_size, &desc_version) <= 0)
     grub_fatal ("cannot get memory map");
 
+  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
   addr = 0;
   for (desc = mmap_buf;
-       desc < NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
+       desc < mmap_end;
        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
     {
       if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
diff --git a/efi/x86_64/loader/linux.c b/efi/x86_64/loader/linux.c
index a5cfbf8..18746ea 100644
--- a/efi/x86_64/loader/linux.c
+++ b/efi/x86_64/loader/linux.c
@@ -43,11 +43,9 @@ static void *real_mode_mem;
 static void *prot_mode_mem;
 static grub_size_t prot_kernel_size;
 static void *initrd_mem;
-static void *mmap_buf;
 static grub_efi_uintn_t real_mode_pages;
 static grub_efi_uintn_t prot_mode_pages;
 static grub_efi_uintn_t initrd_pages;
-static grub_efi_uintn_t mmap_pages;
 static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GRAPHICS_OUTPUT_GUID;
 
 static inline grub_size_t
@@ -56,45 +54,6 @@ page_align (grub_size_t size)
   return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
 }
 
-/* Find the optimal number of pages for the memory map. Is it better to
-   move this code to efimm.c?  */
-static grub_efi_uintn_t
-find_mmap_size (void)
-{
-  static grub_efi_uintn_t mmap_size = 0;
-
-  if (mmap_size != 0)
-    return mmap_size;
-
-  mmap_size = (1 << 12);
-  while (1)
-    {
-      int ret;
-      grub_efi_memory_descriptor_t *mmap;
-      grub_efi_uintn_t desc_size;
-
-      mmap = grub_malloc (mmap_size);
-      if (! mmap)
-	return 0;
-
-      ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
-      grub_free (mmap);
-
-      if (ret < 0)
-	grub_fatal ("cannot get memory map");
-      else if (ret > 0)
-	break;
-
-      mmap_size += (1 << 12);
-    }
-
-  /* Increase the size a bit for safety, because GRUB allocates more on
-     later, and EFI itself may allocate more.  */
-  mmap_size += (1 << 11);
-
-  return page_align (mmap_size);
-}
-
 static void
 free_pages (void)
 {
@@ -123,15 +82,13 @@ static int
 allocate_pages (grub_size_t real_size, grub_size_t prot_size)
 {
   grub_efi_uintn_t desc_size;
-  grub_efi_memory_descriptor_t *mmap, *mmap_end;
-  grub_efi_uintn_t mmap_size, tmp_mmap_size;
+  grub_efi_memory_descriptor_t *mmap_end;
   grub_efi_memory_descriptor_t *desc;
   grub_efi_physical_address_t addr;
 
   /* Make sure that each size is aligned to a page boundary.  */
   real_size = page_align (real_size + SECTOR_SIZE);
   prot_size = page_align (prot_size);
-  mmap_size = find_mmap_size ();
 
   grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
 		(unsigned int) real_size, (unsigned int) prot_size,
@@ -141,30 +98,19 @@ allocate_pages (grub_size_t real_size, grub_size_t prot_size)
      the memory map buffer for simplicity.  */
   real_mode_pages = (real_size >> 12);
   prot_mode_pages = (prot_size >> 12);
-  mmap_pages = (mmap_size >> 12);
 
   /* Initialize the memory pointers with NULL for convenience.  */
   real_mode_mem = 0;
   prot_mode_mem = 0;
-  mmap_buf = 0;
-
-  /* Read the memory map temporarily, to find free space.  */
-  mmap = grub_malloc (mmap_size);
-  if (! mmap)
-    {
-      errnum = ERR_UNRECOGNIZED;
-      return 0;
-    }
 
-  tmp_mmap_size = mmap_size;
-  if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
+  if (grub_efi_get_memory_map (0, &desc_size, 0) <= 0)
     grub_fatal ("cannot get memory map");
 
   addr = 0;
-  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
+  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
   /* First, find free pages for the real mode code
      and the memory map buffer.  */
-  for (desc = mmap;
+  for (desc = mmap_buf;
        desc < mmap_end;
        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
     {
@@ -172,23 +118,22 @@ allocate_pages (grub_size_t real_size, grub_size_t prot_size)
 	  && desc->num_pages >= real_mode_pages)
 	{
 	  grub_efi_physical_address_t physical_end;
-          int allocsize = real_size + mmap_size;
 
           physical_end = desc->physical_start + (desc->num_pages << 12);
 
           grub_dprintf ("linux", "physical_start = %x, physical_end = %x\n",
                         (unsigned) desc->physical_start,
                         (unsigned) physical_end);
-          addr = physical_end - allocsize;
+          addr = physical_end - real_size;
           if (addr < 0x10000)
             continue;
 
           /* the kernel wants this address to be under 1 gig.*/
-          if (desc->physical_start > 0x40000000 - allocsize)
+          if (desc->physical_start > 0x40000000 - real_size)
             continue;
 
-          if (addr > 0x40000000 - allocsize)
-            addr = 0x40000000 - allocsize;
+          if (addr > 0x40000000 - real_size)
+            addr = 0x40000000 - real_size;
 
           grub_dprintf ("linux", "trying to allocate %u pages at %x\n",
                         (unsigned) real_mode_pages, (unsigned) addr);
@@ -215,19 +160,9 @@ allocate_pages (grub_size_t real_size, grub_size_t prot_size)
   if (!prot_mode_mem)
 	grub_fatal("Cannot allocate pages for VMLINUZ");
     
-  mmap_buf = grub_efi_allocate_pages (0, mmap_pages);
-  if (! mmap_buf)
-    {
-      grub_printf("cannot allocate efi mmap pages");
-      errnum = ERR_WONT_FIT;
-      goto fail;
-    }
-
-  grub_free (mmap);
   return 1;
 
  fail:
-  grub_free (mmap);
   free_pages ();
   return 0;
 }
@@ -245,7 +180,6 @@ big_linux_boot (void)
 {
   struct linux_kernel_params *params;
   struct grub_linux_kernel_header *lh;
-  grub_efi_uintn_t mmap_size;
   grub_efi_uintn_t map_key;
   grub_efi_uintn_t desc_size;
   grub_efi_uint32_t desc_version;
@@ -255,9 +189,7 @@ big_linux_boot (void)
 
   graphics_set_kernel_params (params);
 
-  mmap_size = find_mmap_size ();
-  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
-			       &desc_size, &desc_version) <= 0)
+  if (grub_efi_get_memory_map (&map_key, &desc_size, &desc_version) <= 0)
     grub_fatal ("cannot get memory map");
 
   /* Pass e820 memmap. */
@@ -544,7 +476,6 @@ grub_load_initrd (char *initrd)
   grub_ssize_t size;
   grub_addr_t addr_min, addr_max;
   grub_addr_t addr;
-  grub_efi_uintn_t mmap_size;
   grub_efi_memory_descriptor_t *desc;
   grub_efi_memory_descriptor_t tdesc;
   grub_efi_uintn_t desc_size;
@@ -582,9 +513,8 @@ grub_load_initrd (char *initrd)
   addr_min = 0;
 
   /* Find the highest address to put the initrd.  */
-  mmap_size = find_mmap_size ();
   grub_dprintf(__func__, "addr_min: 0x%lx addr_max: 0x%lx mmap_size: %lu\n", addr_min, addr_max, mmap_size);
-  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0) <= 0)
+  if (grub_efi_get_memory_map (0, &desc_size, 0) <= 0)
     grub_fatal ("cannot get memory map");
 
   addr = 0;
-- 
1.7.1.1

openSUSE Build Service is sponsored by