[U-Boot] [PATCH v9 00/18] efi: Enable sandbox support for EFI loader

A limitation of the EFI loader at present is that it does not build with sandbox. This makes it hard to write tests, since sandbox is used for most testing in U-Boot.
This series enables the EFI loader feature. It allows sandbox to build and run a trivial function which calls the EFI API to output a message.
Also included in v8 is support for running the full EFI self tests. These run OK with some tweaks to a few parts of the code.
With v9, various EFI patches have been applied which change things. This series includes a partial review of one, which makes 'bootefi test' work. But there are still problems with 'bootefi selftest':
$ sandbox/u-boot -D -c "bootefi selftest" ... Executing 'block device' /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_block_device.c(385): TODO: Wrong volume label 'xxa1', expected 'U-BOOT TEST' map_to_sysmem: Added map from 00007ffd0782d2a0 to 8000000 phys_to_virt: Used map from 8000000 to 00007ffd0782d2a0 writing /u-boot.txt find_tag: Used map from 00007ffd0782d2a0 to 8000000 phys_to_virt: Used map from 8000000 to 00007ffd0782d2a0 /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_block_device.c(458): ERROR: Unexpected file content /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest.c(109): ERROR: Executing 'block device' failed
...
Executing 'simple network protocol' DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_snp.c(311): ERROR: Timeout occurred /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest.c(109): ERROR: Executing 'simple network protocol' failed
This series is at u-boot-dm/efi-working
Changes in v9: - Add comments to bootefi_test_prepare() about the memset()s - Add revert for "efi_loader: Rename sections to allow for implicit data" - Drop fdt_end variable in efi_install_fdt() - Fix 'thi' typo
Changes in v8: - Drop 'efi: Adjust memory handling to support sandbox' - Drop 'efi: sandbox: Add relocation constants' - Drop 'sandbox: smbios: Update to support sandbox' - Expand series substantially to support bootefi selftest - Rebase to master - Rebase to master, bringing in all EFI changes
Changes in v7: - Drop an unnecessary comment - Drop patch "efi: Init the 'rows' and 'cols' variables" - Drop patches previous applied - Update patch subject s/builder/build/
Changes in v6: - Warn about building sandbox EFI support on something other than __x86_64__
Changes in v5: - Add new patch to disallow CMD_BOOTEFI_SELFTEST on sandbox - Drop call to efi_init_obj_list() which is now done in do_bootefi() - Introduce load_options_path to specifyc U-Boot env var for load_options_path - Rebase to master
Changes in v4: - Rebase to master - Update SPDX tags
Changes in v3: - Add new patch to rename bootefi_test_finish() to bootefi_run_finish() - Add new patch to split out test init/uninit into functions - Add patch to create a function to set up for running EFI code - Init the 'rows' and 'cols' vars to avoid a compiler error (gcc 4.8.4) - Rebase to master
Changes in v2: - Rebase to master
Alexander Graf (1): efi_loader: Pass address to fs_read()
Simon Glass (17): Revert "efi_loader: Rename sections to allow for implicit data" efi: Don't allow CMD_BOOTEFI_SELFTEST on sandbox efi: sandbox: Add distroboot support efi: sandbox: Enable EFI loader build for sandbox efi: Split out test init/uninit into functions efi: sandbox: Add a simple 'bootefi test' command efi: Create a function to set up for running EFI code efi: Rename bootefi_test_finish() to bootefi_run_finish() sandbox: Align RAM buffer to the machine page size sandbox: Try to start the RAM buffer at a particular address sandbox: Add support for calling abort() sandbox: Enhance map_to_sysmem() to handle foreign pointers efi: Add a call to exit() along with why we can't use it efi: Relocate FDT to 127MB instead of 128MB efi: sandbox: Tidy up copy_fdt() to work with sandbox efi: Add more debugging for memory allocations efi: sandbox: Enable selftest command
arch/sandbox/config.mk | 3 - arch/sandbox/cpu/cpu.c | 141 ++++++++++++++++-- arch/sandbox/cpu/os.c | 17 ++- arch/sandbox/cpu/state.c | 8 + arch/sandbox/cpu/u-boot.lds | 9 +- arch/sandbox/include/asm/state.h | 21 +++ cmd/bootefi.c | 242 +++++++++++++++++++++---------- configs/sandbox_defconfig | 1 + include/config_distro_bootcmd.h | 11 ++ include/efi_loader.h | 3 + include/os.h | 4 + lib/efi_loader/Kconfig | 12 +- lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_boottime.c | 15 ++ lib/efi_loader/efi_file.c | 5 +- lib/efi_loader/efi_memory.c | 22 ++- lib/efi_loader/efi_test.c | 26 ++++ 17 files changed, 439 insertions(+), 102 deletions(-) create mode 100644 lib/efi_loader/efi_test.c

This partially reverts commit 7e21fbca26d18327cf7cabaad08df276a06a07d8.
That change broke sandbox EFI support for unknown reasons. It also changes sandbox to use--gc-sections which we don't want.
For now I am just reverting the sandbox portion as presumably this change is safe on other architectures.
Fixes: 7e21fbca26 (efi_loader: Rename sections to allow for implicit data) Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: - Add revert for "efi_loader: Rename sections to allow for implicit data"
Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/config.mk | 3 --- arch/sandbox/cpu/u-boot.lds | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index 5e7077bfe75..2babcde8815 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -5,9 +5,6 @@ PLATFORM_CPPFLAGS += -D__SANDBOX__ -U_FORTIFY_SOURCE PLATFORM_CPPFLAGS += -DCONFIG_ARCH_MAP_SYSMEM PLATFORM_LIBS += -lrt
-LDFLAGS_FINAL += --gc-sections -PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections - # Define this to avoid linking with SDL, which requires SDL libraries # This can solve 'sdl-config: Command not found' errors ifneq ($(NO_SDL),) diff --git a/arch/sandbox/cpu/u-boot.lds b/arch/sandbox/cpu/u-boot.lds index 727bcc35981..3a6cf55eb99 100644 --- a/arch/sandbox/cpu/u-boot.lds +++ b/arch/sandbox/cpu/u-boot.lds @@ -24,9 +24,8 @@ SECTIONS }
.efi_runtime : { - *(.text.efi_runtime*) - *(.rodata.efi_runtime*) - *(.data.efi_runtime*) + *(efi_runtime_text) + *(efi_runtime_data) }
.__efi_runtime_stop : { @@ -39,8 +38,8 @@ SECTIONS }
.efi_runtime_rel : { - *(.rel*.efi_runtime) - *(.rel*.efi_runtime.*) + *(.relefi_runtime_text) + *(.relefi_runtime_data) }
.efi_runtime_rel_stop :

On Wed, Aug 08, 2018 at 03:54:16AM -0600, Simon Glass wrote:
This partially reverts commit 7e21fbca26d18327cf7cabaad08df276a06a07d8.
That change broke sandbox EFI support for unknown reasons. It also changes sandbox to use--gc-sections which we don't want.
For now I am just reverting the sandbox portion as presumably this change is safe on other architectures.
Fixes: 7e21fbca26 (efi_loader: Rename sections to allow for implicit data) Signed-off-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!

This does not work at present and gives the following error:
output: 'ld.bfd: read in flex scanner failed scripts/Makefile.lib:390: recipe for target 'lib/efi_selftest/efi_selftest_miniapp_return_efi.so' failed
It may be possible to figure this out with suitable linker magic but it does not seem to be easy. Also, we will be able to run the tests on sandbox without using the miniapp.
So for now at least, disable this option.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: - Add new patch to disallow CMD_BOOTEFI_SELFTEST on sandbox
Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_selftest/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig index 59f9f36801c..b52696778dd 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 + depends on CMD_BOOTEFI && !SANDBOX imply FAT imply FAT_WRITE help

On 08.08.18 11:54, Simon Glass wrote:
This does not work at present and gives the following error:
output: 'ld.bfd: read in flex scanner failed scripts/Makefile.lib:390: recipe for target 'lib/efi_selftest/efi_selftest_miniapp_return_efi.so' failed
It may be possible to figure this out with suitable linker magic but it does not seem to be easy. Also, we will be able to run the tests on sandbox without using the miniapp.
So for now at least, disable this option.
Signed-off-by: Simon Glass sjg@chromium.org
Didn't Heinrich have some magic for this in the works?
Alex
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5:
- Add new patch to disallow CMD_BOOTEFI_SELFTEST on sandbox
Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_selftest/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig index 59f9f36801c..b52696778dd 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
- depends on CMD_BOOTEFI && !SANDBOX imply FAT imply FAT_WRITE help

Hi Alex,
On 26 August 2018 at 18:53, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
This does not work at present and gives the following error:
output: 'ld.bfd: read in flex scanner failed scripts/Makefile.lib:390: recipe for target 'lib/efi_selftest/efi_selftest_miniapp_return_efi.so' failed
It may be possible to figure this out with suitable linker magic but it does not seem to be easy. Also, we will be able to run the tests on sandbox without using the miniapp.
So for now at least, disable this option.
Signed-off-by: Simon Glass sjg@chromium.org
Didn't Heinrich have some magic for this in the works?
Yes I'll rewrite the commit message for this and enable it in the last patch of the series.
Regards, Simon

With sandbox these values depend on the host system. Let's assume that it is x86_64 for now.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: - Drop an unnecessary comment
Changes in v6: - Warn about building sandbox EFI support on something other than __x86_64__
Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
include/config_distro_bootcmd.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index d672e8ebe65..75866f2abf9 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -261,6 +261,17 @@ #elif defined(CONFIG_CPU_RISCV_64) #define BOOTENV_EFI_PXE_ARCH "0x1b" #define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00027:UNDI:003000" +#elif defined(CONFIG_SANDBOX) +/* + * TODO(sjg@chromium.org): Consider providing a way to enable sandbox features + * based on the host architecture + */ +# ifndef __x86_64__ +# warning "sandbox EFI support is only tested on 64-bit x86" +# endif +/* To support other *host* architectures this should be changed */ +#define BOOTENV_EFI_PXE_ARCH "0x7" +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00007:UNDI:003000" #else #error Please specify an EFI client identifier #endif

