[PATCH 00/13] ulib: Enable the Rust demo on more architectures
From: Simon Glass <simon.glass@canonical.com> The Rust ulib demo currently only builds on x86_64. This series extends it to x86 (32-bit), RISC-V 64 and ARM64, for both QEMU and EFI targets. Along the way it fixes a rustc jobserver warning (MAKEFLAGS leak) and a linker crash on aarch64 caused by a Rust null-pointer-dereference check emitting an undefined panic symbol. Simon Glass (13): ulib: x86: Disable regparm when Rust examples are enabled disk: part_efi: Check the block size in part_test_efi() ulib: Extract common example build rules for demo ulib: Add Rust demo example ulib: x86: Add Rust demo for qemu-x86_64_nospl ulib: x86: Add 32-bit Rust target support ulib: x86: Enable Rust demo for qemu-x86 ulib: riscv: Enable Rust demo for qemu-riscv64 ulib: riscv: Add Rust demo test for qemu-riscv64 ulib: x86: Enable Rust demo for efi-x86_app64 ulib: arm64: Enable Rust demo for efi-arm_app64 ulib: riscv: Enable Rust demo for efi-riscv_app64 ulib: arm64: Enable Rust demo for qemu_arm64 arch/x86/Kconfig | 11 +- arch/x86/cpu/i386/setjmp.S | 2 + arch/x86/dts/u-boot.dtsi | 18 ++++ configs/efi-arm_app64_defconfig | 1 + configs/efi-riscv_app64_defconfig | 1 + configs/efi-x86_app64_defconfig | 1 + configs/qemu-riscv64_defconfig | 1 + configs/qemu-x86_64_nospl_defconfig | 1 + configs/qemu-x86_defconfig | 1 + configs/qemu_arm64_defconfig | 1 + disk/part_efi.c | 11 +- examples/Kconfig | 13 +++ examples/ulib/rust_demo.rs | 55 ++++++++++ scripts/Makefile.ulib-example | 81 ++++++++++++--- test/py/tests/test_ulib.py | 155 ++++++++++++++++++++++++++++ 15 files changed, 332 insertions(+), 21 deletions(-) create mode 100644 examples/ulib/rust_demo.rs -- 2.43.0 base-commit: 3b07f0d65d65584140eeffb16447175f9e230397 branch: ulibd
From: Simon Glass <simon.glass@canonical.com> The regparm=3 calling convention is incompatible with Rust's extern "C" ABI, which uses cdecl (all arguments on the stack). Rather than adding an inline-assembly bridge for every non-variadic C call, automatically disable regparm when CONFIG_RUST_EXAMPLES is set by defaulting X86_NO_REGPARM to y. Also update setjmp.S to use the stack-based calling convention when regparm is disabled, since its _REGPARM define was unconditional. This is somewhat less efficient but is a worthwhile trade-off when using Rust. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/Kconfig | 11 +++++------ arch/x86/cpu/i386/setjmp.S | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 4fdfe5b90fe..14ee4a6d400 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -755,16 +755,15 @@ config X86_HARDFP config X86_NO_REGPARM bool "Disable register parameters (regparm=3)" depends on !X86_64 + default y if RUST_EXAMPLES help By default, U-Boot on 32-bit x86 uses -mregparm=3 to pass the first three function arguments in registers (EAX, EDX, ECX) rather than on - the stack. This is more efficient but can cause issues with debugging - tools or when interfacing with code that expects the standard calling - convention. + the stack. This is more efficient but is incompatible with Rust's + extern "C" ABI which uses the standard cdecl convention. - Select this option to disable regparm and use the standard i386 - calling convention where all arguments are passed on the stack. This - may be useful for debugging or for running in certain emulators. + This is automatically enabled when RUST_EXAMPLES is set. It may + also be useful for debugging or for running in certain emulators. config HAVE_ITSS bool "Enable ITSS" diff --git a/arch/x86/cpu/i386/setjmp.S b/arch/x86/cpu/i386/setjmp.S index eceeafa7c8b..f699850680c 100644 --- a/arch/x86/cpu/i386/setjmp.S +++ b/arch/x86/cpu/i386/setjmp.S @@ -5,7 +5,9 @@ * From Linux arch/um/sys-i386/setjmp.S */ +#ifndef CONFIG_X86_NO_REGPARM #define _REGPARM +#endif /* * The jmp_buf is assumed to contain the following, in order: -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The ALLOC_CACHE_ALIGN_BUFFER_PAD() macro divides by desc->blksz to compute alignment padding, causing a Divide Error when blksz is 0. This happens when bootmeth_rauc scans block devices that have no block size set. This happens on qemu-x86, for example. Return -ENOENT early when blksz is zero, since a device without a block size cannot have a valid EFI partition table. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- disk/part_efi.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/disk/part_efi.c b/disk/part_efi.c index fbed515b303..b48ff518b03 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -314,9 +314,18 @@ static int __maybe_unused part_get_info_efi(struct blk_desc *desc, int part, static int part_test_efi(struct blk_desc *desc) { - ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, legacymbr, 1, desc->blksz); long ret; + /* + * An ATAPI device (e.g. CD-ROM) may have blksz == 0 if the + * capacity query failed or no media is present. The buffer + * macro below divides by blksz, so bail out early. + */ + if (!desc->blksz) + return -ENOENT; + + ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, legacymbr, 1, desc->blksz); + /* Read legacy MBR from block 0 and validate it */ ret = blk_dread(desc, 0, 1, (ulong *)legacymbr); if (IS_ERR_VALUE(ret)) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Generalise the per-example link, objcopy and cat rules in Makefile.ulib-example from demo-specific to foreach+eval over the ULIB_EXAMPLES list. This is a pure refactoring that produces identical build output but makes it straightforward to add further examples. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/Makefile.ulib-example | 48 +++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index 575dedcfbf2..796b74b0c14 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -25,32 +25,52 @@ quiet_cmd_u-boot-example = LD $@ # 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) +# Generate link rule for each example +define example_link_rule +examples/ulib/$(1): $$(example-$(1)-objs) u-boot FORCE + $$(eval example-objs := $$(example-$(1)-objs)) + $$(call if_changed,u-boot-example) + $$(EXAMPLE_POST_LINK) +endef + +$(foreach e,$(ULIB_EXAMPLES),$(eval $(call example_link_rule,$(e)))) 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) +$(foreach e,$(ULIB_EXAMPLES),\ + $(eval OBJCOPYFLAGS_$(e)-app.efi := $$(OBJCOPYFLAGS_EFI))) + +define example_efi_rule +examples/ulib/$(1)-app.efi: examples/ulib/$(1) dts/dt.dtb FORCE + $$(if $$(CONFIG_OF_SEPARATE),$$(call if_changed,embeddtb)) + $$(call if_changed,zobjcopy) +endef + +$(foreach e,$(ULIB_EXAMPLES),$(eval $(call example_efi_rule,$(e)))) 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 - $(call if_changed,objcopy) +$(foreach e,$(ULIB_EXAMPLES),\ + $(eval OBJCOPYFLAGS_$(e)-nodtb.bin = $$(OBJCOPYFLAGS_u-boot-nodtb.bin))) + +define example_nodtb_rule +examples/ulib/$(1)-nodtb.bin: examples/ulib/$(1) FORCE + $$(call if_changed,objcopy) +endef + +$(foreach e,$(ULIB_EXAMPLES),$(eval $(call example_nodtb_rule,$(e)))) 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) +define example_bin_rule +examples/ulib/$(1).bin: examples/ulib/$(1)-nodtb.bin dts/dt.dtb FORCE + $$(call if_changed,cat) +endef + +$(foreach e,$(ULIB_EXAMPLES),$(eval $(call example_bin_rule,$(e)))) examples_$(EXAMPLE_ARCH): \ $(foreach e,$(ULIB_EXAMPLES),examples/ulib/$(e).bin) FORCE -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a #![no_std] Rust equivalent of the C ulib demo, compiled to an object file with rustc and linked into U-Boot via the existing u-boot-link mechanism. The Rust demo calls the same C helpers (demo_show_banner, demo_show_footer, demo_add_numbers) via FFI and produces identical output, so assert_demo_output() works unchanged. Use core::ptr::addr_of!() rather than a reference cast to access the extern static version_string, since the compiler cannot prove that an extern static is non-null and would emit a call to an undefined panic symbol. Add CONFIG_RUST_EXAMPLES Kconfig option to gate the Rust examples. When enabled, rustc must be available with the appropriate target for the architecture. Clear MAKEFLAGS when invoking rustc so that it does not inherit make's --jobserver-auth file descriptors, which would produce a spurious warning on every compilation. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- examples/Kconfig | 13 +++++++++ examples/ulib/rust_demo.rs | 55 +++++++++++++++++++++++++++++++++++ scripts/Makefile.ulib-example | 17 +++++++++++ 3 files changed, 85 insertions(+) create mode 100644 examples/ulib/rust_demo.rs diff --git a/examples/Kconfig b/examples/Kconfig index 5738d555d22..7f099808908 100644 --- a/examples/Kconfig +++ b/examples/Kconfig @@ -9,6 +9,19 @@ config EXAMPLES U-Boot provides an legacy API for standalone applications. Examples are provided in directory examples/. +config RUST_EXAMPLES + bool "Build Rust example programs" + depends on EXAMPLES + help + Build example programs written in Rust alongside the C + examples. The Rust demo calls the same C helpers via FFI + and produces identical output. + + Requires a Rust toolchain (rustc) with the appropriate + cross-compilation target for the architecture, e.g. + x86_64-unknown-none for x86_64 or aarch64-unknown-none + for ARM64. + config EXAMPLES_STANDALONE bool "Compile standalone examples" depends on !SANDBOX diff --git a/examples/ulib/rust_demo.rs b/examples/ulib/rust_demo.rs new file mode 100644 index 00000000000..7a85658520d --- /dev/null +++ b/examples/ulib/rust_demo.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Rust demo program showing U-Boot library functionality +// +// Demonstrates calling C helper functions from Rust via FFI, producing +// identical output to demo.c so assert_demo_output() works unchanged. +// +// Copyright 2026 Canonical Ltd. +// Written by Simon Glass <simon.glass@canonical.com> + +#![no_std] +#![no_main] + +use core::ffi::c_int; + +extern "C" { + fn printf(fmt: *const u8, ...) -> c_int; + fn demo_show_banner(); + fn demo_show_footer(); + fn demo_add_numbers(a: c_int, b: c_int) -> c_int; + static version_string: u8; +} + +#[no_mangle] +pub extern "C" fn ulib_has_main() -> bool { + true +} + +fn demo_run() -> c_int { + unsafe { + demo_show_banner(); + // Use addr_of!() rather than &version_string to avoid a + // null-pointer check: &T must be non-null, but the compiler + // cannot prove that for an extern static, so it emits a call + // to an undefined panic symbol that crashes ld.bfd on aarch64. + printf( + b"U-Boot version: %s\n\0".as_ptr(), + core::ptr::addr_of!(version_string), + ); + printf(b"\n\0".as_ptr()); + demo_add_numbers(42, 13); + demo_show_footer(); + } + 0 +} + +#[no_mangle] +pub extern "C" fn main() -> c_int { + demo_run() +} + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index 796b74b0c14..18d526f5b6b 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -19,11 +19,28 @@ PHONY += examples_$(EXAMPLE_ARCH) ULIB_EXAMPLES := demo +# --- Rust examples --- +RUSTC := rustc +RUST_TARGET := $(RUST_TARGET_$(EXAMPLE_ARCH)) + +ifeq ($(CONFIG_RUST_EXAMPLES),y) +ULIB_EXAMPLES += rust-demo +endif + quiet_cmd_u-boot-example = LD $@ cmd_u-boot-example = $(call u-boot-link,$(example-objs),$@.map) +quiet_cmd_rustc_obj = RUSTC $@ + cmd_rustc_obj = \ + mkdir -p $(dir $@) && \ + MAKEFLAGS= $(RUSTC) --edition 2021 --emit=obj -o $@ --target=$(RUST_TARGET) $< + +examples/ulib/rust_demo.o: examples/ulib/rust_demo.rs FORCE + $(call if_changed,rustc_obj) + # Per-example object lists (matches examples/ulib/Kbuild) example-demo-objs := examples/ulib/demo.o examples/ulib/demo_helper.o +example-rust-demo-objs := examples/ulib/rust_demo.o examples/ulib/demo_helper.o # Generate link rule for each example define example_link_rule -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Define the x86_64-unknown-none Rust target and enable CONFIG_RUST_EXAMPLES in the qemu-x86_64_nospl defconfig. Add the DTS node for the Rust demo binary and a QEMU boot test. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/dts/u-boot.dtsi | 18 +++++++++++++++++ configs/qemu-x86_64_nospl_defconfig | 1 + scripts/Makefile.ulib-example | 1 + test/py/tests/test_ulib.py | 30 +++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi index 69925c49c49..57b7be9ae29 100644 --- a/arch/x86/dts/u-boot.dtsi +++ b/arch/x86/dts/u-boot.dtsi @@ -213,4 +213,22 @@ }; }; }; #endif + +#ifdef CONFIG_RUST_EXAMPLES +/ { binman { + rust-demo-rom { + filename = "rust-demo.rom"; + insert-template = <&rom_common>; + + blob { + filename = "examples/ulib/rust-demo-nodtb.bin"; + offset = <CONFIG_X86_OFFSET_U_BOOT>; + }; + u-boot-dtb { + }; + fdtmap { + }; + }; +}; }; +#endif #endif diff --git a/configs/qemu-x86_64_nospl_defconfig b/configs/qemu-x86_64_nospl_defconfig index 950d2d2e419..91aabf5d1a2 100644 --- a/configs/qemu-x86_64_nospl_defconfig +++ b/configs/qemu-x86_64_nospl_defconfig @@ -81,4 +81,5 @@ CONFIG_CMD_DHRYSTONE=y # CONFIG_GZIP is not set CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y CONFIG_UNIT_TEST=y diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index 18d526f5b6b..4d8af88b898 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -21,6 +21,7 @@ ULIB_EXAMPLES := demo # --- Rust examples --- RUSTC := rustc +RUST_TARGET_x86 := x86_64-unknown-none RUST_TARGET := $(RUST_TARGET_$(EXAMPLE_ARCH)) ifeq ($(CONFIG_RUST_EXAMPLES),y) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 50784ea9c60..5ca46df0fe8 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -292,6 +292,36 @@ 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') +def run_x86_rom_rust_demo(ubman, qemu_binary): + """Boot the Rust demo ROM image under QEMU and check for expected output. + + Locates rust-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-x86_64') + """ + build = ubman.config.build_dir + demo_rom = os.path.join(build, 'rust-demo.rom') + + assert os.path.exists(demo_rom), \ + 'rust-demo.rom not found in build directory' + assert shutil.which(qemu_binary), f'{qemu_binary} not found' + + cmd = [qemu_binary, '-bios', demo_rom, '-nographic', '-no-reboot'] + out = run_qemu_demo(cmd) + assert_demo_output(out) + +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu-x86_64_nospl') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_rom_64(ubman): + """Test the Rust ulib demo ROM image under QEMU x86_64.""" + run_x86_rom_rust_demo(ubman, 'qemu-system-x86_64') + def run_bios_demo(ubman, qemu_binary, extra_qemu_args=None): """Boot the demo.bin binary under QEMU and check for expected output. -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> There is no built-in i686-unknown-none Rust target, so use i686-unknown-linux-gnu with flags to disable unwinding and force static relocations, which produces a clean freestanding ELF 32-bit object suitable for linking into U-Boot. Select the Rust target and flags based on CONFIG_X86_64 so that the 64-bit build continues to use x86_64-unknown-none with its built-in defaults (PIE-compatible code, no unwinding). Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/Makefile.ulib-example | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index 4d8af88b898..a398f3e9621 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -21,8 +21,18 @@ ULIB_EXAMPLES := demo # --- Rust examples --- RUSTC := rustc -RUST_TARGET_x86 := x86_64-unknown-none +RUST_TARGET_x86 := i686-unknown-linux-gnu +RUST_TARGET_x86_64 := x86_64-unknown-none +RUST_FLAGS_x86 := -C panic=abort -C relocation-model=static +RUST_FLAGS_x86_64 := + +ifdef CONFIG_X86_64 +RUST_TARGET := $(RUST_TARGET_$(EXAMPLE_ARCH)_64) +RUST_FLAGS := $(RUST_FLAGS_$(EXAMPLE_ARCH)_64) +else RUST_TARGET := $(RUST_TARGET_$(EXAMPLE_ARCH)) +RUST_FLAGS := $(RUST_FLAGS_$(EXAMPLE_ARCH)) +endif ifeq ($(CONFIG_RUST_EXAMPLES),y) ULIB_EXAMPLES += rust-demo @@ -34,7 +44,8 @@ quiet_cmd_u-boot-example = LD $@ quiet_cmd_rustc_obj = RUSTC $@ cmd_rustc_obj = \ mkdir -p $(dir $@) && \ - MAKEFLAGS= $(RUSTC) --edition 2021 --emit=obj -o $@ --target=$(RUST_TARGET) $< + MAKEFLAGS= $(RUSTC) --edition 2021 --emit=obj -o $@ \ + --target=$(RUST_TARGET) $(RUST_FLAGS) $< examples/ulib/rust_demo.o: examples/ulib/rust_demo.rs FORCE $(call if_changed,rustc_obj) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Enable CONFIG_RUST_EXAMPLES in qemu-x86_defconfig and add a pytest for the 32-bit Rust demo ROM, using the existing run_x86_rom_rust_demo() helper with qemu-system-i386. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/qemu-x86_defconfig | 1 + test/py/tests/test_ulib.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/configs/qemu-x86_defconfig b/configs/qemu-x86_defconfig index 2275ff9de1a..5e68c8283ec 100644 --- a/configs/qemu-x86_defconfig +++ b/configs/qemu-x86_defconfig @@ -15,6 +15,7 @@ CONFIG_GENERATE_PIRQ_TABLE=y CONFIG_GENERATE_MP_TABLE=y CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y CONFIG_FIT=y CONFIG_BOOTSTD_FULL=y CONFIG_BOOTSTAGE=y diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 5ca46df0fe8..2090a06efbc 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -315,6 +315,13 @@ def run_x86_rom_rust_demo(ubman, qemu_binary): out = run_qemu_demo(cmd) assert_demo_output(out) +@pytest.mark.localqemu +@pytest.mark.boardspec('qemu-x86') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_rom(ubman): + """Test the Rust ulib demo ROM image under QEMU x86.""" + run_x86_rom_rust_demo(ubman, 'qemu-system-i386') + @pytest.mark.localqemu @pytest.mark.boardspec('qemu-x86_64_nospl') @pytest.mark.buildconfigspec("rust_examples") -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The Rust ulib demo currently works on x86 (32-bit and 64-bit) but not on RISC-V. Unlike x86, RISC-V has no calling-convention mismatch, so Rust's extern "C" naturally matches the standard ABI with no extra flags. Add the riscv64gc-unknown-none-elf built-in Rust target (which already has panic-strategy: abort) and enable CONFIG_RUST_EXAMPLES in the qemu-riscv64 defconfig so that rust-demo.bin is produced alongside demo.bin Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/qemu-riscv64_defconfig | 1 + scripts/Makefile.ulib-example | 2 ++ 2 files changed, 3 insertions(+) diff --git a/configs/qemu-riscv64_defconfig b/configs/qemu-riscv64_defconfig index 4423d36b782..d30b017169e 100644 --- a/configs/qemu-riscv64_defconfig +++ b/configs/qemu-riscv64_defconfig @@ -24,3 +24,4 @@ CONFIG_UTHREAD=y CONFIG_UNIT_TEST=y CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index a398f3e9621..ae3f75b33fb 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -25,6 +25,8 @@ RUST_TARGET_x86 := i686-unknown-linux-gnu RUST_TARGET_x86_64 := x86_64-unknown-none RUST_FLAGS_x86 := -C panic=abort -C relocation-model=static RUST_FLAGS_x86_64 := +RUST_TARGET_riscv := riscv64gc-unknown-none-elf +RUST_FLAGS_riscv := ifdef CONFIG_X86_64 RUST_TARGET := $(RUST_TARGET_$(EXAMPLE_ARCH)_64) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a run_bios_rust_demo() helper that mirrors run_bios_demo() but boots rust-demo.bin instead of demo.bin, and a test_ulib_rust_demo_riscv64 test that exercises it under qemu-system-riscv64. 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 | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 2090a06efbc..b6fa30bcfd2 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -357,6 +357,34 @@ def run_bios_demo(ubman, qemu_binary, extra_qemu_args=None): out = run_qemu_demo(cmd) assert_demo_output(out) +def run_bios_rust_demo(ubman, qemu_binary, extra_qemu_args=None): + """Boot the rust-demo.bin binary under QEMU and check expected output. + + Locates rust-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-riscv64') + extra_qemu_args (list): Additional QEMU arguments + """ + build = ubman.config.build_dir + demo_bin = os.path.join(build, 'examples', 'ulib', 'rust-demo.bin') + + assert os.path.exists(demo_bin), \ + 'rust-demo.bin not found in build directory' + assert shutil.which(qemu_binary), f'{qemu_binary} not found' + + 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") @@ -371,6 +399,13 @@ 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('qemu-riscv64') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_riscv64(ubman): + """Test the Rust ulib demo binary under QEMU RISC-V 64.""" + run_bios_rust_demo(ubman, 'qemu-system-riscv64') + 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. -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The C demo already builds and runs as an EFI application on x86_64, but the Rust demo is missing. The EFI build pipeline in Makefile.ulib-example already handles the rust-demo entry via the ULIB_EXAMPLES list, and RUST_TARGET_x86_64 is already defined. Enable CONFIG_RUST_EXAMPLES in efi-x86_app64_defconfig and add a QEMU boot test that launches rust-demo-app.efi under OVMF. Add a shared run_efi_rust_demo() helper that mirrors the existing run_efi_demo() but uses the Rust binary and startup script. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/efi-x86_app64_defconfig | 1 + test/py/tests/test_ulib.py | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/configs/efi-x86_app64_defconfig b/configs/efi-x86_app64_defconfig index 3f9783faf6f..394bab55f0b 100644 --- a/configs/efi-x86_app64_defconfig +++ b/configs/efi-x86_app64_defconfig @@ -52,4 +52,5 @@ CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y # CONFIG_GZIP is not set diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index b6fa30bcfd2..8ec5e985b52 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -452,6 +452,54 @@ def run_efi_demo(ubman, qemu_binary, fw_code, fw_vars, extra_qemu_args=None): out = run_qemu_demo(cmd, timeout=15) assert_demo_output(out) +def run_efi_rust_demo(ubman, qemu_binary, fw_code, fw_vars, + extra_qemu_args=None): + """Run a Rust ulib demo EFI application under QEMU with UEFI firmware. + + Writes a startup.nsh script next to rust-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, 'rust-demo-app.efi') + + assert os.path.exists(demo_efi), \ + 'rust-demo-app.efi not found in build directory' + 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:rust-demo-app.efi\n') + + cmd = [qemu_binary] + + # 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") @@ -461,6 +509,15 @@ def test_ulib_demo_efi_x86(ubman): '/usr/share/OVMF/OVMF_CODE_4M.fd', '/usr/share/OVMF/OVMF_VARS_4M.fd') +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-x86_app64') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_efi_x86(ubman): + """Test the Rust ulib demo EFI app under QEMU x86_64 with OVMF.""" + run_efi_rust_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") -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The EFI ARM64 board sets EXAMPLE_ARCH to arm but no RUST_TARGET_arm is defined, so the Rust demo cannot build. Since only 64-bit ARM has ULIB support, define RUST_TARGET_arm as aarch64-unknown-none (the same approach used for RISC-V where only 64-bit is supported). Enable CONFIG_RUST_EXAMPLES in efi-arm_app64_defconfig and add a QEMU boot test that launches rust-demo-app.efi under QEMU_EFI.fd. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/efi-arm_app64_defconfig | 1 + scripts/Makefile.ulib-example | 2 ++ test/py/tests/test_ulib.py | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/configs/efi-arm_app64_defconfig b/configs/efi-arm_app64_defconfig index a9c4686a106..4d43a5bc754 100644 --- a/configs/efi-arm_app64_defconfig +++ b/configs/efi-arm_app64_defconfig @@ -55,4 +55,5 @@ CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_FAT_WRITE=y CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y CONFIG_CMD_DHRYSTONE=y diff --git a/scripts/Makefile.ulib-example b/scripts/Makefile.ulib-example index ae3f75b33fb..fdb5a27fa1d 100644 --- a/scripts/Makefile.ulib-example +++ b/scripts/Makefile.ulib-example @@ -25,6 +25,8 @@ RUST_TARGET_x86 := i686-unknown-linux-gnu RUST_TARGET_x86_64 := x86_64-unknown-none RUST_FLAGS_x86 := -C panic=abort -C relocation-model=static RUST_FLAGS_x86_64 := +RUST_TARGET_arm := aarch64-unknown-none +RUST_FLAGS_arm := RUST_TARGET_riscv := riscv64gc-unknown-none-elf RUST_FLAGS_riscv := diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 8ec5e985b52..851732fa1da 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -527,6 +527,15 @@ def test_ulib_demo_efi_arm64(ubman): '/usr/share/qemu-efi-aarch64/QEMU_EFI.fd', None, ['--machine', 'virt', '-cpu', 'max']) +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-arm_app64') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_efi_arm64(ubman): + """Test the Rust ulib demo EFI app under QEMU aarch64 with UEFI.""" + run_efi_rust_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") -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> RUST_TARGET_riscv is already defined as riscv64gc-unknown-none-elf, so the Rust demo just needs to be enabled in the defconfig. Enable CONFIG_RUST_EXAMPLES in efi-riscv_app64_defconfig and add a QEMU boot test that launches rust-demo-app.efi under RISC-V UEFI firmware. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/efi-riscv_app64_defconfig | 1 + test/py/tests/test_ulib.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/configs/efi-riscv_app64_defconfig b/configs/efi-riscv_app64_defconfig index d48ecf19585..e89d392050b 100644 --- a/configs/efi-riscv_app64_defconfig +++ b/configs/efi-riscv_app64_defconfig @@ -54,4 +54,5 @@ CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_FAT_WRITE=y CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y CONFIG_CMD_DHRYSTONE=y diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 851732fa1da..65485118812 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -545,3 +545,13 @@ def test_ulib_demo_efi_riscv64(ubman): '/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd', '/usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd', ['--machine', 'virt']) + +@pytest.mark.localqemu +@pytest.mark.boardspec('efi-riscv_app64') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_efi_riscv64(ubman): + """Test the Rust ulib demo EFI app under QEMU RISC-V 64 with UEFI.""" + run_efi_rust_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> RUST_TARGET_arm is defined as aarch64-unknown-none and the Rust source no longer generates undefined panic symbols, so the Rust demo can now build and link for the ARM64 QEMU target. Enable CONFIG_RUST_EXAMPLES in qemu_arm64_defconfig and add a QEMU boot test that launches rust-demo.bin under qemu-system-aarch64. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- configs/qemu_arm64_defconfig | 1 + test/py/tests/test_ulib.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig index 507c08bc514..5c25bd313c6 100644 --- a/configs/qemu_arm64_defconfig +++ b/configs/qemu_arm64_defconfig @@ -78,3 +78,4 @@ CONFIG_UTHREAD=y CONFIG_UNIT_TEST=y CONFIG_ULIB=y CONFIG_EXAMPLES=y +CONFIG_RUST_EXAMPLES=y diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 65485118812..f4234253b7d 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -392,6 +392,13 @@ 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_arm64') +@pytest.mark.buildconfigspec("rust_examples") +def test_ulib_rust_demo_arm64(ubman): + """Test the Rust ulib demo binary under QEMU ARM64.""" + run_bios_rust_demo(ubman, 'qemu-system-aarch64', ['-cpu', 'cortex-a57']) + @pytest.mark.localqemu @pytest.mark.boardspec('qemu-riscv64') @pytest.mark.buildconfigspec("examples") -- 2.43.0
participants (1)
-
Simon Glass