LCOV - code coverage report
Current view: top level - shared - efivars.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 14 479 2.9 %
Date: 2019-08-22 15:41:25 Functions: 4 33 12.1 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <dirent.h>
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <limits.h>
       7             : #include <linux/fs.h>
       8             : #include <stdio.h>
       9             : #include <stdlib.h>
      10             : #include <string.h>
      11             : #include <sys/stat.h>
      12             : #include <unistd.h>
      13             : 
      14             : #include "sd-id128.h"
      15             : 
      16             : #include "alloc-util.h"
      17             : #include "chattr-util.h"
      18             : #include "dirent-util.h"
      19             : #include "efivars.h"
      20             : #include "fd-util.h"
      21             : #include "io-util.h"
      22             : #include "macro.h"
      23             : #include "parse-util.h"
      24             : #include "sort-util.h"
      25             : #include "stdio-util.h"
      26             : #include "strv.h"
      27             : #include "time-util.h"
      28             : #include "utf8.h"
      29             : #include "virt.h"
      30             : 
      31             : #if ENABLE_EFI
      32             : 
      33             : #define LOAD_OPTION_ACTIVE            0x00000001
      34             : #define MEDIA_DEVICE_PATH                   0x04
      35             : #define MEDIA_HARDDRIVE_DP                  0x01
      36             : #define MEDIA_FILEPATH_DP                   0x04
      37             : #define SIGNATURE_TYPE_GUID                 0x02
      38             : #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
      39             : #define END_DEVICE_PATH_TYPE                0x7f
      40             : #define END_ENTIRE_DEVICE_PATH_SUBTYPE      0xff
      41             : #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI    0x0000000000000001
      42             : 
      43             : #define boot_option__contents {                 \
      44             :         uint32_t attr;                          \
      45             :         uint16_t path_len;                      \
      46             :         uint16_t title[];                       \
      47             :         }
      48             : 
      49             : struct boot_option boot_option__contents;
      50             : struct boot_option__packed boot_option__contents _packed_;
      51             : assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title));
      52             : /* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so
      53             :  * the *size* of the structure should not be used anywhere below. */
      54             : 
      55             : struct drive_path {
      56             :         uint32_t part_nr;
      57             :         uint64_t part_start;
      58             :         uint64_t part_size;
      59             :         char signature[16];
      60             :         uint8_t mbr_type;
      61             :         uint8_t signature_type;
      62             : } _packed_;
      63             : 
      64             : #define device_path__contents {                 \
      65             :         uint8_t type;                           \
      66             :         uint8_t sub_type;                       \
      67             :         uint16_t length;                        \
      68             :         union {                                 \
      69             :                 uint16_t path[0];               \
      70             :                 struct drive_path drive;        \
      71             :         };                                      \
      72             :         }
      73             : 
      74             : struct device_path device_path__contents;
      75             : struct device_path__packed device_path__contents _packed_;
      76             : assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
      77             : 
      78           5 : bool is_efi_boot(void) {
      79           5 :         if (detect_container() > 0)
      80           0 :                 return false;
      81             : 
      82           5 :         return access("/sys/firmware/efi/", F_OK) >= 0;
      83             : }
      84             : 
      85           3 : static int read_flag(const char *varname) {
      86           3 :         _cleanup_free_ void *v = NULL;
      87             :         uint8_t b;
      88             :         size_t s;
      89             :         int r;
      90             : 
      91           3 :         if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
      92           3 :                 return 0;
      93             : 
      94           0 :         r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
      95           0 :         if (r < 0)
      96           0 :                 return r;
      97             : 
      98           0 :         if (s != 1)
      99           0 :                 return -EINVAL;
     100             : 
     101           0 :         b = *(uint8_t *)v;
     102           0 :         return !!b;
     103             : }
     104             : 
     105           3 : bool is_efi_secure_boot(void) {
     106           3 :         return read_flag("SecureBoot") > 0;
     107             : }
     108             : 
     109           0 : bool is_efi_secure_boot_setup_mode(void) {
     110           0 :         return read_flag("SetupMode") > 0;
     111             : }
     112             : 
     113           0 : int efi_reboot_to_firmware_supported(void) {
     114           0 :         _cleanup_free_ void *v = NULL;
     115             :         uint64_t b;
     116             :         size_t s;
     117             :         int r;
     118             : 
     119           0 :         if (!is_efi_boot())
     120           0 :                 return -EOPNOTSUPP;
     121             : 
     122           0 :         r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
     123           0 :         if (r == -ENOENT) /* variable doesn't exist? it's not supported then */
     124           0 :                 return -EOPNOTSUPP;
     125           0 :         if (r < 0)
     126           0 :                 return r;
     127           0 :         if (s != sizeof(uint64_t))
     128           0 :                 return -EINVAL;
     129             : 
     130           0 :         b = *(uint64_t*) v;
     131           0 :         if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
     132           0 :                 return -EOPNOTSUPP; /* bit unset? it's not supported then */
     133             : 
     134           0 :         return 0;
     135             : }
     136             : 
     137           0 : static int get_os_indications(uint64_t *os_indication) {
     138           0 :         _cleanup_free_ void *v = NULL;
     139             :         size_t s;
     140             :         int r;
     141             : 
     142             :         /* Let's verify general support first */
     143           0 :         r = efi_reboot_to_firmware_supported();
     144           0 :         if (r < 0)
     145           0 :                 return r;
     146             : 
     147           0 :         r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
     148           0 :         if (r == -ENOENT) {
     149             :                 /* Some firmware implementations that do support OsIndications and report that with
     150             :                  * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's pretend it's 0
     151             :                  * then, to hide this implementation detail. Note that this call will return -ENOENT then only if the
     152             :                  * support for OsIndications is missing entirely, as determined by efi_reboot_to_firmware_supported()
     153             :                  * above. */
     154           0 :                 *os_indication = 0;
     155           0 :                 return 0;
     156           0 :         } else if (r < 0)
     157           0 :                 return r;
     158           0 :         else if (s != sizeof(uint64_t))
     159           0 :                 return -EINVAL;
     160             : 
     161           0 :         *os_indication = *(uint64_t *)v;
     162           0 :         return 0;
     163             : }
     164             : 
     165           0 : int efi_get_reboot_to_firmware(void) {
     166             :         int r;
     167             :         uint64_t b;
     168             : 
     169           0 :         r = get_os_indications(&b);
     170           0 :         if (r < 0)
     171           0 :                 return r;
     172             : 
     173           0 :         return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
     174             : }
     175             : 
     176           0 : int efi_set_reboot_to_firmware(bool value) {
     177             :         int r;
     178             :         uint64_t b, b_new;
     179             : 
     180           0 :         r = get_os_indications(&b);
     181           0 :         if (r < 0)
     182           0 :                 return r;
     183             : 
     184           0 :         if (value)
     185           0 :                 b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
     186             :         else
     187           0 :                 b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
     188             : 
     189             :         /* Avoid writing to efi vars store if we can due to firmware bugs. */
     190           0 :         if (b != b_new)
     191           0 :                 return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
     192             : 
     193           0 :         return 0;
     194             : }
     195             : 
     196           0 : char* efi_variable_path(sd_id128_t vendor, const char *name) {
     197             :         char *p;
     198             : 
     199           0 :         if (asprintf(&p,
     200             :                      "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
     201           0 :                      name, SD_ID128_FORMAT_VAL(vendor)) < 0)
     202           0 :                 return NULL;
     203             : 
     204           0 :         return p;
     205             : }
     206             : 
     207           0 : int efi_get_variable(
     208             :                 sd_id128_t vendor,
     209             :                 const char *name,
     210             :                 uint32_t *ret_attribute,
     211             :                 void **ret_value,
     212             :                 size_t *ret_size) {
     213             : 
     214           0 :         _cleanup_close_ int fd = -1;
     215           0 :         _cleanup_free_ char *p = NULL;
     216           0 :         _cleanup_free_ void *buf = NULL;
     217             :         struct stat st;
     218             :         uint32_t a;
     219             :         ssize_t n;
     220             : 
     221           0 :         assert(name);
     222             : 
     223           0 :         p = efi_variable_path(vendor, name);
     224           0 :         if (!p)
     225           0 :                 return -ENOMEM;
     226             : 
     227           0 :         if (!ret_value && !ret_size && !ret_attribute) {
     228             :                 /* If caller is not interested in anything, just check if the variable exists and is readable
     229             :                  * to us. */
     230           0 :                 if (access(p, R_OK) < 0)
     231           0 :                         return -errno;
     232             : 
     233           0 :                 return 0;
     234             :         }
     235             : 
     236           0 :         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
     237           0 :         if (fd < 0)
     238           0 :                 return -errno;
     239             : 
     240           0 :         if (fstat(fd, &st) < 0)
     241           0 :                 return -errno;
     242           0 :         if (st.st_size < 4)
     243           0 :                 return -ENODATA;
     244           0 :         if (st.st_size > 4*1024*1024 + 4)
     245           0 :                 return -E2BIG;
     246             : 
     247           0 :         if (ret_value || ret_attribute) {
     248           0 :                 n = read(fd, &a, sizeof(a));
     249           0 :                 if (n < 0)
     250           0 :                         return -errno;
     251           0 :                 if (n != sizeof(a))
     252           0 :                         return -EIO;
     253             :         }
     254             : 
     255           0 :         if (ret_value) {
     256           0 :                 buf = malloc(st.st_size - 4 + 2);
     257           0 :                 if (!buf)
     258           0 :                         return -ENOMEM;
     259             : 
     260           0 :                 n = read(fd, buf, (size_t) st.st_size - 4);
     261           0 :                 if (n < 0)
     262           0 :                         return -errno;
     263           0 :                 if (n != st.st_size - 4)
     264           0 :                         return -EIO;
     265             : 
     266             :                 /* Always NUL terminate (2 bytes, to protect UTF-16) */
     267           0 :                 ((char*) buf)[st.st_size - 4] = 0;
     268           0 :                 ((char*) buf)[st.st_size - 4 + 1] = 0;
     269             :         }
     270             : 
     271             :         /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
     272             :          * with a smaller value. */
     273             : 
     274           0 :         if (ret_attribute)
     275           0 :                 *ret_attribute = a;
     276             : 
     277           0 :         if (ret_value)
     278           0 :                 *ret_value = TAKE_PTR(buf);
     279             : 
     280           0 :         if (ret_size)
     281           0 :                 *ret_size = (size_t) st.st_size - 4;
     282             : 
     283           0 :         return 0;
     284             : }
     285             : 
     286           0 : int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
     287           0 :         _cleanup_free_ void *s = NULL;
     288           0 :         size_t ss = 0;
     289             :         int r;
     290             :         char *x;
     291             : 
     292           0 :         r = efi_get_variable(vendor, name, NULL, &s, &ss);
     293           0 :         if (r < 0)
     294           0 :                 return r;
     295             : 
     296           0 :         x = utf16_to_utf8(s, ss);
     297           0 :         if (!x)
     298           0 :                 return -ENOMEM;
     299             : 
     300           0 :         *p = x;
     301           0 :         return 0;
     302             : }
     303             : 
     304           0 : int efi_set_variable(
     305             :                 sd_id128_t vendor,
     306             :                 const char *name,
     307             :                 const void *value,
     308             :                 size_t size) {
     309             : 
     310             :         struct var {
     311             :                 uint32_t attr;
     312             :                 char buf[];
     313           0 :         } _packed_ * _cleanup_free_ buf = NULL;
     314           0 :         _cleanup_free_ char *p = NULL;
     315           0 :         _cleanup_close_ int fd = -1;
     316           0 :         bool saved_flags_valid = false;
     317             :         unsigned saved_flags;
     318             :         int r;
     319             : 
     320           0 :         assert(name);
     321           0 :         assert(value || size == 0);
     322             : 
     323           0 :         p = efi_variable_path(vendor, name);
     324           0 :         if (!p)
     325           0 :                 return -ENOMEM;
     326             : 
     327             :         /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
     328             :          * them for accidental removal and modification. We are not changing these variables accidentally however,
     329             :          * hence let's unset the bit first. */
     330             : 
     331           0 :         r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
     332           0 :         if (r < 0 && r != -ENOENT)
     333           0 :                 log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
     334             : 
     335           0 :         saved_flags_valid = r >= 0;
     336             : 
     337           0 :         if (size == 0) {
     338           0 :                 if (unlink(p) < 0) {
     339           0 :                         r = -errno;
     340           0 :                         goto finish;
     341             :                 }
     342             : 
     343           0 :                 return 0;
     344             :         }
     345             : 
     346           0 :         fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
     347           0 :         if (fd < 0) {
     348           0 :                 r = -errno;
     349           0 :                 goto finish;
     350             :         }
     351             : 
     352           0 :         buf = malloc(sizeof(uint32_t) + size);
     353           0 :         if (!buf) {
     354           0 :                 r = -ENOMEM;
     355           0 :                 goto finish;
     356             :         }
     357             : 
     358           0 :         buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
     359           0 :         memcpy(buf->buf, value, size);
     360             : 
     361           0 :         r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
     362           0 :         if (r < 0)
     363           0 :                 goto finish;
     364             : 
     365           0 :         r = 0;
     366             : 
     367           0 : finish:
     368           0 :         if (saved_flags_valid) {
     369             :                 int q;
     370             : 
     371             :                 /* Restore the original flags field, just in case */
     372           0 :                 if (fd < 0)
     373           0 :                         q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
     374             :                 else
     375           0 :                         q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
     376           0 :                 if (q < 0)
     377           0 :                         log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
     378             :         }
     379             : 
     380           0 :         return r;
     381             : }
     382             : 
     383           0 : int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
     384           0 :         _cleanup_free_ char16_t *u16 = NULL;
     385             : 
     386           0 :         u16 = utf8_to_utf16(v, strlen(v));
     387           0 :         if (!u16)
     388           0 :                 return -ENOMEM;
     389             : 
     390           0 :         return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
     391             : }
     392             : 
     393           0 : static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
     394           0 :         size_t l = 0;
     395             : 
     396             :         /* Returns the size of the string in bytes without the terminating two zero bytes */
     397             : 
     398           0 :         if (buf_len_bytes % sizeof(uint16_t) != 0)
     399           0 :                 return -EINVAL;
     400             : 
     401           0 :         while (l < buf_len_bytes / sizeof(uint16_t)) {
     402           0 :                 if (s[l] == 0)
     403           0 :                         return (l + 1) * sizeof(uint16_t);
     404           0 :                 l++;
     405             :         }
     406             : 
     407           0 :         return -EINVAL; /* The terminator was not found */
     408             : }
     409             : 
     410             : struct guid {
     411             :         uint32_t u1;
     412             :         uint16_t u2;
     413             :         uint16_t u3;
     414             :         uint8_t u4[8];
     415             : } _packed_;
     416             : 
     417           0 : static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
     418             :         uint32_t u1;
     419             :         uint16_t u2, u3;
     420           0 :         const struct guid *uuid = guid;
     421             : 
     422           0 :         memcpy(&u1, &uuid->u1, sizeof(uint32_t));
     423           0 :         id128->bytes[0] = (u1 >> 24) & 0xff;
     424           0 :         id128->bytes[1] = (u1 >> 16) & 0xff;
     425           0 :         id128->bytes[2] = (u1 >> 8) & 0xff;
     426           0 :         id128->bytes[3] = u1 & 0xff;
     427           0 :         memcpy(&u2, &uuid->u2, sizeof(uint16_t));
     428           0 :         id128->bytes[4] = (u2 >> 8) & 0xff;
     429           0 :         id128->bytes[5] = u2 & 0xff;
     430           0 :         memcpy(&u3, &uuid->u3, sizeof(uint16_t));
     431           0 :         id128->bytes[6] = (u3 >> 8) & 0xff;
     432           0 :         id128->bytes[7] = u3 & 0xff;
     433           0 :         memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
     434           0 : }
     435             : 
     436           0 : int efi_get_boot_option(
     437             :                 uint16_t id,
     438             :                 char **title,
     439             :                 sd_id128_t *part_uuid,
     440             :                 char **path,
     441             :                 bool *active) {
     442             : 
     443             :         char boot_id[9];
     444           0 :         _cleanup_free_ uint8_t *buf = NULL;
     445             :         size_t l;
     446             :         struct boot_option *header;
     447             :         ssize_t title_size;
     448           0 :         _cleanup_free_ char *s = NULL, *p = NULL;
     449           0 :         sd_id128_t p_uuid = SD_ID128_NULL;
     450             :         int r;
     451             : 
     452           0 :         if (!is_efi_boot())
     453           0 :                 return -EOPNOTSUPP;
     454             : 
     455           0 :         xsprintf(boot_id, "Boot%04X", id);
     456           0 :         r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
     457           0 :         if (r < 0)
     458           0 :                 return r;
     459           0 :         if (l < offsetof(struct boot_option, title))
     460           0 :                 return -ENOENT;
     461             : 
     462           0 :         header = (struct boot_option *)buf;
     463           0 :         title_size = utf16_size(header->title, l - offsetof(struct boot_option, title));
     464           0 :         if (title_size < 0)
     465           0 :                 return title_size;
     466             : 
     467           0 :         if (title) {
     468           0 :                 s = utf16_to_utf8(header->title, title_size);
     469           0 :                 if (!s)
     470           0 :                         return -ENOMEM;
     471             :         }
     472             : 
     473           0 :         if (header->path_len > 0) {
     474             :                 uint8_t *dbuf;
     475             :                 size_t dnext, doff;
     476             : 
     477           0 :                 doff = offsetof(struct boot_option, title) + title_size;
     478           0 :                 dbuf = buf + doff;
     479           0 :                 if (header->path_len > l - doff)
     480           0 :                         return -EINVAL;
     481             : 
     482           0 :                 dnext = 0;
     483           0 :                 while (dnext < header->path_len) {
     484             :                         struct device_path *dpath;
     485             : 
     486           0 :                         dpath = (struct device_path *)(dbuf + dnext);
     487           0 :                         if (dpath->length < 4)
     488           0 :                                 break;
     489             : 
     490             :                         /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
     491           0 :                         if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
     492           0 :                                 break;
     493             : 
     494           0 :                         dnext += dpath->length;
     495             : 
     496             :                         /* Type 0x04 – Media Device Path */
     497           0 :                         if (dpath->type != MEDIA_DEVICE_PATH)
     498           0 :                                 continue;
     499             : 
     500             :                         /* Sub-Type 1 – Hard Drive */
     501           0 :                         if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
     502             :                                 /* 0x02 – GUID Partition Table */
     503           0 :                                 if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
     504           0 :                                         continue;
     505             : 
     506             :                                 /* 0x02 – GUID signature */
     507           0 :                                 if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
     508           0 :                                         continue;
     509             : 
     510           0 :                                 if (part_uuid)
     511           0 :                                         efi_guid_to_id128(dpath->drive.signature, &p_uuid);
     512           0 :                                 continue;
     513             :                         }
     514             : 
     515             :                         /* Sub-Type 4 – File Path */
     516           0 :                         if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
     517           0 :                                 p = utf16_to_utf8(dpath->path, dpath->length-4);
     518           0 :                                 if (!p)
     519           0 :                                         return  -ENOMEM;
     520             : 
     521           0 :                                 efi_tilt_backslashes(p);
     522           0 :                                 continue;
     523             :                         }
     524             :                 }
     525             :         }
     526             : 
     527           0 :         if (title)
     528           0 :                 *title = TAKE_PTR(s);
     529           0 :         if (part_uuid)
     530           0 :                 *part_uuid = p_uuid;
     531           0 :         if (path)
     532           0 :                 *path = TAKE_PTR(p);
     533           0 :         if (active)
     534           0 :                 *active = header->attr & LOAD_OPTION_ACTIVE;
     535             : 
     536           0 :         return 0;
     537             : }
     538             : 
     539           0 : static void to_utf16(uint16_t *dest, const char *src) {
     540             :         int i;
     541             : 
     542           0 :         for (i = 0; src[i] != '\0'; i++)
     543           0 :                 dest[i] = src[i];
     544           0 :         dest[i] = '\0';
     545           0 : }
     546             : 
     547           0 : static void id128_to_efi_guid(sd_id128_t id, void *guid) {
     548           0 :         struct guid uuid = {
     549           0 :                 .u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3],
     550           0 :                 .u2 = id.bytes[4] << 8 | id.bytes[5],
     551           0 :                 .u3 = id.bytes[6] << 8 | id.bytes[7],
     552             :         };
     553           0 :         memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4));
     554           0 :         memcpy(guid, &uuid, sizeof(uuid));
     555           0 : }
     556             : 
     557           0 : static uint16_t *tilt_slashes(uint16_t *s) {
     558             :         uint16_t *p;
     559             : 
     560           0 :         for (p = s; *p; p++)
     561           0 :                 if (*p == '/')
     562           0 :                         *p = '\\';
     563             : 
     564           0 :         return s;
     565             : }
     566             : 
     567           0 : int efi_add_boot_option(
     568             :                 uint16_t id,
     569             :                 const char *title,
     570             :                 uint32_t part,
     571             :                 uint64_t pstart,
     572             :                 uint64_t psize,
     573             :                 sd_id128_t part_uuid,
     574             :                 const char *path) {
     575             : 
     576             :         size_t size, title_len, path_len;
     577           0 :         _cleanup_free_ char *buf = NULL;
     578             :         struct boot_option *option;
     579             :         struct device_path *devicep;
     580             :         char boot_id[9];
     581             : 
     582           0 :         if (!is_efi_boot())
     583           0 :                 return -EOPNOTSUPP;
     584             : 
     585           0 :         title_len = (strlen(title)+1) * 2;
     586           0 :         path_len = (strlen(path)+1) * 2;
     587             : 
     588           0 :         buf = malloc0(offsetof(struct boot_option, title) + title_len +
     589             :                       sizeof(struct drive_path) +
     590             :                       sizeof(struct device_path) + path_len);
     591           0 :         if (!buf)
     592           0 :                 return -ENOMEM;
     593             : 
     594             :         /* header */
     595           0 :         option = (struct boot_option *)buf;
     596           0 :         option->attr = LOAD_OPTION_ACTIVE;
     597           0 :         option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
     598           0 :                            offsetof(struct device_path, path) + path_len +
     599             :                            offsetof(struct device_path, path);
     600           0 :         to_utf16(option->title, title);
     601           0 :         size = offsetof(struct boot_option, title) + title_len;
     602             : 
     603             :         /* partition info */
     604           0 :         devicep = (struct device_path *)(buf + size);
     605           0 :         devicep->type = MEDIA_DEVICE_PATH;
     606           0 :         devicep->sub_type = MEDIA_HARDDRIVE_DP;
     607           0 :         devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
     608           0 :         memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t));
     609           0 :         memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t));
     610           0 :         memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t));
     611           0 :         id128_to_efi_guid(part_uuid, devicep->drive.signature);
     612           0 :         devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
     613           0 :         devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
     614           0 :         size += devicep->length;
     615             : 
     616             :         /* path to loader */
     617           0 :         devicep = (struct device_path *)(buf + size);
     618           0 :         devicep->type = MEDIA_DEVICE_PATH;
     619           0 :         devicep->sub_type = MEDIA_FILEPATH_DP;
     620           0 :         devicep->length = offsetof(struct device_path, path) + path_len;
     621           0 :         to_utf16(devicep->path, path);
     622           0 :         tilt_slashes(devicep->path);
     623           0 :         size += devicep->length;
     624             : 
     625             :         /* end of path */
     626           0 :         devicep = (struct device_path *)(buf + size);
     627           0 :         devicep->type = END_DEVICE_PATH_TYPE;
     628           0 :         devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
     629           0 :         devicep->length = offsetof(struct device_path, path);
     630           0 :         size += devicep->length;
     631             : 
     632           0 :         xsprintf(boot_id, "Boot%04X", id);
     633           0 :         return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
     634             : }
     635             : 
     636           0 : int efi_remove_boot_option(uint16_t id) {
     637             :         char boot_id[9];
     638             : 
     639           0 :         if (!is_efi_boot())
     640           0 :                 return -EOPNOTSUPP;
     641             : 
     642           0 :         xsprintf(boot_id, "Boot%04X", id);
     643           0 :         return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
     644             : }
     645             : 
     646           0 : int efi_get_boot_order(uint16_t **order) {
     647           0 :         _cleanup_free_ void *buf = NULL;
     648             :         size_t l;
     649             :         int r;
     650             : 
     651           0 :         if (!is_efi_boot())
     652           0 :                 return -EOPNOTSUPP;
     653             : 
     654           0 :         r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
     655           0 :         if (r < 0)
     656           0 :                 return r;
     657             : 
     658           0 :         if (l <= 0)
     659           0 :                 return -ENOENT;
     660             : 
     661           0 :         if (l % sizeof(uint16_t) > 0 ||
     662           0 :             l / sizeof(uint16_t) > INT_MAX)
     663           0 :                 return -EINVAL;
     664             : 
     665           0 :         *order = TAKE_PTR(buf);
     666           0 :         return (int) (l / sizeof(uint16_t));
     667             : }
     668             : 
     669           0 : int efi_set_boot_order(uint16_t *order, size_t n) {
     670             : 
     671           0 :         if (!is_efi_boot())
     672           0 :                 return -EOPNOTSUPP;
     673             : 
     674           0 :         return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
     675             : }
     676             : 
     677           0 : static int boot_id_hex(const char s[static 4]) {
     678           0 :         int id = 0, i;
     679             : 
     680           0 :         assert(s);
     681             : 
     682           0 :         for (i = 0; i < 4; i++)
     683           0 :                 if (s[i] >= '0' && s[i] <= '9')
     684           0 :                         id |= (s[i] - '0') << (3 - i) * 4;
     685           0 :                 else if (s[i] >= 'A' && s[i] <= 'F')
     686           0 :                         id |= (s[i] - 'A' + 10) << (3 - i) * 4;
     687             :                 else
     688           0 :                         return -EINVAL;
     689             : 
     690           0 :         return id;
     691             : }
     692             : 
     693           0 : static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
     694           0 :         return CMP(*a, *b);
     695             : }
     696             : 
     697           0 : int efi_get_boot_options(uint16_t **options) {
     698           0 :         _cleanup_closedir_ DIR *dir = NULL;
     699           0 :         _cleanup_free_ uint16_t *list = NULL;
     700             :         struct dirent *de;
     701           0 :         size_t alloc = 0;
     702           0 :         int count = 0;
     703             : 
     704           0 :         assert(options);
     705             : 
     706           0 :         if (!is_efi_boot())
     707           0 :                 return -EOPNOTSUPP;
     708             : 
     709           0 :         dir = opendir("/sys/firmware/efi/efivars/");
     710           0 :         if (!dir)
     711           0 :                 return -errno;
     712             : 
     713           0 :         FOREACH_DIRENT(de, dir, return -errno) {
     714             :                 int id;
     715             : 
     716           0 :                 if (strncmp(de->d_name, "Boot", 4) != 0)
     717           0 :                         continue;
     718             : 
     719           0 :                 if (strlen(de->d_name) != 45)
     720           0 :                         continue;
     721             : 
     722           0 :                 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
     723           0 :                         continue;
     724             : 
     725           0 :                 id = boot_id_hex(de->d_name + 4);
     726           0 :                 if (id < 0)
     727           0 :                         continue;
     728             : 
     729           0 :                 if (!GREEDY_REALLOC(list, alloc, count + 1))
     730           0 :                         return -ENOMEM;
     731             : 
     732           0 :                 list[count++] = id;
     733             :         }
     734             : 
     735           0 :         typesafe_qsort(list, count, cmp_uint16);
     736             : 
     737           0 :         *options = TAKE_PTR(list);
     738             : 
     739           0 :         return count;
     740             : }
     741             : 
     742           0 : static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
     743           0 :         _cleanup_free_ char *j = NULL;
     744             :         int r;
     745           0 :         uint64_t x = 0;
     746             : 
     747           0 :         assert(name);
     748           0 :         assert(u);
     749             : 
     750           0 :         r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
     751           0 :         if (r < 0)
     752           0 :                 return r;
     753             : 
     754           0 :         r = safe_atou64(j, &x);
     755           0 :         if (r < 0)
     756           0 :                 return r;
     757             : 
     758           0 :         *u = x;
     759           0 :         return 0;
     760             : }
     761             : 
     762           2 : int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
     763             :         uint64_t x, y;
     764             :         int r;
     765             : 
     766           2 :         assert(firmware);
     767           2 :         assert(loader);
     768             : 
     769           2 :         if (!is_efi_boot())
     770           2 :                 return -EOPNOTSUPP;
     771             : 
     772           0 :         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
     773           0 :         if (r < 0)
     774           0 :                 return r;
     775             : 
     776           0 :         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
     777           0 :         if (r < 0)
     778           0 :                 return r;
     779             : 
     780           0 :         if (y == 0 || y < x)
     781           0 :                 return -EIO;
     782             : 
     783           0 :         if (y > USEC_PER_HOUR)
     784           0 :                 return -EIO;
     785             : 
     786           0 :         *firmware = x;
     787           0 :         *loader = y;
     788             : 
     789           0 :         return 0;
     790             : }
     791             : 
     792           0 : int efi_loader_get_device_part_uuid(sd_id128_t *u) {
     793           0 :         _cleanup_free_ char *p = NULL;
     794             :         int r, parsed[16];
     795             : 
     796           0 :         if (!is_efi_boot())
     797           0 :                 return -EOPNOTSUPP;
     798             : 
     799           0 :         r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
     800           0 :         if (r < 0)
     801           0 :                 return r;
     802             : 
     803           0 :         if (sscanf(p, SD_ID128_UUID_FORMAT_STR,
     804             :                    &parsed[0], &parsed[1], &parsed[2], &parsed[3],
     805             :                    &parsed[4], &parsed[5], &parsed[6], &parsed[7],
     806             :                    &parsed[8], &parsed[9], &parsed[10], &parsed[11],
     807             :                    &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
     808           0 :                 return -EIO;
     809             : 
     810           0 :         if (u) {
     811             :                 unsigned i;
     812             : 
     813           0 :                 for (i = 0; i < ELEMENTSOF(parsed); i++)
     814           0 :                         u->bytes[i] = parsed[i];
     815             :         }
     816             : 
     817           0 :         return 0;
     818             : }
     819             : 
     820           0 : int efi_loader_get_entries(char ***ret) {
     821           0 :         _cleanup_free_ char16_t *entries = NULL;
     822           0 :         _cleanup_strv_free_ char **l = NULL;
     823             :         size_t size, i, start;
     824             :         int r;
     825             : 
     826           0 :         assert(ret);
     827             : 
     828           0 :         if (!is_efi_boot())
     829           0 :                 return -EOPNOTSUPP;
     830             : 
     831           0 :         r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderEntries", NULL, (void**) &entries, &size);
     832           0 :         if (r < 0)
     833           0 :                 return r;
     834             : 
     835             :         /* The variable contains a series of individually NUL terminated UTF-16 strings. */
     836             : 
     837           0 :         for (i = 0, start = 0;; i++) {
     838           0 :                 _cleanup_free_ char *decoded = NULL;
     839             :                 bool end;
     840             : 
     841             :                 /* Is this the end of the variable's data? */
     842           0 :                 end = i * sizeof(char16_t) >= size;
     843             : 
     844             :                 /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
     845             :                  * so, let's go to the next entry. */
     846           0 :                 if (!end && entries[i] != 0)
     847           0 :                         continue;
     848             : 
     849             :                 /* We reached the end of a string, let's decode it into UTF-8 */
     850           0 :                 decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
     851           0 :                 if (!decoded)
     852           0 :                         return -ENOMEM;
     853             : 
     854           0 :                 if (efi_loader_entry_name_valid(decoded)) {
     855           0 :                         r = strv_consume(&l, TAKE_PTR(decoded));
     856           0 :                         if (r < 0)
     857           0 :                                 return r;
     858             :                 } else
     859           0 :                         log_debug("Ignoring invalid loader entry '%s'.", decoded);
     860             : 
     861             :                 /* We reached the end of the variable */
     862           0 :                 if (end)
     863           0 :                         break;
     864             : 
     865             :                 /* Continue after the NUL byte */
     866           0 :                 start = i + 1;
     867             :         }
     868             : 
     869           0 :         *ret = TAKE_PTR(l);
     870           0 :         return 0;
     871             : }
     872             : 
     873           0 : int efi_loader_get_features(uint64_t *ret) {
     874           0 :         _cleanup_free_ void *v = NULL;
     875             :         size_t s;
     876             :         int r;
     877             : 
     878           0 :         if (!is_efi_boot()) {
     879           0 :                 *ret = 0;
     880           0 :                 return 0;
     881             :         }
     882             : 
     883           0 :         r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderFeatures", NULL, &v, &s);
     884           0 :         if (r == -ENOENT) {
     885           0 :                 _cleanup_free_ char *info = NULL;
     886             : 
     887             :                 /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
     888           0 :                 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info);
     889           0 :                 if (r < 0) {
     890           0 :                         if (r != -ENOENT)
     891           0 :                                 return r;
     892             : 
     893             :                         /* Variable not set, definitely means not systemd-boot */
     894             : 
     895           0 :                 } else if (first_word(info, "systemd-boot")) {
     896             : 
     897             :                         /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
     898             :                          * static in all its versions. */
     899             : 
     900           0 :                         *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
     901             :                                 EFI_LOADER_FEATURE_ENTRY_DEFAULT |
     902             :                                 EFI_LOADER_FEATURE_ENTRY_ONESHOT;
     903             : 
     904           0 :                         return 0;
     905             :                 }
     906             : 
     907             :                 /* No features supported */
     908           0 :                 *ret = 0;
     909           0 :                 return 0;
     910             :         }
     911           0 :         if (r < 0)
     912           0 :                 return r;
     913             : 
     914           0 :         if (s != sizeof(uint64_t))
     915           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     916             :                                        "LoaderFeatures EFI variable doesn't have the right size.");
     917             : 
     918           0 :         memcpy(ret, v, sizeof(uint64_t));
     919           0 :         return 0;
     920             : }
     921             : 
     922             : #endif
     923             : 
     924           0 : bool efi_loader_entry_name_valid(const char *s) {
     925           0 :         if (isempty(s))
     926           0 :                 return false;
     927             : 
     928           0 :         if (strlen(s) > FILENAME_MAX) /* Make sure entry names fit in filenames */
     929           0 :                 return false;
     930             : 
     931           0 :         return in_charset(s, ALPHANUMERICAL "+-_.");
     932             : }
     933             : 
     934           0 : char *efi_tilt_backslashes(char *s) {
     935             :         char *p;
     936             : 
     937           0 :         for (p = s; *p; p++)
     938           0 :                 if (*p == '\\')
     939           0 :                         *p = '/';
     940             : 
     941           0 :         return s;
     942             : }

Generated by: LCOV version 1.14