[PATCH 00/14] ulib: Add support for Rust main programs

From: GitLab CI Runner <trini@konsulko.com> So far ulib only supports linking against C programs. While this is the common case, there is no particular reason why other languages could not be used to write the main program, so long as they can interoperate with the libu-boot.so/.a library. Rust provides a Foreign Function Interface feature, which makes it possible to declare and call C functions from Rust code. It provides memory safety without the uncertainty of garbage collection and with less overhead. It would be nice if ulib could be used with Rust. This series provides the necessary pieces to make this work. It adds an example program which works just like the C one, but written in Rust. As with the existing example, the program can be built against the static ulib or can use dynamic linking. Note that only sandbox is supported at present, i.e. native execution on (for example) a Linux machine. Future work will add support for running on other architectures. In order to make more use of ulib features, U-Boot's headers will need to be made available in Rust format. For now, only a very few are provided, as a starting point. Simon Glass (13): docker: Provide a rust toolchain CI: Switch to new docker image examples: Correct dependencies for ulib ulib: Return the full version with ulib_get_version() ulib: Add a way to obtain the version ulib: Add a very basic rust library ulib: Add a Rust example doc: Fix a few nits in the ulib docs doc: ulib: Clarify calling functions in any header doc: ulib: Add Rust examples documentation test: ulib: Add a test for the rust example doc: Provide a motivation for ulib CI: Build the Rust examples Tom Rini (1): CI: Update to coreboot 25.03 .gitlab-ci.yml | 15 ++- Makefile | 18 +++- doc/develop/ulib.rst | 114 ++++++++++++++++++-- examples/rust/.gitignore | 5 + examples/rust/Cargo.lock | 14 +++ examples/rust/Cargo.toml | 17 +++ examples/rust/Makefile | 95 +++++++++++++++++ examples/rust/README.md | 120 +++++++++++++++++++++ examples/rust/build.rs | 105 +++++++++++++++++++ examples/rust/src/demo.rs | 112 ++++++++++++++++++++ examples/rust/src/rust_helper.rs | 59 +++++++++++ examples/ulib/demo.c | 2 +- examples/ulib/rules.mk | 13 +-- include/u-boot-lib.h | 9 +- lib/rust/.gitignore | 1 + lib/rust/Cargo.lock | 7 ++ lib/rust/Cargo.toml | 17 +++ lib/rust/src/lib.rs | 173 +++++++++++++++++++++++++++++++ lib/ulib/ulib.c | 3 +- test/py/tests/test_ulib.py | 65 ++++++++++++ tools/docker/Dockerfile | 10 +- 21 files changed, 947 insertions(+), 27 deletions(-) create mode 100644 examples/rust/.gitignore create mode 100644 examples/rust/Cargo.lock create mode 100644 examples/rust/Cargo.toml create mode 100644 examples/rust/Makefile create mode 100644 examples/rust/README.md create mode 100644 examples/rust/build.rs create mode 100644 examples/rust/src/demo.rs create mode 100644 examples/rust/src/rust_helper.rs create mode 100644 lib/rust/.gitignore create mode 100644 lib/rust/Cargo.lock create mode 100644 lib/rust/Cargo.toml create mode 100644 lib/rust/src/lib.rs -- 2.43.0 base-commit: 10d080a43d5a980a44cc64547fb2a41683cba272 branch: rus

From: Tom Rini <trini@konsulko.com> At this point there's problems rebuilding coreboot-24.08 without manual intervention. Let us upgrade to a newer version. Signed-off-by: Tom Rini <trini@konsulko.com> --- tools/docker/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index ceb7a25ad4d..38e1f3a941d 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -294,8 +294,8 @@ RUN mkdir /tmp/trace && \ rm -rf /tmp/trace # Build coreboot -RUN wget -O - https://coreboot.org/releases/coreboot-24.08.tar.xz | tar -C /tmp -xJ && \ - cd /tmp/coreboot-24.08 && \ +RUN wget -O - https://coreboot.org/releases/coreboot-25.03.tar.xz | tar -C /tmp -xJ && \ + cd /tmp/coreboot-25.03 && \ make crossgcc-i386 CPUS=$(nproc) && \ make -C payloads/coreinfo olddefconfig && \ make -C payloads/coreinfo && \ @@ -306,7 +306,7 @@ RUN wget -O - https://coreboot.org/releases/coreboot-24.08.tar.xz | tar -C /tmp make -j $(nproc) && \ sudo mkdir /opt/coreboot && \ sudo cp build/coreboot.rom build/cbfstool /opt/coreboot/ && \ - rm -rf /tmp/coreboot-24.08 + rm -rf /tmp/coreboot-25.03 # Create our user/group RUN echo uboot ALL=NOPASSWD: ALL > /etc/sudoers.d/uboot -- 2.43.0

From: Simon Glass <sjg@chromium.org> Install a Rust toolchain in the CI image so that it is possible to build the Rust examples. Co-developed-by: Claude <noreply@anthropic.com> Co-developed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> --- tools/docker/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 38e1f3a941d..ba98a293e4b 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -313,6 +313,10 @@ RUN echo uboot ALL=NOPASSWD: ALL > /etc/sudoers.d/uboot RUN useradd -m -U uboot USER uboot:uboot +# Install Rust toolchain for building Rust examples +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable +ENV PATH="/home/uboot/.cargo/bin:${PATH}" + # Populate the cache for pip to use. Get these via wget as the # COPY / ADD directives don't work as we need them to. RUN wget -O /tmp/pytest-requirements.txt https://source.denx.de/u-boot/u-boot/-/raw/master/test/py/requirements.txt -- 2.43.0

From: Simon Glass <sjg@chromium.org> Use an image with Rust support so we can test the Rust integration. Signed-off-by: Simon Glass <sjg@chromium.org> --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4e86801fdc..3785e61ec3f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,7 +20,7 @@ default: - ${DEFAULT_TAG} # Grab our configured image. The source for this is found # in the u-boot tree at tools/docker/Dockerfile - image: ${MIRROR_DOCKER}/trini/u-boot-gitlab-ci-runner:jammy-20250404-29Apr2025 + image: ${MIRROR_DOCKER}/sjg20/u-boot-gitlab-ci-runner:jammy-20250714-10Sep2025p2 services: - name: container-$(CI_JOB_ID) command: ["--rm"] -- 2.43.0

