[U-Boot] [PATCH 0/9] efi_loader: fix fdt handling

This patch series fixes several errors in the handling of addresses on the sandbox when setting up the flattened device tree.
The memory type used for the fdt is corrected. It is checked by a unit test.
A macro is supplied to calculate the numbers of pages needed for a buffer.
Heinrich Schuchardt (9): EDT_support: fdt reservations on the sandbox efi_loader: fix efi_find_free_memory() efi_loader: memory reservation for fdt efi_loader: carving out memory reservations efi_loader: correct efi_add_known_memory() efi_loader: macro efi_size_in_pages() efi_loader: fix memory mapping for sandbox efi_loader: do not use magic address for fdt efi_selftest: check fdt is marked as runtime data
cmd/bootefi.c | 79 +++++++++++--------------- common/fdt_support.c | 6 +- include/efi_loader.h | 11 +++- lib/efi_loader/efi_memory.c | 29 +++++++--- lib/efi_selftest/efi_selftest_memory.c | 24 ++++++++ 5 files changed, 91 insertions(+), 58 deletions(-)

On the sandbox the memory addresses in the device tree refer to the virtual address space of the sandbox. This implies that the memory reservations for the fdt also have to be converted to this address space.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- common/fdt_support.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/common/fdt_support.c b/common/fdt_support.c index e6daa67990d..ec6af92dbba 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <mapmem.h> #include <stdio_dev.h> #include <linux/ctype.h> #include <linux/types.h> @@ -240,7 +241,8 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) } }
- err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start); + err = fdt_add_mem_rsv((void *)(uintptr_t)map_to_sysmem(fdt), + initrd_start, initrd_end - initrd_start); if (err < 0) { printf("fdt_initrd: %s\n", fdt_strerror(err)); return err; @@ -633,7 +635,8 @@ int fdt_shrink_to_minimum(void *blob, uint extrasize) fdt_set_totalsize(blob, actualsize);
/* Add the new reservation */ - ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize); + ret = fdt_add_mem_rsv((void *)(uintptr_t)map_to_sysmem(blob), + (uintptr_t)blob, actualsize); if (ret < 0) return ret;

In efi_find_free_memory() the sandbox uses its virtual address space. Add the missing mapping.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_memory.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index dc282fe249f..c0277355056 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -294,6 +294,9 @@ static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr) { struct list_head *lhandle;
+ /* Map to virtual address on sandbox */ + max_addr = map_to_sysmem((void *)(uintptr_t)max_addr); + /* * Prealign input max address, so we simplify our matching * logic below and can just reuse it as return pointer.

In copy_fdt() we allocate EFI pages for the fdt plus extra 12 KiB as EFI_RUNTIME_SERVICES_DATA. Afterwards in efi_install_fdt() we overwrite part of this memory allocation by marking it as EFI_BOOT_SERVICES_DATA.
Remove the code marking the fdt as EFI_BOOT_SERVICES_DATA.
Cf. commit 17ff6f02f5ad ("efi_loader: store DT in EFI_RUNTIME_SERVICES_DATA memory")
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- cmd/bootefi.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index e16ed4adba9..f2950817a13 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -157,12 +157,11 @@ static void set_load_options(struct efi_loaded_image *loaded_image_info, * An additional 12KB is added to the space in case the device tree needs to be * expanded later with fdt_open_into(). * - * @fdt_addr: On entry, address of start of FDT. On exit, address of relocated - * FDT start - * @fdt_sizep: Returns new size of FDT, including - * @return new relocated address of FDT + * @fdt_addr: On entry, address of start of FDT. On exit, address of relocated + * FDT start + * Return: status code */ -static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep) +static efi_status_t copy_fdt(ulong *fdt_addrp) { unsigned long fdt_ram_start = -1L, fdt_pages; efi_status_t ret = 0; @@ -214,7 +213,6 @@ static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep) fdt_set_totalsize(new_fdt, fdt_size);
*fdt_addrp = new_fdt_addr; - *fdt_sizep = fdt_size; done: return ret; } @@ -292,7 +290,6 @@ static void efi_carve_out_dt_rsv(void *fdt) static efi_status_t efi_install_fdt(ulong fdt_addr) { bootm_headers_t img = { 0 }; - ulong fdt_pages, fdt_size, fdt_start; efi_status_t ret; void *fdt;
@@ -303,13 +300,12 @@ static efi_status_t efi_install_fdt(ulong fdt_addr) }
/* Prepare fdt for payload */ - ret = copy_fdt(&fdt_addr, &fdt_size); + ret = copy_fdt(&fdt_addr); if (ret) return ret;
unmap_sysmem(fdt); fdt = map_sysmem(fdt_addr, 0); - fdt_size = fdt_totalsize(fdt); if (image_setup_libfdt(&img, fdt, 0, NULL)) { printf("ERROR: failed to process device tree\n"); return EFI_LOAD_ERROR; @@ -322,13 +318,6 @@ static efi_status_t efi_install_fdt(ulong fdt_addr) if (ret != EFI_SUCCESS) return EFI_OUT_OF_RESOURCES;
- /* And reserve the space in the memory map */ - fdt_start = fdt_addr; - fdt_pages = fdt_size >> EFI_PAGE_SHIFT; - - ret = efi_add_memory_map(fdt_start, fdt_pages, - EFI_BOOT_SERVICES_DATA, true); - return ret; }

