[PATCH] efi_loader: Trigger capsule updates with automatically generated boot options
Ilias Apalodimas
ilias.apalodimas at linaro.org
Thu Dec 4 11:24:47 CET 2025
Hi Baaji,
Thanks for testing, can you also send a reply with a tested-by: tag?
/Ilias
On Thu, 4 Dec 2025 at 06:42, Balaji Selvanathan
<balaji.selvanathan at oss.qualcomm.com> wrote:
>
>
> On 12/2/2025 2:20 PM, 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.
> >
> > find_handle() implicitly changes it's behavior depending on the passed
> > arguments as well. If the 'rem' ptr is not valid it will only search for
> > an exact match. Otherwise it will match on the best match and return the
> > remaining portion of the matched device path to 'rem'.
> >
> > Tweak this slightly and pass an enum with 3 options
> > - EXACT - Exact match, as if 'rem' was NULL
> > - SHORTER - Match a shorter device path and put the remainder in 'rem'
> > - LONGER - Inverse the logic and match a longer device path. Put the
> > remainder into 'rem'.
> >
> > To avoid any regressions, LONGER is only used during capsule updates and
> > since 'rem' always carries the remainder of the matched device path we
> > can plug it in with minimal changes.
> >
> > 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>
> > ---
> >
> > John, Balaji, can you check if this fixes your problem?
>
> Hi Ilias,
>
> Thanks for this patch. This fixes the problem and have verified the patch.
>
> Regards,
>
> Balaji
>
> >
> > cmd/eficonfig.c | 2 +-
> > include/efi_device_path.h | 10 +++---
> > lib/efi_loader/efi_bootmgr.c | 6 ++--
> > lib/efi_loader/efi_boottime.c | 5 +--
> > lib/efi_loader/efi_capsule.c | 20 +++++++-----
> > lib/efi_loader/efi_device_path.c | 55 +++++++++++++++++++++++++-------
> > lib/efi_loader/efi_disk.c | 2 +-
> > 7 files changed, 70 insertions(+), 30 deletions(-)
> >
> > diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
> > index d8d946c87ac8..e317f3c90c14 100644
> > --- a/cmd/eficonfig.c
> > +++ b/cmd/eficonfig.c
> > @@ -1321,7 +1321,7 @@ static efi_status_t prepare_file_selection_entry(struct efimenu *efi_menu, char
> > return EFI_OUT_OF_RESOURCES;
> >
> > /* get the device name only when the user already selected the file path */
> > - handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL);
> > + handle = efi_dp_find_obj(file_info->dp_volume, NULL, NULL, false);
> > if (handle) {
> > ret = efi_disk_get_device_name(handle, devname, EFICONFIG_VOLUME_PATH_MAX);
> > if (ret != EFI_SUCCESS)
> > diff --git a/include/efi_device_path.h b/include/efi_device_path.h
> > index aae85228f681..f9c538730c94 100644
> > --- a/include/efi_device_path.h
> > +++ b/include/efi_device_path.h
> > @@ -73,13 +73,15 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp);
> > *
> > * If @rem is provided, the handle with the longest partial match is returned.
> > *
> > - * @dp: device path to search
> > - * @guid: GUID of protocol that must be installed on path or NULL
> > - * @rem: pointer to receive remaining device path
> > + * @dp: device path to search
> > + * @guid: GUID of protocol that must be installed on path or NULL
> > + * @rem: pointer to receive remaining device path
> > + * @inversed_match: Match even if the dp is shorter. rem will stil hoild the
> > + * remaining device path
> > * Return: matching handle
> > */
> > efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, const efi_guid_t *guid,
> > - struct efi_device_path **rem);
> > + struct efi_device_path **rem, bool inversed_match);
> >
> > /**
> > * efi_dp_last_node() - Determine the last device path node before the end node
> > diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> > index a687f4d8e85c..df7500a94876 100644
> > --- a/lib/efi_loader/efi_bootmgr.c
> > +++ b/lib/efi_loader/efi_bootmgr.c
> > @@ -84,7 +84,8 @@ struct efi_device_path *expand_media_path(struct efi_device_path *device_path)
> > * booting from removable media.
> > */
> > handle = efi_dp_find_obj(device_path,
> > - &efi_simple_file_system_protocol_guid, &rem);
> > + &efi_simple_file_system_protocol_guid, &rem,
> > + false);
> > if (handle) {
> > if (rem->type == DEVICE_PATH_TYPE_END) {
> > char fname[30];
> > @@ -625,7 +626,8 @@ static efi_status_t try_load_from_media(struct efi_device_path *file_path,
> > struct efi_device_path *rem, *dp = NULL;
> > struct efi_device_path *final_dp = file_path;
> >
> > - handle_blkdev = efi_dp_find_obj(file_path, &efi_block_io_guid, &rem);
> > + handle_blkdev = efi_dp_find_obj(file_path, &efi_block_io_guid, &rem,
> > + false);
> > if (handle_blkdev) {
> > if (rem->type == DEVICE_PATH_TYPE_END) {
> > /* no file name present, try default file */
> > diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> > index ddc935d22409..0391b92a8be4 100644
> > --- a/lib/efi_loader/efi_boottime.c
> > +++ b/lib/efi_loader/efi_boottime.c
> > @@ -1816,7 +1816,8 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
> > info->system_table = &systab;
> >
> > if (device_path) {
> > - info->device_handle = efi_dp_find_obj(device_path, NULL, NULL);
> > + info->device_handle = efi_dp_find_obj(device_path, NULL, NULL,
> > + false);
> >
> > dp = efi_dp_concat(device_path, file_path, 0);
> > if (!dp) {
> > @@ -2016,7 +2017,7 @@ efi_status_t efi_load_image_from_path(bool boot_policy,
> > *size = 0;
> >
> > dp = file_path;
> > - device = efi_dp_find_obj(dp, NULL, &rem);
> > + device = efi_dp_find_obj(dp, NULL, &rem, false);
> > ret = efi_search_protocol(device, &efi_simple_file_system_protocol_guid,
> > NULL);
> > if (ret == EFI_SUCCESS)
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > index eafc647f558f..103328c76a6e 100644
> > --- a/lib/efi_loader/efi_capsule.c
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -886,18 +886,18 @@ static efi_status_t get_dp_device(u16 *boot_var,
> > * @dp device path
> > * Return: true - yes, false - no
> > */
> > -static bool device_is_present_and_system_part(struct efi_device_path *dp)
> > +static bool device_is_present_and_system_part(struct efi_device_path *dp,
> > + struct efi_device_path **rem)
> > {
> > efi_handle_t handle;
> > - struct efi_device_path *rem;
> >
> > /* Check device exists */
> > - handle = efi_dp_find_obj(dp, NULL, NULL);
> > + handle = efi_dp_find_obj(dp, NULL, NULL, false);
> > if (!handle)
> > return false;
> >
> > /* Check device is on system partition */
> > - handle = efi_dp_find_obj(dp, &efi_system_partition_guid, &rem);
> > + handle = efi_dp_find_obj(dp, &efi_system_partition_guid, rem, true);
> > if (!handle)
> > return false;
> >
> > @@ -919,7 +919,7 @@ static efi_status_t find_boot_device(void)
> > efi_uintn_t size;
> > int i, num;
> > struct efi_simple_file_system_protocol *volume;
> > - struct efi_device_path *boot_dev = NULL;
> > + struct efi_device_path *boot_dev = NULL, *rem = NULL;
> > efi_status_t ret;
> >
> > /* find active boot device in BootNext */
> > @@ -940,7 +940,7 @@ 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)) {
> > + if (device_is_present_and_system_part(boot_dev, &rem)) {
> > goto found;
> > } else {
> > efi_free_pool(boot_dev);
> > @@ -967,7 +967,7 @@ skip:
> > if (ret != EFI_SUCCESS)
> > continue;
> >
> > - if (device_is_present_and_system_part(boot_dev))
> > + if (device_is_present_and_system_part(boot_dev, &rem))
> > break;
> >
> > efi_free_pool(boot_dev);
> > @@ -977,7 +977,11 @@ found:
> > if (boot_dev) {
> > log_debug("Boot device %pD\n", boot_dev);
> >
> > - volume = efi_fs_from_path(boot_dev);
> > + if (rem->type == DEVICE_PATH_TYPE_END)
> > + volume = efi_fs_from_path(boot_dev);
> > + else
> > + volume = efi_fs_from_path(rem);
> > +
> > if (!volume)
> > ret = EFI_DEVICE_ERROR;
> > else
> > diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
> > index b3fb20b2501e..ad7f452c411b 100644
> > --- a/lib/efi_loader/efi_device_path.c
> > +++ b/lib/efi_loader/efi_device_path.c
> > @@ -30,6 +30,12 @@ const struct efi_device_path EFI_DP_END = {
> > .length = sizeof(EFI_DP_END),
> > };
> >
> > +enum match {
> > + EXACT,
> > + SHORTER,
> > + LONGER
> > +};
> > +
> > #if defined(CONFIG_MMC)
> > /*
> > * Determine if an MMC device is an SD card.
> > @@ -109,10 +115,11 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp)
> > */
> > static efi_handle_t find_handle(struct efi_device_path *dp,
> > const efi_guid_t *guid, bool short_path,
> > - struct efi_device_path **rem)
> > + struct efi_device_path **rem,
> > + enum match match)
> > {
> > efi_handle_t handle, best_handle = NULL;
> > - efi_uintn_t len, best_len = 0;
> > + efi_uintn_t len, match_len = 0, best_len = 0;
> >
> > len = efi_dp_instance_size(dp);
> >
> > @@ -137,22 +144,35 @@ static efi_handle_t find_handle(struct efi_device_path *dp,
> > if (!dp_current)
> > continue;
> > }
> > - len_current = efi_dp_instance_size(dp_current);
> > - if (rem) {
> > +
> > + match_len = len_current = efi_dp_instance_size(dp_current);
> > + switch (match) {
> > + case EXACT:
> > + if (len_current != len)
> > + continue;
> > + break;
> > + case SHORTER:
> > if (len_current > len)
> > continue;
> > - } else {
> > - if (len_current != len)
> > + break;
> > + case LONGER:
> > + if (len_current < len)
> > continue;
> > + match_len = len;
> > + break;
> > }
> > - if (memcmp(dp_current, dp, len_current))
> > +
> > + if (memcmp(dp_current, dp, match_len))
> > continue;
> > if (!rem)
> > return handle;
> > if (len_current > best_len) {
> > best_len = len_current;
> > best_handle = handle;
> > - *rem = (void*)((u8 *)dp + len_current);
> > + if (len_current <= len)
> > + *rem = (void *)((uintptr_t)dp + len_current);
> > + else
> > + *rem = (void *)((uintptr_t)dp_current + match_len);
> > }
> > }
> > return best_handle;
> > @@ -160,14 +180,25 @@ static efi_handle_t find_handle(struct efi_device_path *dp,
> >
> > efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
> > const efi_guid_t *guid,
> > - struct efi_device_path **rem)
> > + struct efi_device_path **rem, bool inversed_match)
> > {
> > efi_handle_t handle;
> >
> > - handle = find_handle(dp, guid, false, rem);
> > + handle = find_handle(dp, guid, false, rem, EXACT);
> > if (!handle)
> > - /* Match short form device path */
> > - handle = find_handle(dp, guid, true, rem);
> > + handle = find_handle(dp, guid, true, rem, EXACT);
> > + if (handle)
> > + return handle;
> > +
> > + handle = find_handle(dp, guid, false, rem, SHORTER);
> > + if (!handle)
> > + handle = find_handle(dp, guid, true, rem, SHORTER);
> > +
> > + if (inversed_match && !handle) {
> > + handle = find_handle(dp, guid, false, rem, LONGER);
> > + if (!handle)
> > + handle = find_handle(dp, guid, true, rem, LONGER);
> > + }
> >
> > return handle;
> > }
> > diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> > index 130c4db9606f..0d3d7c187d72 100644
> > --- a/lib/efi_loader/efi_disk.c
> > +++ b/lib/efi_loader/efi_disk.c
> > @@ -339,7 +339,7 @@ efi_fs_from_path(struct efi_device_path *full_path)
> > efi_free_pool(file_path);
> >
> > /* Get the EFI object for the partition */
> > - efiobj = efi_dp_find_obj(device_path, NULL, NULL);
> > + efiobj = efi_dp_find_obj(device_path, NULL, NULL, false);
> > efi_free_pool(device_path);
> > if (!efiobj)
> > return NULL;
> > --
> > 2.51.0
> >
More information about the U-Boot
mailing list