[PATCH v3] efi_loader: Trigger capsule updates with automatically generated boot options
Heinrich Schuchardt
xypron.glpk at gmx.de
Tue Dec 16 16:24:56 CET 2025
On 12/16/25 08:47, Ilias Apalodimas wrote:
> The EFI spec in §8.5.5 says
> "The directory \EFI\UpdateCapsule is checked for capsules only within
> the EFI system partition on the device specified in the active boot
> option determine by reference to BootNext variable or BootOrder variable
> processing."
>
> Automatically generated boot options don't point to the ESP, they point to
> the disk itself and find_handle() won't match when searching for an ESP
> during a capsule update.
> This happens because find_handle() only matches device paths that are
> shorter or equal to the device path passed as an argument.
> Since the EFI spec allows it we want to allow capsule updates, when the
> boot option points to a disk, but that disk contains an ESP with a
> \EFI\UpdateCapsule directory.
>
> So, let's change device_is_present_and_system_part() and check if the
> supplied device path contains an ESP. If it does return the handle of
> the device. Otherwise, iterate over child devices and return the handle
> of the first child that contains an ESP.
>
> The returned handle can then be reused later. Rather than calling
> efi_fs_from_path(), we can simply look up the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
> on the discovered handle, avoiding the need to re-parse device paths.
>
> Reported-by: Balaji Selvanathan <balaji.selvanathan at oss.qualcomm.com>
> Reported-by: John Toomey <john.toomey at amd.com>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> ---
> Changes from v2:
> - Changing efi_dp_find_obj() wasn't too intuitive, since we only matched
> the longest device path we found from a prefix and made the code complex.
> - Change the implementation and iterate over the device children to locate an
> ESP, if the supplied DP is not one.
>
> lib/efi_loader/efi_capsule.c | 67 +++++++++++++++++++++++++-----------
> 1 file changed, 46 insertions(+), 21 deletions(-)
>
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index eafc647f558f..60ce03b4254a 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -8,6 +8,7 @@
>
> #define LOG_CATEGORY LOGC_EFI
>
> +#include <dm/device.h>
> #include <efi_device_path.h>
> #include <efi_loader.h>
> #include <efi_variable.h>
> @@ -878,30 +879,48 @@ static efi_status_t get_dp_device(u16 *boot_var,
> }
>
> /**
> - * device_is_present_and_system_part - check if a device exists
> + * get_esp_handle - check if a device exists and contains an ESP
The logic looks ok. Yet, the descriptions need rework.
"check if a device exists and contains an ESP":
This is not what this function does. It checks if a device has an ESP as
immediate child.
> *
> * Check if a device pointed to by the device path, @dp, exists and is
> - * located in UEFI system partition.
> + * either an ESP or a disk containing an ESP.
Nowhere do you check if the device is a disk. The end node of @dp could
be anything.
An NVMe drive may have many namespaces, a SCSI drive many LUNs.
The partition is not the child of the drive but of the namespace or the LUN.
An NVMe(NSID,EUI) node in a device-path is for a namespace not for a
drive. Similarly an Scsi(PUN,LUN) node is for the LUN and not for the
drive (PUN).
Please, provide an accurate function description.
> *
> * @dp device path
> - * Return: true - yes, false - no
> + * Return: ESP handle or NULL
> */
> -static bool device_is_present_and_system_part(struct efi_device_path *dp)
> +efi_handle_t get_esp_handle(struct efi_device_path *dp)
> {
> - efi_handle_t handle;
> + efi_handle_t handle, dev_handle;
> + struct udevice *child_dev;
> struct efi_device_path *rem;
> + efi_status_t ret;
>
> /* Check device exists */
> - handle = efi_dp_find_obj(dp, NULL, NULL);
> - if (!handle)
> - return false;
> + dev_handle = efi_dp_find_obj(dp, NULL, NULL);
> + if (!dev_handle)
> + return NULL;
>
> /* Check device is on system partition */
Should this be
"Check if the device path points to an EFI system partition"?
> handle = efi_dp_find_obj(dp, &efi_system_partition_guid, &rem);
> - if (!handle)
> - return false;
> + if (handle)
> + return handle;
> +
> + /* If it's a disk check its children */
The object that you check may be anything, not necessarily a disk.
Best regards
Heinrich
> + list_for_each_entry(child_dev, &dev_handle->dev->child_head, sibling_node) {
> + if (device_get_uclass_id(child_dev) != UCLASS_PARTITION)
> + continue;
> + if (dev_tag_get_ptr(child_dev, DM_TAG_EFI, (void **)&handle))
> + continue;
> +
> + ret = EFI_CALL(systab.boottime->open_protocol(
> + handle, &efi_system_partition_guid, NULL, NULL,
> + NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL));
> + if (ret != EFI_SUCCESS)
> + continue;
>
> - return true;
> + return handle;
> + }
> +
> + return NULL;
> }
>
> /**
> @@ -920,6 +939,8 @@ static efi_status_t find_boot_device(void)
> int i, num;
> struct efi_simple_file_system_protocol *volume;
> struct efi_device_path *boot_dev = NULL;
> + struct efi_handler *handler;
> + efi_handle_t esp = NULL;
> efi_status_t ret;
>
> /* find active boot device in BootNext */
> @@ -940,7 +961,8 @@ static efi_status_t find_boot_device(void)
>
> ret = get_dp_device(boot_var16, &boot_dev);
> if (ret == EFI_SUCCESS) {
> - if (device_is_present_and_system_part(boot_dev)) {
> + esp = get_esp_handle(boot_dev);
> + if (esp) {
> goto found;
> } else {
> efi_free_pool(boot_dev);
> @@ -967,26 +989,29 @@ skip:
> if (ret != EFI_SUCCESS)
> continue;
>
> - if (device_is_present_and_system_part(boot_dev))
> + esp = get_esp_handle(boot_dev);
> + if (esp)
> break;
>
> efi_free_pool(boot_dev);
> boot_dev = NULL;
> }
> +
> found:
> - if (boot_dev) {
> + ret = EFI_NOT_FOUND;
> + if (esp) {
> log_debug("Boot device %pD\n", boot_dev);
>
> - volume = efi_fs_from_path(boot_dev);
> - if (!volume)
> - ret = EFI_DEVICE_ERROR;
> - else
> + efi_free_pool(boot_dev);
> + ret = efi_search_protocol(esp, &efi_simple_file_system_protocol_guid,
> + &handler);
> + if (ret == EFI_SUCCESS) {
> + volume = handler->protocol_interface;
> ret = EFI_CALL(volume->open_volume(volume,
> &bootdev_root));
> - efi_free_pool(boot_dev);
> - } else {
> - ret = EFI_NOT_FOUND;
> + }
> }
> +
> out:
> free(boot_order);
>
> --
> 2.43.0
>
More information about the U-Boot
mailing list