Hi,

I was trying for a few days to enable EFI boot on my laptop, and i've
encountered some issues with Grub2.

My motherboard is one of the modern Intel chipset motherboard, that
come with UEFI v2, and when i tried to boot using grub 1.98, compiled
with efi support, there was some memory allocation error message at
the kernel loading time, saying "cannot allocate protected mode page"
or something like that.

So, i looked into the grub source code, and found that it tries to
allocate memory at a hard coded memory address (0X10000), that was
already in use, for my computer.

I've also found parts of the code in loader/i386/linux.c that were
looking for free pages in the memory map, which is already done by the
EFI memory allocation function.
Actually, i've managed to make it working, by letting EFI find free
memory pages and allocate them, but i dont really know how important
this 0x10000 address is.

My fix works for me, but I think it may be connected to the fact I've
also build a relocatable kernel. I don't know enough about "kernel
loading" and bootloaders to determine if my fix will work well in all
situations or not, so maybe someone can tell me that.

Actually, i've still have an issue at shutdown time : the kernel (or
grub ?) crashes with an error message "Bad RIP value" and a full stack
trace with an EFI call, just when it is expected to power down. So, i
need to poweroff or reboot it manually, using the power switch. I
anyone have an idea from where the error comes, i'm interested.

Rémi
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 5852a47..996b545 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -37,9 +37,22 @@ void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle,
 					   grub_efi_uint32_t attributes);
 int EXPORT_FUNC(grub_efi_set_text_mode) (int on);
 void EXPORT_FUNC(grub_efi_stall) (grub_efi_uintn_t microseconds);
+
+void *
+EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages,
+                                          grub_efi_memory_type_t memtype);
+void *
+EXPORT_FUNC(grub_efi_allocate_pages_before) (grub_efi_physical_address_t address,
+		 			     grub_efi_uintn_t pages,
+                                             grub_efi_memory_type_t memtype);
 void *
 EXPORT_FUNC(grub_efi_allocate_pages) (grub_efi_physical_address_t address,
 				      grub_efi_uintn_t pages);
+void *
+EXPORT_FUNC(grub_efi_typed_allocate_pages) (grub_efi_physical_address_t address,
+					    grub_efi_uintn_t pages,
+					    grub_efi_allocate_type_t type,
+	                                    grub_efi_memory_type_t memtype);
 void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address,
 				       grub_efi_uintn_t pages);
 int
diff --git a/kern/efi/mm.c b/kern/efi/mm.c
index ceb8fc9..0138ed3 100644
--- a/kern/efi/mm.c
+++ b/kern/efi/mm.c
@@ -49,6 +49,20 @@ static struct allocated_page *allocated_pages = 0;
 #define MIN_HEAP_SIZE	0x100000
 #define MAX_HEAP_SIZE	(1600 * 0x100000)
 
+void *
+grub_efi_allocate_any_pages (grub_efi_uintn_t pages,
+                             grub_efi_memory_type_t memtype)
+{
+  return grub_efi_typed_allocate_pages (0, pages, GRUB_EFI_ALLOCATE_ANY_PAGES, memtype);
+}
+
+void *
+grub_efi_allocate_pages_before (grub_efi_physical_address_t address,
+		 		grub_efi_uintn_t pages,
+                                grub_efi_memory_type_t memtype)
+{
+  return grub_efi_typed_allocate_pages (address, pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, memtype);
+}
 
 /* Allocate pages. Return the pointer to the first of allocated pages.  */
 void *
@@ -56,14 +70,6 @@ grub_efi_allocate_pages (grub_efi_physical_address_t address,
 			 grub_efi_uintn_t pages)
 {
   grub_efi_allocate_type_t type;
-  grub_efi_status_t status;
-  grub_efi_boot_services_t *b;
-
-#if GRUB_TARGET_SIZEOF_VOID_P < 8
-  /* Limit the memory access to less than 4GB for 32-bit platforms.  */
-  if (address > 0xffffffff)
-    return 0;
-#endif
 
 #if GRUB_TARGET_SIZEOF_VOID_P < 8 || defined (MCMODEL_SMALL)
   if (address == 0)
@@ -80,20 +86,40 @@ grub_efi_allocate_pages (grub_efi_physical_address_t address,
     type = GRUB_EFI_ALLOCATE_ADDRESS;
 #endif
 
+  return grub_efi_typed_allocate_pages (address, pages, type, GRUB_EFI_LOADER_DATA);
+}
+
+void *
+grub_efi_typed_allocate_pages (grub_efi_physical_address_t address,
+			       grub_efi_uintn_t pages,
+			       grub_efi_allocate_type_t type,
+                               grub_efi_memory_type_t memtype)
+{
+  grub_efi_status_t status;
+  grub_efi_boot_services_t *b;
+
+#if GRUB_TARGET_SIZEOF_VOID_P < 8
+  /* Limit the memory access to less than 4GB for 32-bit platforms.  */
+  if (address > 0xffffffff)
+    return 0;
+#endif
+
   b = grub_efi_system_table->boot_services;
-  status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address);
-  if (status != GRUB_EFI_SUCCESS)
+  status = efi_call_4 (b->allocate_pages, type, memtype, pages, &address);
+  if (status != GRUB_EFI_SUCCESS) {
     return 0;
+  }
 
   if (address == 0)
     {
       /* Uggh, the address 0 was allocated... This is too annoying,
 	 so reallocate another one.  */
       address = 0xffffffff;
-      status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address);
+      status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, memtype, pages, &address);
       grub_efi_free_pages (0, pages);
