]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/efi-api.c
hwdb: Add mapping for Xiaomi Mipad 2 bottom bezel capacitive buttons
[thirdparty/systemd.git] / src / shared / efi-api.c
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
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
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
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
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
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 while (l < buf_len_bytes / sizeof(uint16_t)) {
184 if (s[l] == 0)
185 return (l + 1) * sizeof(uint16_t);
186 l++;
187 }
188
189 return -EINVAL; /* The terminator was not found */
190 }
191
192 int efi_get_boot_option(
193 uint16_t id,
194 char **ret_title,
195 sd_id128_t *ret_part_uuid,
196 char **ret_path,
197 bool *ret_active) {
198
199 char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
200 _cleanup_free_ uint8_t *buf = NULL;
201 size_t l;
202 struct boot_option *header;
203 ssize_t title_size;
204 _cleanup_free_ char *s = NULL, *p = NULL;
205 sd_id128_t p_uuid = SD_ID128_NULL;
206 int r;
207
208 if (!is_efi_boot())
209 return -EOPNOTSUPP;
210
211 xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
212 r = efi_get_variable(variable, NULL, (void **)&buf, &l);
213 if (r < 0)
214 return r;
215 if (l < offsetof(struct boot_option, title))
216 return -ENOENT;
217
218 header = (struct boot_option *)buf;
219 title_size = utf16_size(header->title, l - offsetof(struct boot_option, title));
220 if (title_size < 0)
221 return title_size;
222
223 if (ret_title) {
224 s = utf16_to_utf8(header->title, title_size);
225 if (!s)
226 return -ENOMEM;
227 }
228
229 if (header->path_len > 0) {
230 uint8_t *dbuf;
231 size_t dnext, doff;
232
233 doff = offsetof(struct boot_option, title) + title_size;
234 dbuf = buf + doff;
235 if (header->path_len > l - doff)
236 return -EINVAL;
237
238 dnext = 0;
239 while (dnext < header->path_len) {
240 struct device_path *dpath;
241
242 dpath = (struct device_path *)(dbuf + dnext);
243 if (dpath->length < 4)
244 break;
245
246 /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
247 if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
248 break;
249
250 dnext += dpath->length;
251
252 /* Type 0x04 – Media Device Path */
253 if (dpath->type != MEDIA_DEVICE_PATH)
254 continue;
255
256 /* Sub-Type 1 – Hard Drive */
257 if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
258 /* 0x02 – GUID Partition Table */
259 if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
260 continue;
261
262 /* 0x02 – GUID signature */
263 if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
264 continue;
265
266 if (ret_part_uuid)
267 p_uuid = efi_guid_to_id128(dpath->drive.signature);
268 continue;
269 }
270
271 /* Sub-Type 4 – File Path */
272 if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && ret_path) {
273 p = utf16_to_utf8(dpath->path, dpath->length-4);
274 if (!p)
275 return -ENOMEM;
276
277 efi_tilt_backslashes(p);
278 continue;
279 }
280 }
281 }
282
283 if (ret_title)
284 *ret_title = TAKE_PTR(s);
285 if (ret_part_uuid)
286 *ret_part_uuid = p_uuid;
287 if (ret_path)
288 *ret_path = TAKE_PTR(p);
289 if (ret_active)
290 *ret_active = header->attr & LOAD_OPTION_ACTIVE;
291
292 return 0;
293 }
294
295 static void to_utf16(uint16_t *dest, const char *src) {
296 int i;
297
298 for (i = 0; src[i] != '\0'; i++)
299 dest[i] = src[i];
300 dest[i] = '\0';
301 }
302
303 static uint16_t *tilt_slashes(uint16_t *s) {
304 for (uint16_t *p = s; *p; p++)
305 if (*p == '/')
306 *p = '\\';
307
308 return s;
309 }
310
311 int efi_add_boot_option(
312 uint16_t id,
313 const char *title,
314 uint32_t part,
315 uint64_t pstart,
316 uint64_t psize,
317 sd_id128_t part_uuid,
318 const char *path) {
319
320 size_t size, title_len, path_len;
321 _cleanup_free_ char *buf = NULL;
322 struct boot_option *option;
323 struct device_path *devicep;
324 char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
325
326 if (!is_efi_boot())
327 return -EOPNOTSUPP;
328
329 title_len = (strlen(title)+1) * 2;
330 path_len = (strlen(path)+1) * 2;
331
332 buf = malloc0(offsetof(struct boot_option, title) + title_len +
333 sizeof(struct drive_path) +
334 sizeof(struct device_path) + path_len);
335 if (!buf)
336 return -ENOMEM;
337
338 /* header */
339 option = (struct boot_option *)buf;
340 option->attr = LOAD_OPTION_ACTIVE;
341 option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
342 offsetof(struct device_path, path) + path_len +
343 offsetof(struct device_path, path);
344 to_utf16(option->title, title);
345 size = offsetof(struct boot_option, title) + title_len;
346
347 /* partition info */
348 devicep = (struct device_path *)(buf + size);
349 devicep->type = MEDIA_DEVICE_PATH;
350 devicep->sub_type = MEDIA_HARDDRIVE_DP;
351 devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
352 memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t));
353 memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t));
354 memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t));
355 efi_id128_to_guid(part_uuid, devicep->drive.signature);
356 devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
357 devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
358 size += devicep->length;
359
360 /* path to loader */
361 devicep = (struct device_path *)(buf + size);
362 devicep->type = MEDIA_DEVICE_PATH;
363 devicep->sub_type = MEDIA_FILEPATH_DP;
364 devicep->length = offsetof(struct device_path, path) + path_len;
365 to_utf16(devicep->path, path);
366 tilt_slashes(devicep->path);
367 size += devicep->length;
368
369 /* end of path */
370 devicep = (struct device_path *)(buf + size);
371 devicep->type = END_DEVICE_PATH_TYPE;
372 devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
373 devicep->length = offsetof(struct device_path, path);
374 size += devicep->length;
375
376 xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
377 return efi_set_variable(variable, buf, size);
378 }
379
380 int efi_remove_boot_option(uint16_t id) {
381 char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
382
383 if (!is_efi_boot())
384 return -EOPNOTSUPP;
385
386 xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
387 return efi_set_variable(variable, NULL, 0);
388 }
389
390 int efi_get_boot_order(uint16_t **ret_order) {
391 _cleanup_free_ void *buf = NULL;
392 size_t l;
393 int r;
394
395 assert(ret_order);
396
397 if (!is_efi_boot())
398 return -EOPNOTSUPP;
399
400 r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l);
401 if (r < 0)
402 return r;
403
404 if (l <= 0)
405 return -ENOENT;
406
407 if (l % sizeof(uint16_t) > 0 ||
408 l / sizeof(uint16_t) > INT_MAX)
409 return -EINVAL;
410
411 *ret_order = TAKE_PTR(buf);
412 return (int) (l / sizeof(uint16_t));
413 }
414
415 int efi_set_boot_order(const uint16_t *order, size_t n) {
416
417 if (!is_efi_boot())
418 return -EOPNOTSUPP;
419
420 return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t));
421 }
422
423 static int boot_id_hex(const char s[static 4]) {
424 int id = 0;
425
426 assert(s);
427
428 for (int i = 0; i < 4; i++)
429 if (s[i] >= '0' && s[i] <= '9')
430 id |= (s[i] - '0') << (3 - i) * 4;
431 else if (s[i] >= 'A' && s[i] <= 'F')
432 id |= (s[i] - 'A' + 10) << (3 - i) * 4;
433 else
434 return -EINVAL;
435
436 return id;
437 }
438
439 int efi_get_boot_options(uint16_t **ret_options) {
440 _cleanup_closedir_ DIR *dir = NULL;
441 _cleanup_free_ uint16_t *list = NULL;
442 int count = 0;
443
444 assert(ret_options);
445
446 if (!is_efi_boot())
447 return -EOPNOTSUPP;
448
449 dir = opendir(EFIVAR_PATH("."));
450 if (!dir)
451 return -errno;
452
453 FOREACH_DIRENT(de, dir, return -errno) {
454 int id;
455
456 if (!startswith(de->d_name, "Boot"))
457 continue;
458
459 if (strlen(de->d_name) != 45)
460 continue;
461
462 if (!streq(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR(""))) /* generate variable suffix using macro */
463 continue;
464
465 id = boot_id_hex(de->d_name + 4);
466 if (id < 0)
467 continue;
468
469 if (!GREEDY_REALLOC(list, count + 1))
470 return -ENOMEM;
471
472 list[count++] = id;
473 }
474
475 typesafe_qsort(list, count, cmp_uint16);
476
477 *ret_options = TAKE_PTR(list);
478
479 return count;
480 }
481
482 bool efi_has_tpm2(void) {
483 static int cache = -1;
484
485 /* Returns whether the system has a TPM2 chip which is known to the EFI firmware. */
486
487 if (cache >= 0)
488 return cache;
489
490 /* First, check if we are on an EFI boot at all. */
491 if (!is_efi_boot()) {
492 cache = 0;
493 return cache;
494 }
495
496 /* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see:
497 * https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf
498 * This table exists whenever the firmware is hooked up to TPM2. */
499 cache = access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0;
500 if (cache)
501 return cache;
502
503 if (errno != ENOENT)
504 log_debug_errno(errno, "Unable to test whether /sys/firmware/acpi/tables/TPM2 exists, assuming it doesn't: %m");
505
506 /* As the last try, check if the EFI firmware provides the EFI_TCG2_FINAL_EVENTS_TABLE
507 * stored in EFI configuration table, see:
508 * https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf
509 */
510 cache = access("/sys/kernel/security/tpm0/binary_bios_measurements", F_OK) >= 0;
511 if (!cache && errno != ENOENT)
512 log_debug_errno(errno, "Unable to test whether /sys/kernel/security/tpm0/binary_bios_measurements exists, assuming it doesn't: %m");
513
514 return cache;
515 }
516
517 #endif
518
519 struct efi_guid {
520 uint32_t u1;
521 uint16_t u2;
522 uint16_t u3;
523 uint8_t u4[8];
524 } _packed_;
525
526 sd_id128_t efi_guid_to_id128(const void *guid) {
527 const struct efi_guid *uuid = ASSERT_PTR(guid); /* cast is safe, because struct efi_guid is packed */
528 sd_id128_t id128;
529
530 id128.bytes[0] = (uuid->u1 >> 24) & 0xff;
531 id128.bytes[1] = (uuid->u1 >> 16) & 0xff;
532 id128.bytes[2] = (uuid->u1 >> 8) & 0xff;
533 id128.bytes[3] = uuid->u1 & 0xff;
534
535 id128.bytes[4] = (uuid->u2 >> 8) & 0xff;
536 id128.bytes[5] = uuid->u2 & 0xff;
537
538 id128.bytes[6] = (uuid->u3 >> 8) & 0xff;
539 id128.bytes[7] = uuid->u3 & 0xff;
540
541 memcpy(&id128.bytes[8], uuid->u4, sizeof(uuid->u4));
542
543 return id128;
544 }
545
546 void efi_id128_to_guid(sd_id128_t id, void *ret_guid) {
547 assert(ret_guid);
548
549 struct efi_guid uuid = {
550 .u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3],
551 .u2 = id.bytes[4] << 8 | id.bytes[5],
552 .u3 = id.bytes[6] << 8 | id.bytes[7],
553 };
554 memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4));
555 memcpy(ret_guid, &uuid, sizeof(uuid));
556 }