From: Simon Glass <sjg@chromium.org> We need to check for the ulib library before building any examples. Add this and ensure that the examples are rebuilt if the library changes. Signed-off-by: Simon Glass <sjg@chromium.org> --- examples/ulib/rules.mk | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/ulib/rules.mk b/examples/ulib/rules.mk index aba6c63d8b6..c4b0bdb3668 100644 --- a/examples/ulib/rules.mk +++ b/examples/ulib/rules.mk @@ -19,11 +19,11 @@ $(foreach obj,$(sys-objs),$(eval $(OUTDIR)/$(obj): \ # Automatic build rules for all programs $(foreach prog,$(progs),$(eval $(OUTDIR)/$(prog): \ - $$(addprefix $(OUTDIR)/,$$($(prog)_objs)) ; \ - $$(CC) $$(CFLAGS) -o $$@ $$^ $$(SHARED_LDFLAGS))) + $$(addprefix $(OUTDIR)/,$$($(prog)_objs)) $(UBOOT_BUILD)/libu-boot.so ; \ + $$(CC) $$(CFLAGS) -o $$@ $$(filter-out %.so,$$^) $$(SHARED_LDFLAGS))) $(foreach prog,$(progs),$(eval $(OUTDIR)/$(prog)_static: \ - $$(addprefix $(OUTDIR)/,$$($(prog)_objs)) ; \ - $$(CC) $$(CFLAGS) -o $$@ $$^ $$(STATIC_LDFLAGS))) + $$(addprefix $(OUTDIR)/,$$($(prog)_objs)) $(UBOOT_BUILD)/libu-boot.a ; \ + $$(CC) $$(CFLAGS) -o $$@ $$(filter-out %.a,$$^) $$(STATIC_LDFLAGS))) # Create the output directory if it doesn't exist $(OUTDIR): @@ -32,8 +32,3 @@ $(OUTDIR): # Default rule: compile with U-Boot headers $(OUTDIR)/%.o: $(EXAMPLE_DIR)/%.c | $(OUTDIR) $(CC) $(CFLAGS) $(UBOOT_CFLAGS) -c -o $@ $< - -# The U-Boot library must be built before we can link against it -# Order-only prerequisites ensure libraries exist before linking -$(all_bins): | $(UBOOT_BUILD)/libu-boot.a $(UBOOT_BUILD)/libu-boot.so \ - $(OUTDIR) \ No newline at end of file -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide the entire version string so it is possible to see the date of the build. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/ulib/ulib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ulib/ulib.c b/lib/ulib/ulib.c index 385faa60844..ccaa655f398 100644 --- a/lib/ulib/ulib.c +++ b/lib/ulib/ulib.c @@ -9,6 +9,7 @@ #include <string.h> #include <version.h> #include <asm/global_data.h> +#include <version_string.h> #include <u-boot-lib.h> /* Static storage for global data when using simplified API */ @@ -32,5 +33,5 @@ void ulib_uninit(void) const char *ulib_get_version(void) { - return PLAIN_VERSION; + return version_string; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> It is possible to simply read the 'version_string' variable, but it seems better to add a proper API call to get this. It is already defined in lib/ulib/ulib.c Add a prototype to u-boot-lib.h Make use of it from the demo program. Fix the comment for ulib_uninit() while we are here. Signed-off-by: Simon Glass <sjg@chromium.org> --- examples/ulib/demo.c | 2 +- include/u-boot-lib.h | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/ulib/demo.c b/examples/ulib/demo.c index 9d916d3878a..5077fcda0a6 100644 --- a/examples/ulib/demo.c +++ b/examples/ulib/demo.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) } demo_show_banner(); - printf("U-Boot version: %s\n", version_string); + printf("U-Boot version: %s\n", ulib_get_version()); printf("\n"); /* Use U-Boot's os_open to open a file */ diff --git a/include/u-boot-lib.h b/include/u-boot-lib.h index aabc77aa4eb..934cc33eff5 100644 --- a/include/u-boot-lib.h +++ b/include/u-boot-lib.h @@ -25,10 +25,17 @@ struct global_data; int ulib_init(char *progname); /** - * ulib_uninit() shut down the U-Boot librrary + * ulib_uninit() - shut down the U-Boot library * * Call this when your program has finished using the library, before it exits */ void ulib_uninit(void); +/** + * ulib_get_version() - Get the version string + * + * Return: Full U-Boot version string + */ +const char *ulib_get_version(void); + #endif -- 2.43.0

From: Simon Glass <sjg@chromium.org> Create a new lib/rust directory and add a very basic rust library in there. This will eventually expand to include more features. For now it only has a few ulib calls needed for the example programs. Co-developed-by: Claude <noreply@anthropic.com> Co-developed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/rust/.gitignore | 1 + lib/rust/Cargo.lock | 7 ++ lib/rust/Cargo.toml | 17 +++++ lib/rust/src/lib.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 lib/rust/.gitignore create mode 100644 lib/rust/Cargo.lock create mode 100644 lib/rust/Cargo.toml create mode 100644 lib/rust/src/lib.rs diff --git a/lib/rust/.gitignore b/lib/rust/.gitignore new file mode 100644 index 00000000000..2f7896d1d13 --- /dev/null +++ b/lib/rust/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/lib/rust/Cargo.lock b/lib/rust/Cargo.lock new file mode 100644 index 00000000000..dd8371f6255 --- /dev/null +++ b/lib/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "uboot-sys" +version = "0.1.0" diff --git a/lib/rust/Cargo.toml b/lib/rust/Cargo.toml new file mode 100644 index 00000000000..a321b31271e --- /dev/null +++ b/lib/rust/Cargo.toml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ +[package] +name = "u-boot-sys" +version = "0.1.0" +edition = "2021" +license = "GPL-2.0+" +description = "Rust FFI bindings for U-Boot library functions" +authors = ["Simon Glass <simon.glass@canonical.com>"] +repository = "https://concept.u-boot.org/u-boot/u-boot" +keywords = ["u-boot", "ffi", "bindings", "embedded"] +categories = ["external-ffi-bindings", "embedded"] + +[lib] +name = "u_boot_sys" +path = "src/lib.rs" + +[dependencies] diff --git a/lib/rust/src/lib.rs b/lib/rust/src/lib.rs new file mode 100644 index 00000000000..4ac9f9d32f1 --- /dev/null +++ b/lib/rust/src/lib.rs @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * U-Boot FFI bindings for Rust + * + * This crate provides Rust FFI bindings for U-Boot library functions, + * equivalent to include/u-boot-lib.h + * + * Copyright 2025 Canonical Ltd. + * Written by Simon Glass <simon.glass@canonical.com> + */ + +//! # U-Boot Bindings +//! +//! This crate provides Rust FFI bindings for U-Boot library functions. +//! It includes bindings for: +//! +//! - U-Boot library management functions (`ulib_*`) +//! - U-Boot API functions (`ub_*`) +//! - OS abstraction functions (`os_*`) +//! +//! ## Usage +//! +//! ```rust,no_run +//! use uboot_sys::{ulib_init, ulib_uninit, ub_printf}; +//! use std::ffi::CString; +//! +//! let program_name = CString::new("my_program").unwrap(); +//! unsafe { +//! if ulib_init(program_name.as_ptr() as *mut _) == 0 { +//! ub_printf(b"Hello from U-Boot!\n\0".as_ptr() as *const _); +//! ulib_uninit(); +//! } +//! } +//! ``` + +#![allow(clippy::manual_c_str_literals)] +#![allow(non_camel_case_types)] + +use std::os::raw::{c_char, c_int}; + +/// U-Boot library management functions +pub mod uboot_lib { + use super::*; + + extern "C" { + /// Set up the U-Boot library + /// + /// # Arguments + /// + /// * `progname` - Program name to use (must be a writeable string, + /// typically argv\[0\]) + /// + /// # Returns + /// + /// 0 if OK, negative error code on error + pub fn ulib_init(progname: *mut c_char) -> c_int; + + /// Shut down the U-Boot library + /// + /// Call this when your program has finished using the library, + /// before it exits + pub fn ulib_uninit(); + + /// Get the version string + /// + /// # Returns + /// + /// Full U-Boot version string + pub fn ulib_get_version() -> *const c_char; + } +} + +/// U-Boot API functions +pub mod uboot_api { + use super::*; + + extern "C" { + /// Print directly to console (equivalent to printf) + /// + /// # Arguments + /// + /// * `fmt` - The format string to use + /// * `...` - Arguments for the format string + /// + /// # Returns + /// + /// Number of characters printed + pub fn ub_printf(fmt: *const c_char, ...) -> c_int; + + /// Format a string and place it in a buffer + /// + /// # Arguments + /// + /// * `buf` - The buffer to place the result into + /// * `size` - The size of the buffer, including the trailing null + /// space + /// * `fmt` - The format string to use + /// * `...` - Arguments for the format string + /// + /// # Returns + /// + /// The number of characters which would be generated for the given + /// input, excluding the trailing null, as per ISO C99 + pub fn ub_snprintf( + buf: *mut c_char, + size: usize, + fmt: *const c_char, + ... + ) -> c_int; + + /// Format a string and place it in a buffer + /// + /// # Arguments + /// + /// * `buf` - The buffer to place the result into + /// * `fmt` - The format string to use + /// * `...` - Arguments for the format string + /// + /// # Returns + /// + /// The number of characters written into buf + pub fn ub_sprintf(buf: *mut c_char, fmt: *const c_char, ...) -> c_int; + } +} + +/// OS abstraction functions +pub mod os { + use super::*; + + extern "C" { + /// Access the OS open() system call + /// + /// # Arguments + /// + /// * `pathname` - Path to the file to open + /// * `flags` - Open flags + /// + /// # Returns + /// + /// File descriptor, or negative error code on error + pub fn os_open(pathname: *const c_char, flags: c_int) -> c_int; + + /// Access the OS close() system call + /// + /// # Arguments + /// + /// * `fd` - File descriptor to close + /// + /// # Returns + /// + /// 0 on success, negative error code on error + pub fn os_close(fd: c_int) -> c_int; + + /// Read a string from a file stream + /// + /// # Arguments + /// + /// * `s` - Buffer to store the string + /// * `size` - Maximum number of characters to read + /// * `fd` - File descriptor to read from + /// + /// # Returns + /// + /// Pointer to the string on success, null pointer on EOF or + /// error + pub fn os_fgets(s: *mut c_char, size: c_int, fd: c_int) -> *mut c_char; + } +} + +// Re-export commonly used functions at crate root +pub use uboot_lib::{ulib_get_version, ulib_init, ulib_uninit}; +pub use uboot_api::{ub_printf, ub_snprintf, ub_sprintf}; +pub use os::{os_close, os_fgets, os_open}; -- 2.43.0

From: Simon Glass <sjg@chromium.org> The U-Boot library can be used from Rust fairly easily. Add an example for this, following along the lines of the existing ulib example. Note that the new way of representing C strings is not used for now, since it is not available in v1.75 of cargo, as shipped by Ubuntu 24.04 Co-developed-by: Claude <noreply@anthropic.com> Co-developed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> --- Makefile | 18 ++++- examples/rust/.gitignore | 5 ++ examples/rust/Cargo.lock | 14 ++++ examples/rust/Cargo.toml | 17 +++++ examples/rust/Makefile | 95 ++++++++++++++++++++++++ examples/rust/README.md | 120 +++++++++++++++++++++++++++++++ examples/rust/build.rs | 105 +++++++++++++++++++++++++++ examples/rust/src/demo.rs | 112 +++++++++++++++++++++++++++++ examples/rust/src/rust_helper.rs | 59 +++++++++++++++ 9 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 examples/rust/.gitignore create mode 100644 examples/rust/Cargo.lock create mode 100644 examples/rust/Cargo.toml create mode 100644 examples/rust/Makefile create mode 100644 examples/rust/README.md create mode 100644 examples/rust/build.rs create mode 100644 examples/rust/src/demo.rs create mode 100644 examples/rust/src/rust_helper.rs diff --git a/Makefile b/Makefile index c4a5dbea199..8a55b944374 100644 --- a/Makefile +++ b/Makefile @@ -1049,7 +1049,7 @@ ifeq ($(NO_LIBS),) INPUTS-$(CONFIG_ULIB) += libu-boot.so test/ulib/ulib_test INPUTS-$(CONFIG_ULIB) += libu-boot.a test/ulib/ulib_test_static ifdef CONFIG_EXAMPLES -INPUTS-$(CONFIG_ULIB) += examples_ulib +INPUTS-$(CONFIG_ULIB) += examples_ulib examples_rust endif endif endif @@ -1950,6 +1950,18 @@ examples_ulib: libu-boot.a libu-boot.so FORCE PLATFORM_LIBS="$(PLATFORM_LIBS)" \ LIB_STATIC_LDS="$(abspath $(LIB_STATIC_LDS))" +PHONY += examples_rust +examples_rust: libu-boot.a libu-boot.so FORCE + @if command -v cargo >/dev/null 2>&1; then \ + $(MAKE) -C $(srctree)/examples/rust \ + UBOOT_BUILD=$(abspath $(obj)) \ + OUTDIR=$(abspath $(obj)/examples/rust) \ + srctree="$(abspath $(srctree))"; \ + else \ + echo "Skipping Rust examples (cargo not found)"; \ + echo "Install from https://rustup.rs/"; \ + fi + quiet_cmd_sym ?= SYM $@ cmd_sym ?= $(OBJDUMP) -t $< > $@ u-boot.sym: u-boot FORCE @@ -2383,6 +2395,10 @@ clean: $(clean-dirs) $(call cmd,rmfiles) @$(MAKE) -C $(srctree)/examples/ulib clean \ OUTDIR=$(abspath $(obj)/examples/ulib) + @if command -v cargo >/dev/null 2>&1 && [ -d $(srctree)/examples/rust ]; then \ + $(MAKE) -C $(srctree)/examples/rust clean \ + OUTDIR=$(abspath $(obj)/examples/rust); \ + fi @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '*.ko.*' -o -name '*.su' -o -name '*.pyc' \ diff --git a/examples/rust/.gitignore b/examples/rust/.gitignore new file mode 100644 index 00000000000..ff2e4c064a4 --- /dev/null +++ b/examples/rust/.gitignore @@ -0,0 +1,5 @@ +build-dynamic/ +build-static/ +demo +demo_static +target/ diff --git a/examples/rust/Cargo.lock b/examples/rust/Cargo.lock new file mode 100644 index 00000000000..bd483c2704f --- /dev/null +++ b/examples/rust/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "u-boot-sys" +version = "0.1.0" + +[[package]] +name = "uboot-rust-demo" +version = "0.1.0" +dependencies = [ + "u-boot-sys", +] diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml new file mode 100644 index 00000000000..e86f71af699 --- /dev/null +++ b/examples/rust/Cargo.toml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ +[package] +name = "uboot-rust-demo" +version = "0.1.0" +edition = "2021" +license = "GPL-2.0+" +description = "Rust demo program for U-Boot library" +authors = ["Simon Glass <simon.glass@canonical.com>"] + +[dependencies] +u-boot-sys = { path = "../../lib/rust" } + +[[bin]] +name = "demo" +path = "src/demo.rs" + +[build-dependencies] diff --git a/examples/rust/Makefile b/examples/rust/Makefile new file mode 100644 index 00000000000..d9fcedea60d --- /dev/null +++ b/examples/rust/Makefile @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for U-Boot Rust example +# +# Copyright 2025 Canonical Ltd. +# Written by Simon Glass <simon.glass@canonical.com> + +# This Makefile integrates with the U-Boot build system, similar to +# examples/ulib +# +# Usage: cd examples/rust; make UBOOT_BUILD=/tmp/b/sandbox srctree=../.. +# + +# Default paths - can be overridden +UBOOT_BUILD ?= /tmp/b/sandbox +srctree ?= ../.. +OUTDIR ?= . + +# Export for build.rs +export UBOOT_BUILD + +# Common source dependencies +RUST_SOURCES = Cargo.toml src/demo.rs build.rs + +# Absolute output directory for Cargo builds +OUTDIR_ABS = $(abspath $(OUTDIR)) + +# Ensure 'cargo' is available +CARGO := $(shell command -v cargo 2> /dev/null) +ifeq ($(CARGO),) +$(error "Cargo not found. Please install Rust toolchain from \ +https://rustup.rs/") +endif + +# Default target - build both static and dynamic versions like examples_ulib +all: $(OUTDIR)/demo $(OUTDIR)/demo_static + +# Create output directory +$(OUTDIR): + @mkdir -p $(OUTDIR) + +# Build dynamic version (links with libu-boot.so) +$(OUTDIR)/demo: $(RUST_SOURCES) $(UBOOT_BUILD)/libu-boot.so | $(OUTDIR) + @echo "Building Rust demo (dynamic) with library from $(UBOOT_BUILD)" + @echo "OUTDIR=$(OUTDIR), abspath=$(OUTDIR_ABS)" + @if [ ! -f "$(UBOOT_BUILD)/libu-boot.so" ]; then \ + echo "No shared library at $(UBOOT_BUILD)/libu-boot.so" >&2; \ + echo "Please build U-Boot: make sandbox_defconfig && make" >&2; \ + exit 1; \ + fi + @UBOOT_DYNAMIC=1 $(CARGO) build --target-dir \ + $(OUTDIR_ABS)/build-dynamic --release --bin demo -q + @cp $(OUTDIR_ABS)/build-dynamic/release/demo $(OUTDIR)/ + +# Build static version (links with libu-boot.a) +$(OUTDIR)/demo_static: $(RUST_SOURCES) $(UBOOT_BUILD)/libu-boot.a | $(OUTDIR) + @echo "Building Rust demo (static) with library from $(UBOOT_BUILD)" + @if [ ! -f "$(UBOOT_BUILD)/libu-boot.a" ]; then \ + echo "No static library at $(UBOOT_BUILD)/libu-boot.a" >&2; \ + echo "Please build U-Boot: make sandbox_defconfig && make" >&2; \ + exit 1; \ + fi + @$(CARGO) build --target-dir $(OUTDIR_ABS)/build-static --release \ + --bin demo -q + @cp $(OUTDIR_ABS)/build-static/release/demo $(OUTDIR)/demo_static + +demo: $(OUTDIR)/demo + +# Test the programs +test: $(OUTDIR)/demo $(OUTDIR)/demo_static + @echo "Testing Rust demos..." + @echo "Testing dynamic version:" + @LD_LIBRARY_PATH="$(UBOOT_BUILD)" $(OUTDIR)/demo + @echo "Testing static version:" + @$(OUTDIR)/demo_static + +# Clean build artifacts +clean: + $(CARGO) clean + @if [ "$(OUTDIR)" != "." ]; then \ + rm -rf $(OUTDIR_ABS)/build-dynamic \ + $(OUTDIR_ABS)/build-static $(OUTDIR)/demo \ + $(OUTDIR)/demo_static; \ + else \ + rm -f demo demo_static; \ + fi + +# Show cargo version and info +info: + @echo "Rust toolchain information:" + $(CARGO) --version + @echo "U-Boot build directory: $(UBOOT_BUILD)" + @echo "U-Boot source tree: $(srctree)" + +.PHONY: all test clean info diff --git a/examples/rust/README.md b/examples/rust/README.md new file mode 100644 index 00000000000..cc2a1d0b82f --- /dev/null +++ b/examples/rust/README.md @@ -0,0 +1,120 @@ +# Technical Notes - Rust U-Boot Integration + +This directory contains a simple Rust implementation for using U-Boot's +library. Both static and dynamic linking (of libu-boot) are available. + +This directory is intended to be used separately from U-Boot's build system, +e.g. by copying it somewhere else. + +For comprehensive documentation, see :doc:`/doc/develop/ulib`. + +## Build System Success + +The following components are included: + +### 1. u-boot-sys Crate + +The `lib/rust/u-boot-sys` crate provides FFI bindings organized into modules: +- `uboot_lib`: Library management functions (`ulib_*`) +- `uboot_api`: U-Boot API functions (`ub_*`) +- `os`: OS abstraction functions (`os_*`) + +Functions are re-exported at the crate root for convenience. + +### 2. Example Program Structure + +- `demo.rs`: Main program using the u-boot-sys crate +- `rust_helper.rs`: Helper functions similar to C demo_helper.c +- Modular design with proper separation of concerns + +### 3. Build Configuration +- `build.rs` configures linking with both static and dynamic libraries +- Uses `--whole-archive` for static linking to preserve U-Boot linker lists +- Links required system libraries (pthread, dl, rt, SDL2) with proper ordering +- Uses U-Boot's existing stack-protection implementation from stackprot.c + +### 4. Integration +- Makefile can be use standalone or from U-Boot build system +- Cargo project depends on `u-boot-sys` crate from `../../lib/rust` +- Examples are built automatically as part of U-Boot's build system + +### 5. Runtime Execution ✅ +- `demo` (dynamic) and `demo-static` executables work the same +- Calls library init with `ulib_init()` +- File operations using U-Boot's OS abstraction layer +- Uses renamed U-Boot library functions (`ub_printf`) +- Clean shutdown with `ulib_uninit()` + +## Architecture Overview + +### u-boot-sys Crate Design +- Located in `lib/rust/` for reuse across U-Boot Rust projects +- Follows Rust `*-sys` naming conventions for FFI binding crates +- Intended to provide a safe, well-documented interfaces to U-Boot functions +- Organized into logical modules with re-exports for convenience + +### Dependency Structure +``` +examples/rust/demo → u-boot-sys → U-Boot C library + ↓ + libu-boot.so/.a +``` + +### Build System Integration +- Uses standard Cargo dependency resolution +- Makefile provides U-Boot-specific environment setup +- Compatible with both U-Boot's build system and standalone Cargo builds + +## Features Demonstrated + +- **FFI Integration**: Shows how to call U-Boot C functions from Rust +- **Library Initialization**: Proper `ulib_init()` and `ulib_uninit()` usage +- **File Operations**: Using U-Boot's `os_open()`, `os_fgets()`, `os_close()` +- **Print Functions**: Using U-Boot's `ub_printf()` vs Rust's `println!()` +- **Mixed Languages**: Combining Rust and C functionality seamlessly + +## Prerequisites + +1. **Rust toolchain**: Install from https://rustup.rs/ +2. **U-Boot library**: Build U-Boot sandbox with library support: + ```bash + make sandbox_defconfig + make + ``` + +## Usage Examples + +```bash +# Ensure U-Boot is built first +# Note this builds rust examples at /tmp/b/sandbox/example/rust +make O=/tmp/b/sandbox sandbox_defconfig all + +# Build both versions using Makefile +cd examples/rust +make UBOOT_BUILD=/tmp/b/sandbox srctree=../.. + +# Or build directly with Cargo +env UBOOT_BUILD=/tmp/b/sandbox cargo build --release + +# Test dynamic version +LD_LIBRARY_PATH=/tmp/b/sandbox ./demo + +# Test static version +./demo-static + +# Run tests +make test +``` + +## License + +GPL-2.0+ (same as U-Boot) + +Note: Linking with U-Boot's GPL-2.0+ library makes your program subject to +GPL licensing terms. + +## Documentation and Further Reading + +- See `doc/develop/ulib.rst` +- **C examples**: Located in `examples/ulib/` for comparison +- **u-boot-sys crate**: API documentation via `cargo doc` in `lib/rust/` diff --git a/examples/rust/build.rs b/examples/rust/build.rs new file mode 100644 index 00000000000..9c713ba574c --- /dev/null +++ b/examples/rust/build.rs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Build script for U-Boot Rust demo + * + * This configures linking with the U-Boot library, similar to how + * the C examples are built in examples/ulib + * + * Copyright 2025 Canonical Ltd. + * Written by Simon Glass <simon.glass@canonical.com> + */ + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + // Get the U-Boot build directory from environment or use default + let uboot_build = env::var("UBOOT_BUILD") + .unwrap_or_else(|_| "/tmp/b/sandbox".to_string()); + + + let uboot_build_path = PathBuf::from(&uboot_build); + + println!("cargo:rerun-if-env-changed=UBOOT_BUILD"); + println!("cargo:rerun-if-changed=build.rs"); + + // Add library search path + println!("cargo:rustc-link-search=native={}", uboot_build_path.display()); + + // Check if dynamic linking is requested + if env::var("UBOOT_DYNAMIC").is_ok() { + // Dynamic linking with libu-boot.so + println!("cargo:rustc-link-lib=dylib=u-boot"); + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", uboot_build_path.display()); + } else { + // Static linking with libu-boot.a using whole-archive to ensure + // all symbols are included (required for U-Boot linker lists) + // Use the same linker script as the C examples for proper linker list ordering + let static_lib_path = uboot_build_path.join("libu-boot.a"); + // Use absolute path to the linker script in the source tree + let current_dir = env::current_dir().expect("Failed to get current directory"); + let linker_script_path = current_dir.join("../ulib/static.lds"); + + println!("cargo:rustc-link-arg=-Wl,-T,{}", linker_script_path.display()); + println!("cargo:rustc-link-arg=-Wl,--whole-archive"); + println!("cargo:rustc-link-arg={}", static_lib_path.display()); + println!("cargo:rustc-link-arg=-Wl,--no-whole-archive"); + println!("cargo:rustc-link-arg=-Wl,-z,noexecstack"); + + // Add required system libraries AFTER --no-whole-archive like the C version + println!("cargo:rustc-link-arg=-lpthread"); + println!("cargo:rustc-link-arg=-ldl"); + + // Get SDL libraries using sdl2-config like the C version does + if let Ok(output) = Command::new("sdl2-config").arg("--libs").output() { + if output.status.success() { + let libs_str = String::from_utf8_lossy(&output.stdout); + for lib_flag in libs_str.split_whitespace() { + if lib_flag.starts_with("-l") { + println!("cargo:rustc-link-arg={}", lib_flag); + } + } + } + } else { + // Fallback to just SDL2 if sdl2-config is not available + println!("cargo:rustc-link-arg=-lSDL2"); + } + } + + // For dynamic linking, link required system libraries normally + if env::var("UBOOT_DYNAMIC").is_ok() { + println!("cargo:rustc-link-lib=pthread"); + println!("cargo:rustc-link-lib=dl"); + println!("cargo:rustc-link-lib=rt"); + } + + // Optional SDL2 support (if available) - only for dynamic linking + if env::var("UBOOT_DYNAMIC").is_ok() { + if let Ok(sdl_libs) = env::var("SDL_LIBS") { + for lib in sdl_libs.split_whitespace() { + if let Some(lib_name) = lib.strip_prefix("-l") { + println!("cargo:rustc-link-lib={}", lib_name); + } + } + } + } + + // Define symbols that may be missing (from U-Boot troubleshooting docs) + // Note: __stack_chk_guard is provided by U-Boot's common/stackprot.c + println!("cargo:rustc-link-arg=-Wl,--defsym,sandbox_sdl_sync=0"); + + // For static linking, we need to ensure libc is available for atexit and other functions + if env::var("UBOOT_DYNAMIC").is_err() { + println!("cargo:rustc-link-arg=-lc"); + } + + // Add include path for headers if needed for bindgen (future enhancement) + let uboot_include = uboot_build_path.join("include"); + if uboot_include.exists() { + println!("cargo:include={}", uboot_include.display()); + } + + // Recompile if main source changes + println!("cargo:rerun-if-changed=src/demo.rs"); +} diff --git a/examples/rust/src/demo.rs b/examples/rust/src/demo.rs new file mode 100644 index 00000000000..4ff58e700c7 --- /dev/null +++ b/examples/rust/src/demo.rs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Rust demo program showing U-Boot library functionality + * + * This demonstrates using U-Boot library functions from Rust, + * modeled after examples/ulib/demo.c + * + * Copyright 2025 Canonical Ltd. + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#![allow(clippy::manual_c_str_literals)] + +use std::ffi::CString; +use std::os::raw::{c_char, c_int}; + +mod rust_helper; + +use rust_helper::{demo_add_numbers, demo_show_banner, demo_show_footer}; +use u_boot_sys::{os_close, os_fgets, os_open, ub_printf, ulib_get_version, + ulib_init, ulib_uninit}; + +fn main() -> Result<(), Box<dyn std::error::Error>> { + // Get program name for ulib_init + let args: Vec<String> = std::env::args().collect(); + let program_name = CString::new(args[0].clone())?; + + // Init U-Boot library + let ret = unsafe { + ulib_init(program_name.as_ptr() as *mut c_char) + }; + if ret != 0 { + eprintln!("Failed to initialize U-Boot library"); + return Err("ulib_init failed".into()); + } + + demo_show_banner(); + + // Display U-Boot version using ulib_get_version() + let version_ptr = unsafe { ulib_get_version() }; + unsafe { + ub_printf( + b"U-Boot version: %s\n\0".as_ptr() as *const c_char, + version_ptr, + ); + ub_printf(b"\n\0".as_ptr() as *const c_char); + } + + // Use U-Boot's os_open() to open a file + let filename = CString::new("/proc/version")?; + let fd = unsafe { os_open(filename.as_ptr(), 0) }; + if fd < 0 { + eprintln!("Failed to open /proc/version"); + unsafe { ulib_uninit(); } + return Err("os_open failed".into()); + } + + unsafe { + ub_printf( + b"System version:\n\0".as_ptr() as *const c_char, + ); + } + + // Read lines using U-Boot's os_fgets() + let mut lines = 0; + let mut buffer = [0i8; 256]; // Use array instead of Vec to avoid heap + // allocation + loop { + let result = unsafe { + os_fgets(buffer.as_mut_ptr(), buffer.len() as c_int, fd) + }; + if result.is_null() { + break; + } + + unsafe { + ub_printf( + b" %s\0".as_ptr() as *const c_char, + buffer.as_ptr(), + ); + } + lines += 1; + } + + unsafe { os_close(fd) }; + + unsafe { + ub_printf( + b"\nRead %d line(s) using U-Boot library functions.\n\0" + .as_ptr() as *const c_char, + lines, + ); + } + + // Test the helper function + let result = demo_add_numbers(42, 13); + unsafe { + ub_printf( + b"Helper function result: %d\n\0".as_ptr() as *const c_char, + result, + ); + } + + demo_show_footer(); + + // Clean up + unsafe { + ulib_uninit(); + } + + Ok(()) +} diff --git a/examples/rust/src/rust_helper.rs b/examples/rust/src/rust_helper.rs new file mode 100644 index 00000000000..e2270065124 --- /dev/null +++ b/examples/rust/src/rust_helper.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Helper functions for Rust U-Boot library demo + * + * Copyright 2025 Canonical Ltd. + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#![allow(clippy::manual_c_str_literals)] + +use std::os::raw::c_char; + +use u_boot_sys::ub_printf; + +/// Show the demo banner +pub fn demo_show_banner() { + unsafe { + ub_printf( + b"U-Boot Library Demo Helper\n\0".as_ptr() as *const c_char, + ); + ub_printf( + b"==========================\n\0".as_ptr() as *const c_char, + ); + } +} + +/// Show the demo footer +pub fn demo_show_footer() { + unsafe { + ub_printf( + b"=================================\n\0".as_ptr() as *const c_char, + ); + ub_printf( + b"Demo complete (hi from rust)\n\0".as_ptr() as *const c_char, + ); + } +} + +/// Add two numbers and print the result +/// +/// # Arguments +/// +/// * `a` - First number +/// * `b` - Second number +/// +/// # Returns +/// +/// Sum of the two numbers +pub fn demo_add_numbers(a: i32, b: i32) -> i32 { + unsafe { + ub_printf( + b"helper: Adding %d + %d = %d\n\0".as_ptr() as *const c_char, + a, + b, + a + b, + ); + } + a + b +} \ No newline at end of file -- 2.43.0

From: Simon Glass <sjg@chromium.org> TIdy up a few minor typos and grammar errors. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/ulib.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/develop/ulib.rst b/doc/develop/ulib.rst index d68ffcaa964..ccef5f36e3c 100644 --- a/doc/develop/ulib.rst +++ b/doc/develop/ulib.rst @@ -6,7 +6,7 @@ U-Boot Library (ulib) The U-Boot Library (ulib) allows U-Boot to be built as a shared or static library that can be used by external programs. This enables reuse of U-Boot functionality in test programs and other applications without needing to -build that functionality directly into U-Boot image. +build that functionality directly into a U-Boot image. Please read `License Implications`_ below. @@ -94,7 +94,7 @@ This is possible using the provided examples as a template. The ``examples/ulib` directory contains a standalone Makefile that can build programs against a pre-built U-Boot library. -The examples works as expected, but note that as soon as you want to call +The examples work as expected, but note that as soon as you want to call functions that are not in the main API headers, you may have problems with missing dependencies and header files. See below. @@ -104,11 +104,11 @@ Including U-Boot header files from outside ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ U-Boot has many header files, some of which are arch-specific. These are -typically including via:: +typically included via:: #include <asm/...> -and are located in the ```arch/<arch>/include/asm/...`` directory within the +and are located in the ``arch/<arch>/include/asm/...`` directory within the U-Boot source tree. You will need to ensure that this directory is present in the include path. @@ -204,7 +204,7 @@ out of memory or simple crashes during library init. Dependencies ------------ -When linking with the U-Boot library for sanbod, you may need these system +When linking with the U-Boot library for sandbox, you may need these system libraries: * ``pthread`` - POSIX threads @@ -341,7 +341,7 @@ The format rules are: * Lines starting with ``file:`` specify a header file * Indented lines (space or tab) define symbols from that header -* Use ``symbol=new_name`` for custom renaming, otherwise ``ub_`` a prefix is +* Use ``symbol=new_name`` for custom renaming, otherwise a ``ub_`` prefix is added by default. No space around ``=`` * Use ``#`` at the beginning of a line for a comment * Empty lines are allowed -- 2.43.0

From: Simon Glass <sjg@chromium.org> It is already possible to call functions in any U-Boot header. Add more detail to explain what is meant in this 'future work' item. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/ulib.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/develop/ulib.rst b/doc/develop/ulib.rst index ccef5f36e3c..b2510a781aa 100644 --- a/doc/develop/ulib.rst +++ b/doc/develop/ulib.rst @@ -415,5 +415,6 @@ Future Work * Selective inclusion of U-Boot subsystems * API versioning and stability guarantees * pkg-config support for easier integration -* Support for calling functions in any U-Boot header +* Support for calling functions in any U-Boot header, without needing the source + tree * Improved symbol renaming with namespace support -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add docs for the Rust examples in the ulib documentation, covering: - Rust demo programs (dynamic and static linking) - Build instructions using both Makefile and cargo - u-boot-sys crate structure and FFI bindings - Building examples outside the U-Boot tree This provides parallel documentation to the existing C examples, making it possible to use either language with ulib. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/ulib.rst | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/doc/develop/ulib.rst b/doc/develop/ulib.rst index b2510a781aa..56e4e437dbf 100644 --- a/doc/develop/ulib.rst +++ b/doc/develop/ulib.rst @@ -180,6 +180,66 @@ The Makefile supports both single-file and multi-object programs through the ``demo-objs`` variable. Set this to build from multiple object files, or leave empty to build directly from source. +Rust Examples +------------- + +U-Boot also includes Rust examples that demonstrate the same functionality using +the ``u-boot-sys`` crate: + +**Rust Demo Program** + +The ``examples/rust`` directory contains Rust examples: + +* ``demo`` - Dynamically linked Rust demo program +* ``demo_static`` - Statically linked Rust version + +These Rust examples demonstrate: + +* Using the ``u-boot-sys`` crate for FFI bindings +* Proper library initialization with ``ulib_init()`` +* Using U-Boot OS functions like ``os_open()``, ``os_fgets()``, ``os_close()`` +* Using renamed U-Boot library functions (e.g., ``ub_printf()``) +* Modular program structure with helper functions in ``rust_helper.rs`` +* Proper cleanup with ``ulib_uninit()`` + +**Building Rust Examples** + +To build and run the Rust examples:: + + # Make sure U-Boot itself is built + make O=/tmp/b/sandbox sandbox_defconfig all + + cd examples/rust + make UBOOT_BUILD=/tmp/b/sandbox srctree=../.. + ./demo_static + +Or using Cargo directly:: + + cd examples/rust + env UBOOT_BUILD=/tmp/b/sandbox cargo build --release --bin demo + LD_LIBRARY_PATH=/tmp/b/sandbox ./target/release/demo + +**Rust Crate Structure** + +The Rust examples use the ``u-boot-sys`` crate located in ``lib/rust/``, which provides: + +* FFI bindings for U-Boot library functions (``ulib_*``) +* FFI bindings for U-Boot API functions (``ub_*``) +* FFI bindings for OS abstraction functions (``os_*``) +* Proper Rust documentation and module organization + +The crate follows Rust ``*-sys`` naming conventions for low-level FFI bindings. + +**Building Rust Examples Outside U-Boot Tree** + +The Rust examples can be built independently using the ``u-boot-sys`` crate:: + + cd examples/rust + env UBOOT_BUILD=/path/to/uboot/build cargo build --release + +The examples demonstrate both static and dynamic linking approaches compatible +with the Rust toolchain. + Linking and the Linker Script ----------------------------- @@ -418,3 +478,4 @@ Future Work * Support for calling functions in any U-Boot header, without needing the source tree * Improved symbol renaming with namespace support +* Expanded features in the lib/rust library -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide a test which is similar to the ulib example, to make sure that the Rust programs can be built correctly. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/tests/test_ulib.py | 65 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/py/tests/test_ulib.py b/test/py/tests/test_ulib.py index 76d40e1385c..9b8b7097db4 100644 --- a/test/py/tests/test_ulib.py +++ b/test/py/tests/test_ulib.py @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2025, Canonical Ltd. +"""Test U-Boot library functionality""" + import os import subprocess import pytest @@ -74,6 +76,31 @@ def check_demo_output(ubman, out): assert lines[i] == expected, \ f"Line {i}: expected '{expected}', got '{lines[i]}'" +def check_rust_demo_output(_ubman, out): + """Check output from the Rust ulib demo programs exactly line by line""" + lines = out.split('\n') + + # Read the actual system version from /proc/version + with open('/proc/version', 'r', encoding='utf-8') as f: + proc_version = f.read().strip() + + # Check individual parts of the output + assert len(lines) == 13, f"Expected 13 lines, got {len(lines)}" + + assert lines[0] == 'U-Boot Library Demo Helper\r' + assert lines[1] == '==========================\r' + assert lines[2].startswith('U-Boot version: ') and lines[2].endswith('\r') + assert lines[3] == '\r' + assert lines[4] == 'System version:\r' + assert lines[5] == f' {proc_version}\r' + assert lines[6] == '\r' + assert lines[7] == 'Read 1 line(s) using U-Boot library functions.\r' + assert lines[8] == 'helper: Adding 42 + 13 = 55\r' + assert lines[9] == 'Helper function result: 55\r' + assert lines[10] == '=================================\r' + assert lines[11] == 'Demo complete (hi from rust)\r' + assert lines[12] == '' + @pytest.mark.boardspec('sandbox') def test_ulib_demos(ubman): """Test both ulib demo programs (dynamic and static).""" @@ -107,6 +134,44 @@ def test_ulib_demos(ubman): out_dynamic = utils.run_and_log(ubman, [demo], env=env) check_demo_output(ubman, out_dynamic) +@pytest.mark.boardspec('sandbox') +def test_ulib_rust_demos(ubman): + """Test both Rust ulib demo programs (dynamic and static).""" + + build = ubman.config.build_dir + src = ubman.config.source_dir + 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') + + # Check if cargo is available + try: + utils.run_and_log(ubman, ['cargo', '--version']) + except (subprocess.CalledProcessError, FileNotFoundError): + pytest.skip('cargo not found - Rust toolchain required') + + # Build the Rust demo programs - clean first to ensure fresh build + cmd = ['make', 'clean'] + utils.run_and_log(ubman, cmd, cwd=examples) + + cmd = ['make', f'UBOOT_BUILD={os.path.abspath(build)}', f'srctree={src}'] + utils.run_and_log(ubman, cmd, cwd=examples) + + # Test static demo program + demo_static = os.path.join(examples, 'demo_static') + out_static = utils.run_and_log(ubman, [demo_static]) + check_rust_demo_output(ubman, out_static) + + # Test dynamic demo program (with proper LD_LIBRARY_PATH) + demo = os.path.join(examples, 'demo') + env = os.environ.copy() + env['LD_LIBRARY_PATH'] = os.path.abspath(build) + out_dynamic = utils.run_and_log(ubman, [demo], env=env) + check_rust_demo_output(ubman, out_dynamic) + @pytest.mark.boardspec('sandbox') def test_ulib_api_header(ubman): """Test that the u-boot-api.h header is generated correctly.""" -- 2.43.0

From: Simon Glass <sjg@chromium.org> The reason for providing a U-Boot library will not be obvious to many. Add a comment about this. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/ulib.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/doc/develop/ulib.rst b/doc/develop/ulib.rst index 56e4e437dbf..e6ee5aa98c9 100644 --- a/doc/develop/ulib.rst +++ b/doc/develop/ulib.rst @@ -10,6 +10,44 @@ build that functionality directly into a U-Boot image. Please read `License Implications`_ below. +Motivation +---------- + +U-Boot contains a vast arrange of functionality. It supports standard boot, +native execution (sandbox) for development and testing, filesystems, networking, +all sorts of boot protocols, drivers and support for about 1300 boards, a full +command line interface, a configuration editor / graphical menu, good Linux +compatibility for porting drivers, a powerful but efficient driver model which +uses Linux devicetree and many other features. The code base is fairly modern, +albeit with some dark spaces. Unusually for firmware, U-Boot provides a vast +array of tests. It can boot EFI apps or as an EFI app. It supports most relevant +architectures and modern SoCs. + +But of course time marches on and innovation must continue. U-Boot will clearly +be part of the picture in the future, but what is next? + +Ulib is an attempt to make U-Boot's functionality more easily available to other +projects, so they can build on it improve it or even replace parts of it. Ulib +aims to open up the capabilities of U-Boot to new use cases. + +Ulib also provides the ability to write the main program in another language. +For now C and Rust are supported, but Python should also be possible, albeit +with a significant amount of work. + +Few can predict where boot firmware will be in 10 years. The author of this file +rashly believes that we may have moved on from U-Boot, EFI and many other things +considered essential today. Perhaps firmware will be written in Rust or Zig or +Carbon or some other new language. Our AI overlords may be capable of writing +firmware based on a specification, if we can feed them enough electricity. Or it +could be that the complexity of SoCs grows at such a rate that we just carry on +as we are, content to just be able to make something boot. + +Ulib aims to provide a bridge from the best (more or less) of what we have today +to whatever it is that will replace it. Ulib is not an end itself, just a +platform for further innovation in booting, to new languages, new boot protocols +and new development methods. + + Building the Libraries ---------------------- -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add the Rust examples to the 'Examples' test suite, so that we can be sure they continue to build and run. Signed-off-by: Simon Glass <sjg@chromium.org> --- .gitlab-ci.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3785e61ec3f..fbe56152d7a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -349,12 +349,19 @@ Examples: ./tools/buildman/buildman -T0 -o ${UBOOT_TRAVIS_BUILD_DIR} -w --board sandbox; set -e; - - echo "Building examples"; + - echo "Building C examples"; cd examples/ulib; make UBOOT_BUILD=${UBOOT_TRAVIS_BUILD_DIR} srctree=../.. - - echo "Running static"; + - echo "Running C static"; ./demo_static - - echo "Running dynamic"; + - echo "Running C dynamic"; + LD_LIBRARY_PATH=${UBOOT_TRAVIS_BUILD_DIR} ./demo + - echo "Building Rust examples"; + cd ../rust; + make UBOOT_BUILD=${UBOOT_TRAVIS_BUILD_DIR} srctree=../.. + - echo "Running Rust static"; + ./demo_static + - echo "Running Rust dynamic"; LD_LIBRARY_PATH=${UBOOT_TRAVIS_BUILD_DIR} ./demo # Template for running the 'make check' tools -- 2.43.0
participants (1)
-
Simon Glass