]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/efivars.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / shared / efivars.c
index 20067c601adb41d205c2a4eda7c8e7fbfbc7c333..86bb0b57c3d61227be98e3361783c4703bcc5ada 100644 (file)
 #include <string.h>
 #include <fcntl.h>
 
-#include "util.h"
-#include "utf8.h"
+#include "alloc-util.h"
+#include "dirent-util.h"
 #include "efivars.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "utf8.h"
+#include "util.h"
+#include "virt.h"
 
 #ifdef ENABLE_EFI
 
@@ -37,6 +44,7 @@
 #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
 #define END_DEVICE_PATH_TYPE                0x7f
 #define END_ENTIRE_DEVICE_PATH_SUBTYPE      0xff
+#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI    0x0000000000000001
 
 struct boot_option {
         uint32_t attr;
@@ -85,12 +93,94 @@ static int read_flag(const char *varname) {
         return r;
 }
 
-int is_efi_secure_boot(void) {
-        return read_flag("SecureBoot");
+bool is_efi_secure_boot(void) {
+        return read_flag("SecureBoot") > 0;
+}
+
+bool is_efi_secure_boot_setup_mode(void) {
+        return read_flag("SetupMode") > 0;
+}
+
+int efi_reboot_to_firmware_supported(void) {
+        int r;
+        size_t s;
+        uint64_t b;
+        _cleanup_free_ void *v = NULL;
+
+        if (!is_efi_boot() || detect_container() > 0)
+                return -EOPNOTSUPP;
+
+        r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
+        if (r < 0)
+                return r;
+        else if (s != sizeof(uint64_t))
+                return -EINVAL;
+
+        b = *(uint64_t *)v;
+        b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+        return b > 0 ? 0 : -EOPNOTSUPP;
+}
+
+static int get_os_indications(uint64_t *os_indication) {
+        int r;
+        size_t s;
+        _cleanup_free_ void *v = NULL;
+
+        r = efi_reboot_to_firmware_supported();
+        if (r < 0)
+                return r;
+
+        r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
+        if (r == -ENOENT) {
+                /* Some firmware implementations that do support
+                 * OsIndications and report that with
+                 * OsIndicationsSupported will remove the
+                 * OsIndications variable when it is unset. Let's
+                 * pretend it's 0 then, to hide this implementation
+                 * detail. Note that this call will return -ENOENT
+                 * then only if the support for OsIndications is
+                 * missing entirely, as determined by
+                 * efi_reboot_to_firmware_supported() above. */
+                *os_indication = 0;
+                return 0;
+        } else if (r < 0)
+                return r;
+        else if (s != sizeof(uint64_t))
+                return -EINVAL;
+
+        *os_indication = *(uint64_t *)v;
+        return 0;
+}
+
+int efi_get_reboot_to_firmware(void) {
+        int r;
+        uint64_t b;
+
+        r = get_os_indications(&b);
+        if (r < 0)
+                return r;
+
+        return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
 }
 
-int is_efi_secure_boot_setup_mode(void) {
-        return read_flag("SetupMode");
+int efi_set_reboot_to_firmware(bool value) {
+        int r;
+        uint64_t b, b_new;
+
+        r = get_os_indications(&b);
+        if (r < 0)
+                return r;
+
+        if (value)
+                b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+        else
+                b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+
+        /* Avoid writing to efi vars store if we can due to firmware bugs. */
+        if (b != b_new)
+                return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
+
+        return 0;
 }
 
 int efi_get_variable(
@@ -375,16 +465,6 @@ static uint16_t *tilt_slashes(uint16_t *s) {
         return s;
 }
 
-char *efi_tilt_backslashes(char *s) {
-        char *p;
-
-        for (p = s; *p; p++)
-                if (*p == '\\')
-                        *p = '/';
-
-        return s;
-}
-
 int 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) {
@@ -421,7 +501,7 @@ int efi_add_boot_option(uint16_t id, const char *title,
         devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
         devicep->drive.part_nr = part;
         devicep->drive.part_start = pstart;
-        devicep->drive.part_size =  psize;
+        devicep->drive.part_size = psize;
         devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
         devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
         id128_to_efi_guid(part_uuid, devicep->drive.signature);
@@ -615,3 +695,13 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
 }
 
 #endif
+
+char *efi_tilt_backslashes(char *s) {
+        char *p;
+
+        for (p = s; *p; p++)
+                if (*p == '\\')
+                        *p = '/';
+
+        return s;
+}