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