diff options
65 files changed, 1549 insertions, 582 deletions
@@ -86,6 +86,7 @@ config DISTRO_DEFAULTS select SUPPORT_RAW_INITRD select SYS_LONGHELP imply CMD_MII if NET + imply USB_STORAGE imply USE_BOOTCOMMAND help Select this to enable various options and commands which are suitable diff --git a/MAINTAINERS b/MAINTAINERS index 214629e283..8ea8ef9924 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2,6 +2,8 @@ Descriptions of section entries: P: Person (obsolete) M: Mail patches to: FullName <address@domain> + R: Designated reviewer: FullName <address@domain> + These reviewers should be CCed on patches. L: Mailing list that is relevant to this area W: Web-page with status/info Q: Patchwork web based patch tracking system site @@ -414,6 +416,7 @@ F: test/dm/ EFI PAYLOAD M: Alexander Graf <agraf@suse.de> +R: Heinrich Schuchardt <xypron.glpk@gmx.de> S: Maintained T: git git://github.com/agraf/u-boot.git F: doc/README.uefi diff --git a/arch/arm/cpu/armv7/smccc-call.S b/arch/arm/cpu/armv7/smccc-call.S index 0d8b59eb6b..eae69e36c3 100644 --- a/arch/arm/cpu/armv7/smccc-call.S +++ b/arch/arm/cpu/armv7/smccc-call.S @@ -7,6 +7,8 @@ #include <asm/opcodes-sec.h> #include <asm/opcodes-virt.h> + .section .text.efi_runtime + #define UNWIND(x...) /* * Wrap c macros in asm macros to delay expansion until after the diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig index ff42791fb4..1c12bbde75 100644 --- a/arch/arm/cpu/armv8/Kconfig +++ b/arch/arm/cpu/armv8/Kconfig @@ -96,6 +96,7 @@ endmenu config PSCI_RESET bool "Use PSCI for reset and shutdown" default y + select ARM_SMCCC if OF_CONTROL depends on !ARCH_EXYNOS7 && !ARCH_BCM283X && \ !TARGET_LS2080A_SIMU && !TARGET_LS2080AQDS && \ !TARGET_LS2080ARDB && !TARGET_LS2080A_EMU && \ diff --git a/arch/arm/cpu/armv8/fwcall.c b/arch/arm/cpu/armv8/fwcall.c index 0ba3dad8cc..9957c2974b 100644 --- a/arch/arm/cpu/armv8/fwcall.c +++ b/arch/arm/cpu/armv8/fwcall.c @@ -7,7 +7,6 @@ #include <asm-offsets.h> #include <config.h> -#include <efi_loader.h> #include <version.h> #include <asm/macro.h> #include <asm/psci.h> @@ -19,7 +18,7 @@ * x0~x7: input arguments * x0~x3: output arguments */ -static void __efi_runtime hvc_call(struct pt_regs *args) +static void hvc_call(struct pt_regs *args) { asm volatile( "ldr x0, %0\n" @@ -53,7 +52,7 @@ static void __efi_runtime hvc_call(struct pt_regs *args) * x0~x3: output arguments */ -void __efi_runtime smc_call(struct pt_regs *args) +void smc_call(struct pt_regs *args) { asm volatile( "ldr x0, %0\n" @@ -83,9 +82,9 @@ void __efi_runtime smc_call(struct pt_regs *args) * use PSCI on U-Boot running below a hypervisor, please detect * this and set the flag accordingly. */ -static const __efi_runtime_data bool use_smc_for_psci = true; +static const bool use_smc_for_psci = true; -void __noreturn __efi_runtime psci_system_reset(void) +void __noreturn psci_system_reset(void) { struct pt_regs regs; @@ -100,7 +99,7 @@ void __noreturn __efi_runtime psci_system_reset(void) ; } -void __noreturn __efi_runtime psci_system_off(void) +void __noreturn psci_system_off(void) { struct pt_regs regs; @@ -114,44 +113,3 @@ void __noreturn __efi_runtime psci_system_off(void) while (1) ; } - -#ifdef CONFIG_CMD_POWEROFF -int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - puts("poweroff ...\n"); - - udelay(50000); /* wait 50 ms */ - - disable_interrupts(); - - psci_system_off(); - - /*NOTREACHED*/ - return 0; -} -#endif - -#ifdef CONFIG_PSCI_RESET -void reset_misc(void) -{ - psci_system_reset(); -} - -#ifdef CONFIG_EFI_LOADER -void __efi_runtime EFIAPI efi_reset_system( - enum efi_reset_type reset_type, - efi_status_t reset_status, - unsigned long data_size, void *reset_data) -{ - if (reset_type == EFI_RESET_COLD || - reset_type == EFI_RESET_WARM || - reset_type == EFI_RESET_PLATFORM_SPECIFIC) { - psci_system_reset(); - } else if (reset_type == EFI_RESET_SHUTDOWN) { - psci_system_off(); - } - - while (1) { } -} -#endif /* CONFIG_EFI_LOADER */ -#endif /* CONFIG_PSCI_RESET */ diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index 16c9e298b4..86de4b4089 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -6,6 +6,8 @@ #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> + .section .text.efi_runtime + .macro SMCCC instr .cfi_startproc \instr #0 diff --git a/arch/arm/lib/crt0_aarch64_efi.S b/arch/arm/lib/crt0_aarch64_efi.S index 0db4360bcf..cb205fa30a 100644 --- a/arch/arm/lib/crt0_aarch64_efi.S +++ b/arch/arm/lib/crt0_aarch64_efi.S @@ -28,13 +28,13 @@ coff_header: .short 2 /* nr_sections */ .long 0 /* TimeDateStamp */ .long 0 /* PointerToSymbolTable */ - .long 1 /* NumberOfSymbols */ + .long 0 /* NumberOfSymbols */ .short section_table - optional_header /* SizeOfOptionalHeader */ - /* - * Characteristics: IMAGE_FILE_DEBUG_STRIPPED | - * IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LINE_NUMS_STRIPPED - */ - .short 0x206 + /* Characteristics */ + .short (IMAGE_FILE_EXECUTABLE_IMAGE | \ + IMAGE_FILE_LINE_NUMS_STRIPPED | \ + IMAGE_FILE_LOCAL_SYMS_STRIPPED | \ + IMAGE_FILE_DEBUG_STRIPPED) optional_header: .short 0x20b /* PE32+ format */ .byte 0x02 /* MajorLinkerVersion */ diff --git a/arch/arm/lib/crt0_arm_efi.S b/arch/arm/lib/crt0_arm_efi.S index 23db49f1fc..5470e2ff0e 100644 --- a/arch/arm/lib/crt0_arm_efi.S +++ b/arch/arm/lib/crt0_arm_efi.S @@ -27,16 +27,16 @@ coff_header: .short 2 /* nr_sections */ .long 0 /* TimeDateStamp */ .long 0 /* PointerToSymbolTable */ - .long 1 /* NumberOfSymbols */ + .long 0 /* NumberOfSymbols */ .short section_table - optional_header /* SizeOfOptionalHeader */ - /* - * Characteristics: IMAGE_FILE_32BIT_MACHINE | - * IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | - * IMAGE_FILE_LINE_NUMS_STRIPPED - */ - .short 0x306 + /* Characteristics */ + .short (IMAGE_FILE_EXECUTABLE_IMAGE | \ + IMAGE_FILE_LINE_NUMS_STRIPPED | \ + IMAGE_FILE_LOCAL_SYMS_STRIPPED | \ + IMAGE_FILE_32BIT_MACHINE | \ + IMAGE_FILE_DEBUG_STRIPPED) optional_header: - .short 0x10b /* PE32+ format */ + .short 0x10b /* PE32 format */ .byte 0x02 /* MajorLinkerVersion */ .byte 0x14 /* MinorLinkerVersion */ .long _edata - _start /* SizeOfCode */ diff --git a/arch/riscv/lib/crt0_riscv_efi.S b/arch/riscv/lib/crt0_riscv_efi.S index 18f61f515a..b7b5329e1f 100644 --- a/arch/riscv/lib/crt0_riscv_efi.S +++ b/arch/riscv/lib/crt0_riscv_efi.S @@ -41,13 +41,13 @@ coff_header: .short 2 /* nr_sections */ .long 0 /* TimeDateStamp */ .long 0 /* PointerToSymbolTable */ - .long 1 /* NumberOfSymbols */ + .long 0 /* NumberOfSymbols */ .short section_table - optional_header /* SizeOfOptionalHeader */ - /* - * Characteristics: IMAGE_FILE_DEBUG_STRIPPED | - * IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LINE_NUMS_STRIPPED - */ - .short 0x206 + /* Characteristics */ + .short (IMAGE_FILE_EXECUTABLE_IMAGE | \ + IMAGE_FILE_LINE_NUMS_STRIPPED | \ + IMAGE_FILE_LOCAL_SYMS_STRIPPED | \ + IMAGE_FILE_DEBUG_STRIPPED) optional_header: .short 0x20b /* PE32+ format */ .byte 0x02 /* MajorLinkerVersion */ diff --git a/arch/x86/config.mk b/arch/x86/config.mk index 8151e476d4..b5e8f46297 100644 --- a/arch/x86/config.mk +++ b/arch/x86/config.mk @@ -34,7 +34,7 @@ PLATFORM_LDFLAGS += -m $(if $(IS_32BIT),elf_i386,elf_x86_64) # This is used in the top-level Makefile which does not include # PLATFORM_LDFLAGS -LDFLAGS_EFI_PAYLOAD := -Bsymbolic -Bsymbolic-functions -shared --no-undefined +LDFLAGS_EFI_PAYLOAD := -Bsymbolic -Bsymbolic-functions -shared --no-undefined -s OBJCOPYFLAGS_EFI := -j .text -j .sdata -j .data -j .dynamic -j .dynsym \ -j .rel -j .rela -j .reloc @@ -65,7 +65,7 @@ CPPFLAGS_crt0-efi-$(EFIARCH).o += $(CFLAGS_EFI) ifeq ($(CONFIG_EFI_APP),y) PLATFORM_CPPFLAGS += $(CFLAGS_EFI) -LDFLAGS_FINAL += -znocombreloc -shared +LDFLAGS_FINAL += -znocombreloc -shared -s LDSCRIPT := $(LDSCRIPT_EFI) else diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 4d68d80748..38679ffc56 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -148,16 +148,16 @@ 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 + * The FDT is copied to a suitable location within the EFI memory map. + * 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 - * FDT start - * @fdt_sizep: Returns new size of FDT, including - * @return new relocated address of FDT + * @fdtp: On entry a pointer to the flattened device tree. + * On exit a pointer to the copy of the flattened device tree. + * FDT start + * Return: status code */ -static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep) +static efi_status_t copy_fdt(void **fdtp) { unsigned long fdt_ram_start = -1L, fdt_pages; efi_status_t ret = 0; @@ -178,17 +178,19 @@ static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep) } /* - * 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; - - /* Safe fdt location is at 127MB */ - new_fdt_addr = fdt_ram_start + (127 * 1024 * 1024) + fdt_size; + fdt = *fdtp; + 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 sandbox 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); @@ -203,13 +205,11 @@ static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep) 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); - *fdt_addrp = new_fdt_addr; - *fdt_sizep = fdt_size; + *fdtp = (void *)(uintptr_t)new_fdt_addr; done: return ret; } @@ -277,7 +277,11 @@ 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; + /* Convert from sandbox address space. */ + addr = (uintptr_t)map_sysmem(addr, 0); + + 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)) printf("FDT memrsv map %d: Failed to add to map\n", i); @@ -287,7 +291,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; @@ -297,34 +300,58 @@ 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, &fdt_size); + ret = copy_fdt(&fdt); 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; } - 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) return EFI_OUT_OF_RESOURCES; - /* And reserve the space in the memory map */ - fdt_start = fdt_addr; - fdt_pages = fdt_size >> EFI_PAGE_SHIFT; + return ret; +} + +static efi_status_t bootefi_run_prepare(const char *load_options_path, + struct efi_device_path *device_path, + struct efi_device_path *image_path, + struct efi_loaded_image_obj **image_objp, + struct efi_loaded_image **loaded_image_infop) +{ + efi_status_t ret; - ret = efi_add_memory_map(fdt_start, fdt_pages, - EFI_BOOT_SERVICES_DATA, true); + ret = efi_setup_loaded_image(device_path, image_path, image_objp, + loaded_image_infop); + if (ret != EFI_SUCCESS) + return ret; - return ret; + /* Transfer environment variable as load options */ + set_load_options(*loaded_image_infop, load_options_path); + + return 0; +} + +/** + * bootefi_run_finish() - finish up after running an EFI test + * + * @loaded_image_info: Pointer to a struct which holds the loaded image info + * @image_objj: Pointer to a struct which holds the loaded image object + */ +static void bootefi_run_finish(struct efi_loaded_image_obj *image_obj, + struct efi_loaded_image *loaded_image_info) +{ + efi_restore_gd(); + free(loaded_image_info->load_options); + efi_delete_handle(&image_obj->header); } /** @@ -345,7 +372,7 @@ static efi_status_t do_bootefi_exec(void *efi, efi_handle_t mem_handle = NULL; struct efi_device_path *memdp = NULL; efi_status_t ret; - struct efi_loaded_image_obj *image_handle = NULL; + struct efi_loaded_image_obj *image_obj = NULL; struct efi_loaded_image *loaded_image_info = NULL; EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, @@ -354,7 +381,7 @@ static efi_status_t do_bootefi_exec(void *efi, /* * Special case for efi payload not loaded from disk, such as * 'bootefi hello' or for example payload loaded directly into - * memory via jtag, etc: + * memory via JTAG, etc: */ if (!device_path && !image_path) { printf("WARNING: using memory device/image path, this may confuse some payloads!\n"); @@ -367,27 +394,25 @@ static efi_status_t do_bootefi_exec(void *efi, */ ret = efi_create_handle(&mem_handle); if (ret != EFI_SUCCESS) - goto exit; + return ret; /* TODO: leaks device_path */ ret = efi_add_protocol(mem_handle, &efi_guid_device_path, device_path); if (ret != EFI_SUCCESS) - goto exit; + goto err_add_protocol; } else { assert(device_path && image_path); } - ret = efi_setup_loaded_image(device_path, image_path, &image_handle, - &loaded_image_info); - if (ret != EFI_SUCCESS) - goto exit; + ret = bootefi_run_prepare("bootargs", device_path, image_path, + &image_obj, &loaded_image_info); + if (ret) + goto err_prepare; - /* Transfer environment variable bootargs as load options */ - set_load_options(loaded_image_info, "bootargs"); /* Load the EFI payload */ - entry = efi_load_pe(image_handle, efi, loaded_image_info); + entry = efi_load_pe(image_obj, efi, loaded_image_info); if (!entry) { ret = EFI_LOAD_ERROR; - goto exit; + goto err_prepare; } if (memdp) { @@ -405,9 +430,9 @@ static efi_status_t do_bootefi_exec(void *efi, /* Call our payload! */ debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); - if (setjmp(&image_handle->exit_jmp)) { - ret = image_handle->exit_status; - goto exit; + if (setjmp(&image_obj->exit_jmp)) { + ret = image_obj->exit_status; + goto err_prepare; } #ifdef CONFIG_ARM64 @@ -418,7 +443,7 @@ static efi_status_t do_bootefi_exec(void *efi, /* Move into EL2 and keep running there */ armv8_switch_to_el2((ulong)entry, - (ulong)image_handle, + (ulong)&image_obj->header, (ulong)&systab, 0, (ulong)efi_run_in_el2, ES_TO_AARCH64); @@ -435,7 +460,7 @@ static efi_status_t do_bootefi_exec(void *efi, secure_ram_addr(_do_nonsec_entry)( efi_run_in_hyp, (uintptr_t)entry, - (uintptr_t)image_handle, + (uintptr_t)&image_obj->header, (uintptr_t)&systab); /* Should never reach here, efi exits with longjmp */ @@ -443,18 +468,59 @@ static efi_status_t do_bootefi_exec(void *efi, } #endif - ret = efi_do_enter(image_handle, &systab, entry); + ret = efi_do_enter(&image_obj->header, &systab, entry); -exit: +err_prepare: /* image has returned, loaded-image obj goes *poof*: */ - if (image_handle) - efi_delete_handle(&image_handle->parent); + bootefi_run_finish(image_obj, loaded_image_info); + +err_add_protocol: if (mem_handle) efi_delete_handle(mem_handle); return ret; } +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST +/** + * bootefi_test_prepare() - prepare to run an EFI test + * + * This sets things up so we can call EFI functions. This involves preparing + * the 'gd' pointer and setting up the load ed image data structures. + * + * @image_objp: loaded_image_infop: Pointer to a struct which will hold the + * loaded image object. This struct will be inited by this function before + * use. + * @loaded_image_infop: Pointer to a struct which will hold the loaded image + * info. This struct will be inited by this function before use. + * @path: File path to the test being run (often just the test name with a + * backslash before it + * @test_func: Address of the test function that is being run + * @load_options_path: U-Boot environment variable to use as load options + * @return 0 if OK, -ve on error + */ +static efi_status_t bootefi_test_prepare + (struct efi_loaded_image_obj **image_objp, + struct efi_loaded_image **loaded_image_infop, const char *path, + ulong test_func, const char *load_options_path) +{ + /* Construct a dummy device path */ + bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, + (uintptr_t)test_func, + (uintptr_t)test_func); + if (!bootefi_device_path) + return EFI_OUT_OF_RESOURCES; + bootefi_image_path = efi_dp_from_file(NULL, 0, path); + if (!bootefi_image_path) + return EFI_OUT_OF_RESOURCES; + + return bootefi_run_prepare(load_options_path, bootefi_device_path, + bootefi_image_path, image_objp, + loaded_image_infop); +} + +#endif /* CONFIG_CMD_BOOTEFI_SELFTEST */ + static int do_bootefi_bootmgr_exec(void) { struct efi_device_path *device_path, *file_path; @@ -527,29 +593,17 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #endif #ifdef CONFIG_CMD_BOOTEFI_SELFTEST if (!strcmp(argv[1], "selftest")) { - struct efi_loaded_image_obj *image_handle; + struct efi_loaded_image_obj *image_obj; struct efi_loaded_image *loaded_image_info; - /* Construct a dummy device path. */ - bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, - (uintptr_t)&efi_selftest, - (uintptr_t)&efi_selftest); - bootefi_image_path = efi_dp_from_file(NULL, 0, "\\selftest"); - - r = efi_setup_loaded_image(bootefi_device_path, - bootefi_image_path, &image_handle, - &loaded_image_info); - if (r != EFI_SUCCESS) + if (bootefi_test_prepare(&image_obj, &loaded_image_info, + "\\selftest", (uintptr_t)&efi_selftest, + "efi_selftest")) return CMD_RET_FAILURE; - efi_save_gd(); - /* Transfer environment variable efi_selftest as load options */ - set_load_options(loaded_image_info, "efi_selftest"); /* Execute the test */ - r = efi_selftest(image_handle, &systab); - efi_restore_gd(); - free(loaded_image_info->load_options); - efi_delete_handle(&image_handle->parent); + r = efi_selftest(&image_obj->header, &systab); + bootefi_run_finish(image_obj, loaded_image_info); return r != EFI_SUCCESS; } else #endif @@ -608,45 +662,19 @@ U_BOOT_CMD( void efi_set_bootdev(const char *dev, const char *devnr, const char *path) { - char filename[32] = { 0 }; /* dp->str is u16[32] long */ - char *s; + struct efi_device_path *device, *image; + efi_status_t ret; /* efi_set_bootdev is typically called repeatedly, recover memory */ efi_free_pool(bootefi_device_path); efi_free_pool(bootefi_image_path); - /* If blk_get_device_part_str fails, avoid duplicate free. */ - bootefi_device_path = NULL; - bootefi_image_path = NULL; - - if (strcmp(dev, "Net")) { - struct blk_desc *desc; - disk_partition_t fs_partition; - int part; - - part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, - 1); - if (part < 0) - return; - - bootefi_device_path = efi_dp_from_part(desc, part); - } else { -#ifdef CONFIG_NET - bootefi_device_path = efi_dp_from_eth(); -#endif - } - - if (!path) - return; - if (strcmp(dev, "Net")) { - /* Add leading / to fs paths, because they're absolute */ - snprintf(filename, sizeof(filename), "/%s", path); + ret = efi_dp_from_name(dev, devnr, path, &device, &image); + if (ret == EFI_SUCCESS) { + bootefi_device_path = device; + bootefi_image_path = image; } else { - snprintf(filename, sizeof(filename), "%s", path); + bootefi_device_path = NULL; + bootefi_image_path = NULL; } - /* DOS style file path: */ - s = filename; - while ((s = strchr(s, '/'))) - *s++ = '\\'; - bootefi_image_path = efi_dp_from_file(NULL, 0, filename); } diff --git a/common/board_r.c b/common/board_r.c index 0c2129b8ce..21d3b3c64c 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -453,7 +453,8 @@ static int initr_env(void) else set_default_env(NULL, 0); #ifdef CONFIG_OF_CONTROL - env_set_addr("fdtcontroladdr", gd->fdt_blob); + env_set_hex("fdtcontroladdr", + (unsigned long)map_to_sysmem(gd->fdt_blob)); #endif /* Initialize from environment */ diff --git a/common/fdt_support.c b/common/fdt_support.c index e6daa67990..3440e42a25 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> @@ -633,7 +634,7 @@ 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(blob, map_to_sysmem(blob), actualsize); if (ret < 0) return ret; diff --git a/configs/vf610twr_defconfig b/configs/vf610twr_defconfig index a928cd4fbc..504eebbf5d 100644 --- a/configs/vf610twr_defconfig +++ b/configs/vf610twr_defconfig @@ -41,4 +41,4 @@ CONFIG_PHY_MICREL=y CONFIG_MII=y CONFIG_DM_SERIAL=y CONFIG_FSL_LPUART=y -# CONFIG_EFI_UNICODE_CAPITALIZATION is not set +# CONFIG_EFI_LOADER is not set diff --git a/configs/vf610twr_nand_defconfig b/configs/vf610twr_nand_defconfig index 560776fe56..0a1e28e2ec 100644 --- a/configs/vf610twr_nand_defconfig +++ b/configs/vf610twr_nand_defconfig @@ -41,4 +41,4 @@ CONFIG_PHY_MICREL=y CONFIG_MII=y CONFIG_DM_SERIAL=y CONFIG_FSL_LPUART=y -# CONFIG_EFI_UNICODE_CAPITALIZATION is not set +# CONFIG_EFI_LOADER is not set diff --git a/doc/README.iscsi b/doc/README.iscsi index faee636264..3a12438f90 100644 --- a/doc/README.iscsi +++ b/doc/README.iscsi @@ -1,8 +1,6 @@ -iSCSI booting with U-Boot and iPXE -================================== +# iSCSI booting with U-Boot and iPXE -Motivation ----------- +## Motivation U-Boot has only a reduced set of supported network protocols. The focus for network booting has been on UDP based protocols. A TCP stack and HTTP support @@ -41,8 +39,7 @@ fine grained control of the boot process and can provide a command shell. iPXE can be built as an EFI application (named snp.efi) which can be loaded and run by U-Boot. -Boot sequence -------------- +## Boot sequence U-Boot loads the EFI application iPXE snp.efi using the bootefi command. This application has network access via the simple network protocol offered by @@ -106,19 +103,16 @@ the EFI stub Linux is called as an EFI application:: | | | ~ ~ ~ ~| -Security --------- +## Security The iSCSI protocol is not encrypted. The traffic could be secured using IPsec but neither U-Boot nor iPXE does support this. So we should at least separate the iSCSI traffic from all other network traffic. This can be achieved using a virtual local area network (VLAN). -Configuration -------------- +## Configuration -iPXE -^^^^ +### iPXE For running iPXE on arm64 the bin-arm64-efi/snp.efi build target is needed:: @@ -157,9 +151,20 @@ following into src/config/local/general.h is sufficient for most use cases:: #define DOWNLOAD_PROTO_NFS /* Network File System Protocol */ #define DOWNLOAD_PROTO_FILE /* Local file system access */ -Links ------ +### Open-iSCSI + +When the root file system is on an iSCSI drive you should disable pings and set +the replacement timer to a high value [3]: + + node.conn[0].timeo.noop_out_interval = 0 + node.conn[0].timeo.noop_out_timeout = 0 + node.session.timeo.replacement_timeout = 86400 + +## Links * [1](https://ipxe.org) https://ipxe.org - iPXE open source boot firmware * [2](https://www.gnu.org/software/grub/) https://www.gnu.org/software/grub/ - - GNU GRUB (Grand Unified Bootloader) + GNU GRUB (Grand Unified Bootloader) +* [3](https://github.com/open-iscsi/open-iscsi/blob/master/README) + https://github.com/open-iscsi/open-iscsi/blob/master/README - + Open-iSCSI README diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index 2cb35f356f..c8c47acfd3 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -9,31 +9,38 @@ #include <common.h> #include <dm.h> #include <dm/lists.h> +#include <efi_loader.h> #include <linux/libfdt.h> #include <linux/arm-smccc.h> #include <linux/errno.h> #include <linux/printk.h> #include <linux/psci.h> -psci_fn *invoke_psci_fn; +#define DRIVER_NAME "psci" -static unsigned long __invoke_psci_fn_hvc(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2) -{ - struct arm_smccc_res res; +#define PSCI_METHOD_HVC 1 +#define PSCI_METHOD_SMC 2 - arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); - return res.a0; -} +int __efi_runtime_data psci_method; -static unsigned long __invoke_psci_fn_smc(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2) +unsigned long __efi_runtime invoke_psci_fn + (unsigned long function_id, unsigned long arg0, + unsigned long arg1, unsigned long arg2) { struct arm_smccc_res res; - arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + /* + * In the __efi_runtime we need to avoid the switch statement. In some + * cases the compiler creates lookup tables to implement switch. These + * tables are not correctly relocated when SetVirtualAddressMap is + * called. + */ + if (psci_method == PSCI_METHOD_SMC) + arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + else if (psci_method == PSCI_METHOD_HVC) + arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + else + res.a0 = PSCI_RET_DISABLED; return res.a0; } @@ -67,9 +74,9 @@ static int psci_probe(struct udevice *dev) } if (!strcmp("hvc", method)) { - invoke_psci_fn = __invoke_psci_fn_hvc; + psci_method = PSCI_METHOD_HVC; } else if (!strcmp("smc", method)) { - invoke_psci_fn = __invoke_psci_fn_smc; + psci_method = PSCI_METHOD_SMC; } else { pr_warn("invalid \"method\" property: %s\n", method); return -EINVAL; @@ -78,6 +85,67 @@ static int psci_probe(struct udevice *dev) return 0; } +/** + * void do_psci_probe() - probe PSCI firmware driver + * + * Ensure that psci_method is initialized. + */ +static void __maybe_unused do_psci_probe(void) +{ + struct udevice *dev; + + uclass_get_device_by_name(UCLASS_FIRMWARE, DRIVER_NAME, &dev); +} + +#if IS_ENABLED(CONFIG_EFI_LOADER) && IS_ENABLED(CONFIG_PSCI_RESET) +efi_status_t efi_reset_system_init(void) +{ + do_psci_probe(); + return EFI_SUCCESS; +} + +void __efi_runtime EFIAPI efi_reset_system(enum efi_reset_type reset_type, + efi_status_t reset_status, + unsigned long data_size, + void *reset_data) +{ + if (reset_type == EFI_RESET_COLD || + reset_type == EFI_RESET_WARM || + reset_type == EFI_RESET_PLATFORM_SPECIFIC) { + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + } else if (reset_type == EFI_RESET_SHUTDOWN) { + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); + } + while (1) + ; +} +#endif /* IS_ENABLED(CONFIG_EFI_LOADER) && IS_ENABLED(CONFIG_PSCI_RESET) */ + +#ifdef CONFIG_PSCI_RESET +void reset_misc(void) +{ + do_psci_probe(); + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); +} +#endif /* CONFIG_PSCI_RESET */ + +#ifdef CONFIG_CMD_POWEROFF +int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + do_psci_probe(); + + puts("poweroff ...\n"); + udelay(50000); /* wait 50 ms */ + + disable_interrupts(); + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); + enable_interrupts(); + + log_err("Power off not supported on this platform\n"); + return CMD_RET_FAILURE; +} +#endif + static const struct udevice_id psci_of_match[] = { { .compatible = "arm,psci" }, { .compatible = "arm,psci-0.2" }, @@ -86,7 +154,7 @@ static const struct udevice_id psci_of_match[] = { }; U_BOOT_DRIVER(psci) = { - .name = "psci", + .name = DRIVER_NAME, .id = UCLASS_FIRMWARE, .of_match = psci_of_match, .bind = psci_bind, diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 03746dd12f..d456beb43f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -70,6 +70,7 @@ comment "USB peripherals" config USB_STORAGE bool "USB Mass Storage support" + depends on !(BLK && !DM_USB) ---help--- Say Y here if you want to connect USB mass storage devices to your board's USB port. @@ -365,6 +365,7 @@ int fs_set_blk_dev_with_part(struct blk_desc *desc, int part) for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { if (!info->probe(fs_dev_desc, &fs_partition)) { fs_type = info->fstype; + fs_dev_part = part; return 0; } } diff --git a/include/asm-generic/pe.h b/include/asm-generic/pe.h index 9a8b5e82e3..faae534e37 100644 --- a/include/asm-generic/pe.h +++ b/include/asm-generic/pe.h @@ -11,6 +11,24 @@ #ifndef _ASM_PE_H #define _ASM_PE_H +/* Characteristics */ +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +/* Reserved 0x0040 */ +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + /* Subsystem type */ #define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 #define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 diff --git a/include/common.h b/include/common.h index 8b56137032..a8e879e1b9 100644 --- a/include/common.h +++ b/include/common.h @@ -106,6 +106,17 @@ int mdm_init(void); void board_show_dram(phys_size_t size); /** + * Get the uppermost pointer that is valid to access + * + * Some systems may not map all of their address space. This function allows + * boards to indicate what their highest support pointer value is for DRAM + * access. + * + * @param total_size Size of U-Boot (unused?) + */ +ulong board_get_usable_ram_top(ulong total_size); + +/** * arch_fixup_fdt() - Write arch-specific information to fdt * * Defined in arch/$(ARCH)/lib/bootm-fdt.c diff --git a/include/efi.h b/include/efi.h index b1deb609b4..b5e2c64f38 100644 --- a/include/efi.h +++ b/include/efi.h @@ -96,7 +96,7 @@ typedef struct { typedef unsigned long efi_status_t; typedef u64 efi_physical_addr_t; typedef u64 efi_virtual_addr_t; -typedef void *efi_handle_t; +typedef struct efi_object *efi_handle_t; #define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, \ diff --git a/include/efi_api.h b/include/efi_api.h index e850b951eb..aef77b6319 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -85,10 +85,10 @@ struct efi_boot_services { efi_status_t (EFIAPI *check_event)(struct efi_event *event); #define EFI_NATIVE_INTERFACE 0x00000000 efi_status_t (EFIAPI *install_protocol_interface)( - void **handle, const efi_guid_t *protocol, + efi_handle_t *handle, const efi_guid_t *protocol, int protocol_interface_type, void *protocol_interface); efi_status_t (EFIAPI *reinstall_protocol_interface)( - void *handle, const efi_guid_t *protocol, + efi_handle_t handle, const efi_guid_t *protocol, void *old_interface, void *new_interface); efi_status_t (EFIAPI *uninstall_protocol_interface)( efi_handle_t handle, const efi_guid_t *protocol, @@ -164,9 +164,9 @@ struct efi_boot_services { efi_status_t (EFIAPI *locate_protocol)(const efi_guid_t *protocol, void *registration, void **protocol_interface); efi_status_t (EFIAPI *install_multiple_protocol_interfaces)( - void **handle, ...); + efi_handle_t *handle, ...); efi_status_t (EFIAPI *uninstall_multiple_protocol_interfaces)( - void *handle, ...); + efi_handle_t handle, ...); efi_status_t (EFIAPI *calculate_crc32)(const void *data, efi_uintn_t data_size, u32 *crc32); @@ -241,8 +241,8 @@ struct efi_runtime_services { efi_status_t (EFIAPI *query_capsule_caps)( struct efi_capsule_header **capsule_header_array, efi_uintn_t capsule_count, - u64 maximum_capsule_size, - u32 reset_type); + u64 *maximum_capsule_size, + u32 *reset_type); efi_status_t (EFIAPI *query_variable_info)( u32 attributes, u64 *maximum_variable_storage_size, @@ -965,7 +965,7 @@ struct efi_file_info { struct efi_time last_access_time; struct efi_time modification_time; u64 attribute; - s16 file_name[0]; + u16 file_name[0]; }; struct efi_file_system_info { diff --git a/include/efi_loader.h b/include/efi_loader.h index 1417c3588f..53f08161ab 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -167,28 +167,41 @@ struct efi_handler { struct list_head open_infos; }; -/* - * UEFI has a poor man's OO model where one "object" can be polymorphic and have - * multiple different protocols (classes) attached to it. +/** + * struct efi_object - dereferenced EFI handle + * + * @link: pointers to put the handle into a linked list + * @protocols: linked list with the protocol interfaces installed on this + * handle + * + * UEFI offers a flexible and expandable object model. The objects in the UEFI + * API are devices, drivers, and loaded images. struct efi_object is our storage + * structure for these objects. * - * This struct is the parent struct for all of our actual implementation objects - * that can include it to make themselves an EFI object + * When including this structure into a larger structure always put it first so + * that when deleting a handle the whole encompassing structure can be freed. + * + * A pointer to this structure is referred to as a handle. Typedef efi_handle_t + * has been created for such pointers. */ struct efi_object { /* Every UEFI object is part of a global object list */ struct list_head link; /* The list of protocols */ struct list_head protocols; - /* The object spawner can either use this for data or as identifier */ - void *handle; }; /** * struct efi_loaded_image_obj - handle of a loaded image + * + * @header: EFI object header + * @reloc_base: base address for the relocated image + * @reloc_size: size of the relocated image + * @exit_jmp: long jump buffer for returning form started image + * @entry: entry address of the relocated image */ struct efi_loaded_image_obj { - /* Generic EFI object parent class data */ - struct efi_object parent; + struct efi_object header; void *reloc_base; aligned_u64 reloc_size; efi_status_t exit_status; @@ -290,11 +303,11 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map); /* Call this to set the current device name */ void efi_set_bootdev(const char *dev, const char *devnr, const char *path); /* Add a new object to the object list. */ -void efi_add_handle(struct efi_object *obj); +void efi_add_handle(efi_handle_t obj); /* Create handle */ efi_status_t efi_create_handle(efi_handle_t *handle); /* Delete handle */ -void efi_delete_handle(struct efi_object *obj); +void efi_delete_handle(efi_handle_t obj); /* Call this to validate a handle and find the EFI object for it */ struct efi_object *efi_search_obj(const efi_handle_t handle); /* Find a protocol on a handle */ @@ -331,7 +344,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 */ @@ -419,6 +441,10 @@ const struct efi_device_path *efi_dp_last_node( efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, struct efi_device_path **device_path, struct efi_device_path **file_path); +efi_status_t efi_dp_from_name(const char *dev, const char *devnr, + const char *path, + struct efi_device_path **device, + struct efi_device_path **file); #define EFI_DP_TYPE(_dp, _type, _subtype) \ (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \ @@ -492,6 +518,29 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor, u32 attributes, efi_uintn_t data_size, void *data); +/* + * See section 3.1.3 in the v2.7 UEFI spec for more details on + * the layout of EFI_LOAD_OPTION. In short it is: + * + * typedef struct _EFI_LOAD_OPTION { + * UINT32 Attributes; + * UINT16 FilePathListLength; + * // CHAR16 Description[]; <-- variable length, NULL terminated + * // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; + * <-- FilePathListLength bytes + * // UINT8 OptionalData[]; + * } EFI_LOAD_OPTION; + */ +struct efi_load_option { + u32 attributes; + u16 file_path_length; + u16 *label; + struct efi_device_path *file_path; + u8 *optional_data; +}; + +void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data); +unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data); void *efi_bootmgr_load(struct efi_device_path **device_path, struct efi_device_path **file_path); diff --git a/include/efi_selftest.h b/include/efi_selftest.h index 56beac305e..49d3d6d0b4 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -129,7 +129,6 @@ u16 efi_st_get_key(void); * @setup: set up the unit test * @teardown: tear down the unit test * @execute: execute the unit test - * @setup_ok: setup was successful (set at runtime) * @on_request: test is only executed on request */ struct efi_unit_test { @@ -139,7 +138,6 @@ struct efi_unit_test { const struct efi_system_table *systable); int (*execute)(void); int (*teardown)(void); - int setup_ok; bool on_request; }; diff --git a/include/linux/psci.h b/include/linux/psci.h index 8d13bd2702..9433df836b 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -88,10 +88,8 @@ #define PSCI_RET_DISABLED -8 #ifdef CONFIG_ARM_PSCI_FW -typedef unsigned long (psci_fn)(unsigned long, unsigned long, - unsigned long, unsigned long); - -extern psci_fn *invoke_psci_fn; +unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3); #else unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) diff --git a/lib/Makefile b/lib/Makefile index 4d2e22027d..8321355a44 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,7 +8,7 @@ ifndef CONFIG_SPL_BUILD obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ -obj-$(CONFIG_EFI_LOADER) += efi_selftest/ +obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/efi/efi.c b/lib/efi/efi.c index c6639f96cc..2c6a50824f 100644 --- a/lib/efi/efi.c +++ b/lib/efi/efi.c @@ -69,7 +69,7 @@ int efi_init(struct efi_priv *priv, const char *banner, efi_handle_t image, efi_putc(priv, ' '); ret = boot->open_protocol(priv->parent_image, &loaded_image_guid, - (void **)&loaded_image, &priv->parent_image, + (void **)&loaded_image, priv->parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (ret) { efi_puts(priv, "Failed to get loaded image protocol\n"); diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 0c5764db12..a095df3f54 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * EFI utils + * EFI boot manager * * Copyright (c) 2017 Rob Clark */ @@ -9,6 +9,7 @@ #include <charset.h> #include <malloc.h> #include <efi_loader.h> +#include <asm/unaligned.h> static const struct efi_boot_services *bs; static const struct efi_runtime_services *rs; @@ -30,42 +31,68 @@ static const struct efi_runtime_services *rs; */ -/* - * See section 3.1.3 in the v2.7 UEFI spec for more details on - * the layout of EFI_LOAD_OPTION. In short it is: - * - * typedef struct _EFI_LOAD_OPTION { - * UINT32 Attributes; - * UINT16 FilePathListLength; - * // CHAR16 Description[]; <-- variable length, NULL terminated - * // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes - * // UINT8 OptionalData[]; - * } EFI_LOAD_OPTION; - */ -struct load_option { - u32 attributes; - u16 file_path_length; - u16 *label; - struct efi_device_path *file_path; - u8 *optional_data; -}; - -/* parse an EFI_LOAD_OPTION, as described above */ -static void parse_load_option(struct load_option *lo, void *ptr) +/* Parse serialized data and transform it into efi_load_option structure */ +void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data) { - lo->attributes = *(u32 *)ptr; - ptr += sizeof(u32); + lo->attributes = get_unaligned_le32(data); + data += sizeof(u32); + + lo->file_path_length = get_unaligned_le16(data); + data += sizeof(u16); - lo->file_path_length = *(u16 *)ptr; - ptr += sizeof(u16); + /* FIXME */ + lo->label = (u16 *)data; + data += (u16_strlen(lo->label) + 1) * sizeof(u16); - lo->label = ptr; - ptr += (u16_strlen(lo->label) + 1) * 2; + /* FIXME */ + lo->file_path = (struct efi_device_path *)data; + data += lo->file_path_length; - lo->file_path = ptr; - ptr += lo->file_path_length; + lo->optional_data = data; +} - lo->optional_data = ptr; +/* + * Serialize efi_load_option structure into byte stream for BootXXXX. + * Return a size of allocated data. + */ +unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data) +{ + unsigned long label_len, option_len; + unsigned long size; + u8 *p; + + label_len = (u16_strlen(lo->label) + 1) * sizeof(u16); + option_len = strlen((char *)lo->optional_data); + + /* total size */ + size = sizeof(lo->attributes); + size += sizeof(lo->file_path_length); + size += label_len; + size += lo->file_path_length; + size += option_len + 1; + p = malloc(size); + if (!p) + return 0; + + /* copy data */ + *data = p; + memcpy(p, &lo->attributes, sizeof(lo->attributes)); + p += sizeof(lo->attributes); + + memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length)); + p += sizeof(lo->file_path_length); + + memcpy(p, lo->label, label_len); + p += label_len; + + memcpy(p, lo->file_path, lo->file_path_length); + p += lo->file_path_length; + + memcpy(p, lo->optional_data, option_len); + p += option_len; + *(char *)p = '\0'; + + return size; } /* free() the result */ @@ -100,7 +127,7 @@ static void *get_var(u16 *name, const efi_guid_t *vendor, static void *try_load_entry(uint16_t n, struct efi_device_path **device_path, struct efi_device_path **file_path) { - struct load_option lo; + struct efi_load_option lo; u16 varname[] = L"Boot0000"; u16 hexmap[] = L"0123456789ABCDEF"; void *load_option, *image = NULL; @@ -115,7 +142,7 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path, if (!load_option) return NULL; - parse_load_option(&lo, load_option); + efi_deserialize_load_option(&lo, load_option); if (lo.attributes & LOAD_OPTION_ACTIVE) { efi_status_t ret; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index da978d2b34..cc9efbb0cb 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -26,6 +26,14 @@ LIST_HEAD(efi_obj_list); /* List of all events */ LIST_HEAD(efi_events); +/* + * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) + * we need to do trickery with caches. Since we don't want to break the EFI + * aware boot path, only apply hacks when loading exiting directly (breaking + * direct Linux EFI booting along the way - oh well). + */ +static bool efi_is_direct_boot = true; + #ifdef CONFIG_ARM /* * The "gd" pointer lives in a register on ARM and AArch64 that we declare @@ -416,13 +424,12 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) * * The protocols list is initialized. The object handle is set. */ -void efi_add_handle(struct efi_object *obj) +void efi_add_handle(efi_handle_t handle) { - if (!obj) + if (!handle) return; - INIT_LIST_HEAD(&obj->protocols); - obj->handle = obj; - list_add_tail(&obj->link, &efi_obj_list); + INIT_LIST_HEAD(&handle->protocols); + list_add_tail(&handle->link, &efi_obj_list); } /** @@ -440,7 +447,7 @@ efi_status_t efi_create_handle(efi_handle_t *handle) return EFI_OUT_OF_RESOURCES; efi_add_handle(obj); - *handle = obj->handle; + *handle = obj; return EFI_SUCCESS; } @@ -536,13 +543,13 @@ efi_status_t efi_remove_all_protocols(const efi_handle_t handle) * * @obj: handle to delete */ -void efi_delete_handle(struct efi_object *obj) +void efi_delete_handle(efi_handle_t handle) { - if (!obj) + if (!handle) return; - efi_remove_all_protocols(obj->handle); - list_del(&obj->link); - free(obj); + efi_remove_all_protocols(handle); + list_del(&handle->link); + free(handle); } /** @@ -927,7 +934,7 @@ struct efi_object *efi_search_obj(const efi_handle_t handle) struct efi_object *efiobj; list_for_each_entry(efiobj, &efi_obj_list, link) { - if (efiobj->handle == handle) + if (efiobj == handle) return efiobj; } @@ -1019,7 +1026,7 @@ efi_status_t efi_add_protocol(const efi_handle_t handle, * Return: status code */ static efi_status_t EFIAPI efi_install_protocol_interface( - void **handle, const efi_guid_t *protocol, + efi_handle_t *handle, const efi_guid_t *protocol, int protocol_interface_type, void *protocol_interface) { efi_status_t r; @@ -1052,7 +1059,7 @@ out: /** * efi_get_drivers() - get all drivers associated to a controller - * @efiobj: handle of the controller + * @handle: handle of the controller * @protocol: protocol GUID (optional) * @number_of_drivers: number of child controllers * @driver_handle_buffer: handles of the the drivers @@ -1061,7 +1068,7 @@ out: * * Return: status code */ -static efi_status_t efi_get_drivers(struct efi_object *efiobj, +static efi_status_t efi_get_drivers(efi_handle_t handle, const efi_guid_t *protocol, efi_uintn_t *number_of_drivers, efi_handle_t **driver_handle_buffer) @@ -1072,7 +1079,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, bool duplicate; /* Count all driver associations */ - list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(handler, &handle->protocols, link) { if (protocol && guidcmp(handler->guid, protocol)) continue; list_for_each_entry(item, &handler->open_infos, link) { @@ -1090,7 +1097,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, if (!*driver_handle_buffer) return EFI_OUT_OF_RESOURCES; /* Collect unique driver handles */ - list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(handler, &handle->protocols, link) { if (protocol && guidcmp(handler->guid, protocol)) continue; list_for_each_entry(item, &handler->open_infos, link) { @@ -1117,7 +1124,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, /** * efi_disconnect_all_drivers() - disconnect all drivers from a controller - * @efiobj: handle of the controller + * @handle: handle of the controller * @protocol: protocol GUID (optional) * @child_handle: handle of the child to destroy * @@ -1128,16 +1135,16 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, * * Return: status code */ -static efi_status_t efi_disconnect_all_drivers( - struct efi_object *efiobj, - const efi_guid_t *protocol, - efi_handle_t child_handle) +static efi_status_t efi_disconnect_all_drivers + (efi_handle_t handle, + const efi_guid_t *protocol, + efi_handle_t child_handle) { efi_uintn_t number_of_drivers; efi_handle_t *driver_handle_buffer; efi_status_t r, ret; - ret = efi_get_drivers(efiobj, protocol, &number_of_drivers, + ret = efi_get_drivers(handle, protocol, &number_of_drivers, &driver_handle_buffer); if (ret != EFI_SUCCESS) return ret; @@ -1145,7 +1152,7 @@ static efi_status_t efi_disconnect_all_drivers( ret = EFI_NOT_FOUND; while (number_of_drivers) { r = EFI_CALL(efi_disconnect_controller( - efiobj->handle, + handle, driver_handle_buffer[--number_of_drivers], child_handle)); if (r == EFI_SUCCESS) @@ -1156,21 +1163,19 @@ static efi_status_t efi_disconnect_all_drivers( } /** - * efi_uninstall_protocol_interface() - uninstall protocol interface + * efi_uninstall_protocol() - uninstall protocol interface + * * @handle: handle from which the protocol shall be removed * @protocol: GUID of the protocol to be removed * @protocol_interface: interface to be removed * - * This function implements the UninstallProtocolInterface service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. + * This function DOES NOT delete a handle without installed protocol. * * Return: status code */ -static efi_status_t EFIAPI efi_uninstall_protocol_interface( - efi_handle_t handle, const efi_guid_t *protocol, - void *protocol_interface) +static efi_status_t efi_uninstall_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface) { struct efi_object *efiobj; struct efi_handler *handler; @@ -1178,8 +1183,6 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface( struct efi_open_protocol_info_item *pos; efi_status_t r; - EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); - /* Check handle */ efiobj = efi_search_obj(handle); if (!efiobj) { @@ -1210,7 +1213,41 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface( } r = efi_remove_protocol(handle, protocol, protocol_interface); out: - return EFI_EXIT(r); + return r; +} + +/** + * efi_uninstall_protocol_interface() - uninstall protocol interface + * @handle: handle from which the protocol shall be removed + * @protocol: GUID of the protocol to be removed + * @protocol_interface: interface to be removed + * + * This function implements the UninstallProtocolInterface service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +static efi_status_t EFIAPI efi_uninstall_protocol_interface + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); + + ret = efi_uninstall_protocol(handle, protocol, protocol_interface); + if (ret != EFI_SUCCESS) + goto out; + + /* If the last protocol has been removed, delete the handle. */ + if (list_empty(&handle->protocols)) { + list_del(&handle->link); + free(handle); + } +out: + return EFI_EXIT(ret); } /** @@ -1240,7 +1277,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( * @search_type: selection criterion * @protocol: GUID of the protocol * @search_key: registration key - * @efiobj: handle + * @handle: handle * * See the documentation of the LocateHandle service in the UEFI specification. * @@ -1248,7 +1285,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( */ static int efi_search(enum efi_locate_search_type search_type, const efi_guid_t *protocol, void *search_key, - struct efi_object *efiobj) + efi_handle_t handle) { efi_status_t ret; @@ -1259,7 +1296,7 @@ static int efi_search(enum efi_locate_search_type search_type, /* TODO: RegisterProtocolNotify is not implemented yet */ return -1; case BY_PROTOCOL: - ret = efi_search_protocol(efiobj->handle, protocol, NULL); + ret = efi_search_protocol(handle, protocol, NULL); return (ret != EFI_SUCCESS); default: /* Invalid search type */ @@ -1331,7 +1368,7 @@ static efi_status_t efi_locate_handle( /* Then fill the array */ list_for_each_entry(efiobj, &efi_obj_list, link) { if (!efi_search(search_type, protocol, search_key, efiobj)) - *buffer++ = efiobj->handle; + *buffer++ = efiobj; } return EFI_SUCCESS; @@ -1489,7 +1526,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, } /* Add internal object to object list */ - efi_add_handle(&obj->parent); + efi_add_handle(&obj->header); if (info_ptr) *info_ptr = info; @@ -1506,7 +1543,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, * When asking for the device path interface, return * bootefi_device_path */ - ret = efi_add_protocol(obj->parent.handle, + ret = efi_add_protocol(&obj->header, &efi_guid_device_path, device_path); if (ret != EFI_SUCCESS) goto failure; @@ -1516,7 +1553,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, * When asking for the loaded_image interface, just * return handle which points to loaded_image_info */ - ret = efi_add_protocol(obj->parent.handle, + ret = efi_add_protocol(&obj->header, &efi_guid_loaded_image, info); if (ret != EFI_SUCCESS) goto failure; @@ -1678,6 +1715,8 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); + efi_is_direct_boot = false; + /* call the image! */ if (setjmp(&image_obj->exit_jmp)) { /* @@ -1786,6 +1825,21 @@ static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) } /** + * efi_exit_caches() - fix up caches for EFI payloads if necessary + */ +static void efi_exit_caches(void) +{ +#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64) + /* + * Grub on 32bit ARM needs to have caches disabled before jumping into + * a zImage, but does not know of all cache layers. Give it a hand. + */ + if (efi_is_direct_boot) + cleanup_before_linux(); +#endif +} + +/** * efi_exit_boot_services() - stop all boot services * @image_handle: handle of the loaded image * @map_key: key of the memory map @@ -1838,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, board_quiesce_devices(); + /* Fix up caches for EFI payloads if necessary */ + efi_exit_caches(); + /* This stops all lingering devices */ bootm_disable_interrupts(); @@ -2176,7 +2233,7 @@ static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol, efiobj = list_entry(lhandle, struct efi_object, link); - ret = efi_search_protocol(efiobj->handle, protocol, &handler); + ret = efi_search_protocol(efiobj, protocol, &handler); if (ret == EFI_SUCCESS) { *protocol_interface = handler->protocol_interface; return EFI_EXIT(EFI_SUCCESS); @@ -2279,8 +2336,8 @@ out: * * Return: status code */ -static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( - void **handle, ...) +static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces + (efi_handle_t *handle, ...) { EFI_ENTRY("%p", handle); @@ -2316,7 +2373,7 @@ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( for (; i; --i) { protocol = efi_va_arg(argptr, efi_guid_t*); protocol_interface = efi_va_arg(argptr, void*); - EFI_CALL(efi_uninstall_protocol_interface(handle, protocol, + EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol, protocol_interface)); } efi_va_end(argptr); @@ -2339,7 +2396,7 @@ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( * Return: status code */ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( - void *handle, ...) + efi_handle_t handle, ...) { EFI_ENTRY("%p", handle); @@ -2358,16 +2415,21 @@ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( if (!protocol) break; protocol_interface = efi_va_arg(argptr, void*); - r = EFI_CALL(efi_uninstall_protocol_interface( - handle, protocol, - protocol_interface)); + r = efi_uninstall_protocol(handle, protocol, + protocol_interface); if (r != EFI_SUCCESS) break; i++; } efi_va_end(argptr); - if (r == EFI_SUCCESS) + if (r == EFI_SUCCESS) { + /* If the last protocol has been removed, delete the handle. */ + if (list_empty(&handle->protocols)) { + list_del(&handle->link); + free(handle); + } return EFI_EXIT(r); + } /* If an error occurred undo all changes. */ efi_va_start(argptr, handle); @@ -2380,7 +2442,8 @@ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( } efi_va_end(argptr); - return EFI_EXIT(r); + /* In case of an error always return EFI_INVALID_PARAMETER */ + return EFI_EXIT(EFI_INVALID_PARAMETER); } /** @@ -2553,10 +2616,10 @@ out: * * Return: status code */ -static efi_status_t EFIAPI efi_open_protocol( - void *handle, const efi_guid_t *protocol, - void **protocol_interface, void *agent_handle, - void *controller_handle, uint32_t attributes) +static efi_status_t EFIAPI efi_open_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void **protocol_interface, efi_handle_t agent_handle, + efi_handle_t controller_handle, uint32_t attributes) { struct efi_handler *handler; efi_status_t r = EFI_INVALID_PARAMETER; @@ -2828,13 +2891,19 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface( EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface, new_interface); - ret = EFI_CALL(efi_uninstall_protocol_interface(handle, protocol, - old_interface)); + + /* Uninstall protocol but do not delete handle */ + ret = efi_uninstall_protocol(handle, protocol, old_interface); if (ret != EFI_SUCCESS) goto out; - ret = EFI_CALL(efi_install_protocol_interface(&handle, protocol, - EFI_NATIVE_INTERFACE, - new_interface)); + + /* Install the new protocol */ + ret = efi_add_protocol(handle, protocol, new_interface); + /* + * The UEFI spec does not specify what should happen to the handle + * if in case of an error no protocol interface remains on the handle. + * So let's do nothing here. + */ if (ret != EFI_SUCCESS) goto out; /* diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 0225222a61..66c33a551d 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -205,7 +205,7 @@ static int query_console_serial(int *rows, int *cols) /* * Not all terminals understand CSI [18t for querying the console size. * We should adhere to escape sequences documented in the console_codes - * manpage and the ECMA-48 standard. + * man page and the ECMA-48 standard. * * So here we follow a different approach. We position the cursor to the * bottom right and query its position. Before leaving the function we @@ -480,7 +480,7 @@ void set_shift_mask(int mod, struct efi_key_state *key_state) * * This gets called when we have already parsed CSI. * - * @modifiers: bitmask (shift, alt, ctrl) + * @modifiers: bit mask (shift, alt, ctrl) * @return: the unmodified code */ static int analyze_modifiers(struct efi_key_state *key_state) @@ -1051,34 +1051,34 @@ static void EFIAPI efi_key_notify(struct efi_event *event, void *context) efi_status_t efi_console_register(void) { efi_status_t r; - struct efi_object *efi_console_output_obj; - struct efi_object *efi_console_input_obj; + efi_handle_t console_output_handle; + efi_handle_t console_input_handle; /* Set up mode information */ query_console_size(); /* Create handles */ - r = efi_create_handle((efi_handle_t *)&efi_console_output_obj); + r = efi_create_handle(&console_output_handle); if (r != EFI_SUCCESS) goto out_of_memory; - r = efi_add_protocol(efi_console_output_obj->handle, + r = efi_add_protocol(console_output_handle, &efi_guid_text_output_protocol, &efi_con_out); if (r != EFI_SUCCESS) goto out_of_memory; - systab.con_out_handle = efi_console_output_obj->handle; - systab.stderr_handle = efi_console_output_obj->handle; + systab.con_out_handle = console_output_handle; + systab.stderr_handle = console_output_handle; - r = efi_create_handle((efi_handle_t *)&efi_console_input_obj); + r = efi_create_handle(&console_input_handle); if (r != EFI_SUCCESS) goto out_of_memory; - r = efi_add_protocol(efi_console_input_obj->handle, + r = efi_add_protocol(console_input_handle, &efi_guid_text_input_protocol, &efi_con_in); if (r != EFI_SUCCESS) goto out_of_memory; - systab.con_in_handle = efi_console_input_obj->handle; - r = efi_add_protocol(efi_console_input_obj->handle, + systab.con_in_handle = console_input_handle; + r = efi_add_protocol(console_input_handle, &efi_guid_text_input_ex_protocol, &efi_con_in_ex); if (r != EFI_SUCCESS) goto out_of_memory; diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 46a24f7882..d94982314a 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -82,7 +82,7 @@ struct efi_device_path *efi_dp_next(const struct efi_device_path *dp) /* * Compare two device-paths, stopping when the shorter of the two hits - * an End* node. This is useful to, for example, compare a device-path + * an End* node. This is useful to, for example, compare a device-path * representing a device with one representing a file on the device, or * a device with a parent device. */ @@ -109,16 +109,17 @@ int efi_dp_match(const struct efi_device_path *a, } /* - * See UEFI spec (section 3.1.2, about short-form device-paths.. - * tl;dr: we can have a device-path that starts with a USB WWID - * or USB Class node, and a few other cases which don't encode - * the full device path with bus hierarchy: + * We can have device paths that start with a USB WWID or a USB Class node, + * and a few other cases which don't encode the full device path with bus + * hierarchy: * * - MESSAGING:USB_WWID * - MESSAGING:USB_CLASS * - MEDIA:FILE_PATH * - MEDIA:HARD_DRIVE * - MESSAGING:URI + * + * See UEFI spec (section 3.1.2, about short-form device-paths) */ static struct efi_device_path *shorten_path(struct efi_device_path *dp) { @@ -150,7 +151,7 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, struct efi_device_path *obj_dp; efi_status_t ret; - ret = efi_search_protocol(efiobj->handle, + ret = efi_search_protocol(efiobj, &efi_guid_device_path, &handler); if (ret != EFI_SUCCESS) continue; @@ -644,7 +645,7 @@ static unsigned dp_part_size(struct blk_desc *desc, int part) /* * Create a device node for a block device partition. * - * @buf buffer to which the device path is wirtten + * @buf buffer to which the device path is written * @desc block device descriptor * @part partition number, 0 identifies a block device */ @@ -709,7 +710,7 @@ static void *dp_part_node(void *buf, struct blk_desc *desc, int part) /* * Create a device path for a block device or one of its partitions. * - * @buf buffer to which the device path is wirtten + * @buf buffer to which the device path is written * @desc block device descriptor * @part partition number, 0 identifies a block device */ @@ -728,7 +729,7 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) /* * We *could* make a more accurate path, by looking at if_type * and handling all the different cases like we do for non- - * legacy (ie CONFIG_BLK=y) case. But most important thing + * legacy (i.e. CONFIG_BLK=y) case. But most important thing * is just to have a unique device-path for if_type+devnum. * So map things to a fictitious USB device. */ @@ -752,7 +753,7 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) return dp_part_node(buf, desc, part); } -/* Construct a device-path from a partition on a blk device: */ +/* Construct a device-path from a partition on a block device: */ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) { void *buf, *start; @@ -771,7 +772,7 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) /* * Create a device node for a block device partition. * - * @buf buffer to which the device path is wirtten + * @buf buffer to which the device path is written * @desc block device descriptor * @part partition number, 0 identifies a block device */ @@ -791,7 +792,7 @@ struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part) return buf; } -/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */ +/* convert path to an UEFI style path (i.e. DOS style backslashes and UTF-16) */ static void path_to_uefi(u16 *uefi, const char *path) { while (*path) { @@ -941,3 +942,53 @@ efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, *file_path = fp; return EFI_SUCCESS; } + +efi_status_t efi_dp_from_name(const char *dev, const char *devnr, + const char *path, + struct efi_device_path **device, + struct efi_device_path **file) +{ + int is_net; + struct blk_desc *desc = NULL; + disk_partition_t fs_partition; + int part = 0; + char filename[32] = { 0 }; /* dp->str is u16[32] long */ + char *s; + + if (path && !file) + return EFI_INVALID_PARAMETER; + + is_net = !strcmp(dev, "Net"); + if (!is_net) { + part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, + 1); + if (part < 0) + return EFI_INVALID_PARAMETER; + + if (device) + *device = efi_dp_from_part(desc, part); + } else { +#ifdef CONFIG_NET + if (device) + *device = efi_dp_from_eth(); +#endif + } + + if (!path) + return EFI_SUCCESS; + + if (!is_net) { + /* Add leading / to fs paths, because they're absolute */ + snprintf(filename, sizeof(filename), "/%s", path); + } else { + snprintf(filename, sizeof(filename), "%s", path); + } + /* DOS style file path: */ + s = filename; + while ((s = strchr(s, '/'))) + *s++ = '\\'; + *file = efi_dp_from_file(((!is_net && device) ? desc : NULL), + part, filename); + + return EFI_SUCCESS; +} diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 0082236359..e219f84b28 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -269,9 +269,9 @@ static char *efi_convert_single_device_node_to_text( * for details. * * device_node device node to be converted - * display_only true if the shorter text represenation shall be used + * display_only true if the shorter text representation shall be used * allow_shortcuts true if shortcut forms may be used - * @return text represenation of the device path + * @return text representation of the device path * NULL if out of memory of device_path is NULL */ static uint16_t EFIAPI *efi_convert_device_node_to_text( @@ -302,9 +302,9 @@ out: * for details. * * device_path device path to be converted - * display_only true if the shorter text represenation shall be used + * display_only true if the shorter text representation shall be used * allow_shortcuts true if shortcut forms may be used - * @return text represenation of the device path + * @return text representation of the device path * NULL if out of memory of device_path is NULL */ static uint16_t EFIAPI *efi_convert_device_path_to_text( diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 13fcc1b471..c037526ad2 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -14,26 +14,30 @@ const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; +/** + * struct efi_disk_obj - EFI disk object + * + * @header: EFI object header + * @ops: EFI disk I/O protocol interface + * @ifname: interface name for block device + * @dev_index: device index of block device + * @media: block I/O media information + * @dp: device path to the block device + * @part: partition + * @volume: simple file system protocol of the partition + * @offset: offset into disk for simple partition + * @desc: internal block device descriptor + */ struct efi_disk_obj { - /* Generic EFI object parent class data */ - struct efi_object parent; - /* EFI Interface callback struct for block I/O */ + struct efi_object header; struct efi_block_io ops; - /* U-Boot ifname for block device */ const char *ifname; - /* U-Boot dev_index for block device */ int dev_index; - /* EFI Interface Media descriptor struct, referenced by ops */ struct efi_block_io_media media; - /* EFI device path to this block device */ struct efi_device_path *dp; - /* partition # */ unsigned int part; - /* handle to filesys proto (for partition objects) */ struct efi_simple_file_system_protocol *volume; - /* Offset into disk for simple partitions */ lbaint_t offset; - /* Internal block device */ struct blk_desc *desc; }; @@ -246,7 +250,7 @@ static efi_status_t efi_disk_add_dev( return EFI_OUT_OF_RESOURCES; /* Hook up to the device list */ - efi_add_handle(&diskobj->parent); + efi_add_handle(&diskobj->header); /* Fill in object data */ if (part) { @@ -258,18 +262,18 @@ static efi_status_t efi_disk_add_dev( diskobj->dp = efi_dp_from_part(desc, part); } diskobj->part = part; - ret = efi_add_protocol(diskobj->parent.handle, &efi_block_io_guid, + ret = efi_add_protocol(&diskobj->header, &efi_block_io_guid, &diskobj->ops); if (ret != EFI_SUCCESS) return ret; - ret = efi_add_protocol(diskobj->parent.handle, &efi_guid_device_path, + ret = efi_add_protocol(&diskobj->header, &efi_guid_device_path, diskobj->dp); if (ret != EFI_SUCCESS) return ret; if (part >= 1) { diskobj->volume = efi_simple_file_system(desc, part, diskobj->dp); - ret = efi_add_protocol(diskobj->parent.handle, + ret = efi_add_protocol(&diskobj->header, &efi_simple_file_system_protocol_guid, diskobj->volume); if (ret != EFI_SUCCESS) @@ -381,7 +385,7 @@ efi_status_t efi_disk_register(void) /* Partitions show up as block devices in EFI */ disks += efi_disk_create_partitions( - disk->parent.handle, desc, if_typename, + &disk->header, desc, if_typename, desc->devnum, dev->name); } #else @@ -426,9 +430,9 @@ efi_status_t efi_disk_register(void) disks++; /* Partitions show up as block devices in EFI */ - disks += efi_disk_create_partitions( - disk->parent.handle, desc, - if_typename, i, devname); + disks += efi_disk_create_partitions + (&disk->header, desc, + if_typename, i, devname); } } #endif diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index beb4fba917..128cb0a627 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -563,7 +563,7 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, if (fh->isdir) info->attribute |= EFI_FILE_DIRECTORY; - ascii2unicode((u16 *)info->file_name, filename); + ascii2unicode(info->file_name, filename); } else if (!guidcmp(info_type, &efi_file_system_info_guid)) { struct efi_file_system_info *info = buffer; disk_partition_t part; diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index a4aa9bcf61..d62ce45912 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -16,15 +16,22 @@ DECLARE_GLOBAL_DATA_PTR; static const efi_guid_t efi_gop_guid = EFI_GOP_GUID; +/** + * struct efi_gop_obj - graphical output protocol object + * + * @header: EFI object header + * @ops: graphical output protocol interface + * @info: graphical output mode information + * @mode: graphical output mode + * @bpix: bits per pixel + * @fb: frame buffer + */ struct efi_gop_obj { - /* Generic EFI object parent class data */ - struct efi_object parent; - /* EFI Interface callback struct for gop */ + struct efi_object header; struct efi_gop ops; - /* The only mode we support */ struct efi_gop_mode_info info; struct efi_gop_mode mode; - /* Fields we only have acces to during init */ + /* Fields we only have access to during init */ u32 bpix; void *fb; }; @@ -236,12 +243,12 @@ static efi_uintn_t gop_get_bpp(struct efi_gop *this) } /* - * Gcc can't optimize our BLT function well, but we need to make sure that + * GCC can't optimize our BLT function well, but we need to make sure that * our 2-dimensional loop gets executed very quickly, otherwise the system * will feel slow. * * By manually putting all obvious branch targets into functions which call - * our generic blt function with constants, the compiler can successfully + * our generic BLT function with constants, the compiler can successfully * optimize for speed. */ static efi_status_t gop_blt_video_fill(struct efi_gop *this, @@ -439,13 +446,13 @@ efi_status_t efi_gop_register(void) } /* Hook up to the device list */ - efi_add_handle(&gopobj->parent); + efi_add_handle(&gopobj->header); /* Fill in object data */ - ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid, + ret = efi_add_protocol(&gopobj->header, &efi_gop_guid, &gopobj->ops); if (ret != EFI_SUCCESS) { - printf("ERROR: Failure adding gop protocol\n"); + printf("ERROR: Failure adding GOP protocol\n"); return ret; } gopobj->ops.query_mode = gop_query_mode; @@ -463,7 +470,10 @@ efi_status_t efi_gop_register(void) if (bpix == LCD_COLOR32) #endif { - /* With 32bit color space we can directly expose the fb */ + /* + * With 32bit color space we can directly expose the frame + * buffer + */ gopobj->mode.fb_base = fb_base; gopobj->mode.fb_size = fb_size; } diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 5bd4f4d7fc..4bb517473e 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -11,6 +11,7 @@ #include <mapmem.h> #include <watchdog.h> #include <linux/list_sort.h> +#include <linux/sizes.h> DECLARE_GLOBAL_DATA_PTR; @@ -294,6 +295,12 @@ static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr) { struct list_head *lhandle; + /* + * Prealign input max address, so we simplify our matching + * logic below and can just reuse it as return pointer. + */ + max_addr &= ~EFI_PAGE_MASK; + list_for_each(lhandle, &efi_mem) { struct efi_mem_list *lmem = list_entry(lhandle, struct efi_mem_list, link); @@ -378,7 +385,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type, /* Reserve that map in our memory maps */ ret = efi_add_memory_map(addr, pages, memory_type, true); if (ret == addr) { - *memory = (uintptr_t)map_sysmem(addr, len); + *memory = addr; } else { /* Map would overlap, bail out */ r = EFI_OUT_OF_RESOURCES; @@ -391,7 +398,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, @@ -412,12 +419,11 @@ void *efi_alloc(uint64_t len, int memory_type) efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) { uint64_t r = 0; - uint64_t addr = map_to_sysmem((void *)(uintptr_t)memory); - r = efi_add_memory_map(addr, pages, EFI_CONVENTIONAL_MEMORY, false); + r = efi_add_memory_map(memory, pages, EFI_CONVENTIONAL_MEMORY, false); /* Merging of adjacent free regions is missing */ - if (r == addr) + if (r == memory) return EFI_SUCCESS; return EFI_NOT_FOUND; @@ -435,8 +441,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; @@ -545,17 +551,51 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, __weak void efi_add_known_memory(void) { + u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK; int i; + /* Fix for 32bit targets with ram_top at 4G */ + if (!ram_top) + ram_top = 0x100000000ULL; + /* Add RAM */ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - u64 ram_start = gd->bd->bi_dram[i].start; - u64 ram_size = gd->bd->bi_dram[i].size; - u64 start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; - u64 pages = (ram_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; + u64 ram_end, ram_start, pages; + + ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0); + ram_end = ram_start + gd->bd->bi_dram[i].size; + + /* Remove partial pages */ + ram_end &= ~EFI_PAGE_MASK; + ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + + if (ram_end <= ram_start) { + /* Invalid mapping, keep going. */ + continue; + } + + pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(ram_start, pages, + EFI_CONVENTIONAL_MEMORY, false); - efi_add_memory_map(start, pages, EFI_CONVENTIONAL_MEMORY, - false); + /* + * Boards may indicate to the U-Boot memory core that they + * can not support memory above ram_top. Let's honor this + * in the efi_loader subsystem too by declaring any memory + * above ram_top as "already occupied by firmware". + */ + if (ram_top < ram_start) { + /* ram_top is before this region, reserve all */ + efi_add_memory_map(ram_start, pages, + EFI_BOOT_SERVICES_DATA, true); + } else if ((ram_top >= ram_start) && (ram_top < ram_end)) { + /* ram_top is inside this region, reserve parts */ + pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(ram_top, pages, + EFI_BOOT_SERVICES_DATA, true); + } } } @@ -563,6 +603,7 @@ __weak void efi_add_known_memory(void) static void add_u_boot_and_runtime(void) { unsigned long runtime_start, runtime_end, runtime_pages; + unsigned long runtime_mask = EFI_PAGE_MASK; unsigned long uboot_start, uboot_pages; unsigned long uboot_stack_size = 16 * 1024 * 1024; @@ -571,10 +612,22 @@ static void add_u_boot_and_runtime(void) uboot_pages = (gd->ram_top - uboot_start) >> EFI_PAGE_SHIFT; efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false); - /* Add Runtime Services */ - runtime_start = (ulong)&__efi_runtime_start & ~EFI_PAGE_MASK; +#if defined(__aarch64__) + /* + * Runtime Services must be 64KiB aligned according to the + * "AArch64 Platforms" section in the UEFI spec (2.7+). + */ + + runtime_mask = SZ_64K - 1; +#endif + + /* + * Add Runtime Services. We mark surrounding boottime code as runtime as + * well to fulfill the runtime alignment constraints but avoid padding. + */ + runtime_start = (ulong)&__efi_runtime_start & ~runtime_mask; runtime_end = (ulong)&__efi_runtime_stop; - runtime_end = (runtime_end + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + runtime_end = (runtime_end + runtime_mask) & ~runtime_mask; runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT; efi_add_memory_map(runtime_start, runtime_pages, EFI_RUNTIME_SERVICES_CODE, false); diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 4e8b2d597d..c7d9da8521 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -14,6 +14,8 @@ static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID; static struct efi_pxe_packet *dhcp_ack; static bool new_rx_packet; static void *new_tx_packet; +static void *transmit_buffer; + /* * The notification function of this event is called in every timer cycle * to check if a new network packet has been received. @@ -24,33 +26,85 @@ static struct efi_event *network_timer_event; */ static struct efi_event *wait_for_packet; +/** + * struct efi_net_obj - EFI object representing a network interface + * + * @header: EFI object header + * @net: simple network protocol interface + * @net_mode: status of the network interface + * @pxe: PXE base code protocol interface + * @pxe_mode: status of the PXE base code protocol + */ struct efi_net_obj { - /* Generic EFI object parent class data */ - struct efi_object parent; - /* EFI Interface callback struct for network */ + struct efi_object header; struct efi_simple_network net; struct efi_simple_network_mode net_mode; - /* PXE struct to transmit dhcp data */ struct efi_pxe pxe; struct efi_pxe_mode pxe_mode; }; +/* + * efi_net_start() - start the network interface + * + * This function implements the Start service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * Return: status code + */ static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p", this); - return EFI_EXIT(EFI_SUCCESS); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (this->mode->state != EFI_NETWORK_STOPPED) + ret = EFI_ALREADY_STARTED; + else + this->mode->state = EFI_NETWORK_STARTED; +out: + return EFI_EXIT(ret); } +/* + * efi_net_stop() - stop the network interface + * + * This function implements the Stop service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * Return: status code + */ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p", this); - return EFI_EXIT(EFI_SUCCESS); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (this->mode->state == EFI_NETWORK_STOPPED) + ret = EFI_NOT_STARTED; + else + this->mode->state = EFI_NETWORK_STOPPED; +out: + return EFI_EXIT(ret); } /* - * Initialize network adapter and allocate transmit and receive buffers. + * efi_net_initialize() - initialize the network interface * * This function implements the Initialize service of the * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface @@ -59,7 +113,7 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) * @this: pointer to the protocol instance * @extra_rx: extra receive buffer to be allocated * @extra_tx: extra transmit buffer to be allocated - * @return: status code + * Return: status code */ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, ulong extra_rx, ulong extra_tx) @@ -69,9 +123,10 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx); + /* Check parameters */ if (!this) { r = EFI_INVALID_PARAMETER; - goto error; + goto out; } /* Setup packet buffers */ @@ -84,32 +139,83 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, ret = eth_init(); if (ret < 0) { eth_halt(); + this->mode->state = EFI_NETWORK_STOPPED; r = EFI_DEVICE_ERROR; + goto out; + } else { + this->mode->state = EFI_NETWORK_INITIALIZED; } - -error: +out: return EFI_EXIT(r); } +/* + * efi_net_reset() - reinitialize the network interface + * + * This function implements the Reset service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @extended_verification: execute exhaustive verification + * Return: status code + */ static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this, int extended_verification) { EFI_ENTRY("%p, %x", this, extended_verification); - return EFI_EXIT(EFI_SUCCESS); + return EFI_EXIT(EFI_CALL(efi_net_initialize(this, 0, 0))); } +/* + * efi_net_shutdown() - shut down the network interface + * + * This function implements the Shutdown service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * Return: status code + */ static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p", this); - return EFI_EXIT(EFI_SUCCESS); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + eth_halt(); + this->mode->state = EFI_NETWORK_STOPPED; + +out: + return EFI_EXIT(ret); } -static efi_status_t EFIAPI efi_net_receive_filters( - struct efi_simple_network *this, u32 enable, u32 disable, - int reset_mcast_filter, ulong mcast_filter_count, - struct efi_mac_address *mcast_filter) +/* + * efi_net_receive_filters() - mange multicast receive filters + * + * This function implements the ReceiveFilters service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @enable: bit mask of receive filters to enable + * @disable: bit mask of receive filters to disable + * @reset_mcast_filter: true resets contents of the filters + * @mcast_filter_count: number of hardware MAC addresses in the new filters list + * @mcast_filter: list of new filters + * Return: status code + */ +static efi_status_t EFIAPI efi_net_receive_filters + (struct efi_simple_network *this, u32 enable, u32 disable, + int reset_mcast_filter, ulong mcast_filter_count, + struct efi_mac_address *mcast_filter) { EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable, reset_mcast_filter, mcast_filter_count, mcast_filter); @@ -117,15 +223,40 @@ static efi_status_t EFIAPI efi_net_receive_filters( return EFI_EXIT(EFI_UNSUPPORTED); } -static efi_status_t EFIAPI efi_net_station_address( - struct efi_simple_network *this, int reset, - struct efi_mac_address *new_mac) +/* + * efi_net_station_address() - set the hardware MAC address + * + * This function implements the StationAddress service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @reset: if true reset the address to default + * @new_mac: new MAC address + * Return: status code + */ +static efi_status_t EFIAPI efi_net_station_address + (struct efi_simple_network *this, int reset, + struct efi_mac_address *new_mac) { EFI_ENTRY("%p, %x, %p", this, reset, new_mac); return EFI_EXIT(EFI_UNSUPPORTED); } +/* + * efi_net_statistics() - reset or collect statistics of the network interface + * + * This function implements the Statistics service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @reset: if true, the statistics are reset + * @stat_size: size of the statistics table + * @stat_table: table to receive the statistics + * Return: status code + */ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, int reset, ulong *stat_size, void *stat_table) @@ -135,6 +266,19 @@ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, return EFI_EXIT(EFI_UNSUPPORTED); } +/* + * efi_net_mcastiptomac() - translate multicast IP address to MAC address + * + * This function implements the Statistics service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @ipv6: true if the IP address is an IPv6 address + * @ip: IP address + * @mac: MAC address + * Return: status code + */ static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, int ipv6, struct efi_ip_address *ip, @@ -145,6 +289,19 @@ static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, return EFI_EXIT(EFI_INVALID_PARAMETER); } +/** + * efi_net_nvdata() - read or write NVRAM + * + * This function implements the GetStatus service of the Simple Network + * Protocol. See the UEFI spec for details. + * + * @this: the instance of the Simple Network Protocol + * @readwrite: true for read, false for write + * @offset: offset in NVRAM + * @buffer_size: size of buffer + * @buffer: buffer + * Return: status code + */ static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this, int read_write, ulong offset, ulong buffer_size, char *buffer) @@ -155,13 +312,42 @@ static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this, return EFI_EXIT(EFI_UNSUPPORTED); } +/** + * efi_net_get_status() - get interrupt status + * + * This function implements the GetStatus service of the Simple Network + * Protocol. See the UEFI spec for details. + * + * @this: the instance of the Simple Network Protocol + * @int_status: interface status + * @txbuf: transmission buffer + */ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, u32 *int_status, void **txbuf) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p, %p, %p", this, int_status, txbuf); efi_timer_check(); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + switch (this->mode->state) { + case EFI_NETWORK_STOPPED: + ret = EFI_NOT_STARTED; + goto out; + case EFI_NETWORK_STARTED: + ret = EFI_DEVICE_ERROR; + goto out; + default: + break; + } + if (int_status) { /* We send packets synchronously, so nothing is outstanding */ *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; @@ -172,65 +358,103 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, *txbuf = new_tx_packet; new_tx_packet = NULL; - - return EFI_EXIT(EFI_SUCCESS); +out: + return EFI_EXIT(ret); } -static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this, - size_t header_size, size_t buffer_size, void *buffer, - struct efi_mac_address *src_addr, - struct efi_mac_address *dest_addr, u16 *protocol) +/** + * efi_net_transmit() - transmit a packet + * + * This function implements the Transmit service of the Simple Network Protocol. + * See the UEFI spec for details. + * + * @this: the instance of the Simple Network Protocol + * @header_size: size of the media header + * @buffer_size: size of the buffer to receive the packet + * @buffer: buffer to receive the packet + * @src_addr: source hardware MAC address + * @dest_addr: destination hardware MAC address + * @protocol: type of header to build + * Return: status code + */ +static efi_status_t EFIAPI efi_net_transmit + (struct efi_simple_network *this, size_t header_size, + size_t buffer_size, void *buffer, + struct efi_mac_address *src_addr, + struct efi_mac_address *dest_addr, u16 *protocol) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this, (unsigned long)header_size, (unsigned long)buffer_size, buffer, src_addr, dest_addr, protocol); efi_timer_check(); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We do not support jumbo packets */ + if (buffer_size > PKTSIZE_ALIGN) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + if (header_size) { - /* We would need to create the header if header_size != 0 */ - return EFI_EXIT(EFI_INVALID_PARAMETER); + /* + * TODO: We would need to create the header + * if header_size != 0 + */ + ret = EFI_INVALID_PARAMETER; + goto out; + } + + switch (this->mode->state) { + case EFI_NETWORK_STOPPED: + ret = EFI_NOT_STARTED; + goto out; + case EFI_NETWORK_STARTED: + ret = EFI_DEVICE_ERROR; + goto out; + default: + break; } -#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER /* Ethernet packets always fit, just bounce */ - memcpy(efi_bounce_buffer, buffer, buffer_size); - net_send_packet(efi_bounce_buffer, buffer_size); -#else - net_send_packet(buffer, buffer_size); -#endif + memcpy(transmit_buffer, buffer, buffer_size); + net_send_packet(transmit_buffer, buffer_size); new_tx_packet = buffer; - return EFI_EXIT(EFI_SUCCESS); -} - -static void efi_net_push(void *pkt, int len) -{ - new_rx_packet = true; - wait_for_packet->is_signaled = true; +out: + return EFI_EXIT(ret); } -/* - * Receive a packet from a network interface. +/** + * efi_net_receive() - receive a packet from a network interface * * This function implements the Receive service of the Simple Network Protocol. * See the UEFI spec for details. * - * @this the instance of the Simple Network Protocol - * @header_size size of the media header - * @buffer_size size of the buffer to receive the packet - * @buffer buffer to receive the packet - * @src_addr source MAC address - * @dest_addr destination MAC address - * @protocol protocol - * @return status code + * @this: the instance of the Simple Network Protocol + * @header_size: size of the media header + * @buffer_size: size of the buffer to receive the packet + * @buffer: buffer to receive the packet + * @src_addr: source MAC address + * @dest_addr: destination MAC address + * @protocol: protocol + * Return: status code */ -static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, - size_t *header_size, size_t *buffer_size, void *buffer, - struct efi_mac_address *src_addr, - struct efi_mac_address *dest_addr, u16 *protocol) +static efi_status_t EFIAPI efi_net_receive + (struct efi_simple_network *this, size_t *header_size, + size_t *buffer_size, void *buffer, + struct efi_mac_address *src_addr, + struct efi_mac_address *dest_addr, u16 *protocol) { + efi_status_t ret = EFI_SUCCESS; struct ethernet_hdr *eth_hdr; size_t hdr_size = sizeof(struct ethernet_hdr); u16 protlen; @@ -238,14 +462,35 @@ static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size, buffer_size, buffer, src_addr, dest_addr, protocol); + /* Execute events */ efi_timer_check(); - if (!new_rx_packet) - return EFI_EXIT(EFI_NOT_READY); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + switch (this->mode->state) { + case EFI_NETWORK_STOPPED: + ret = EFI_NOT_STARTED; + goto out; + case EFI_NETWORK_STARTED: + ret = EFI_DEVICE_ERROR; + goto out; + default: + break; + } + + if (!new_rx_packet) { + ret = EFI_NOT_READY; + goto out; + } /* Check that we at least received an Ethernet header */ if (net_rx_packet_len < sizeof(struct ethernet_hdr)) { new_rx_packet = false; - return EFI_EXIT(EFI_NOT_READY); + ret = EFI_NOT_READY; + goto out; } /* Fill export parameters */ eth_hdr = (struct ethernet_hdr *)net_rx_packet; @@ -263,18 +508,24 @@ static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, if (protocol) *protocol = protlen; if (*buffer_size < net_rx_packet_len) { - /* Packet doesn't fit, try again with bigger buf */ + /* Packet doesn't fit, try again with bigger buffer */ *buffer_size = net_rx_packet_len; - return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + ret = EFI_BUFFER_TOO_SMALL; + goto out; } /* Copy packet */ memcpy(buffer, net_rx_packet, net_rx_packet_len); *buffer_size = net_rx_packet_len; new_rx_packet = false; - - return EFI_EXIT(EFI_SUCCESS); +out: + return EFI_EXIT(ret); } +/** + * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address + * + * This function is called by dhcp_handler(). + */ void efi_net_set_dhcp_ack(void *pkt, int len) { int maxsize = sizeof(*dhcp_ack); @@ -285,8 +536,22 @@ void efi_net_set_dhcp_ack(void *pkt, int len) memcpy(dhcp_ack, pkt, min(len, maxsize)); } -/* - * Check if a new network packet has been received. +/** + * efi_net_push() - callback for received network packet + * + * This function is called when a network packet is received by eth_rx(). + * + * @pkt: network packet + * @len: length + */ +static void efi_net_push(void *pkt, int len) +{ + new_rx_packet = true; + wait_for_packet->is_signaled = true; +} + +/** + * efi_network_timer_notify() - check if a new network packet has been received * * This notification function is called in every timer cycle. * @@ -296,47 +561,65 @@ void efi_net_set_dhcp_ack(void *pkt, int len) static void EFIAPI efi_network_timer_notify(struct efi_event *event, void *context) { + struct efi_simple_network *this = (struct efi_simple_network *)context; + EFI_ENTRY("%p, %p", event, context); + /* + * Some network drivers do not support calling eth_rx() before + * initialization. + */ + if (!this || this->mode->state != EFI_NETWORK_INITIALIZED) + goto out; + if (!new_rx_packet) { push_packet = efi_net_push; eth_rx(); push_packet = NULL; } +out: EFI_EXIT(EFI_SUCCESS); } -/* This gets called from do_bootefi_exec(). */ +/** + * efi_net_register() - register the simple network protocol + * + * This gets called from do_bootefi_exec(). + */ efi_status_t efi_net_register(void) { - struct efi_net_obj *netobj; + struct efi_net_obj *netobj = NULL; efi_status_t r; if (!eth_get_dev()) { - /* No eth device active, don't expose any */ + /* No network device active, don't expose any */ return EFI_SUCCESS; } - /* We only expose the "active" eth device, so one is enough */ + /* We only expose the "active" network device, so one is enough */ netobj = calloc(1, sizeof(*netobj)); - if (!netobj) { - printf("ERROR: Out of memory\n"); - return EFI_OUT_OF_RESOURCES; - } + if (!netobj) + goto out_of_resources; + + /* Allocate an aligned transmit buffer */ + transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN); + if (!transmit_buffer) + goto out_of_resources; + transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN); /* Hook net up to the device list */ - efi_add_handle(&netobj->parent); + efi_add_handle(&netobj->header); /* Fill in object data */ - r = efi_add_protocol(netobj->parent.handle, &efi_net_guid, + r = efi_add_protocol(&netobj->header, &efi_net_guid, &netobj->net); if (r != EFI_SUCCESS) goto failure_to_add_protocol; - r = efi_add_protocol(netobj->parent.handle, &efi_guid_device_path, + r = efi_add_protocol(&netobj->header, &efi_guid_device_path, efi_dp_from_eth()); if (r != EFI_SUCCESS) goto failure_to_add_protocol; - r = efi_add_protocol(netobj->parent.handle, &efi_pxe_guid, + r = efi_add_protocol(&netobj->header, &efi_pxe_guid, &netobj->pxe); if (r != EFI_SUCCESS) goto failure_to_add_protocol; @@ -385,13 +668,13 @@ efi_status_t efi_net_register(void) * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL. */ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, - efi_network_timer_notify, NULL, NULL, + efi_network_timer_notify, &netobj->net, NULL, &network_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); return r; } - /* Network is time critical, create event in every timer cyle */ + /* Network is time critical, create event in every timer cycle */ r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0); if (r != EFI_SUCCESS) { printf("ERROR: Failed to set network timer\n"); @@ -402,4 +685,9 @@ efi_status_t efi_net_register(void) failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); return r; +out_of_resources: + free(netobj); + /* free(transmit_buffer) not needed yet */ + printf("ERROR: Out of memory\n"); + return EFI_OUT_OF_RESOURCES; } diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index f059dc97fd..95844efdb0 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -141,7 +141,9 @@ static void EFIAPI efi_reset_system_boottime( do_reset(NULL, 0, 0, NULL); break; case EFI_RESET_SHUTDOWN: - /* We don't have anything to map this to */ +#ifdef CONFIG_CMD_POWEROFF + do_poweroff(NULL, 0, 0, NULL); +#endif break; } @@ -282,7 +284,7 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { }, { /* invalidate_*cache_all are gone */ .ptr = &efi_runtime_services.set_virtual_address_map, - .patchto = &efi_invalid_parameter, + .patchto = &efi_unimplemented, }, { /* RTC accessors are gone */ .ptr = &efi_runtime_services.get_time, @@ -378,6 +380,9 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) ulong symidx = rel->info >> SYM_INDEX; extern struct dyn_sym __dyn_sym_start[]; newaddr = __dyn_sym_start[symidx].addr + offset; +#ifdef IS_RELA + newaddr -= CONFIG_SYS_TEXT_BASE; +#endif break; } #endif @@ -623,8 +628,8 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule( efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( struct efi_capsule_header **capsule_header_array, efi_uintn_t capsule_count, - u64 maximum_capsule_size, - u32 reset_type) + u64 *maximum_capsule_size, + u32 *reset_type) { return EFI_UNSUPPORTED; } diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index 38e42fa243..a81488495e 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -7,6 +7,7 @@ #include <common.h> #include <efi_loader.h> +#include <mapmem.h> #include <smbios.h> static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; @@ -19,17 +20,19 @@ static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; efi_status_t efi_smbios_register(void) { /* Map within the low 32 bits, to allow for 32bit SMBIOS tables */ - u64 dmi = U32_MAX; + u64 dmi_addr = U32_MAX; efi_status_t ret; + void *dmi; /* Reserve 4kiB page for SMBIOS */ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_RUNTIME_SERVICES_DATA, 1, &dmi); + EFI_RUNTIME_SERVICES_DATA, 1, &dmi_addr); if (ret != EFI_SUCCESS) { /* Could not find space in lowmem, use highmem instead */ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, - EFI_RUNTIME_SERVICES_DATA, 1, &dmi); + EFI_RUNTIME_SERVICES_DATA, 1, + &dmi_addr); if (ret != EFI_SUCCESS) return ret; @@ -39,11 +42,14 @@ efi_status_t efi_smbios_register(void) * Generate SMBIOS tables - we know that efi_allocate_pages() returns * a 4k-aligned address, so it is safe to assume that * write_smbios_table() will write the table at that address. + * + * Note that on sandbox, efi_allocate_pages() unfortunately returns a + * pointer even though it uses a uint64_t type. Convert it. */ - assert(!(dmi & 0xf)); - write_smbios_table(dmi); + assert(!(dmi_addr & 0xf)); + dmi = (void *)(uintptr_t)dmi_addr; + write_smbios_table(map_to_sysmem(dmi)); /* And expose them to our EFI payload */ - return efi_install_configuration_table(&smbios_guid, - (void *)(uintptr_t)dmi); + return efi_install_configuration_table(&smbios_guid, dmi); } diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c index 3b8de5b4ea..2905479e65 100644 --- a/lib/efi_loader/helloworld.c +++ b/lib/efi_loader/helloworld.c @@ -17,6 +17,16 @@ static const efi_guid_t fdt_guid = EFI_FDT_GUID; static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; +/** + * hw_memcmp() - compare memory areas + * + * @buf1: pointer to first area + * @buf2: pointer to second area + * @length: number of bytes to compare + * Return: 0 if both memory areas are the same, otherwise the sign of the + * result value is the same as the sign of ghe difference between + * the first differing pair of bytes taken as u8. + */ static int hw_memcmp(const void *buf1, const void *buf2, size_t length) { const u8 *pos1 = buf1; @@ -31,12 +41,12 @@ static int hw_memcmp(const void *buf1, const void *buf2, size_t length) return 0; } -/* - * Entry point of the EFI application. +/** + * efi_main() - entry point of the EFI application. * - * @handle handle of the loaded image - * @systable system table - * @return status code + * @handle: handle of the loaded image + * @systable: system table + * @return: status code */ efi_status_t EFIAPI efi_main(efi_handle_t handle, struct efi_system_table *systable) @@ -48,7 +58,8 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, efi_uintn_t i; u16 rev[] = L"0.0.0"; - con_out->output_string(con_out, L"Hello, world!\n"); + /* UEFI requires CR LF */ + con_out->output_string(con_out, L"Hello, world!\r\n"); /* Print the revision number */ rev[0] = (systable->hdr.revision >> 16) + '0'; @@ -65,27 +76,30 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, con_out->output_string(con_out, L"Running on UEFI "); con_out->output_string(con_out, rev); - con_out->output_string(con_out, L"\n"); + con_out->output_string(con_out, L"\r\n"); /* Get the loaded image protocol */ ret = boottime->handle_protocol(handle, &loaded_image_guid, (void **)&loaded_image); if (ret != EFI_SUCCESS) { - con_out->output_string(con_out, - L"Cannot open loaded image protocol\n"); + con_out->output_string + (con_out, L"Cannot open loaded image protocol\r\n"); goto out; } /* Find configuration tables */ for (i = 0; i < systable->nr_tables; ++i) { if (!hw_memcmp(&systable->tables[i].guid, &fdt_guid, sizeof(efi_guid_t))) - con_out->output_string(con_out, L"Have device tree\n"); + con_out->output_string + (con_out, L"Have device tree\r\n"); if (!hw_memcmp(&systable->tables[i].guid, &acpi_guid, sizeof(efi_guid_t))) - con_out->output_string(con_out, L"Have ACPI 2.0 table\n"); + con_out->output_string + (con_out, L"Have ACPI 2.0 table\r\n"); if (!hw_memcmp(&systable->tables[i].guid, &smbios_guid, sizeof(efi_guid_t))) - con_out->output_string(con_out, L"Have SMBIOS table\n"); + con_out->output_string + (con_out, L"Have SMBIOS table\r\n"); } /* Output the load options */ con_out->output_string(con_out, L"Load options: "); @@ -94,7 +108,7 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, (u16 *)loaded_image->load_options); else con_out->output_string(con_out, L"<none>"); - con_out->output_string(con_out, L"\n"); + con_out->output_string(con_out, L"\r\n"); out: boottime->exit(handle, ret, 0, NULL); diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig index b52696778d..59f9f36801 100644 --- a/lib/efi_selftest/Kconfig +++ b/lib/efi_selftest/Kconfig @@ -1,6 +1,6 @@ config CMD_BOOTEFI_SELFTEST bool "Allow booting an EFI efi_selftest" - depends on CMD_BOOTEFI && !SANDBOX + depends on CMD_BOOTEFI imply FAT imply FAT_WRITE help diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 2f55d9d66f..743b482044 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -10,7 +10,7 @@ CFLAGS_REMOVE_efi_selftest_miniapp_exit.o := $(CFLAGS_NON_EFI) -Os CFLAGS_efi_selftest_miniapp_return.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI) -Os -obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \ +obj-y += \ efi_selftest.o \ efi_selftest_bitblt.o \ efi_selftest_config_table.o \ @@ -21,11 +21,13 @@ efi_selftest_devicepath.o \ efi_selftest_devicepath_util.o \ efi_selftest_events.o \ efi_selftest_event_groups.o \ +efi_selftest_exception.o \ efi_selftest_exitbootservices.o \ efi_selftest_fdt.o \ efi_selftest_gop.o \ efi_selftest_loaded_image.o \ efi_selftest_manageprotocols.o \ +efi_selftest_memory.o \ efi_selftest_rtc.o \ efi_selftest_snp.o \ efi_selftest_textinput.o \ @@ -37,20 +39,16 @@ efi_selftest_util.o \ efi_selftest_variables.o \ efi_selftest_watchdog.o -ifeq ($(CONFIG_CMD_BOOTEFI_SELFTEST),y) obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o -endif ifeq ($(CONFIG_BLK)$(CONFIG_PARTITIONS),yy) -obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest_block_device.o +obj-y += efi_selftest_block_device.o endif # TODO: As of v2018.01 the relocation code for the EFI application cannot # be built on x86_64. ifeq ($(CONFIG_X86_64)$(CONFIG_SANDBOX),) -ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST),) - obj-y += \ efi_selftest_startimage_exit.o \ efi_selftest_startimage_return.o @@ -74,5 +72,3 @@ $(obj)/efi_selftest_startimage_exit.o: $(obj)/efi_miniapp_file_image_exit.h $(obj)/efi_selftest_startimage_return.o: $(obj)/efi_miniapp_file_image_return.h endif - -endif diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c index dd338db687..5b01610eca 100644 --- a/lib/efi_selftest/efi_selftest.c +++ b/lib/efi_selftest/efi_selftest.c @@ -18,6 +18,7 @@ static const struct efi_boot_services *boottime; static const struct efi_runtime_services *runtime; static efi_handle_t handle; static u16 reset_message[] = L"Selftest completed"; +static int *setup_status; /* * Exit the boot services. @@ -74,20 +75,20 @@ void efi_st_exit_boot_services(void) */ static int setup(struct efi_unit_test *test, unsigned int *failures) { - if (!test->setup) { - test->setup_ok = EFI_ST_SUCCESS; + int ret; + + if (!test->setup) return EFI_ST_SUCCESS; - } efi_st_printc(EFI_LIGHTBLUE, "\nSetting up '%s'\n", test->name); - test->setup_ok = test->setup(handle, systable); - if (test->setup_ok != EFI_ST_SUCCESS) { + ret = test->setup(handle, systable); + if (ret != EFI_ST_SUCCESS) { efi_st_error("Setting up '%s' failed\n", test->name); ++*failures; } else { efi_st_printc(EFI_LIGHTGREEN, "Setting up '%s' succeeded\n", test->name); } - return test->setup_ok; + return ret; } /* @@ -186,18 +187,20 @@ static void list_all_tests(void) void efi_st_do_tests(const u16 *testname, unsigned int phase, unsigned int steps, unsigned int *failures) { + int i = 0; struct efi_unit_test *test; for (test = ll_entry_start(struct efi_unit_test, efi_unit_test); - test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) { + test < ll_entry_end(struct efi_unit_test, efi_unit_test); + ++test, ++i) { if (testname ? efi_st_strcmp_16_8(testname, test->name) : test->on_request) continue; if (test->phase != phase) continue; if (steps & EFI_ST_SETUP) - setup(test, failures); - if (steps & EFI_ST_EXECUTE && test->setup_ok == EFI_ST_SUCCESS) + setup_status[i] = setup(test, failures); + if (steps & EFI_ST_EXECUTE && setup_status[i] == EFI_ST_SUCCESS) execute(test, failures); if (steps & EFI_ST_TEARDOWN) teardown(test, failures); @@ -271,6 +274,16 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle, ll_entry_count(struct efi_unit_test, efi_unit_test)); + /* Allocate buffer for setup results */ + ret = boottime->allocate_pool(EFI_RUNTIME_SERVICES_DATA, sizeof(int) * + ll_entry_count(struct efi_unit_test, + efi_unit_test), + (void **)&setup_status); + if (ret != EFI_SUCCESS) { + efi_st_error("Allocate pool failed\n"); + return ret; + } + /* Execute boottime tests */ efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN, diff --git a/lib/efi_selftest/efi_selftest_config_table.c b/lib/efi_selftest/efi_selftest_config_table.c index 2aa3fc7284..0bc5da6b0c 100644 --- a/lib/efi_selftest/efi_selftest_config_table.c +++ b/lib/efi_selftest/efi_selftest_config_table.c @@ -18,7 +18,7 @@ static efi_guid_t table_guid = 0x17, 0x2e, 0x51, 0x6b, 0x49, 0x75); /* - * Notification function, increments the notfication count if parameter + * Notification function, increments the notification count if parameter * context is provided. * * @event notified event @@ -33,23 +33,23 @@ static void EFIAPI notify(struct efi_event *event, void *context) } /* - * Check crc32 of a table. + * Check CRC32 of a table. */ static int check_table(const void *table) { efi_status_t ret; u32 crc32, res; - /* Casting from const to not const */ + /* Casting from constant to not constant */ struct efi_table_hdr *hdr = (struct efi_table_hdr *)table; crc32 = hdr->crc32; /* - * Setting the crc32 of the 'const' table to zero is easier than + * Setting the CRC32 of the 'const' table to zero is easier than * copying */ hdr->crc32 = 0; ret = boottime->calculate_crc32(table, hdr->headersize, &res); - /* Reset table crc32 so it stays constant */ + /* Reset table CRC32 so it stays constant */ hdr->crc32 = crc32; if (ret != EFI_ST_SUCCESS) { efi_st_error("CalculateCrc32 failed\n"); @@ -203,7 +203,7 @@ static int execute(void) return EFI_ST_FAILURE; } if (tabcnt > 1) { - efi_st_error("Duplicate table guid\n"); + efi_st_error("Duplicate table GUID\n"); return EFI_ST_FAILURE; } if (table != &tables[1]) { diff --git a/lib/efi_selftest/efi_selftest_controllers.c b/lib/efi_selftest/efi_selftest_controllers.c index d08c377c72..38720bb63d 100644 --- a/lib/efi_selftest/efi_selftest_controllers.c +++ b/lib/efi_selftest/efi_selftest_controllers.c @@ -33,7 +33,7 @@ static efi_handle_t handle_driver; * Count child controllers * * @handle handle on which child controllers are installed - * @protocol protocol for which the child controlles where installed + * @protocol protocol for which the child controllers were installed * @count number of child controllers * @return status code */ diff --git a/lib/efi_selftest/efi_selftest_crc32.c b/lib/efi_selftest/efi_selftest_crc32.c index 8555b8f114..4881e8ac6f 100644 --- a/lib/efi_selftest/efi_selftest_crc32.c +++ b/lib/efi_selftest/efi_selftest_crc32.c @@ -5,7 +5,7 @@ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> * * This unit test checks the CalculateCrc32 bootservice and checks the - * headers of the system table, the boot services tablle, and the runtime + * headers of the system table, the boot services table, and the runtime * services table before and after ExitBootServices(). */ @@ -19,7 +19,7 @@ static int check_table(const void *table) { efi_status_t ret; u32 crc32, res; - /* Casting from const to not const */ + /* Casting from constant to not constant */ struct efi_table_hdr *hdr = (struct efi_table_hdr *)table; if (!hdr->signature) { diff --git a/lib/efi_selftest/efi_selftest_devicepath.c b/lib/efi_selftest/efi_selftest_devicepath.c index adcf531e90..105ce2c92b 100644 --- a/lib/efi_selftest/efi_selftest_devicepath.c +++ b/lib/efi_selftest/efi_selftest_devicepath.c @@ -257,7 +257,7 @@ static int teardown(void) static int execute(void) { struct efi_device_path *remaining_dp; - void *handle; + efi_handle_t handle; /* * This device path node ends with the letter 't' of 'u-boot'. * The following '.bin' does not belong to the node but is diff --git a/lib/efi_selftest/efi_selftest_event_groups.c b/lib/efi_selftest/efi_selftest_event_groups.c index 9b3c5132ef..5a7980c5d0 100644 --- a/lib/efi_selftest/efi_selftest_event_groups.c +++ b/lib/efi_selftest/efi_selftest_event_groups.c @@ -19,7 +19,7 @@ static efi_guid_t event_group = 0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91); /* - * Notification function, increments the notfication count if parameter + * Notification function, increments the notification count if parameter * context is provided. * * @event notified event @@ -114,7 +114,7 @@ static int execute(void) (unsigned int)i, (unsigned int)j, (unsigned int)counter[j]); efi_st_error( - "Nofification function not called\n"); + "Notification function not called\n"); return EFI_ST_FAILURE; } } diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c index 47f9f99318..ed99a53804 100644 --- a/lib/efi_selftest/efi_selftest_events.c +++ b/lib/efi_selftest/efi_selftest_events.c @@ -17,7 +17,7 @@ static unsigned int timer_ticks; static struct efi_boot_services *boottime; /* - * Notification function, increments the notfication count if parameter + * Notification function, increments the notification count if parameter * context is provided. * * @event notified event diff --git a/lib/efi_selftest/efi_selftest_exception.c b/lib/efi_selftest/efi_selftest_exception.c new file mode 100644 index 0000000000..76cfb88d7c --- /dev/null +++ b/lib/efi_selftest/efi_selftest_exception.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_exception + * + * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Test the handling of exceptions by trying to execute an undefined + * instruction. + */ + +#include <efi_selftest.h> + +/** + * undefined_instruction() - try to executed an undefined instruction + */ +static void undefined_instruction(void) +{ +#if defined(CONFIG_ARM) + /* + * 0xe7f...f. is undefined in ARM mode + * 0xde.. is undefined in Thumb mode + */ + asm volatile (".word 0xe7f7defb\n"); +#elif defined(CONFIG_RISCV) + asm volatile (".word 0xffffffff\n"); +#elif defined(CONFIG_X86) + asm volatile (".word 0xffff\n"); +#endif +} + +/** + * execute() - execute unit test + * + * Return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + undefined_instruction(); + + efi_st_error("An undefined instruction exception was not raised\n"); + + return EFI_ST_FAILURE; +} + +EFI_UNIT_TEST(exception) = { + .name = "exception", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .execute = execute, + .on_request = true, +}; diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c index c7bc242b5c..d545d51812 100644 --- a/lib/efi_selftest/efi_selftest_fdt.c +++ b/lib/efi_selftest/efi_selftest_fdt.c @@ -16,7 +16,7 @@ static struct efi_boot_services *boottime; static const char *fdt; -/* This should be sufficent for */ +/* This should be sufficient for */ #define BUFFERSIZE 0x100000 static efi_guid_t fdt_guid = EFI_FDT_GUID; diff --git a/lib/efi_selftest/efi_selftest_loaded_image.c b/lib/efi_selftest/efi_selftest_loaded_image.c index f9b54ae263..ea2b380a77 100644 --- a/lib/efi_selftest/efi_selftest_loaded_image.c +++ b/lib/efi_selftest/efi_selftest_loaded_image.c @@ -53,7 +53,7 @@ static int execute(void) efi_st_error("ProtocolsPerHandle failed\n"); return EFI_ST_FAILURE; } - if (!protocol_buffer_count | !protocol_buffer) { + if (!protocol_buffer_count || !protocol_buffer) { efi_st_error("ProtocolsPerHandle returned no protocol\n"); return EFI_ST_FAILURE; } diff --git a/lib/efi_selftest/efi_selftest_manageprotocols.c b/lib/efi_selftest/efi_selftest_manageprotocols.c index b09e4cdcfa..0ff35cec8a 100644 --- a/lib/efi_selftest/efi_selftest_manageprotocols.c +++ b/lib/efi_selftest/efi_selftest_manageprotocols.c @@ -189,7 +189,14 @@ static int execute(void) /* * Test error handling in UninstallMultipleProtocols * - * Try to uninstall more protocols than there are installed. + * These are the installed protocol interfaces on handle 2: + * + * guid1 interface4 + * guid2 interface2 + * + * Try to uninstall more protocols than there are installed. This + * should return an error EFI_INVALID_PARAMETER. All deleted protocols + * should be reinstalled. */ ret = boottime->uninstall_multiple_protocol_interfaces( handle2, @@ -197,13 +204,18 @@ static int execute(void) &guid2, &interface2, &guid3, &interface3, NULL); - if (ret == EFI_SUCCESS) { + if (ret != EFI_INVALID_PARAMETER) { + printf("%lx", ret); efi_st_error("UninstallMultipleProtocolInterfaces did not catch error\n"); return EFI_ST_FAILURE; } /* * Test LocateHandleBuffer with ByProtocol + * + * These are the handles with a guid1 protocol interface installed: + * + * handle1, handle2 */ count = buffer_size; ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL, @@ -213,7 +225,7 @@ static int execute(void) return EFI_ST_FAILURE; } if (count != 2) { - efi_st_error("LocateHandleBuffer failed to locate new handles\n"); + efi_st_error("UninstallMultipleProtocolInterfaces deleted handle\n"); return EFI_ST_FAILURE; } ret = find_in_buffer(handle1, count, buffer); diff --git a/lib/efi_selftest/efi_selftest_memory.c b/lib/efi_selftest/efi_selftest_memory.c new file mode 100644 index 0000000000..24b4438ce4 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_memory.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_memory + * + * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * 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 + * + * @handle: handle of the loaded image + * @systable: system table + * Return: EFI_ST_SUCCESS for success + */ +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; +} + +/** + * find_in_memory_map() - check matching memory map entry exists + * + * @memory_map: memory map + * @desc_size: number of memory map entries + * @addr: physical address to find in the map + * @type: expected memory type + * Return: EFI_ST_SUCCESS for success + */ +static int find_in_memory_map(efi_uintn_t map_size, + struct efi_mem_desc *memory_map, + efi_uintn_t desc_size, + u64 addr, int memory_type) +{ + efi_uintn_t i; + bool found = false; + + for (i = 0; map_size; ++i, map_size -= desc_size) { + struct efi_mem_desc *entry = &memory_map[i]; + + if (addr >= entry->physical_start && + addr < entry->physical_start + + (entry->num_pages << EFI_PAGE_SHIFT)) { + if (found) { + efi_st_error("Duplicate memory map entry\n"); + return EFI_ST_FAILURE; + } + found = true; + if (memory_type != entry->type) { + efi_st_error + ("Wrong memory type %d, expected %d\n", + entry->type, memory_type); + return EFI_ST_FAILURE; + } + } + } + if (!found) { + efi_st_error("Missing memory map entry\n"); + return EFI_ST_FAILURE; + } + return EFI_ST_SUCCESS; +} + +/* + * execute() - execute unit test + * + * Return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + u64 p1; + u64 p2; + efi_uintn_t map_size = 0; + efi_uintn_t map_key; + efi_uintn_t desc_size; + u32 desc_version; + struct efi_mem_desc *memory_map; + efi_status_t ret; + + /* Allocate two page ranges with different memory type */ + ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_CODE, + EFI_ST_NUM_PAGES, &p1); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePages did not return EFI_SUCCESS\n"); + return EFI_ST_FAILURE; + } + ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + EFI_ST_NUM_PAGES, &p2); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePages did not return EFI_SUCCESS\n"); + return EFI_ST_FAILURE; + } + + /* Load memory map */ + ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size, + &desc_version); + if (ret != EFI_BUFFER_TOO_SMALL) { + efi_st_error + ("GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n"); + return EFI_ST_FAILURE; + } + /* Allocate extra space for newly allocated memory */ + map_size += sizeof(struct efi_mem_desc); + ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size, + (void **)&memory_map); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePool did not return EFI_SUCCESS\n"); + return EFI_ST_FAILURE; + } + ret = boottime->get_memory_map(&map_size, memory_map, &map_key, + &desc_size, &desc_version); + if (ret != EFI_SUCCESS) { + efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n"); + return EFI_ST_FAILURE; + } + + /* Check memory map entries */ + if (find_in_memory_map(map_size, memory_map, desc_size, p1, + EFI_RUNTIME_SERVICES_CODE) != EFI_ST_SUCCESS) + return EFI_ST_FAILURE; + if (find_in_memory_map(map_size, memory_map, desc_size, p2, + EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS) + return EFI_ST_FAILURE; + + /* Free memory */ + ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePages did not return EFI_SUCCESS\n"); + return EFI_ST_FAILURE; + } + ret = boottime->free_pages(p2, EFI_ST_NUM_PAGES); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePages did not return EFI_SUCCESS\n"); + return EFI_ST_FAILURE; + } + ret = boottime->free_pool(memory_map); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePool did not return EFI_SUCCESS\n"); + 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; +} + +EFI_UNIT_TEST(memory) = { + .name = "memory", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +}; diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c index 09bd53da82..e10a34ba64 100644 --- a/lib/efi_selftest/efi_selftest_snp.c +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -103,7 +103,7 @@ static efi_status_t send_dhcp_discover(void) struct dhcp p = {}; /* - * Fill ethernet header + * Fill Ethernet header */ boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN); boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address, @@ -229,19 +229,19 @@ static int setup(const efi_handle_t handle, return EFI_ST_FAILURE; } /* - * Initialize network adapter. + * Start network adapter. */ - ret = net->initialize(net, 0, 0); - if (ret != EFI_SUCCESS) { - efi_st_error("Failed to initialize network adapter\n"); + ret = net->start(net); + if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) { + efi_st_error("Failed to start network adapter\n"); return EFI_ST_FAILURE; } /* - * Start network adapter. + * Initialize network adapter. */ - ret = net->start(net); + ret = net->initialize(net, 0, 0); if (ret != EFI_SUCCESS) { - efi_st_error("Failed to start network adapter\n"); + efi_st_error("Failed to initialize network adapter\n"); return EFI_ST_FAILURE; } return EFI_ST_SUCCESS; diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c index 164fbffe6c..b90671cdd2 100644 --- a/lib/efi_selftest/efi_selftest_textinput.c +++ b/lib/efi_selftest/efi_selftest_textinput.c @@ -5,7 +5,7 @@ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> * * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_PROTOCOL. - * The unicode character and the scan code are printed for text + * The Unicode character and the scan code are printed for text * input. To run the test: * * setenv efi_selftest text input diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c index 61ae46b797..97d256abe4 100644 --- a/lib/efi_selftest/efi_selftest_tpl.c +++ b/lib/efi_selftest/efi_selftest_tpl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * efi_selftest_events + * efi_selftest_tpl * * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> * diff --git a/lib/efi_selftest/efi_selftest_unicode_collation.c b/lib/efi_selftest/efi_selftest_unicode_collation.c index 9765bd3e44..75294307d9 100644 --- a/lib/efi_selftest/efi_selftest_unicode_collation.c +++ b/lib/efi_selftest/efi_selftest_unicode_collation.c @@ -52,7 +52,7 @@ static int test_stri_coll(void) c1, c2); if (ret) { efi_st_error( - "stri_coll(\"%ps\", \"%ps\") = %zu\n", c1, c2, ret); + "stri_coll(\"%ps\", \"%ps\") = %d\n", c1, c2, (int)ret); return EFI_ST_FAILURE; } @@ -60,7 +60,7 @@ static int test_stri_coll(void) c1, c3); if (ret >= 0) { efi_st_error( - "stri_coll(\"%ps\", \"%ps\") = %zu\n", c1, c3, ret); + "stri_coll(\"%ps\", \"%ps\") = %d\n", c1, c3, (int)ret); return EFI_ST_FAILURE; } @@ -68,7 +68,7 @@ static int test_stri_coll(void) c3, c1); if (ret <= 0) { efi_st_error( - "stri_coll(\"%ps\", \"%ps\") = %zu\n", c3, c1, ret); + "stri_coll(\"%ps\", \"%ps\") = %d\n", c3, c1, (int)ret); return EFI_ST_FAILURE; } diff --git a/lib/efi_selftest/efi_selftest_variables.c b/lib/efi_selftest/efi_selftest_variables.c index 146378fb9a..e4c389a872 100644 --- a/lib/efi_selftest/efi_selftest_variables.c +++ b/lib/efi_selftest/efi_selftest_variables.c @@ -4,10 +4,8 @@ * * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> * - * This unit test checks the following protocol services: - * ConnectController, DisconnectController, - * InstallProtocol, ReinstallProtocol, UninstallProtocol, - * OpenProtocol, CloseProtcol, OpenProtocolInformation + * This unit test checks the runtime services for variables: + * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo. */ #include <efi_selftest.h> diff --git a/lib/efi_selftest/efi_selftest_watchdog.c b/lib/efi_selftest/efi_selftest_watchdog.c index bff2330918..cbc6761721 100644 --- a/lib/efi_selftest/efi_selftest_watchdog.c +++ b/lib/efi_selftest/efi_selftest_watchdog.c @@ -35,7 +35,7 @@ static struct notify_context notification_context; static bool watchdog_reset; /* - * Notification function, increments the notfication count if parameter + * Notification function, increments the notification count if parameter * context is provided. * * @event notified event diff --git a/lib/fdtdec.c b/lib/fdtdec.c index d28f2cbb1c..cbdc077825 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -11,6 +11,7 @@ #include <errno.h> #include <fdtdec.h> #include <fdt_support.h> +#include <mapmem.h> #include <linux/libfdt.h> #include <serial.h> #include <asm/sections.h> @@ -1253,8 +1254,9 @@ int fdtdec_setup(void) # if CONFIG_IS_ENABLED(OF_PRIOR_STAGE) gd->fdt_blob = (void *)prior_stage_fdt_address; # else - gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16, - (uintptr_t)gd->fdt_blob); + gd->fdt_blob = map_sysmem + (env_get_ulong("fdtcontroladdr", 16, + (unsigned long)map_to_sysmem(gd->fdt_blob)), 0); # endif # endif diff --git a/lib/smbios.c b/lib/smbios.c index 326eb00230..e8ee55c4ae 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -6,6 +6,7 @@ */ #include <common.h> +#include <mapmem.h> #include <smbios.h> #include <tables_csum.h> #include <version.h> @@ -72,9 +73,10 @@ static int smbios_string_table_len(char *start) static int smbios_write_type0(ulong *current, int handle) { - struct smbios_type0 *t = (struct smbios_type0 *)*current; + struct smbios_type0 *t; int len = sizeof(struct smbios_type0); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type0)); fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle); t->vendor = smbios_add_string(t->eos, "U-Boot"); @@ -101,16 +103,18 @@ static int smbios_write_type0(ulong *current, int handle) len = t->length + smbios_string_table_len(t->eos); *current += len; + unmap_sysmem(t); return len; } static int smbios_write_type1(ulong *current, int handle) { - struct smbios_type1 *t = (struct smbios_type1 *)*current; + struct smbios_type1 *t; int len = sizeof(struct smbios_type1); char *serial_str = env_get("serial#"); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type1)); fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle); t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER); @@ -122,15 +126,17 @@ static int smbios_write_type1(ulong *current, int handle) len = t->length + smbios_string_table_len(t->eos); *current += len; + unmap_sysmem(t); return len; } static int smbios_write_type2(ulong *current, int handle) { - struct smbios_type2 *t = (struct smbios_type2 *)*current; + struct smbios_type2 *t; int len = sizeof(struct smbios_type2); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type2)); fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle); t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER); @@ -140,15 +146,17 @@ static int smbios_write_type2(ulong *current, int handle) len = t->length + smbios_string_table_len(t->eos); *current += len; + unmap_sysmem(t); return len; } static int smbios_write_type3(ulong *current, int handle) { - struct smbios_type3 *t = (struct smbios_type3 *)*current; + struct smbios_type3 *t; int len = sizeof(struct smbios_type3); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type3)); fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle); t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER); @@ -160,6 +168,7 @@ static int smbios_write_type3(ulong *current, int handle) len = t->length + smbios_string_table_len(t->eos); *current += len; + unmap_sysmem(t); return len; } @@ -198,9 +207,10 @@ static void smbios_write_type4_dm(struct smbios_type4 *t) static int smbios_write_type4(ulong *current, int handle) { - struct smbios_type4 *t = (struct smbios_type4 *)*current; + struct smbios_type4 *t; int len = sizeof(struct smbios_type4); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type4)); fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle); t->processor_type = SMBIOS_PROCESSOR_TYPE_CENTRAL; @@ -214,32 +224,37 @@ static int smbios_write_type4(ulong *current, int handle) len = t->length + smbios_string_table_len(t->eos); *current += len; + unmap_sysmem(t); return len; } static int smbios_write_type32(ulong *current, int handle) { - struct smbios_type32 *t = (struct smbios_type32 *)*current; + struct smbios_type32 *t; int len = sizeof(struct smbios_type32); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type32)); fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle); *current += len; + unmap_sysmem(t); return len; } static int smbios_write_type127(ulong *current, int handle) { - struct smbios_type127 *t = (struct smbios_type127 *)*current; + struct smbios_type127 *t; int len = sizeof(struct smbios_type127); + t = map_sysmem(*current, len); memset(t, 0, sizeof(struct smbios_type127)); fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle); *current += len; + unmap_sysmem(t); return len; } @@ -257,6 +272,7 @@ static smbios_write_type smbios_write_funcs[] = { ulong write_smbios_table(ulong addr) { struct smbios_entry *se; + ulong table_addr; ulong tables; int len = 0; int max_struct_size = 0; @@ -268,7 +284,7 @@ ulong write_smbios_table(ulong addr) /* 16 byte align the table address */ addr = ALIGN(addr, 16); - se = (struct smbios_entry *)(uintptr_t)addr; + se = map_sysmem(addr, sizeof(struct smbios_entry)); memset(se, 0, sizeof(struct smbios_entry)); addr += sizeof(struct smbios_entry); @@ -290,7 +306,24 @@ ulong write_smbios_table(ulong addr) se->max_struct_size = max_struct_size; memcpy(se->intermediate_anchor, "_DMI_", 5); se->struct_table_length = len; - se->struct_table_address = tables; + + /* + * We must use a pointer here so things work correctly on sandbox. The + * user of this table is not aware of the mapping of addresses to + * sandbox's DRAM buffer. + */ + table_addr = (ulong)map_sysmem(tables, 0); + if (sizeof(table_addr) > sizeof(u32) && table_addr > (ulong)UINT_MAX) { + /* + * We need to put this >32-bit pointer into the table but the + * field is only 32 bits wide. + */ + printf("WARNING: SMBIOS table_address overflow %llx\n", + (unsigned long long)table_addr); + table_addr = 0; + } + se->struct_table_address = table_addr; + se->struct_count = handle; /* calculate checksums */ @@ -298,6 +331,7 @@ ulong write_smbios_table(ulong addr) isize = sizeof(struct smbios_entry) - SMBIOS_INTERMEDIATE_OFFSET; se->intermediate_checksum = table_compute_checksum(istart, isize); se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry)); + unmap_sysmem(se); return addr; } diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 4dceb6d1b3..a4f16bb4bb 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -381,7 +381,7 @@ $(obj)/%.efi: $(obj)/%_efi.so quiet_cmd_efi_ld = LD $@ cmd_efi_ld = $(LD) -nostdlib -znocombreloc -T $(EFI_LDS_PATH) -shared \ - -Bsymbolic $^ -o $@ + -Bsymbolic -s $^ -o $@ EFI_LDS_PATH = $(srctree)/arch/$(ARCH)/lib/$(EFI_LDS) diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index e0833ffe22..36b35ee536 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -8,12 +8,14 @@ import u_boot_utils @pytest.mark.buildconfigspec('cmd_bootefi_selftest') def test_efi_selftest(u_boot_console): - """ - Run bootefi selftest - """ + """Test the UEFI implementation + + :param u_boot_console: U-Boot console + This function executes all selftests that are not marked as on request. + """ u_boot_console.run_command(cmd='setenv efi_selftest') - u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False) + u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False) m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) if m != 0: raise Exception('Failures occurred during the EFI selftest') |