[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