[PATCH v4 06/11] efi_loader: bootmgr: add booting from removable media
Heinrich Schuchardt
xypron.glpk at gmx.de
Mon Apr 4 23:54:57 CEST 2022
On 4/4/22 08:48, Masahisa Kojima wrote:
> On Sat, 2 Apr 2022 at 15:12, Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
>>
>> On 3/24/22 14:54, Masahisa Kojima wrote:
>>> From: AKASHI Takahiro <takahiro.akashi at linaro.org>
>>>
>>> Under the current implementation, booting from removable media using
>>> a architecture-specific default image name, say BOOTAA64.EFI, is
>>> supported only in distro_bootcmd script. See the commit 74522c898b35
>>> ("efi_loader: Add distro boot script for removable media").
>>>
>>> This is, however, half-baked implementation because
>>> 1) UEFI specification requires this feature to be implemented as part
>>> of Boot Manager's responsibility:
>>>
>>> 3 - Boot Manager
>>> 3.5.1 Boot via the Simple File Protocol
>>> When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
>>> start with a device path that points to the device that implements the
>>> EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
>>> part of the FilePath may point to the file name, including
>>> subdirectories, which contain the bootable image. If the file name is
>>> a null device path, the file name must be generated from the rules
>>> defined below.
>>> ...
>>> 3.5.1.1 Removable Media Boot Behavior
>>> To generate a file name when none is present in the FilePath, the
>>> firmware must append a default file name in the form
>>> \EFI\BOOT\BOOT{machine type short-name}.EFI ...
>>>
>>> 2) So (1) entails the hehavior that the user's preference of boot media
>>> order should be determined by Boot#### and BootOrder variables.
>>
>> At every boot you will have to delete autogenerated boot options and
>> create new ones according to the media which are present.
>>
>> Your implementation does not offer any possibility to identify
>> autogenerated boot options.
>>
>> On my laptop all autogenerated boot options use a VenMsg() device path
>>
>> Boot0016* USB CD
>> VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,86701296aa5a7848b66cd49dd3ba6a55)
>> Boot0017* USB FDD
>> VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,6ff015a28830b543a8b8641009461e49)
>> Boot0018* NVMe0
>> VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,001c199932d94c4eae9aa0b6e98eb8a400)
>> Boot0019* ATA HDD0
>> VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,91af625956449f41a7b91f4f892ab0f600)
>> Boot001A* USB HDD
>> VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,33e821aaaf33bc4789bd419f88c50803)
>> Boot001B* PCI LAN
>> VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,78a84aaf2b2afc4ea79cf5cc8f3d3803)
>>
>> while manual boot options use normal device paths
>>
>> Boot0001* debian
>> HD(2,GPT,54e58b03-c1db-4c6b-afda-24340c3acda5,0x109000,0x32000)/File(\EFI\debian\shimx64.efi)
>> Boot0002* ubuntu
>> HD(2,GPT,54e58b03-c1db-4c6b-afda-24340c3acda5,0x109000,0x32000)/File(\EFI\ubuntu\shimx64.efi)
>>
>> Please, provide a concept that can differentiate between autogenerated
>> and manually set boot options.
>
> The patch "[PATCH v4 10/11] bootmenu: add removable media entries" [*1]
> handles this auto generation and auto deletion.
> # Sorry, I should clearly describe this in the commit message.
>
> u"bootmenu" string is stored in EFI_LOAD_OPTION.OptionalData of the
> autogenerated Boot#### variable, to differentiate the boot options
> between autogenerated and manually set.
> # In EDK2 implementation, a special GUID is used for this purpose and GUID
> is stored in EFI_LOAD_OPTION.OptionalData.
EDK II uses GUID mBmAutoCreateBootOptionGuid
in MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c.
The chance of a collision for a GUID is much less then when using a
string. Hence I would prefer a GUID based solution.
An alternative to storing the GUID in the optional data is in the device
path. This is what I see on my laptop:
Boot001A* USB HDD
VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,
33e821aaaf33bc4783bd419f88c70803)
But using a GUID in the optional data is good enough.
Best regards
Heinrich
> # In U-Boot, lib/efi_loader/efi_load_option.c::efi_serialize_load_option()
> handles the EFI_LOAD_OPTION.OptionalData as u16 string, so I use
> "bootmenu" string.
>
> The example is as below, Boot0003 is an auto generated boot option,
> and Boot0004 is manually set.
> ==========
> Boot0003:
> attributes: A-- (0x00000001)
> label: virtio1:2
> file_path: /VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(63293792-adf5-9325-b99f-4e0e455c1b1e,01)/HD(2,GPT,fb12642d-1b5a-4e31-b074-ba2d813bed71,0x100800,0x18fff)
> data:
> 00000000: 62 00 6f 00 6f 00 74 00 6d 00 65 00 6e 00 75 00 b.o.o.t.m.e.n.u.
> 00000010: 00 00 ..
> Boot0004:
> attributes: A-- (0x00000001)
> label: debian
> file_path: /VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(63293792-adf5-9325-b99f-4e0e455c1b1e,00)/HD(1,GPT,c2475a57-2735-4744-9f01-67fefa1d06ae,0x800,0x100000)/EFI\debian\grubaa64.efi
> data:
> ==========
>
> In "[PATCH v4 10/11]" [*1], auto generation is handled as follows.
> 1) bootmenu enumerates the all devices having EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
> 2) bootmenu compares the device path enumerated in step#1 and
> existing boot option read from EFI variable
> 3-a) if the enumerated device path in step#1 already exist in EFI variable,
> nothing to do.
> 3-b) if the enumerated device path in step#1 does not exist in EFI variable,
> boot option is auto generated.
> 3-c) If the boot option stored in EFI variable with "bootmenu" OptionalData
> does not appear in the device path enumerated in step#1,
> this boot option is treated as "invalid" and auto deleted.
>
> [*1] https://lore.kernel.org/u-boot/20220324135443.1571-11-masahisa.kojima@linaro.org/
>
> Thanks,
> Masahisa Kojima
>
>>
>> Best regards
>>
>> Heinrich
>>
>>>
>>> With this patch, the semantics mentioned above is fully implemented.
>>> For example, if you want to boot the system from USB and SCSI in this
>>> order,
>>> * define Boot0001 which contains only a device path to the USB device
>>> (without any file path/name)
>>> * define Boot0002 which contains only a device path to the SCSI device,
>>> and
>>> * set BootOrder to Boot0001:Boot0002
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
>>> Signed-off-by: Masahisa Kojima <masahisa.kojima at linaro.org>
>>> ---
>>> Changes from original version:
>>> - create new include file "efi_default_filename.h" to
>>> avoid conflict with config_distro_bootcmd.h
>>> - modify the target pointer of efi_free_pool(), expand_media_path() should
>>> only free the pointer allocated by efi_dp_from_file() function.
>>>
>>> include/config_distro_bootcmd.h | 14 +--------
>>> include/efi_default_filename.h | 26 +++++++++++++++++
>>> lib/efi_loader/efi_bootmgr.c | 50 ++++++++++++++++++++++++++++++++-
>>> 3 files changed, 76 insertions(+), 14 deletions(-)
>>> create mode 100644 include/efi_default_filename.h
>>>
>>> diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
>>> index 2f90929178..ef2c9f330e 100644
>>> --- a/include/config_distro_bootcmd.h
>>> +++ b/include/config_distro_bootcmd.h
>>> @@ -91,19 +91,7 @@
>>> #endif
>>>
>>> #ifdef CONFIG_EFI_LOADER
>>> -#if defined(CONFIG_ARM64)
>>> -#define BOOTEFI_NAME "bootaa64.efi"
>>> -#elif defined(CONFIG_ARM)
>>> -#define BOOTEFI_NAME "bootarm.efi"
>>> -#elif defined(CONFIG_X86_RUN_32BIT)
>>> -#define BOOTEFI_NAME "bootia32.efi"
>>> -#elif defined(CONFIG_X86_RUN_64BIT)
>>> -#define BOOTEFI_NAME "bootx64.efi"
>>> -#elif defined(CONFIG_ARCH_RV32I)
>>> -#define BOOTEFI_NAME "bootriscv32.efi"
>>> -#elif defined(CONFIG_ARCH_RV64I)
>>> -#define BOOTEFI_NAME "bootriscv64.efi"
>>> -#endif
>>> +#include <efi_default_filename.h>
>>> #endif
>>>
>>> #ifdef BOOTEFI_NAME
>>> diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
>>> new file mode 100644
>>> index 0000000000..de030d2692
>>> --- /dev/null
>>> +++ b/include/efi_default_filename.h
>>> @@ -0,0 +1,26 @@
>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>> +/*
>>> + * Default boot file name when none is present in the FilePath.
>>> + *
>>> + * Copyright (c) 2022, Linaro Limited
>>> + */
>>> +#ifndef _EFI_DEFAULT_FILENAME_H
>>> +#define _EFI_DEFAULT_FILENAME_H
>>> +
>>> +#if defined(CONFIG_ARM64)
>>> +#define BOOTEFI_NAME "BOOTAA64.EFI"
>>> +#elif defined(CONFIG_ARM)
>>> +#define BOOTEFI_NAME "BOOTARM.EFI"
>>> +#elif defined(CONFIG_X86_64)
>>> +#define BOOTEFI_NAME "BOOTX64.EFI"
>>> +#elif defined(CONFIG_X86)
>>> +#define BOOTEFI_NAME "BOOTIA32.EFI"
>>> +#elif defined(CONFIG_ARCH_RV32I)
>>> +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
>>> +#elif defined(CONFIG_ARCH_RV64I)
>>> +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
>>> +#else
>>> +#error Unsupported UEFI architecture
>>> +#endif
>>> +
>>> +#endif
>>> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
>>> index 8c04ecbdc8..22a4302aac 100644
>>> --- a/lib/efi_loader/efi_bootmgr.c
>>> +++ b/lib/efi_loader/efi_bootmgr.c
>>> @@ -11,6 +11,7 @@
>>> #include <charset.h>
>>> #include <log.h>
>>> #include <malloc.h>
>>> +#include <efi_default_filename.h>
>>> #include <efi_loader.h>
>>> #include <efi_variable.h>
>>> #include <asm/unaligned.h>
>>> @@ -30,6 +31,50 @@ static const struct efi_runtime_services *rs;
>>> * should do normal or recovery boot.
>>> */
>>>
>>> +/**
>>> + * expand_media_path() - expand a device path for default file name
>>> + * @device_path: device path to check against
>>> + *
>>> + * If @device_path is a media or disk partition which houses a file
>>> + * system, this function returns a full device path which contains
>>> + * an architecture-specific default file name for removable media.
>>> + *
>>> + * Return: a newly allocated device path
>>> + */
>>> +static
>>> +struct efi_device_path *expand_media_path(struct efi_device_path *device_path)
>>> +{
>>> + struct efi_device_path *dp, *full_path;
>>> + efi_handle_t handle;
>>> + efi_status_t ret;
>>> +
>>> + if (!device_path)
>>> + return NULL;
>>> +
>>> + /*
>>> + * If device_path is a (removable) media or partition which provides
>>> + * simple file system protocol, append a default file name to support
>>> + * booting from removable media.
>>> + */
>>> + dp = device_path;
>>> + ret = efi_locate_device_path(&efi_simple_file_system_protocol_guid,
>>> + &dp, &handle);
>>> + if (ret == EFI_SUCCESS) {
>>> + if (dp->type == DEVICE_PATH_TYPE_END) {
>>> + dp = efi_dp_from_file(NULL, 0,
>>> + "/EFI/BOOT/" BOOTEFI_NAME);
>>> + full_path = efi_dp_append(device_path, dp);
>>> + efi_free_pool(dp);
>>> + } else {
>>> + full_path = efi_dp_dup(device_path);
>>> + }
>>> + } else {
>>> + full_path = efi_dp_dup(device_path);
>>> + }
>>> +
>>> + return full_path;
>>> +}
>>> +
>>> /**
>>> * try_load_entry() - try to load image for boot option
>>> *
>>> @@ -68,13 +113,16 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
>>> }
>>>
>>> if (lo.attributes & LOAD_OPTION_ACTIVE) {
>>> + struct efi_device_path *file_path;
>>> u32 attributes;
>>>
>>> log_debug("%s: trying to load \"%ls\" from %pD\n",
>>> __func__, lo.label, lo.file_path);
>>>
>>> - ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
>>> + file_path = expand_media_path(lo.file_path);
>>> + ret = EFI_CALL(efi_load_image(true, efi_root, file_path,
>>> NULL, 0, handle));
>>> + efi_free_pool(file_path);
>>> if (ret != EFI_SUCCESS) {
>>> log_warning("Loading %ls '%ls' failed\n",
>>> varname, lo.label);
>>
More information about the U-Boot
mailing list