mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-10-31 15:44:20 +00:00 
			
		
		
		
	 76ce1de740
			
		
	
	
		76ce1de740
		
	
	
	
	
		
			
			Define * GRUB_EFI_PERSISTENT_MEMORY (UEFI memory map type 14) per UEFI 2.5 * GRUB_MEMORY_PERSISTENT (E820 type 7) per ACPI 3.0 * GRUB_MEMORY_PERSISTENT_LEGACY (E820 unofficial type 12) per ACPI 3.0 and translate GRUB_EFI_PERSISTENT_MEMORY to GRUB_MEMORY_PERSISTENT in grub_efi_mmap_iterate(). Includes * adding the E820 names to lsmmap * handling the E820 types in make_efi_memtype() Suggested-by: Vladimir 'φ-coder/phcoder' Serbinenko <phcoder@gmail.com> Suggested-by: Andrei Borzenkov <arvidjaar@gmail.com>
		
			
				
	
	
		
			319 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Mmap management. */
 | |
| /*
 | |
|  *  GRUB  --  GRand Unified Bootloader
 | |
|  *  Copyright (C) 2009  Free Software Foundation, Inc.
 | |
|  *
 | |
|  *  GRUB is free software: you can redistribute it and/or modify
 | |
|  *  it under the terms of the GNU General Public License as published by
 | |
|  *  the Free Software Foundation, either version 3 of the License, or
 | |
|  *  (at your option) any later version.
 | |
|  *
 | |
|  *  GRUB is distributed in the hope that it will be useful,
 | |
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  *  GNU General Public License for more details.
 | |
|  *
 | |
|  *  You should have received a copy of the GNU General Public License
 | |
|  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include <grub/machine/memory.h>
 | |
| #include <grub/memory.h>
 | |
| #include <grub/err.h>
 | |
| #include <grub/efi/api.h>
 | |
| #include <grub/efi/efi.h>
 | |
| #include <grub/mm.h>
 | |
| #include <grub/misc.h>
 | |
| 
 | |
| #define NEXT_MEMORY_DESCRIPTOR(desc, size)      \
 | |
|   ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
 | |
| 
 | |
| grub_err_t
 | |
| grub_efi_mmap_iterate (grub_memory_hook_t hook, void *hook_data,
 | |
| 		       int avoid_efi_boot_services)
 | |
| {
 | |
|   grub_efi_uintn_t mmap_size = 0;
 | |
|   grub_efi_memory_descriptor_t *map_buf = 0;
 | |
|   grub_efi_uintn_t map_key = 0;
 | |
|   grub_efi_uintn_t desc_size = 0;
 | |
|   grub_efi_uint32_t desc_version = 0;
 | |
|   grub_efi_memory_descriptor_t *desc;
 | |
| 
 | |
|   if (grub_efi_get_memory_map (&mmap_size, map_buf,
 | |
| 			       &map_key, &desc_size,
 | |
| 			       &desc_version) < 0)
 | |
|     return grub_errno;
 | |
| 
 | |
|   map_buf = grub_malloc (mmap_size);
 | |
|   if (! map_buf)
 | |
|     return grub_errno;
 | |
| 
 | |
|   if (grub_efi_get_memory_map (&mmap_size, map_buf,
 | |
| 			       &map_key, &desc_size,
 | |
| 			       &desc_version) <= 0)
 | |
|     {
 | |
|       grub_free (map_buf);
 | |
|       return grub_errno;
 | |
|     }
 | |
| 
 | |
|   for (desc = map_buf;
 | |
|        desc < NEXT_MEMORY_DESCRIPTOR (map_buf, mmap_size);
 | |
|        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
 | |
|     {
 | |
|       grub_dprintf ("mmap", "EFI memory region 0x%llx-0x%llx: %d\n",
 | |
| 		    (unsigned long long) desc->physical_start,
 | |
| 		    (unsigned long long) desc->physical_start
 | |
| 		    + desc->num_pages * 4096, desc->type);
 | |
|       switch (desc->type)
 | |
| 	{
 | |
| 	case GRUB_EFI_BOOT_SERVICES_CODE:
 | |
| 	  if (!avoid_efi_boot_services)
 | |
| 	    {
 | |
| 	      hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		    GRUB_MEMORY_AVAILABLE, hook_data);
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  /* FALLTHROUGH */
 | |
| 	case GRUB_EFI_RUNTIME_SERVICES_CODE:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_CODE, hook_data);
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_EFI_UNUSABLE_MEMORY:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_BADRAM, hook_data);
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_EFI_BOOT_SERVICES_DATA:
 | |
