[RFC PATCH 1/2] bootmeth: implement semihosting (smh) boot method
Andre Przywara
andre.przywara at arm.com
Wed Sep 10 18:48:09 CEST 2025
On Thu, 28 Aug 2025 14:06:56 +0100
Said Nasibov <said.nasibov at arm.com> wrote:
Hi Said,
thanks for posting this!
(quite a long CC: list, but missing the semihosting maintainer! ;-) Adding
Sean)
> 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
it's not really "block devices", but "on a filesystem"
> 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.
So I like that this exposes this to all platforms, but it seems to
inherit some VExpress specific choices. And while $fdtfile seems to be used
quite universally across the tree, $kernel_name and $ramdisk_name seem more
arbitrary. I guess they are fine, but would be curious to hear if someone
else has a pointer to prior art which sets file names for those components.
Also, when stdboot replaces distroboot in the next patch, this will
silently drop support for the Android boot images. I am personally fine
with this, but wonder if we should keep the current boot flow, to not
break existing users.
So either we add bootimg support here, protected by ARCH_VEXPRESS, or we
add it in a separate file, with some kind of platform specific callback?
>
> Signed-off-by: Said Nasibov <said.nasibov at 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.nasibov at 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");
As Tom already mentioned, this seems a bit of cargo cult, carried on from
generation to generation, we should drop this.
> +
> + 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");
Should we support running without an initramfs? So when this call fails,
we just use "-" for the second booti parameter.
But in general this seems to work, though it is a bit silent, in that it
doesn't announce the files (successfully) loaded, as it did before.
Cheers,
Andre
> +
> + 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,
> +};
More information about the U-Boot
mailing list