1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include "alloc-util.h"
14 #include "chattr-util.h"
19 #include "stdio-util.h"
21 #include "time-util.h"
27 /* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
28 #define EFI_N_RETRIES 5
29 #define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
31 char* efi_variable_path(sd_id128_t vendor
, const char *name
) {
35 "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR
,
36 name
, SD_ID128_FORMAT_VAL(vendor
)) < 0)
45 uint32_t *ret_attribute
,
49 _cleanup_close_
int fd
= -1;
50 _cleanup_free_
char *p
= NULL
;
51 _cleanup_free_
void *buf
= NULL
;
59 p
= efi_variable_path(vendor
, name
);
63 if (!ret_value
&& !ret_size
&& !ret_attribute
) {
64 /* If caller is not interested in anything, just check if the variable exists and is
66 if (access(p
, R_OK
) < 0)
73 begin
= now(CLOCK_MONOTONIC
);
75 fd
= open(p
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
77 return log_debug_errno(errno
, "open(\"%s\") failed: %m", p
);
79 if (fstat(fd
, &st
) < 0)
83 if (st
.st_size
> 4*1024*1024 + 4)
86 if (ret_value
|| ret_attribute
) {
87 /* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll
88 * occasionally fail with EINTR here. A slowdown is better than a failure for us, so
89 * retry a few times and eventually fail with -EBUSY.
91 * See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75
93 * https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
95 for (unsigned try = 0;; try++) {
96 n
= read(fd
, &a
, sizeof(a
));
99 log_debug_errno(errno
, "read from \"%s\" failed: %m", p
);
102 if (try >= EFI_N_RETRIES
)
104 usleep(EFI_RETRY_DELAY
);
112 buf
= malloc(st
.st_size
- 4 + 2);
116 n
= read(fd
, buf
, (size_t) st
.st_size
- 4);
119 assert(n
<= st
.st_size
- 4);
121 /* Always NUL terminate (2 bytes, to protect UTF-16) */
122 ((char*) buf
)[n
] = 0;
123 ((char*) buf
)[n
+ 1] = 0;
125 /* Assume that the reported size is accurate */
129 char ts
[FORMAT_TIMESPAN_MAX
];
132 end
= now(CLOCK_MONOTONIC
);
133 if (end
> begin
+ EFI_RETRY_DELAY
)
134 log_debug("Detected slow EFI variable read access on " SD_ID128_FORMAT_STR
"-%s: %s",
135 SD_ID128_FORMAT_VAL(vendor
), name
, format_timespan(ts
, sizeof(ts
), end
- begin
, 1));
138 /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
139 * with a smaller value. */
145 *ret_value
= TAKE_PTR(buf
);
153 int efi_get_variable_string(sd_id128_t vendor
, const char *name
, char **p
) {
154 _cleanup_free_
void *s
= NULL
;
159 r
= efi_get_variable(vendor
, name
, NULL
, &s
, &ss
);
163 x
= utf16_to_utf8(s
, ss
);
171 int efi_set_variable(
180 } _packed_
* _cleanup_free_ buf
= NULL
;
181 _cleanup_free_
char *p
= NULL
;
182 _cleanup_close_
int fd
= -1;
183 bool saved_flags_valid
= false;
184 unsigned saved_flags
;
188 assert(value
|| size
== 0);
190 p
= efi_variable_path(vendor
, name
);
194 /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
195 * them for accidental removal and modification. We are not changing these variables accidentally however,
196 * hence let's unset the bit first. */
198 r
= chattr_path(p
, 0, FS_IMMUTABLE_FL
, &saved_flags
);
199 if (r
< 0 && r
!= -ENOENT
)
200 log_debug_errno(r
, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p
);
202 saved_flags_valid
= r
>= 0;
213 fd
= open(p
, O_WRONLY
|O_CREAT
|O_NOCTTY
|O_CLOEXEC
, 0644);
219 buf
= malloc(sizeof(uint32_t) + size
);
225 buf
->attr
= EFI_VARIABLE_NON_VOLATILE
|EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
226 memcpy(buf
->buf
, value
, size
);
228 r
= loop_write(fd
, buf
, sizeof(uint32_t) + size
, false);
235 if (saved_flags_valid
) {
238 /* Restore the original flags field, just in case */
240 q
= chattr_path(p
, saved_flags
, FS_IMMUTABLE_FL
, NULL
);
242 q
= chattr_fd(fd
, saved_flags
, FS_IMMUTABLE_FL
, NULL
);
244 log_debug_errno(q
, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p
);
250 int efi_set_variable_string(sd_id128_t vendor
, const char *name
, const char *v
) {
251 _cleanup_free_ char16_t
*u16
= NULL
;
253 u16
= utf8_to_utf16(v
, strlen(v
));
257 return efi_set_variable(vendor
, name
, u16
, (char16_strlen(u16
) + 1) * sizeof(char16_t
));
260 bool is_efi_boot(void) {
261 static int cache
= -1;
264 if (detect_container() > 0)
267 cache
= access("/sys/firmware/efi/", F_OK
) >= 0;
273 static int read_flag(const char *varname
) {
274 _cleanup_free_
void *v
= NULL
;
279 if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
282 r
= efi_get_variable(EFI_VENDOR_GLOBAL
, varname
, NULL
, &v
, &s
);
293 bool is_efi_secure_boot(void) {
294 static int cache
= -1;
297 cache
= read_flag("SecureBoot");
302 bool is_efi_secure_boot_setup_mode(void) {
303 static int cache
= -1;
306 cache
= read_flag("SetupMode");
311 int systemd_efi_options_variable(char **line
) {
317 /* For testing purposes it is sometimes useful to be able to override this */
318 e
= secure_getenv("SYSTEMD_EFI_OPTIONS");
330 /* In SecureBoot mode this is probably not what you want. As your cmdline is cryptographically signed
331 * like when using Type #2 EFI Unified Kernel Images (https://systemd.io/BOOT_LOADER_SPECIFICATION/)
332 * The user's intention is then that the cmdline should not be modified. You want to make sure that
333 * the system starts up as exactly specified in the signed artifact.
335 * (NB: to make testing purposes we still check the $SYSTEMD_EFI_OPTIONS env var above, even when in
336 * SecureBoot mode.) */
337 if (is_efi_secure_boot()) {
338 _cleanup_free_
char *k
;
340 k
= efi_variable_path(EFI_VENDOR_SYSTEMD
, "SystemdOptions");
344 /* Let's be helpful with the returned error and check if the variable exists at all. If it
345 * does, let's return a recognizable error (EPERM), and if not ENODATA. */
347 if (access(k
, F_OK
) < 0)
348 return errno
== ENOENT
? -ENODATA
: -errno
;
353 r
= efi_get_variable_string(EFI_VENDOR_SYSTEMD
, "SystemdOptions", line
);