On 08.08.18 11:54, Simon Glass wrote:
With sandbox these values depend on the host system. Let's assume that it is x86_64 for now.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7:
- Drop an unnecessary comment
Changes in v6:
- Warn about building sandbox EFI support on something other than __x86_64__
Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
include/config_distro_bootcmd.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index d672e8ebe65..75866f2abf9 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -261,6 +261,17 @@ #elif defined(CONFIG_CPU_RISCV_64) #define BOOTENV_EFI_PXE_ARCH "0x1b" #define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00027:UNDI:003000" +#elif defined(CONFIG_SANDBOX)
Can we just change the #ifdef checks here to compiler based ones instead? That way sandbox automatically gets the correct architecture type depending on what it's running on.
Alex
+/*
- TODO(sjg@chromium.org): Consider providing a way to enable sandbox features
- based on the host architecture
- */
+# ifndef __x86_64__ +# warning "sandbox EFI support is only tested on 64-bit x86" +# endif +/* To support other *host* architectures this should be changed */ +#define BOOTENV_EFI_PXE_ARCH "0x7" +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00007:UNDI:003000" #else #error Please specify an EFI client identifier #endif

This allows this feature to build within sandbox. This is for testing purposes only since it is not possible for sandbox to load native code.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: - Update patch subject s/builder/build/
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: - Init the 'rows' and 'cols' vars to avoid a compiler error (gcc 4.8.4)
Changes in v2: None
lib/efi_loader/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index ce6a09f0b43..bfd7b19d791 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -1,6 +1,6 @@ config EFI_LOADER bool "Support running EFI Applications in U-Boot" - depends on (ARM || X86 || RISCV) && OF_LIBFDT + depends on (ARM || X86 || RISCV || SANDBOX) && OF_LIBFDT # We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB

On 08.08.18 11:54, Simon Glass wrote:
This allows this feature to build within sandbox. This is for testing purposes only since it is not possible for sandbox to load native code.
Last time I tried I successfully ran native code?
Alex
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7:
- Update patch subject s/builder/build/
Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3:
- Init the 'rows' and 'cols' vars to avoid a compiler error (gcc 4.8.4)
Changes in v2: None
lib/efi_loader/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index ce6a09f0b43..bfd7b19d791 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -1,6 +1,6 @@ config EFI_LOADER bool "Support running EFI Applications in U-Boot"
- depends on (ARM || X86 || RISCV) && OF_LIBFDT
- depends on (ARM || X86 || RISCV || SANDBOX) && OF_LIBFDT # We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB

Hi Alex,
On 26 August 2018 at 18:55, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
This allows this feature to build within sandbox. This is for testing purposes only since it is not possible for sandbox to load native code.
Last time I tried I successfully ran native code?
OK well I'll update the commit message. I thought that exit was broken but perhaps you fixed that?
Regards, Simon

On 14.09.18 17:46, Simon Glass wrote:
Hi Alex,
On 26 August 2018 at 18:55, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
This allows this feature to build within sandbox. This is for testing purposes only since it is not possible for sandbox to load native code.
Last time I tried I successfully ran native code?
OK well I'll update the commit message. I thought that exit was broken but perhaps you fixed that?
The only reason for exit to break is the longjmp/setjmp complication. With that resolved, exit should just work as is too :)
Alex

Hi Alex,
On 15 September 2018 at 10:11, Alexander Graf agraf@suse.de wrote:
On 14.09.18 17:46, Simon Glass wrote:
Hi Alex,
On 26 August 2018 at 18:55, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
This allows this feature to build within sandbox. This is for testing purposes only since it is not possible for sandbox to load native code.
Last time I tried I successfully ran native code?
OK well I'll update the commit message. I thought that exit was broken but perhaps you fixed that?
The only reason for exit to break is the longjmp/setjmp complication. With that resolved, exit should just work as is too :)
OK well let's go with your solution for that. We can print an error if not on GNU linux, etc.
Regards, Simon

We plan to run more than one EFI test. In order to avoid duplicating code, create functions which handle preparing for running the test and cleaning up afterwards.
Also shorten the awfully long variable names here.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: - Add comments to bootefi_test_prepare() about the memset()s
Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: - Drop call to efi_init_obj_list() which is now done in do_bootefi()
Changes in v4: None Changes in v3: - Add new patch to split out test init/uninit into functions
Changes in v2: None
cmd/bootefi.c | 91 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 24 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index b60c151fb4a..af13492836d 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -409,6 +409,64 @@ exit: 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: Pointer to a struct which will hold the loaded image info. + * This struct will be inited by this function before use. + * @obj: Pointer to a struct which will hold the loaded image object + * 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 + * @return 0 if OK, -ve on error + */ +static efi_status_t bootefi_test_prepare(struct efi_loaded_image *image, + struct efi_object *obj, + const char *path, ulong test_func) +{ + /* Zero out the structures since the caller did not */ + memset(image, '\0', sizeof(*image)); + memset(obj, '\0', sizeof(*obj)); + + /* Construct a dummy device path */ + bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, + (uintptr_t)test_func, + (uintptr_t)test_func); + bootefi_image_path = efi_dp_from_file(NULL, 0, path); + efi_setup_loaded_image(image, obj, bootefi_device_path, + bootefi_image_path); + /* + * gd lives in a fixed register which may get clobbered while we execute + * the payload. So save it here and restore it on every callback entry + */ + efi_save_gd(); + + /* Transfer environment variable efi_selftest as load options */ + set_load_options(image, "efi_selftest"); + + return 0; +} + +/** + * bootefi_test_finish() - finish up after running an EFI test + * + * @image: Pointer to a struct which holds the loaded image info + * @obj: Pointer to a struct which holds the loaded image object + */ +static void bootefi_test_finish(struct efi_loaded_image *image, + struct efi_object *obj) +{ + efi_restore_gd(); + free(image->load_options); + list_del(&obj->link); +} +#endif /* CONFIG_CMD_BOOTEFI_SELFTEST */ + static int do_bootefi_bootmgr_exec(void) { struct efi_device_path *device_path, *file_path; @@ -489,31 +547,16 @@ 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 loaded_image_info = {}; - struct efi_object loaded_image_info_obj = {}; - - /* 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"); - - efi_setup_loaded_image(&loaded_image_info, - &loaded_image_info_obj, - bootefi_device_path, bootefi_image_path); - /* - * gd lives in a fixed register which may get clobbered while we - * execute the payload. So save it here and restore it on every - * callback entry - */ - efi_save_gd(); - /* Transfer environment variable efi_selftest as load options */ - set_load_options(&loaded_image_info, "efi_selftest"); + struct efi_loaded_image image; + struct efi_object obj; + + if (bootefi_test_prepare(&image, &obj, "\selftest", + (uintptr_t)&efi_selftest)) + return CMD_RET_FAILURE; + /* Execute the test */ - r = efi_selftest(loaded_image_info_obj.handle, &systab); - efi_restore_gd(); - free(loaded_image_info.load_options); - list_del(&loaded_image_info_obj.link); + r = efi_selftest(obj.handle, &systab); + bootefi_test_finish(&image, &obj); return r != EFI_SUCCESS; } else #endif

This jumps to test code which can call directly into the EFI support. It does not need a separate image so it is easy to write tests with it.
This test can be executed without causing problems to the run-time environment (e.g. U-Boot does not need to reboot afterwards).
For now the test just outputs a message. To try it:
./sandbox/u-boot -c "bootefi test" U-Boot 2017.09-00204-g696c9855fe (Sep 17 2017 - 16:43:53 -0600)
DRAM: 128 MiB MMC: Using default environment
In: serial Out: serial Err: serial SCSI: Net: No ethernet found. IDE: Bus 0: not available Found 0 disks WARNING: booting without device tree Hello, world! Test passed
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
cmd/bootefi.c | 26 ++++++++++++++++++++------ include/efi_loader.h | 3 +++ lib/efi_loader/Kconfig | 10 ++++++++++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_test.c | 16 ++++++++++++++++ 5 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 lib/efi_loader/efi_test.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index af13492836d..edb7f786a27 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -409,7 +409,6 @@ exit: return ret; }
-#ifdef CONFIG_CMD_BOOTEFI_SELFTEST /** * bootefi_test_prepare() - prepare to run an EFI test * @@ -465,7 +464,6 @@ static void bootefi_test_finish(struct efi_loaded_image *image, free(image->load_options); list_del(&obj->link); } -#endif /* CONFIG_CMD_BOOTEFI_SELFTEST */
static int do_bootefi_bootmgr_exec(void) { @@ -497,8 +495,10 @@ static int do_bootefi_bootmgr_exec(void) /* Interpreter command to boot an arbitrary EFI image from memory */ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - unsigned long addr; + struct efi_loaded_image image; + struct efi_object obj; char *saddr; + unsigned long addr; efi_status_t r; unsigned long fdt_addr; void *fdt; @@ -545,11 +545,25 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) memcpy(map_sysmem(addr, size), __efi_helloworld_begin, size); } else #endif + if (IS_ENABLED(CONFIG_BOOTEFI_TEST) && !strcmp(argv[1], "test")) { + int ret; + + if (bootefi_test_prepare(&image, &obj, "\test", + (ulong)&efi_test)) + return CMD_RET_FAILURE; + + ret = efi_test(&image, &systab); + bootefi_test_finish(&image, &obj); + if (ret) { + printf("Test failed: err=%d\n", ret); + return CMD_RET_FAILURE; + } + printf("Test passed\n"); + + return 0; + } #ifdef CONFIG_CMD_BOOTEFI_SELFTEST if (!strcmp(argv[1], "selftest")) { - struct efi_loaded_image image; - struct efi_object obj; - if (bootefi_test_prepare(&image, &obj, "\selftest", (uintptr_t)&efi_selftest)) return CMD_RET_FAILURE; diff --git a/include/efi_loader.h b/include/efi_loader.h index 57ca5502726..616dfae7b70 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -457,6 +457,9 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor, void *efi_bootmgr_load(struct efi_device_path **device_path, struct efi_device_path **file_path);
+/* Perform EFI tests */ +int efi_test(efi_handle_t image_handle, struct efi_system_table *systab); + #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index bfd7b19d791..254b18da744 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -23,3 +23,13 @@ config EFI_LOADER_BOUNCE_BUFFER Some hardware does not support DMA to full 64bit addresses. For this hardware we can create a bounce buffer so that payloads don't have to worry about platform details. + +config BOOTEFI_TEST + bool "Provide a test for the EFI loader" + depends on EFI_LOADER && SANDBOX + default y + help + Provides a test of the EFI loader functionality accessed via the + command line ('bootefi test'). This runs within U-Boot so does not + need a separate EFI application to work. It aims to include coverage + of all EFI code which can be accessed within sandbox. diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 1ffbf52a898..e6a23d96122 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o +obj-$(CONFIG_BOOTEFI_TEST) += efi_test.o diff --git a/lib/efi_loader/efi_test.c b/lib/efi_loader/efi_test.c new file mode 100644 index 00000000000..4b8d49f324b --- /dev/null +++ b/lib/efi_loader/efi_test.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017, Google Inc. + */ + +#include <common.h> +#include <efi_api.h> + +int efi_test(efi_handle_t image_handle, struct efi_system_table *systable) +{ + struct efi_simple_text_output_protocol *con_out = systable->con_out; + + con_out->output_string(con_out, L"Hello, world!\n"); + + return 0; +}

