[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