| 	  if (!avoid_efi_boot_services)
 | |
| 	    {
 | |
| 	      hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		    GRUB_MEMORY_AVAILABLE, hook_data);
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  /* FALLTHROUGH */
 | |
| 	case GRUB_EFI_RESERVED_MEMORY_TYPE:
 | |
| 	case GRUB_EFI_RUNTIME_SERVICES_DATA:
 | |
| 	case GRUB_EFI_MEMORY_MAPPED_IO:
 | |
| 	case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE:
 | |
| 	case GRUB_EFI_PAL_CODE:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_RESERVED, hook_data);
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_EFI_LOADER_CODE:
 | |
| 	case GRUB_EFI_LOADER_DATA:
 | |
| 	case GRUB_EFI_CONVENTIONAL_MEMORY:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_AVAILABLE, hook_data);
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_EFI_ACPI_RECLAIM_MEMORY:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_ACPI, hook_data);
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_EFI_ACPI_MEMORY_NVS:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_NVS, hook_data);
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_EFI_PERSISTENT_MEMORY:
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_PERSISTENT, hook_data);
 | |
| 	break;
 | |
| 
 | |
| 	default:
 | |
| 	  grub_printf ("Unknown memory type %d, considering reserved\n",
 | |
| 		       desc->type);
 | |
| 	  hook (desc->physical_start, desc->num_pages * 4096,
 | |
| 		GRUB_MEMORY_RESERVED, hook_data);
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return GRUB_ERR_NONE;
 | |
| }
 | |
| 
 | |
| grub_err_t
 | |
| grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
 | |
| {
 | |
|   return grub_efi_mmap_iterate (hook, hook_data, 0);
 | |
| }
 | |
| 
 | |
| static inline grub_efi_memory_type_t
 | |
| make_efi_memtype (int type)
 | |
| {
 | |
|   switch (type)
 | |
|     {
 | |
|     case GRUB_MEMORY_CODE:
 | |
|       return GRUB_EFI_RUNTIME_SERVICES_CODE;
 | |
| 
 | |
|       /* No way to remove a chunk of memory from EFI mmap.
 | |
| 	 So mark it as unusable. */
 | |
|     case GRUB_MEMORY_HOLE:
 | |
|     /*
 | |
|      * AllocatePages() does not support GRUB_EFI_PERSISTENT_MEMORY,
 | |
|      * so no translation for GRUB_MEMORY_PERSISTENT or
 | |
|      * GRUB_MEMORY_PERSISTENT_LEGACY.
 | |
|      */
 | |
|     case GRUB_MEMORY_PERSISTENT:
 | |
|     case GRUB_MEMORY_PERSISTENT_LEGACY:
 | |
|     case GRUB_MEMORY_RESERVED:
 | |
|       return GRUB_EFI_UNUSABLE_MEMORY;
 | |
| 
 | |
|     case GRUB_MEMORY_AVAILABLE:
 | |
|       return GRUB_EFI_CONVENTIONAL_MEMORY;
 | |
| 
 | |
|     case GRUB_MEMORY_ACPI:
 | |
|       return GRUB_EFI_ACPI_RECLAIM_MEMORY;
 | |
| 
 | |
|     case GRUB_MEMORY_NVS:
 | |
|       return GRUB_EFI_ACPI_MEMORY_NVS;
 | |
|     }
 | |
| 
 | |
|   return GRUB_EFI_UNUSABLE_MEMORY;
 | |
| }
 | |
| 
 | |
| struct overlay
 | |
| {
 | |
|   struct overlay *next;
 | |
|   grub_efi_physical_address_t address;
 | |
|   grub_efi_uintn_t pages;
 | |
|   int handle;
 | |
| };
 | |
| 
 | |
| static struct overlay *overlays = 0;
 | |
| static int curhandle = 1;
 | |
| 
 | |
| int
 | |
| grub_mmap_register (grub_uint64_t start, grub_uint64_t size, int type)
 | |
