This commit introduces a new standard boot method that supports booting
via semihosting as provided by ARM FVP platforms.

Semihosting enables the virtual platform to access host-side files as if
they were block devices, which is particularly useful in early boot
development and simulation environments. It removes the need for physical
storage or networking when loading kernel components.

This method mirrors the distroboot logic for semihosting, which is the
default boot method for vexpress64 platform. The load_file_from_host
helper function is implemented to mirror behaviour of "load hostfs" u-boot
command, so it similarly sets filesize environment variable after loading a
file - this is useful for later commands.

This implementation is marked with BOOTMETH_GLOBAL so it is always
considered during bootflow scan without requiring a boot device.

Signed-off-by: Said Nasibov <said.nasi...@arm.com>
---
 boot/Kconfig        |  10 ++++
 boot/Makefile       |   1 +
 boot/bootmeth_smh.c | 113 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 boot/bootmeth_smh.c

diff --git a/boot/Kconfig b/boot/Kconfig
index 2ff6f003738..fbd3f068908 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -936,6 +936,16 @@ config BOOTMETH_SCRIPT
          This provides a way to try out standard boot on an existing boot flow.
          It is not enabled by default to save space.

+config BOOTMETH_SMH
+       bool "Bootdev support for semihosting"
+       depends on SEMIHOSTING
+       select CMD_FDT
+       select BOOTMETH_GLOBAL
+       help
+         Enables support for booting via semihosting. This bootmeth reads
+         files including the kernel, device tree and ramdisk directly from
+         the host's filesystem.
+
 config UPL
        bool "upl - Universal Payload Specification"
        imply CMD_UPL
diff --git a/boot/Makefile b/boot/Makefile
index 3da6f7a0914..5bed9e66507 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o
 obj-$(CONFIG_$(PHASE_)BOOTMETH_SCRIPT) += bootmeth_script.o
 obj-$(CONFIG_$(PHASE_)CEDIT) += cedit.o
 obj-$(CONFIG_$(PHASE_)BOOTMETH_EFI_BOOTMGR) += bootmeth_efi_mgr.o
+obj-$(CONFIG_$(PHASE_)BOOTMETH_SMH) += bootmeth_smh.o

 obj-$(CONFIG_$(PHASE_)OF_LIBFDT) += fdt_support.o
 obj-$(CONFIG_$(PHASE_)FDT_SIMPLEFB) += fdt_simplefb.o
diff --git a/boot/bootmeth_smh.c b/boot/bootmeth_smh.c
new file mode 100644
index 00000000000..2ceb66900ed
--- /dev/null
+++ b/boot/bootmeth_smh.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for booting using semihosting
+ *
+ * Copyright 2025 Arm Ltd.
+ * Written by Said Nasibov <said.nasi...@arm.com>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <bootmeth.h>
+#include <dm.h>
+#include <env.h>
+#include <fs.h>
+
+static int script_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+       return 0;
+}
+
+static int load_file_from_host(const char *name_var, const char *addr_var)
+{
+       const char *filename = env_get(name_var);
+       const char *addr_str = env_get(addr_var);
+       ulong addr;
+       loff_t len;
+       int ret;
+
+       /* Mount hostfs (semihosting) */
+       ret = fs_set_blk_dev("hostfs", NULL, FS_TYPE_ANY);
+       if (ret)
+               return log_msg_ret("hostfs", ret);
+
+       if (!filename || !addr_str)
+               return log_msg_ret("env missing", -EINVAL);
+
+       addr = hextoul(addr_str, NULL);
+       if (!addr)
+               return log_msg_ret("invalid addr", -EINVAL);
+
+       /* Read file into memory */
+       ret = fs_read(filename, addr, 0, 0, &len);
+       if (ret)
+               return log_msg_ret("fs_read", ret);
+
+       /* Set filesize environment variable in hex */
+       char size_str[32];
+       sprintf(size_str, "%lx", (unsigned long)len);
+       env_set("filesize", size_str);
+
+       return 0;
+}
+
+static int smh_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+       int ret;
+
+       ret = load_file_from_host("kernel_name", "kernel_addr_r");
+       if (ret)
+               return log_msg_ret("kernel", ret);
+
+       env_set("fdt_high", "0xffffffffffffffff");
+       env_set("initrd_high", "0xffffffffffffffff");
+
+       ret = load_file_from_host("fdtfile", "fdt_addr_r");
+       if (ret) {
+               ret = run_command("fdt move $fdtcontroladdr $fdt_addr_r;", 0);
+               if (ret)
+                       return log_msg_ret("fdt move", ret);
+       }
+
+       load_file_from_host("ramdisk_name", "ramdisk_addr_r");
+
+       bflow->state = BOOTFLOWST_READY;
+
+       return 0;
+}
+
+static int smh_boot(struct udevice *dev, struct bootflow *bflow)
+{
+       int ret = run_command("booti $kernel_addr_r 
${ramdisk_addr_r}:${filesize} ${fdt_addr_r};", 0);
+
+       return ret;
+}
+
+static int smh_bootmeth_bind(struct udevice *dev)
+{
+       struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+       plat->desc = "semihosting";
+       plat->flags = BOOTMETHF_GLOBAL;
+
+       return 0;
+}
+
+static struct bootmeth_ops smh_bootmeth_ops = {
+       .check          = script_check,
+       .read_bootflow  = smh_read_bootflow,
+       .boot           = smh_boot,
+};
+
+static const struct udevice_id smh_bootmeth_ids[] = {
+       { .compatible = "u-boot,smh" },
+       {}
+};
+
+U_BOOT_DRIVER(bootmeth_0smh) = {
+       .name           = "bootmeth_smh",
+       .id             = UCLASS_BOOTMETH,
+       .of_match       = smh_bootmeth_ids,
+       .ops            = &smh_bootmeth_ops,
+       .bind       = &smh_bootmeth_bind,
+};
--
2.34.1

IMPORTANT NOTICE: The contents of this email and any attachments are 
confidential and may also be privileged. If you are not the intended recipient, 
please notify the sender immediately and do not disclose the contents to any 
other person, use it for any purpose, or store or copy the information in any 
medium. Thank you.

Reply via email to