The "Devicetree Specification 0.2" does not prescribe that memory reservations must be EFI page aligned. So let's not make such an assumption in our code.
Do not carve out the pages for the device tree. This memory area is already marked as EFI_RUNTIME_SERVICES_DATA.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- cmd/bootefi.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index f2950817a13..c73e6228d3e 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -280,7 +280,16 @@ static void efi_carve_out_dt_rsv(void *fdt) if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0) continue;
- pages = ALIGN(size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT; + /* + * Do not carve out the device tree. It is already marked as + * EFI_RUNTIME_SERVICES_DATA + */ + if (addr == (uintptr_t)fdt) + continue; + + pages = ALIGN(size + (addr & EFI_PAGE_MASK), EFI_PAGE_SIZE) >> + EFI_PAGE_SHIFT; + addr &= ~EFI_PAGE_MASK; if (!efi_add_memory_map(addr, pages, EFI_RESERVED_MEMORY_TYPE, false)) printf("FDT memrsv map %d: Failed to add to map\n", i);

If a memory bank is not EFI_PAGE_SIZE aligned efi_add_known_memory() the number of memory pages may be incorrectly calculated.
We have to round up the start address and to round down the end address to determine which complete pages are provided by the memory bank.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_memory.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index c0277355056..490e4921cce 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -563,13 +563,21 @@ __weak void efi_add_known_memory(void)
/* 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;
- efi_add_memory_map(start, pages, EFI_CONVENTIONAL_MEMORY, - false); + ram_start = gd->bd->bi_dram[i].start; + 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) { + pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(ram_start, pages, + EFI_CONVENTIONAL_MEMORY, false); + } } }

When allocating EFI memory pages the size in bytes has to be converted to pages.
Provide a macro efi_size_in_pages() for this conversion. Use it in the EFI subsystem and correct related comments.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- cmd/bootefi.c | 15 ++++++--------- include/efi_loader.h | 11 ++++++++++- lib/efi_loader/efi_memory.c | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index c73e6228d3e..2c9b2eb8b6f 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -154,7 +154,7 @@ static void set_load_options(struct efi_loaded_image *loaded_image_info, * copy_fdt() - Copy the device tree to a new location available to EFI * * The FDT is relocated into a suitable location within the EFI memory map. - * An additional 12KB is added to the space in case the device tree needs to be + * Additional 12 KiB are added to the space in case the device tree needs to be * expanded later with fdt_open_into(). * * @fdt_addr: On entry, address of start of FDT. On exit, address of relocated @@ -182,14 +182,12 @@ static efi_status_t copy_fdt(ulong *fdt_addrp) }
/* - * Give us at least 4KB of breathing room in case the device tree needs - * to be expanded later. Round up to the nearest EFI page boundary. + * Give us at least 12 KiB of breathing room in case the device tree + * needs to be expanded later. */ fdt = map_sysmem(*fdt_addrp, 0); - fdt_size = fdt_totalsize(fdt); - fdt_size += 4096 * 3; - fdt_size = ALIGN(fdt_size + EFI_PAGE_SIZE - 1, EFI_PAGE_SIZE); - fdt_pages = fdt_size >> EFI_PAGE_SHIFT; + fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000); + fdt_size = fdt_pages << EFI_PAGE_SHIFT;
/* Safe fdt location is at 127MB */ new_fdt_addr = fdt_ram_start + (127 * 1024 * 1024) + fdt_size; @@ -287,8 +285,7 @@ static void efi_carve_out_dt_rsv(void *fdt) if (addr == (uintptr_t)fdt) continue;
- pages = ALIGN(size + (addr & EFI_PAGE_MASK), EFI_PAGE_SIZE) >> - EFI_PAGE_SHIFT; + pages = efi_size_in_pages(size + (addr & EFI_PAGE_MASK)); addr &= ~EFI_PAGE_MASK; if (!efi_add_memory_map(addr, pages, EFI_RESERVED_MEMORY_TYPE, false)) diff --git a/include/efi_loader.h b/include/efi_loader.h index bdb806cfce4..244e754e8fd 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -350,7 +350,16 @@ struct efi_simple_file_system_protocol *efi_simple_file_system( /* open file from device-path: */ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
- +/** + * efi_size_in_pages() - convert size in bytes to size in pages + * + * This macro returns the number of EFI memory pages required to hold 'size' + * bytes. + * + * @size: size in bytes + * Return: size in pages + */ +#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT) /* Generic EFI memory allocator, call this to get memory */ void *efi_alloc(uint64_t len, int memory_type); /* More specific EFI memory allocator, called by EFI payloads */ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 490e4921cce..5c387fa8024 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -400,7 +400,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type, 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, @@ -444,8 +444,8 @@ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer) { efi_status_t r; 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;

The sandbox is using a virtual address space. The addresses used insided the flattened device tree use the virtual address space. The EFI subsystem uses the addressable address space and this is where the fdt is stored.
Fix all incorrect addresses for the fdt.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- cmd/bootefi.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 2c9b2eb8b6f..7752f3dec63 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -185,12 +185,16 @@ static efi_status_t copy_fdt(ulong *fdt_addrp) * Give us at least 12 KiB of breathing room in case the device tree * needs to be expanded later. */ - fdt = map_sysmem(*fdt_addrp, 0); + fdt = (void *)*fdt_addrp; fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000); fdt_size = fdt_pages << EFI_PAGE_SHIFT;
- /* Safe fdt location is at 127MB */ - new_fdt_addr = fdt_ram_start + (127 * 1024 * 1024) + fdt_size; + /* + * Safe fdt location is at 127 MiB. On the sandbox convert from the + * virtual address space. + */ + new_fdt_addr = (uintptr_t)map_sysmem(fdt_ram_start + 0x7f00000 + + fdt_size, 0); ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_RUNTIME_SERVICES_DATA, fdt_pages, &new_fdt_addr); @@ -205,8 +209,7 @@ static efi_status_t copy_fdt(ulong *fdt_addrp) goto done; } } - - new_fdt = map_sysmem(new_fdt_addr, fdt_size); + new_fdt = (void *)(uintptr_t)new_fdt_addr; memcpy(new_fdt, fdt, fdt_totalsize(fdt)); fdt_set_totalsize(new_fdt, fdt_size);
@@ -278,6 +281,9 @@ static void efi_carve_out_dt_rsv(void *fdt) if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0) continue;
+ /* Convert from sandbox address space. */ + addr = (uintptr_t)map_sysmem(addr, 0); + /* * Do not carve out the device tree. It is already marked as * EFI_RUNTIME_SERVICES_DATA @@ -297,9 +303,8 @@ static efi_status_t efi_install_fdt(ulong fdt_addr) { bootm_headers_t img = { 0 }; efi_status_t ret; - void *fdt; + void *fdt = (void *)fdt_addr;
- fdt = map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt)) { printf("ERROR: invalid device tree\n"); return EFI_INVALID_PARAMETER; @@ -310,8 +315,6 @@ static efi_status_t efi_install_fdt(ulong fdt_addr) if (ret) return ret;
- unmap_sysmem(fdt); - fdt = map_sysmem(fdt_addr, 0); if (image_setup_libfdt(&img, fdt, 0, NULL)) { printf("ERROR: failed to process device tree\n"); return EFI_LOAD_ERROR;

We currently place the flattened device tree 127 MiB from the start of the first memory bank. This may be in a reserved memory area.
So let's change the sequence: first create memory reservations and then copy the device tree.
Do not use any magic address.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- cmd/bootefi.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 7752f3dec63..25991fd2d2d 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -189,32 +189,20 @@ static efi_status_t copy_fdt(ulong *fdt_addrp) fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000); fdt_size = fdt_pages << EFI_PAGE_SHIFT;
- /* - * Safe fdt location is at 127 MiB. On the sandbox convert from the - * virtual address space. - */ - new_fdt_addr = (uintptr_t)map_sysmem(fdt_ram_start + 0x7f00000 + - fdt_size, 0); + new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size); ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_RUNTIME_SERVICES_DATA, fdt_pages, &new_fdt_addr); if (ret != EFI_SUCCESS) { - /* If we can't put it there, put it somewhere */ - new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size); - ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_RUNTIME_SERVICES_DATA, fdt_pages, - &new_fdt_addr); - if (ret != EFI_SUCCESS) { - printf("ERROR: Failed to reserve space for FDT\n"); - goto done; - } + printf("ERROR: Failed to allocate memory for FDT\n"); + return ret; } new_fdt = (void *)(uintptr_t)new_fdt_addr; memcpy(new_fdt, fdt, fdt_totalsize(fdt)); fdt_set_totalsize(new_fdt, fdt_size);
*fdt_addrp = new_fdt_addr; -done: + return ret; }
@@ -310,6 +298,9 @@ static efi_status_t efi_install_fdt(ulong fdt_addr) return EFI_INVALID_PARAMETER; }
+ /* Create memory reservation as indicated by the device tree */ + efi_carve_out_dt_rsv(fdt); + /* Prepare fdt for payload */ ret = copy_fdt(&fdt_addr); if (ret) @@ -320,8 +311,6 @@ static efi_status_t efi_install_fdt(ulong fdt_addr) return EFI_LOAD_ERROR; }
- efi_carve_out_dt_rsv(fdt); - /* Link to it in the efi tables */ ret = efi_install_configuration_table(&efi_guid_fdt, fdt); if (ret != EFI_SUCCESS)

