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