On 08.08.18 11:54, Simon Glass wrote:
This jumps to test code which can call directly into the EFI support. It does not need a separate image so it is easy to write tests with it.
This test can be executed without causing problems to the run-time environment (e.g. U-Boot does not need to reboot afterwards).
For now the test just outputs a message. To try it:
./sandbox/u-boot -c "bootefi test" U-Boot 2017.09-00204-g696c9855fe (Sep 17 2017 - 16:43:53 -0600)
DRAM: 128 MiB MMC: Using default environment
In: serial Out: serial Err: serial SCSI: Net: No ethernet found. IDE: Bus 0: not available Found 0 disks WARNING: booting without device tree Hello, world! Test passed
Signed-off-by: Simon Glass sjg@chromium.org
I think I'm missing the point of this exercise. What's the problem with running the selftest suite? Its set of tests that it runs can be configured using an environment variable just fine?
Alex
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
cmd/bootefi.c | 26 ++++++++++++++++++++------ include/efi_loader.h | 3 +++ lib/efi_loader/Kconfig | 10 ++++++++++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_test.c | 16 ++++++++++++++++ 5 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 lib/efi_loader/efi_test.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index af13492836d..edb7f786a27 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -409,7 +409,6 @@ exit: return ret; }
-#ifdef CONFIG_CMD_BOOTEFI_SELFTEST /**
- bootefi_test_prepare() - prepare to run an EFI test
@@ -465,7 +464,6 @@ static void bootefi_test_finish(struct efi_loaded_image *image, free(image->load_options); list_del(&obj->link); } -#endif /* CONFIG_CMD_BOOTEFI_SELFTEST */
static int do_bootefi_bootmgr_exec(void) { @@ -497,8 +495,10 @@ static int do_bootefi_bootmgr_exec(void) /* Interpreter command to boot an arbitrary EFI image from memory */ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
- unsigned long addr;
- struct efi_loaded_image image;
- struct efi_object obj; char *saddr;
- unsigned long addr; efi_status_t r; unsigned long fdt_addr; void *fdt;
@@ -545,11 +545,25 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) memcpy(map_sysmem(addr, size), __efi_helloworld_begin, size); } else #endif
- if (IS_ENABLED(CONFIG_BOOTEFI_TEST) && !strcmp(argv[1], "test")) {
int ret;
if (bootefi_test_prepare(&image, &obj, "\\test",
(ulong)&efi_test))
return CMD_RET_FAILURE;
ret = efi_test(&image, &systab);
bootefi_test_finish(&image, &obj);
if (ret) {
printf("Test failed: err=%d\n", ret);
return CMD_RET_FAILURE;
}
printf("Test passed\n");
return 0;
- }
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST if (!strcmp(argv[1], "selftest")) {
struct efi_loaded_image image;
struct efi_object obj;
- if (bootefi_test_prepare(&image, &obj, "\selftest", (uintptr_t)&efi_selftest)) return CMD_RET_FAILURE;
diff --git a/include/efi_loader.h b/include/efi_loader.h index 57ca5502726..616dfae7b70 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -457,6 +457,9 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor, void *efi_bootmgr_load(struct efi_device_path **device_path, struct efi_device_path **file_path);
+/* Perform EFI tests */ +int efi_test(efi_handle_t image_handle, struct efi_system_table *systab);
#else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index bfd7b19d791..254b18da744 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -23,3 +23,13 @@ config EFI_LOADER_BOUNCE_BUFFER Some hardware does not support DMA to full 64bit addresses. For this hardware we can create a bounce buffer so that payloads don't have to worry about platform details.
+config BOOTEFI_TEST
- bool "Provide a test for the EFI loader"
- depends on EFI_LOADER && SANDBOX
- default y
- help
Provides a test of the EFI loader functionality accessed via the
command line ('bootefi test'). This runs within U-Boot so does not
need a separate EFI application to work. It aims to include coverage
of all EFI code which can be accessed within sandbox.
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 1ffbf52a898..e6a23d96122 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o +obj-$(CONFIG_BOOTEFI_TEST) += efi_test.o diff --git a/lib/efi_loader/efi_test.c b/lib/efi_loader/efi_test.c new file mode 100644 index 00000000000..4b8d49f324b --- /dev/null +++ b/lib/efi_loader/efi_test.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2017, Google Inc.
- */
+#include <common.h> +#include <efi_api.h>
+int efi_test(efi_handle_t image_handle, struct efi_system_table *systable) +{
- struct efi_simple_text_output_protocol *con_out = systable->con_out;
- con_out->output_string(con_out, L"Hello, world!\n");
- return 0;
+}

Hi Alex,
On 26 August 2018 at 18:58, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
This jumps to test code which can call directly into the EFI support. It does not need a separate image so it is easy to write tests with it.
This test can be executed without causing problems to the run-time environment (e.g. U-Boot does not need to reboot afterwards).
For now the test just outputs a message. To try it:
./sandbox/u-boot -c "bootefi test" U-Boot 2017.09-00204-g696c9855fe (Sep 17 2017 - 16:43:53 -0600)
DRAM: 128 MiB MMC: Using default environment
In: serial Out: serial Err: serial SCSI: Net: No ethernet found. IDE: Bus 0: not available Found 0 disks WARNING: booting without device tree Hello, world! Test passed
Signed-off-by: Simon Glass sjg@chromium.org
I think I'm missing the point of this exercise. What's the problem with running the selftest suite? Its set of tests that it runs can be configured using an environment variable just fine?
I'm not saying you can't run the selftest suite, but that is for a different purpose. This is just a sanity check. I'll change the name to 'ping instead'.
Regards, Simon

Add a new bootefi_run_prepare() function which holds common code used to set up U-Boot to run EFI code. Make use of this from the existing bootefi_test_prepare() function, as well as do_bootefi_exec().
Also shorten a few variable names.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: - Drop call to efi_init_obj_list() which is now done in do_bootefi() - Introduce load_options_path to specifyc U-Boot env var for load_options_path
Changes in v4: - Rebase to master
Changes in v3: - Add patch to create a function to set up for running EFI code
Changes in v2: None
cmd/bootefi.c | 81 ++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 37 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index edb7f786a27..54732ba3f37 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -290,6 +290,26 @@ static efi_status_t efi_install_fdt(void *fdt) return ret; }
+static efi_status_t bootefi_run_prepare(struct efi_loaded_image *image, + struct efi_object *obj, + const char *load_options_path, + struct efi_device_path *device_path, + struct efi_device_path *image_path) +{ + efi_setup_loaded_image(image, obj, device_path, image_path); + + /* + * gd lives in a fixed register which may get clobbered while we execute + * the payload. So save it here and restore it on every callback entry + */ + efi_save_gd(); + + /* Transfer environment variable as load options */ + set_load_options(image, load_options_path); + + return 0; +} + /* * Load an EFI payload into a newly allocated piece of memory, register all * EFI objects it would want to access and jump to it. @@ -298,8 +318,8 @@ static efi_status_t do_bootefi_exec(void *efi, struct efi_device_path *device_path, struct efi_device_path *image_path) { - struct efi_loaded_image loaded_image_info = {}; - struct efi_object loaded_image_info_obj = {}; + struct efi_loaded_image image; + struct efi_object obj; struct efi_object mem_obj = {}; struct efi_device_path *memdp = NULL; efi_status_t ret; @@ -327,19 +347,13 @@ static efi_status_t do_bootefi_exec(void *efi, assert(device_path && image_path); }
- efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, - device_path, image_path); + ret = bootefi_run_prepare(&image, &obj, "bootargs", device_path, + image_path); + if (ret) + return ret;
- /* - * gd lives in a fixed register which may get clobbered while we execute - * the payload. So save it here and restore it on every callback entry - */ - efi_save_gd(); - - /* Transfer environment variable bootargs as load options */ - set_load_options(&loaded_image_info, "bootargs"); /* Load the EFI payload */ - entry = efi_load_pe(efi, &loaded_image_info); + entry = efi_load_pe(efi, &image); if (!entry) { ret = EFI_LOAD_ERROR; goto exit; @@ -347,10 +361,10 @@ static efi_status_t do_bootefi_exec(void *efi,
if (memdp) { struct efi_device_path_memory *mdp = (void *)memdp; - mdp->memory_type = loaded_image_info.image_code_type; - mdp->start_address = (uintptr_t)loaded_image_info.image_base; + mdp->memory_type = image.image_code_type; + mdp->start_address = (uintptr_t)image.image_base; mdp->end_address = mdp->start_address + - loaded_image_info.image_size; + image.image_size; }
/* we don't support much: */ @@ -360,8 +374,8 @@ 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(&loaded_image_info.exit_jmp)) { - ret = loaded_image_info.exit_status; + if (setjmp(&image.exit_jmp)) { + ret = image.exit_status; goto exit; }
@@ -373,7 +387,7 @@ static efi_status_t do_bootefi_exec(void *efi,
/* Move into EL2 and keep running there */ armv8_switch_to_el2((ulong)entry, - (ulong)&loaded_image_info_obj.handle, + (ulong)&obj.handle, (ulong)&systab, 0, (ulong)efi_run_in_el2, ES_TO_AARCH64);
@@ -390,7 +404,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)loaded_image_info_obj.handle, + (uintptr_t)obj.handle, (uintptr_t)&systab);
/* Should never reach here, efi exits with longjmp */ @@ -398,11 +412,11 @@ static efi_status_t do_bootefi_exec(void *efi, } #endif
- ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry); + ret = efi_do_enter(obj.handle, &systab, entry);
exit: /* image has returned, loaded-image obj goes *poof*: */ - list_del(&loaded_image_info_obj.link); + list_del(&obj.link); if (mem_obj.handle) list_del(&mem_obj.link);
@@ -422,11 +436,13 @@ exit: * @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 *image, struct efi_object *obj, - const char *path, ulong test_func) + const char *path, ulong test_func, + const char *load_options_path) { /* Zero out the structures since the caller did not */ memset(image, '\0', sizeof(*image)); @@ -437,18 +453,8 @@ static efi_status_t bootefi_test_prepare(struct efi_loaded_image *image, (uintptr_t)test_func, (uintptr_t)test_func); bootefi_image_path = efi_dp_from_file(NULL, 0, path); - efi_setup_loaded_image(image, obj, bootefi_device_path, - bootefi_image_path); - /* - * gd lives in a fixed register which may get clobbered while we execute - * the payload. So save it here and restore it on every callback entry - */ - efi_save_gd(); - - /* Transfer environment variable efi_selftest as load options */ - set_load_options(image, "efi_selftest"); - - return 0; + return bootefi_run_prepare(image, obj, load_options_path, + bootefi_device_path, bootefi_image_path); }
/** @@ -549,7 +555,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int ret;
if (bootefi_test_prepare(&image, &obj, "\test", - (ulong)&efi_test)) + (ulong)&efi_test, "efi_test")) return CMD_RET_FAILURE;
ret = efi_test(&image, &systab); @@ -565,7 +571,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #ifdef CONFIG_CMD_BOOTEFI_SELFTEST if (!strcmp(argv[1], "selftest")) { if (bootefi_test_prepare(&image, &obj, "\selftest", - (uintptr_t)&efi_selftest)) + (uintptr_t)&efi_selftest, + "efi_selftest")) return CMD_RET_FAILURE;
/* Execute the test */

