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 <xypron.glpk@gmx.de>
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
This commit is contained in:
Michal Simek
2026-03-14 08:14:20 +01:00
committed by Heinrich Schuchardt
parent a9080e600c
commit 94c5c0835b
6 changed files with 81 additions and 12 deletions
+7 -1
View File
@@ -216,6 +216,11 @@ void efi_var_mem_del(struct efi_var_entry *var);
* The variable is appended without checking if a variable of the same name
* already exists. The two data buffers are concatenated.
*
* When @changep is non-NULL and @size2 is 0, the function compares the new
* value against an existing variable with the same name and vendor. If
* attributes and data are identical the insertion is skipped and *@changep
* is set to false, avoiding superfluous writes.
*
* @variable_name: variable name
* @vendor: GUID
* @attributes: variable attributes
@@ -224,13 +229,14 @@ void efi_var_mem_del(struct efi_var_entry *var);
* @size2: size of the second data field
* @data2: second data buffer
* @time: time of authentication (as seconds since start of epoch)
* @changep: pointer to change flag (may be NULL)
* Result: status code
*/
efi_status_t efi_var_mem_ins(const u16 *variable_name,
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);
/**
* efi_var_mem_free() - determine free memory for variables