[PATCH] efi: fix semihosting EFI payload booting

Heinrich Schuchardt xypron.glpk at gmx.de
Thu May 11 08:22:30 CEST 2023


On 5/11/23 02:00, Andre Przywara wrote:
> On Wed, 10 May 2023 23:19:33 +0200
> Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
>
> Hi,
>
>> On 5/10/23 19:26, Andre Przywara wrote:
>>> 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
>>
>> This does not tell me which arguments I should pass to qemu-system-aarch64.
>
> I would say that's out of scope for this document, and is explained in
> doc/boards/emulation/qemu-arm.rst. A minimum working example is:
> $ qemu-system-aarch64 -M virt -cpu cortex-a57 -nographic -bios u-boot.bin
>
>>> 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.
>>
>> If it is missing in Ubuntu's QEMU, please, indicate how I can build a
>> QEMU that allows testing. This information needs to go into
>> semihosting.rst.
>
> It's all there and it works on my 20.04 QEMU build out of the box. What
> I meant is that -semihosting is a QEMU command line *option*, and a very
> optional one, so to speak, as it's more an ARM low level debugger
> technology than anything else, and it was just adopted by QEMU. And
> until recently you had to make sure that you give that option to QEMU
> if you enable semihosting support in U-Boot, otherwise it would crash.
> For more details see Sean's FOSDEM talk:
> https://fosdem.org/2023/schedule/event/semihosting_uboot/
>
>>> Regardless I was doing those experiments with the FVP fastmodel, which is
>>
>> It seems FVP's are not open source
>> (https://developer.arm.com/Tools%20and%20Software/Fixed%20Virtual%20Platforms).
>>
>> We need some open source solution for testing the suggested changes.
>
> Sure, I just mentioned this because the models are just the reference
> implementation and natural habitat for semihosting: because it's
> enabled by default there, and it's an ancient ARM technology.
>
> If you want to test it, just use your favourite QEMU command line (or
> the one I mention above) and add "-semihosting". Then load your EFI app:
> => load hostfs - $kernel_addr_r app.efi
> => bootefi $kernel_addr_r
>
> Cheers,
> Andre

I have compiled qemu_arm64_defconfig with CONFIG_SERIAL_PROBE_ALL=y and
invoked qemu-system-aarch64 (1:7.2+dfsg-5ubuntu2) on Ubuntu 23.10 with
-semihosting.

qemu-system-aarch64 -semihosting \
-machine virt,gic-version=max -accel tcg,thread=multi \
-m 1G -smp cores=2 \
-bios denx/u-boot.bin -cpu cortex-a72 -nographic -gdb tcp::1234 \
-netdev user,id=eth0,tftp=tftp -device e1000,netdev=eth0,romfile= \
-drive if=none,file=arm64.img,format=raw,id=mydisk \
-drive if=pflash,format=raw,index=1,file=envstore.img \
-device virtio-rng-pci \
-device ich9-ahci,id=ahci -device ide-hd,drive=mydisk,bus=ahci.0

With and without your patch I get:

=> load hostfs - $kernel_addr_r foo
** No device specified **
Couldn't find partition hostfs -
Can't set block device

Please, describe in sufficient detail how to use semihosting.

Best regards

Heinrich

>
>>
>> Best regards
>>
>> Heinrich
>>
>>> 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