[Concept,07/17] ulib: arm: Add demo binary and examples for qemu_arm64

Message ID 20260216013511.4079770-8-sjg@u-boot.org
State New
Headers
Series ulib: Add multi-arch demo and EFI app support |

Commit Message

Simon Glass Feb. 16, 2026, 1:34 a.m. UTC
  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(-)
  

Patch

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)