1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "alloc-util.h"
6 #include "dirent-util.h"
7 #include "efi-api.h"
8 #include "efivars.h"
9 #include "fd-util.h"
10 #include "sort-util.h"
11 #include "stat-util.h"
12 #include "stdio-util.h"
13 #include "utf8.h"
14
15 #if ENABLE_EFI
16
17 #define LOAD_OPTION_ACTIVE 0x00000001
18 #define MEDIA_DEVICE_PATH 0x04
19 #define MEDIA_HARDDRIVE_DP 0x01
20 #define MEDIA_FILEPATH_DP 0x04
21 #define SIGNATURE_TYPE_GUID 0x02
22 #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
23 #define END_DEVICE_PATH_TYPE 0x7f
24 #define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
25
26 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI UINT64_C(0x0000000000000001)
27
28 #define boot_option__contents \
29 { \
30 uint32_t attr; \
31 uint16_t path_len; \
32 uint16_t title[]; \
33 }
34
35 struct boot_option boot_option__contents;
36 struct boot_option__packed boot_option__contents _packed_;
37 assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title));
38 /* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so
39 * the *size* of the structure should not be used anywhere below. */
40
41 struct drive_path {
42 uint32_t part_nr;
43 uint64_t part_start;
44 uint64_t part_size;
45 char signature[16];
46 uint8_t mbr_type;
47 uint8_t signature_type;
48 } _packed_;
49
50 #define device_path__contents \
51 { \
52 uint8_t type; \
53 uint8_t sub_type; \
54 uint16_t length; \
55 union { \
56 uint16_t path[0]; \
57 struct drive_path drive; \
58 }; \
59 }
60
61 struct device_path device_path__contents;
62 struct device_path__packed device_path__contents _packed_;
63 assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
64
efi_reboot_to_firmware_supported(void)65 int efi_reboot_to_firmware_supported(void) {
66 _cleanup_free_ void *v = NULL;
67 static int cache = -1;
68 uint64_t b;
69 size_t s;
70 int r;
71
72 if (cache > 0)
73 return 0;
74 if (cache == 0)
75 return -EOPNOTSUPP;
76
77 if (!is_efi_boot())
78 goto not_supported;
79
80 r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndicationsSupported), NULL, &v, &s);
81 if (r == -ENOENT)
82 goto not_supported; /* variable doesn't exist? it's not supported then */
83 if (r < 0)
84 return r;
85 if (s != sizeof(uint64_t))
86 return -EINVAL;
87
88 b = *(uint64_t*) v;
89 if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
90 goto not_supported; /* bit unset? it's not supported then */
91
92 cache = 1;
93 return 0;
94
95 not_supported:
96 cache = 0;
97 return -EOPNOTSUPP;
98 }
99
get_os_indications(uint64_t * ret)100 static int get_os_indications(uint64_t *ret) {
101 static struct stat cache_stat = {};
102 _cleanup_free_ void *v = NULL;
103 static uint64_t cache;
104 struct stat new_stat;
105 size_t s;
106 int r;
107
108 assert(ret);
109
110 /* Let's verify general support first */
111 r = efi_reboot_to_firmware_supported();
112 if (r < 0)
113 return r;
114
115 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
116 if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE(OsIndications)), &new_stat) < 0) {
117 if (errno != ENOENT)
118 return -errno;
119
120 /* Doesn't exist? Then we can exit early (also see below) */
121 *ret = 0;
122 return 0;
123
124 } else if (stat_inode_unmodified(&new_stat, &cache_stat)) {
125 /* inode didn't change, we can return the cached value */
126 *ret = cache;
127 return 0;
128 }
129
130 r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndications), NULL, &v, &s);
131 if (r == -ENOENT) {
132 /* Some firmware implementations that do support OsIndications and report that with
133 * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
134 * pretend it's 0 then, to hide this implementation detail. Note that this call will return
135 * -ENOENT then only if the support for OsIndications is missing entirely, as determined by
136 * efi_reboot_to_firmware_supported() above. */
137 *ret = 0;
138 return 0;
139 }
140 if (r < 0)
141 return r;
142 if (s != sizeof(uint64_t))
143 return -EINVAL;
144
145 cache_stat = new_stat;
146 *ret = cache = *(uint64_t *)v;
147 return 0;
148 }
149
efi_get_reboot_to_firmware(void)150 int efi_get_reboot_to_firmware(void) {
151 int r;
152 uint64_t b;
153
154 r = get_os_indications(&b);
155 if (r < 0)
156 return r;
157
158 return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
159 }
160
efi_set_reboot_to_firmware(bool value)161 int efi_set_reboot_to_firmware(bool value) {
162 int r;
163 uint64_t b, b_new;
164
165 r = get_os_indications(&b);
166 if (r < 0)
167 return r;
168
169 b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value);
170
171 /* Avoid writing to efi vars store if we can due to firmware bugs. */
172 if (b != b_new)
173 return efi_set_variable(EFI_GLOBAL_VARIABLE(OsIndications), &b_new, sizeof(uint64_t));
174
175 return 0;
176 }
177
utf16_size(const uint16_t * s,size_t buf_len_bytes)178 static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
179 size_t l = 0;
180
181 /* Returns the size of the string in bytes without the terminating two zero bytes */
182
183 if (buf_len_bytes % sizeof(uint16_t) != 0)
184 return -EINVAL;
185
186 while (l < buf_len_bytes / sizeof(uint16_t)) {
187 if (s[l] == 0)
188 return (l + 1) * sizeof(uint16_t);
189 l++;
190 }
191
192 return -EINVAL; /* The terminator was not found */
193 }
194
195 struct guid {
196 uint32_t u1;
197 uint16_t u2;
198 uint16_t u3;
199 uint8_t u4[8];
200 } _packed_;
201
efi_guid_to_id128(const void * guid,sd_id128_t * id128)202 static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
203 uint32_t u1;
204 uint16_t u2, u3;
205 const struct guid *uuid = guid;
206
207 memcpy(&u1, &uuid->u1, sizeof(uint32_t));
208 id128->bytes[0] = (u1 >> 24) & 0xff;
209 id128->bytes[1] = (u1 >> 16) & 0xff;
210 id128->bytes[2] = (u1 >> 8) & 0xff;
211 id128->bytes[3] = u1 & 0xff;
212 memcpy(&u2, &uuid->u2, sizeof(uint16_t));
213 id128->bytes[4] = (u2 >> 8) & 0xff;
214 id128->bytes[5] = u2 & 0xff;
215 memcpy(&u3, &uuid->u3, sizeof(uint16_t));
216 id128->bytes[6] = (u3 >> 8) & 0xff;
217 id128->bytes[7] = u3 & 0xff;
218 memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
219 }
220
efi_get_boot_option(uint16_t id,char ** ret_title,sd_id128_t * ret_part_uuid,char ** ret_path,bool * ret_active)221 int efi_get_boot_option(
222 uint16_t id,
223 char **ret_title,
224 sd_id128_t *ret_part_uuid,
225 char **ret_path,
226 bool *ret_active) {
227
228 char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
229 _cleanup_free_ uint8_t *buf = NULL;
230 size_t l;
231 struct boot_option *header;
232 ssize_t title_size;
233 _cleanup_free_ char *s = NULL, *p = NULL;
234 sd_id128_t p_uuid = SD_ID128_NULL;
235 int r;
236
237 if (!is_efi_boot())
238 return -EOPNOTSUPP;
239
240 xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
241 r = efi_get_variable(variable, NULL, (void **)&buf, &l);
242 if (r < 0)
243 return r;
244 if (l < offsetof(struct boot_option, title))
245 return -ENOENT;
246
247 header = (struct boot_option *)buf;
248 title_size = utf16_size(header->title, l - offsetof(struct boot_option, title));
249 if (title_size < 0)
250 return title_size;
251
252 if (ret_title) {
253 s = utf16_to_utf8(header->title, title_size);
254 if (!s)
255 return -ENOMEM;
256 }
257
258 if (header->path_len > 0) {
259 uint8_t *dbuf;
260 size_t dnext, doff;
261
262 doff = offsetof(struct boot_option, title) + title_size;
263 dbuf = buf + doff;
264 if (header->path_len > l - doff)
265 return -EINVAL;
266
267 dnext = 0;
268 while (dnext < header->path_len) {
269 struct device_path *dpath;
270
271 dpath = (struct device_path *)(dbuf + dnext);
272 if (dpath->length < 4)
273 break;
274
275 /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
276 if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
277 break;
278
279 dnext += dpath->length;
280
281 /* Type 0x04 – Media Device Path */
282 if (dpath->type != MEDIA_DEVICE_PATH)
283 continue;
284
285 /* Sub-Type 1 – Hard Drive */
286 if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
287 /* 0x02 – GUID Partition Table */
288 if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
289 continue;
290
291 /* 0x02 – GUID signature */
292 if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
293 continue;
294
295 if (ret_part_uuid)
296 efi_guid_to_id128(dpath->drive.signature, &p_uuid);
297 continue;
298 }
299
300 /* Sub-Type 4 – File Path */
301 if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && ret_path) {
302 p = utf16_to_utf8(dpath->path, dpath->length-4);
303 if (!p)
304 return -ENOMEM;
305
306 efi_tilt_backslashes(p);
307 continue;
308 }
309 }
310 }
311
312 if (ret_title)
313 *ret_title = TAKE_PTR(s);
314 if (ret_part_uuid)
315 *ret_part_uuid = p_uuid;
316 if (ret_path)
317 *ret_path = TAKE_PTR(p);
318 if (ret_active)
319 *ret_active = header->attr & LOAD_OPTION_ACTIVE;
320
321 return 0;
322 }
323
to_utf16(uint16_t * dest,const char * src)324 static void to_utf16(uint16_t *dest, const char *src) {
325 int i;
326
327 for (i = 0; src[i] != '\0'; i++)
328 dest[i] = src[i];
329 dest[i] = '\0';
330 }
331
id128_to_efi_guid(sd_id128_t id,void * guid)332 static void id128_to_efi_guid(sd_id128_t id, void *guid) {
333 struct guid uuid = {
334 .u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3],
335 .u2 = id.bytes[4] << 8 | id.bytes[5],
336 .u3 = id.bytes[6] << 8 | id.bytes[7],
337 };
338 memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4));
339 memcpy(guid, &uuid, sizeof(uuid));
340 }
341
tilt_slashes(uint16_t * s)342 static uint16_t *tilt_slashes(uint16_t *s) {
343 for (uint16_t *p = s; *p; p++)
344 if (*p == '/')
345 *p = '\\';
346
347 return s;
348 }
349
efi_add_boot_option(uint16_t id,const char * title,uint32_t part,uint64_t pstart,uint64_t psize,sd_id128_t part_uuid,const char * path)350 int efi_add_boot_option(
351 uint16_t id,
352 const char *title,
353 uint32_t part,
354 uint64_t pstart,
355 uint64_t psize,
356 sd_id128_t part_uuid,
357 const char *path) {
358
359 size_t size, title_len, path_len;
360 _cleanup_free_ char *buf = NULL;
361 struct boot_option *option;
362 struct device_path *devicep;
363 char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
364
365 if (!is_efi_boot())
366 return -EOPNOTSUPP;
367
368 title_len = (strlen(title)+1) * 2;
369 path_len = (strlen(path)+1) * 2;
370
371 buf = malloc0(offsetof(struct boot_option, title) + title_len +
372 sizeof(struct drive_path) +
373 sizeof(struct device_path) + path_len);
374 if (!buf)
375 return -ENOMEM;
376
377 /* header */
378 option = (struct boot_option *)buf;
379 option->attr = LOAD_OPTION_ACTIVE;
380 option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
381 offsetof(struct device_path, path) + path_len +
382 offsetof(struct device_path, path);
383 to_utf16(option->title, title);
384 size = offsetof(struct boot_option, title) + title_len;
385
386 /* partition info */
387 devicep = (struct device_path *)(buf + size);
388 devicep->type = MEDIA_DEVICE_PATH;
389 devicep->sub_type = MEDIA_HARDDRIVE_DP;
390 devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
391 memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t));
392 memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t));
393 memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t));
394 id128_to_efi_guid(part_uuid, devicep->drive.signature);
395 devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
396 devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
397 size += devicep->length;
398
399 /* path to loader */
400 devicep = (struct device_path *)(buf + size);
401 devicep->type = MEDIA_DEVICE_PATH;
402 devicep->sub_type = MEDIA_FILEPATH_DP;
403 devicep->length = offsetof(struct device_path, path) + path_len;
404 to_utf16(devicep->path, path);
405 tilt_slashes(devicep->path);
406 size += devicep->length;
407
408 /* end of path */
409 devicep = (struct device_path *)(buf + size);
410 devicep->type = END_DEVICE_PATH_TYPE;
411 devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
412 devicep->length = offsetof(struct device_path, path);
413 size += devicep->length;
414
415 xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
416 return efi_set_variable(variable, buf, size);
417 }
418
efi_remove_boot_option(uint16_t id)419 int efi_remove_boot_option(uint16_t id) {
420 char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
421
422 if (!is_efi_boot())
423 return -EOPNOTSUPP;
424
425 xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
426 return efi_set_variable(variable, NULL, 0);
427 }
428
efi_get_boot_order(uint16_t ** ret_order)429 int efi_get_boot_order(uint16_t **ret_order) {
430 _cleanup_free_ void *buf = NULL;
431 size_t l;
432 int r;
433
434 assert(ret_order);
435
436 if (!is_efi_boot())
437 return -EOPNOTSUPP;
438
439 r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l);
440 if (r < 0)
441 return r;
442
443 if (l <= 0)
444 return -ENOENT;
445
446 if (l % sizeof(uint16_t) > 0 ||
447 l / sizeof(uint16_t) > INT_MAX)
448 return -EINVAL;
449
450 *ret_order = TAKE_PTR(buf);
451 return (int) (l / sizeof(uint16_t));
452 }
453
efi_set_boot_order(const uint16_t * order,size_t n)454 int efi_set_boot_order(const uint16_t *order, size_t n) {
455
456 if (!is_efi_boot())
457 return -EOPNOTSUPP;
458
459 return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t));
460 }
461
boot_id_hex(const char s[static4])462 static int boot_id_hex(const char s[static 4]) {
463 int id = 0;
464
465 assert(s);
466
467 for (int i = 0; i < 4; i++)
468 if (s[i] >= '0' && s[i] <= '9')
469 id |= (s[i] - '0') << (3 - i) * 4;
470 else if (s[i] >= 'A' && s[i] <= 'F')
471 id |= (s[i] - 'A' + 10) << (3 - i) * 4;
472 else
473 return -EINVAL;
474
475 return id;
476 }
477
cmp_uint16(const uint16_t * a,const uint16_t * b)478 static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
479 return CMP(*a, *b);
480 }
481
efi_get_boot_options(uint16_t ** ret_options)482 int efi_get_boot_options(uint16_t **ret_options) {
483 _cleanup_closedir_ DIR *dir = NULL;
484 _cleanup_free_ uint16_t *list = NULL;
485 int count = 0;
486
487 assert(ret_options);
488
489 if (!is_efi_boot())
490 return -EOPNOTSUPP;
491
492 dir = opendir(EFIVAR_PATH("."));
493 if (!dir)
494 return -errno;
495
496 FOREACH_DIRENT(de, dir, return -errno) {
497 int id;
498
499 if (strncmp(de->d_name, "Boot", 4) != 0)
500 continue;
501
502 if (strlen(de->d_name) != 45)
503 continue;
504
505 if (strcmp(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR("")) != 0) /* generate variable suffix using macro */
506 continue;
507
508 id = boot_id_hex(de->d_name + 4);
509 if (id < 0)
510 continue;
511
512 if (!GREEDY_REALLOC(list, count + 1))
513 return -ENOMEM;
514
515 list[count++] = id;
516 }
517
518 typesafe_qsort(list, count, cmp_uint16);
519
520 *ret_options = TAKE_PTR(list);
521
522 return count;
523 }
524
efi_has_tpm2(void)525 bool efi_has_tpm2(void) {
526 static int cache = -1;
527
528 /* Returns whether the system has a TPM2 chip which is known to the EFI firmware. */
529
530 if (cache < 0) {
531
532 /* First, check if we are on an EFI boot at all. */
533 if (!is_efi_boot())
534 cache = false;
535 else {
536 /* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see:
537 * https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf
538 * This table exists whenever the firmware is hooked up to TPM2. */
539 cache = access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0;
540 if (!cache && errno != ENOENT)
541 log_debug_errno(errno, "Unable to test whether /sys/firmware/acpi/tables/TPM2 exists, assuming it doesn't: %m");
542 }
543 }
544
545 return cache;
546 }
547
548 #endif
549