-      if (status != GRUB_EFI_SUCCESS)
-	return 0;
+      if (status != GRUB_EFI_SUCCESS) {
+        return 0;
+      }
     }
 
   if (allocated_pages)
diff --git a/loader/i386/efi/linux.c b/loader/i386/efi/linux.c
index a6db22e..fda4362 100644
--- a/loader/i386/efi/linux.c
+++ b/loader/i386/efi/linux.c
@@ -161,10 +161,8 @@ free_pages (void)
 static int
 allocate_pages (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 *desc;
+  grub_efi_memory_descriptor_t *mmap;
+  grub_efi_uintn_t mmap_size;
   grub_size_t real_size;
 
   /* Make sure that each size is aligned to a page boundary.  */
@@ -184,53 +182,7 @@ allocate_pages (grub_size_t prot_size)
   real_mode_mem = 0;
   prot_mode_mem = 0;
 
-  /* Read the memory map temporarily, to find free space.  */
-  mmap = grub_malloc (mmap_size);
-  if (! mmap)
-    return 0;
-
-  tmp_mmap_size = mmap_size;
-  if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
-    grub_fatal ("cannot get memory map");
-
-  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
-
-  /* First, find free pages for the real mode code
-     and the memory map buffer.  */
-  for (desc = mmap;
-       desc < mmap_end;
-       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
-    {
-      /* Probably it is better to put the real mode code in the traditional
-	 space for safety.  */
-      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
-	  && desc->physical_start <= 0x90000
-	  && desc->num_pages >= real_mode_pages)
-	{
-	  grub_efi_physical_address_t physical_end;
-	  grub_efi_physical_address_t addr;
-
-	  physical_end = desc->physical_start + (desc->num_pages << 12);
-	  if (physical_end > 0x90000)
-	    physical_end = 0x90000;
-
-	  grub_dprintf ("linux", "physical_start = %x, physical_end = %x\n",
-			(unsigned) desc->physical_start,
-			(unsigned) physical_end);
-	  addr = physical_end - real_size - mmap_size;
-	  if (addr < 0x10000)
-	    continue;
-
-	  grub_dprintf ("linux", "trying to allocate %u pages at %lx\n",
-			(unsigned) real_mode_pages, (unsigned long) addr);
-	  real_mode_mem = grub_efi_allocate_pages (addr, real_mode_pages);
-	  if (! real_mode_mem)
-	    grub_fatal ("cannot allocate pages");
-
-	  desc->num_pages -= real_mode_pages;
-	  break;
-	}
-    }
+  real_mode_mem = grub_efi_allocate_pages_before (0x90000, real_mode_pages, GRUB_EFI_LOADER_DATA);
 
   if (! real_mode_mem)
     {
@@ -242,7 +194,7 @@ allocate_pages (grub_size_t prot_size)
 
   /* 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 + 1);
+  prot_mode_mem = grub_efi_allocate_any_pages(prot_mode_pages + 1, GRUB_EFI_LOADER_DATA);
   if (! prot_mode_mem)
     {
       grub_error (GRUB_ERR_OUT_OF_MEMORY,
@@ -900,11 +852,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
 {
   grub_file_t file = 0;
   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_uintn_t desc_size;
+  grub_addr_t addr_max;
   struct linux_kernel_header *lh;
 
   if (argc == 0)
@@ -938,48 +886,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
      worse than that of Linux 2.3.xx, so avoid the last 64kb.  */
   addr_max -= 0x10000;
 
-  /* Usually, the compression ratio is about 50%.  */
-  addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12)
-	     + page_align (size);
-
-  /* Find the highest address to put the initrd.  */
-  mmap_size = find_mmap_size ();
-  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0) <= 0)
-    grub_fatal ("cannot get memory map");
-
-  addr = 0;
-  for (desc = mmap_buf;
-       desc < NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
-       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
-    {
-      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
-	  && desc->num_pages >= initrd_pages)
-	{
-	  grub_efi_physical_address_t physical_end;
-
-	  physical_end = desc->physical_start + (desc->num_pages << 12);
-	  if (physical_end > addr_max)
-	    physical_end = addr_max;
-
-	  if (physical_end < page_align (size))
-	    continue;
-
-	  physical_end -= page_align (size);
-
-	  if ((physical_end >= addr_min) &&
-	      (physical_end >= desc->physical_start) &&
-	      (physical_end > addr))
-	    addr = physical_end;
-	}
-    }
-
-  if (addr == 0)
-    {
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, "no free pages available");
-      goto fail;
-    }
-
-  initrd_mem = grub_efi_allocate_pages (addr, initrd_pages);
+  initrd_mem = grub_efi_allocate_pages_before (addr_max, initrd_pages, GRUB_EFI_LOADER_DATA);
   if (! initrd_mem)
     grub_fatal ("cannot allocate pages");
 
@@ -989,10 +896,10 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
       goto fail;
     }
 
-  grub_printf ("   [Initrd, addr=0x%x, size=0x%x]\n",
-	       (unsigned) addr, (unsigned) size);
+  grub_printf ("   [Initrd, addr=0x%lx, size=0x%x]\n",
+	       (grub_addr_t) initrd_mem, (unsigned) size);
 
-  lh->ramdisk_image = addr;
+  lh->ramdisk_image = (grub_addr_t) initrd_mem;
   lh->ramdisk_size = size;
   lh->root_dev = 0x0100; /* XXX */
 
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to