From 94c5c0835bccdb0763c25af7fdf72cdd2b9f7e5a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 13 Mar 2026 12:20:37 +0100 Subject: efi_loader: avoid superfluous variable store writes on unchanged data Every SetVariable() call triggers efi_var_mem_ins() followed by efi_var_to_storage(), even when the variable value is not actually changing. This is unfriendly to flash-backed stores that suffer wear from unnecessary erase/write cycles. Add a change-detection path to efi_var_mem_ins(): when size2 == 0 (i.e. not an append) and the caller passes a non-NULL changep flag, look up the existing variable and compare attributes, length, time and data byte-by-byte. If everything matches, set *changep = false and return EFI_SUCCESS without touching the variable buffer. Both efi_set_variable_int() and efi_set_variable_runtime() now check the flag and skip efi_var_mem_del() / efi_var_to_storage() when nothing changed. Introduce efi_memcmp_runtime() - a runtime-safe byte-by-byte memory comparison helper, following the same pattern as the existing efi_memcpy_runtime(). The standard memcmp() is not available after ExitBootServices() and calling it from Linux will crash. Tested-by: Heinrich Schuchardt Reviewed-by: Heinrich Schuchardt Signed-off-by: Michal Simek Reviewed-by: Ilias Apalodimas --- lib/efi_loader/efi_variable.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'lib/efi_loader/efi_variable.c') 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; -- cgit v1.2.3