This function can be used from do_bootefi_exec() so that we use mostly the same code for a normal EFI application and an EFI test.
Rename the function and use it in both places.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: - Drop patch "efi: Init the 'rows' and 'cols' variables" - Drop patches previous applied
Changes in v6: None Changes in v5: - Rebase to master
Changes in v4: - Rebase to master
Changes in v3: - Add new patch to rename bootefi_test_finish() to bootefi_run_finish()
Changes in v2: None
cmd/bootefi.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 54732ba3f37..905e659da42 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -310,6 +310,20 @@ static efi_status_t bootefi_run_prepare(struct efi_loaded_image *image, return 0; }
+/** + * bootefi_run_finish() - finish up after running an EFI test + * + * @image: Pointer to a struct which holds the loaded image info + * @obj: Pointer to a struct which holds the loaded image object + */ +static void bootefi_run_finish(struct efi_loaded_image *image, + struct efi_object *obj) +{ + efi_restore_gd(); + free(image->load_options); + list_del(&obj->link); +} + /* * Load an EFI payload into a newly allocated piece of memory, register all * EFI objects it would want to access and jump to it. @@ -415,8 +429,7 @@ static efi_status_t do_bootefi_exec(void *efi, ret = efi_do_enter(obj.handle, &systab, entry);
exit: - /* image has returned, loaded-image obj goes *poof*: */ - list_del(&obj.link); + bootefi_run_finish(&image, &obj); if (mem_obj.handle) list_del(&mem_obj.link);
@@ -457,20 +470,6 @@ static efi_status_t bootefi_test_prepare(struct efi_loaded_image *image, bootefi_device_path, bootefi_image_path); }
-/** - * bootefi_test_finish() - finish up after running an EFI test - * - * @image: Pointer to a struct which holds the loaded image info - * @obj: Pointer to a struct which holds the loaded image object - */ -static void bootefi_test_finish(struct efi_loaded_image *image, - struct efi_object *obj) -{ - efi_restore_gd(); - free(image->load_options); - list_del(&obj->link); -} - static int do_bootefi_bootmgr_exec(void) { struct efi_device_path *device_path, *file_path; @@ -559,7 +558,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_FAILURE;
ret = efi_test(&image, &systab); - bootefi_test_finish(&image, &obj); + bootefi_run_finish(&image, &obj); if (ret) { printf("Test failed: err=%d\n", ret); return CMD_RET_FAILURE; @@ -577,7 +576,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
/* Execute the test */ r = efi_selftest(obj.handle, &systab); - bootefi_test_finish(&image, &obj); + bootefi_run_finish(&image, &obj); return r != EFI_SUCCESS; } else #endif

At present the sandbox RAM buffer is not aligned to any particular address boundary. This makes the internal pointers somewhat random with respect to the associated RAM buffer addresses.
Align the buffer to the page size of the machine to help with this.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/cpu/os.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 5839932b005..a1a982af2de 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -143,14 +143,15 @@ void os_tty_raw(int fd, bool allow_sigs) void *os_malloc(size_t length) { struct os_mem_hdr *hdr; + int page_size = getpagesize();
- hdr = mmap(NULL, length + sizeof(*hdr), PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + hdr = mmap(NULL, length + page_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (hdr == MAP_FAILED) return NULL; hdr->length = length;
- return hdr + 1; + return (void *)hdr + page_size; }
void os_free(void *ptr)

On 08.08.18 11:54, Simon Glass wrote:
At present the sandbox RAM buffer is not aligned to any particular address boundary. This makes the internal pointers somewhat random with respect to the associated RAM buffer addresses.
Align the buffer to the page size of the machine to help with this.
Please describe in the patch description that you pad the cookie that os_malloc writes to a page boundary. It's pretty hard to grasp that from the current patch description :)
Alex
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/cpu/os.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 5839932b005..a1a982af2de 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -143,14 +143,15 @@ void os_tty_raw(int fd, bool allow_sigs) void *os_malloc(size_t length) { struct os_mem_hdr *hdr;
- int page_size = getpagesize();
- hdr = mmap(NULL, length + sizeof(*hdr), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- hdr = mmap(NULL, length + page_size,
if (hdr == MAP_FAILED) return NULL; hdr->length = length;PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- return hdr + 1;
- return (void *)hdr + page_size;
}
void os_free(void *ptr)

Use a starting address of 256MB which should be available. This helps to make sandbox RAM buffers pointers more recognisable.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/cpu/os.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index a1a982af2de..1553aa687df 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -145,7 +145,12 @@ void *os_malloc(size_t length) struct os_mem_hdr *hdr; int page_size = getpagesize();
- hdr = mmap(NULL, length + page_size, + /* + * Use an address that is hopefully available to us so that pointers + * to this memory are fairly obvious. If we end up with a different + * address, that's fine too. + */ + hdr = mmap((void *)0x10000000, length + page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (hdr == MAP_FAILED) return NULL;

On 08.08.18 11:54, Simon Glass wrote:
Use a starting address of 256MB which should be available. This helps to make sandbox RAM buffers pointers more recognisable.
Signed-off-by: Simon Glass sjg@chromium.org
Is this really necessary still?
Alex
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/cpu/os.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index a1a982af2de..1553aa687df 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -145,7 +145,12 @@ void *os_malloc(size_t length) struct os_mem_hdr *hdr; int page_size = getpagesize();
- hdr = mmap(NULL, length + page_size,
- /*
* Use an address that is hopefully available to us so that pointers
* to this memory are fairly obvious. If we end up with a different
* address, that's fine too.
*/
- hdr = mmap((void *)0x10000000, length + page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (hdr == MAP_FAILED) return NULL;

Hi Alex,
On 26 August 2018 at 19:01, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
Use a starting address of 256MB which should be available. This helps to make sandbox RAM buffers pointers more recognisable.
Signed-off-by: Simon Glass sjg@chromium.org
Is this really necessary still?
It was never necessary, but it seems like a useful thing to do. It makes all the pointers smaller (the first 32 bits are 0 which makes them easier to read).
Regards, Simon

On 14 September 2018 at 09:46, Simon Glass sjg@chromium.org wrote:
Hi Alex,
On 26 August 2018 at 19:01, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
Use a starting address of 256MB which should be available. This helps to make sandbox RAM buffers pointers more recognisable.
Signed-off-by: Simon Glass sjg@chromium.org
Is this really necessary still?
It was never necessary, but it seems like a useful thing to do. It makes all the pointers smaller (the first 32 bits are 0 which makes them easier to read).
Applied to u-boot-dm.

This function is useful to signal that the application needs to exit immediate. It can be caught with a debugger (e.g. gdb). Add a stub for it so that it can be called from within sandbox when an internal error occurs.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/cpu/os.c | 5 +++++ include/os.h | 4 ++++ 2 files changed, 9 insertions(+)
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 1553aa687df..f8d87df7b63 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -657,3 +657,8 @@ void os_longjmp(ulong *jmp, int ret) { longjmp((struct __jmp_buf_tag *)jmp, ret); } + +void os_abort(void) +{ + abort(); +} diff --git a/include/os.h b/include/os.h index c8e0f52d306..e850f879ec3 100644 --- a/include/os.h +++ b/include/os.h @@ -351,4 +351,8 @@ int os_setjmp(ulong *jmp, int size); */ void os_longjmp(ulong *jmp, int ret);
+/** + * os_abort() - Raise SIGABRT to exit sandbox (e.g. to debugger) + */ +void os_abort(void); #endif

At present map_sysmem() maps an address into the sandbox RAM buffer, return a pointer, while map_to_sysmem() goes the other way.
The mapping is currently just 1:1 since a case was not found where a more flexible mapping was needed. PCI does have a separate and more complex mapping, but uses its own mechanism.
However this arrange cannot handle one important case, which is where a test declares a stack variable and passes a pointer to it into a U-Boot function which uses map_to_sysmem() to turn it into a address. Since the pointer is not inside emulated DRAM, this will fail.
Add a mapping feature which can handle any such pointer, mapping it to a simple tag value which can be passed around in U-Boot as an address.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: - Fix 'thi' typo
Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
arch/sandbox/cpu/cpu.c | 141 +++++++++++++++++++++++++++++-- arch/sandbox/cpu/state.c | 8 ++ arch/sandbox/include/asm/state.h | 21 +++++ 3 files changed, 161 insertions(+), 9 deletions(-)
diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index cde0b055a67..e523223ebf8 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -57,14 +57,104 @@ int cleanup_before_linux_select(int flags) return 0; }
+/** + * is_in_sandbox_mem() - Checks if a pointer is within sandbox's emulated DRAM + * + * This provides a way to check if a pointer is owned by sandbox (and is within + * its RAM) or not. Sometimes pointers come from a test which conceptually runs + * output sandbox, potentially with direct access to the C-library malloc() + * function, or the sandbox stack (which is not actually within the emulated + * DRAM. + * + * Such pointers obviously cannot be mapped into sandbox's DRAM, so we must + * detect them an process them separately, by recording a mapping to a tag, + * which we can use to map back to the pointer later. + * + * @ptr: Pointer to check + * @return true if this is within sandbox emulated DRAM, false if not + */ +static bool is_in_sandbox_mem(const void *ptr) +{ + return (const uint8_t *)ptr >= gd->arch.ram_buf && + (const uint8_t *)ptr < gd->arch.ram_buf + gd->ram_size; +} + +/** + * phys_to_virt() - Converts a sandbox RAM address to a pointer + * + * Sandbox uses U-Boot addresses from 0 to the size of DRAM. These index into + * the emulated DRAM buffer used by sandbox. This function converts such an + * address to a pointer into this buffer, which can be used to access the + * memory. + * + * If the address is outside this range, it is assumed to be a tag + */ void *phys_to_virt(phys_addr_t paddr) { - return (void *)(gd->arch.ram_buf + paddr); + struct sandbox_mapmem_entry *mentry; + struct sandbox_state *state; + + /* If the address is within emulated DRAM, calculate the value */ + if (paddr < gd->ram_size) + return (void *)(gd->arch.ram_buf + paddr); + + /* + * Otherwise search out list of tags for the correct pointer previously + * created by map_to_sysmem() + */ + state = state_get_current(); + list_for_each_entry(mentry, &state->mapmem_head, sibling_node) { + if (mentry->tag == paddr) { + printf("%s: Used map from %lx to %p\n", __func__, + (ulong)paddr, mentry->ptr); + return mentry->ptr; + } + } + + printf("%s: Cannot map sandbox address %lx (SDRAM from 0 to %lx)\n", + __func__, (ulong)paddr, (ulong)gd->ram_size); + os_abort(); + + /* Not reached */ + return NULL; +} + +struct sandbox_mapmem_entry *find_tag(const void *ptr) +{ + struct sandbox_mapmem_entry *mentry; + struct sandbox_state *state = state_get_current(); + + list_for_each_entry(mentry, &state->mapmem_head, sibling_node) { + if (mentry->ptr == ptr) { + debug("%s: Used map from %p to %lx\n", __func__, ptr, + mentry->tag); + return mentry; + } + } + return NULL; }
-phys_addr_t virt_to_phys(void *vaddr) +phys_addr_t virt_to_phys(void *ptr) { - return (phys_addr_t)((uint8_t *)vaddr - gd->arch.ram_buf); + struct sandbox_mapmem_entry *mentry; + + /* + * If it is in emulated RAM, don't bother looking for a tag. Just + * calculate the pointer using the provides offset into the RAM buffer. + */ + if (is_in_sandbox_mem(ptr)) + return (phys_addr_t)((uint8_t *)ptr - gd->arch.ram_buf); + + mentry = find_tag(ptr); + if (!mentry) { + /* Abort so that gdb can be used here */ + printf("%s: Cannot map sandbox address %p (SDRAM from 0 to %lx)\n", + __func__, ptr, (ulong)gd->ram_size); + os_abort(); + } + printf("%s: Used map from %p to %lx\n", __func__, ptr, mentry->tag); + + return mentry->tag; }
void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) @@ -87,24 +177,57 @@ void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) return phys_to_virt(paddr); }
-void unmap_physmem(const void *vaddr, unsigned long flags) +void unmap_physmem(const void *ptr, unsigned long flags) { #ifdef CONFIG_PCI if (map_dev) { - pci_unmap_physmem(vaddr, map_len, map_dev); + pci_unmap_physmem(ptr, map_len, map_dev); map_dev = NULL; } #endif }
-void sandbox_set_enable_pci_map(int enable) +phys_addr_t map_to_sysmem(const void *ptr) { - enable_pci_map = enable; + struct sandbox_mapmem_entry *mentry; + + /* + * If it is in emulated RAM, don't bother creating a tag. Just return + * the offset into the RAM buffer. + */ + if (is_in_sandbox_mem(ptr)) + return (u8 *)ptr - gd->arch.ram_buf; + + /* + * See if there is an existing tag with this pointer. If not, set up a + * new one. + */ + mentry = find_tag(ptr); + if (!mentry) { + struct sandbox_state *state = state_get_current(); + + mentry = malloc(sizeof(*mentry)); + if (!mentry) { + printf("%s: Error: Out of memory\n", __func__); + os_exit(ENOMEM); + } + mentry->tag = state->next_tag++; + mentry->ptr = (void *)ptr; + list_add_tail(&mentry->sibling_node, &state->mapmem_head); + debug("%s: Added map from %p to %lx\n", __func__, ptr, + (ulong)mentry->tag); + } + + /* + * Return the tag as the address to use. A later call to map_sysmem() + * will return ptr + */ + return mentry->tag; }
-phys_addr_t map_to_sysmem(const void *ptr) +void sandbox_set_enable_pci_map(int enable) { - return (u8 *)ptr - gd->arch.ram_buf; + enable_pci_map = enable; }
void flush_dcache_range(unsigned long start, unsigned long stop) diff --git a/arch/sandbox/cpu/state.c b/arch/sandbox/cpu/state.c index cc50819ab93..04a11fed559 100644 --- a/arch/sandbox/cpu/state.c +++ b/arch/sandbox/cpu/state.c @@ -359,6 +359,14 @@ void state_reset_for_test(struct sandbox_state *state)
memset(&state->wdt, '\0', sizeof(state->wdt)); memset(state->spi, '\0', sizeof(state->spi)); + + /* + * Set up the memory tag list. Use the top of emulated SDRAM for the + * first tag number, since that address offset is outside the legal + * range, and can be assumed to be a tag. + */ + INIT_LIST_HEAD(&state->mapmem_head); + state->next_tag = state->ram_size; }
int state_init(void) diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 7ed4b512d2e..a612ce89447 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -9,6 +9,7 @@ #include <config.h> #include <sysreset.h> #include <stdbool.h> +#include <linux/list.h> #include <linux/stringify.h>
/** @@ -45,6 +46,23 @@ struct sandbox_wdt_info { bool running; };
+/** + * struct sandbox_mapmem_entry - maps pointers to/from U-Boot addresses + * + * When map_to_sysmem() is called with an address outside sandbox's emulated + * RAM, a record is created with a tag that can be used to reference that + * pointer. When map_sysmem() is called later with that tag, the pointer will + * be returned, just as it would for a normal sandbox address. + * + * @tag: Address tag (a value which U-Boot uses to refer to the address) + * @ptr: Associated pointer for that tag + */ +struct sandbox_mapmem_entry { + ulong tag; + void *ptr; + struct list_head sibling_node; +}; + /* The complete state of the test system */ struct sandbox_state { const char *cmd; /* Command to execute */ @@ -78,6 +96,9 @@ struct sandbox_state {
/* Information about Watchdog */ struct sandbox_wdt_info wdt; + + ulong next_tag; /* Next address tag to allocate */ + struct list_head mapmem_head; /* struct sandbox_mapmem_entry */ };
/* Minimum space we guarantee in the state FDT when calling read/write*/

On 08.08.18 11:54, Simon Glass wrote:
At present map_sysmem() maps an address into the sandbox RAM buffer, return a pointer, while map_to_sysmem() goes the other way.
The mapping is currently just 1:1 since a case was not found where a more flexible mapping was needed. PCI does have a separate and more complex mapping, but uses its own mechanism.
However this arrange cannot handle one important case, which is where a test declares a stack variable and passes a pointer to it into a U-Boot function which uses map_to_sysmem() to turn it into a address. Since the pointer is not inside emulated DRAM, this will fail.
Add a mapping feature which can handle any such pointer, mapping it to a simple tag value which can be passed around in U-Boot as an address.
Signed-off-by: Simon Glass sjg@chromium.org
I think you are aware that this logic will fall apart spectacularly if any arithmetic operation happens on the virtual (U-Boot) address, right? So simple code like
readl(vaddr + 1);
will just fail (hopefully) or (more likely) return a completely incorrect value.
I assume this is intentional, but shouldn't the tag increment be something slightly larger then?
Alex

Hi Alex,
On 26 August 2018 at 19:11, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
At present map_sysmem() maps an address into the sandbox RAM buffer, return a pointer, while map_to_sysmem() goes the other way.
The mapping is currently just 1:1 since a case was not found where a more flexible mapping was needed. PCI does have a separate and more complex mapping, but uses its own mechanism.
However this arrange cannot handle one important case, which is where a test declares a stack variable and passes a pointer to it into a U-Boot function which uses map_to_sysmem() to turn it into a address. Since the pointer is not inside emulated DRAM, this will fail.
Add a mapping feature which can handle any such pointer, mapping it to a simple tag value which can be passed around in U-Boot as an address.
Signed-off-by: Simon Glass sjg@chromium.org
I think you are aware that this logic will fall apart spectacularly if any arithmetic operation happens on the virtual (U-Boot) address, right? So simple code like
readl(vaddr + 1);
will just fail (hopefully) or (more likely) return a completely incorrect value.
I assume this is intentional, but shouldn't the tag increment be something slightly larger then?
What do you expect readl() to do on sandbox? At present it is just a no-op. I suppose we could support memory-mapped I/O but it has not been attempted yet.
Regards, Simon

On 14.09.18 17:46, Simon Glass wrote:
Hi Alex,
On 26 August 2018 at 19:11, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
At present map_sysmem() maps an address into the sandbox RAM buffer, return a pointer, while map_to_sysmem() goes the other way.
The mapping is currently just 1:1 since a case was not found where a more flexible mapping was needed. PCI does have a separate and more complex mapping, but uses its own mechanism.
However this arrange cannot handle one important case, which is where a test declares a stack variable and passes a pointer to it into a U-Boot function which uses map_to_sysmem() to turn it into a address. Since the pointer is not inside emulated DRAM, this will fail.
Add a mapping feature which can handle any such pointer, mapping it to a simple tag value which can be passed around in U-Boot as an address.
Signed-off-by: Simon Glass sjg@chromium.org
I think you are aware that this logic will fall apart spectacularly if any arithmetic operation happens on the virtual (U-Boot) address, right? So simple code like
readl(vaddr + 1);
will just fail (hopefully) or (more likely) return a completely incorrect value.
I assume this is intentional, but shouldn't the tag increment be something slightly larger then?
What do you expect readl() to do on sandbox? At present it is just a no-op. I suppose we could support memory-mapped I/O but it has not been attempted yet.
It was really just meant as an arbitrary example of something where you assume "address + 1" == "pointer(address) + 1".
Alex

Hi Alex,
On 15 September 2018 at 10:16, Alexander Graf agraf@suse.de wrote:
On 14.09.18 17:46, Simon Glass wrote:
Hi Alex,
On 26 August 2018 at 19:11, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
At present map_sysmem() maps an address into the sandbox RAM buffer, return a pointer, while map_to_sysmem() goes the other way.
The mapping is currently just 1:1 since a case was not found where a more flexible mapping was needed. PCI does have a separate and more complex mapping, but uses its own mechanism.
However this arrange cannot handle one important case, which is where a test declares a stack variable and passes a pointer to it into a U-Boot function which uses map_to_sysmem() to turn it into a address. Since the pointer is not inside emulated DRAM, this will fail.
Add a mapping feature which can handle any such pointer, mapping it to a simple tag value which can be passed around in U-Boot as an address.
Signed-off-by: Simon Glass sjg@chromium.org
I think you are aware that this logic will fall apart spectacularly if any arithmetic operation happens on the virtual (U-Boot) address, right? So simple code like
readl(vaddr + 1);
will just fail (hopefully) or (more likely) return a completely incorrect value.
I assume this is intentional, but shouldn't the tag increment be something slightly larger then?
What do you expect readl() to do on sandbox? At present it is just a no-op. I suppose we could support memory-mapped I/O but it has not been attempted yet.
It was really just meant as an arbitrary example of something where you assume "address + 1" == "pointer(address) + 1".
So long as the addresses are within the range sandbox supports (normally 0 to 128MB unless you increase RAM size) then there is no problem adding 1 to either an address (or a pointer derived from it with map_sysmem()).
If you use an address outside that range, it is actually a tag, not an address. Trying to use an out-of-range address is invalid anyway, so it probably doesn't matter.
Regards, Simon

The test should exit like any other EFI application, by calling exit() in the boot services API. But this crashes at present on sandbox. For now, add in the placeholder code.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_test.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_test.c b/lib/efi_loader/efi_test.c index 4b8d49f324b..5401a0f4715 100644 --- a/lib/efi_loader/efi_test.c +++ b/lib/efi_loader/efi_test.c @@ -9,8 +9,18 @@ int efi_test(efi_handle_t image_handle, struct efi_system_table *systable) { struct efi_simple_text_output_protocol *con_out = systable->con_out; + struct efi_boot_services *boottime = systable->boottime; + int ret;
- con_out->output_string(con_out, L"Hello, world!\n"); + ret = con_out->output_string(con_out, L"Hello, world!\n"); + + /* + * We should really call EFI's exit() here but this crashes at present + * on sandbox due to the incorrect use of setjmp() and longjmp(). Once + * we can figure out how to make that work, this can be enabled. + */ + if (0) + boottime->exit(image_handle, ret, 0, NULL);
return 0; }

On 08/08/2018 11:54 AM, Simon Glass wrote:
The test should exit like any other EFI application, by calling exit() in the boot services API. But this crashes at present on sandbox. For now, add in the placeholder code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_test.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_test.c b/lib/efi_loader/efi_test.c index 4b8d49f324b..5401a0f4715 100644 --- a/lib/efi_loader/efi_test.c +++ b/lib/efi_loader/efi_test.c @@ -9,8 +9,18 @@ int efi_test(efi_handle_t image_handle, struct efi_system_table *systable) { struct efi_simple_text_output_protocol *con_out = systable->con_out;
- struct efi_boot_services *boottime = systable->boottime;
- int ret;
- con_out->output_string(con_out, L"Hello, world!\n");
- ret = con_out->output_string(con_out, L"Hello, world!\n");
- /*
* We should really call EFI's exit() here but this crashes at present
* on sandbox due to the incorrect use of setjmp() and longjmp(). Once
What makes you think that setjmp and longjmp are incorrectly used? Couldn't the sandbox implementation of both create the problem?
Best regards
Heinrich Schuchardt
* we can figure out how to make that work, this can be enabled.
*/
if (0)
boottime->exit(image_handle, ret, 0, NULL);
return 0;
}

On 23.08.18 22:37, Heinrich Schuchardt wrote:
On 08/08/2018 11:54 AM, Simon Glass wrote:
The test should exit like any other EFI application, by calling exit() in the boot services API. But this crashes at present on sandbox. For now, add in the placeholder code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_test.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_test.c b/lib/efi_loader/efi_test.c index 4b8d49f324b..5401a0f4715 100644 --- a/lib/efi_loader/efi_test.c +++ b/lib/efi_loader/efi_test.c @@ -9,8 +9,18 @@ int efi_test(efi_handle_t image_handle, struct efi_system_table *systable) { struct efi_simple_text_output_protocol *con_out = systable->con_out;
- struct efi_boot_services *boottime = systable->boottime;
- int ret;
- con_out->output_string(con_out, L"Hello, world!\n");
- ret = con_out->output_string(con_out, L"Hello, world!\n");
- /*
* We should really call EFI's exit() here but this crashes at present
* on sandbox due to the incorrect use of setjmp() and longjmp(). Once
What makes you think that setjmp and longjmp are incorrectly used? Couldn't the sandbox implementation of both create the problem?
I agree, last time we tracked exit problems down to incorrect setjmp/longjmp implementations in sandbox.
Alex

Hi Alex,
On 26 August 2018 at 19:13, Alexander Graf agraf@suse.de wrote:
On 23.08.18 22:37, Heinrich Schuchardt wrote:
On 08/08/2018 11:54 AM, Simon Glass wrote:
The test should exit like any other EFI application, by calling exit() in the boot services API. But this crashes at present on sandbox. For now, add in the placeholder code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_test.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_test.c b/lib/efi_loader/efi_test.c index 4b8d49f324b..5401a0f4715 100644 --- a/lib/efi_loader/efi_test.c +++ b/lib/efi_loader/efi_test.c @@ -9,8 +9,18 @@ int efi_test(efi_handle_t image_handle, struct efi_system_table *systable) { struct efi_simple_text_output_protocol *con_out = systable->con_out;
- struct efi_boot_services *boottime = systable->boottime;
- int ret;
- con_out->output_string(con_out, L"Hello, world!\n");
- ret = con_out->output_string(con_out, L"Hello, world!\n");
- /*
* We should really call EFI's exit() here but this crashes at present
* on sandbox due to the incorrect use of setjmp() and longjmp(). Once
What makes you think that setjmp and longjmp are incorrectly used? Couldn't the sandbox implementation of both create the problem?
I agree, last time we tracked exit problems down to incorrect setjmp/longjmp implementations in sandbox.
Yes that's what I mean. I'll reword it a bit.
Regards, Simon

Sandbox only has 128MB of memory so we cannot relocate the device tree up to start at 128MB. Use 127MB instead, which should be safe.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
cmd/bootefi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 905e659da42..6481444cca6 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -158,8 +158,8 @@ static void *copy_fdt(void *fdt) fdt_size = ALIGN(fdt_size + EFI_PAGE_SIZE - 1, EFI_PAGE_SIZE); fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
- /* Safe fdt location is at 128MB */ - new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size; + /* Safe fdt location is at 127MB */ + new_fdt_addr = fdt_ram_start + (127 * 1024 * 1024) + fdt_size; if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_RUNTIME_SERVICES_DATA, fdt_pages, &new_fdt_addr) != EFI_SUCCESS) {

Sandbox only has 128MB of memory so we cannot relocate the device tree up to start at 128MB. Use 127MB instead, which should be safe.
Signed-off-by: Simon Glass sjg@chromium.org
Thanks, applied to efi-next
Alex

At present this function takes a pointer as its argument, then passes this to efi_allocate_pages(), which actually takes an address. It uses casts, which are not supported on sandbox.
Also the function calculates the FDT size rounded up to the neared EFI page size, then its caller recalculates the size and adds a bit more to it.
This function is much better written as something that works with addresses only, and returns both the address and the size of the relocated FDT.
Also, copy_fdt() returns NULL on error, but really should propagate the error from efi_allocate_pages(). To do this it needs to return an efi_status_t, not a void *.
Update the code in this way, so that it is easier to follow, and also supports sandbox.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: - Drop fdt_end variable in efi_install_fdt()
Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
cmd/bootefi.c | 79 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 29 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 6481444cca6..18378f2eacc 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -131,17 +131,30 @@ static void set_load_options(struct efi_loaded_image *loaded_image_info, loaded_image_info->load_options_size = size * 2; }
-static void *copy_fdt(void *fdt) +/** + * 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 + * 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 + */ +static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep) { - u64 fdt_size = fdt_totalsize(fdt); unsigned long fdt_ram_start = -1L, fdt_pages; + efi_status_t ret = 0; + void *fdt, *new_fdt; u64 new_fdt_addr; - void *new_fdt; + uint fdt_size; int i;
- for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - u64 ram_start = gd->bd->bi_dram[i].start; - u64 ram_size = gd->bd->bi_dram[i].size; + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + u64 ram_start = gd->bd->bi_dram[i].start; + u64 ram_size = gd->bd->bi_dram[i].size;
if (!ram_size) continue; @@ -154,30 +167,37 @@ static void *copy_fdt(void *fdt) * 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. */ - fdt_size += 4096; + 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; - if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_RUNTIME_SERVICES_DATA, fdt_pages, - &new_fdt_addr) != EFI_SUCCESS) { + ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, + EFI_RUNTIME_SERVICES_DATA, fdt_pages, + &new_fdt_addr); + if (ret != EFI_SUCCESS) { /* If we can't put it there, put it somewhere */ new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size); - if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_RUNTIME_SERVICES_DATA, fdt_pages, - &new_fdt_addr) != EFI_SUCCESS) { + ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, + EFI_RUNTIME_SERVICES_DATA, fdt_pages, + &new_fdt_addr); + if (ret != EFI_SUCCESS) { printf("ERROR: Failed to reserve space for FDT\n"); - return NULL; + goto done; } }
- new_fdt = (void*)(ulong)new_fdt_addr; + new_fdt = map_sysmem(new_fdt_addr, fdt_size); memcpy(new_fdt, fdt, fdt_totalsize(fdt)); fdt_set_totalsize(new_fdt, fdt_size);
- return new_fdt; + *fdt_addrp = new_fdt_addr; + *fdt_sizep = fdt_size; +done: + return ret; }
static efi_status_t efi_do_enter( @@ -250,22 +270,27 @@ static void efi_carve_out_dt_rsv(void *fdt) } }
-static efi_status_t efi_install_fdt(void *fdt) +static efi_status_t efi_install_fdt(ulong fdt_addr) { bootm_headers_t img = { 0 }; - ulong fdt_pages, fdt_size, fdt_start, fdt_end; + ulong fdt_pages, fdt_size, fdt_start; efi_status_t ret; + void *fdt;
+ fdt = map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt)) { printf("ERROR: invalid device tree\n"); return EFI_INVALID_PARAMETER; }
/* Prepare fdt for payload */ - fdt = copy_fdt(fdt); - if (!fdt) - return EFI_OUT_OF_RESOURCES; + ret = copy_fdt(&fdt_addr, &fdt_size); + 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; @@ -279,14 +304,12 @@ static efi_status_t efi_install_fdt(void *fdt) return EFI_OUT_OF_RESOURCES;
/* And reserve the space in the memory map */ - fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK; - fdt_end = ((ulong)fdt) + fdt_totalsize(fdt); - fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK; + fdt_start = fdt_addr; fdt_pages = fdt_size >> EFI_PAGE_SHIFT; - /* Give a bootloader the chance to modify the device tree */ - fdt_pages += 2; + ret = efi_add_memory_map(fdt_start, fdt_pages, EFI_BOOT_SERVICES_DATA, true); + return ret; }
@@ -506,7 +529,6 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) unsigned long addr; efi_status_t r; unsigned long fdt_addr; - void *fdt;
/* Allow unaligned memory access */ allow_unaligned(); @@ -527,8 +549,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (!fdt_addr && *argv[2] != '0') return CMD_RET_USAGE; /* Install device tree */ - fdt = map_sysmem(fdt_addr, 0); - r = efi_install_fdt(fdt); + r = efi_install_fdt(fdt_addr); if (r != EFI_SUCCESS) { printf("ERROR: failed to install device tree\n"); return CMD_RET_FAILURE;

At present this function takes a pointer as its argument, then passes this to efi_allocate_pages(), which actually takes an address. It uses casts, which are not supported on sandbox.
Also the function calculates the FDT size rounded up to the neared EFI page size, then its caller recalculates the size and adds a bit more to it.
This function is much better written as something that works with addresses only, and returns both the address and the size of the relocated FDT.
Also, copy_fdt() returns NULL on error, but really should propagate the error from efi_allocate_pages(). To do this it needs to return an efi_status_t, not a void *.
Update the code in this way, so that it is easier to follow, and also supports sandbox.
Signed-off-by: Simon Glass sjg@chromium.org
Thanks, applied to efi-next
Alex

Add some more verbose debugging when doing memory allocations. This might help to find bugs later.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ lib/efi_loader/efi_memory.c | 22 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b9e54f551a4..851d282f79d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -330,6 +330,7 @@ static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory); r = efi_allocate_pages(type, memory_type, pages, memory); + return EFI_EXIT(r); }
@@ -379,11 +380,25 @@ static efi_status_t EFIAPI efi_get_memory_map_ext( uint32_t *descriptor_version) { efi_status_t r; + int i, entries;
EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); r = efi_get_memory_map(memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); + entries = *memory_map_size / sizeof(struct efi_mem_desc); + debug(" memory_map_size=%zx (%lx entries)\n", *memory_map_size, + (ulong)(*memory_map_size / sizeof(struct efi_mem_desc))); + if (memory_map) { + for (i = 0; i < entries; i++) { + struct efi_mem_desc *desc = &memory_map[i]; + + debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n", + desc->type, (ulong)desc->physical_start, + (ulong)desc->virtual_start, + (ulong)desc->num_pages, (ulong)desc->attribute); + } + } return EFI_EXIT(r); }
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 967c3f733e4..607152bc4e7 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -151,6 +151,24 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, return EFI_CARVE_LOOP_AGAIN; }
+static void efi_mem_print(const char *name) +{ + struct list_head *lhandle; + + debug(" %s: memory map\n", name); + list_for_each(lhandle, &efi_mem) { + struct efi_mem_list *lmem = list_entry(lhandle, + struct efi_mem_list, link); + struct efi_mem_desc *desc = &lmem->desc; + + debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n", + desc->type, (ulong)desc->physical_start, + (ulong)desc->virtual_start, (ulong)desc->num_pages, + (ulong)desc->attribute); + } + debug("\n"); +} + uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, bool overlap_only_ram) { @@ -243,6 +261,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
/* And make sure memory is listed in descending order */ efi_mem_sort(); + efi_mem_print(__func__);
return start; } @@ -466,7 +485,8 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, map_entries++;
map_size = map_entries * sizeof(struct efi_mem_desc); - + debug("%s: map_size %lx, provided_map_size %lx\n", __func__, + (ulong)map_size, (ulong)provided_map_size); *memory_map_size = map_size;
if (provided_map_size < map_size)

On 08/08/2018 11:54 AM, Simon Glass wrote:
Add some more verbose debugging when doing memory allocations. This might help to find bugs later.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ lib/efi_loader/efi_memory.c | 22 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b9e54f551a4..851d282f79d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -330,6 +330,7 @@ static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory); r = efi_allocate_pages(type, memory_type, pages, memory);
- return EFI_EXIT(r);
}
@@ -379,11 +380,25 @@ static efi_status_t EFIAPI efi_get_memory_map_ext( uint32_t *descriptor_version) { efi_status_t r;
int i, entries;
EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); r = efi_get_memory_map(memory_map_size, memory_map, map_key, descriptor_size, descriptor_version);
entries = *memory_map_size / sizeof(struct efi_mem_desc);
debug(" memory_map_size=%zx (%lx entries)\n", *memory_map_size,
(ulong)(*memory_map_size / sizeof(struct efi_mem_desc)));
if (memory_map) {
for (i = 0; i < entries; i++) {
struct efi_mem_desc *desc = &memory_map[i];
debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n",
desc->type, (ulong)desc->physical_start,
(ulong)desc->virtual_start,
(ulong)desc->num_pages, (ulong)desc->attribute);
}
} return EFI_EXIT(r);
}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 967c3f733e4..607152bc4e7 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -151,6 +151,24 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, return EFI_CARVE_LOOP_AGAIN; }
+static void efi_mem_print(const char *name) +{
- struct list_head *lhandle;
- debug(" %s: memory map\n", name);
- list_for_each(lhandle, &efi_mem) {
struct efi_mem_list *lmem = list_entry(lhandle,
struct efi_mem_list, link);
struct efi_mem_desc *desc = &lmem->desc;
debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n",
For printing 64bit types, please, use PRIx64 and don't convert to ulong. I would prefer to see 0x at the beginning of hexadecimal output.
Best regards
Heinrich
desc->type, (ulong)desc->physical_start,
(ulong)desc->virtual_start, (ulong)desc->num_pages,
(ulong)desc->attribute);
- }
- debug("\n");
+}
uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, bool overlap_only_ram) { @@ -243,6 +261,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
/* And make sure memory is listed in descending order */ efi_mem_sort();
efi_mem_print(__func__);
return start;
} @@ -466,7 +485,8 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, map_entries++;
map_size = map_entries * sizeof(struct efi_mem_desc);
debug("%s: map_size %lx, provided_map_size %lx\n", __func__,
(ulong)map_size, (ulong)provided_map_size);
*memory_map_size = map_size;
if (provided_map_size < map_size)

