[PATCH v10] efi_vars: Implement SPI Flash store

Heinrich Schuchardt xypron.glpk at gmx.de
Fri Mar 13 15:12:56 CET 2026


On 3/12/26 09:37, Michal Simek wrote:
> From: Shantur Rathore <i at shantur.com>
> 
> Currently U-Boot uses ESP as storage for EFI variables.
> Devices with SPI Flash are used for storing environment with this
> commit we allow EFI variables to be stored on SPI Flash.
> 
> Signed-off-by: Shantur Rathore <i at shantur.com>
> Signed-off-by: Michal Simek <michal.simek at amd.com>
> Tested-by: Neil Armstrong <neil.armstrong at linaro.org> # on AML-S905D3-CC
> Acked-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> ---
> 
> Changes in v10:
> - Clean log messages
> - separate return variables
> - Fix return value
> - Move length check to efi_var_restore()
> 
> Changes in v9:
> - Remove EFI_RT_VOLATILE_STORE enabling
> - Cleanup commit message
> 
> Changes in v8:
> - Add missing EFI_VARIABLE_SF_STORE dependency to EFI_VARIABLE_SF_DEVICE_INDEX
> 
> Changes in v7:
> - sed -i 's/efi_var_from/efi_var_from_storage/g'
> 
> Changes in v6:
> - sed -i 's/efi_var_read/efi_var_from/g'
> - sed -i 's/efi_var_write/efi_var_to_storage/g'
> 
> Changes in v4:
> - Extend Kconfig description
> - Extend commit message and describe efivar missing part
> - use unify methods for reading/writing variable
> 
> Changes in v3:
> - Fixed compiler warnings.
> 
> Changes in v2:
> - Refactored efi_var_file to move common parts out as requested
> - Changed ifdefs to use CONFIG_IS_DEFINED
> - Fixed typos
> 
> The patch depends on https://lore.kernel.org/u-boot/20260311173033.10002-1-heinrich.schuchardt@canonical.com
> 
> ---
>   lib/efi_loader/Kconfig      |  33 ++++++++++++
>   lib/efi_loader/Makefile     |   1 +
>   lib/efi_loader/efi_var_sf.c | 105 ++++++++++++++++++++++++++++++++++++
>   3 files changed, 139 insertions(+)
>   create mode 100644 lib/efi_loader/efi_var_sf.c
> 
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 579eed658801..315dd4f3e794 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -124,6 +124,24 @@ config EFI_VARIABLE_FILE_STORE
>   	  Select this option if you want non-volatile UEFI variables to be
>   	  stored as file /ubootefi.var on the EFI system partition.
>   
> +config EFI_VARIABLE_SF_STORE
> +	bool "Store non-volatile UEFI variables in SPI Flash"
> +	depends on SPI_FLASH
> +	help
> +	  Select this option if you want non-volatile UEFI variables to be
> +	  stored in SPI Flash.
> +
> +	  Define CONFIG_EFI_VARIABLE_SF_OFFSET as offset in SPI Flash to use as
> +	  the storage for variables. CONFIG_EFI_VAR_BUF_SIZE defines the space
> +	  needed.
> +
> +	  Note that SPI Flash devices have a limited number of program/erase
> +	  cycles. Frequent updates to UEFI variables may cause excessive wear
> +	  and can permanently damage the flash device, particularly on SPI NAND
> +	  or low-end SPI NOR parts without wear leveling. This option should be
> +	  used with care on such systems, and is not recommended for platforms
> +	  where UEFI variables are updated frequently.
> +
>   config EFI_MM_COMM_TEE
>   	bool "UEFI variables storage service via the trusted world"
>   	depends on OPTEE
> @@ -194,6 +212,21 @@ config FFA_SHARED_MM_BUF_ADDR
>   	  the MM SP in secure world.
>   	  It is assumed that the MM SP knows the address of the shared MM communication buffer.
>   
> +config EFI_VARIABLE_SF_OFFSET
> +	hex "EFI variables in SPI flash offset"
> +	depends on EFI_VARIABLE_SF_STORE
> +	help
> +	  Offset from the start of the SPI Flash where EFI variables will be stored.
> +	  This should be aligned to the sector size of SPI Flash.
> +
> +config EFI_VARIABLE_SF_DEVICE_INDEX
> +	int "Device Index for target SPI Flash"
> +	depends on EFI_VARIABLE_SF_STORE
> +	default 0
> +	help
> +	  The index of SPI Flash device used for storing EFI variables. This would be
> +	  needed if there are more than 1 SPI Flash devices available to use.
> +
>   config EFI_VARIABLES_PRESEED
>   	bool "Initial values for UEFI variables"
>   	depends on !COMPILE_TEST
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index ca1775eb03be..d73ad43951b1 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -54,6 +54,7 @@ obj-y += efi_variable_tee.o
>   else
>   obj-y += efi_variable.o
>   obj-$(CONFIG_EFI_VARIABLE_FILE_STORE) += efi_var_file.o
> +obj-$(CONFIG_EFI_VARIABLE_SF_STORE) += efi_var_sf.o
>   obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o
>   endif
>   obj-y += efi_watchdog.o
> diff --git a/lib/efi_loader/efi_var_sf.c b/lib/efi_loader/efi_var_sf.c
> new file mode 100644
> index 000000000000..c0755c67cda9
> --- /dev/null
> +++ b/lib/efi_loader/efi_var_sf.c
> @@ -0,0 +1,105 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * SPI Flash interface for UEFI variables
> + *
> + * Copyright (c) 2023, Shantur Rathore
> + * Copyright (C) 2026, Advanced Micro Devices, Inc.
> + */
> +
> +#define LOG_CATEGORY LOGC_EFI
> +
> +#include <efi_loader.h>
> +#include <efi_variable.h>
> +#include <spi_flash.h>
> +#include <dm.h>
> +
> +efi_status_t efi_var_to_storage(void)
> +{
> +	struct efi_var_file *buf;
> +	struct udevice *sfdev;
> +	efi_status_t ret;
> +	loff_t len;
> +	int r;
> +
> +	ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
> +	if (ret != EFI_SUCCESS)
> +		goto error;
> +
> +	if (len > EFI_VAR_BUF_SIZE) {
> +		log_err("EFI var buffer length more than target SPI Flash size\n");

This function may be called by EFI applications. We should avoid console 
output here. Let the EFI application write a message.

log_debug() is ok.

> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto error;
> +	}
> +
> +	log_debug("Got buffer to write buf->len: %d\n", buf->length);
> +
> +	r = uclass_get_device(UCLASS_SPI_FLASH,
> +			      CONFIG_EFI_VARIABLE_SF_DEVICE_INDEX, &sfdev);
> +	if (r) {
> +		ret = EFI_DEVICE_ERROR;
> +		goto error;
> +	}
> +
> +	r = spi_flash_erase_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET,
> +			       EFI_VAR_BUF_SIZE);