Check that the memory area containing the device tree is marked as runtime data.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/efi_selftest_memory.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/lib/efi_selftest/efi_selftest_memory.c b/lib/efi_selftest/efi_selftest_memory.c index 0623e8e62da..24b4438ce4f 100644 --- a/lib/efi_selftest/efi_selftest_memory.c +++ b/lib/efi_selftest/efi_selftest_memory.c @@ -6,13 +6,17 @@ * * This unit test checks the following runtime services: * AllocatePages, FreePages, GetMemoryMap + * + * The memory type used for the device tree is checked. */
#include <efi_selftest.h>
#define EFI_ST_NUM_PAGES 8
+static const efi_guid_t fdt_guid = EFI_FDT_GUID; static struct efi_boot_services *boottime; +static u64 fdt_addr;
/** * setup() - setup unit test @@ -24,8 +28,20 @@ static struct efi_boot_services *boottime; static int setup(const efi_handle_t handle, const struct efi_system_table *systable) { + size_t i; + boottime = systable->boottime;
+ for (i = 0; i < systable->nr_tables; ++i) { + if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid, + sizeof(efi_guid_t))) { + if (fdt_addr) { + efi_st_error("Duplicate device tree\n"); + return EFI_ST_FAILURE; + } + fdt_addr = (uintptr_t)systable->tables[i].table; + } + } return EFI_ST_SUCCESS; }
@@ -152,6 +168,14 @@ static int execute(void) return EFI_ST_FAILURE; }
+ /* Check memory reservation for the device tree */ + if (fdt_addr && + find_in_memory_map(map_size, memory_map, desc_size, fdt_addr, + EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS) { + efi_st_error + ("Device tree not marked as runtime services data\n"); + return EFI_ST_FAILURE; + } return EFI_ST_SUCCESS; }
participants (1)
-
Heinrich Schuchardt