]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/efivars.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / shared / efivars.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2e3d0692
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
a8fbdf54
TA
21#include <dirent.h>
22#include <errno.h>
2e3d0692 23#include <fcntl.h>
a8fbdf54
TA
24#include <limits.h>
25#include <stdio.h>
26#include <stdlib.h>
cf0fbc49 27#include <string.h>
a8fbdf54 28#include <sys/stat.h>
cf0fbc49 29#include <unistd.h>
2e3d0692 30
a8fbdf54
TA
31#include "sd-id128.h"
32
b5efdb8a 33#include "alloc-util.h"
a0956174 34#include "dirent-util.h"
3ffd4af2
LP
35#include "efivars.h"
36#include "fd-util.h"
c004493c 37#include "io-util.h"
a8fbdf54 38#include "macro.h"
6bedfcbb 39#include "parse-util.h"
15a5e950 40#include "stdio-util.h"
a8fbdf54 41#include "time-util.h"
2e3d0692 42#include "utf8.h"
3ffd4af2 43#include "util.h"
5bdf2243 44#include "virt.h"
2e3d0692 45
349cc4a5 46#if ENABLE_EFI
b872e9a0 47
0974a682
KS
48#define LOAD_OPTION_ACTIVE 0x00000001
49#define MEDIA_DEVICE_PATH 0x04
50#define MEDIA_HARDDRIVE_DP 0x01
51#define MEDIA_FILEPATH_DP 0x04
52#define SIGNATURE_TYPE_GUID 0x02
53#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
54#define END_DEVICE_PATH_TYPE 0x7f
55#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
5bdf2243 56#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
0974a682
KS
57
58struct boot_option {
59 uint32_t attr;
60 uint16_t path_len;
61 uint16_t title[];
885fdebc 62} _packed_;
0974a682
KS
63
64struct drive_path {
65 uint32_t part_nr;
66 uint64_t part_start;
67 uint64_t part_size;
68 char signature[16];
69 uint8_t mbr_type;
70 uint8_t signature_type;
885fdebc 71} _packed_;
0974a682
KS
72
73struct device_path {
74 uint8_t type;
75 uint8_t sub_type;
76 uint16_t length;
77 union {
78 uint16_t path[0];
79 struct drive_path drive;
80 };
885fdebc 81} _packed_;
0974a682 82
9cde64ff 83bool is_efi_boot(void) {
34e5a31e
LP
84 return access("/sys/firmware/efi", F_OK) >= 0;
85}
86
bc6f2e7c 87static int read_flag(const char *varname) {
b47d419c 88 _cleanup_free_ void *v = NULL;
bc6f2e7c 89 uint8_t b;
e22c567f
LP
90 size_t s;
91 int r;
bc6f2e7c
KS
92
93 r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
94 if (r < 0)
95 return r;
96
b47d419c
ZJS
97 if (s != 1)
98 return -EINVAL;
bc6f2e7c
KS
99
100 b = *(uint8_t *)v;
e22c567f 101 return b > 0;
bc6f2e7c
KS
102}
103
9df49b33
TG
104bool is_efi_secure_boot(void) {
105 return read_flag("SecureBoot") > 0;
bc6f2e7c
KS
106}
107
9df49b33
TG
108bool is_efi_secure_boot_setup_mode(void) {
109 return read_flag("SetupMode") > 0;
bc6f2e7c
KS
110}
111
5bdf2243 112int efi_reboot_to_firmware_supported(void) {
5bdf2243 113 _cleanup_free_ void *v = NULL;
e22c567f
LP
114 uint64_t b;
115 size_t s;
116 int r;
5bdf2243 117
75f86906 118 if (!is_efi_boot() || detect_container() > 0)
5bdf2243
JJ
119 return -EOPNOTSUPP;
120
121 r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
846ab104
LP
122 if (r == -ENOENT) /* variable doesn't exist? it's not supported then */
123 return -EOPNOTSUPP;
5bdf2243
JJ
124 if (r < 0)
125 return r;
e22c567f 126 if (s != sizeof(uint64_t))
5bdf2243
JJ
127 return -EINVAL;
128
e22c567f
LP
129 b = *(uint64_t*) v;
130 if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
131 return -EOPNOTSUPP; /* bit unset? it's not supported then */
132
133 return 0;
5bdf2243
JJ
134}
135
136static int get_os_indications(uint64_t *os_indication) {
5bdf2243 137 _cleanup_free_ void *v = NULL;
e22c567f
LP
138 size_t s;
139 int r;
5bdf2243
JJ
140
141 r = efi_reboot_to_firmware_supported();
142 if (r < 0)
143 return r;
144
145 r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
6b62bbbc
LP
146 if (r == -ENOENT) {
147 /* Some firmware implementations that do support
148 * OsIndications and report that with
149 * OsIndicationsSupported will remove the
150 * OsIndications variable when it is unset. Let's
151 * pretend it's 0 then, to hide this implementation
152 * detail. Note that this call will return -ENOENT
153 * then only if the support for OsIndications is
154 * missing entirely, as determined by
155 * efi_reboot_to_firmware_supported() above. */
156 *os_indication = 0;
157 return 0;
158 } else if (r < 0)
5bdf2243
JJ
159 return r;
160 else if (s != sizeof(uint64_t))
161 return -EINVAL;
162
163 *os_indication = *(uint64_t *)v;
164 return 0;
165}
166
167int efi_get_reboot_to_firmware(void) {
168 int r;
169 uint64_t b;
170
171 r = get_os_indications(&b);
172 if (r < 0)
173 return r;
174
175 return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
176}
177
178int efi_set_reboot_to_firmware(bool value) {
179 int r;
180 uint64_t b, b_new;
181
182 r = get_os_indications(&b);
183 if (r < 0)
184 return r;
185
186 if (value)
187 b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
188 else
189 b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
190
191 /* Avoid writing to efi vars store if we can due to firmware bugs. */
192 if (b != b_new)
193 return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
194
195 return 0;
196}
197
9cde64ff
LP
198int efi_get_variable(
199 sd_id128_t vendor,
200 const char *name,
201 uint32_t *attribute,
202 void **value,
203 size_t *size) {
204
2e3d0692
LP
205 _cleanup_close_ int fd = -1;
206 _cleanup_free_ char *p = NULL;
207 uint32_t a;
208 ssize_t n;
209 struct stat st;
ad7bcf52 210 _cleanup_free_ void *buf = NULL;
2e3d0692
LP
211
212 assert(name);
213 assert(value);
214 assert(size);
215
216 if (asprintf(&p,
217 "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
218 name, SD_ID128_FORMAT_VAL(vendor)) < 0)
219 return -ENOMEM;
220
221 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
222 if (fd < 0)
223 return -errno;
224
225 if (fstat(fd, &st) < 0)
226 return -errno;
227 if (st.st_size < 4)
228 return -EIO;
229 if (st.st_size > 4*1024*1024 + 4)
230 return -E2BIG;
231
232 n = read(fd, &a, sizeof(a));
233 if (n < 0)
9cde64ff 234 return -errno;
2e3d0692
LP
235 if (n != sizeof(a))
236 return -EIO;
237
0797f232
ZJS
238 buf = malloc(st.st_size - 4 + 2);
239 if (!buf)
2e3d0692
LP
240 return -ENOMEM;
241
0797f232
ZJS
242 n = read(fd, buf, (size_t) st.st_size - 4);
243 if (n < 0)
742af54a 244 return -errno;
0797f232 245 if (n != (ssize_t) st.st_size - 4)
2e3d0692 246 return -EIO;
2e3d0692
LP
247
248 /* Always NUL terminate (2 bytes, to protect UTF-16) */
0797f232
ZJS
249 ((char*) buf)[st.st_size - 4] = 0;
250 ((char*) buf)[st.st_size - 4 + 1] = 0;
2e3d0692 251
ae2a15bc 252 *value = TAKE_PTR(buf);
ff47c895 253 *size = (size_t) st.st_size - 4;
2e3d0692
LP
254
255 if (attribute)
256 *attribute = a;
257
258 return 0;
259}
260
0974a682
KS
261int efi_set_variable(
262 sd_id128_t vendor,
263 const char *name,
264 const void *value,
265 size_t size) {
266
267 struct var {
268 uint32_t attr;
269 char buf[];
b7749eb5
ZJS
270 } _packed_ * _cleanup_free_ buf = NULL;
271 _cleanup_free_ char *p = NULL;
272 _cleanup_close_ int fd = -1;
0974a682
KS
273
274 assert(name);
e1e26566 275 assert(value || size == 0);
0974a682
KS
276
277 if (asprintf(&p,
278 "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
279 name, SD_ID128_FORMAT_VAL(vendor)) < 0)
280 return -ENOMEM;
281
282 if (size == 0) {
b7749eb5
ZJS
283 if (unlink(p) < 0)
284 return -errno;
285 return 0;
0974a682
KS
286 }
287
288 fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
b7749eb5
ZJS
289 if (fd < 0)
290 return -errno;
0974a682
KS
291
292 buf = malloc(sizeof(uint32_t) + size);
b7749eb5
ZJS
293 if (!buf)
294 return -ENOMEM;
0974a682
KS
295
296 buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
297 memcpy(buf->buf, value, size);
298
b7749eb5 299 return loop_write(fd, buf, sizeof(uint32_t) + size, false);
0974a682
KS
300}
301
9cde64ff 302int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
7b4d7cc0 303 _cleanup_free_ void *s = NULL;
39883f62 304 size_t ss = 0;
9cde64ff
LP
305 int r;
306 char *x;
7b4d7cc0 307
9cde64ff
LP
308 r = efi_get_variable(vendor, name, NULL, &s, &ss);
309 if (r < 0)
310 return r;
311
312 x = utf16_to_utf8(s, ss);
313 if (!x)
314 return -ENOMEM;
315
316 *p = x;
317 return 0;
7b4d7cc0
KS
318}
319
320static size_t utf16_size(const uint16_t *s) {
321 size_t l = 0;
322
323 while (s[l] > 0)
324 l++;
9cde64ff 325
7b4d7cc0
KS
326 return (l+1) * sizeof(uint16_t);
327}
328
329static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
330 struct uuid {
331 uint32_t u1;
332 uint16_t u2;
333 uint16_t u3;
334 uint8_t u4[8];
335 } _packed_;
336 const struct uuid *uuid = guid;
337
338 id128->bytes[0] = (uuid->u1 >> 24) & 0xff;
339 id128->bytes[1] = (uuid->u1 >> 16) & 0xff;
340 id128->bytes[2] = (uuid->u1 >> 8) & 0xff;
341 id128->bytes[3] = (uuid->u1) & 0xff;
342 id128->bytes[4] = (uuid->u2 >> 8) & 0xff;
343 id128->bytes[5] = (uuid->u2) & 0xff;
344 id128->bytes[6] = (uuid->u3 >> 8) & 0xff;
345 id128->bytes[7] = (uuid->u3) & 0xff;
346 memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
347}
348
9cde64ff
LP
349int efi_get_boot_option(
350 uint16_t id,
351 char **title,
352 sd_id128_t *part_uuid,
0974a682
KS
353 char **path,
354 bool *active) {
7b4d7cc0 355
9cde64ff
LP
356 char boot_id[9];
357 _cleanup_free_ uint8_t *buf = NULL;
7b4d7cc0
KS
358 size_t l;
359 struct boot_option *header;
360 size_t title_size;
b7749eb5 361 _cleanup_free_ char *s = NULL, *p = NULL;
7b4d7cc0 362 sd_id128_t p_uuid = SD_ID128_NULL;
a8436474 363 int r;
7b4d7cc0 364
b7749eb5 365 xsprintf(boot_id, "Boot%04X", id);
a8436474
ZJS
366 r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
367 if (r < 0)
368 return r;
7b4d7cc0
KS
369 if (l < sizeof(struct boot_option))
370 return -ENOENT;
371
372 header = (struct boot_option *)buf;
373 title_size = utf16_size(header->title);
374 if (title_size > l - offsetof(struct boot_option, title))
375 return -EINVAL;
376
5483a186
ZJS
377 if (title) {
378 s = utf16_to_utf8(header->title, title_size);
b7749eb5
ZJS
379 if (!s)
380 return -ENOMEM;
7b4d7cc0
KS
381 }
382
383 if (header->path_len > 0) {
9cde64ff 384 uint8_t *dbuf;
7b4d7cc0
KS
385 size_t dnext;
386
387 dbuf = buf + offsetof(struct boot_option, title) + title_size;
388 dnext = 0;
389 while (dnext < header->path_len) {
390 struct device_path *dpath;
391
392 dpath = (struct device_path *)(dbuf + dnext);
393 if (dpath->length < 4)
394 break;
395
396 /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
0974a682 397 if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
7b4d7cc0
KS
398 break;
399
400 dnext += dpath->length;
401
402 /* Type 0x04 – Media Device Path */
0974a682 403 if (dpath->type != MEDIA_DEVICE_PATH)
7b4d7cc0
KS
404 continue;
405
406 /* Sub-Type 1 – Hard Drive */
0974a682 407 if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
7b4d7cc0 408 /* 0x02 – GUID Partition Table */
0974a682 409 if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
7b4d7cc0
KS
410 continue;
411
412 /* 0x02 – GUID signature */
0974a682 413 if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
7b4d7cc0
KS
414 continue;
415
5483a186
ZJS
416 if (part_uuid)
417 efi_guid_to_id128(dpath->drive.signature, &p_uuid);
7b4d7cc0
KS
418 continue;
419 }
420
421 /* Sub-Type 4 – File Path */
0974a682 422 if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
7b4d7cc0 423 p = utf16_to_utf8(dpath->path, dpath->length-4);
0974a682 424 efi_tilt_backslashes(p);
7b4d7cc0
KS
425 continue;
426 }
427 }
428 }
429
1cc6c93a
YW
430 if (title)
431 *title = TAKE_PTR(s);
7b4d7cc0
KS
432 if (part_uuid)
433 *part_uuid = p_uuid;
1cc6c93a
YW
434 if (path)
435 *path = TAKE_PTR(p);
0974a682 436 if (active)
0aa3b783 437 *active = !!(header->attr & LOAD_OPTION_ACTIVE);
9cde64ff 438
7b4d7cc0 439 return 0;
7b4d7cc0
KS
440}
441
0974a682
KS
442static void to_utf16(uint16_t *dest, const char *src) {
443 int i;
444
445 for (i = 0; src[i] != '\0'; i++)
446 dest[i] = src[i];
447 dest[i] = '\0';
448}
449
450struct guid {
451 uint32_t u1;
452 uint16_t u2;
453 uint16_t u3;
454 uint8_t u4[8];
885fdebc 455} _packed_;
0974a682
KS
456
457static void id128_to_efi_guid(sd_id128_t id, void *guid) {
458 struct guid *uuid = guid;
459
460 uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3];
461 uuid->u2 = id.bytes[4] << 8 | id.bytes[5];
462 uuid->u3 = id.bytes[6] << 8 | id.bytes[7];
463 memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4));
464}
465
466static uint16_t *tilt_slashes(uint16_t *s) {
467 uint16_t *p;
468
469 for (p = s; *p; p++)
470 if (*p == '/')
471 *p = '\\';
472
473 return s;
474}
475
0974a682
KS
476int efi_add_boot_option(uint16_t id, const char *title,
477 uint32_t part, uint64_t pstart, uint64_t psize,
478 sd_id128_t part_uuid, const char *path) {
479 char boot_id[9];
0974a682
KS
480 size_t size;
481 size_t title_len;
482 size_t path_len;
483 struct boot_option *option;
484 struct device_path *devicep;
b7749eb5 485 _cleanup_free_ char *buf = NULL;
0974a682
KS
486
487 title_len = (strlen(title)+1) * 2;
488 path_len = (strlen(path)+1) * 2;
489
490 buf = calloc(sizeof(struct boot_option) + title_len +
491 sizeof(struct drive_path) +
492 sizeof(struct device_path) + path_len, 1);
b7749eb5
ZJS
493 if (!buf)
494 return -ENOMEM;
0974a682
KS
495
496 /* header */
497 option = (struct boot_option *)buf;
498 option->attr = LOAD_OPTION_ACTIVE;
499 option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
500 offsetof(struct device_path, path) + path_len +
501 offsetof(struct device_path, path);
502 to_utf16(option->title, title);
503 size = offsetof(struct boot_option, title) + title_len;
504
505 /* partition info */
506 devicep = (struct device_path *)(buf + size);
507 devicep->type = MEDIA_DEVICE_PATH;
508 devicep->sub_type = MEDIA_HARDDRIVE_DP;
509 devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
510 devicep->drive.part_nr = part;
511 devicep->drive.part_start = pstart;
920b52e4 512 devicep->drive.part_size = psize;
0974a682
KS
513 devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
514 devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
515 id128_to_efi_guid(part_uuid, devicep->drive.signature);
516 size += devicep->length;
517
518 /* path to loader */
519 devicep = (struct device_path *)(buf + size);
520 devicep->type = MEDIA_DEVICE_PATH;
521 devicep->sub_type = MEDIA_FILEPATH_DP;
522 devicep->length = offsetof(struct device_path, path) + path_len;
523 to_utf16(devicep->path, path);
524 tilt_slashes(devicep->path);
525 size += devicep->length;
526
527 /* end of path */
528 devicep = (struct device_path *)(buf + size);
529 devicep->type = END_DEVICE_PATH_TYPE;
530 devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
531 devicep->length = offsetof(struct device_path, path);
532 size += devicep->length;
533
b7749eb5
ZJS
534 xsprintf(boot_id, "Boot%04X", id);
535 return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
0974a682
KS
536}
537
538int efi_remove_boot_option(uint16_t id) {
539 char boot_id[9];
540
b7749eb5 541 xsprintf(boot_id, "Boot%04X", id);
0974a682
KS
542 return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
543}
544
9cde64ff 545int efi_get_boot_order(uint16_t **order) {
0797f232 546 _cleanup_free_ void *buf = NULL;
7b4d7cc0 547 size_t l;
9cde64ff 548 int r;
7b4d7cc0 549
9cde64ff
LP
550 r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
551 if (r < 0)
552 return r;
7b4d7cc0 553
0797f232 554 if (l <= 0)
7b4d7cc0 555 return -ENOENT;
7b4d7cc0 556
0797f232
ZJS
557 if (l % sizeof(uint16_t) > 0 ||
558 l / sizeof(uint16_t) > INT_MAX)
7b4d7cc0 559 return -EINVAL;
7b4d7cc0 560
ae2a15bc 561 *order = TAKE_PTR(buf);
9cde64ff
LP
562 return (int) (l / sizeof(uint16_t));
563}
564
0974a682
KS
565int efi_set_boot_order(uint16_t *order, size_t n) {
566 return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
567}
568
4d34c495
KS
569static int boot_id_hex(const char s[4]) {
570 int i;
571 int id = 0;
572
573 for (i = 0; i < 4; i++)
574 if (s[i] >= '0' && s[i] <= '9')
575 id |= (s[i] - '0') << (3 - i) * 4;
576 else if (s[i] >= 'A' && s[i] <= 'F')
577 id |= (s[i] - 'A' + 10) << (3 - i) * 4;
578 else
7e8185ef 579 return -EINVAL;
4d34c495
KS
580
581 return id;
582}
583
9db11a99
LP
584static int cmp_uint16(const void *_a, const void *_b) {
585 const uint16_t *a = _a, *b = _b;
586
02a6fc3e 587 return (int)*a - (int)*b;
9db11a99
LP
588}
589
9cde64ff
LP
590int efi_get_boot_options(uint16_t **options) {
591 _cleanup_closedir_ DIR *dir = NULL;
592 struct dirent *de;
b7749eb5 593 _cleanup_free_ uint16_t *list = NULL;
7432b24b 594 size_t alloc = 0;
b7749eb5 595 int count = 0;
9cde64ff
LP
596
597 assert(options);
598
599 dir = opendir("/sys/firmware/efi/efivars/");
600 if (!dir)
601 return -errno;
602
b7749eb5 603 FOREACH_DIRENT(de, dir, return -errno) {
4d34c495 604 int id;
9cde64ff
LP
605
606 if (strncmp(de->d_name, "Boot", 4) != 0)
607 continue;
608
4d34c495 609 if (strlen(de->d_name) != 45)
9cde64ff
LP
610 continue;
611
612 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
613 continue;
614
4d34c495
KS
615 id = boot_id_hex(de->d_name + 4);
616 if (id < 0)
9cde64ff
LP
617 continue;
618
7432b24b 619 if (!GREEDY_REALLOC(list, alloc, count + 1))
b7749eb5 620 return -ENOMEM;
9cde64ff 621
7432b24b 622 list[count++] = id;
9cde64ff
LP
623 }
624
7ff7394d 625 qsort_safe(list, count, sizeof(uint16_t), cmp_uint16);
9db11a99 626
1cc6c93a
YW
627 *options = TAKE_PTR(list);
628
9cde64ff 629 return count;
7b4d7cc0
KS
630}
631
5dbe9f53 632static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
2e3d0692 633 _cleanup_free_ char *j = NULL;
2e3d0692 634 int r;
39883f62 635 uint64_t x = 0;
2e3d0692
LP
636
637 assert(name);
638 assert(u);
639
61cc634b 640 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
2e3d0692
LP
641 if (r < 0)
642 return r;
643
2e3d0692
LP
644 r = safe_atou64(j, &x);
645 if (r < 0)
646 return r;
647
5dbe9f53 648 *u = x;
2e3d0692
LP
649 return 0;
650}
651
c51d84dc 652int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
2e3d0692
LP
653 uint64_t x, y;
654 int r;
2e3d0692
LP
655
656 assert(firmware);
657 assert(loader);
658
e9cea16d 659 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
2e3d0692
LP
660 if (r < 0)
661 return r;
662
e9cea16d 663 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
2e3d0692
LP
664 if (r < 0)
665 return r;
666
667 if (y == 0 || y < x)
668 return -EIO;
669
670 if (y > USEC_PER_HOUR)
671 return -EIO;
672
673 *firmware = x;
674 *loader = y;
675
676 return 0;
677}
678
c51d84dc 679int efi_loader_get_device_part_uuid(sd_id128_t *u) {
f4ce2b3e 680 _cleanup_free_ char *p = NULL;
f4ce2b3e 681 int r, parsed[16];
f4ce2b3e 682
61cc634b 683 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
f4ce2b3e
LP
684 if (r < 0)
685 return r;
686
f4ce2b3e
LP
687 if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
688 &parsed[0], &parsed[1], &parsed[2], &parsed[3],
689 &parsed[4], &parsed[5], &parsed[6], &parsed[7],
690 &parsed[8], &parsed[9], &parsed[10], &parsed[11],
691 &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
692 return -EIO;
693
73b80ec2
LP
694 if (u) {
695 unsigned i;
696
697 for (i = 0; i < ELEMENTSOF(parsed); i++)
698 u->bytes[i] = parsed[i];
699 }
f4ce2b3e
LP
700
701 return 0;
702}
b872e9a0
LP
703
704#endif
b28ce7c6
TG
705
706char *efi_tilt_backslashes(char *s) {
707 char *p;
708
709 for (p = s; *p; p++)
710 if (*p == '\\')
711 *p = '/';
712
713 return s;
714}