[PATCH] efi_loader: Trigger capsule updates with automatically generated boot options

Balaji Selvanathan balaji.selvanathan at oss.qualcomm.com
Thu Dec 4 05:42:35 CET 2025


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