diff options
| author | Tom Rini <[email protected]> | 2026-03-16 08:23:18 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-03-16 08:23:18 -0600 |
| commit | fa3a11fcf01a27f038789f4ef36d0414fe78b493 (patch) | |
| tree | 416a413625c79ac468a679a5f6cb0c1610a70280 /lib | |
| parent | 071e914b4aa139c9c411efda3fe1ffaebda5c76e (diff) | |
| parent | 8f83a4596677fe6a3f3b587b76d460644205a922 (diff) | |
Merge tag 'efi-2026-03-14' of https://source.denx.de/u-boot/custodians/u-boot-efi into next
Pull request efi-2026-03-14
CI: https://source.denx.de/u-boot/custodians/u-boot-efi/-/pipelines/29512
UEFI:
* Require at least 128 KiB of stack space to use EFI sub-system.
* Avoid buffer overrun in efi_var_restore().
* Avoid superfluous variable store writes on unchanged data
* Implement SPI Flash store for EFI variables.
* Add an efidebug ecpt sub-command to display the ECPT table
and a unit test for the command.
Others:
* Add missing include string.h to make exception command build again.
* lib: uuid: add EBBR 2.1 conformance profile GUID
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/efi_loader/Kconfig | 35 | ||||
| -rw-r--r-- | lib/efi_loader/Makefile | 1 | ||||
| -rw-r--r-- | lib/efi_loader/efi_conformance.c | 2 | ||||
| -rw-r--r-- | lib/efi_loader/efi_runtime.c | 24 | ||||
| -rw-r--r-- | lib/efi_loader/efi_var_common.c | 4 | ||||
| -rw-r--r-- | lib/efi_loader/efi_var_mem.c | 29 | ||||
| -rw-r--r-- | lib/efi_loader/efi_var_sf.c | 111 | ||||
| -rw-r--r-- | lib/efi_loader/efi_variable.c | 27 | ||||
| -rw-r--r-- | lib/uuid.c | 6 |
9 files changed, 227 insertions, 12 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 579eed65880..b5f81e0ff53 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -15,6 +15,8 @@ config EFI_LOADER # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB depends on !EFI_STUB || !X86 || X86_64 || EFI_STUB_32BIT depends on !EFI_APP + # The EFI specification requires 128 KiB or more of stack space + depends on STACK_SIZE >= 0x20000 default y if !ARM || SYS_CPU = armv7 || SYS_CPU = armv8 select EFI select CHARSET @@ -124,6 +126,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 +214,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 ca1775eb03b..d73ad43951b 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_conformance.c b/lib/efi_loader/efi_conformance.c index 2d31800ccb8..470141af483 100644 --- a/lib/efi_loader/efi_conformance.c +++ b/lib/efi_loader/efi_conformance.c @@ -12,7 +12,7 @@ #include <efi_api.h> #include <malloc.h> -static const efi_guid_t efi_ecpt_guid = EFI_CONFORMANCE_PROFILES_TABLE_GUID; +const efi_guid_t efi_ecpt_guid = EFI_CONFORMANCE_PROFILES_TABLE_GUID; /** * efi_ecpt_register() - Install the ECPT system table. diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 35eb6a77766..73d4097464c 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -210,6 +210,30 @@ void __efi_runtime efi_memcpy_runtime(void *dest, const void *src, size_t n) } /** + * efi_memcmp_runtime() - compare memory areas + * + * At runtime memcmp() is not available. + * + * @s1: first memory area + * @s2: second memory area + * @n: number of bytes to compare + * Return: 0 if equal, negative if s1 < s2, positive if s1 > s2 + */ +int __efi_runtime efi_memcmp_runtime(const void *s1, const void *s2, size_t n) +{ + const u8 *pos1 = s1; + const u8 *pos2 = s2; + + for (; n; --n) { + if (*pos1 != *pos2) + return *pos1 - *pos2; + ++pos1; + ++pos2; + } + return 0; +} + +/** * efi_update_table_header_crc32() - Update crc32 in table header * * @table: EFI table diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c index 5ea1688dca3..d63c2d1b1cd 100644 --- a/lib/efi_loader/efi_var_common.c +++ b/lib/efi_loader/efi_var_common.c @@ -497,6 +497,8 @@ efi_status_t efi_var_restore(struct efi_var_file *buf, bool safe) efi_status_t ret; if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC || + buf->length > EFI_VAR_BUF_SIZE || + buf->length < sizeof(struct efi_var_file) || buf->crc32 != crc32(0, (u8 *)buf->var, buf->length - sizeof(struct efi_var_file))) { log_err("Invalid EFI variables file\n"); @@ -524,7 +526,7 @@ efi_status_t efi_var_restore(struct efi_var_file *buf, bool safe) continue; ret = efi_var_mem_ins(var->name, &var->guid, var->attr, var->length, data, 0, NULL, - var->time); + var->time, NULL); if (ret != EFI_SUCCESS) log_err("Failed to set EFI variable %ls\n", var->name); } diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c index 31180df9e3a..8d5f99f4870 100644 --- a/lib/efi_loader/efi_var_mem.c +++ b/lib/efi_loader/efi_var_mem.c @@ -159,12 +159,39 @@ efi_status_t __efi_runtime efi_var_mem_ins( const efi_guid_t *vendor, u32 attributes, const efi_uintn_t size1, const void *data1, const efi_uintn_t size2, const void *data2, - const u64 time) + const u64 time, bool *changep) { u16 *data; struct efi_var_entry *var; u32 var_name_len; + if (changep) + *changep = true; + + /* + * If this is not an append (size2 == 0), check whether the variable + * already exists with identical attributes and data. When nothing + * changed we can skip the write and avoid superfluous erases. + */ + if (!size2 && changep) { + struct efi_var_entry *old; + + old = efi_var_mem_find(vendor, variable_name, NULL); + if (old && old->attr == attributes && + old->length == size1 && old->time == time) { + u16 *old_data; + + for (old_data = old->name; *old_data; ++old_data) + ; + ++old_data; + + if (!efi_memcmp_runtime(old_data, data1, size1)) { + *changep = false; + return EFI_SUCCESS; + } + } + } + var = (struct efi_var_entry *) ((uintptr_t)efi_var_buf + efi_var_buf->length); var_name_len = u16_strlen(variable_name) + 1; diff --git a/lib/efi_loader/efi_var_sf.c b/lib/efi_loader/efi_var_sf.c new file mode 100644 index 00000000000..6eae8d46464 --- /dev/null +++ b/lib/efi_loader/efi_var_sf.c @@ -0,0 +1,111 @@ +// 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 spi_flash *flash; + struct udevice *sfdev; + efi_status_t ret; + size_t erase_len; + 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_debug("EFI var buffer length more than target SPI Flash size\n"); + 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; + } + + flash = dev_get_uclass_priv(sfdev); + if (!flash) { + log_debug("Failed to get SPI Flash priv data\n"); + ret = EFI_DEVICE_ERROR; + goto error; + } + erase_len = ALIGN(len, flash->sector_size); + + r = spi_flash_erase_dm(sfdev, CONFIG_EFI_VARIABLE_SF_OFFSET, + erase_len); + if (r) { + log_debug("Failed to erase SPI Flash\n"); + 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; + } + +error: + 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_err("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_err("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_err("Failed to read from SPI Flash: %d\n", r); + ret = EFI_DEVICE_ERROR; + goto error; + } + + if (efi_var_restore(buf, false) != EFI_SUCCESS) { + log_err("No valid EFI variables in SPI Flash\n"); + ret = EFI_DEVICE_ERROR; + goto error; + } + + ret = EFI_SUCCESS; +error: + free(buf); + return ret; +} diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 8512bc20f11..9923936c1b5 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -277,6 +277,7 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, struct efi_var_entry *var; efi_uintn_t ret; bool append, delete; + bool changed = false; u64 time = 0; enum efi_auth_var_type var_type; @@ -366,6 +367,7 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, if (delete) { /* EFI_NOT_FOUND has been handled before */ attributes = var->attr; + changed = true; ret = EFI_SUCCESS; } else if (append && var) { /* @@ -380,15 +382,19 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, ret = efi_var_mem_ins(variable_name, vendor, attributes & ~EFI_VARIABLE_APPEND_WRITE, var->length, old_data, data_size, data, - time); + time, &changed); } else { ret = efi_var_mem_ins(variable_name, vendor, attributes, - data_size, data, 0, NULL, time); + data_size, data, 0, NULL, time, + &changed); } if (ret != EFI_SUCCESS) return ret; + if (!changed) + return EFI_SUCCESS; + efi_var_mem_del(var); if (var_type == EFI_AUTH_VAR_PK) @@ -396,10 +402,7 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, else ret = EFI_SUCCESS; - /* - * Write non-volatile EFI variables - * TODO: check if a value change has occured to avoid superfluous writes - */ + /* Write non-volatile EFI variables to storage */ if (attributes & EFI_VARIABLE_NON_VOLATILE) { if (IS_ENABLED(CONFIG_EFI_VARIABLE_NO_STORE)) return EFI_SUCCESS; @@ -498,6 +501,7 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, struct efi_var_entry *var; efi_uintn_t ret; bool append, delete; + bool changed = false; u64 time = 0; if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) @@ -549,6 +553,7 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, if (delete) { /* EFI_NOT_FOUND has been handled before */ attributes = var->attr; + changed = true; ret = EFI_SUCCESS; } else if (append && var) { u16 *old_data = (void *)((uintptr_t)var->name + @@ -556,15 +561,19 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, ret = efi_var_mem_ins(variable_name, vendor, attributes, var->length, old_data, data_size, data, - time); + time, &changed); } else { ret = efi_var_mem_ins(variable_name, vendor, attributes, - data_size, data, 0, NULL, time); + data_size, data, 0, NULL, time, + &changed); } if (ret != EFI_SUCCESS) return ret; - /* We are always inserting new variables, get rid of the old copy */ + + if (!changed) + return EFI_SUCCESS; + efi_var_mem_del(var); return EFI_SUCCESS; diff --git a/lib/uuid.c b/lib/uuid.c index 0a166320e07..3a666d0430d 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -254,6 +254,12 @@ static const struct { NULL, "EFI Conformance Profiles Table", EFI_CONFORMANCE_PROFILES_TABLE_GUID, }, +#if CONFIG_IS_ENABLED(EFI_ECPT) + { + NULL, "EFI EBBR 2.1 Conformance Profile", + EFI_CONFORMANCE_PROFILE_EBBR_2_1_GUID, + }, +#endif #ifdef CONFIG_EFI_RISCV_BOOT_PROTOCOL { NULL, "RISC-V Boot", |
