[PATCH 00/17] ulib: Add multi-arch demo and EFI app support
From: Simon Glass <simon.glass@canonical.com> This series extends the ulib demo to ARM64 and RISC-V64, following the existing x86 pattern. Each architecture re-links U-Boot with the example objects so the demo's main() overrides the weak default. RISC-V requires an additional prelink-riscv step to resolve GOT entries in the static PIE binary. EFI app builds are also supported on all three architectures, producing a demo-app.efi binary that can boot under QEMU with UEFI firmware. A build_api.py fix handles empty archives that appear in cross-compiled EFI builds. All new demos have pytests that boot under QEMU and verify the expected output. A new 'localqemu' pytest marker skips these tests in lab mode, since they launch their own QEMU rather than using the lab's managed U-Boot instance. This also updates the build-qemu script to support RISC-V. Simon Glass (17): ulib: Skip empty archives in build_api.py objcopy step ulib: Build the static test binary by default ulib: test: Add localqemu marker to skip tests in lab ulib: test: Detect clang instead of checking binaries ulib: Extract common example build rules for demo ulib: x86: Add demo pytest for qemu-x86_64_nospl ulib: arm: Add demo binary and examples for qemu_arm64 ulib: riscv: Add demo binary and examples for qemu-riscv64 ulib: efi: Use ulib_has_main() in efi_main() ulib: x86: Add demo-app.efi target for EFI app builds ulib: x86: Enable examples for efi-x86_app64 ulib: test: Add a pytest for efi-x86_app64 demo ulib: arm: Enable demo EFI binary for efi-arm_app64 ulib: riscv: Enable demo EFI binary for efi-riscv_app64 ulib: test: Add EFI demo pytest for arm64 ulib: test: Add EFI demo pytest for riscv64 ulib: scripts: Add RISC-V support to build-qemu Makefile | 3 + arch/arm/Makefile | 6 + arch/riscv/Makefile | 7 + arch/x86/Makefile | 31 +--- configs/efi-arm_app64_defconfig | 2 + configs/efi-riscv_app64_defconfig | 2 + configs/efi-x86_app64_defconfig | 2 + configs/qemu-riscv64_defconfig | 2 + configs/qemu-x86_64_nospl_defconfig | 2 + configs/qemu_arm64_defconfig | 2 + doc/develop/pytest/usage.rst | 9 ++ examples/ulib/Kbuild | 2 +- lib/efi_client/efi_main.c | 3 +- scripts/Makefile.ulib-example | 63 ++++++++ scripts/build-qemu | 23 ++- scripts/build_api.py | 6 + test/py/conftest.py | 15 ++ test/py/pytest.ini | 1 + test/py/tests/test_ulib.py | 237 ++++++++++++++++++++++++---- 19 files changed, 351 insertions(+), 67 deletions(-) create mode 100644 scripts/Makefile.ulib-example -- 2.43.0 base-commit: a0102ffa945d6a92b52167d11faa06a4e52edd31 branch: ulibb
From: Simon Glass <simon.glass@canonical.com> When CONFIG_ULIB is enabled for cross-compiled EFI builds, some built-in.o archives are empty (e.g. arch/riscv/cpu/generic/built-in.o for efi-riscv_app64). The objcopy --redefine-sym command fails on empty files. Skip them by checking the file size before calling objcopy, producing an empty output file to keep the archive list consistent. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/build_api.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/build_api.py b/scripts/build_api.py index aa8b4f16a1a..a82cd83503c 100755 --- a/scripts/build_api.py +++ b/scripts/build_api.py @@ -356,6 +356,12 @@ class SymbolRedefiner: Returns: bool: True if file was modified, False otherwise """ + # Skip empty archives (objcopy cannot process them) + if os.path.getsize(path) == 0: + with open(outfile, 'wb'): + pass + return False + # Always run objcopy to apply redefinitions self.redefine_file(path, outfile) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The ulib_test_static binary has a build rule in the Makefile but is not added to the INPUTS targets, so it is never built automatically. The test_ulib_static pytest then fails because the binary does not exist. Add test/ulib/ulib_test_static to the INPUTS line for sandbox builds, since the binary uses HOSTCC and can only run on the host. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index f1fdace661d..be1814e009c 100644 --- a/Makefile +++ b/Makefile @@ -1060,6 +1060,9 @@ ifneq ($(cc-name),clang) ifeq ($(NO_LIBS),) INPUTS-$(CONFIG_ULIB_SHARED_LIB) += libu-boot.so test/ulib/ulib_test INPUTS-$(CONFIG_ULIB) += libu-boot.a +ifdef CONFIG_SANDBOX +INPUTS-$(CONFIG_ULIB) += test/ulib/ulib_test_static +endif ifdef CONFIG_EXAMPLES ifdef CONFIG_SANDBOX INPUTS-$(CONFIG_ULIB) += examples_ulib examples_rust -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Some tests launch their own QEMU process via subprocess rather than using the lab's managed U-Boot instance. In lab mode the locally launched QEMU produces empty output, causing assertion failures. There isn't really any point in running these tests on the lab since they run happily in CI. Add a 'localqemu' pytest marker and the corresponding setup_localqemu() hook in conftest.py that skips marked tests when ubconfig.role is set. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- doc/develop/pytest/usage.rst | 9 +++++++++ test/py/conftest.py | 15 +++++++++++++++ test/py/pytest.ini | 1 + test/py/tests/test_ulib.py | 1 + 4 files changed, 26 insertions(+) diff --git a/doc/develop/pytest/usage.rst b/doc/develop/pytest/usage.rst index 9ba6ed429a9..32037d47dcd 100644 --- a/doc/develop/pytest/usage.rst +++ b/doc/develop/pytest/usage.rst @@ -596,3 +596,12 @@ option not to be set. The following annotation requires CONFIG_RISCV=n: .. code-block:: python @pytest.mark.notbuildconfigspec('riscv') + +The localqemu marker indicates that a test launches its own QEMU process +rather than using the lab's managed U-Boot instance. Tests with this marker +are automatically skipped when running in lab mode (i.e. when ``--role`` is +set), since the locally launched QEMU is not available in that environment: + +.. code-block:: python + + @pytest.mark.localqemu diff --git a/test/py/conftest.py b/test/py/conftest.py index eb64f2a82be..b7a03669751 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -818,6 +818,20 @@ def setup_role(item): if required_roles and ubconfig.role not in required_roles: pytest.skip(f'board "{ubconfig.role}" not supported') +def setup_localqemu(item): + """Process any 'localqemu' marker for a test. + + Skip this test if running in lab mode (i.e. role is set), since the + test launches its own QEMU rather than using the lab's U-Boot. + + Args: + item (pytest.Item): The pytest test item + """ + for _ in item.iter_markers('localqemu'): + if ubconfig.role: + pytest.skip('test requires local QEMU (not supported in lab)') + return + def start_test_section(item): anchors[item.name] = log.start_section(item.name) @@ -840,6 +854,7 @@ def pytest_runtest_setup(item): setup_requiredtool(item) setup_singlethread(item) setup_role(item) + setup_localqemu(item) def pytest_runtest_protocol(item, nextitem): """pytest hook: Called to execute a test. diff --git a/test/py/pytest.ini b/test/py/pytest.ini index 0a0268ec247..bebb22cd3d6 100644 --- a/test/py/pytest.ini +++ b/test/py/pytest.ini @@ -14,3 +14,4 @@ markers = slow: U-Boot: Specific test will run slowly. singlethread: Cannot run in parallel role: U-Boot: Indicates the lab 'role' which can execute this test + localqemu: U-Boot: Test launches its own QEMU, skip in lab mode diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index aff586c4d79..16af47fd840 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -210,6 +210,7 @@ def test_ulib_api_header(ubman): assert 'ub_snprintf(char *buf, size_t size, const char *fmt, ...)' in out assert 'ub_vprintf(const char *fmt, va_list args)' in out +@pytest.mark.localqemu @pytest.mark.boardspec('qemu-x86') @pytest.mark.buildconfigspec("examples") def test_ulib_demo_rom(ubman): -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The ulib tests skip when build artefacts are missing, with a comment saying this happens with clang. This is fragile since a missing binary for any other reason is silently skipped. Use ubman.config.buildconfig to check for CONFIG_CC_IS_CLANG and skip explicitly when clang is detected. Otherwise assert that the expected artefacts exist, so a genuine build failure is reported as a test failure. Add boardspec('sandbox') to test_ulib_shared since it runs a host binary directly, which only works on sandbox. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/py/tests/test_ulib.py | 41 ++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 16af47fd840..957417ede31 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -16,6 +16,7 @@ def check_output(out): assert 'Uses libc printf before ulib_init' in out assert 'another printf()' in out +@pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec("ulib") def test_ulib_shared(ubman): """Test the ulib shared library test program""" @@ -23,9 +24,11 @@ def test_ulib_shared(ubman): build = ubman.config.build_dir prog = os.path.join(build, 'test', 'ulib', 'ulib_test') - # Skip test if ulib_test doesn't exist (clang) - if not os.path.exists(prog): - pytest.skip('ulib_test not found - library build may be disabled') + # ulib is not yet supported with clang + if ubman.config.buildconfig.get('config_cc_is_clang'): + pytest.skip('ulib not supported with clang') + + assert os.path.exists(prog), 'ulib_test not found in build dir' out = utils.run_and_log(ubman, [prog], cwd=build) check_output(out) @@ -38,9 +41,11 @@ def test_ulib_static(ubman): build = ubman.config.build_dir prog = os.path.join(build, 'test', 'ulib', 'ulib_test_static') - # Skip test if ulib_test_static doesn't exist (clang) - if not os.path.exists(prog): - pytest.skip('ulib_test_static not found - library build may be disabled') + # ulib is not yet supported with clang + if ubman.config.buildconfig.get('config_cc_is_clang'): + pytest.skip('ulib not supported with clang') + + assert os.path.exists(prog), 'ulib_test_static not found in build directory' out = utils.run_and_log(ubman, [prog]) check_output(out) @@ -115,9 +120,11 @@ def test_ulib_demos(ubman): examples = os.path.join(src, 'examples', 'ulib') test_program = os.path.join(build, 'test', 'ulib', 'ulib_test') - # Skip test if ulib_test doesn't exist (clang) - if not os.path.exists(test_program): - pytest.skip('ulib_test not found - library build may be disabled') + # ulib is not yet supported with clang + if ubman.config.buildconfig.get('config_cc_is_clang'): + pytest.skip('ulib not supported with clang') + + assert os.path.exists(test_program), 'ulib_test not found in build dir' # Build the demo programs - clean first to ensure fresh build, since this # test is run in the source directory @@ -148,9 +155,11 @@ def test_ulib_rust_demos(ubman): examples = os.path.join(src, 'examples', 'rust') test_program = os.path.join(build, 'test', 'ulib', 'ulib_test') - # Skip test if ulib_test doesn't exist (clang) - if not os.path.exists(test_program): - pytest.skip('ulib_test not found - library build may be disabled') + # ulib is not yet supported with clang + if ubman.config.buildconfig.get('config_cc_is_clang'): + pytest.skip('ulib not supported with clang') + + assert os.path.exists(test_program), 'ulib_test not found in build dir' # Check if cargo is available try: @@ -183,9 +192,11 @@ def test_ulib_api_header(ubman): hdr = os.path.join(ubman.config.build_dir, 'include', 'u-boot-api.h') - # Skip if header doesn't exist (clang) - if not os.path.exists(hdr): - pytest.skip('u-boot-api.h not found - library build may be disabled') + # ulib is not yet supported with clang + if ubman.config.buildconfig.get('config_cc_is_clang'): + pytest.skip('ulib not supported with clang') + + assert os.path.exists(hdr), 'u-boot-api.h not found in build directory' # Read and verify header content with open(hdr, 'r', encoding='utf-8') as inf: -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The example build rules (link command, object lists, objcopy, and final-target logic) are duplicated in each arch Makefile. As more architectures gain ulib support, this duplication grows. Extract the common rules into scripts/Makefile.ulib-example, so that each arch's Makefile sets EXAMPLE_ARCH (and optionally EXAMPLE_APPEND_DTB or EXAMPLE_POST_LINK), then includes the shared file. Convert x86 to use it. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Makefile | 31 ++------------------- scripts/Makefile.ulib-example | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 scripts/Makefile.ulib-example diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 7df20c43de1..661e75fbdaa 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -65,34 +65,7 @@ u-boot-x86-reset16.bin: u-boot-x86-16bit.elf FORCE $(call if_changed,objcopy) endif -# x86 example targets: re-link U-Boot with example objects providing main() -# -# The example .o files are compiled via kbuild (examples/ulib/Kbuild). -# This re-links u-boot with those objects so the example's strong main() -# overrides the weak one in board_r.c, using the shared u-boot-link helper. ifdef CONFIG_EXAMPLES -INPUTS-$(CONFIG_ULIB) += examples_x86 - -PHONY += examples_x86 - -X86_EXAMPLES := demo - -quiet_cmd_u-boot-example = LD $@ - cmd_u-boot-example = $(call u-boot-link,$(example-objs),$@.map) - -# Per-example object lists (matches examples/ulib/Makefile:demo_objs) -example-demo-objs := examples/ulib/demo.o examples/ulib/demo_helper.o - -# Link each example ELF (depends on u-boot to ensure archives exist) -examples/ulib/demo: $(example-demo-objs) u-boot FORCE - $(eval example-objs := $(example-demo-objs)) - $(call if_changed,u-boot-example) - -# Binary targets (same objcopy flags as u-boot-nodtb.bin) -OBJCOPYFLAGS_demo-nodtb.bin = $(OBJCOPYFLAGS_u-boot-nodtb.bin) -examples/ulib/demo-nodtb.bin: examples/ulib/demo FORCE - $(call if_changed,objcopy) - -examples_x86: $(foreach e,$(X86_EXAMPLES),examples/ulib/$(e)-nodtb.bin) FORCE - @: +EXAMPLE_ARCH := x86 +include scripts/Makefile.ulib-example endif diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example new file mode 100644 index 00000000000..fd7c60e4483 --- /dev/null +++ b/scripts/Makefile.ulib-example @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Common build rules for ulib example programs. +# +# Re-links U-Boot with example objects providing main(). The example .o +# files are compiled via kbuild (examples/ulib/Kbuild). This re-links +# u-boot with those objects so the example's strong main() overrides the +# weak one in board_r.c, using the shared u-boot-link helper. +# +# Required variables (set by arch Makefile before including): +# EXAMPLE_ARCH - architecture name (x86, arm, riscv, ...) +# +# Optional variables: +# EXAMPLE_APPEND_DTB - set to y to append DTB to the binary +# EXAMPLE_POST_LINK - extra recipe line run after linking the ELF + +INPUTS-$(CONFIG_ULIB) += examples_$(EXAMPLE_ARCH) +PHONY += examples_$(EXAMPLE_ARCH) + +ULIB_EXAMPLES := demo + +quiet_cmd_u-boot-example = LD $@ + cmd_u-boot-example = $(call u-boot-link,$(example-objs),$@.map) + +# Per-example object lists (matches examples/ulib/Kbuild) +example-demo-objs := examples/ulib/demo.o examples/ulib/demo_helper.o + +# Link each example ELF (depends on u-boot to ensure archives exist) +examples/ulib/demo: $(example-demo-objs) u-boot FORCE + $(eval example-objs := $(example-demo-objs)) + $(call if_changed,u-boot-example) + $(EXAMPLE_POST_LINK) + +# Binary target (without DTB) +OBJCOPYFLAGS_demo-nodtb.bin = $(OBJCOPYFLAGS_u-boot-nodtb.bin) +examples/ulib/demo-nodtb.bin: examples/ulib/demo FORCE + $(call if_changed,objcopy) + +ifeq ($(EXAMPLE_APPEND_DTB),y) +# Binary with DTB appended +examples/ulib/demo.bin: examples/ulib/demo-nodtb.bin dts/dt.dtb FORCE + $(call if_changed,cat) + +examples_$(EXAMPLE_ARCH): \ + $(foreach e,$(ULIB_EXAMPLES),examples/ulib/$(e).bin) FORCE + @: +else +examples_$(EXAMPLE_ARCH): \ + $(foreach e,$(ULIB_EXAMPLES),examples/ulib/$(e)-nodtb.bin) FORCE + @: +endif -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Enable CONFIG_ULIB and CONFIG_EXAMPLES in qemu-x86_64_nospl_defconfig so that the demo.rom binary is built. Add test_ulib_demo_rom_64() which boots demo.rom under qemu-system-x86_64 and verifies the expected output, following the same pattern as the existing qemu-x86 test. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/qemu-x86_64_nospl_defconfig | 2 ++ test/py/tests/test_ulib.py | 37 ++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/configs/qemu-x86_64_nospl_defconfig b/configs/qemu-x86_64_nospl_defconfig index 4452b8c6e0a..950d2d2e419 100644 --- a/configs/qemu-x86_64_nospl_defconfig +++ b/configs/qemu-x86_64_nospl_defconfig @@ -79,4 +79,6 @@ CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_GENERATE_ACPI_TABLE=y CONFIG_CMD_DHRYSTONE=y # CONFIG_GZIP is not set +CONFIG_ULIB=y +CONFIG_EXAMPLES=y CONFIG_UNIT_TEST=y diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 957417ede31..b1ce5792801 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -221,19 +221,26 @@ def test_ulib_api_header(ubman): assert 'ub_snprintf(char *buf, size_t size, const char *fmt, ...)' in out assert 'ub_vprintf(const char *fmt, va_list args)' in out -@pytest.mark.localqemu -@pytest.mark.boardspec('qemu-x86') -@pytest.mark.buildconfigspec("examples") -def test_ulib_demo_rom(ubman): - """Test the ulib demo ROM image under QEMU x86.""" +def run_x86_rom_demo(ubman, qemu_binary): + """Boot the demo ROM image under QEMU and check for expected output. + + Locates demo.rom in the build directory, launches the given QEMU + binary with it, and asserts that the expected demo output is present. + + Args: + ubman (ConsoleBase): Test fixture providing build directory + etc. + qemu_binary (str): QEMU system binary + (e.g. 'qemu-system-i386') + """ build = ubman.config.build_dir demo_rom = os.path.join(build, 'demo.rom') assert os.path.exists(demo_rom), 'demo.rom not found in build directory' - assert shutil.which('qemu-system-i386'), 'qemu-system-i386 not found' + assert shutil.which(qemu_binary), f'{qemu_binary} not found' + + cmd = [qemu_binary, '-bios', demo_rom, '-nographic', '-no-reboot'] - cmd = ['qemu-system-i386', '-bios', demo_rom, '-nographic', - '-no-reboot'] with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: try: @@ -250,3 +257,17 @@ def test_ulib_demo_rom(ubman): assert 'helper: Adding 42 + 13 = 55' in out assert '=================================' in out assert 'Demo complete' in out + +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu-x86') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_rom(ubman): + """Test the ulib demo ROM image under QEMU x86.""" + run_x86_rom_demo(ubman, 'qemu-system-i386') + +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu-x86_64_nospl') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_rom_64(ubman): + """Test the ulib demo ROM image under QEMU x86_64.""" + run_x86_rom_demo(ubman, 'qemu-system-x86_64') -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add build infrastructure to compile and link the ulib demo for ARM64. In this case, arch/arm/Makefile re-links U-Boot with example objects using the u-boot-link helper, so the example's strong main() overrides the weak default, then creates demo-nodtb.bin and appends the devicetree to produce demo.bin Unlike x86 which uses a ROM image, ARM64 produces a binary that is loaded directly via QEMU's -bios parameter. Enable CONFIG_EXAMPLES in qemu_arm64_defconfig so the demo is built by default. Add a pytest which boots demo.bin under qemu-system-aarch64 and verifies the expected output. Also refactor the test code to share QEMU demo helpers between x86 and ARM64: run_qemu_demo() handles subprocess execution with timeout, and assert_demo_output() validates the expected output strings. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/arm/Makefile | 6 ++++ configs/qemu_arm64_defconfig | 2 ++ examples/ulib/Kbuild | 2 +- test/py/tests/test_ulib.py | 68 +++++++++++++++++++++++++++--------- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 90e7d33c881..9c521a11f29 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -124,5 +124,11 @@ ifneq (,$(filter $(SOC), kirkwood)) libs-y += arch/arm/mach-mvebu/ endif +ifdef CONFIG_EXAMPLES +EXAMPLE_ARCH := arm +EXAMPLE_APPEND_DTB := y +include scripts/Makefile.ulib-example +endif + # deprecated -include $(machdirs)/config.mk diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig index 4e0bfa3a572..507c08bc514 100644 --- a/configs/qemu_arm64_defconfig +++ b/configs/qemu_arm64_defconfig @@ -76,3 +76,5 @@ CONFIG_TPM_PCR_ALLOCATE=y CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE=y CONFIG_UTHREAD=y CONFIG_UNIT_TEST=y +CONFIG_ULIB=y +CONFIG_EXAMPLES=y diff --git a/examples/ulib/Kbuild b/examples/ulib/Kbuild index 88d6a805b91..f1f0131ae11 100644 --- a/examples/ulib/Kbuild +++ b/examples/ulib/Kbuild @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Example objects for x86 platforms (compiled via kbuild, not linked into +# Example objects for platform builds (compiled via kbuild, not linked into # u-boot). The standalone Makefile is used for sandbox builds instead. extra-y += demo.o demo_helper.o diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index b1ce5792801..94d40256c59 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -221,6 +221,39 @@ def test_ulib_api_header(ubman): assert 'ub_snprintf(char *buf, size_t size, const char *fmt, ...)' in out assert 'ub_vprintf(const char *fmt, va_list args)' in out +def assert_demo_output(out): + """Assert that demo output contains expected strings. + + Args: + out (str): Decoded output string from QEMU + """ + assert 'U-Boot Library Demo Helper' in out + assert '==========================' in out + assert 'U-Boot version:' in out + assert 'helper: Adding 42 + 13 = 55' in out + assert '=================================' in out + assert 'Demo complete' in out + +def run_qemu_demo(qemu_cmd): + """Run a ulib demo under QEMU and return output. + + Args: + qemu_cmd (list): QEMU command and arguments (e.g. + ['qemu-system-i386', '-bios', 'demo.rom', ...]) + + Returns: + str: Decoded stdout from QEMU + """ + with subprocess.Popen(qemu_cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) as proc: + try: + stdout, _ = proc.communicate(timeout=5) + except subprocess.TimeoutExpired: + proc.kill() + stdout, _ = proc.communicate() + + return stdout.decode('utf-8', errors='replace') + def run_x86_rom_demo(ubman, qemu_binary): """Boot the demo ROM image under QEMU and check for expected output. @@ -240,23 +273,8 @@ def run_x86_rom_demo(ubman, qemu_binary): assert shutil.which(qemu_binary), f'{qemu_binary} not found' cmd = [qemu_binary, '-bios', demo_rom, '-nographic', '-no-reboot'] - - with subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) as proc: - try: - stdout, _ = proc.communicate(timeout=5) - except subprocess.TimeoutExpired: - proc.kill() - stdout, _ = proc.communicate() - - out = stdout.decode('utf-8', errors='replace') - - assert 'U-Boot Library Demo Helper' in out - assert '==========================' in out - assert 'U-Boot version:' in out - assert 'helper: Adding 42 + 13 = 55' in out - assert '=================================' in out - assert 'Demo complete' in out + out = run_qemu_demo(cmd) + assert_demo_output(out) @pytest.mark.localqemu @pytest.mark.boardspec('qemu-x86') @@ -271,3 +289,19 @@ def test_ulib_demo_rom(ubman): def test_ulib_demo_rom_64(ubman): """Test the ulib demo ROM image under QEMU x86_64.""" run_x86_rom_demo(ubman, 'qemu-system-x86_64') + +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu_arm64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_arm64(ubman): + """Test the ulib demo binary under QEMU ARM64.""" + build = ubman.config.build_dir + demo_bin = os.path.join(build, 'examples', 'ulib', 'demo.bin') + + assert os.path.exists(demo_bin), 'demo.bin not found in build directory' + assert shutil.which('qemu-system-aarch64'), 'qemu-system-aarch64 not found' + + cmd = ['qemu-system-aarch64', '-machine', 'virt', '-cpu', 'cortex-a57', + '-nographic', '-no-reboot', '-bios', demo_bin] + out = run_qemu_demo(cmd) + assert_demo_output(out) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a riscv64 example following the same pattern as ARM64. The demo binary is re-linked with the example objects and then run through prelink-riscv to resolve GOT entries, which is required for RISC-V static PIE binaries. Enable CONFIG_ULIB and CONFIG_EXAMPLES in qemu-riscv64_defconfig and add a pytest for the demo. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/Makefile | 7 ++++++ configs/qemu-riscv64_defconfig | 2 ++ test/py/tests/test_ulib.py | 42 +++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index aaa5a02e17c..afae33812e4 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -62,3 +62,10 @@ endif libs-y += arch/riscv/cpu/ libs-y += arch/riscv/cpu/$(CPU)/ libs-y += arch/riscv/lib/ + +ifdef CONFIG_EXAMPLES +EXAMPLE_ARCH := riscv +EXAMPLE_APPEND_DTB := y +EXAMPLE_POST_LINK = @tools/prelink-riscv $@ +include scripts/Makefile.ulib-example +endif diff --git a/configs/qemu-riscv64_defconfig b/configs/qemu-riscv64_defconfig index 6b2fed4ad16..4423d36b782 100644 --- a/configs/qemu-riscv64_defconfig +++ b/configs/qemu-riscv64_defconfig @@ -22,3 +22,5 @@ CONFIG_FLASH_SHOW_PROGRESS=0 CONFIG_SYS_MAX_FLASH_BANKS=2 CONFIG_UTHREAD=y CONFIG_UNIT_TEST=y +CONFIG_ULIB=y +CONFIG_EXAMPLES=y diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 94d40256c59..71fe751efc9 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -290,18 +290,44 @@ def test_ulib_demo_rom_64(ubman): """Test the ulib demo ROM image under QEMU x86_64.""" run_x86_rom_demo(ubman, 'qemu-system-x86_64') -@pytest.mark.localqemu -@pytest.mark.boardspec('qemu_arm64') -@pytest.mark.buildconfigspec("examples") -def test_ulib_demo_arm64(ubman): - """Test the ulib demo binary under QEMU ARM64.""" +def run_bios_demo(ubman, qemu_binary, extra_qemu_args=None): + """Boot the demo.bin binary under QEMU and check for expected output. + + Locates demo.bin in the build directory, launches the given QEMU + binary with it as -bios, and asserts that the expected demo output + is present. + + Args: + ubman (ConsoleBase): Test fixture providing build directory + etc. + qemu_binary (str): QEMU system binary + (e.g. 'qemu-system-aarch64') + extra_qemu_args (list): Additional QEMU arguments + (e.g. ['-cpu', 'cortex-a57']) + """ build = ubman.config.build_dir demo_bin = os.path.join(build, 'examples', 'ulib', 'demo.bin') assert os.path.exists(demo_bin), 'demo.bin not found in build directory' - assert shutil.which('qemu-system-aarch64'), 'qemu-system-aarch64 not found' + assert shutil.which(qemu_binary), f'{qemu_binary} not found' - cmd = ['qemu-system-aarch64', '-machine', 'virt', '-cpu', 'cortex-a57', - '-nographic', '-no-reboot', '-bios', demo_bin] + cmd = [qemu_binary, '-machine', 'virt', '-nographic', '-no-reboot', + '-bios', demo_bin] + if extra_qemu_args: + cmd += extra_qemu_args out = run_qemu_demo(cmd) assert_demo_output(out) + +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu_arm64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_arm64(ubman): + """Test the ulib demo binary under QEMU ARM64.""" + run_bios_demo(ubman, 'qemu-system-aarch64', ['-cpu', 'cortex-a57']) + +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu-riscv64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_riscv64(ubman): + """Test the ulib demo binary under QEMU RISC-V 64.""" + run_bios_demo(ubman, 'qemu-system-riscv64') -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The EFI entry point currently hardcodes is_ulib to false when calling efi_startup(). This means EFI app builds cannot run ulib demo binaries, since the GD_FLG_ULIB flag is never set. Use ulib_has_main() instead, which returns false by default (weak symbol) but returns true when a demo's strong version is linked in. This allows the same efi_main.o to work for both normal U-Boot and ulib demo EFI binaries without needing a separate source file. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- lib/efi_client/efi_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/efi_client/efi_main.c b/lib/efi_client/efi_main.c index e94460c86e1..dfd44695227 100644 --- a/lib/efi_client/efi_main.c +++ b/lib/efi_client/efi_main.c @@ -8,13 +8,14 @@ #include <efi.h> #include <efi_api.h> +#include <init.h> efi_status_t EFIAPI efi_main(efi_handle_t image, struct efi_system_table *sys_table) { efi_status_t ret; - ret = efi_startup(image, sys_table, false); + ret = efi_startup(image, sys_table, ulib_has_main()); if (ret) return ret; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The x86 example targets currently only produce raw binaries (demo-nodtb.bin), which do not work for EFI app builds that need PE format output. Add an EFI-specific path that embeds the DTB and converts the demo ELF to a PE binary (demo-app.efi), following the same flow as u-boot-app.efi. Non-EFI builds continue to produce demo-nodtb.bin as before. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/Makefile.ulib-example | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index fd7c60e4483..575dedcfbf2 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -31,6 +31,17 @@ examples/ulib/demo: $(example-demo-objs) u-boot FORCE $(call if_changed,u-boot-example) $(EXAMPLE_POST_LINK) +ifeq ($(CONFIG_EFI_APP),y) +# EFI: embed DTB and convert to PE binary +OBJCOPYFLAGS_demo-app.efi := $(OBJCOPYFLAGS_EFI) +examples/ulib/demo-app.efi: examples/ulib/demo dts/dt.dtb FORCE + $(if $(CONFIG_OF_SEPARATE),$(call if_changed,embeddtb)) + $(call if_changed,zobjcopy) + +examples_$(EXAMPLE_ARCH): \ + $(foreach e,$(ULIB_EXAMPLES),examples/ulib/$(e)-app.efi) FORCE + @: +else # Binary target (without DTB) OBJCOPYFLAGS_demo-nodtb.bin = $(OBJCOPYFLAGS_u-boot-nodtb.bin) examples/ulib/demo-nodtb.bin: examples/ulib/demo FORCE @@ -49,3 +60,4 @@ examples_$(EXAMPLE_ARCH): \ $(foreach e,$(ULIB_EXAMPLES),examples/ulib/$(e)-nodtb.bin) FORCE @: endif +endif # CONFIG_EFI_APP -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Enable CONFIG_ULIB and CONFIG_EXAMPLES so that the demo-app.efi binary is built alongside u-boot-app.efi. This allows the ulib demo to run as a standalone EFI application under OVMF/QEMU. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/efi-x86_app64_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/efi-x86_app64_defconfig b/configs/efi-x86_app64_defconfig index 874feee862a..3f9783faf6f 100644 --- a/configs/efi-x86_app64_defconfig +++ b/configs/efi-x86_app64_defconfig @@ -50,4 +50,6 @@ CONFIG_BMP_16BPP=y CONFIG_BMP_24BPP=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y +CONFIG_ULIB=y +CONFIG_EXAMPLES=y # CONFIG_GZIP is not set -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add test_ulib_demo_efi_x86() which boots demo-app.efi under QEMU x86_64 with OVMF firmware. The test writes a startup.nsh script next to demo-app.efi in the build directory, then boots QEMU with the FAT directory and checks for the expected demo output. Also add a timeout parameter to run_qemu_demo() since OVMF boot takes longer than bare-metal demos. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/py/tests/test_ulib.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 71fe751efc9..1c98b8b6880 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -234,12 +234,13 @@ def assert_demo_output(out): assert '=================================' in out assert 'Demo complete' in out -def run_qemu_demo(qemu_cmd): +def run_qemu_demo(qemu_cmd, timeout=5): """Run a ulib demo under QEMU and return output. Args: qemu_cmd (list): QEMU command and arguments (e.g. ['qemu-system-i386', '-bios', 'demo.rom', ...]) + timeout (int): Seconds to wait before killing QEMU (default 5) Returns: str: Decoded stdout from QEMU @@ -247,7 +248,7 @@ def run_qemu_demo(qemu_cmd): with subprocess.Popen(qemu_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: try: - stdout, _ = proc.communicate(timeout=5) + stdout, _ = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired: proc.kill() stdout, _ = proc.communicate() @@ -331,3 +332,35 @@ def test_ulib_demo_arm64(ubman): def test_ulib_demo_riscv64(ubman): """Test the ulib demo binary under QEMU RISC-V 64.""" run_bios_demo(ubman, 'qemu-system-riscv64') + +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-x86_app64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_efi_x86(ubman): + """Test the ulib demo EFI application under QEMU x86_64 with OVMF.""" + build = ubman.config.build_dir + efi_dir = os.path.join(build, 'examples', 'ulib') + demo_efi = os.path.join(efi_dir, 'demo-app.efi') + + assert os.path.exists(demo_efi), 'demo-app.efi not found in build directory' + assert shutil.which('qemu-system-x86_64'), 'qemu-system-x86_64 not found' + + ovmf_code = '/usr/share/OVMF/OVMF_CODE_4M.fd' + ovmf_vars = '/usr/share/OVMF/OVMF_VARS_4M.fd' + assert os.path.exists(ovmf_code), 'OVMF firmware not found' + + with open(os.path.join(efi_dir, 'startup.nsh'), 'w', + encoding='utf-8') as nsh: + nsh.write('fs0:demo-app.efi\n') + + # OVMF needs a writable copy of the vars file + vars_copy = os.path.join(efi_dir, 'OVMF_VARS_4M.fd') + shutil.copy(ovmf_vars, vars_copy) + + cmd = ['qemu-system-x86_64', + '-drive', f'if=pflash,format=raw,file={ovmf_code},readonly=on', + '-drive', f'if=pflash,format=raw,file={vars_copy}', + '-drive', f'file=fat:rw:{efi_dir},format=raw', + '-nographic', '-no-reboot', '-nic', 'none'] + out = run_qemu_demo(cmd, timeout=15) + assert_demo_output(out) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Enable CONFIG_ULIB and CONFIG_EXAMPLES in efi-arm_app64_defconfig so that the demo-app.efi binary is built. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/efi-arm_app64_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/efi-arm_app64_defconfig b/configs/efi-arm_app64_defconfig index 35e8b4d36ed..a9c4686a106 100644 --- a/configs/efi-arm_app64_defconfig +++ b/configs/efi-arm_app64_defconfig @@ -53,4 +53,6 @@ CONFIG_CONSOLE_TRUETYPE=y CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_FAT_WRITE=y +CONFIG_ULIB=y +CONFIG_EXAMPLES=y CONFIG_CMD_DHRYSTONE=y -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Enable CONFIG_ULIB and CONFIG_EXAMPLES in efi-riscv_app64_defconfig so that the demo-app.efi binary is built. Skip the prelink-riscv step on EFI builds since the shared-object linking style has no dynamic section for prelink to process. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/Makefile | 2 +- configs/efi-riscv_app64_defconfig | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index afae33812e4..acfdb9e167f 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -66,6 +66,6 @@ libs-y += arch/riscv/lib/ ifdef CONFIG_EXAMPLES EXAMPLE_ARCH := riscv EXAMPLE_APPEND_DTB := y -EXAMPLE_POST_LINK = @tools/prelink-riscv $@ +EXAMPLE_POST_LINK = $(if $(CONFIG_EFI_APP),,@tools/prelink-riscv $@) include scripts/Makefile.ulib-example endif diff --git a/configs/efi-riscv_app64_defconfig b/configs/efi-riscv_app64_defconfig index f18bfb653ad..d48ecf19585 100644 --- a/configs/efi-riscv_app64_defconfig +++ b/configs/efi-riscv_app64_defconfig @@ -52,4 +52,6 @@ CONFIG_CONSOLE_TRUETYPE=y CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_FAT_WRITE=y +CONFIG_ULIB=y +CONFIG_EXAMPLES=y CONFIG_CMD_DHRYSTONE=y -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add run_efi_demo() helper that writes a startup.nsh script next to demo-app.efi in the build directory, boots QEMU with the given UEFI firmware, and checks for the expected demo output. Refactor test_ulib_demo_efi_x86() to use the shared helper and add test_ulib_demo_efi_arm64() which uses qemu-system-aarch64 with the appropriate firmware path. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/py/tests/test_ulib.py | 69 ++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 1c98b8b6880..c7d79515bb9 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -6,6 +6,7 @@ import os import shutil import subprocess +import tempfile import pytest import utils @@ -333,34 +334,66 @@ def test_ulib_demo_riscv64(ubman): """Test the ulib demo binary under QEMU RISC-V 64.""" run_bios_demo(ubman, 'qemu-system-riscv64') -@pytest.mark.localqemu -@pytest.mark.boardspec('efi-x86_app64') -@pytest.mark.buildconfigspec("examples") -def test_ulib_demo_efi_x86(ubman): - """Test the ulib demo EFI application under QEMU x86_64 with OVMF.""" +def run_efi_demo(ubman, qemu_binary, fw_code, fw_vars, extra_qemu_args=None): + """Run a ulib demo EFI application under QEMU with UEFI firmware. + + Writes a startup.nsh script next to demo-app.efi in the build + directory, boots QEMU with the given firmware, and checks for + expected output. + + Args: + ubman (ConsoleBase): Test fixture providing build directory + etc. + qemu_binary (str): QEMU system binary name + (e.g. 'qemu-system-x86_64') + fw_code (str): Path to UEFI firmware code file + fw_vars (str): Path to UEFI firmware variables file (or None) + extra_qemu_args (list): Additional QEMU arguments + """ build = ubman.config.build_dir efi_dir = os.path.join(build, 'examples', 'ulib') demo_efi = os.path.join(efi_dir, 'demo-app.efi') assert os.path.exists(demo_efi), 'demo-app.efi not found in build directory' - assert shutil.which('qemu-system-x86_64'), 'qemu-system-x86_64 not found' - - ovmf_code = '/usr/share/OVMF/OVMF_CODE_4M.fd' - ovmf_vars = '/usr/share/OVMF/OVMF_VARS_4M.fd' - assert os.path.exists(ovmf_code), 'OVMF firmware not found' + assert shutil.which(qemu_binary), f'{qemu_binary} not found' + assert os.path.exists(fw_code), f'UEFI firmware not found: {fw_code}' with open(os.path.join(efi_dir, 'startup.nsh'), 'w', encoding='utf-8') as nsh: nsh.write('fs0:demo-app.efi\n') - # OVMF needs a writable copy of the vars file - vars_copy = os.path.join(efi_dir, 'OVMF_VARS_4M.fd') - shutil.copy(ovmf_vars, vars_copy) + cmd = [qemu_binary] - cmd = ['qemu-system-x86_64', - '-drive', f'if=pflash,format=raw,file={ovmf_code},readonly=on', - '-drive', f'if=pflash,format=raw,file={vars_copy}', - '-drive', f'file=fat:rw:{efi_dir},format=raw', - '-nographic', '-no-reboot', '-nic', 'none'] + # Set up firmware pflash drives + cmd += ['-drive', f'if=pflash,format=raw,file={fw_code},readonly=on'] + if fw_vars: + vars_copy = os.path.join(efi_dir, 'vars.fd') + shutil.copy(fw_vars, vars_copy) + cmd += ['-drive', f'if=pflash,format=raw,file={vars_copy}'] + + if extra_qemu_args: + cmd += extra_qemu_args + + # FAT drive with EFI binary and startup script + cmd += ['-drive', f'file=fat:rw:{efi_dir},format=raw', + '-nographic', '-no-reboot', '-nic', 'none'] out = run_qemu_demo(cmd, timeout=15) assert_demo_output(out) + +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-x86_app64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_efi_x86(ubman): + """Test the ulib demo EFI application under QEMU x86_64 with OVMF.""" + run_efi_demo(ubman, 'qemu-system-x86_64', + '/usr/share/OVMF/OVMF_CODE_4M.fd', + '/usr/share/OVMF/OVMF_VARS_4M.fd') + +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-arm_app64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_efi_arm64(ubman): + """Test the ulib demo EFI application under QEMU aarch64 with UEFI.""" + run_efi_demo(ubman, 'qemu-system-aarch64', + '/usr/share/qemu-efi-aarch64/QEMU_EFI.fd', None, + ['--machine', 'virt', '-cpu', 'max']) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add test_ulib_demo_efi_riscv64() which uses the run_efi_demo() helper to boot demo-app.efi under qemu-system-riscv64 with the appropriate UEFI firmware. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/py/tests/test_ulib.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index c7d79515bb9..50784ea9c60 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -397,3 +397,13 @@ def test_ulib_demo_efi_arm64(ubman): run_efi_demo(ubman, 'qemu-system-aarch64', '/usr/share/qemu-efi-aarch64/QEMU_EFI.fd', None, ['--machine', 'virt', '-cpu', 'max']) + +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-riscv_app64') +@pytest.mark.buildconfigspec("examples") +def test_ulib_demo_efi_riscv64(ubman): + """Test the ulib demo EFI application under QEMU RISC-V 64 with UEFI.""" + run_efi_demo(ubman, 'qemu-system-riscv64', + '/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd', + '/usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd', + ['--machine', 'virt']) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The build-qemu script accepts --arch riscv via build_helper's argument parser but raises ValueError since no riscv block exists. Add RISC-V support with: - Board names qemu-riscv64/qemu-riscv32 (and _spl variants) - QEMU binary selection (qemu-system-riscv64/riscv32) - Machine type 'virt' with TCG acceleration - Tianocore firmware path (RISCV_VIRT_CODE.fd) - virtio-gpu-pci display setup (shared with arm, since the virt machine has no native VGA) Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/build-qemu | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index 4f77eb88167..5c69457647e 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -9,7 +9,7 @@ It assumes that board config - your OS images are in ${imagedir}/{distroname}/ -So far the script supports only ARM and x86 +So far the script supports ARM, RISC-V and x86 """ import argparse @@ -122,6 +122,8 @@ class BuildQemu: elif args.use_tianocore: if args.arch == 'arm': bios_override = Path(self.tiano, 'OVMF-pure-efi.aarch64.fd.64m') + elif args.arch == 'riscv': + bios_override = Path(self.tiano, 'RISCV_VIRT_CODE.fd') else: bios_override = Path(self.tiano, 'OVMF-pure-efi.x64.fd') if not bios_override.exists(): @@ -156,6 +158,23 @@ class BuildQemu: self.board = 'qemu_arm64' self.helper.qemu = 'qemu-system-aarch64' self.qemu_extra.extend(['-cpu', 'cortex-a57']) + elif args.arch == 'riscv': + if args.xpl: + self.board = 'qemu-riscv64_spl' + default_bios = 'u-boot.bin' + else: + self.board = 'qemu-riscv64' + default_bios = 'u-boot.bin' + self.helper.qemu = 'qemu-system-riscv64' + self.qemu_extra.extend(['-machine', 'virt']) + if not args.kvm: + self.qemu_extra.extend(['-accel', 'tcg']) + if self.helper.bitness == 32: + if args.xpl: + self.board = 'qemu-riscv32_spl' + else: + self.board = 'qemu-riscv32' + self.helper.qemu = 'qemu-system-riscv32' elif args.arch == 'x86': self.board = 'qemu-x86' default_bios = 'u-boot.rom' @@ -291,7 +310,7 @@ class BuildQemu: # SCT usually runs headlessly if self.args.serial_only or self.args.sct_seq: qemu_cmd.extend(['-display', 'none']) - elif self.args.arch == 'arm': + elif self.args.arch in ('arm', 'riscv'): qemu_cmd.extend(['-device', 'virtio-gpu-pci']) qemu_cmd.extend(['-device', 'qemu-xhci', '-device', 'usb-kbd', '-device', 'usb-tablet', '-device', 'usb-mouse']) -- 2.43.0
participants (1)
-
Simon Glass