/* 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 . */ #include #include #include #include #include #include #include #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; 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: 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); }