diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/acpi/acpi_table.c | 6 | ||||
-rw-r--r-- | lib/alist.c | 41 | ||||
-rw-r--r-- | lib/efi_loader/Kconfig | 10 | ||||
-rw-r--r-- | lib/efi_loader/Makefile | 1 | ||||
-rw-r--r-- | lib/efi_loader/efi_bootmgr.c | 10 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 4 | ||||
-rw-r--r-- | lib/efi_loader/efi_file.c | 30 | ||||
-rw-r--r-- | lib/efi_loader/efi_helper.c | 71 | ||||
-rw-r--r-- | lib/efi_loader/efi_tcg2.c | 2 | ||||
-rw-r--r-- | lib/efi_loader/testapp.c | 56 | ||||
-rw-r--r-- | lib/lmb.c | 578 |
11 files changed, 546 insertions, 263 deletions
diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 6473d95c102..150f75027a5 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -420,7 +420,7 @@ int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev, static int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry) { struct serial_device_info serial_info = {0}; - ulong serial_address, serial_offset; + u64 serial_address, serial_offset; struct acpi_table_header *header; struct acpi_spcr *spcr; struct udevice *dev; @@ -473,7 +473,7 @@ static int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry } serial_width = serial_info.reg_width * 8; - serial_offset = serial_info.reg_offset << serial_info.reg_shift; + serial_offset = ((u64)serial_info.reg_offset) << serial_info.reg_shift; serial_address = serial_info.addr + serial_offset; /* Encode register access size */ @@ -495,7 +495,7 @@ static int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry break; } - debug("UART type %u @ %lx\n", spcr->interface_type, serial_address); + debug("UART type %u @ %llx\n", spcr->interface_type, serial_address); /* Fill GAS */ spcr->serial_port.space_id = space_id; diff --git a/lib/alist.c b/lib/alist.c index b7928cad520..4ce651f5c45 100644 --- a/lib/alist.c +++ b/lib/alist.c @@ -41,6 +41,11 @@ void alist_uninit(struct alist *lst) memset(lst, '\0', sizeof(struct alist)); } +void alist_empty(struct alist *lst) +{ + lst->count = 0; +} + /** * alist_expand_to() - Expand a list to the given size * @@ -106,6 +111,42 @@ const void *alist_get_ptr(const struct alist *lst, uint index) return lst->data + index * lst->obj_size; } +int alist_calc_index(const struct alist *lst, const void *ptr) +{ + uint index; + + if (!lst->count || ptr < lst->data) + return -1; + + index = (ptr - lst->data) / lst->obj_size; + + return index; +} + +void alist_update_end(struct alist *lst, const void *ptr) +{ + int index; + + index = alist_calc_index(lst, ptr); + lst->count = index == -1 ? 0 : index; +} + +bool alist_chk_ptr(const struct alist *lst, const void *ptr) +{ + int index = alist_calc_index(lst, ptr); + + return index >= 0 && index < lst->count; +} + +const void *alist_next_ptrd(const struct alist *lst, const void *ptr) +{ + int index = alist_calc_index(lst, ptr); + + assert(index != -1); + + return alist_get_ptr(lst, index + 1); +} + void *alist_ensure_ptr(struct alist *lst, uint index) { uint minsize = index + 1; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 066f0ca0da7..58d49789f12 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -567,6 +567,16 @@ config BOOTEFI_HELLO_COMPILE No additional space will be required in the resulting U-Boot binary when this option is enabled. +config BOOTEFI_TESTAPP_COMPILE + bool "Compile an EFI test app for testing" + default y + help + This compiles an app designed for testing. It is packed into an image + by the test.py testing frame in the setup_efi_image() function. + + No additional space will be required in the resulting U-Boot binary + when this option is enabled. + endif source "lib/efi/Kconfig" diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 00d18966f9e..87131ab911d 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -20,6 +20,7 @@ apps-$(CONFIG_EFI_LOAD_FILE2_INITRD) += initrddump ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) apps-y += dtbdump endif +apps-$(CONFIG_BOOTEFI_TESTAPP_COMPILE) += testapp obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-$(CONFIG_EFI_BOOTMGR) += efi_bootmgr.o diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index f9b5988a06e..8c51a6ef2ed 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -11,10 +11,10 @@ #include <blkmap.h> #include <charset.h> #include <dm.h> +#include <efi.h> #include <log.h> #include <malloc.h> #include <net.h> -#include <efi_default_filename.h> #include <efi_loader.h> #include <efi_variable.h> #include <asm/unaligned.h> @@ -82,8 +82,12 @@ struct efi_device_path *expand_media_path(struct efi_device_path *device_path) &efi_simple_file_system_protocol_guid, &rem); if (handle) { if (rem->type == DEVICE_PATH_TYPE_END) { - full_path = efi_dp_from_file(device_path, - "/EFI/BOOT/" BOOTEFI_NAME); + char fname[30]; + + snprintf(fname, sizeof(fname), "/EFI/BOOT/%s", + efi_get_basename()); + full_path = efi_dp_from_file(device_path, fname); + } else { full_path = efi_dp_dup(device_path); } diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 9de3b95d073..ee387e1dfd4 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -977,7 +977,7 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) /* Construct a device-path for memory-mapped image */ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, uint64_t start_address, - uint64_t end_address) + size_t size) { struct efi_device_path_memory *mdp; void *buf, *start; @@ -992,7 +992,7 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, mdp->dp.length = sizeof(*mdp); mdp->memory_type = memory_type; mdp->start_address = start_address; - mdp->end_address = end_address; + mdp->end_address = start_address + size; buf = &mdp[1]; *((struct efi_device_path *)buf) = END; diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index c92d8ccf004..95b3c890ee9 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -864,8 +864,16 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, } ret = efi_get_file_size(fh, &file_size); - if (ret != EFI_SUCCESS) - goto error; + if (ret != EFI_SUCCESS) { + if (!fh->isdir) + goto error; + /* + * Some file drivers don't implement fs_size() for + * directories. Use a dummy non-zero value. + */ + file_size = 4096; + ret = EFI_SUCCESS; + } memset(info, 0, required_size); @@ -976,14 +984,16 @@ static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file, } free(new_file_name); /* Check for truncation */ - ret = efi_get_file_size(fh, &file_size); - if (ret != EFI_SUCCESS) - goto out; - if (file_size != info->file_size) { - /* TODO: we do not support truncation */ - EFI_PRINT("Truncation not supported\n"); - ret = EFI_ACCESS_DENIED; - goto out; + if (!fh->isdir) { + ret = efi_get_file_size(fh, &file_size); + if (ret != EFI_SUCCESS) + goto out; + if (file_size != info->file_size) { + /* TODO: we do not support truncation */ + EFI_PRINT("Truncation not supported\n"); + ret = EFI_ACCESS_DENIED; + goto out; + } } /* * We do not care for the other attributes diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index 00167bd2a10..bf96f61d3d0 100644 --- a/lib/efi_loader/efi_helper.c +++ b/lib/efi_loader/efi_helper.c @@ -12,18 +12,89 @@ #include <mapmem.h> #include <dm.h> #include <fs.h> +#include <efi.h> #include <efi_api.h> #include <efi_load_initrd.h> #include <efi_loader.h> #include <efi_variable.h> +#include <host_arch.h> #include <linux/libfdt.h> #include <linux/list.h> +#undef BOOTEFI_NAME + +#if HOST_ARCH == HOST_ARCH_X86_64 +#define HOST_BOOTEFI_NAME "BOOTX64.EFI" +#define HOST_PXE_ARCH 0x6 +#elif HOST_ARCH == HOST_ARCH_X86 +#define HOST_BOOTEFI_NAME "BOOTIA32.EFI" +#define HOST_PXE_ARCH 0x7 +#elif HOST_ARCH == HOST_ARCH_AARCH64 +#define HOST_BOOTEFI_NAME "BOOTAA64.EFI" +#define HOST_PXE_ARCH 0xb +#elif HOST_ARCH == HOST_ARCH_ARM +#define HOST_BOOTEFI_NAME "BOOTARM.EFI" +#define HOST_PXE_ARCH 0xa +#elif HOST_ARCH == HOST_ARCH_RISCV32 +#define HOST_BOOTEFI_NAME "BOOTRISCV32.EFI" +#define HOST_PXE_ARCH 0x19 +#elif HOST_ARCH == HOST_ARCH_RISCV64 +#define HOST_BOOTEFI_NAME "BOOTRISCV64.EFI" +#define HOST_PXE_ARCH 0x1b +#else +#error Unsupported Host architecture +#endif + +#if defined(CONFIG_SANDBOX) +#define BOOTEFI_NAME "BOOTSBOX.EFI" +#elif defined(CONFIG_ARM64) +#define BOOTEFI_NAME "BOOTAA64.EFI" +#elif defined(CONFIG_ARM) +#define BOOTEFI_NAME "BOOTARM.EFI" +#elif defined(CONFIG_X86_64) +#define BOOTEFI_NAME "BOOTX64.EFI" +#elif defined(CONFIG_X86) +#define BOOTEFI_NAME "BOOTIA32.EFI" +#elif defined(CONFIG_ARCH_RV32I) +#define BOOTEFI_NAME "BOOTRISCV32.EFI" +#elif defined(CONFIG_ARCH_RV64I) +#define BOOTEFI_NAME "BOOTRISCV64.EFI" +#else +#error Unsupported UEFI architecture +#endif + #if defined(CONFIG_CMD_EFIDEBUG) || defined(CONFIG_EFI_LOAD_FILE2_INITRD) /* GUID used by Linux to identify the LoadFile2 protocol with the initrd */ const efi_guid_t efi_lf2_initrd_guid = EFI_INITRD_MEDIA_GUID; #endif +const char *efi_get_basename(void) +{ + return efi_use_host_arch() ? HOST_BOOTEFI_NAME : BOOTEFI_NAME; +} + +int efi_get_pxe_arch(void) +{ + if (efi_use_host_arch()) + return HOST_PXE_ARCH; + + /* http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml */ + if (IS_ENABLED(CONFIG_ARM64)) + return 0xb; + else if (IS_ENABLED(CONFIG_ARM)) + return 0xa; + else if (IS_ENABLED(CONFIG_X86_64)) + return 0x6; + else if (IS_ENABLED(CONFIG_X86)) + return 0x7; + else if (IS_ENABLED(CONFIG_ARCH_RV32I)) + return 0x19; + else if (IS_ENABLED(CONFIG_ARCH_RV64I)) + return 0x1b; + + return -EINVAL; +} + /** * efi_create_current_boot_var() - Return Boot#### name were #### is replaced by * the value of BootCurrent diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c index 45f451ef6b6..866a529857e 100644 --- a/lib/efi_loader/efi_tcg2.c +++ b/lib/efi_loader/efi_tcg2.c @@ -789,7 +789,7 @@ static const struct efi_tcg2_protocol efi_tcg2_protocol = { /** * tcg2_uninit - remove the final event table and free efi memory on failures */ -void tcg2_uninit(void) +static void tcg2_uninit(void) { efi_status_t ret; diff --git a/lib/efi_loader/testapp.c b/lib/efi_loader/testapp.c new file mode 100644 index 00000000000..804ca7e4679 --- /dev/null +++ b/lib/efi_loader/testapp.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EFI test application + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + * + * This test program is used to test the invocation of an EFI application. + * It writes a few messages to the console and then exits boot services + */ + +#include <efi_api.h> + +static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; + +static struct efi_system_table *systable; +static struct efi_boot_services *boottime; +static struct efi_simple_text_output_protocol *con_out; + +/** + * efi_main() - entry point of the EFI application. + * + * @handle: handle of the loaded image + * @systab: system table + * Return: status code + */ +efi_status_t EFIAPI efi_main(efi_handle_t handle, + struct efi_system_table *systab) +{ + struct efi_loaded_image *loaded_image; + efi_status_t ret; + + systable = systab; + boottime = systable->boottime; + con_out = systable->con_out; + + /* Get the loaded image protocol */ + ret = boottime->open_protocol(handle, &loaded_image_guid, + (void **)&loaded_image, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + con_out->output_string + (con_out, u"Cannot open loaded image protocol\r\n"); + goto out; + } + + /* UEFI requires CR LF */ + con_out->output_string(con_out, u"U-Boot test app for EFI_LOADER\r\n"); + +out: + con_out->output_string(con_out, u"Exiting test app\n"); + ret = boottime->exit(handle, ret, 0, NULL); + + /* We should never arrive here */ + return ret; +} diff --git a/lib/lmb.c b/lib/lmb.c index 9423301cdbc..74ffa9f9272 100644 --- a/lib/lmb.c +++ b/lib/lmb.c @@ -27,96 +27,11 @@ DECLARE_GLOBAL_DATA_PTR; #define MAP_OP_FREE (u8)0x2 #define MAP_OP_ADD (u8)0x3 -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, enum lmb_flags flags) -{ - 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; - } - - if (!lmb_should_notify(flags)) - return 0; - - 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; - } - - return 0; -} - -static void lmb_print_region_flags(enum lmb_flags flags) -{ - u64 bitpos; - const char *flag_str[] = { "none", "no-map", "no-overwrite", "no-notify" }; - - do { - bitpos = flags ? fls(flags) - 1 : 0; - assert_noisy(bitpos < ARRAY_SIZE(flag_str)); - printf("%s", flag_str[bitpos]); - flags &= ~(1ull << bitpos); - puts(flags ? ", " : "\n"); - } while (flags); -} - -static void lmb_dump_region(struct alist *lmb_rgn_lst, char *name) -{ - struct lmb_region *rgn = lmb_rgn_lst->data; - unsigned long long base, size, end; - enum lmb_flags flags; - int i; - - printf(" %s.count = 0x%x\n", name, lmb_rgn_lst->count); - - for (i = 0; i < lmb_rgn_lst->count; i++) { - base = rgn[i].base; - size = rgn[i].size; - end = base + size - 1; - flags = rgn[i].flags; - - printf(" %s[%d]\t[0x%llx-0x%llx], 0x%08llx bytes flags: ", - name, i, base, end, size); - lmb_print_region_flags(flags); - } -} - -void lmb_dump_all_force(void) -{ - printf("lmb_dump_all:\n"); - lmb_dump_region(&lmb.free_mem, "memory"); - lmb_dump_region(&lmb.used_mem, "reserved"); -} - -void lmb_dump_all(void) -{ -#ifdef DEBUG - lmb_dump_all_force(); -#endif -} +/* + * The following low level LMB functions must not access the global LMB memory + * map since they are also used to manage IOVA memory maps in iommu drivers like + * apple_dart. + */ static long lmb_addrs_overlap(phys_addr_t base1, phys_size_t size1, phys_addr_t base2, phys_size_t size2) @@ -205,117 +120,6 @@ static void lmb_fix_over_lap_regions(struct alist *lmb_rgn_lst, lmb_remove_region(lmb_rgn_lst, r2); } -static void lmb_reserve_uboot_region(void) -{ - int bank; - ulong end, bank_end; - phys_addr_t rsv_start; - - rsv_start = gd->start_addr_sp - CONFIG_STACK_SIZE; - end = gd->ram_top; - - /* - * Reserve memory from aligned address below the bottom of U-Boot stack - * until end of RAM area to prevent LMB from overwriting that memory. - */ - debug("## Current stack ends at 0x%08lx ", (ulong)rsv_start); - - /* adjust sp by 16K to be safe */ - rsv_start -= SZ_16K; - for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { - if (!gd->bd->bi_dram[bank].size || - rsv_start < gd->bd->bi_dram[bank].start) - continue; - /* Watch out for RAM at end of address space! */ - bank_end = gd->bd->bi_dram[bank].start + - gd->bd->bi_dram[bank].size - 1; - if (rsv_start > bank_end) - continue; - if (bank_end > end) - bank_end = end - 1; - - lmb_reserve_flags(rsv_start, bank_end - rsv_start + 1, - LMB_NOOVERWRITE); - - if (gd->flags & GD_FLG_SKIP_RELOC) - lmb_reserve_flags((phys_addr_t)(uintptr_t)_start, - gd->mon_len, LMB_NOOVERWRITE); - - break; - } -} - -static void lmb_reserve_common(void *fdt_blob) -{ - lmb_reserve_uboot_region(); - - if (CONFIG_IS_ENABLED(OF_LIBFDT) && fdt_blob) - boot_fdt_add_mem_rsv_regions(fdt_blob); -} - -static __maybe_unused void lmb_reserve_common_spl(void) -{ - phys_addr_t rsv_start; - phys_size_t rsv_size; - - /* - * Assume a SPL stack of 16KB. This must be - * more than enough for the SPL stage. - */ - if (IS_ENABLED(CONFIG_SPL_STACK_R_ADDR)) { - rsv_start = gd->start_addr_sp - 16384; - rsv_size = 16384; - lmb_reserve_flags(rsv_start, rsv_size, LMB_NOOVERWRITE); - } - - if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) { - /* Reserve the bss region */ - rsv_start = (phys_addr_t)(uintptr_t)__bss_start; - rsv_size = (phys_addr_t)(uintptr_t)__bss_end - - (phys_addr_t)(uintptr_t)__bss_start; - lmb_reserve_flags(rsv_start, rsv_size, LMB_NOOVERWRITE); - } -} - -/** - * lmb_add_memory() - Add memory range for LMB allocations - * - * Add the entire available memory range to the pool of memory that - * can be used by the LMB module for allocations. - * - * Return: None - */ -void lmb_add_memory(void) -{ - int i; - phys_size_t size; - 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; - - for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - size = bd->bi_dram[i].size; - if (size) { - 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); - } - } -} - static long lmb_resize_regions(struct alist *lmb_rgn_lst, unsigned long idx_start, phys_addr_t base, phys_size_t size) @@ -475,29 +279,10 @@ static long lmb_add_region_flags(struct alist *lmb_rgn_lst, phys_addr_t base, return 0; } -static long lmb_add_region(struct alist *lmb_rgn_lst, phys_addr_t base, - phys_size_t size) -{ - return lmb_add_region_flags(lmb_rgn_lst, base, size, LMB_NONE); -} - -/* 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; - - ret = lmb_add_region(lmb_rgn_lst, base, size); - if (ret) - return ret; - - return lmb_map_update_notify(base, size, MAP_OP_ADD, LMB_NONE); -} - -static long _lmb_free(phys_addr_t base, phys_size_t size) +static long _lmb_free(struct alist *lmb_rgn_lst, phys_addr_t base, + phys_size_t size) { struct lmb_region *rgn; - struct alist *lmb_rgn_lst = &lmb.used_mem; phys_addr_t rgnbegin, rgnend; phys_addr_t end = base + size - 1; int i; @@ -545,6 +330,332 @@ static long _lmb_free(phys_addr_t base, phys_size_t size) rgn[i].flags); } +static long lmb_overlaps_region(struct alist *lmb_rgn_lst, phys_addr_t base, + phys_size_t size) +{ + unsigned long i; + struct lmb_region *rgn = lmb_rgn_lst->data; + + for (i = 0; i < lmb_rgn_lst->count; i++) { + phys_addr_t rgnbase = rgn[i].base; + phys_size_t rgnsize = rgn[i].size; + if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) + break; + } + + return (i < lmb_rgn_lst->count) ? i : -1; +} + +static phys_addr_t lmb_align_down(phys_addr_t addr, phys_size_t size) +{ + return addr & ~(size - 1); +} + +/* + * IOVA LMB memory maps using lmb pointers instead of the global LMB memory map. + */ + +int io_lmb_setup(struct lmb *io_lmb) +{ + int ret; + + ret = alist_init(&io_lmb->free_mem, sizeof(struct lmb_region), + (uint)LMB_ALIST_INITIAL_SIZE); + if (!ret) { + log_debug("Unable to initialise the list for LMB free IOVA\n"); + return -ENOMEM; + } + + ret = alist_init(&io_lmb->used_mem, sizeof(struct lmb_region), + (uint)LMB_ALIST_INITIAL_SIZE); + if (!ret) { + log_debug("Unable to initialise the list for LMB used IOVA\n"); + return -ENOMEM; + } + + io_lmb->test = false; + + return 0; +} + +void io_lmb_teardown(struct lmb *io_lmb) +{ + alist_uninit(&io_lmb->free_mem); + alist_uninit(&io_lmb->used_mem); +} + +long io_lmb_add(struct lmb *io_lmb, phys_addr_t base, phys_size_t size) +{ + return lmb_add_region_flags(&io_lmb->free_mem, base, size, LMB_NONE); +} + +/* derived and simplified from _lmb_alloc_base() */ +phys_addr_t io_lmb_alloc(struct lmb *io_lmb, phys_size_t size, ulong align) +{ + long i, rgn; + phys_addr_t base = 0; + phys_addr_t res_base; + struct lmb_region *lmb_used = io_lmb->used_mem.data; + struct lmb_region *lmb_memory = io_lmb->free_mem.data; + + for (i = io_lmb->free_mem.count - 1; i >= 0; i--) { + phys_addr_t lmbbase = lmb_memory[i].base; + phys_size_t lmbsize = lmb_memory[i].size; + + if (lmbsize < size) + continue; + base = lmb_align_down(lmbbase + lmbsize - size, align); + + while (base && lmbbase <= base) { + rgn = lmb_overlaps_region(&io_lmb->used_mem, base, size); + if (rgn < 0) { + /* This area isn't reserved, take it */ + if (lmb_add_region_flags(&io_lmb->used_mem, base, + size, LMB_NONE) < 0) + return 0; + + return base; + } + + res_base = lmb_used[rgn].base; + if (res_base < size) + break; + base = lmb_align_down(res_base - size, align); + } + } + return 0; +} + +long io_lmb_free(struct lmb *io_lmb, phys_addr_t base, phys_size_t size) +{ + return _lmb_free(&io_lmb->used_mem, base, size); +} + +/* + * Low level LMB functions are used to manage IOVA memory maps for the Apple + * dart iommu. They must not access the global LMB memory map. + * So keep the global LMB variable declaration unreachable from them. + */ + +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 lmb_map_update_notify(phys_addr_t addr, phys_size_t size, u8 op, + enum lmb_flags flags) +{ + 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; + } + + if (!lmb_should_notify(flags)) + return 0; + + 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; + } + unmap_sysmem((void *)(uintptr_t)efi_addr); + + return 0; +} + +static void lmb_print_region_flags(enum lmb_flags flags) +{ + u64 bitpos; + const char *flag_str[] = { "none", "no-map", "no-overwrite", "no-notify" }; + + do { + bitpos = flags ? fls(flags) - 1 : 0; + assert_noisy(bitpos < ARRAY_SIZE(flag_str)); + printf("%s", flag_str[bitpos]); + flags &= ~(1ull << bitpos); + puts(flags ? ", " : "\n"); + } while (flags); +} + +static void lmb_dump_region(struct alist *lmb_rgn_lst, char *name) +{ + struct lmb_region *rgn = lmb_rgn_lst->data; + unsigned long long base, size, end; + enum lmb_flags flags; + int i; + + printf(" %s.count = 0x%x\n", name, lmb_rgn_lst->count); + + for (i = 0; i < lmb_rgn_lst->count; i++) { + base = rgn[i].base; + size = rgn[i].size; + end = base + size - 1; + flags = rgn[i].flags; + + printf(" %s[%d]\t[0x%llx-0x%llx], 0x%08llx bytes flags: ", + name, i, base, end, size); + lmb_print_region_flags(flags); + } +} + +void lmb_dump_all_force(void) +{ + printf("lmb_dump_all:\n"); + lmb_dump_region(&lmb.free_mem, "memory"); + lmb_dump_region(&lmb.used_mem, "reserved"); +} + +void lmb_dump_all(void) +{ +#ifdef DEBUG + lmb_dump_all_force(); +#endif +} + +static void lmb_reserve_uboot_region(void) +{ + int bank; + ulong end, bank_end; + phys_addr_t rsv_start; + + rsv_start = gd->start_addr_sp - CONFIG_STACK_SIZE; + end = gd->ram_top; + + /* + * Reserve memory from aligned address below the bottom of U-Boot stack + * until end of RAM area to prevent LMB from overwriting that memory. + */ + debug("## Current stack ends at 0x%08lx ", (ulong)rsv_start); + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + if (!gd->bd->bi_dram[bank].size || + rsv_start < gd->bd->bi_dram[bank].start) + continue; + /* Watch out for RAM at end of address space! */ + bank_end = gd->bd->bi_dram[bank].start + + gd->bd->bi_dram[bank].size - 1; + if (rsv_start > bank_end) + continue; + if (bank_end > end) + bank_end = end - 1; + + lmb_reserve_flags(rsv_start, bank_end - rsv_start + 1, + LMB_NOOVERWRITE); + + if (gd->flags & GD_FLG_SKIP_RELOC) + lmb_reserve_flags((phys_addr_t)(uintptr_t)_start, + gd->mon_len, LMB_NOOVERWRITE); + + break; + } +} + +static void lmb_reserve_common(void *fdt_blob) +{ + lmb_reserve_uboot_region(); + + if (CONFIG_IS_ENABLED(OF_LIBFDT) && fdt_blob) + boot_fdt_add_mem_rsv_regions(fdt_blob); +} + +static __maybe_unused void lmb_reserve_common_spl(void) +{ + phys_addr_t rsv_start; + phys_size_t rsv_size; + + /* + * Assume a SPL stack of 16KB. This must be + * more than enough for the SPL stage. + */ + if (IS_ENABLED(CONFIG_SPL_STACK_R_ADDR)) { + rsv_start = gd->start_addr_sp - 16384; + rsv_size = 16384; + lmb_reserve_flags(rsv_start, rsv_size, LMB_NOOVERWRITE); + } + + if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) { + /* Reserve the bss region */ + rsv_start = (phys_addr_t)(uintptr_t)__bss_start; + rsv_size = (phys_addr_t)(uintptr_t)__bss_end - + (phys_addr_t)(uintptr_t)__bss_start; + lmb_reserve_flags(rsv_start, rsv_size, LMB_NOOVERWRITE); + } +} + +/** + * lmb_add_memory() - Add memory range for LMB allocations + * + * Add the entire available memory range to the pool of memory that + * can be used by the LMB module for allocations. + * + * Return: None + */ +void lmb_add_memory(void) +{ + int i; + phys_size_t size; + 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; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + size = bd->bi_dram[i].size; + if (size) { + 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); + } + } +} + +static long lmb_add_region(struct alist *lmb_rgn_lst, phys_addr_t base, + phys_size_t size) +{ + return lmb_add_region_flags(lmb_rgn_lst, base, size, LMB_NONE); +} + +/* 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; + + ret = lmb_add_region(lmb_rgn_lst, base, size); + if (ret) + return ret; + + return lmb_map_update_notify(base, size, MAP_OP_ADD, LMB_NONE); +} + /** * lmb_free_flags() - Free up a region of memory * @base: Base Address of region to be freed @@ -560,7 +671,7 @@ long lmb_free_flags(phys_addr_t base, phys_size_t size, { long ret; - ret = _lmb_free(base, size); + ret = _lmb_free(&lmb.used_mem, base, size); if (ret < 0) return ret; @@ -589,27 +700,6 @@ long lmb_reserve(phys_addr_t base, phys_size_t size) return lmb_reserve_flags(base, size, LMB_NONE); } -static long lmb_overlaps_region(struct alist *lmb_rgn_lst, phys_addr_t base, - phys_size_t size) -{ - unsigned long i; - struct lmb_region *rgn = lmb_rgn_lst->data; - - for (i = 0; i < lmb_rgn_lst->count; i++) { - phys_addr_t rgnbase = rgn[i].base; - phys_size_t rgnsize = rgn[i].size; - if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) - break; - } - - return (i < lmb_rgn_lst->count) ? i : -1; -} - -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, phys_addr_t max_addr, enum lmb_flags flags) { |