1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
27 #include "acpi-fpdt.h"
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
47 } __attribute__((packed));
55 uint8_t signature_type;
56 } __attribute__((packed));
64 struct drive_path drive;
66 } __attribute__((packed));
68 bool is_efi_boot(void) {
69 return access("/sys/firmware/efi", F_OK) >= 0;
72 static int read_flag(const char *varname) {
74 _cleanup_free_ void *v = NULL;
78 r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
90 int is_efi_secure_boot(void) {
91 return read_flag("SecureBoot");
94 int is_efi_secure_boot_setup_mode(void) {
95 return read_flag("SetupMode");
105 _cleanup_close_ int fd = -1;
106 _cleanup_free_ char *p = NULL;
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)
121 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
125 if (fstat(fd, &st) < 0)
129 if (st.st_size > 4*1024*1024 + 4)
132 n = read(fd, &a, sizeof(a));
138 r = malloc(st.st_size - 4 + 2);
142 n = read(fd, r, (size_t) st.st_size - 4);
147 if (n != (ssize_t) st.st_size - 4) {
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;
157 *size = (size_t) st.st_size - 4;
165 int efi_set_variable(
174 } __attribute__((packed)) *buf = NULL;
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)
191 fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
197 buf = malloc(sizeof(uint32_t) + size);
203 buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
204 memcpy(buf->buf, value, size);
206 r = write(fd, buf, sizeof(uint32_t) + size);
212 if ((size_t)r != sizeof(uint32_t) + size) {
225 int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
226 _cleanup_free_ void *s = NULL;
231 r = efi_get_variable(vendor, name, NULL, &s, &ss);
235 x = utf16_to_utf8(s, ss);
243 static size_t utf16_size(const uint16_t *s) {
249 return (l+1) * sizeof(uint16_t);
252 static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
259 const struct uuid *uuid = guid;
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));
272 int efi_get_boot_option(
275 sd_id128_t *part_uuid,
290 uint8_t signature_type;
299 struct drive_path drive;
304 _cleanup_free_ uint8_t *buf = NULL;
306 struct boot_option *header;
310 sd_id128_t p_uuid = SD_ID128_NULL;
313 snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
314 err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
317 if (l < sizeof(struct boot_option))
320 header = (struct boot_option *)buf;
321 title_size = utf16_size(header->title);
322 if (title_size > l - offsetof(struct boot_option, title))
326 s = utf16_to_utf8(header->title, title_size);
333 if (header->path_len > 0) {
337 dbuf = buf + offsetof(struct boot_option, title) + title_size;
339 while (dnext < header->path_len) {
340 struct device_path *dpath;
342 dpath = (struct device_path *)(dbuf + dnext);
343 if (dpath->length < 4)
346 /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
347 if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
350 dnext += dpath->length;
352 /* Type 0x04 – Media Device Path */
353 if (dpath->type != MEDIA_DEVICE_PATH)
356 /* Sub-Type 1 – Hard Drive */
357 if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
358 /* 0x02 – GUID Partition Table */
359 if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
362 /* 0x02 – GUID signature */
363 if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
367 efi_guid_to_id128(dpath->drive.signature, &p_uuid);
371 /* Sub-Type 4 – File Path */
372 if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
373 p = utf16_to_utf8(dpath->path, dpath->length-4);
374 efi_tilt_backslashes(p);
387 *active = !!header->attr & LOAD_OPTION_ACTIVE;
396 static void to_utf16(uint16_t *dest, const char *src) {
399 for (i = 0; src[i] != '\0'; i++)
409 } __attribute__((packed));
411 static void id128_to_efi_guid(sd_id128_t id, void *guid) {
412 struct guid *uuid = guid;
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));
420 static uint16_t *tilt_slashes(uint16_t *s) {
430 char *efi_tilt_backslashes(char *s) {
440 int 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) {
448 struct boot_option *option;
449 struct device_path *devicep;
452 title_len = (strlen(title)+1) * 2;
453 path_len = (strlen(path)+1) * 2;
455 buf = calloc(sizeof(struct boot_option) + title_len +
456 sizeof(struct drive_path) +
457 sizeof(struct device_path) + path_len, 1);
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;
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;
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;
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;
501 snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
502 err = efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
509 int efi_remove_boot_option(uint16_t id) {
512 snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
513 return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
516 int efi_get_boot_order(uint16_t **order) {
521 r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
530 if ((l % sizeof(uint16_t) > 0) ||
531 (l / sizeof(uint16_t) > INT_MAX)) {
537 return (int) (l / sizeof(uint16_t));
540 int efi_set_boot_order(uint16_t *order, size_t n) {
541 return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
544 static int boot_id_hex(const char s[4]) {
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;
559 static int cmp_uint16(const void *_a, const void *_b) {
560 const uint16_t *a = _a, *b = _b;
562 return (int)*a - (int)*b;
565 int efi_get_boot_options(uint16_t **options) {
566 _cleanup_closedir_ DIR *dir = NULL;
568 uint16_t *list = NULL;
573 dir = opendir("/sys/firmware/efi/efivars/");
577 FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
581 if (strncmp(de->d_name, "Boot", 4) != 0)
584 if (strlen(de->d_name) != 45)
587 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
590 id = boot_id_hex(de->d_name + 4);
594 t = realloc(list, (count + 1) * sizeof(uint16_t));
604 qsort_safe(list, count, sizeof(uint16_t), cmp_uint16);
614 static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
615 _cleanup_free_ char *j = NULL;
622 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
626 r = safe_atou64(j, &x);
634 int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
641 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
645 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
652 if (y > USEC_PER_HOUR)
661 int efi_loader_get_device_part_uuid(sd_id128_t *u) {
662 _cleanup_free_ char *p = NULL;
665 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
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)
679 for (i = 0; i < ELEMENTSOF(parsed); i++)
680 u->bytes[i] = parsed[i];