From: Shantur Rathore <[email protected]>

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.

https://github.com/rhboot/efivar
is not updated to support this location that's why you can mimic it by
running:
dd if=/sys/firmware/efi/efivars/VarToFile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c \
   of=/tmp/vars skip=4 bs=1
flashcp /tmp/vars /dev/mtdX

where mtdX should match location defined by
CONFIG_EFI_VARIABLE_SF_OFFSET/CONFIG_EFI_VAR_BUF_SIZE.

Signed-off-by: Shantur Rathore <[email protected]>
Signed-off-by: Michal Simek <[email protected]>
---

(no changes since v4)

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

 lib/efi_loader/Kconfig      | 34 +++++++++++++-
 lib/efi_loader/Makefile     |  1 +
 lib/efi_loader/efi_var_sf.c | 92 +++++++++++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+), 1 deletion(-)
 create mode 100644 lib/efi_loader/efi_var_sf.c

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 13e44be1d067..bb40cc899a13 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -123,6 +123,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
@@ -152,7 +170,7 @@ endchoice
 
 config EFI_RT_VOLATILE_STORE
        bool "Allow variable runtime services in volatile storage (e.g RAM)"
-       depends on EFI_VARIABLE_FILE_STORE
+       depends on EFI_VARIABLE_FILE_STORE || EFI_VARIABLE_SF_STORE
        help
          When EFI variables are stored on file we don't allow SetVariableRT,
          since the OS doesn't know how to write that file. At the same time
@@ -193,6 +211,20 @@ 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"
+       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..0c6f9136cb0c
--- /dev/null
+++ b/lib/efi_loader/efi_var_sf.c
@@ -0,0 +1,92 @@
+// 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_write(void)
+{
+       efi_status_t ret;
+       struct efi_var_file *buf;
+       loff_t len;
+       struct udevice *sfdev;
+
+       ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
+       if (len > EFI_VAR_BUF_SIZE) {
+               log_err("EFI var buffer length more than target SPI Flash 
size");
+               ret = EFI_OUT_OF_RESOURCES;
+               goto error;
+       }
+
+       log_debug("%s - Got buffer to write buf->len : %d\n", __func__, 
buf->length);
+
+       if (ret != EFI_SUCCESS)
+               goto error;
+
+       ret = uclass_get_device(UCLASS_SPI_FLASH, 
CONFIG_EFI_VARIABLE_SF_DEVICE_INDEX, &sfdev);
+       if (ret)
+               goto error;
+
+       ret = spi_flash_erase_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET, 
EFI_VAR_BUF_SIZE);
+       log_debug("%s - Erased SPI Flash offset %x\n", __func__, 
CONFIG_EFI_VARIABLE_SF_OFFSET);
+       if (ret)
+               goto error;
+
+       ret = spi_flash_write_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET, len, 
buf);
+       log_debug("%s - Wrote buffer to SPI Flash : %ld\n", __func__, ret);
+
+       if (ret)
+               goto error;
+
+       ret = EFI_SUCCESS;
+error:
+       if (ret)
+               log_err("Failed to persist EFI variables in SF\n");
+       free(buf);
+       return ret;
+}
+
+efi_status_t efi_var_read(void)
+{
+       struct efi_var_file *buf;
+       efi_status_t ret;
+       struct udevice *sfdev;
+
+       buf = calloc(1, EFI_VAR_BUF_SIZE);
+       if (!buf) {
+               log_err("%s - Unable to allocate buffer\n", __func__);
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+       ret = uclass_get_device(UCLASS_SPI_FLASH, 0, &sfdev);
+       if (ret)
+               goto error;
+
+       ret = spi_flash_read_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET,
+                               EFI_VAR_BUF_SIZE, buf);
+
+       log_debug("%s - read buffer buf->length: %x\n", __func__, buf->length);
+
+       if (ret || buf->length < sizeof(struct efi_var_file)) {
+               log_err("%s - buffer read from SPI Flash isn't valid\n", 
__func__);
+               goto error;
+       }
+
+       ret = efi_var_restore(buf, false);
+       if (ret != EFI_SUCCESS)
+               log_err("%s - Unable to restore EFI variables from buffer\n", 
__func__);
+
+       ret = EFI_SUCCESS;
+error:
+       free(buf);
+       return ret;
+}
-- 
2.43.0

Reply via email to