[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