summaryrefslogtreecommitdiffstats
path: root/grub-core/mmap/efi/mmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/mmap/efi/mmap.c')
-rw-r--r--grub-core/mmap/efi/mmap.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/grub-core/mmap/efi/mmap.c b/grub-core/mmap/efi/mmap.c
new file mode 100644
index 0000000..bd495a1
--- /dev/null
+++ b/grub-core/mmap/efi/mmap.c
@@ -0,0 +1,318 @@
+/* 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);
+}