Hi Heinich,
On 23 August 2018 at 14:49, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/08/2018 11:54 AM, Simon Glass wrote:
Add some more verbose debugging when doing memory allocations. This might help to find bugs later.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ lib/efi_loader/efi_memory.c | 22 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b9e54f551a4..851d282f79d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -330,6 +330,7 @@ static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory); r = efi_allocate_pages(type, memory_type, pages, memory);
return EFI_EXIT(r);
}
@@ -379,11 +380,25 @@ static efi_status_t EFIAPI efi_get_memory_map_ext( uint32_t *descriptor_version) { efi_status_t r;
int i, entries; EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); r = efi_get_memory_map(memory_map_size, memory_map, map_key, descriptor_size, descriptor_version);
entries = *memory_map_size / sizeof(struct efi_mem_desc);
debug(" memory_map_size=%zx (%lx entries)\n", *memory_map_size,
(ulong)(*memory_map_size / sizeof(struct efi_mem_desc)));
if (memory_map) {
for (i = 0; i < entries; i++) {
struct efi_mem_desc *desc = &memory_map[i];
debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n",
desc->type, (ulong)desc->physical_start,
(ulong)desc->virtual_start,
(ulong)desc->num_pages, (ulong)desc->attribute);
}
} return EFI_EXIT(r);
}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 967c3f733e4..607152bc4e7 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -151,6 +151,24 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, return EFI_CARVE_LOOP_AGAIN; }
+static void efi_mem_print(const char *name) +{
struct list_head *lhandle;
debug(" %s: memory map\n", name);
list_for_each(lhandle, &efi_mem) {
struct efi_mem_list *lmem = list_entry(lhandle,
struct efi_mem_list, link);
struct efi_mem_desc *desc = &lmem->desc;
debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n",
For printing 64bit types, please, use PRIx64 and don't convert to ulong.
OK I can do that. Also please check out Masahiro's series which goes in the opposite direction.
I would prefer to see 0x at the beginning of hexadecimal output.
Actually the U-Boot convention is to print hex values without 0x. U-Boot almost always uses hex.
Best regards
Heinrich
Regards, Simon

On 08.08.18 11:54, Simon Glass wrote:
Add some more verbose debugging when doing memory allocations. This might help to find bugs later.
Signed-off-by: Simon Glass sjg@chromium.org
I think I asked in some earlier version whether you double checked that this patch does not increase the binary size of a non-debug enabled build.
Please note so in the commit message, so that it's clear at first glance.
Thanks,
Alex
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ lib/efi_loader/efi_memory.c | 22 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b9e54f551a4..851d282f79d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -330,6 +330,7 @@ static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory); r = efi_allocate_pages(type, memory_type, pages, memory);
- return EFI_EXIT(r);
}
@@ -379,11 +380,25 @@ static efi_status_t EFIAPI efi_get_memory_map_ext( uint32_t *descriptor_version) { efi_status_t r;
int i, entries;
EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, map_key, descriptor_size, descriptor_version); r = efi_get_memory_map(memory_map_size, memory_map, map_key, descriptor_size, descriptor_version);
entries = *memory_map_size / sizeof(struct efi_mem_desc);
debug(" memory_map_size=%zx (%lx entries)\n", *memory_map_size,
(ulong)(*memory_map_size / sizeof(struct efi_mem_desc)));
if (memory_map) {
for (i = 0; i < entries; i++) {
struct efi_mem_desc *desc = &memory_map[i];
debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n",
desc->type, (ulong)desc->physical_start,
(ulong)desc->virtual_start,
(ulong)desc->num_pages, (ulong)desc->attribute);
}
} return EFI_EXIT(r);
}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 967c3f733e4..607152bc4e7 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -151,6 +151,24 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, return EFI_CARVE_LOOP_AGAIN; }
+static void efi_mem_print(const char *name) +{
- struct list_head *lhandle;
- debug(" %s: memory map\n", name);
- list_for_each(lhandle, &efi_mem) {
struct efi_mem_list *lmem = list_entry(lhandle,
struct efi_mem_list, link);
struct efi_mem_desc *desc = &lmem->desc;
debug(" type %d, phys %lx, virt %lx, num_pages %lx, attribute %lx\n",
desc->type, (ulong)desc->physical_start,
(ulong)desc->virtual_start, (ulong)desc->num_pages,
(ulong)desc->attribute);
- }
- debug("\n");
+}
uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, bool overlap_only_ram) { @@ -243,6 +261,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
/* And make sure memory is listed in descending order */ efi_mem_sort();
efi_mem_print(__func__);
return start;
} @@ -466,7 +485,8 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, map_entries++;
map_size = map_entries * sizeof(struct efi_mem_desc);
debug("%s: map_size %lx, provided_map_size %lx\n", __func__,
(ulong)map_size, (ulong)provided_map_size);
*memory_map_size = map_size;
if (provided_map_size < map_size)