We could use the buf->length rounded up to full sectors here to avoid 
unnecessary SPI flash wear.

> +	if (r) {
> +		log_debug("Failed to erase SPI Flash at offset %x\n",

The offset is already written in spi_flash_erase_dm() by calling log_ret().

> +			  CONFIG_EFI_VARIABLE_SF_OFFSET);
> +		ret = EFI_DEVICE_ERROR;
> +		goto error;
> +	}
> +
> +	r = spi_flash_write_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET, len, buf);
> +	if (r) {
> +		log_debug("Failed to write to SPI Flash: %d\n", r);
> +		ret = EFI_DEVICE_ERROR;
> +		goto error;

This goto could be removed. But that is a matter of taste.

> +	}
> +
> +error:
> +	if (ret != EFI_SUCCESS)
> +		log_err("Failed to persist EFI variables in SF\n");

We already created debug messages above and inside an API we don't want 
to write to the console.

> +	free(buf);
> +	return ret;
> +}
> +
> +efi_status_t efi_var_from_storage(void)
> +{
> +	struct efi_var_file *buf;
> +	struct udevice *sfdev;
> +	efi_status_t ret;
> +	int r;
> +
> +	buf = calloc(1, EFI_VAR_BUF_SIZE);
> +	if (!buf) {
> +		log_debug("Unable to allocate buffer\n");
> +		return EFI_OUT_OF_RESOURCES;
> +	}
> +
> +	r = uclass_get_device(UCLASS_SPI_FLASH,
> +			      CONFIG_EFI_VARIABLE_SF_DEVICE_INDEX, &sfdev);
> +	if (r) {
> +		log_debug("Failed to get SPI Flash device: %d\n", r);
> +		ret = EFI_DEVICE_ERROR;
> +		goto error;
> +	}
> +
> +	r = spi_flash_read_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET,
> +			      EFI_VAR_BUF_SIZE, buf);
> +	if (r) {
> +		log_debug("Failed to read from SPI Flash: %d\n", r);
> +		ret = EFI_DEVICE_ERROR;
> +		goto error;
> +	}
> +
> +	if (efi_var_restore(buf, false) != EFI_SUCCESS) {
> +		log_debug("No valid EFI variables in SPI Flash\n");
> +		ret = EFI_DEVICE_ERROR;
> +		goto error;
> +	}
> +
> +	ret = EFI_SUCCESS;
> +error:

This function is called when initializing the UEFI sub-system. log_err() 
would be ok here.

Best regards

Heinrich

> +	free(buf);
> +	return ret;
> +}



More information about the U-Boot mailing list