diff options
author | Tom Rini <trini@konsulko.com> | 2024-10-15 13:46:08 -0600 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2024-10-15 13:46:08 -0600 |
commit | 77072f9efd3bdb62afc60b98f38d346e506e7e4f (patch) | |
tree | 0311d6efdb9d31d30ebcc8ec56b8e77f95e75996 | |
parent | 29502f6a344be06ad0bcd9076ec612b9e6a7d1cb (diff) | |
parent | 8d0df5fde3b8d4083936129e0c01884f9555f197 (diff) |
Merge patch series "Make EFI memory allocations synchronous with LMB"
Sughosh Ganu <sughosh.ganu@linaro.org> says:
This is part two of the series to have the EFI and LMB modules have a
coherent view of memory. Part one of this goal was to change the LMB
module to have a global and persistent memory map. Those patches have
now been applied to the next branch.
These patches are changing the EFI memory allocation API's such that
they rely on the LMB module to allocate RAM memory. This fixes the
current scenario where the EFI memory module has no visibility of the
allocations/reservations made by the LMB module. One thing to note
here is that this is limited to the RAM memory region, i.e. the
EFI_CONVENTIONAL_MEMORY type. Any other memory type that is to be
added to the EFI memory map, still gets handled by the EFI memory
module.
Changes since V3:
* Add comments for the LMB_NOOVERWRITE and LMB_NONOTIFY flags
* Drop use of is_addr_in_ram() function
* Drop use of CONFIG_MEM_MAP_UPDATE_NOTIFY symbol to check if the
notification needs to be sent.
* s/lmb_notify/lmb_should_notify
* Put a check for EFI_LOADER in the lmb_should_notify() function
Some test logs to highlight the issue that is being fixed by the series.
Without patch series
--------------------
lmb_dump_all:
memory.count = 0x1
memory[0] [0x40000000-0x820fffff], 0x42100000 bytes flags: none
reserved.count = 0x3
reserved[0] [0xe100000-0xeffffff], 0x00f00000 bytes flags: no-map
reserved[1] [0x42000000-0x421fffff], 0x00200000 bytes flags: no-map
reserved[2] [0x7f77da00-0x820fffff], 0x02982600 bytes flags: no-overwrite
=> efidebug memmap -- does not show regions allocated by lmb
Missing TPMv2 device for EFI_TCG_PROTOCOL
Type Start End Attributes
================ ================ ================ ==========
CONVENTIONAL 0000000040000000-000000007f751000 WB
BOOT DATA 000000007f751000-000000007f756000 WB
RUNTIME DATA 000000007f756000-000000007f757000 WB|RT
BOOT DATA 000000007f757000-000000007f758000 WB
RUNTIME DATA 000000007f758000-000000007f77a000 WB|RT
BOOT DATA 000000007f77a000-000000007f781000 WB
BOOT CODE 000000007f781000-00000000807b5000 WB
RUNTIME DATA 00000000807b5000-00000000807b6000 WB|RT
BOOT CODE 00000000807b6000-00000000817c0000 WB
RUNTIME CODE 00000000817c0000-00000000817d0000 WB|RT
BOOT CODE 00000000817d0000-0000000082100000 WB
=>
Trying to allocate EFI memory with already allocated region succeeds(should fail)
---------------------------------------------------------------------------------
=> efi_mem alloc 2000 42000000
Address returned 0x42000000
=> efidebug memmap
Type Start End Attributes
================ ================ ================ ==========
CONVENTIONAL 0000000040000000-0000000042000000 WB
BOOT DATA 0000000042000000-0000000042002000 WB
CONVENTIONAL 0000000042002000-000000007f751000 WB
BOOT DATA 000000007f751000-000000007f756000 WB
RUNTIME DATA 000000007f756000-000000007f757000 WB|RT
BOOT DATA 000000007f757000-000000007f758000 WB
RUNTIME DATA 000000007f758000-000000007f77a000 WB|RT
BOOT DATA 000000007f77a000-000000007f781000 WB
BOOT CODE 000000007f781000-00000000807b5000 WB
RUNTIME DATA 00000000807b5000-00000000807b6000 WB|RT
BOOT CODE 00000000807b6000-00000000817c0000 WB
RUNTIME CODE 00000000817c0000-00000000817d0000 WB|RT
BOOT CODE 00000000817d0000-0000000082100000 WB
=>
With patch series
-----------------
lmb_dump_all:
memory.count = 0x1
memory[0] [0x40000000-0x820fffff], 0x42100000 bytes flags: none
reserved.count = 0x4
reserved[0] [0xe100000-0xeffffff], 0x00f00000 bytes flags: no-map
reserved[1] [0x42000000-0x421fffff], 0x00200000 bytes flags: no-map
reserved[2] [0x7f74f000-0x7f77dfff], 0x0002f000 bytes flags: no-notify, no-overwrite
reserved[3] [0x7f77ea00-0x820fffff], 0x02981600 bytes flags: no-overwrite
=> efidebug memmap
Type Start End Attributes
================ ================ ================ ==========
BOOT DATA 000000000e100000-000000000f000000 WB
CONVENTIONAL 0000000040000000-0000000042000000 WB
BOOT DATA 0000000042000000-0000000042200000 WB
CONVENTIONAL 0000000042200000-000000007f74e000 WB
BOOT DATA 000000007f74e000-000000007f753000 WB
RUNTIME DATA 000000007f753000-000000007f754000 WB|RT
BOOT DATA 000000007f754000-000000007f755000 WB
RUNTIME DATA 000000007f755000-000000007f777000 WB|RT
BOOT DATA 000000007f777000-00000000807b6000 WB
RUNTIME DATA 00000000807b6000-00000000807b7000 WB|RT
BOOT DATA 00000000807b7000-00000000817c0000 WB
RUNTIME CODE 00000000817c0000-00000000817d0000 WB|RT
BOOT DATA 00000000817d0000-0000000082100000 WB
Trying to allocate EFI memory with already allocated region fails
-----------------------------------------------------------------
=> efi_mem alloc 2000 42000000
efi_allocate_pages failed 800000000000000e
=>
Trying to allocate EFI memory with non-allocated region succeeds
----------------------------------------------------------------
=> efi_mem alloc 2000 42200000
Address returned 0x42200000
=> efidebug memmap
Type Start End Attributes
================ ================ ================ ==========
BOOT DATA 000000000e100000-000000000f000000 WB
CONVENTIONAL 0000000040000000-0000000042000000 WB
BOOT DATA 0000000042000000-0000000042202000 WB
CONVENTIONAL 0000000042202000-000000007f74d000 WB
BOOT DATA 000000007f74d000-000000007f752000 WB
RUNTIME DATA 000000007f752000-000000007f753000 WB|RT
BOOT DATA 000000007f753000-000000007f754000 WB
RUNTIME DATA 000000007f754000-000000007f776000 WB|RT
BOOT DATA 000000007f776000-00000000807b5000 WB
RUNTIME DATA 00000000807b5000-00000000807b6000 WB|RT
BOOT DATA 00000000807b6000-00000000817c0000 WB
RUNTIME CODE 00000000817c0000-00000000817d0000 WB|RT
BOOT DATA 00000000817d0000-0000000082100000 WB
=>
lmb_dump_all:
memory.count = 0x1
memory[0] [0x40000000-0x820fffff], 0x42100000 bytes flags: none
reserved.count = 0x5
reserved[0] [0xe100000-0xeffffff], 0x00f00000 bytes flags: no-map
reserved[1] [0x42000000-0x421fffff], 0x00200000 bytes flags: no-map
reserved[2] [0x42200000-0x42201fff], 0x00002000 bytes flags: no-notify, no-overwrite
reserved[3] [0x7f74e000-0x7f77cfff], 0x0002f000 bytes flags: no-notify, no-overwrite
reserved[4] [0x7f77da00-0x820fffff], 0x02982600 bytes flags: no-overwrite
Link: https://lore.kernel.org/r/20241015153717.401371-1-sughosh.ganu@linaro.org
-rw-r--r-- | arch/arm/cpu/armv8/fsl-layerscape/cpu.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-k3/common.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/dram_init.c | 11 | ||||
-rw-r--r-- | arch/x86/lib/e820.c | 47 | ||||
-rw-r--r-- | include/efi_loader.h | 27 | ||||
-rw-r--r-- | include/lmb.h | 63 | ||||
-rw-r--r-- | lib/Kconfig | 19 | ||||
-rw-r--r-- | lib/efi_loader/Kconfig | 1 | ||||
-rw-r--r-- | lib/efi_loader/efi_memory.c | 217 | ||||
-rw-r--r-- | lib/lmb.c | 242 |
10 files changed, 389 insertions, 257 deletions
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c index f9c2083677a..d2d3e346a36 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c +++ b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c @@ -10,6 +10,7 @@ #include <env.h> #include <init.h> #include <hang.h> +#include <lmb.h> #include <log.h> #include <net.h> #include <vsprintf.h> @@ -1525,8 +1526,8 @@ int dram_init_banksize(void) return 0; } -#if CONFIG_IS_ENABLED(EFI_LOADER) -void efi_add_known_memory(void) +#if CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP) +void lmb_arch_add_memory(void) { int i; phys_addr_t ram_start; @@ -1548,8 +1549,7 @@ void efi_add_known_memory(void) gd->arch.resv_ram < ram_start + ram_size) ram_size = gd->arch.resv_ram - ram_start; #endif - efi_add_memory_map(ram_start, ram_size, - EFI_CONVENTIONAL_MEMORY); + lmb_add(ram_start, ram_size); } } #endif diff --git a/arch/arm/mach-k3/common.c b/arch/arm/mach-k3/common.c index df48ec8d479..f2086cbdf51 100644 --- a/arch/arm/mach-k3/common.c +++ b/arch/arm/mach-k3/common.c @@ -310,14 +310,3 @@ void setup_qos(void) writel(qos_data[i].val, (uintptr_t)qos_data[i].reg); } #endif - -void efi_add_known_memory(void) -{ - if (IS_ENABLED(CONFIG_EFI_LOADER)) - /* - * Memory over ram_top can be used by various firmware - * Declare to EFI only memory area below ram_top - */ - efi_add_memory_map(gd->ram_base, gd->ram_top - gd->ram_base, - EFI_CONVENTIONAL_MEMORY); -} diff --git a/arch/arm/mach-stm32mp/dram_init.c b/arch/arm/mach-stm32mp/dram_init.c index 198785353f1..3698fc49bf1 100644 --- a/arch/arm/mach-stm32mp/dram_init.c +++ b/arch/arm/mach-stm32mp/dram_init.c @@ -86,14 +86,3 @@ phys_addr_t board_get_usable_ram_top(phys_size_t total_size) return reg + size; } - -void efi_add_known_memory(void) -{ - if (IS_ENABLED(CONFIG_EFI_LOADER)) - /* - * Memory over ram_top is reserved to OPTEE. - * Declare to EFI only memory area below ram_top - */ - efi_add_memory_map(gd->ram_base, gd->ram_top - gd->ram_base, - EFI_CONVENTIONAL_MEMORY); -} diff --git a/arch/x86/lib/e820.c b/arch/x86/lib/e820.c index 122b4f7ca01..d478b7486e3 100644 --- a/arch/x86/lib/e820.c +++ b/arch/x86/lib/e820.c @@ -4,6 +4,7 @@ */ #include <efi_loader.h> +#include <lmb.h> #include <asm/e820.h> #include <asm/global_data.h> @@ -41,15 +42,11 @@ void efi_add_known_memory(void) { struct e820_entry e820[E820MAX]; unsigned int i, num; - u64 start, ram_top; + u64 start; int type; num = install_e820_map(ARRAY_SIZE(e820), e820); - ram_top = (u64)gd->ram_top & ~EFI_PAGE_MASK; - if (!ram_top) - ram_top = 0x100000000ULL; - for (i = 0; i < num; ++i) { start = e820[i].addr; @@ -72,13 +69,41 @@ void efi_add_known_memory(void) break; } - if (type == EFI_CONVENTIONAL_MEMORY) { - efi_add_conventional_memory_map(start, - start + e820[i].size, - ram_top); - } else { + if (type != EFI_CONVENTIONAL_MEMORY) efi_add_memory_map(start, e820[i].size, type); - } } } #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */ + +#if CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP) +void lmb_arch_add_memory(void) +{ + struct e820_entry e820[E820MAX]; + unsigned int i, num; + u64 ram_top; + + num = install_e820_map(ARRAY_SIZE(e820), e820); + + ram_top = (u64)gd->ram_top & ~EFI_PAGE_MASK; + if (!ram_top) + ram_top = 0x100000000ULL; + + for (i = 0; i < num; ++i) { + if (e820[i].type == E820_RAM) { + u64 start, size, rgn_top; + + start = e820[i].addr; + size = e820[i].size; + rgn_top = start + size; + + if (start > ram_top) + continue; + + if (rgn_top > ram_top) + size -= rgn_top - ram_top; + + lmb_add(start, size); + } + } +} +#endif /* CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP) */ diff --git a/include/efi_loader.h b/include/efi_loader.h index 511281e150e..291eca5c077 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -784,9 +784,21 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, uint32_t *descriptor_version); /* Adds a range into the EFI memory map */ efi_status_t efi_add_memory_map(u64 start, u64 size, int memory_type); -/* Adds a conventional range into the EFI memory map */ -efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end, - u64 ram_top); + +/** + * efi_add_memory_map_pg() - add pages 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_conventional: region may only overlap free(conventional) + * memory + * Return: status code + */ +efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, + int memory_type, + bool overlap_conventional); /* Called by board init to initialize the EFI drivers */ efi_status_t efi_driver_init(void); @@ -1172,9 +1184,14 @@ efi_status_t efi_console_get_u16_string efi_status_t efi_disk_get_device_name(const efi_handle_t handle, char *buf, int size); /** - * efi_add_known_memory() - add memory banks to EFI memory map + * efi_add_known_memory() - add memory types to the EFI memory map + * + * This function is to be used to add different memory types other + * than EFI_CONVENTIONAL_MEMORY to the EFI memory map. The conventional + * memory is handled by the LMB module and gets added to the memory + * map through the LMB module. * - * This weak function may be overridden for specific architectures. + * This function may be overridden for architectures specific purposes. */ void efi_add_known_memory(void); diff --git a/include/lmb.h b/include/lmb.h index aee2f9fcdaa..e46abf400c6 100644 --- a/include/lmb.h +++ b/include/lmb.h @@ -18,11 +18,14 @@ * enum lmb_flags - definition of memory region attributes * @LMB_NONE: no special request * @LMB_NOMAP: don't add to mmu configuration + * @LMB_NOOVERWRITE: the memory region cannot be overwritten/re-reserved + * @LMB_NONOTIFY: do not notify other modules of changes to this memory region */ enum lmb_flags { LMB_NONE = 0, LMB_NOMAP = BIT(1), LMB_NOOVERWRITE = BIT(2), + LMB_NONOTIFY = BIT(3), }; /** @@ -43,10 +46,12 @@ struct lmb_region { * * @free_mem: List of free memory regions * @used_mem: List of used/reserved memory regions + * @test: Is structure being used for LMB tests */ struct lmb { struct alist free_mem; struct alist used_mem; + bool test; }; /** @@ -91,6 +96,50 @@ phys_addr_t lmb_alloc_addr(phys_addr_t base, phys_size_t size); phys_size_t lmb_get_free_size(phys_addr_t addr); /** + * lmb_alloc_flags() - Allocate memory region with specified attributes + * @size: Size of the region requested + * @align: Alignment of the memory region requested + * @flags: Memory region attributes to be set + * + * Allocate a region of memory with the attributes specified through the + * parameter. + * + * Return: base address on success, 0 on error + */ +phys_addr_t lmb_alloc_flags(phys_size_t size, ulong align, uint flags); + +/** + * lmb_alloc_base_flags() - Allocate specified memory region with specified attributes + * @size: Size of the region requested + * @align: Alignment of the memory region requested + * @max_addr: Maximum address of the requested region + * @flags: Memory region attributes to be set + * + * Allocate a region of memory with the attributes specified through the + * parameter. The max_addr parameter is used to specify the maximum address + * below which the requested region should be allocated. + * + * Return: base address on success, 0 on error + */ +phys_addr_t lmb_alloc_base_flags(phys_size_t size, ulong align, + phys_addr_t max_addr, uint flags); + +/** + * lmb_alloc_addr_flags() - Allocate specified memory address with specified attributes + * @base: Base Address requested + * @size: Size of the region requested + * @flags: Memory region attributes to be set + * + * Allocate a region of memory with the attributes specified through the + * parameter. The base parameter is used to specify the base address + * of the requested region. + * + * Return: base address on success, 0 on error + */ +phys_addr_t lmb_alloc_addr_flags(phys_addr_t base, phys_size_t size, + uint flags); + +/** * lmb_is_reserved_flags() - test if address is in reserved region with flag bits set * * The function checks if a reserved region comprising @addr exists which has @@ -102,11 +151,25 @@ phys_size_t lmb_get_free_size(phys_addr_t addr); */ int lmb_is_reserved_flags(phys_addr_t addr, int flags); +/** + * lmb_free_flags() - Free up a region of memory + * @base: Base Address of region to be freed + * @size: Size of the region to be freed + * @flags: Memory region attributes + * + * Free up a region of memory. + * + * Return: 0 if successful, -1 on failure + */ +long lmb_free_flags(phys_addr_t base, phys_size_t size, uint flags); + long lmb_free(phys_addr_t base, phys_size_t size); void lmb_dump_all(void); void lmb_dump_all_force(void); +void lmb_arch_add_memory(void); + struct lmb *lmb_get(void); int lmb_push(struct lmb *store); void lmb_pop(struct lmb *store); diff --git a/lib/Kconfig b/lib/Kconfig index 67a60160dac..fc2db747dcc 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -1119,6 +1119,25 @@ config SPL_LMB SPL. This will require a malloc() implementation for defining the data structures needed for maintaining the LMB memory map. +config LMB_ARCH_MEM_MAP + bool "Add an architecture specific memory map" + depends on LMB + default y if FSL_LAYERSCAPE || X86 + help + Some architectures have special or unique aspects which need + consideration when adding memory ranges to the list of available + memory map. Enable this config in such scenarios which allow + architectures and boards to define their own memory map. + +config SPL_LMB_ARCH_MEM_MAP + bool "Add an architecture specific memory map" + depends on SPL_LMB + help + Some architectures have special or unique scenarios which need + consideration when adding memory ranges to the list of available + memory map. Enable this config in such scenarios which allow + architectures and boards to define their own memory map. + config PHANDLE_CHECK_SEQ bool "Enable phandle check while getting sequence number" help diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6f6fa8d629d..69b2c9360d8 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -20,6 +20,7 @@ config EFI_LOADER select DM_EVENT select EVENT_DYNAMIC select LIB_UUID + select LMB imply PARTITION_UUIDS select REGEX imply FAT diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index c6f1dd09456..b63b5cca71e 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -9,6 +9,7 @@ #include <efi_loader.h> #include <init.h> +#include <lmb.h> #include <log.h> #include <malloc.h> #include <mapmem.h> @@ -172,17 +173,19 @@ static void efi_mem_sort(void) /** * efi_mem_carve_out() - unmap memory region * - * @map: memory map - * @carve_desc: memory region to unmap - * @overlap_only_ram: the carved out region may only overlap RAM - * Return: the number of overlapping pages which have been - * removed from the map, - * EFI_CARVE_NO_OVERLAP, if the regions don't overlap, - * EFI_CARVE_OVERLAPS_NONRAM, if the carve and map overlap, - * and the map contains anything but free ram - * (only when overlap_only_ram is true), - * EFI_CARVE_LOOP_AGAIN, if the mapping list should be - * traversed again, as it has been altered. + * @map: memory map + * @carve_desc: memory region to unmap + * @overlap_conventional: the carved out region may only overlap free, + * or conventional memory + * Return: the number of overlapping pages which have been + * removed from the map, + * EFI_CARVE_NO_OVERLAP, if the regions don't + * overlap, EFI_CARVE_OVERLAPS_NONRAM, if the carve + * and map overlap, and the map contains anything + * but free ram(only when overlap_conventional is + * true), + * EFI_CARVE_LOOP_AGAIN, if the mapping list should + * be traversed again, as it has been altered. * * Unmaps all memory occupied by the carve_desc region from the list entry * pointed to by map. @@ -192,7 +195,7 @@ static void efi_mem_sort(void) */ static s64 efi_mem_carve_out(struct efi_mem_list *map, struct efi_mem_desc *carve_desc, - bool overlap_only_ram) + bool overlap_conventional) { struct efi_mem_list *newmap; struct efi_mem_desc *map_desc = &map->desc; @@ -207,7 +210,7 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, return EFI_CARVE_NO_OVERLAP; /* We're overlapping with non-RAM, warn the caller if desired */ - if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY)) + if (overlap_conventional && (map_desc->type != EFI_CONVENTIONAL_MEMORY)) return EFI_CARVE_OVERLAPS_NONRAM; /* Sanitize carve_start and carve_end to lie within our bounds */ @@ -257,15 +260,17 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, /** * efi_add_memory_map_pg() - add pages 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: region may only overlap RAM - * Return: status code + * @start: start address, must be a multiple of + * EFI_PAGE_SIZE + * @pages: number of pages to add + * @memory_type: type of memory added + * @overlap_conventional: region may only overlap free(conventional) + * memory + * Return: status code */ -static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, - int memory_type, - bool overlap_only_ram) +efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, + int memory_type, + bool overlap_conventional) { struct efi_mem_list *lmem; struct efi_mem_list *newlist; @@ -274,7 +279,8 @@ static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, struct efi_event *evt; EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__, - start, pages, memory_type, overlap_only_ram ? "yes" : "no"); + start, pages, memory_type, overlap_conventional ? + "yes" : "no"); if (memory_type >= EFI_MAX_MEMORY_TYPE) return EFI_INVALID_PARAMETER; @@ -311,7 +317,7 @@ static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, s64 r; r = efi_mem_carve_out(lmem, &newlist->desc, - overlap_only_ram); + overlap_conventional); switch (r) { case EFI_CARVE_OUT_OF_RESOURCES: free(newlist); @@ -347,7 +353,7 @@ static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, } } while (carve_again); - if (overlap_only_ram && (carved_pages != pages)) { + if (overlap_conventional && (carved_pages != pages)) { /* * The payload wanted to have RAM overlaps, but we overlapped * with an unallocated region. Error out. @@ -433,53 +439,6 @@ static efi_status_t efi_check_allocated(u64 addr, bool must_be_allocated) } /** - * efi_find_free_memory() - find free memory pages - * - * @len: size of memory area needed - * @max_addr: highest address to allocate - * Return: pointer to free memory area or 0 - */ -static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr) -{ - struct efi_mem_list *lmem; - - /* - * 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_entry(lmem, &efi_mem, link) { - struct efi_mem_desc *desc = &lmem->desc; - uint64_t desc_len = desc->num_pages << EFI_PAGE_SHIFT; - uint64_t desc_end = desc->physical_start + desc_len; - uint64_t curmax = min(max_addr, desc_end); - uint64_t ret = curmax - len; - - /* We only take memory from free RAM */ - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - /* Out of bounds for max_addr */ - if ((ret + len) > max_addr) - continue; - - /* Out of bounds for upper map limit */ - if ((ret + len) > desc_end) - continue; - - /* Out of bounds for lower map limit */ - if (ret < desc->physical_start) - continue; - - /* Return the highest address in this map within bounds */ - return ret; - } - - return 0; -} - -/** * efi_allocate_pages - allocate memory pages * * @type: type of allocation to be performed @@ -493,8 +452,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, efi_uintn_t pages, uint64_t *memory) { u64 len; + uint flags; efi_status_t ret; - uint64_t addr; + phys_addr_t addr; /* Check import parameters */ if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE && @@ -508,33 +468,37 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, (len >> EFI_PAGE_SHIFT) != (u64)pages) return EFI_OUT_OF_RESOURCES; + flags = LMB_NOOVERWRITE | LMB_NONOTIFY; switch (type) { case EFI_ALLOCATE_ANY_PAGES: /* Any page */ - addr = efi_find_free_memory(len, -1ULL); + addr = (u64)lmb_alloc_flags(len, EFI_PAGE_SIZE, flags); if (!addr) return EFI_OUT_OF_RESOURCES; break; case EFI_ALLOCATE_MAX_ADDRESS: /* Max address */ - addr = efi_find_free_memory(len, *memory); + addr = map_to_sysmem((void *)(uintptr_t)*memory); + addr = (u64)lmb_alloc_base_flags(len, EFI_PAGE_SIZE, addr, + flags); if (!addr) return EFI_OUT_OF_RESOURCES; break; case EFI_ALLOCATE_ADDRESS: if (*memory & EFI_PAGE_MASK) return EFI_NOT_FOUND; - /* Exact address, reserve it. The addr is already in *memory. */ - ret = efi_check_allocated(*memory, false); - if (ret != EFI_SUCCESS) + + addr = map_to_sysmem((void *)(uintptr_t)*memory); + addr = (u64)lmb_alloc_addr_flags(addr, len, flags); + if (!addr) return EFI_NOT_FOUND; - addr = *memory; break; default: /* UEFI doesn't specify other allocation types */ return EFI_INVALID_PARAMETER; } + addr = (u64)(uintptr_t)map_sysmem(addr, 0); /* Reserve that map in our memory maps */ ret = efi_add_memory_map_pg(addr, pages, memory_type, true); if (ret != EFI_SUCCESS) @@ -555,6 +519,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, */ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) { + u64 len; + uint flags; + long status; efi_status_t ret; ret = efi_check_allocated(memory, true); @@ -568,6 +535,13 @@ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) return EFI_INVALID_PARAMETER; } + flags = LMB_NOOVERWRITE | LMB_NONOTIFY; + len = (u64)pages << EFI_PAGE_SHIFT; + status = lmb_free_flags(map_to_sysmem((void *)(uintptr_t)memory), len, + flags); + if (status) + return EFI_NOT_FOUND; + ret = efi_add_memory_map_pg(memory, pages, EFI_CONVENTIONAL_MEMORY, false); if (ret != EFI_SUCCESS) @@ -814,82 +788,17 @@ efi_status_t efi_get_memory_map_alloc(efi_uintn_t *map_size, } /** - * efi_add_conventional_memory_map() - add a RAM memory area to the map + * efi_add_known_memory() - add memory types to the EFI memory map * - * @ram_start: start address of a RAM memory area - * @ram_end: end address of a RAM memory area - * @ram_top: max address to be used as conventional memory - * Return: status code - */ -efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end, - u64 ram_top) -{ - u64 pages; - - /* 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 */ - return EFI_INVALID_PARAMETER; - } - - pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT; - - efi_add_memory_map_pg(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_pg(ram_start, pages, - EFI_BOOT_SERVICES_DATA, true); - } else if (ram_top < ram_end) { - /* ram_top is inside this region, reserve parts */ - pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT; - - efi_add_memory_map_pg(ram_top, pages, - EFI_BOOT_SERVICES_DATA, true); - } - - return EFI_SUCCESS; -} - -/** - * efi_add_known_memory() - add memory banks to map + * This function is to be used to add different memory types other + * than EFI_CONVENTIONAL_MEMORY to the EFI memory map. The conventional + * memory is handled by the LMB module and gets added to the memory + * map through the LMB module. * - * This function may be overridden for specific architectures. + * This function may be overridden for architectures specific purposes. */ __weak void efi_add_known_memory(void) { - u64 ram_top = gd->ram_top & ~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_end, ram_start; - - ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0); - ram_end = ram_start + gd->bd->bi_dram[i].size; - - efi_add_conventional_memory_map(ram_start, ram_end, ram_top); - } } /** @@ -901,16 +810,6 @@ 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 = CONFIG_STACK_SIZE; - - /* Add U-Boot */ - uboot_start = ((uintptr_t)map_sysmem(gd->start_addr_sp, 0) - - uboot_stack_size) & ~EFI_PAGE_MASK; - uboot_pages = ((uintptr_t)map_sysmem(gd->ram_top - 1, 0) - - uboot_start + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; - efi_add_memory_map_pg(uboot_start, uboot_pages, EFI_BOOT_SERVICES_CODE, - false); #if defined(__aarch64__) /* diff --git a/lib/lmb.c b/lib/lmb.c index 380d92a6718..78fe2d4de7c 100644 --- a/lib/lmb.c +++ b/lib/lmb.c @@ -8,6 +8,7 @@ #include <alist.h> #include <efi_loader.h> +#include <event.h> #include <image.h> #include <mapmem.h> #include <lmb.h> @@ -22,15 +23,56 @@ DECLARE_GLOBAL_DATA_PTR; +#define MAP_OP_RESERVE (u8)0x1 +#define MAP_OP_FREE (u8)0x2 +#define MAP_OP_ADD (u8)0x3 + #define LMB_ALLOC_ANYWHERE 0 #define LMB_ALIST_INITIAL_SIZE 4 static struct lmb lmb; +static bool lmb_should_notify(enum lmb_flags flags) +{ + return !lmb.test && !(flags & LMB_NONOTIFY) && + CONFIG_IS_ENABLED(EFI_LOADER); +} + +static int __maybe_unused lmb_map_update_notify(phys_addr_t addr, + phys_size_t size, + u8 op) +{ + u64 efi_addr; + u64 pages; + efi_status_t status; + + if (op != MAP_OP_RESERVE && op != MAP_OP_FREE && op != MAP_OP_ADD) { + log_err("Invalid map update op received (%d)\n", op); + return -1; + } + + efi_addr = (uintptr_t)map_sysmem(addr, 0); + pages = efi_size_in_pages(size + (efi_addr & EFI_PAGE_MASK)); + efi_addr &= ~EFI_PAGE_MASK; + + status = efi_add_memory_map_pg(efi_addr, pages, + op == MAP_OP_RESERVE ? + EFI_BOOT_SERVICES_DATA : + EFI_CONVENTIONAL_MEMORY, + false); + if (status != EFI_SUCCESS) { + log_err("%s: LMB Map notify failure %lu\n", __func__, + status & ~EFI_ERROR_MASK); + return -1; + } else { + return 0; + } +} + static void lmb_print_region_flags(enum lmb_flags flags) { u64 bitpos; - const char *flag_str[] = { "none", "no-map", "no-overwrite" }; + const char *flag_str[] = { "none", "no-map", "no-overwrite", "no-notify" }; do { bitpos = flags ? fls(flags) - 1 : 0; @@ -162,38 +204,6 @@ static void lmb_fix_over_lap_regions(struct alist *lmb_rgn_lst, lmb_remove_region(lmb_rgn_lst, r2); } -/** - * efi_lmb_reserve() - add reservations for EFI memory - * - * Add reservations for all EFI memory areas that are not - * EFI_CONVENTIONAL_MEMORY. - * - * Return: 0 on success, 1 on failure - */ -static __maybe_unused int efi_lmb_reserve(void) -{ - struct efi_mem_desc *memmap = NULL, *map; - efi_uintn_t i, map_size = 0; - efi_status_t ret; - - ret = efi_get_memory_map_alloc(&map_size, &memmap); - if (ret != EFI_SUCCESS) - return 1; - - for (i = 0, map = memmap; i < map_size / sizeof(*map); ++map, ++i) { - if (map->type != EFI_CONVENTIONAL_MEMORY) { - lmb_reserve_flags(map_to_sysmem((void *)(uintptr_t) - map->physical_start), - map->num_pages * EFI_PAGE_SIZE, - map->type == EFI_RESERVED_MEMORY_TYPE - ? LMB_NOMAP : LMB_NONE); - } - } - efi_free_pool(memmap); - - return 0; -} - static void lmb_reserve_uboot_region(void) { int bank; @@ -240,9 +250,6 @@ static void lmb_reserve_common(void *fdt_blob) if (CONFIG_IS_ENABLED(OF_LIBFDT) && fdt_blob) boot_fdt_add_mem_rsv_regions(fdt_blob); - - if (CONFIG_IS_ENABLED(EFI_LOADER)) - efi_lmb_reserve(); } static __maybe_unused void lmb_reserve_common_spl(void) @@ -281,10 +288,12 @@ void lmb_add_memory(void) { int i; phys_size_t size; - phys_addr_t rgn_top; u64 ram_top = gd->ram_top; struct bd_info *bd = gd->bd; + if (CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP)) + return lmb_arch_add_memory(); + /* Assume a 4GB ram_top if not defined */ if (!ram_top) ram_top = 0x100000000ULL; @@ -292,16 +301,16 @@ void lmb_add_memory(void) for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { size = bd->bi_dram[i].size; if (size) { - if (bd->bi_dram[i].start > ram_top) - continue; - - rgn_top = bd->bi_dram[i].start + - bd->bi_dram[i].size; - - if (rgn_top > ram_top) - size -= rgn_top - ram_top; - lmb_add(bd->bi_dram[i].start, size); + + /* + * Reserve memory above ram_top as + * no-overwrite so that it cannot be + * allocated + */ + if (bd->bi_dram[i].start >= ram_top) + lmb_reserve_flags(bd->bi_dram[i].start, size, + LMB_NOOVERWRITE); } } } @@ -474,12 +483,20 @@ static long lmb_add_region(struct alist *lmb_rgn_lst, phys_addr_t base, /* This routine may be called with relocation disabled. */ long lmb_add(phys_addr_t base, phys_size_t size) { + long ret; struct alist *lmb_rgn_lst = &lmb.free_mem; - return lmb_add_region(lmb_rgn_lst, base, size); + ret = lmb_add_region(lmb_rgn_lst, base, size); + if (ret) + return ret; + + if (lmb_should_notify(LMB_NONE)) + return lmb_map_update_notify(base, size, MAP_OP_ADD); + + return 0; } -long lmb_free(phys_addr_t base, phys_size_t size) +static long _lmb_free(phys_addr_t base, phys_size_t size) { struct lmb_region *rgn; struct alist *lmb_rgn_lst = &lmb.used_mem; @@ -530,11 +547,49 @@ long lmb_free(phys_addr_t base, phys_size_t size) rgn[i].flags); } +/** + * lmb_free_flags() - Free up a region of memory + * @base: Base Address of region to be freed + * @size: Size of the region to be freed + * @flags: Memory region attributes + * + * Free up a region of memory. + * + * Return: 0 if successful, -1 on failure + */ +long lmb_free_flags(phys_addr_t base, phys_size_t size, + uint flags) +{ + long ret; + + ret = _lmb_free(base, size); + if (ret < 0) + return ret; + + if (lmb_should_notify(flags)) + return lmb_map_update_notify(base, size, MAP_OP_FREE); + + return ret; +} + +long lmb_free(phys_addr_t base, phys_size_t size) +{ + return lmb_free_flags(base, size, LMB_NONE); +} + long lmb_reserve_flags(phys_addr_t base, phys_size_t size, enum lmb_flags flags) { + long ret = 0; struct alist *lmb_rgn_lst = &lmb.used_mem; - return lmb_add_region_flags(lmb_rgn_lst, base, size, flags); + ret = lmb_add_region_flags(lmb_rgn_lst, base, size, flags); + if (ret < 0) + return -1; + + if (lmb_should_notify(flags)) + return lmb_map_update_notify(base, size, MAP_OP_RESERVE); + + return ret; } long lmb_reserve(phys_addr_t base, phys_size_t size) @@ -563,9 +618,11 @@ static phys_addr_t lmb_align_down(phys_addr_t addr, phys_size_t size) return addr & ~(size - 1); } -static phys_addr_t __lmb_alloc_base(phys_size_t size, ulong align, +static phys_addr_t _lmb_alloc_base(phys_size_t size, ulong align, phys_addr_t max_addr, enum lmb_flags flags) { + u8 op; + int ret; long i, rgn; phys_addr_t base = 0; phys_addr_t res_base; @@ -596,6 +653,15 @@ static phys_addr_t __lmb_alloc_base(phys_size_t size, ulong align, if (lmb_add_region_flags(&lmb.used_mem, base, size, flags) < 0) return 0; + + if (lmb_should_notify(flags)) { + op = MAP_OP_RESERVE; + ret = lmb_map_update_notify(base, size, + op); + if (ret) + return ret; + } + return base; } @@ -613,11 +679,28 @@ phys_addr_t lmb_alloc(phys_size_t size, ulong align) return lmb_alloc_base(size, align, LMB_ALLOC_ANYWHERE); } +/** + * lmb_alloc_flags() - Allocate memory region with specified attributes + * @size: Size of the region requested + * @align: Alignment of the memory region requested + * @flags: Memory region attributes to be set + * + * Allocate a region of memory with the attributes specified through the + * parameter. + * + * Return: base address on success, 0 on error + */ +phys_addr_t lmb_alloc_flags(phys_size_t size, ulong align, uint flags) +{ + return _lmb_alloc_base(size, align, LMB_ALLOC_ANYWHERE, + flags); +} + phys_addr_t lmb_alloc_base(phys_size_t size, ulong align, phys_addr_t max_addr) { phys_addr_t alloc; - alloc = __lmb_alloc_base(size, align, max_addr, LMB_NONE); + alloc = _lmb_alloc_base(size, align, max_addr, LMB_NONE); if (alloc == 0) printf("ERROR: Failed to allocate 0x%lx bytes below 0x%lx.\n", @@ -626,7 +709,34 @@ phys_addr_t lmb_alloc_base(phys_size_t size, ulong align, phys_addr_t max_addr) return alloc; } -static phys_addr_t __lmb_alloc_addr(phys_addr_t base, phys_size_t size, +/** + * lmb_alloc_base_flags() - Allocate specified memory region with specified attributes + * @size: Size of the region requested + * @align: Alignment of the memory region requested + * @max_addr: Maximum address of the requested region + * @flags: Memory region attributes to be set + * + * Allocate a region of memory with the attributes specified through the + * parameter. The max_addr parameter is used to specify the maximum address + * below which the requested region should be allocated. + * + * Return: base address on success, 0 on error + */ +phys_addr_t lmb_alloc_base_flags(phys_size_t size, ulong align, + phys_addr_t max_addr, uint flags) +{ + phys_addr_t alloc; + + alloc = _lmb_alloc_base(size, align, max_addr, flags); + + if (alloc == 0) + printf("ERROR: Failed to allocate 0x%lx bytes below 0x%lx.\n", + (ulong)size, (ulong)max_addr); + + return alloc; +} + +static phys_addr_t _lmb_alloc_addr(phys_addr_t base, phys_size_t size, enum lmb_flags flags) { long rgn; @@ -657,7 +767,25 @@ static phys_addr_t __lmb_alloc_addr(phys_addr_t base, phys_size_t size, */ phys_addr_t lmb_alloc_addr(phys_addr_t base, phys_size_t size) { - return __lmb_alloc_addr(base, size, LMB_NONE); + return _lmb_alloc_addr(base, size, LMB_NONE); +} + +/** + * lmb_alloc_addr_flags() - Allocate specified memory address with specified attributes + * @base: Base Address requested + * @size: Size of the region requested + * @flags: Memory region attributes to be set + * + * Allocate a region of memory with the attributes specified through the + * parameter. The base parameter is used to specify the base address + * of the requested region. + * + * Return: base address on success, 0 on error + */ +phys_addr_t lmb_alloc_addr_flags(phys_addr_t base, phys_size_t size, + uint flags) +{ + return _lmb_alloc_addr(base, size, flags); } /* Return number of bytes from a given address that are free */ @@ -703,7 +831,7 @@ int lmb_is_reserved_flags(phys_addr_t addr, int flags) return 0; } -static int lmb_setup(void) +static int lmb_setup(bool test) { bool ret; @@ -721,6 +849,8 @@ static int lmb_setup(void) return -ENOMEM; } + lmb.test = test; + return 0; } @@ -740,7 +870,7 @@ int lmb_init(void) { int ret; - ret = lmb_setup(); + ret = lmb_setup(false); if (ret) { log_info("Unable to init LMB\n"); return ret; @@ -768,7 +898,7 @@ int lmb_push(struct lmb *store) int ret; *store = lmb; - ret = lmb_setup(); + ret = lmb_setup(true); if (ret) return ret; |