summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@bitwise.fi>2021-10-18 16:31:30 +0300
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-10-23 23:57:56 +0900
commit37e4637a9e6b87aa89c629abd39d03d8c43ef5aa (patch)
treed70e7880f86d063e16b61e721cedc3574dbfb346
parentaa0379f16fc32f66b37fbcc9fc33c1afa22a61a8 (diff)
downloadsystemd-37e4637a9e6b87aa89c629abd39d03d8c43ef5aa.tar.gz
efivars: skip writing if variable is already in wanted state
In order to minimize EFI variable NVRAM wear, do not rewrite variables if they are already in the wanted state (i.e. same data and attributes). This allows e.g. performing repeat calls of "bootctl install" (which always rewrites the EFI boot entry) without consuming EFI NVRAM write cycles.
-rw-r--r--src/basic/efivars.c26
1 files changed, 25 insertions, 1 deletions
diff --git a/src/basic/efivars.c b/src/basic/efivars.c
index bb115a7b99..7a9d1bf641 100644
--- a/src/basic/efivars.c
+++ b/src/basic/efivars.c
@@ -17,6 +17,7 @@
#include "fileio.h"
#include "io-util.h"
#include "macro.h"
+#include "memory-util.h"
#include "stdio-util.h"
#include "strv.h"
#include "time-util.h"
@@ -159,12 +160,29 @@ int efi_get_variable_string(const char *variable, char **p) {
return 0;
}
+static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
+ _cleanup_free_ void *buf = NULL;
+ size_t n;
+ uint32_t a;
+ int r;
+
+ assert(variable);
+ assert(value || size == 0);
+
+ r = efi_get_variable(variable, &a, &buf, &n);
+ if (r < 0)
+ return r;
+
+ return a == attr && memcmp_nn(buf, n, value, size) == 0;
+}
+
int efi_set_variable(const char *variable, const void *value, size_t size) {
struct var {
uint32_t attr;
char buf[];
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_close_ int fd = -1;
+ uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
bool saved_flags_valid = false;
unsigned saved_flags;
int r;
@@ -174,6 +192,12 @@ int efi_set_variable(const char *variable, const void *value, size_t size) {
const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
+ /* size 0 means removal, empty variable would not be enough for that */
+ if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
+ log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
+ return 0;
+ }
+
/* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
* to protect them for accidental removal and modification. We are not changing these variables
* accidentally however, hence let's unset the bit first. */
@@ -205,7 +229,7 @@ int efi_set_variable(const char *variable, const void *value, size_t size) {
goto finish;
}
- buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+ buf->attr = attr;
memcpy(buf->buf, value, size);
r = loop_write(fd, buf, sizeof(uint32_t) + size, false);