From: Alexander Graf agraf@suse.de
The fs_read() function wants to get an address rather than the pointer to a buffer.
So let's convert the passed buffer from pointer back a the address to make efi_loader on sandbox happier.
Signed-off-by: Alexander Graf agraf@suse.de Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: None Changes in v2: None
lib/efi_loader/efi_file.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index e6a15bcb523..2107730ba5a 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -9,6 +9,7 @@ #include <charset.h> #include <efi_loader.h> #include <malloc.h> +#include <mapmem.h> #include <fs.h>
/* GUID for file system information */ @@ -232,8 +233,10 @@ static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, void *buffer) { loff_t actread; + /* fs_read expects buffer address, not pointer */ + uintptr_t buffer_addr = (uintptr_t)map_to_sysmem(buffer);
- if (fs_read(fh->path, (ulong)buffer, fh->offset, + if (fs_read(fh->path, buffer_addr, fh->offset, *buffer_size, &actread)) return EFI_DEVICE_ERROR;

From: Alexander Graf agraf@suse.de
The fs_read() function wants to get an address rather than the pointer to a buffer.
So let's convert the passed buffer from pointer back a the address to make efi_loader on sandbox happier.
Signed-off-by: Alexander Graf agraf@suse.de Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Simon Glass sjg@chromium.org
Thanks, applied to efi-next
Alex

Enable this for sandbox since it passes now.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: None Changes in v8: - Drop 'efi: Adjust memory handling to support sandbox' - Drop 'efi: sandbox: Add relocation constants' - Drop 'sandbox: smbios: Update to support sandbox' - Expand series substantially to support bootefi selftest - Rebase to master - Rebase to master, bringing in all EFI changes
Changes in v7: None Changes in v6: None Changes in v5: None Changes in v4: - Rebase to master - Update SPDX tags
Changes in v3: - Rebase to master
Changes in v2: - Rebase to master
configs/sandbox_defconfig | 1 + lib/efi_selftest/Kconfig | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 61302909191..7e75643bfe6 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -205,3 +205,4 @@ CONFIG_UT_ENV=y CONFIG_UT_OVERLAY=y CONFIG_SMEM=y CONFIG_SANDBOX_SMEM=y +CONFIG_CMD_BOOTEFI_SELFTEST=y diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig index b52696778dd..59f9f36801c 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

On 08.08.18 11:54, Simon Glass wrote:
A limitation of the EFI loader at present is that it does not build with sandbox. This makes it hard to write tests, since sandbox is used for most testing in U-Boot.
This series enables the EFI loader feature. It allows sandbox to build and run a trivial function which calls the EFI API to output a message.
Also included in v8 is support for running the full EFI self tests. These run OK with some tweaks to a few parts of the code.
With v9, various EFI patches have been applied which change things. This series includes a partial review of one, which makes 'bootefi test' work. But there are still problems with 'bootefi selftest':
$ sandbox/u-boot -D -c "bootefi selftest" ... Executing 'block device' /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_block_device.c(385): TODO: Wrong volume label 'xxa1', expected 'U-BOOT TEST' map_to_sysmem: Added map from 00007ffd0782d2a0 to 8000000 phys_to_virt: Used map from 8000000 to 00007ffd0782d2a0 writing /u-boot.txt find_tag: Used map from 00007ffd0782d2a0 to 8000000 phys_to_virt: Used map from 8000000 to 00007ffd0782d2a0 /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_block_device.c(458): ERROR: Unexpected file content /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest.c(109): ERROR: Executing 'block device' failed
...
Executing 'simple network protocol' DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_snp.c(311): ERROR: Timeout occurred /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest.c(109): ERROR: Executing 'simple network protocol' failed
This series is at u-boot-dm/efi-working
Thanks a lot again for pushing this forward.
My ultimate goal is that sandbox is not a special case, but just "yet another" target we support.
That means things like a special "bootefi test" command really don't make any sense and should just go.
The other thing we need to make sure is that every divergence we find between sandbox and non-sandbox behavior potentially hints at a real problem:
- If exit doesn't work, don't just disable it, instead please debug why and resolve it for real.
- If tests are failing that really shouldn't fail, please figure out why. Maybe they used the stack in some cases which breaks your mapping logic?
At the end of the day, I want to have as little divergence between the sandbox target and a real x86_64 QEMU target for example. As long as we don't exit boot services, both should really behave the same.
Alex

Hi Alex,
On 26 August 2018 at 19:28, Alexander Graf agraf@suse.de wrote:
On 08.08.18 11:54, Simon Glass wrote:
A limitation of the EFI loader at present is that it does not build with sandbox. This makes it hard to write tests, since sandbox is used for most testing in U-Boot.
This series enables the EFI loader feature. It allows sandbox to build and run a trivial function which calls the EFI API to output a message.
Also included in v8 is support for running the full EFI self tests. These run OK with some tweaks to a few parts of the code.
With v9, various EFI patches have been applied which change things. This series includes a partial review of one, which makes 'bootefi test' work. But there are still problems with 'bootefi selftest':
$ sandbox/u-boot -D -c "bootefi selftest" ... Executing 'block device' /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_block_device.c(385): TODO: Wrong volume label 'xxa1', expected 'U-BOOT TEST' map_to_sysmem: Added map from 00007ffd0782d2a0 to 8000000 phys_to_virt: Used map from 8000000 to 00007ffd0782d2a0 writing /u-boot.txt find_tag: Used map from 00007ffd0782d2a0 to 8000000 phys_to_virt: Used map from 8000000 to 00007ffd0782d2a0 /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_block_device.c(458): ERROR: Unexpected file content /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest.c(109): ERROR: Executing 'block device' failed
I was able to fix this one.
...
Executing 'simple network protocol' DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover DHCP Discover /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest_snp.c(311): ERROR: Timeout occurred /home/sjg/c/src/third_party/u-boot/files/lib/efi_selftest/efi_selftest.c(109): ERROR: Executing 'simple network protocol' failed
I am not sure what is going on here. I think it is best that Heinrich takes a look once this stuff lands and before we enable the tests on travis-ci.
This series is at u-boot-dm/efi-working
Thanks a lot again for pushing this forward.
My ultimate goal is that sandbox is not a special case, but just "yet another" target we support.
That means things like a special "bootefi test" command really don't make any sense and should just go.
I've renamed this to 'bootefi ping'. While I understand your POV, I feel that a simple sanity check is useful. At present 'bootefi selftest' quits sandbox when it finishes, so itis not really working the way it should anyway.
The other thing we need to make sure is that every divergence we find between sandbox and non-sandbox behavior potentially hints at a real problem:
- If exit doesn't work, don't just disable it, instead please debug
why and resolve it for real.
- If tests are failing that really shouldn't fail, please figure out
why. Maybe they used the stack in some cases which breaks your mapping logic?
Please see above. The tests all used to pass 6 months ago when I revised this work. I think we should get something in and then do some more work.
At the end of the day, I want to have as little divergence between the sandbox target and a real x86_64 QEMU target for example. As long as we don't exit boot services, both should really behave the same.
Fair enough, but Rome wasn't built in a day. I think that's my main problem with this painful year-long process, that you want everything to be done in one series, instead of collaboratively moving towards the ultimate end goal. We would have had this running a long time ago if we have just taken an incremental approach.
Regards, Simon
participants (4)
-
Alexander Graf
-
Heinrich Schuchardt
-
Simon Glass
-
Tom Rini