| {
 | |
|   grub_uint64_t end = start + size;
 | |
|   grub_efi_physical_address_t address;
 | |
|   grub_efi_boot_services_t *b;
 | |
|   grub_efi_uintn_t pages;
 | |
|   grub_efi_status_t status;
 | |
|   struct overlay *curover;
 | |
| 
 | |
|   curover = (struct overlay *) grub_malloc (sizeof (struct overlay));
 | |
|   if (! curover)
 | |
|     return 0;
 | |
| 
 | |
|   b = grub_efi_system_table->boot_services;
 | |
|   address = start & (~0xfffULL);
 | |
|   pages = (end - address + 0xfff) >> 12;
 | |
|   status = efi_call_2 (b->free_pages, address, pages);
 | |
|   if (status != GRUB_EFI_SUCCESS && status != GRUB_EFI_NOT_FOUND)
 | |
|     {
 | |
|       grub_free (curover);
 | |
|       return 0;
 | |
|     }
 | |
|   status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ADDRESS,
 | |
| 		       make_efi_memtype (type), pages, &address);
 | |
|   if (status != GRUB_EFI_SUCCESS)
 | |
|     {
 | |
|       grub_free (curover);
 | |
|       return 0;
 | |
|     }
 | |
|   curover->next = overlays;
 | |
|   curover->handle = curhandle++;
 | |
|   curover->address = address;
 | |
|   curover->pages = pages;
 | |
|   overlays = curover;
 | |
| 
 | |
|   return curover->handle;
 | |
| }
 | |
| 
 | |
| grub_err_t
 | |
| grub_mmap_unregister (int handle)
 | |
| {
 | |
|   struct overlay *curover, *prevover;
 | |
|   grub_efi_boot_services_t *b;
 | |
| 
 | |
|   b = grub_efi_system_table->boot_services;
 | |
| 
 | |
| 
 | |
|   for (curover = overlays, prevover = 0; curover;
 | |
|        prevover = curover, curover = curover->next)
 | |
|     {
 | |
|       if (curover->handle == handle)
 | |
| 	{
 | |
| 	  efi_call_2 (b->free_pages, curover->address, curover->pages);
 | |
| 	  if (prevover != 0)
 | |
| 	    prevover->next = curover->next;
 | |
| 	  else
 | |
| 	    overlays = curover->next;
 | |
| 	  grub_free (curover);
 | |
| 	  return GRUB_ERR_NONE;
 | |
| 	}
 | |
|     }
 | |
|   return grub_error (GRUB_ERR_BUG, "handle %d not found", handle);
 | |
| }
 | |
| 
 | |
| /* Result is always page-aligned. */
 | |
| void *
 | |
| grub_mmap_malign_and_register (grub_uint64_t align __attribute__ ((unused)),
 | |
| 			       grub_uint64_t size,
 | |
| 			       int *handle, int type,
 | |
| 			       int flags __attribute__ ((unused)))
 | |
| {
 | |
|   grub_efi_physical_address_t address;
 | |
|   grub_efi_boot_services_t *b;
 | |
|   grub_efi_uintn_t pages;
 | |
|   grub_efi_status_t status;
 | |
|   struct overlay *curover;
 | |
|   grub_efi_allocate_type_t atype;
 | |
| 
 | |
|   curover = (struct overlay *) grub_malloc (sizeof (struct overlay));
 | |
|   if (! curover)
 | |
|     return 0;
 | |
| 
 | |
|   b = grub_efi_system_table->boot_services;
 | |
| 
 | |
|   address = 0xffffffff;
 | |
| 
 | |
| #if GRUB_TARGET_SIZEOF_VOID_P < 8
 | |
|   /* Limit the memory access to less than 4GB for 32-bit platforms.  */
 | |
|   atype = GRUB_EFI_ALLOCATE_MAX_ADDRESS;
 | |
| #else
 | |
|   atype = GRUB_EFI_ALLOCATE_ANY_PAGES;
 | |
| #endif
 | |
| 
 | |
|   pages = (size + 0xfff) >> 12;
 | |
|   status = efi_call_4 (b->allocate_pages, atype,
 | |
| 		       make_efi_memtype (type), pages, &address);
 | |
|   if (status != GRUB_EFI_SUCCESS)
 | |
|     {
 | |
|       grub_free (curover);
 | |
|       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, atype,
 | |
| 			   make_efi_memtype (type), pages, &address);
 | |
|       grub_efi_free_pages (0, pages);
 | |
|       if (status != GRUB_EFI_SUCCESS)
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|   curover->next = overlays;
 | |
|   curover->handle = curhandle++;
 | |
|   curover->address = address;
 | |
|   curover->pages = pages;
 | |
|   overlays = curover;
 | |
|   *handle = curover->handle;
 | |
| 
 | |
|   return (void *) (grub_addr_t) curover->address;
 | |
| }
 | |
| 
 | |
| void
 | |
| grub_mmap_free_and_unregister (int handle)
 | |
| {
 | |
|   grub_mmap_unregister (handle);
 | |
| }
 |