[PATCH] efi: fix semihosting EFI payload booting

Andre Przywara andre.przywara at arm.com
Wed May 10 19:26:26 CEST 2023


On Wed, 10 May 2023 17:58:06 +0200
Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:

Hi,

> On 5/10/23 16:13, Andre Przywara wrote:
> > At the moment any naive attempt to boot an EFI payload that has just
> > been loaded via "hostfs" (sandbox or semihosting) is met by a rather
> > confusing error message:
> > ===========
> > VExpress64# load hostfs - $kernel_addr_r Image
> > 52752896 bytes read in 8 ms (6.1 GiB/s)
> > VExpress64# bootefi $kernel_addr_r
> > No UEFI binary known at 0x80080000
> > ===========
> > Actually explicitly providing the filesize:
> > VExpress64# bootefi $kernel_addr_r:$filesize
> > works around that problem, but the issue lies deeper: the call to
> > efi_set_bootdev() (as done by the generic load code) bails out at some
> > point, leaving the image_addr and image_size variables unset, which
> > triggers this message. The problem seems to be that "-" is not
> > understood by the code creating an UEFI device path. We could try to fix
> > just that, but actually semihosting seems to have some explicit support
> > in UEFI (at least it does in EDK II): there is a separate GUID for it,
> > and hostfs is significantly different in some aspects to justify special
> > handling.
> >
> > Check for the device name being "hostfs" and create a specific UEFI device
> > path for semihosting in this case. This uses the GUID used by EDK II for
> > almost 15 years.
> > This fixes the above load/bootefi sequence without requiring an explicit
> > file size argument.
> >
> > Signed-off-by: Andre Przywara <andre.przywara at arm.com>  
> 
> Could you, please, indicate how to invoke QEMU with semihosting enabled.
> This information needs to be added to doc/usage/semihosting.rst.

It's already there:
https://u-boot.readthedocs.io/en/latest/usage/semihosting.html#qemu

It's mostly just "-semihosting" on the QEMU side, but it's not enabled by
default in the QEMU defconfig, because it *was* needed to be provided in sync
with the QEMU option. It's a debug feature, so there is no good discovery
mechanism, really. I think Sean made this work, with this autodetect via
trap feature, so we might want to enable this option now, at least for the
filesystem part.

Regardless I was doing those experiments with the FVP fastmodel, which is
the most original semihosting provider, I think, and always enables
semihosting.

> Can there be multiple semihosting block devices?

There is no semihosting *block device*, it's a pure filesystem interface.
And there is conceptually only one instance of that: it's basically a
hook in the debugger (or emulation software) to provide some kind of
syscall interface, triggered by a certain instruction ("brk" on AArch64).
This allows some very limited access to the host filesystem - read files,
write files, but no listing, for instance.
It is super convenient to launch the model and load the kernel (or
whatever) directly from your build machine's filesystem - especially if
you do bisects or trial-and-error experiments.

> > ---
> >   lib/efi_loader/efi_device_path.c | 34 ++++++++++++++++++++++++++++++++
> >   1 file changed, 34 insertions(+)
> >
> > diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
> > index e2e98a39be1..b6d2074dd70 100644
> > --- a/lib/efi_loader/efi_device_path.c
> > +++ b/lib/efi_loader/efi_device_path.c
> > @@ -1079,6 +1079,35 @@ struct efi_device_path *efi_dp_from_uart(void)
> >   	return buf;
> >   }
> >
> > +#define SEMIHOSTING_GUID \
> > +	EFI_GUID(0xc5b9c74a, 0x6d72, 0x4719, \
> > +		 0x99, 0xab, 0xc5, 0x9f, 0x19, 0x90, 0x91, 0xeb)  
> 
> Can semihosting be moved to the driver model?

Mmh, I am not sure this is a thing, since there is no block device, it's a
filesystem. Which is part of the problem, I believe, and probably also a
good reason to treat it separately.

> Then we could create a generic catch all for device path nodes for all
> uclasses.

Need to digest that idea ....

Cheers,
Andre

> 
> Best regards
> 
> Heinrich
> 
> > +
> > +struct efi_device_path *efi_dp_from_semihosting(void)
> > +{
> > +	const struct efi_device_path_vendor smh_vendor = {
> > +		.dp = {
> > +			.type     = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
> > +			.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
> > +			.length   = sizeof(smh_vendor),
> > +		},
> > +		.guid = SEMIHOSTING_GUID,
> > +	};
> > +	void *buf, *pos;
> > +	size_t dpsize = sizeof(smh_vendor) + sizeof(END);
> > +
> > +	buf = efi_alloc(dpsize);
> > +	if (!buf)
> > +		return NULL;
> > +	pos = buf;
> > +	memcpy(pos, &smh_vendor, sizeof(smh_vendor));
> > +	pos += sizeof(smh_vendor);
> > +
> > +	memcpy(pos, &END, sizeof(END));
> > +
> > +	return buf;
> > +}
> > +
> >   #ifdef CONFIG_NETDEVICES
> >   struct efi_device_path *efi_dp_from_eth(void)
> >   {
> > @@ -1210,6 +1239,11 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
> >   			*device = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
> >   						  (uintptr_t)image_addr,
> >   						  image_size);
> > +	} else if (!strcmp(dev, "hostfs")) {
> > +		efi_get_image_parameters(&image_addr, &image_size);
> > +
> > +		if (device)
> > +			*device = efi_dp_from_semihosting();
> >   	} else {
> >   		part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
> >   					       1);  
> 



More information about the U-Boot mailing list