X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=lib%2Fefi_loader%2Fefi_memory.c;h=4c0216b1d245a1a8914d34a6f3c83a7093adab05;hb=b225c92fd0c0b09b99e2290c5e42708f9046a7a2;hp=967c3f733e4c581e2e0adc13c7507d6cf74c71f5;hpb=20a619c61775d99aaed07cf69ce449de054358a4;p=oweals%2Fu-boot.git diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 967c3f733e..4c0216b1d2 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -7,14 +7,17 @@ #include #include -#include #include #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; +/* Magic number identifying memory allocated from pool */ +#define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2 + efi_uintn_t efi_memory_map_key; struct efi_mem_list { @@ -33,7 +36,12 @@ LIST_HEAD(efi_mem); void *efi_bounce_buffer; #endif -/* +/** + * efi_pool_allocation - memory block allocated from pool + * + * @num_pages: number of pages allocated + * @checksum: checksum + * * U-Boot services each EFI AllocatePool request as a separate * (multiple) page allocation. We have to track the number of pages * to be able to free the correct amount later. @@ -43,9 +51,26 @@ void *efi_bounce_buffer; */ struct efi_pool_allocation { u64 num_pages; + u64 checksum; char data[] __aligned(ARCH_DMA_MINALIGN); }; +/** + * checksum() - calculate checksum for memory allocated from pool + * + * @alloc: allocation header + * Return: checksum, always non-zero + */ +static u64 checksum(struct efi_pool_allocation *alloc) +{ + u64 addr = (uintptr_t)alloc; + u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^ + EFI_ALLOC_POOL_MAGIC; + if (!ret) + ++ret; + return ret; +} + /* * Sorts the memory list from highest address to lowest address * @@ -66,9 +91,54 @@ static int efi_mem_cmp(void *priv, struct list_head *a, struct list_head *b) return -1; } +static uint64_t desc_get_end(struct efi_mem_desc *desc) +{ + return desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT); +} + static void efi_mem_sort(void) { + struct list_head *lhandle; + struct efi_mem_list *prevmem = NULL; + bool merge_again = true; + list_sort(NULL, &efi_mem, efi_mem_cmp); + + /* Now merge entries that can be merged */ + while (merge_again) { + merge_again = false; + list_for_each(lhandle, &efi_mem) { + struct efi_mem_list *lmem; + struct efi_mem_desc *prev = &prevmem->desc; + struct efi_mem_desc *cur; + uint64_t pages; + + lmem = list_entry(lhandle, struct efi_mem_list, link); + if (!prevmem) { + prevmem = lmem; + continue; + } + + cur = &lmem->desc; + + if ((desc_get_end(cur) == prev->physical_start) && + (prev->type == cur->type) && + (prev->attribute == cur->attribute)) { + /* There is an existing map before, reuse it */ + pages = cur->num_pages; + prev->num_pages += pages; + prev->physical_start -= pages << EFI_PAGE_SHIFT; + prev->virtual_start -= pages << EFI_PAGE_SHIFT; + list_del(&lmem->link); + free(lmem); + + merge_again = true; + break; + } + + prevmem = lmem; + } + } } /** efi_mem_carve_out - unmap memory region @@ -123,6 +193,7 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, free(map); } else { map->desc.physical_start = carve_end; + map->desc.virtual_start = carve_end; map->desc.num_pages = (map_end - carve_end) >> EFI_PAGE_SHIFT; } @@ -141,6 +212,7 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, newmap = calloc(1, sizeof(*newmap)); newmap->desc = map->desc; newmap->desc.physical_start = carve_start; + newmap->desc.virtual_start = carve_start; newmap->desc.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT; /* Insert before current entry (descending address order) */ list_add_tail(&newmap->link, &map->link); @@ -151,22 +223,32 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, return EFI_CARVE_LOOP_AGAIN; } -uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, - bool overlap_only_ram) +/** + * efi_add_memory_map() - add memory area to the memory map + * + * @start: start address, must be a multiple of EFI_PAGE_SIZE + * @pages: number of pages to add + * @memory_type: type of memory added + * @overlap_only_ram: the memory area must overlap existing + * Return: status code + */ +efi_status_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, + bool overlap_only_ram) { struct list_head *lhandle; struct efi_mem_list *newlist; bool carve_again; uint64_t carved_pages = 0; + struct efi_event *evt; - debug("%s: 0x%" PRIx64 " 0x%" PRIx64 " %d %s\n", __func__, - start, pages, memory_type, overlap_only_ram ? "yes" : "no"); + EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__, + start, pages, memory_type, overlap_only_ram ? "yes" : "no"); if (memory_type >= EFI_MAX_MEMORY_TYPE) return EFI_INVALID_PARAMETER; if (!pages) - return start; + return EFI_SUCCESS; ++efi_memory_map_key; newlist = calloc(1, sizeof(*newlist)); @@ -178,14 +260,13 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, switch (memory_type) { case EFI_RUNTIME_SERVICES_CODE: case EFI_RUNTIME_SERVICES_DATA: - newlist->desc.attribute = (1 << EFI_MEMORY_WB_SHIFT) | - (1ULL << EFI_MEMORY_RUNTIME_SHIFT); + newlist->desc.attribute = EFI_MEMORY_WB | EFI_MEMORY_RUNTIME; break; case EFI_MMAP_IO: - newlist->desc.attribute = 1ULL << EFI_MEMORY_RUNTIME_SHIFT; + newlist->desc.attribute = EFI_MEMORY_RUNTIME; break; default: - newlist->desc.attribute = 1 << EFI_MEMORY_WB_SHIFT; + newlist->desc.attribute = EFI_MEMORY_WB; break; } @@ -205,7 +286,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, * The user requested to only have RAM overlaps, * but we hit a non-RAM region. Error out. */ - return 0; + return EFI_NO_MAPPING; case EFI_CARVE_NO_OVERLAP: /* Just ignore this list entry */ break; @@ -235,7 +316,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, * The payload wanted to have RAM overlaps, but we overlapped * with an unallocated region. Error out. */ - return 0; + return EFI_NO_MAPPING; } /* Add our new map */ @@ -244,13 +325,64 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, /* And make sure memory is listed in descending order */ efi_mem_sort(); - return start; + /* Notify that the memory map was changed */ + list_for_each_entry(evt, &efi_events, link) { + if (evt->group && + !guidcmp(evt->group, + &efi_guid_event_group_memory_map_change)) { + efi_signal_event(evt); + break; + } + } + + return EFI_SUCCESS; +} + +/** + * efi_check_allocated() - validate address to be freed + * + * Check that the address is within allocated memory: + * + * * The address must be in a range of the memory map. + * * The address may not point to EFI_CONVENTIONAL_MEMORY. + * + * Page alignment is not checked as this is not a requirement of + * efi_free_pool(). + * + * @addr: address of page to be freed + * @must_be_allocated: return success if the page is allocated + * Return: status code + */ +static efi_status_t efi_check_allocated(u64 addr, bool must_be_allocated) +{ + struct efi_mem_list *item; + + list_for_each_entry(item, &efi_mem, link) { + u64 start = item->desc.physical_start; + u64 end = start + (item->desc.num_pages << EFI_PAGE_SHIFT); + + if (addr >= start && addr < end) { + if (must_be_allocated ^ + (item->desc.type == EFI_CONVENTIONAL_MEMORY)) + return EFI_SUCCESS; + else + return EFI_NOT_FOUND; + } + } + + return EFI_NOT_FOUND; } static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr) { struct list_head *lhandle; + /* + * Prealign input max address, so we simplify our matching + * logic below and can just reuse it as return pointer. + */ + max_addr &= ~EFI_PAGE_MASK; + list_for_each(lhandle, &efi_mem) { struct efi_mem_list *lmem = list_entry(lhandle, struct efi_mem_list, link); @@ -296,9 +428,13 @@ efi_status_t efi_allocate_pages(int type, int memory_type, efi_uintn_t pages, uint64_t *memory) { u64 len = pages << EFI_PAGE_SHIFT; - efi_status_t r = EFI_SUCCESS; + efi_status_t ret; uint64_t addr; + /* Check import parameters */ + if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE && + memory_type <= 0x6FFFFFFF) + return EFI_INVALID_PARAMETER; if (!memory) return EFI_INVALID_PARAMETER; @@ -306,49 +442,41 @@ efi_status_t efi_allocate_pages(int type, int memory_type, case EFI_ALLOCATE_ANY_PAGES: /* Any page */ addr = efi_find_free_memory(len, -1ULL); - if (!addr) { - r = EFI_NOT_FOUND; - break; - } + if (!addr) + return EFI_OUT_OF_RESOURCES; break; case EFI_ALLOCATE_MAX_ADDRESS: /* Max address */ addr = efi_find_free_memory(len, *memory); - if (!addr) { - r = EFI_NOT_FOUND; - break; - } + if (!addr) + return EFI_OUT_OF_RESOURCES; break; case EFI_ALLOCATE_ADDRESS: /* Exact address, reserve it. The addr is already in *memory. */ + ret = efi_check_allocated(*memory, false); + if (ret != EFI_SUCCESS) + return EFI_NOT_FOUND; addr = *memory; break; default: /* UEFI doesn't specify other allocation types */ - r = EFI_INVALID_PARAMETER; - break; + return EFI_INVALID_PARAMETER; } - if (r == EFI_SUCCESS) { - uint64_t ret; + /* Reserve that map in our memory maps */ + if (efi_add_memory_map(addr, pages, memory_type, true) != EFI_SUCCESS) + /* Map would overlap, bail out */ + return EFI_OUT_OF_RESOURCES; - /* Reserve that map in our memory maps */ - ret = efi_add_memory_map(addr, pages, memory_type, true); - if (ret == addr) { - *memory = (uintptr_t)map_sysmem(addr, len); - } else { - /* Map would overlap, bail out */ - r = EFI_OUT_OF_RESOURCES; - } - } + *memory = addr; - return r; + return EFI_SUCCESS; } void *efi_alloc(uint64_t len, int memory_type) { uint64_t ret = 0; - uint64_t pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; + uint64_t pages = efi_size_in_pages(len); efi_status_t r; r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, memory_type, pages, @@ -359,41 +487,52 @@ void *efi_alloc(uint64_t len, int memory_type) return NULL; } -/* - * Free memory pages. +/** + * efi_free_pages() - free memory pages * - * @memory start of the memory area to be freed - * @pages number of pages to be freed - * @return status code + * @memory: start of the memory area to be freed + * @pages: number of pages to be freed + * Return: status code */ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) { - uint64_t r = 0; - uint64_t addr = map_to_sysmem((void *)(uintptr_t)memory); + efi_status_t ret; - r = efi_add_memory_map(addr, pages, EFI_CONVENTIONAL_MEMORY, false); + ret = efi_check_allocated(memory, true); + if (ret != EFI_SUCCESS) + return ret; + + /* Sanity check */ + if (!memory || (memory & EFI_PAGE_MASK) || !pages) { + printf("%s: illegal free 0x%llx, 0x%zx\n", __func__, + memory, pages); + return EFI_INVALID_PARAMETER; + } + + ret = efi_add_memory_map(memory, pages, EFI_CONVENTIONAL_MEMORY, false); /* Merging of adjacent free regions is missing */ - if (r == addr) - return EFI_SUCCESS; + if (ret != EFI_SUCCESS) + return EFI_NOT_FOUND; - return EFI_NOT_FOUND; + return ret; } -/* - * Allocate memory from pool. +/** + * efi_allocate_pool - allocate memory from pool * - * @pool_type type of the pool from which memory is to be allocated - * @size number of bytes to be allocated - * @buffer allocated memory - * @return status code + * @pool_type: type of the pool from which memory is to be allocated + * @size: number of bytes to be allocated + * @buffer: allocated memory + * Return: status code */ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer) { efi_status_t r; + u64 addr; struct efi_pool_allocation *alloc; - u64 num_pages = (size + sizeof(struct efi_pool_allocation) + - EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; + u64 num_pages = efi_size_in_pages(size + + sizeof(struct efi_pool_allocation)); if (!buffer) return EFI_INVALID_PARAMETER; @@ -404,37 +543,49 @@ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer) } r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages, - (uint64_t *)&alloc); - + &addr); if (r == EFI_SUCCESS) { + alloc = (struct efi_pool_allocation *)(uintptr_t)addr; alloc->num_pages = num_pages; + alloc->checksum = checksum(alloc); *buffer = alloc->data; } return r; } -/* - * Free memory from pool. +/** + * efi_free_pool() - free memory from pool * - * @buffer start of memory to be freed - * @return status code + * @buffer: start of memory to be freed + * Return: status code */ efi_status_t efi_free_pool(void *buffer) { - efi_status_t r; + efi_status_t ret; struct efi_pool_allocation *alloc; - if (buffer == NULL) + if (!buffer) return EFI_INVALID_PARAMETER; + ret = efi_check_allocated((uintptr_t)buffer, true); + if (ret != EFI_SUCCESS) + return ret; + alloc = container_of(buffer, struct efi_pool_allocation, data); - /* Sanity check, was the supplied address returned by allocate_pool */ - assert(((uintptr_t)alloc & EFI_PAGE_MASK) == 0); - r = efi_free_pages((uintptr_t)alloc, alloc->num_pages); + /* Check that this memory was allocated by efi_allocate_pool() */ + if (((uintptr_t)alloc & EFI_PAGE_MASK) || + alloc->checksum != checksum(alloc)) { + printf("%s: illegal free 0x%p\n", __func__, buffer); + return EFI_INVALID_PARAMETER; + } + /* Avoid double free */ + alloc->checksum = 0; - return r; + ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages); + + return ret; } /* @@ -457,11 +608,13 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, efi_uintn_t map_size = 0; int map_entries = 0; struct list_head *lhandle; - efi_uintn_t provided_map_size = *memory_map_size; + efi_uintn_t provided_map_size; if (!memory_map_size) return EFI_INVALID_PARAMETER; + provided_map_size = *memory_map_size; + list_for_each(lhandle, &efi_mem) map_entries++; @@ -500,17 +653,57 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, __weak void efi_add_known_memory(void) { + u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK; int i; + /* + * ram_top is just outside mapped memory. So use an offset of one for + * mapping the sandbox address. + */ + ram_top = (uintptr_t)map_sysmem(ram_top - 1, 0) + 1; + + /* Fix for 32bit targets with ram_top at 4G */ + if (!ram_top) + ram_top = 0x100000000ULL; + /* Add RAM */ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - u64 ram_start = gd->bd->bi_dram[i].start; - u64 ram_size = gd->bd->bi_dram[i].size; - u64 start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; - u64 pages = (ram_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; + u64 ram_end, ram_start, pages; + + ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0); + ram_end = ram_start + gd->bd->bi_dram[i].size; + + /* Remove partial pages */ + ram_end &= ~EFI_PAGE_MASK; + ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + + if (ram_end <= ram_start) { + /* Invalid mapping, keep going. */ + continue; + } + + pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT; - efi_add_memory_map(start, pages, EFI_CONVENTIONAL_MEMORY, - false); + efi_add_memory_map(ram_start, pages, + EFI_CONVENTIONAL_MEMORY, false); + + /* + * Boards may indicate to the U-Boot memory core that they + * can not support memory above ram_top. Let's honor this + * in the efi_loader subsystem too by declaring any memory + * above ram_top as "already occupied by firmware". + */ + if (ram_top < ram_start) { + /* ram_top is before this region, reserve all */ + efi_add_memory_map(ram_start, pages, + EFI_BOOT_SERVICES_DATA, true); + } else if ((ram_top >= ram_start) && (ram_top < ram_end)) { + /* ram_top is inside this region, reserve parts */ + pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(ram_top, pages, + EFI_BOOT_SERVICES_DATA, true); + } } } @@ -518,6 +711,7 @@ __weak void efi_add_known_memory(void) static void add_u_boot_and_runtime(void) { unsigned long runtime_start, runtime_end, runtime_pages; + unsigned long runtime_mask = EFI_PAGE_MASK; unsigned long uboot_start, uboot_pages; unsigned long uboot_stack_size = 16 * 1024 * 1024; @@ -526,10 +720,22 @@ static void add_u_boot_and_runtime(void) uboot_pages = (gd->ram_top - uboot_start) >> EFI_PAGE_SHIFT; efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false); - /* Add Runtime Services */ - runtime_start = (ulong)&__efi_runtime_start & ~EFI_PAGE_MASK; +#if defined(__aarch64__) + /* + * Runtime Services must be 64KiB aligned according to the + * "AArch64 Platforms" section in the UEFI spec (2.7+). + */ + + runtime_mask = SZ_64K - 1; +#endif + + /* + * Add Runtime Services. We mark surrounding boottime code as runtime as + * well to fulfill the runtime alignment constraints but avoid padding. + */ + runtime_start = (ulong)&__efi_runtime_start & ~runtime_mask; runtime_end = (ulong)&__efi_runtime_stop; - runtime_end = (runtime_end + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + runtime_end = (runtime_end + runtime_mask) & ~runtime_mask; runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT; efi_add_memory_map(runtime_start, runtime_pages, EFI_RUNTIME_SERVICES_CODE, false);