[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