diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 9dbf96f169..b3d9329926 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -7,6 +7,7 @@ #include "devicetree.h" #include "drivers.h" #include "efivars-fundamental.h" +#include "efivars.h" #include "export-vars.h" #include "graphics.h" #include "initrd.h" @@ -490,7 +491,7 @@ static void print_status(Config *config, char16_t *loaded_image_path) { query_screen_resolution(&screen_width, &screen_height); secure = secure_boot_mode(); - (void) efivar_get(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", &device_part_uuid); + (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", &device_part_uuid); printf(" systemd-boot version: " GIT_VERSION "\n"); if (loaded_image_path) @@ -1104,14 +1105,14 @@ static bool menu_run( /* Update EFI vars after we left the menu to reduce NVRAM writes. */ if (default_efivar_saved != config->idx_default_efivar) - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", config->entry_default_efivar, EFI_VARIABLE_NON_VOLATILE); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", config->entry_default_efivar, EFI_VARIABLE_NON_VOLATILE); if (console_mode_efivar_saved != config->console_mode_efivar) { if (config->console_mode_efivar == CONSOLE_MODE_KEEP) efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderConfigConsoleMode", EFI_VARIABLE_NON_VOLATILE); else - efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"LoaderConfigConsoleMode", - config->console_mode_efivar, EFI_VARIABLE_NON_VOLATILE); + efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"LoaderConfigConsoleMode", + config->console_mode_efivar, EFI_VARIABLE_NON_VOLATILE); } if (timeout_efivar_saved != config->timeout_sec_efivar) { @@ -1122,15 +1123,15 @@ static bool menu_run( case TIMEOUT_MENU_DISABLED: assert_not_reached(); case TIMEOUT_MENU_FORCE: - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", u"menu-force", EFI_VARIABLE_NON_VOLATILE); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", u"menu-force", EFI_VARIABLE_NON_VOLATILE); break; case TIMEOUT_MENU_HIDDEN: - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", u"menu-hidden", EFI_VARIABLE_NON_VOLATILE); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", u"menu-hidden", EFI_VARIABLE_NON_VOLATILE); break; default: assert(config->timeout_sec_efivar < UINT32_MAX); - efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", - config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE); + efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", + config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE); } } @@ -1396,7 +1397,7 @@ static EFI_STATUS boot_entry_bump_counters(BootEntry *entry) { /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on * success */ new_path = xasprintf("%ls\\%ls", entry->path, entry->next_name); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderBootCountPath", new_path, 0); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderBootCountPath", new_path, 0); /* If the file we just renamed is the loader path, then let's update that. */ if (streq16(entry->loader, old_path)) { @@ -1526,7 +1527,7 @@ static EFI_STATUS efivar_get_timeout(const char16_t *var, uint64_t *ret_value) { assert(var); assert(ret_value); - err = efivar_get(MAKE_GUID_PTR(LOADER), var, &value); + err = efivar_get_str16(MAKE_GUID_PTR(LOADER), var, &value); if (err != EFI_SUCCESS) return err; @@ -1553,7 +1554,7 @@ static EFI_STATUS efivar_get_timeout(const char16_t *var, uint64_t *ret_value) { static void config_load_defaults(Config *config, EFI_FILE *root_dir) { _cleanup_free_ char *content = NULL; - size_t content_size, value = 0; /* avoid false maybe-uninitialized warning */ + size_t content_size; EFI_STATUS err; assert(root_dir); @@ -1602,16 +1603,17 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { } else if (err != EFI_NOT_FOUND) log_error_status(err, "Error reading LoaderConfigTimeoutOneShot EFI variable: %m"); - err = efivar_get_uint_string(MAKE_GUID_PTR(LOADER), u"LoaderConfigConsoleMode", &value); - if (err == EFI_SUCCESS) + uint64_t value; + err = efivar_get_uint64_str16(MAKE_GUID_PTR(LOADER), u"LoaderConfigConsoleMode", &value); + if (err == EFI_SUCCESS && value <= INT64_MAX) config->console_mode_efivar = value; - err = efivar_get(MAKE_GUID_PTR(LOADER), u"LoaderEntryOneShot", &config->entry_oneshot); + err = efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryOneShot", &config->entry_oneshot); if (err == EFI_SUCCESS) /* Unset variable now, after all it's "one shot". */ (void) efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderEntryOneShot", EFI_VARIABLE_NON_VOLATILE); - (void) efivar_get(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", &config->entry_default_efivar); + (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", &config->entry_default_efivar); strtolower16(config->entry_default_config); strtolower16(config->entry_default_efivar); @@ -1621,7 +1623,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { config->use_saved_entry = streq16(config->entry_default_config, u"@saved"); config->use_saved_entry_efivar = streq16(config->entry_default_efivar, u"@saved"); if (config->use_saved_entry || config->use_saved_entry_efivar) - (void) efivar_get(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", &config->entry_saved); + (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", &config->entry_saved); } static void config_load_type1_entries( @@ -2014,7 +2016,7 @@ static EFI_STATUS boot_windows_bitlocker(void) { /* There can be gaps in Boot#### entries. Instead of iterating over the full * EFI var list or uint16_t namespace, just look for "Windows Boot Manager" in BootOrder. */ - err = efivar_get_raw(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"BootOrder", (char **) &boot_order, &boot_order_size); + err = efivar_get_raw(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"BootOrder", (void**) &boot_order, &boot_order_size); if (err != EFI_SUCCESS || boot_order_size % sizeof(uint16_t) != 0) return err; @@ -2023,7 +2025,7 @@ static EFI_STATUS boot_windows_bitlocker(void) { size_t buf_size; _cleanup_free_ char16_t *name = xasprintf("Boot%04x", boot_order[i]); - err = efivar_get_raw(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), name, &buf, &buf_size); + err = efivar_get_raw(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), name, (void**) &buf, &buf_size); if (err != EFI_SUCCESS) continue; @@ -2472,7 +2474,7 @@ static void save_selected_entry(const Config *config, const BootEntry *entry) { assert(entry->loader || !entry->call); /* Always export the selected boot entry to the system in a volatile var. */ - (void) efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderEntrySelected", entry->id, 0); + (void) efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntrySelected", entry->id, 0); /* Do not save or delete if this was a oneshot boot. */ if (streq16(config->entry_oneshot, entry->id)) @@ -2483,7 +2485,7 @@ static void save_selected_entry(const Config *config, const BootEntry *entry) { if (streq16(config->entry_saved, entry->id)) return; - (void) efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", entry->id, EFI_VARIABLE_NON_VOLATILE); + (void) efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", entry->id, EFI_VARIABLE_NON_VOLATILE); } else /* Delete the non-volatile var if not needed. */ (void) efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", EFI_VARIABLE_NON_VOLATILE); @@ -2563,7 +2565,7 @@ static void export_loader_variables( assert(loaded_image); (void) efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeInitUSec", init_usec); - (void) efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderInfo", u"systemd-boot " GIT_VERSION, 0); + (void) efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderInfo", u"systemd-boot " GIT_VERSION, 0); (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", loader_features, 0); } diff --git a/src/boot/efi/efivars.c b/src/boot/efi/efivars.c new file mode 100644 index 0000000000..2613efa6d7 --- /dev/null +++ b/src/boot/efi/efivars.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "efi-string.h" +#include "efivars.h" +#include "ticks.h" +#include "util.h" + +EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const char16_t *name, const void *buf, size_t size, uint32_t flags) { + assert(vendor); + assert(name); + assert(buf || size == 0); + + flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + return RT->SetVariable((char16_t *) name, (EFI_GUID *) vendor, flags, size, (void *) buf); +} + +EFI_STATUS efivar_set_str16(const EFI_GUID *vendor, const char16_t *name, const char16_t *value, uint32_t flags) { + assert(vendor); + assert(name); + + return efivar_set_raw(vendor, name, value, value ? strsize16(value) : 0, flags); +} + +EFI_STATUS efivar_set_uint64_str16(const EFI_GUID *vendor, const char16_t *name, uint64_t i, uint32_t flags) { + assert(vendor); + assert(name); + + _cleanup_free_ char16_t *str = xasprintf("%" PRIu64, i); + return efivar_set_str16(vendor, name, str, flags); +} + +EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t value, uint32_t flags) { + uint8_t buf[4]; + + assert(vendor); + assert(name); + + buf[0] = (uint8_t)(value >> 0U & 0xFF); + buf[1] = (uint8_t)(value >> 8U & 0xFF); + buf[2] = (uint8_t)(value >> 16U & 0xFF); + buf[3] = (uint8_t)(value >> 24U & 0xFF); + + return efivar_set_raw(vendor, name, buf, sizeof(buf), flags); +} + +EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags) { + uint8_t buf[8]; + + assert(vendor); + assert(name); + + buf[0] = (uint8_t)(value >> 0U & 0xFF); + buf[1] = (uint8_t)(value >> 8U & 0xFF); + buf[2] = (uint8_t)(value >> 16U & 0xFF); + buf[3] = (uint8_t)(value >> 24U & 0xFF); + buf[4] = (uint8_t)(value >> 32U & 0xFF); + buf[5] = (uint8_t)(value >> 40U & 0xFF); + buf[6] = (uint8_t)(value >> 48U & 0xFF); + buf[7] = (uint8_t)(value >> 56U & 0xFF); + + return efivar_set_raw(vendor, name, buf, sizeof(buf), flags); +} + +EFI_STATUS efivar_unset(const EFI_GUID *vendor, const char16_t *name, uint32_t flags) { + EFI_STATUS err; + + assert(vendor); + assert(name); + + /* We could be wiping a non-volatile variable here and the spec makes no guarantees that won't incur + * in an extra write (and thus wear out). So check and clear only if needed. */ + err = efivar_get_raw(vendor, name, NULL, NULL); + if (err == EFI_SUCCESS) + return efivar_set_raw(vendor, name, NULL, 0, flags); + + return err; +} + +EFI_STATUS efivar_get_str16(const EFI_GUID *vendor, const char16_t *name, char16_t **ret) { + _cleanup_free_ char16_t *buf = NULL; + EFI_STATUS err; + char16_t *val; + size_t size; + + assert(vendor); + assert(name); + + err = efivar_get_raw(vendor, name, (void**) &buf, &size); + if (err != EFI_SUCCESS) + return err; + + /* Make sure there are no incomplete characters in the buffer */ + if ((size % sizeof(char16_t)) != 0) + return EFI_INVALID_PARAMETER; + + if (!ret) + return EFI_SUCCESS; + + /* Return buffer directly if it happens to be NUL terminated already */ + if (size >= sizeof(char16_t) && buf[size / sizeof(char16_t) - 1] == 0) { + *ret = TAKE_PTR(buf); + return EFI_SUCCESS; + } + + /* Make sure a terminating NUL is available at the end */ + val = xmalloc(size + sizeof(char16_t)); + + memcpy(val, buf, size); + val[size / sizeof(char16_t) - 1] = 0; /* NUL terminate */ + + *ret = val; + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_uint64_str16(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret) { + EFI_STATUS err; + + assert(vendor); + assert(name); + + _cleanup_free_ char16_t *val = NULL; + err = efivar_get_str16(vendor, name, &val); + if (err != EFI_SUCCESS) + return err; + + uint64_t u; + if (!parse_number16(val, &u, NULL)) + return EFI_INVALID_PARAMETER; + + if (ret) + *ret = u; + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret) { + _cleanup_free_ uint8_t *buf = NULL; + size_t size; + EFI_STATUS err; + + assert(vendor); + assert(name); + + err = efivar_get_raw(vendor, name, (void**) &buf, &size); + if (err != EFI_SUCCESS) + return err; + + if (size != sizeof(uint32_t)) + return EFI_BUFFER_TOO_SMALL; + + if (ret) + *ret = (uint32_t) buf[0] << 0U | (uint32_t) buf[1] << 8U | (uint32_t) buf[2] << 16U | + (uint32_t) buf[3] << 24U; + + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret) { + _cleanup_free_ uint8_t *buf = NULL; + size_t size; + EFI_STATUS err; + + assert(vendor); + assert(name); + + err = efivar_get_raw(vendor, name, (void**) &buf, &size); + if (err != EFI_SUCCESS) + return err; + + if (size != sizeof(uint64_t)) + return EFI_BUFFER_TOO_SMALL; + + if (ret) + *ret = (uint64_t) buf[0] << 0U | (uint64_t) buf[1] << 8U | (uint64_t) buf[2] << 16U | + (uint64_t) buf[3] << 24U | (uint64_t) buf[4] << 32U | (uint64_t) buf[5] << 40U | + (uint64_t) buf[6] << 48U | (uint64_t) buf[7] << 56U; + + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, void **ret, size_t *ret_size) { + EFI_STATUS err; + + assert(vendor); + assert(name); + + size_t size = 0; + err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &size, NULL); + if (err != EFI_BUFFER_TOO_SMALL) + return err; + + _cleanup_free_ void *buf = xmalloc(size); + err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &size, buf); + if (err != EFI_SUCCESS) + return err; + + if (ret) + *ret = TAKE_PTR(buf); + if (ret_size) + *ret_size = size; + + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret) { + _cleanup_free_ uint8_t *b = NULL; + size_t size; + EFI_STATUS err; + + assert(vendor); + assert(name); + + err = efivar_get_raw(vendor, name, (void**) &b, &size); + if (err != EFI_SUCCESS) + return err; + + if (ret) + *ret = *b > 0; + + return EFI_SUCCESS; +} + +void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec) { + assert(vendor); + assert(name); + + if (usec == 0) + usec = time_usec(); + if (usec == 0) + return; + + _cleanup_free_ char16_t *str = xasprintf("%" PRIu64, usec); + efivar_set_str16(vendor, name, str, 0); +} + +uint64_t get_os_indications_supported(void) { + uint64_t osind; + EFI_STATUS err; + + /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no + * supported features. */ + + err = efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndicationsSupported", &osind); + if (err != EFI_SUCCESS) + return 0; + + return osind; +} diff --git a/src/boot/efi/efivars.h b/src/boot/efi/efivars.h new file mode 100644 index 0000000000..5a5cc2501c --- /dev/null +++ b/src/boot/efi/efivars.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "efi.h" + +/* + * Allocated random UUID, intended to be shared across tools that implement + * the (ESP)\loader\entries\-.conf convention and the + * associated EFI variables. + */ +#define LOADER_GUID \ + { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } + +EFI_STATUS efivar_set_str16(const EFI_GUID *vendor, const char16_t *name, const char16_t *value, uint32_t flags); +EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const char16_t *name, const void *buf, size_t size, uint32_t flags); +EFI_STATUS efivar_set_uint64_str16(const EFI_GUID *vendor, const char16_t *name, uint64_t i, uint32_t flags); +EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t value, uint32_t flags); +EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags); +void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec); + +EFI_STATUS efivar_unset(const EFI_GUID *vendor, const char16_t *name, uint32_t flags); + +EFI_STATUS efivar_get_str16(const EFI_GUID *vendor, const char16_t *name, char16_t **ret); +EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, void **ret, size_t *ret_size); +EFI_STATUS efivar_get_uint64_str16(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret); +EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret); +EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret); +EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret); + +uint64_t get_os_indications_supported(void); diff --git a/src/boot/efi/export-vars.c b/src/boot/efi/export-vars.c index 3926747bba..3f59922a7e 100644 --- a/src/boot/efi/export-vars.c +++ b/src/boot/efi/export-vars.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "device-path-util.h" +#include "efivars.h" #include "export-vars.h" #include "part-discovery.h" #include "util.h" @@ -12,7 +13,7 @@ void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) { _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle); if (uuid) - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0); } /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the @@ -25,20 +26,20 @@ void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { loaded_image->FilePath) { _cleanup_free_ char16_t *s = NULL; if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS) - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0); } /* if LoaderFirmwareInfo is not set, let's set it */ if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { _cleanup_free_ char16_t *s = NULL; s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0); } /* ditto for LoaderFirmwareType */ if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { _cleanup_free_ char16_t *s = NULL; s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0); + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0); } } diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 42a7914bf6..bcfccfaddc 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -258,6 +258,7 @@ libefi_sources = files( 'devicetree.c', 'drivers.c', 'efi-string.c', + 'efivars.c', 'export-vars.c', 'graphics.c', 'initrd.c', diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c index 03f3ebd9b4..0ce16615c1 100644 --- a/src/boot/efi/random-seed.c +++ b/src/boot/efi/random-seed.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "efivars.h" #include "memory-util-fundamental.h" #include "proto/rng.h" #include "random-seed.h" @@ -55,7 +56,7 @@ static EFI_STATUS acquire_system_token(void **ret, size_t *ret_size) { assert(ret); assert(ret_size); - err = efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderSystemToken", &data, &size); + err = efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderSystemToken", (void**) &data, &size); if (err != EFI_SUCCESS) { if (err != EFI_NOT_FOUND) log_error_status(err, "Failed to read LoaderSystemToken EFI variable: %m"); diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c index 2400324fc5..566688adf8 100644 --- a/src/boot/efi/secure-boot.c +++ b/src/boot/efi/secure-boot.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "console.h" +#include "efivars.h" #include "proto/security-arch.h" #include "secure-boot.h" #include "util.h" diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c index df136ed6d9..5928553471 100644 --- a/src/boot/efi/shim.c +++ b/src/boot/efi/shim.c @@ -9,6 +9,7 @@ */ #include "device-path-util.h" +#include "efivars.h" #include "secure-boot.h" #include "shim.h" #include "util.h" diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index f51812d0d1..57f441c84b 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -3,6 +3,7 @@ #include "cpio.h" #include "device-path-util.h" #include "devicetree.h" +#include "efivars.h" #include "export-vars.h" #include "graphics.h" #include "iovec-util-fundamental.h" @@ -156,7 +157,7 @@ static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our * own data) */ - (void) efivar_set(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0); + (void) efivar_set_str16(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0); (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER), u"StubFeatures", stub_features, 0); } @@ -745,13 +746,13 @@ static void export_pcr_variables( * successfully, and encode in it which PCR was used. */ if (sections_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0); + (void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0); if (parameters_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0); + (void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0); if (sysext_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0); + (void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0); if (confext_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); + (void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); } static void install_embedded_devicetree( diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index eb29eb2d5b..6ceb032fa1 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -7,233 +7,7 @@ #include "ticks.h" #include "util.h" #include "version.h" - -EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const char16_t *name, const void *buf, size_t size, uint32_t flags) { - assert(vendor); - assert(name); - assert(buf || size == 0); - - flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; - return RT->SetVariable((char16_t *) name, (EFI_GUID *) vendor, flags, size, (void *) buf); -} - -EFI_STATUS efivar_set(const EFI_GUID *vendor, const char16_t *name, const char16_t *value, uint32_t flags) { - assert(vendor); - assert(name); - - return efivar_set_raw(vendor, name, value, value ? strsize16(value) : 0, flags); -} - -EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t i, uint32_t flags) { - assert(vendor); - assert(name); - - _cleanup_free_ char16_t *str = xasprintf("%zu", i); - return efivar_set(vendor, name, str, flags); -} - -EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t value, uint32_t flags) { - uint8_t buf[4]; - - assert(vendor); - assert(name); - - buf[0] = (uint8_t)(value >> 0U & 0xFF); - buf[1] = (uint8_t)(value >> 8U & 0xFF); - buf[2] = (uint8_t)(value >> 16U & 0xFF); - buf[3] = (uint8_t)(value >> 24U & 0xFF); - - return efivar_set_raw(vendor, name, buf, sizeof(buf), flags); -} - -EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags) { - uint8_t buf[8]; - - assert(vendor); - assert(name); - - buf[0] = (uint8_t)(value >> 0U & 0xFF); - buf[1] = (uint8_t)(value >> 8U & 0xFF); - buf[2] = (uint8_t)(value >> 16U & 0xFF); - buf[3] = (uint8_t)(value >> 24U & 0xFF); - buf[4] = (uint8_t)(value >> 32U & 0xFF); - buf[5] = (uint8_t)(value >> 40U & 0xFF); - buf[6] = (uint8_t)(value >> 48U & 0xFF); - buf[7] = (uint8_t)(value >> 56U & 0xFF); - - return efivar_set_raw(vendor, name, buf, sizeof(buf), flags); -} - -EFI_STATUS efivar_unset(const EFI_GUID *vendor, const char16_t *name, uint32_t flags) { - EFI_STATUS err; - - assert(vendor); - assert(name); - - /* We could be wiping a non-volatile variable here and the spec makes no guarantees that won't incur - * in an extra write (and thus wear out). So check and clear only if needed. */ - err = efivar_get_raw(vendor, name, NULL, NULL); - if (err == EFI_SUCCESS) - return efivar_set_raw(vendor, name, NULL, 0, flags); - - return err; -} - -EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret) { - _cleanup_free_ char16_t *buf = NULL; - EFI_STATUS err; - char16_t *val; - size_t size; - - assert(vendor); - assert(name); - - err = efivar_get_raw(vendor, name, (char **) &buf, &size); - if (err != EFI_SUCCESS) - return err; - - /* Make sure there are no incomplete characters in the buffer */ - if ((size % sizeof(char16_t)) != 0) - return EFI_INVALID_PARAMETER; - - if (!ret) - return EFI_SUCCESS; - - /* Return buffer directly if it happens to be NUL terminated already */ - if (size >= sizeof(char16_t) && buf[size / sizeof(char16_t) - 1] == 0) { - *ret = TAKE_PTR(buf); - return EFI_SUCCESS; - } - - /* Make sure a terminating NUL is available at the end */ - val = xmalloc(size + sizeof(char16_t)); - - memcpy(val, buf, size); - val[size / sizeof(char16_t) - 1] = 0; /* NUL terminate */ - - *ret = val; - return EFI_SUCCESS; -} - -EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t *ret) { - _cleanup_free_ char16_t *val = NULL; - EFI_STATUS err; - uint64_t u; - - assert(vendor); - assert(name); - - err = efivar_get(vendor, name, &val); - if (err != EFI_SUCCESS) - return err; - - if (!parse_number16(val, &u, NULL) || u > SIZE_MAX) - return EFI_INVALID_PARAMETER; - - if (ret) - *ret = u; - return EFI_SUCCESS; -} - -EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret) { - _cleanup_free_ char *buf = NULL; - size_t size; - EFI_STATUS err; - - assert(vendor); - assert(name); - - err = efivar_get_raw(vendor, name, &buf, &size); - if (err != EFI_SUCCESS) - return err; - - if (size != sizeof(uint32_t)) - return EFI_BUFFER_TOO_SMALL; - - if (ret) - *ret = (uint32_t) buf[0] << 0U | (uint32_t) buf[1] << 8U | (uint32_t) buf[2] << 16U | - (uint32_t) buf[3] << 24U; - - return EFI_SUCCESS; -} - -EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret) { - _cleanup_free_ char *buf = NULL; - size_t size; - EFI_STATUS err; - - assert(vendor); - assert(name); - - err = efivar_get_raw(vendor, name, &buf, &size); - if (err != EFI_SUCCESS) - return err; - - if (size != sizeof(uint64_t)) - return EFI_BUFFER_TOO_SMALL; - - if (ret) - *ret = (uint64_t) buf[0] << 0U | (uint64_t) buf[1] << 8U | (uint64_t) buf[2] << 16U | - (uint64_t) buf[3] << 24U | (uint64_t) buf[4] << 32U | (uint64_t) buf[5] << 40U | - (uint64_t) buf[6] << 48U | (uint64_t) buf[7] << 56U; - - return EFI_SUCCESS; -} - -EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, size_t *ret_size) { - EFI_STATUS err; - - assert(vendor); - assert(name); - - size_t size = 0; - err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &size, NULL); - if (err != EFI_BUFFER_TOO_SMALL) - return err; - - _cleanup_free_ void *buf = xmalloc(size); - err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &size, buf); - if (err != EFI_SUCCESS) - return err; - - if (ret) - *ret = TAKE_PTR(buf); - if (ret_size) - *ret_size = size; - - return EFI_SUCCESS; -} - -EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret) { - _cleanup_free_ char *b = NULL; - size_t size; - EFI_STATUS err; - - assert(vendor); - assert(name); - - err = efivar_get_raw(vendor, name, &b, &size); - if (err != EFI_SUCCESS) - return err; - - if (ret) - *ret = *b > 0; - - return EFI_SUCCESS; -} - -void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec) { - assert(vendor); - assert(name); - - if (usec == 0) - usec = time_usec(); - if (usec == 0) - return; - - _cleanup_free_ char16_t *str = xasprintf("%" PRIu64, usec); - efivar_set(vendor, name, str, 0); -} +#include "efivars.h" void convert_efi_path(char16_t *path) { assert(path); @@ -544,20 +318,6 @@ EFI_STATUS open_directory( return EFI_SUCCESS; } -uint64_t get_os_indications_supported(void) { - uint64_t osind; - EFI_STATUS err; - - /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no - * supported features. */ - - err = efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndicationsSupported", &osind); - if (err != EFI_SUCCESS) - return 0; - - return osind; -} - __attribute__((noinline)) void notify_debugger(const char *identity, volatile bool wait) { #ifdef EFI_DEBUG printf("%s@%p %s\n", identity, __executable_start, GIT_VERSION); diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index dc624f45ae..3a26bc5c92 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -81,22 +81,6 @@ static inline Pages xmalloc_pages( }; } -EFI_STATUS efivar_set(const EFI_GUID *vendor, const char16_t *name, const char16_t *value, uint32_t flags); -EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const char16_t *name, const void *buf, size_t size, uint32_t flags); -EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t i, uint32_t flags); -EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t value, uint32_t flags); -EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags); -void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec); - -EFI_STATUS efivar_unset(const EFI_GUID *vendor, const char16_t *name, uint32_t flags); - -EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret); -EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, size_t *ret_size); -EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t *ret); -EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret); -EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret); -EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret); - void convert_efi_path(char16_t *path); char16_t *xstr8_to_path(const char *stra); char16_t *mangle_stub_cmdline(char16_t *cmdline); @@ -116,14 +100,6 @@ static inline void unload_imagep(EFI_HANDLE *image) { (void) BS->UnloadImage(*image); } -/* - * Allocated random UUID, intended to be shared across tools that implement - * the (ESP)\loader\entries\-.conf convention and the - * associated EFI variables. - */ -#define LOADER_GUID \ - { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } - /* Note that GUID is evaluated multiple times! */ #define GUID_FORMAT_STR "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" #define GUID_FORMAT_VAL(g) (g).Data1, (g).Data2, (g).Data3, (g).Data4[0], (g).Data4[1], \ @@ -162,8 +138,6 @@ static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) { return (void *) (uintptr_t) addr; } -uint64_t get_os_indications_supported(void); - /* If EFI_DEBUG, print our name and version and also report the address of the image base so a debugger can * be attached. See debug-sd-boot.sh for how this can be done. */ void notify_debugger(const char *identity, bool wait); diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index 60e216d54c..0f268a1512 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -8,6 +8,7 @@ #include "device-path-util.h" #include "drivers.h" #include "efi-string.h" +#include "efivars.h" #include "proto/device-path.h" #include "string-util-fundamental.h" #include "util.h" @@ -80,7 +81,7 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) { _cleanup_free_ EFI_DEVICE_PATH *dp = NULL; _cleanup_free_ char16_t *order_str = xasprintf("VMMBootOrder%04zx", order); - dp_err = efivar_get_raw(MAKE_GUID_PTR(VMM_BOOT_ORDER), order_str, (char **) &dp, NULL); + dp_err = efivar_get_raw(MAKE_GUID_PTR(VMM_BOOT_ORDER), order_str, (void**) &dp, NULL); for (size_t i = 0; i < n_handles; i++) { _cleanup_(file_closep) EFI_FILE *root_dir = NULL, *efi_dir = NULL;