]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/efi-loader.c
Merge pull request #16678 from poettering/loop-configure
[thirdparty/systemd.git] / src / shared / efi-loader.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2e3d0692 2
a8fbdf54 3#include <stdlib.h>
c37070c8 4#include <sys/stat.h>
cf0fbc49 5#include <unistd.h>
2e3d0692 6
b5efdb8a 7#include "alloc-util.h"
a0956174 8#include "dirent-util.h"
0bb2f0f1 9#include "efi-loader.h"
3ffd4af2
LP
10#include "efivars.h"
11#include "fd-util.h"
c004493c 12#include "io-util.h"
6bedfcbb 13#include "parse-util.h"
760877e9 14#include "sort-util.h"
c37070c8 15#include "stat-util.h"
15a5e950 16#include "stdio-util.h"
0bb2f0f1 17#include "string-util.h"
2e3d0692 18#include "utf8.h"
5bdf2243 19#include "virt.h"
2e3d0692 20
349cc4a5 21#if ENABLE_EFI
b872e9a0 22
0974a682
KS
23#define LOAD_OPTION_ACTIVE 0x00000001
24#define MEDIA_DEVICE_PATH 0x04
25#define MEDIA_HARDDRIVE_DP 0x01
26#define MEDIA_FILEPATH_DP 0x04
27#define SIGNATURE_TYPE_GUID 0x02
28#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
29#define END_DEVICE_PATH_TYPE 0x7f
30#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
5bdf2243 31#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
0974a682 32
5c25f423
LP
33#define boot_option__contents \
34 { \
35 uint32_t attr; \
36 uint16_t path_len; \
37 uint16_t title[]; \
f7cb1c79
ZJS
38 }
39
40struct boot_option boot_option__contents;
41struct boot_option__packed boot_option__contents _packed_;
42assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title));
43/* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so
44 * the *size* of the structure should not be used anywhere below. */
0974a682
KS
45
46struct drive_path {
47 uint32_t part_nr;
48 uint64_t part_start;
49 uint64_t part_size;
50 char signature[16];
51 uint8_t mbr_type;
52 uint8_t signature_type;
885fdebc 53} _packed_;
0974a682 54
5c25f423
LP
55#define device_path__contents \
56 { \
57 uint8_t type; \
58 uint8_t sub_type; \
59 uint16_t length; \
60 union { \
61 uint16_t path[0]; \
62 struct drive_path drive; \
63 }; \
3c7dddac
ZJS
64 }
65
66struct device_path device_path__contents;
67struct device_path__packed device_path__contents _packed_;
68assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
0974a682 69
5bdf2243 70int efi_reboot_to_firmware_supported(void) {
5bdf2243 71 _cleanup_free_ void *v = NULL;
9e5230aa 72 static int cache = -1;
e22c567f
LP
73 uint64_t b;
74 size_t s;
75 int r;
5bdf2243 76
9e5230aa
LP
77 if (cache > 0)
78 return 0;
79 if (cache == 0)
5bdf2243
JJ
80 return -EOPNOTSUPP;
81
9e5230aa
LP
82 if (!is_efi_boot())
83 goto not_supported;
84
5bdf2243 85 r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
9e5230aa
LP
86 if (r == -ENOENT)
87 goto not_supported; /* variable doesn't exist? it's not supported then */
5bdf2243
JJ
88 if (r < 0)
89 return r;
e22c567f 90 if (s != sizeof(uint64_t))
5bdf2243
JJ
91 return -EINVAL;
92
e22c567f
LP
93 b = *(uint64_t*) v;
94 if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
9e5230aa 95 goto not_supported; /* bit unset? it's not supported then */
e22c567f 96
9e5230aa 97 cache = 1;
e22c567f 98 return 0;
9e5230aa
LP
99
100not_supported:
101 cache = 0;
102 return -EOPNOTSUPP;
5bdf2243
JJ
103}
104
c37070c8
LP
105static int get_os_indications(uint64_t *ret) {
106 static struct stat cache_stat = {};
5bdf2243 107 _cleanup_free_ void *v = NULL;
c37070c8
LP
108 _cleanup_free_ char *fn = NULL;
109 static uint64_t cache;
110 struct stat new_stat;
e22c567f
LP
111 size_t s;
112 int r;
5bdf2243 113
c37070c8
LP
114 assert(ret);
115
e78c250b 116 /* Let's verify general support first */
5bdf2243
JJ
117 r = efi_reboot_to_firmware_supported();
118 if (r < 0)
119 return r;
120
c37070c8
LP
121 fn = efi_variable_path(EFI_VENDOR_GLOBAL, "OsIndications");
122 if (!fn)
123 return -ENOMEM;
124
125 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
126 if (stat(fn, &new_stat) < 0) {
127 if (errno != ENOENT)
128 return -errno;
129
130 /* Doesn't exist? Then we can exit early (also see below) */
131 *ret = 0;
132 return 0;
133
134 } else if (stat_inode_unmodified(&new_stat, &cache_stat)) {
135 /* inode didn't change, we can return the cached value */
136 *ret = cache;
137 return 0;
138 }
139
5bdf2243 140 r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
6b62bbbc 141 if (r == -ENOENT) {
e78c250b 142 /* Some firmware implementations that do support OsIndications and report that with
c37070c8
LP
143 * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
144 * pretend it's 0 then, to hide this implementation detail. Note that this call will return
145 * -ENOENT then only if the support for OsIndications is missing entirely, as determined by
146 * efi_reboot_to_firmware_supported() above. */
147 *ret = 0;
6b62bbbc 148 return 0;
c37070c8
LP
149 }
150 if (r < 0)
5bdf2243 151 return r;
9e5230aa 152 if (s != sizeof(uint64_t))
5bdf2243
JJ
153 return -EINVAL;
154
c37070c8
LP
155 cache_stat = new_stat;
156 *ret = cache = *(uint64_t *)v;
5bdf2243
JJ
157 return 0;
158}
159
160int efi_get_reboot_to_firmware(void) {
161 int r;
162 uint64_t b;
163
164 r = get_os_indications(&b);
165 if (r < 0)
166 return r;
167
168 return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
169}
170
171int efi_set_reboot_to_firmware(bool value) {
172 int r;
173 uint64_t b, b_new;
174
175 r = get_os_indications(&b);
176 if (r < 0)
177 return r;
178
0da96503 179 b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value);
5bdf2243
JJ
180
181 /* Avoid writing to efi vars store if we can due to firmware bugs. */
182 if (b != b_new)
183 return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
184
185 return 0;
186}
187
f7cb1c79 188static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
7b4d7cc0
KS
189 size_t l = 0;
190
f7cb1c79
ZJS
191 /* Returns the size of the string in bytes without the terminating two zero bytes */
192
193 if (buf_len_bytes % sizeof(uint16_t) != 0)
194 return -EINVAL;
195
196 while (l < buf_len_bytes / sizeof(uint16_t)) {
197 if (s[l] == 0)
198 return (l + 1) * sizeof(uint16_t);
7b4d7cc0 199 l++;
f7cb1c79 200 }
9cde64ff 201
f7cb1c79 202 return -EINVAL; /* The terminator was not found */
7b4d7cc0
KS
203}
204
3c7dddac
ZJS
205struct guid {
206 uint32_t u1;
207 uint16_t u2;
208 uint16_t u3;
209 uint8_t u4[8];
210} _packed_;
211
7b4d7cc0 212static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
3c7dddac
ZJS
213 uint32_t u1;
214 uint16_t u2, u3;
215 const struct guid *uuid = guid;
216
217 memcpy(&u1, &uuid->u1, sizeof(uint32_t));
218 id128->bytes[0] = (u1 >> 24) & 0xff;
219 id128->bytes[1] = (u1 >> 16) & 0xff;
220 id128->bytes[2] = (u1 >> 8) & 0xff;
221 id128->bytes[3] = u1 & 0xff;
222 memcpy(&u2, &uuid->u2, sizeof(uint16_t));
223 id128->bytes[4] = (u2 >> 8) & 0xff;
224 id128->bytes[5] = u2 & 0xff;
225 memcpy(&u3, &uuid->u3, sizeof(uint16_t));
226 id128->bytes[6] = (u3 >> 8) & 0xff;
227 id128->bytes[7] = u3 & 0xff;
7b4d7cc0
KS
228 memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
229}
230
9cde64ff
LP
231int efi_get_boot_option(
232 uint16_t id,
233 char **title,
234 sd_id128_t *part_uuid,
0974a682
KS
235 char **path,
236 bool *active) {
7b4d7cc0 237
9cde64ff
LP
238 char boot_id[9];
239 _cleanup_free_ uint8_t *buf = NULL;
7b4d7cc0
KS
240 size_t l;
241 struct boot_option *header;
f7cb1c79 242 ssize_t title_size;
b7749eb5 243 _cleanup_free_ char *s = NULL, *p = NULL;
7b4d7cc0 244 sd_id128_t p_uuid = SD_ID128_NULL;
a8436474 245 int r;
7b4d7cc0 246
337eed30
LP
247 if (!is_efi_boot())
248 return -EOPNOTSUPP;
249
b7749eb5 250 xsprintf(boot_id, "Boot%04X", id);
a8436474
ZJS
251 r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
252 if (r < 0)
253 return r;
f7cb1c79 254 if (l < offsetof(struct boot_option, title))
7b4d7cc0
KS
255 return -ENOENT;
256
257 header = (struct boot_option *)buf;
f7cb1c79
ZJS
258 title_size = utf16_size(header->title, l - offsetof(struct boot_option, title));
259 if (title_size < 0)
260 return title_size;
7b4d7cc0 261
5483a186
ZJS
262 if (title) {
263 s = utf16_to_utf8(header->title, title_size);
b7749eb5
ZJS
264 if (!s)
265 return -ENOMEM;
7b4d7cc0
KS
266 }
267
268 if (header->path_len > 0) {
9cde64ff 269 uint8_t *dbuf;
e7e36b90
DT
270 size_t dnext, doff;
271
272 doff = offsetof(struct boot_option, title) + title_size;
273 dbuf = buf + doff;
274 if (header->path_len > l - doff)
275 return -EINVAL;
7b4d7cc0 276
7b4d7cc0
KS
277 dnext = 0;
278 while (dnext < header->path_len) {
279 struct device_path *dpath;
280
281 dpath = (struct device_path *)(dbuf + dnext);
282 if (dpath->length < 4)
283 break;
284
285 /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
0974a682 286 if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
7b4d7cc0
KS
287 break;
288
289 dnext += dpath->length;
290
291 /* Type 0x04 – Media Device Path */
0974a682 292 if (dpath->type != MEDIA_DEVICE_PATH)
7b4d7cc0
KS
293 continue;
294
295 /* Sub-Type 1 – Hard Drive */
0974a682 296 if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
7b4d7cc0 297 /* 0x02 – GUID Partition Table */
0974a682 298 if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
7b4d7cc0
KS
299 continue;
300
301 /* 0x02 – GUID signature */
0974a682 302 if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
7b4d7cc0
KS
303 continue;
304
5483a186
ZJS
305 if (part_uuid)
306 efi_guid_to_id128(dpath->drive.signature, &p_uuid);
7b4d7cc0
KS
307 continue;
308 }
309
310 /* Sub-Type 4 – File Path */
0974a682 311 if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
7b4d7cc0 312 p = utf16_to_utf8(dpath->path, dpath->length-4);
9db296fd
LP
313 if (!p)
314 return -ENOMEM;
315
0974a682 316 efi_tilt_backslashes(p);
7b4d7cc0
KS
317 continue;
318 }
319 }
320 }
321
1cc6c93a
YW
322 if (title)
323 *title = TAKE_PTR(s);
7b4d7cc0
KS
324 if (part_uuid)
325 *part_uuid = p_uuid;
1cc6c93a
YW
326 if (path)
327 *path = TAKE_PTR(p);
0974a682 328 if (active)
55033662 329 *active = header->attr & LOAD_OPTION_ACTIVE;
9cde64ff 330
7b4d7cc0 331 return 0;
7b4d7cc0
KS
332}
333
0974a682
KS
334static void to_utf16(uint16_t *dest, const char *src) {
335 int i;
336
337 for (i = 0; src[i] != '\0'; i++)
338 dest[i] = src[i];
339 dest[i] = '\0';
340}
341
0974a682 342static void id128_to_efi_guid(sd_id128_t id, void *guid) {
3c7dddac
ZJS
343 struct guid uuid = {
344 .u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3],
345 .u2 = id.bytes[4] << 8 | id.bytes[5],
346 .u3 = id.bytes[6] << 8 | id.bytes[7],
347 };
348 memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4));
349 memcpy(guid, &uuid, sizeof(uuid));
0974a682
KS
350}
351
352static uint16_t *tilt_slashes(uint16_t *s) {
353 uint16_t *p;
354
355 for (p = s; *p; p++)
356 if (*p == '/')
357 *p = '\\';
358
359 return s;
360}
361
337eed30
LP
362int efi_add_boot_option(
363 uint16_t id,
364 const char *title,
365 uint32_t part,
366 uint64_t pstart,
367 uint64_t psize,
368 sd_id128_t part_uuid,
369 const char *path) {
370
371 size_t size, title_len, path_len;
372 _cleanup_free_ char *buf = NULL;
0974a682
KS
373 struct boot_option *option;
374 struct device_path *devicep;
337eed30
LP
375 char boot_id[9];
376
377 if (!is_efi_boot())
378 return -EOPNOTSUPP;
0974a682
KS
379
380 title_len = (strlen(title)+1) * 2;
381 path_len = (strlen(path)+1) * 2;
382
f7cb1c79 383 buf = malloc0(offsetof(struct boot_option, title) + title_len +
e78c250b
LP
384 sizeof(struct drive_path) +
385 sizeof(struct device_path) + path_len);
b7749eb5
ZJS
386 if (!buf)
387 return -ENOMEM;
0974a682
KS
388
389 /* header */
390 option = (struct boot_option *)buf;
391 option->attr = LOAD_OPTION_ACTIVE;
392 option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
393 offsetof(struct device_path, path) + path_len +
394 offsetof(struct device_path, path);
395 to_utf16(option->title, title);
396 size = offsetof(struct boot_option, title) + title_len;
397
398 /* partition info */
399 devicep = (struct device_path *)(buf + size);
400 devicep->type = MEDIA_DEVICE_PATH;
401 devicep->sub_type = MEDIA_HARDDRIVE_DP;
402 devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
3c7dddac
ZJS
403 memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t));
404 memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t));
405 memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t));
0974a682 406 id128_to_efi_guid(part_uuid, devicep->drive.signature);
3c7dddac
ZJS
407 devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
408 devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
0974a682
KS
409 size += devicep->length;
410
411 /* path to loader */
412 devicep = (struct device_path *)(buf + size);
413 devicep->type = MEDIA_DEVICE_PATH;
414 devicep->sub_type = MEDIA_FILEPATH_DP;
415 devicep->length = offsetof(struct device_path, path) + path_len;
416 to_utf16(devicep->path, path);
417 tilt_slashes(devicep->path);
418 size += devicep->length;
419
420 /* end of path */
421 devicep = (struct device_path *)(buf + size);
422 devicep->type = END_DEVICE_PATH_TYPE;
423 devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
424 devicep->length = offsetof(struct device_path, path);
425 size += devicep->length;
426
b7749eb5
ZJS
427 xsprintf(boot_id, "Boot%04X", id);
428 return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
0974a682
KS
429}
430
431int efi_remove_boot_option(uint16_t id) {
432 char boot_id[9];
433
337eed30
LP
434 if (!is_efi_boot())
435 return -EOPNOTSUPP;
436
b7749eb5 437 xsprintf(boot_id, "Boot%04X", id);
0974a682
KS
438 return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
439}
440
9cde64ff 441int efi_get_boot_order(uint16_t **order) {
0797f232 442 _cleanup_free_ void *buf = NULL;
7b4d7cc0 443 size_t l;
9cde64ff 444 int r;
7b4d7cc0 445
337eed30
LP
446 if (!is_efi_boot())
447 return -EOPNOTSUPP;
448
9cde64ff
LP
449 r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
450 if (r < 0)
451 return r;
7b4d7cc0 452
0797f232 453 if (l <= 0)
7b4d7cc0 454 return -ENOENT;
7b4d7cc0 455
0797f232
ZJS
456 if (l % sizeof(uint16_t) > 0 ||
457 l / sizeof(uint16_t) > INT_MAX)
7b4d7cc0 458 return -EINVAL;
7b4d7cc0 459
ae2a15bc 460 *order = TAKE_PTR(buf);
9cde64ff
LP
461 return (int) (l / sizeof(uint16_t));
462}
463
0974a682 464int efi_set_boot_order(uint16_t *order, size_t n) {
337eed30
LP
465
466 if (!is_efi_boot())
467 return -EOPNOTSUPP;
468
0974a682
KS
469 return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
470}
471
3042bbeb 472static int boot_id_hex(const char s[static 4]) {
e78c250b 473 int id = 0, i;
4d34c495 474
2caa38e9
LP
475 assert(s);
476
4d34c495
KS
477 for (i = 0; i < 4; i++)
478 if (s[i] >= '0' && s[i] <= '9')
479 id |= (s[i] - '0') << (3 - i) * 4;
480 else if (s[i] >= 'A' && s[i] <= 'F')
481 id |= (s[i] - 'A' + 10) << (3 - i) * 4;
482 else
7e8185ef 483 return -EINVAL;
4d34c495
KS
484
485 return id;
486}
487
93bab288
YW
488static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
489 return CMP(*a, *b);
9db11a99
LP
490}
491
9cde64ff
LP
492int efi_get_boot_options(uint16_t **options) {
493 _cleanup_closedir_ DIR *dir = NULL;
b7749eb5 494 _cleanup_free_ uint16_t *list = NULL;
e78c250b 495 struct dirent *de;
7432b24b 496 size_t alloc = 0;
b7749eb5 497 int count = 0;
9cde64ff
LP
498
499 assert(options);
500
337eed30
LP
501 if (!is_efi_boot())
502 return -EOPNOTSUPP;
503
9cde64ff
LP
504 dir = opendir("/sys/firmware/efi/efivars/");
505 if (!dir)
506 return -errno;
507
b7749eb5 508 FOREACH_DIRENT(de, dir, return -errno) {
4d34c495 509 int id;
9cde64ff
LP
510
511 if (strncmp(de->d_name, "Boot", 4) != 0)
512 continue;
513
4d34c495 514 if (strlen(de->d_name) != 45)
9cde64ff
LP
515 continue;
516
517 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
518 continue;
519
4d34c495
KS
520 id = boot_id_hex(de->d_name + 4);
521 if (id < 0)
9cde64ff
LP
522 continue;
523
7432b24b 524 if (!GREEDY_REALLOC(list, alloc, count + 1))
b7749eb5 525 return -ENOMEM;
9cde64ff 526
7432b24b 527 list[count++] = id;
9cde64ff
LP
528 }
529
93bab288 530 typesafe_qsort(list, count, cmp_uint16);
9db11a99 531
1cc6c93a
YW
532 *options = TAKE_PTR(list);
533
9cde64ff 534 return count;
7b4d7cc0
KS
535}
536
5dbe9f53 537static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
2e3d0692 538 _cleanup_free_ char *j = NULL;
2e3d0692 539 int r;
39883f62 540 uint64_t x = 0;
2e3d0692
LP
541
542 assert(name);
543 assert(u);
544
61cc634b 545 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
2e3d0692
LP
546 if (r < 0)
547 return r;
548
2e3d0692
LP
549 r = safe_atou64(j, &x);
550 if (r < 0)
551 return r;
552
5dbe9f53 553 *u = x;
2e3d0692
LP
554 return 0;
555}
556
c51d84dc 557int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
2e3d0692
LP
558 uint64_t x, y;
559 int r;
2e3d0692
LP
560
561 assert(firmware);
562 assert(loader);
563
337eed30
LP
564 if (!is_efi_boot())
565 return -EOPNOTSUPP;
566
e9cea16d 567 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
2e3d0692 568 if (r < 0)
a97abb30 569 return log_debug_errno(r, "Failed to read LoaderTimeInitUSec: %m");
2e3d0692 570
e9cea16d 571 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
2e3d0692 572 if (r < 0)
a97abb30 573 return log_debug_errno(r, "Failed to read LoaderTimeExecUSec: %m");
2e3d0692 574
dde5c821 575 if (y == 0 || y < x || y - x > USEC_PER_HOUR)
a97abb30
ZJS
576 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
577 "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.",
578 x, y);
2e3d0692 579
2e3d0692
LP
580 *firmware = x;
581 *loader = y;
582
583 return 0;
584}
585
c51d84dc 586int efi_loader_get_device_part_uuid(sd_id128_t *u) {
f4ce2b3e 587 _cleanup_free_ char *p = NULL;
f4ce2b3e 588 int r, parsed[16];
f4ce2b3e 589
337eed30
LP
590 if (!is_efi_boot())
591 return -EOPNOTSUPP;
592
61cc634b 593 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
f4ce2b3e
LP
594 if (r < 0)
595 return r;
596
bd44566c 597 if (sscanf(p, SD_ID128_UUID_FORMAT_STR,
f4ce2b3e
LP
598 &parsed[0], &parsed[1], &parsed[2], &parsed[3],
599 &parsed[4], &parsed[5], &parsed[6], &parsed[7],
600 &parsed[8], &parsed[9], &parsed[10], &parsed[11],
601 &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
602 return -EIO;
603
73b80ec2
LP
604 if (u) {
605 unsigned i;
606
607 for (i = 0; i < ELEMENTSOF(parsed); i++)
608 u->bytes[i] = parsed[i];
609 }
f4ce2b3e
LP
610
611 return 0;
612}
b872e9a0 613
bd2865ca
LP
614int efi_loader_get_entries(char ***ret) {
615 _cleanup_free_ char16_t *entries = NULL;
616 _cleanup_strv_free_ char **l = NULL;
617 size_t size, i, start;
618 int r;
619
620 assert(ret);
621
622 if (!is_efi_boot())
623 return -EOPNOTSUPP;
624
625 r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderEntries", NULL, (void**) &entries, &size);
626 if (r < 0)
627 return r;
628
629 /* The variable contains a series of individually NUL terminated UTF-16 strings. */
630
631 for (i = 0, start = 0;; i++) {
68d7c268 632 _cleanup_free_ char *decoded = NULL;
bd2865ca
LP
633 bool end;
634
635 /* Is this the end of the variable's data? */
636 end = i * sizeof(char16_t) >= size;
637
638 /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
639 * so, let's go to the next entry. */
640 if (!end && entries[i] != 0)
641 continue;
642
643 /* We reached the end of a string, let's decode it into UTF-8 */
644 decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
645 if (!decoded)
646 return -ENOMEM;
647
68d7c268
LP
648 if (efi_loader_entry_name_valid(decoded)) {
649 r = strv_consume(&l, TAKE_PTR(decoded));
650 if (r < 0)
651 return r;
652 } else
653 log_debug("Ignoring invalid loader entry '%s'.", decoded);
bd2865ca
LP
654
655 /* We reached the end of the variable */
656 if (end)
657 break;
658
659 /* Continue after the NUL byte */
660 start = i + 1;
661 }
662
663 *ret = TAKE_PTR(l);
664 return 0;
665}
666
80641a81
LP
667int efi_loader_get_features(uint64_t *ret) {
668 _cleanup_free_ void *v = NULL;
669 size_t s;
670 int r;
671
672 if (!is_efi_boot()) {
673 *ret = 0;
674 return 0;
675 }
676
677 r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderFeatures", NULL, &v, &s);
678 if (r == -ENOENT) {
679 _cleanup_free_ char *info = NULL;
680
681 /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
682 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info);
683 if (r < 0) {
684 if (r != -ENOENT)
685 return r;
686
687 /* Variable not set, definitely means not systemd-boot */
688
689 } else if (first_word(info, "systemd-boot")) {
690
691 /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
692 * static in all its versions. */
693
694 *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
695 EFI_LOADER_FEATURE_ENTRY_DEFAULT |
696 EFI_LOADER_FEATURE_ENTRY_ONESHOT;
697
698 return 0;
699 }
700
701 /* No features supported */
702 *ret = 0;
703 return 0;
704 }
705 if (r < 0)
706 return r;
707
baaa35ad
ZJS
708 if (s != sizeof(uint64_t))
709 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
710 "LoaderFeatures EFI variable doesn't have the right size.");
80641a81
LP
711
712 memcpy(ret, v, sizeof(uint64_t));
713 return 0;
714}
6917857e 715
e8df4eee
LP
716int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
717 _cleanup_free_ char *v = NULL, *fn = NULL;
718 static struct stat cache_stat = {};
719 struct stat new_stat;
720 static usec_t cache;
721 uint64_t sec;
722 int r;
723
724 assert(ret);
725
726 fn = efi_variable_path(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot");
727 if (!fn)
728 return -ENOMEM;
729
730 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
731 if (stat(fn, &new_stat) < 0)
732 return -errno;
733
734 if (stat_inode_unmodified(&new_stat, &cache_stat)) {
735 *ret = cache;
736 return 0;
737 }
738
739 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v);
740 if (r < 0)
741 return r;
742
743 r = safe_atou64(v, &sec);
744 if (r < 0)
745 return r;
746 if (sec > USEC_INFINITY / USEC_PER_SEC)
747 return -ERANGE;
748
749 cache_stat = new_stat;
750 *ret = cache = sec * USEC_PER_SEC; /* return in µs */
751 return 0;
752}
af2697e8
LP
753
754int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
755 _cleanup_free_ char *fn = NULL, *v = NULL;
756 struct stat new_stat;
757 int r;
758
759 assert(cache);
760 assert(cache_stat);
761
762 fn = efi_variable_path(EFI_VENDOR_LOADER, "LoaderEntryOneShot");
763 if (!fn)
764 return -ENOMEM;
765
766 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
767 if (stat(fn, &new_stat) < 0)
768 return -errno;
769
770 if (stat_inode_unmodified(&new_stat, cache_stat))
771 return 0;
772
773 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v);
774 if (r < 0)
775 return r;
776
777 if (!efi_loader_entry_name_valid(v))
778 return -EINVAL;
779
780 *cache_stat = new_stat;
781 free_and_replace(*cache, v);
782
783 return 0;
784}
7143b955
LB
785
786#endif
787
788bool efi_loader_entry_name_valid(const char *s) {
789 if (isempty(s))
790 return false;
791
792 if (strlen(s) > FILENAME_MAX) /* Make sure entry names fit in filenames */
793 return false;
794
795 return in_charset(s, ALPHANUMERICAL "+-_.");
796}
797
798char *efi_tilt_backslashes(char *s) {
799 char *p;
800
801 for (p = s; *p; p++)
802 if (*p == '\\')
803 *p = '/';
804
805 return s;
806}