@@ -9,7 +9,9 @@
#include <dm.h>
#include <env.h>
+#include <fdt_support.h>
#include <fs_legacy.h>
+#include <linux/libfdt.h>
#include <mapmem.h>
#include <pxe_utils.h>
#include <test/test.h>
@@ -25,6 +27,10 @@
/* Memory address for loading files */
#define PXE_LOAD_ADDR 0x01000000
+#define PXE_KERNEL_ADDR 0x02000000
+#define PXE_INITRD_ADDR 0x02800000
+#define PXE_FDT_ADDR 0x03000000
+#define PXE_OVERLAY_ADDR 0x03100000
/**
* struct pxe_test_info - context for the test getfile callback
@@ -233,3 +239,82 @@ static int pxe_test_parse_norun(struct unit_test_state *uts)
PXE_TEST_ARGS(pxe_test_parse_norun, UTF_CONSOLE | UTF_MANUAL,
{ "fs_image", UT_ARG_STR },
{ "cfg_path", UT_ARG_STR });
+
+/**
+ * Test booting via sysboot command
+ *
+ * This test:
+ * 1. Binds a filesystem image containing extlinux.conf
+ * 2. Sets up environment variables for file loading
+ * 3. Runs sysboot to boot the default label
+ * 4. Verifies files were loaded by checking console output
+ */
+static int pxe_test_sysboot_norun(struct unit_test_state *uts)
+{
+ const char *fs_image = ut_str(PXE_ARG_FS_IMAGE);
+ const char *cfg_path = ut_str(PXE_ARG_CFG_PATH);
+ void *kernel, *initrd, *fdt;
+
+ ut_assertnonnull(fs_image);
+ ut_assertnonnull(cfg_path);
+
+ /* Bind the filesystem image */
+ ut_assertok(run_commandf("host bind 0 %s", fs_image));
+
+ /* Set environment variables for file loading */
+ ut_assertok(env_set_hex("pxefile_addr_r", PXE_LOAD_ADDR));
+ ut_assertok(env_set_hex("kernel_addr_r", PXE_KERNEL_ADDR));
+ ut_assertok(env_set_hex("ramdisk_addr_r", PXE_INITRD_ADDR));
+ ut_assertok(env_set_hex("fdt_addr_r", PXE_FDT_ADDR));
+ ut_assertok(env_set_hex("fdtoverlay_addr_r", PXE_OVERLAY_ADDR));
+ ut_assertok(env_set("bootfile", cfg_path));
+
+ /*
+ * Run sysboot - it will try all labels and return 0 after failing
+ * to boot them all (since sandbox can't actually boot Linux)
+ */
+ ut_assertok(run_commandf("sysboot host 0:0 any %x %s",
+ PXE_LOAD_ADDR, cfg_path));
+
+ /* Skip menu output and find the first label boot attempt */
+ ut_assert_skip_to_line("Enter choice: 1:\tBoot Linux");
+
+ /* Verify files were loaded in order */
+ ut_assert_nextline("Retrieving file: /vmlinuz");
+ ut_assert_nextline("Retrieving file: /initrd.img");
+ ut_assert_nextline("append: root=/dev/sda1 quiet");
+ ut_assert_nextline("Retrieving file: /dtb/board.dtb");
+ ut_assert_nextline("Retrieving file: /dtb/overlay1.dtbo");
+ ut_assert_nextline("Retrieving file: /dtb/overlay2.dtbo");
+
+ /* Boot fails on sandbox */
+ ut_assert_nextline("Unrecognized zImage");
+ ut_assert_nextlinen(" unmap_physmem");
+
+ /* Verify files were loaded at the correct addresses */
+ kernel = map_sysmem(PXE_KERNEL_ADDR, 0);
+ initrd = map_sysmem(PXE_INITRD_ADDR, 0);
+ fdt = map_sysmem(PXE_FDT_ADDR, 0);
+
+ /* Kernel should contain "kernel" at start */
+ ut_asserteq_mem("kernel", kernel, 6);
+
+ /* Initrd should contain "ramdisk" at start */
+ ut_asserteq_mem("ramdisk", initrd, 7);
+
+ /* FDT should have valid magic number */
+ ut_assertok(fdt_check_header(fdt));
+
+ /* Verify overlays were applied - check for properties added by overlays */
+ ut_asserteq_str("from-overlay1",
+ fdt_getprop(fdt, fdt_path_offset(fdt, "/test-node"),
+ "overlay1-property", NULL));
+ ut_asserteq_str("from-overlay2",
+ fdt_getprop(fdt, fdt_path_offset(fdt, "/test-node"),
+ "overlay2-property", NULL));
+
+ return 0;
+}
+PXE_TEST_ARGS(pxe_test_sysboot_norun, UTF_CONSOLE | UTF_MANUAL,
+ { "fs_image", UT_ARG_STR },
+ { "cfg_path", UT_ARG_STR });
@@ -13,10 +13,64 @@ Python handles filesystem image setup and configuration.
import os
import pytest
+import subprocess
from fs_helper import FsHelper
+# Simple base DTS with symbols enabled (for overlay support)
+BASE_DTS = """\
+/dts-v1/;
+
+/ {
+ model = "Test Board";
+ compatible = "test,board";
+
+ test: test-node {
+ test-property = <42>;
+ status = "okay";
+ };
+};
+"""
+
+# Simple overlay that modifies the test node
+OVERLAY1_DTS = """\
+/dts-v1/;
+/plugin/;
+
+&test {
+ overlay1-property = "from-overlay1";
+};
+"""
+
+# Another overlay that adds a different property
+OVERLAY2_DTS = """\
+/dts-v1/;
+/plugin/;
+
+&test {
+ overlay2-property = "from-overlay2";
+};
+"""
+
+
+def compile_dts(dts_content, output_path, is_overlay=False):
+ """Compile DTS content to DTB/DTBO file
+
+ Args:
+ dts_content (str): DTS source content
+ output_path (str): Path to output DTB/DTBO file
+ is_overlay (bool): True if this is an overlay (needs -@)
+
+ Raises:
+ subprocess.CalledProcessError: If dtc fails
+ """
+ # Use -@ for both base (to generate __symbols__) and overlays
+ cmd = ['dtc', '-@', '-I', 'dts', '-O', 'dtb', '-o', output_path]
+ subprocess.run(cmd, input=dts_content.encode(), check=True,
+ capture_output=True)
+
+
def create_extlinux_conf(srcdir, labels, menu_opts=None):
"""Create an extlinux.conf file with the given labels
@@ -190,6 +244,21 @@ def pxe_image(u_boot_config):
next_fname = f'nest{level + 1}.conf'
fd.write(f"\ninclude /extlinux/{next_fname}\n")
+ # Create DTB and overlay files for testing
+ dtbdir = os.path.join(fsh.srcdir, 'dtb')
+ os.makedirs(dtbdir, exist_ok=True)
+ compile_dts(BASE_DTS, os.path.join(dtbdir, 'board.dtb'))
+ compile_dts(OVERLAY1_DTS, os.path.join(dtbdir, 'overlay1.dtbo'),
+ is_overlay=True)
+ compile_dts(OVERLAY2_DTS, os.path.join(dtbdir, 'overlay2.dtbo'),
+ is_overlay=True)
+
+ # Create dummy kernel and initrd files with identifiable content
+ with open(os.path.join(fsh.srcdir, 'vmlinuz'), 'wb') as fd:
+ fd.write(b'kernel')
+ with open(os.path.join(fsh.srcdir, 'initrd.img'), 'wb') as fd:
+ fd.write(b'ramdisk')
+
# Create the filesystem
fsh.mk_fs()
@@ -201,6 +270,7 @@ def pxe_image(u_boot_config):
@pytest.mark.boardspec('sandbox')
+@pytest.mark.requiredtool('dtc')
class TestPxeParser:
"""Test PXE/extlinux parser APIs via C unit tests"""
@@ -210,3 +280,10 @@ class TestPxeParser:
with ubman.log.section('Test PXE parse'):
ubman.run_ut('pxe', 'pxe_test_parse',
fs_image=fs_img, cfg_path=cfg_path)
+
+ def test_pxe_sysboot(self, ubman, pxe_image):
+ """Test booting via sysboot command"""
+ fs_img, cfg_path = pxe_image
+ with ubman.log.section('Test PXE sysboot'):
+ ubman.run_ut('pxe', 'pxe_test_sysboot',
+ fs_image=fs_img, cfg_path=cfg_path)