From b267ab2c532a69931c44e4fb43d249f756ed053f Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Mon, 17 Mar 2025 14:03:55 +0530 Subject: efi_loader: remove unused code from copy_fdt() There is logic in the copy_fdt() function which is iterating over the platform's DRAM banks and setting the fdt_ram_start variable. However, this variable is not used subsequently in the function. Remove this superfluous code. Signed-off-by: Sughosh Ganu Reviewed-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_helper.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index 04b2efc4a3b..15ad042bc61 100644 --- a/lib/efi_loader/efi_helper.c +++ b/lib/efi_loader/efi_helper.c @@ -454,23 +454,11 @@ efi_status_t efi_env_set_load_options(efi_handle_t handle, */ static efi_status_t copy_fdt(void **fdtp) { - unsigned long fdt_ram_start = -1L, fdt_pages; + unsigned long fdt_pages; efi_status_t ret = 0; void *fdt, *new_fdt; u64 new_fdt_addr; uint fdt_size; - int i; - - 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; - - if (!ram_size) - continue; - - if (ram_start < fdt_ram_start) - fdt_ram_start = ram_start; - } /* * Give us at least 12 KiB of breathing room in case the device tree -- cgit v1.3.1 From 7e624377e99314bdfac6cb5a3d216dff49a047e9 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Mon, 17 Mar 2025 14:03:56 +0530 Subject: efi_loader: install device-tree on configuration table on every invocation The efi_install_fdt() function is called before booting an EFI binary, either directly, or through a bootmanager. This function installs a copy of the device-tree(DT) on the EFI configuration table, which is passed on to the OS. The current logic in this function does not install a DT if a device-tree is already installed as an EFI configuration table. However, this existing copy of the DT might not be up-to-date, or it could be a wrong DT for the image that is being booted. Always install a DT afresh to the configuration table before booting the EFI binary. Installing a new DT also involves some additional checks that are needed to clean up memory associated with the existing DT copy. Check for an existing copy, and free up that memory. Signed-off-by: Sughosh Ganu Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_helper.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index 15ad042bc61..f6fbcdffe82 100644 --- a/lib/efi_loader/efi_helper.c +++ b/lib/efi_loader/efi_helper.c @@ -454,11 +454,30 @@ efi_status_t efi_env_set_load_options(efi_handle_t handle, */ static efi_status_t copy_fdt(void **fdtp) { - unsigned long fdt_pages; efi_status_t ret = 0; void *fdt, *new_fdt; - u64 new_fdt_addr; - uint fdt_size; + static u64 new_fdt_addr; + static efi_uintn_t fdt_pages; + ulong fdt_size; + + /* + * Remove the configuration table that might already be + * installed, ignoring EFI_NOT_FOUND if no device-tree + * is installed + */ + efi_install_configuration_table(&efi_guid_fdt, NULL); + + if (new_fdt_addr) { + log_debug("%s: Found allocated memory at %#llx, with %#zx pages\n", + __func__, new_fdt_addr, fdt_pages); + + ret = efi_free_pages(new_fdt_addr, fdt_pages); + if (ret != EFI_SUCCESS) + log_err("Unable to free up existing FDT memory region\n"); + + new_fdt_addr = 0; + fdt_pages = 0; + } /* * Give us at least 12 KiB of breathing room in case the device tree @@ -473,15 +492,18 @@ static efi_status_t copy_fdt(void **fdtp) &new_fdt_addr); if (ret != EFI_SUCCESS) { log_err("Failed to reserve space for FDT\n"); - goto done; + return ret; } + log_debug("%s: Allocated memory at %#llx, with %#zx pages\n", + __func__, new_fdt_addr, fdt_pages); + new_fdt = (void *)(uintptr_t)new_fdt_addr; memcpy(new_fdt, fdt, fdt_totalsize(fdt)); fdt_set_totalsize(new_fdt, fdt_size); - *fdtp = (void *)(uintptr_t)new_fdt_addr; -done: - return ret; + *fdtp = new_fdt; + + return EFI_SUCCESS; } /** @@ -534,9 +556,6 @@ efi_status_t efi_install_fdt(void *fdt) const char *fdt_opt; uintptr_t fdt_addr; - /* Look for device tree that is already installed */ - if (efi_get_configuration_table(&efi_guid_fdt)) - return EFI_SUCCESS; /* Check if there is a hardware device tree */ fdt_opt = env_get("fdt_addr"); /* Use our own device tree as fallback */ -- cgit v1.3.1 From c5f6542aa8ae93b7705e27103a9fdaf8445e2915 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Mon, 17 Mar 2025 14:03:58 +0530 Subject: efi_loader: allow for removal of memory from the EFI map With upcoming changes supporting pmem nodes, we need to remove the pmem area from the EFI memory map. Rename efi_add_memory_map_pg() to efi_update_memory_map(), and allow removing memory from the EFI memory map. Signed-off-by: Sughosh Ganu Signed-off-by: Ilias Apalodimas --- include/efi_loader.h | 15 +++++++++++++++ lib/efi_loader/efi_memory.c | 34 ++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/include/efi_loader.h b/include/efi_loader.h index e9c10819ba2..5f769786786 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -878,6 +878,21 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, /* Adds a range into the EFI memory map */ efi_status_t efi_add_memory_map(u64 start, u64 size, int memory_type); +/** + * efi_update_memory_map() - update the memory map by adding/removing pages + * + * @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 + * @remove: remove memory map + * Return: status code + */ +efi_status_t efi_update_memory_map(u64 start, u64 pages, int memory_type, + bool overlap_conventional, bool remove); + /* Called by board init to initialize the EFI drivers */ efi_status_t efi_driver_init(void); /* Called when a block device is added */ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index c39b53922bb..0abb1f6159a 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -258,7 +258,7 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, } /** - * efi_add_memory_map_pg() - add pages to the memory map + * efi_update_memory_map() - update the memory map by adding/removing pages * * @start: start address, must be a multiple of * EFI_PAGE_SIZE @@ -266,12 +266,11 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, * @memory_type: type of memory added * @overlap_conventional: region may only overlap free(conventional) * memory + * @remove: remove memory map * Return: status code */ -static -efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, - int memory_type, - bool overlap_conventional) +efi_status_t efi_update_memory_map(u64 start, u64 pages, int memory_type, + bool overlap_conventional, bool remove) { struct efi_mem_list *lmem; struct efi_mem_list *newlist; @@ -279,9 +278,9 @@ efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, uint64_t carved_pages = 0; struct efi_event *evt; - EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__, + EFI_PRINT("%s: 0x%llx 0x%llx %d %s %s\n", __func__, start, pages, memory_type, overlap_conventional ? - "yes" : "no"); + "yes" : "no", remove ? "remove" : "add"); if (memory_type >= EFI_MAX_MEMORY_TYPE) return EFI_INVALID_PARAMETER; @@ -364,7 +363,10 @@ efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, } /* Add our new map */ - list_add_tail(&newlist->link, &efi_mem); + if (!remove) + list_add_tail(&newlist->link, &efi_mem); + else + free(newlist); /* And make sure memory is listed in descending order */ efi_mem_sort(); @@ -401,7 +403,7 @@ efi_status_t efi_add_memory_map(u64 start, u64 size, int memory_type) pages = efi_size_in_pages(size + (start & EFI_PAGE_MASK)); start &= ~EFI_PAGE_MASK; - return efi_add_memory_map_pg(start, pages, memory_type, false); + return efi_update_memory_map(start, pages, memory_type, false, false); } /** @@ -501,7 +503,7 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, efi_addr = (u64)(uintptr_t)map_sysmem(addr, 0); /* Reserve that map in our memory maps */ - ret = efi_add_memory_map_pg(efi_addr, pages, memory_type, true); + ret = efi_update_memory_map(efi_addr, pages, memory_type, true, false); if (ret != EFI_SUCCESS) { /* Map would overlap, bail out */ lmb_free_flags(addr, (u64)pages << EFI_PAGE_SHIFT, flags); @@ -822,8 +824,8 @@ static void add_u_boot_and_runtime(void) 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); + efi_update_memory_map(uboot_start, uboot_pages, EFI_BOOT_SERVICES_CODE, + false, false); #if defined(__aarch64__) /* * Runtime Services must be 64KiB aligned according to the @@ -841,8 +843,8 @@ static void add_u_boot_and_runtime(void) runtime_end = (uintptr_t)__efi_runtime_stop; runtime_end = (runtime_end + runtime_mask) & ~runtime_mask; runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT; - efi_add_memory_map_pg(runtime_start, runtime_pages, - EFI_RUNTIME_SERVICES_CODE, false); + efi_update_memory_map(runtime_start, runtime_pages, + EFI_RUNTIME_SERVICES_CODE, false, false); } int efi_memory_init(void) @@ -877,11 +879,11 @@ int efi_map_update_notify(phys_addr_t addr, phys_size_t size, 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, + status = efi_update_memory_map(efi_addr, pages, op == LMB_MAP_OP_RESERVE ? EFI_BOOT_SERVICES_DATA : EFI_CONVENTIONAL_MEMORY, - false); + false, false); if (status != EFI_SUCCESS) { log_err("LMB Map notify failure %lu\n", status & ~EFI_ERROR_MASK); -- cgit v1.3.1 From fc82cf66d05f61339abb5f8e5bfc83647dbf96cf Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Mon, 17 Mar 2025 14:03:59 +0530 Subject: efi_loader: remove memory occupied by a ramdisk from EFI memory map ACPI has NFIT and NVDIMM support to provide ramdisks to the OS. Linux and device trees have support for persistent memory(pmem) devices. The firmware can then add a pmem node for the region of memory occupied by the ramdisk when passing the device-tree to the OS. It's worth noting that for linux to instantiate the /dev/pmemX device, the memory described in the pmem node has to be omitted from the EFI memory map we hand over to the OS if ZONE_DEVICES and SPARSEMEM is enabled. With those enabled the pmem driver ends up calling devm_memremap_pages() instead of devm_memremap(). The latter works whether the memory is omitted or marked as reserved, but mapping pages only works if the memory is omitted. On top of that, depending on how the kernel is configured, that memory area must be page aligned or 2MiB aligned. PowerPC is an exception here and requires 16MiB alignment, but since we don't have EFI support for it, limit the alignment to 2MiB. Ensure that the ISO image is 2MiB aligned and remove the region occupied by the image from the EFI memory map. Signed-off-by: Sughosh Ganu Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_bootmgr.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index c6124c590d9..f9534ef85ed 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include static const struct efi_boot_services *bs; static const struct efi_runtime_services *rs; @@ -348,6 +350,7 @@ static efi_status_t prepare_loaded_image(u16 *label, ulong addr, ulong size, struct efi_device_path **dp, struct udevice **blk) { + u64 pages; efi_status_t ret; struct udevice *ramdisk_blk; @@ -362,13 +365,18 @@ static efi_status_t prepare_loaded_image(u16 *label, ulong addr, ulong size, } /* - * TODO: expose the ramdisk to OS. - * Need to pass the ramdisk information by the architecture-specific - * methods such as 'pmem' device-tree node. + * Linux supports 'pmem' which allows OS installers to find, reclaim + * the mounted images and continue the installation since the contents + * of the pmem region are treated as local media. + * + * The memory regions used for it needs to be carved out of the EFI + * memory map. */ - ret = efi_add_memory_map(addr, size, EFI_RESERVED_MEMORY_TYPE); + pages = efi_size_in_pages(size + (addr & EFI_PAGE_MASK)); + ret = efi_update_memory_map(addr, pages, EFI_CONVENTIONAL_MEMORY, + false, true); if (ret != EFI_SUCCESS) { - log_err("Memory reservation failed\n"); + log_err("Failed to reserve memory\n"); goto err; } @@ -490,6 +498,13 @@ static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, ret = EFI_INVALID_PARAMETER; goto err; } + /* + * Depending on the kernel configuration, pmem memory areas must be + * page aligned or 2MiB aligned. PowerPC is an exception here and + * requires 16MiB alignment, but since we don't have EFI support for + * it, limit the alignment to 2MiB. + */ + image_size = ALIGN(image_size, SZ_2M); /* * If the file extension is ".iso" or ".img", mount it and try to load -- cgit v1.3.1 From 61e0a20aecf4af6cdc223bd8cd1bd82fe5e3d9f6 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Mon, 17 Mar 2025 14:04:02 +0530 Subject: blkmap: pass information on ISO image to the OS The EFI HTTP boot puts the ISO installer image at some location in memory. Information about this image has to be passed on to the OS kernel, which is done by adding a persistent memory(pmem) node to the devicetree(DT) that is passed to the OS. The OS kernel then gets information about the presence of this ISO image and proceeds with the installation. In U-Boot, this ISO image gets mounted as a memory mapped blkmap device slice, with the 'preserve' attribute. Add a helper function which iterates through all such slices, and invokes a callback. The callback adds the pmem node to the DT and removes the corresponding memory region from the EFI memory map. Invoke this helper function as part of the DT fixup which happens before booting the OS. Signed-off-by: Sughosh Ganu Reviewed-by: Tobias Waldekranz Signed-off-by: Ilias Apalodimas --- boot/image-fdt.c | 7 +++++++ drivers/block/blkmap.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/blkmap.h | 29 +++++++++++++++++++++++++++++ include/efi.h | 13 +++++++++++++ lib/efi_loader/efi_helper.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+) (limited to 'lib') diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 9d1598b1a93..8f718ad29f6 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -649,6 +650,12 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) if (!ft_verify_fdt(blob)) goto err; + if (CONFIG_IS_ENABLED(BLKMAP) && CONFIG_IS_ENABLED(EFI_LOADER)) { + fdt_ret = fdt_efi_pmem_setup(blob); + if (fdt_ret) + goto err; + } + /* after here we are using a livetree */ if (!of_live_active() && CONFIG_IS_ENABLED(EVENT)) { struct event_ft_fixup fixup; diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 1b161bd90be..473c65b5911 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -517,6 +517,49 @@ err: return err; } +static bool blkmap_mem_preserve_slice(struct blkmap_slice *bms) +{ + return (bms->attr & (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE)) == + (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE); +} + +int blkmap_get_preserved_pmem_slices(int (*cb)(void *ctx, u64 addr, + u64 size), void *ctx) +{ + int ret; + u64 addr, size; + struct udevice *dev; + struct uclass *uc; + struct blkmap *bm; + struct blkmap_mem *bmm; + struct blkmap_slice *bms; + struct blk_desc *bd; + + if (!cb) { + log_debug("%s: No callback passed to the function\n", __func__); + return 0; + } + + uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) { + bm = dev_get_plat(dev); + bd = dev_get_uclass_plat(bm->blk); + + list_for_each_entry(bms, &bm->slices, node) { + if (!blkmap_mem_preserve_slice(bms)) + continue; + + bmm = container_of(bms, struct blkmap_mem, slice); + addr = (u64)(uintptr_t)bmm->addr; + size = (u64)bms->blkcnt << bd->log2blksz; + ret = cb(ctx, addr, size); + if (ret) + return ret; + } + } + + return 0; +} + int blkmap_destroy(struct udevice *dev) { int err; diff --git a/include/blkmap.h b/include/blkmap.h index 754d8671b01..57555fda4fb 100644 --- a/include/blkmap.h +++ b/include/blkmap.h @@ -7,6 +7,7 @@ #ifndef _BLKMAP_H #define _BLKMAP_H +#include #include /** @@ -104,4 +105,32 @@ int blkmap_destroy(struct udevice *dev); int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size, struct udevice **devp); +/** + * blkmap_get_preserved_pmem_slices() - Look for memory mapped preserved slices + * @cb: Callback function to call for the blkmap slice + * @ctx: Argument to be passed to the callback function + * + * The function is used to iterate through all the blkmap slices, looking + * specifically for memory mapped blkmap mapping which has been + * created with the preserve attribute. The function looks for such slices + * with the relevant attributes and then calls the callback function which + * then does additional configuration as needed. The callback function is + * invoked for all the discovered slices, unless there is an error returned + * by the callback, in which case the function returns that error. + * + * The callback function has the following arguments + * @ctx: Argument to be passed to the callback function + * @addr: Start address of the memory mapped slice + * @size: Size of the memory mapped slice + * + * Typically, the callback will perform some configuration needed for the + * information passed on to it. An example of this would be setting up the + * pmem node in a device-tree(passed through the ctx argument) with the + * parameters passed on to the callback. + * + * Return: 0 on success, negative error on failure + */ +int blkmap_get_preserved_pmem_slices(int (*cb)(void *ctx, u64 addr, + u64 size), void *ctx); + #endif /* _BLKMAP_H */ diff --git a/include/efi.h b/include/efi.h index d005cb6181e..f9bbb175c3a 100644 --- a/include/efi.h +++ b/include/efi.h @@ -705,4 +705,17 @@ static inline bool efi_use_host_arch(void) */ int efi_get_pxe_arch(void); +/** + * fdt_efi_pmem_setup() - Pmem setup in DT and EFI memory map + * @fdt: Devicetree to add the pmem nodes to + * + * Iterate through all the blkmap devices, look for BLKMAP_MEM devices, + * and add pmem nodes corresponding to the blkmap slice to the + * devicetree along with removing the corresponding region from the + * EFI memory map. + * + * Returns: 0 on success, negative error on failure + */ +int fdt_efi_pmem_setup(void *fdt); + #endif /* _LINUX_EFI_H */ diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index f6fbcdffe82..8c32059edda 100644 --- a/lib/efi_loader/efi_helper.c +++ b/lib/efi_loader/efi_helper.c @@ -5,6 +5,7 @@ #define LOG_CATEGORY LOGC_EFI +#include #include #include #include @@ -687,3 +688,44 @@ out: return ret; } + +/** + * pmem_node_efi_memmap_setup() - Add pmem node and tweak EFI memmap + * @fdt: The devicetree to which pmem node is added + * @addr: start address of the pmem node + * @size: size of the memory of the pmem node + * + * The function adds the pmem node to the device-tree along with removing + * the corresponding region from the EFI memory map. Used primarily to + * pass the information of a RAM based ISO image to the OS. + * + * Return: 0 on success, -ve value on error + */ +static int pmem_node_efi_memmap_setup(void *fdt, u64 addr, u64 size) +{ + int ret; + u64 pages; + efi_status_t status; + + ret = fdt_fixup_pmem_region(fdt, addr, size); + if (ret) { + log_err("Failed to setup pmem node for addr %#llx, size %#llx, err %d\n", + addr, size, ret); + return ret; + } + + /* Remove the pmem region from the EFI memory map */ + pages = efi_size_in_pages(size + (addr & EFI_PAGE_MASK)); + status = efi_update_memory_map(addr, pages, EFI_CONVENTIONAL_MEMORY, + false, true); + if (status != EFI_SUCCESS) + return -1; + + return 0; +} + +int fdt_efi_pmem_setup(void *fdt) +{ + return blkmap_get_preserved_pmem_slices(pmem_node_efi_memmap_setup, + fdt); +} -- cgit v1.3.1