diff options
35 files changed, 610 insertions, 387 deletions
diff --git a/Documentation/filesystems/efivarfs.rst b/Documentation/filesystems/efivarfs.rst index f646c3f0980f..23164464ca2b 100644 --- a/Documentation/filesystems/efivarfs.rst +++ b/Documentation/filesystems/efivarfs.rst @@ -37,6 +37,18 @@ accidentally. |4_bytes_of_attributes + efivar_data| +-----------------------------------+ +Mount options +============= + +========== ================================================================== +(no)statfs Control whether ``statfs(2)`` reports the variable-store + used/available. Disabling it skips the ``QueryVariableInfo()`` + EFI runtime service call, which might block all CPUs for a few + milliseconds, and reports 0/0 for used/available instead. + Defaults to ``statfs``, except on ``CONFIG_PREEMPT_RT`` where + it defaults to ``nostatfs``. +========== ================================================================== + *See also:* - Documentation/admin-guide/acpi/ssdt-overlays.rst diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 0c39adb96b91..5eb96c17883e 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -891,20 +891,21 @@ bool efi_is_table_address(unsigned long phys_addr) #define EFI_ATTR_SHOW(name) \ static ssize_t name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ + const struct kobj_attribute *attr, char *buf) \ { \ - return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ + return sysfs_emit(buf, "0x%lx\n", EFI_FIELD(name)); \ } EFI_ATTR_SHOW(fw_vendor); EFI_ATTR_SHOW(runtime); EFI_ATTR_SHOW(config_table); -struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); -struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); -struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); +const struct kobj_attribute efi_attr_fw_vendor = __KOBJ_ATTR_RO(fw_vendor); +const struct kobj_attribute efi_attr_runtime = __KOBJ_ATTR_RO(runtime); +const struct kobj_attribute efi_attr_config_table = __KOBJ_ATTR_RO(config_table); -umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +umode_t efi_attr_is_visible(struct kobject *kobj, const struct attribute *attr, + int n) { if (attr == &efi_attr_fw_vendor.attr) { if (efi_enabled(EFI_PARAVIRT) || diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index 8e8f81f0a5a0..e423df3438da 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -197,7 +197,7 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, page = cap_info->pages[cap_info->index - 1]; } - kbuff = kmap(page); + kbuff = kmap_local_page(page); kbuff += PAGE_SIZE - cap_info->page_bytes_remain; /* Copy capsule binary data from user space to kernel space buffer */ @@ -217,7 +217,7 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, } cap_info->count += write_byte; - kunmap(page); + kunmap_local(kbuff); /* Submit the full binary to efi_capsule_update() API */ if (cap_info->header.headersize > 0 && @@ -236,7 +236,7 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, return write_byte; fail_unmap: - kunmap(page); + kunmap_local(kbuff); failed: efi_free_all_buff_pages(cap_info); return ret; diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c index dd62526380d2..417e8f13a014 100644 --- a/drivers/firmware/efi/capsule.c +++ b/drivers/firmware/efi/capsule.c @@ -245,7 +245,7 @@ int efi_capsule_update(efi_capsule_header_t *capsule, phys_addr_t *pages) for (i = 0; i < sg_count; i++) { efi_capsule_block_desc_t *sglist; - sglist = kmap_atomic(sg_pages[i]); + sglist = kmap_local_page(sg_pages[i]); for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) { u64 sz = min_t(u64, imagesize, @@ -277,7 +277,7 @@ int efi_capsule_update(efi_capsule_header_t *capsule, phys_addr_t *pages) */ efi_capsule_flush_cache_range(sglist, PAGE_SIZE); #endif - kunmap_atomic(sglist); + kunmap_local(sglist); } mutex_lock(&capsule_mutex); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 0327a39d31fa..9bc22de0d67e 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -8,7 +8,7 @@ * * This code registers /sys/firmware/efi{,/efivars} when EFI is supported, * allowing the efivarfs to be mounted or the efivars module to be loaded. - * The existance of /sys/firmware/efi may also be used by userspace to + * The existence of /sys/firmware/efi may also be used by userspace to * determine that the system supports EFI. */ @@ -46,20 +46,14 @@ struct efi __read_mostly efi = { .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, .ovmf_debug_log = EFI_INVALID_TABLE_ADDR, -#ifdef CONFIG_LOAD_UEFI_KEYS + .mem_reserve = EFI_INVALID_TABLE_ADDR, .mokvar_table = EFI_INVALID_TABLE_ADDR, -#endif -#ifdef CONFIG_EFI_COCO_SECRET .coco_secret = EFI_INVALID_TABLE_ADDR, -#endif -#ifdef CONFIG_UNACCEPTED_MEMORY .unaccepted = EFI_INVALID_TABLE_ADDR, -#endif }; EXPORT_SYMBOL(efi); unsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR; -static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; @@ -131,45 +125,45 @@ struct kobject *efi_kobj; * one value per file rule! */ static ssize_t systab_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) + const struct kobj_attribute *attr, char *buf) { - char *str = buf; + int offset = 0; if (!kobj || !buf) return -EINVAL; if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); + offset = sysfs_emit(buf, "ACPI20=0x%lx\n", efi.acpi20); if (efi.acpi != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); + offset += sysfs_emit_at(buf, offset, "ACPI=0x%lx\n", efi.acpi); /* * If both SMBIOS and SMBIOS3 entry points are implemented, the * SMBIOS3 entry point shall be preferred, so we list it first to * let applications stop parsing after the first match. */ if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); + offset += sysfs_emit_at(buf, offset, "SMBIOS3=0x%lx\n", efi.smbios3); if (efi.smbios != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); + offset += sysfs_emit_at(buf, offset, "SMBIOS=0x%lx\n", efi.smbios); - return str - buf; + return offset; } -static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); +static const struct kobj_attribute efi_attr_systab = __KOBJ_ATTR_RO_MODE(systab, 0400); static ssize_t fw_platform_size_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) + const struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); + return sysfs_emit(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); } -extern __weak struct kobj_attribute efi_attr_fw_vendor; -extern __weak struct kobj_attribute efi_attr_runtime; -extern __weak struct kobj_attribute efi_attr_config_table; -static struct kobj_attribute efi_attr_fw_platform_size = - __ATTR_RO(fw_platform_size); +extern __weak const struct kobj_attribute efi_attr_fw_vendor; +extern __weak const struct kobj_attribute efi_attr_runtime; +extern __weak const struct kobj_attribute efi_attr_config_table; +static const struct kobj_attribute efi_attr_fw_platform_size = + __KOBJ_ATTR_RO(fw_platform_size); -static struct attribute *efi_subsys_attrs[] = { +static const struct attribute *const efi_subsys_attrs[] = { &efi_attr_systab.attr, &efi_attr_fw_platform_size.attr, &efi_attr_fw_vendor.attr, @@ -178,15 +172,16 @@ static struct attribute *efi_subsys_attrs[] = { NULL, }; -umode_t __weak efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, +umode_t __weak efi_attr_is_visible(struct kobject *kobj, + const struct attribute *attr, int n) { return attr->mode; } static const struct attribute_group efi_subsys_attr_group = { - .attrs = efi_subsys_attrs, - .is_visible = efi_attr_is_visible, + .attrs_const = efi_subsys_attrs, + .is_visible_const = efi_attr_is_visible, }; struct blocking_notifier_head efivar_ops_nh; @@ -401,6 +396,35 @@ static void __init efi_debugfs_init(void) static inline void efi_debugfs_init(void) {} #endif +static ssize_t efi_runtime_show(struct kobject *kobj, + const struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%d\n", efi_enabled(EFI_RUNTIME_SERVICES)); +} + +static ssize_t efi_runtime_store(struct kobject *kobj, + const struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int ret; + bool enable; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (efi_runtime_set_enable_flag(enable) != EFI_SUCCESS) { + pr_warn("unable to enable/disable efi runtime service\n"); + return -EAGAIN; + } + + return count; +} + +static const struct kobj_attribute efi_runtime_attr = + __KOBJ_ATTR(runtime_enable, 0644, efi_runtime_show, efi_runtime_store); + static int __init efipostcore_init(void) { if (!efi_enabled(EFI_RUNTIME_SERVICES)) @@ -446,6 +470,11 @@ static int __init efisubsys_init(void) goto err_destroy_wq; } + if (IS_ENABLED(CONFIG_PREEMPT_RT) && efi.runtime_supported_mask) { + if (sysfs_create_file(efi_kobj, &efi_runtime_attr.attr)) + pr_warn("unable to register efi dynamic sysfs interface\n"); + } + if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE | EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) { error = generic_ops_register(); @@ -628,7 +657,7 @@ static const efi_config_table_type_t common_tables[] __initconst = { {LINUX_EFI_TPM_EVENT_LOG_GUID, &efi.tpm_log, "TPMEventLog" }, {EFI_TCG2_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "TPMFinalLog" }, {EFI_CC_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "CCFinalLog" }, - {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, + {LINUX_EFI_MEMRESERVE_TABLE_GUID, &efi.mem_reserve, "MEMRESERVE" }, {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" }, {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, #ifdef CONFIG_OVMF_DEBUG_LOG @@ -771,8 +800,8 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, efi_tpm_eventlog_init(); - if (mem_reserve != EFI_INVALID_TABLE_ADDR) { - unsigned long prsv = mem_reserve; + if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { + unsigned long prsv = efi.mem_reserve; while (prsv) { struct linux_efi_memreserve *rsv; @@ -1059,10 +1088,10 @@ static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init; static int __init efi_memreserve_map_root(void) { - if (mem_reserve == EFI_INVALID_TABLE_ADDR) + if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR) return -ENODEV; - efi_memreserve_root = memremap(mem_reserve, + efi_memreserve_root = memremap(efi.mem_reserve, sizeof(*efi_memreserve_root), MEMREMAP_WB); if (WARN_ON_ONCE(!efi_memreserve_root)) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 415f63105b48..bbab12b8eb4b 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -6,31 +6,31 @@ # enabled, even if doing so doesn't break the build. # -# non-x86 reuses KBUILD_CFLAGS, x86 does not cflags-y := $(KBUILD_CFLAGS) -cflags-$(CONFIG_X86_32) := -march=i386 -cflags-$(CONFIG_X86_64) := -mcmodel=small -cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ $(CC_FLAGS_DIALECT) \ - -fPIC -fno-strict-aliasing -mno-red-zone \ - -mno-mmx -mno-sse -fshort-wchar \ - -Wno-pointer-sign \ - $(call cc-disable-warning, address-of-packed-member) \ - -fno-asynchronous-unwind-tables \ - $(CLANG_FLAGS) - -# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly -# disable the stackleak plugin -cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_KSTACK_ERASE) \ - -fno-unwind-tables -fno-asynchronous-unwind-tables +# x86 links the stub into the decompressor rather than the kernel proper, so +# drop the kernel's code model, calling convention, and stack alignment, and +# the mitigations that rely on thunks and patch sites only the kernel has. +cflags-$(CONFIG_X86) := $(filter-out -mcmodel=kernel \ + -mregparm=3 -freg-struct-return \ + -mpreferred-stack-boundary=% \ + -mstack-alignment=% \ + $(RETPOLINE_CFLAGS) $(RETHUNK_CFLAGS) \ + $(PADDING_CFLAGS), $(cflags-y)) +cflags-$(CONFIG_X86_32) += -march=i386 +cflags-$(CONFIG_X86_64) += -mcmodel=small +cflags-$(CONFIG_X86) += -fPIC +# Nothing enables IBT while the stub runs, so ENDBR would only take space. +cflags-$(CONFIG_X86) += $(call cc-option,-fcf-protection=none) + +cflags-$(CONFIG_ARM64) += -fpie -ffixed-x18 -fno-unwind-tables \ + -fno-asynchronous-unwind-tables cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ -DEFI_HAVE_STRCMP -fno-builtin -fpic \ - $(call cc-option,-mno-single-pic-base) \ - $(DISABLE_KSTACK_ERASE) -cflags-$(CONFIG_RISCV) += -fpic -DNO_ALTERNATIVE -mno-relax \ - $(DISABLE_KSTACK_ERASE) -cflags-$(CONFIG_LOONGARCH) += -fpie $(DISABLE_KSTACK_ERASE) + $(call cc-option,-mno-single-pic-base) +cflags-$(CONFIG_RISCV) += -fpic -DNO_ALTERNATIVE -mno-relax +cflags-$(CONFIG_LOONGARCH) += -fpie cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt @@ -40,6 +40,7 @@ KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \ -D__NO_FORTIFY \ -ffreestanding \ -fno-stack-protector \ + $(DISABLE_KSTACK_ERASE) \ $(CONFIG_CC_OPT_NO_ADDRSIG) \ -D__DISABLE_EXPORTS @@ -66,7 +67,8 @@ KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ skip_spaces.o lib-cmdline.o lib-ctype.o \ - alignedmem.o printk.o vsprintf.o + alignedmem.o printk.o vsprintf.o bli.o \ + lib-ucs2_string.o # include the stub's libfdt dependencies from lib/ when needed libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \ diff --git a/drivers/firmware/efi/libstub/bli.c b/drivers/firmware/efi/libstub/bli.c new file mode 100644 index 000000000000..10696886d222 --- /dev/null +++ b/drivers/firmware/efi/libstub/bli.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <generated/utsrelease.h> + +#include <linux/efi.h> +#include <linux/unaligned.h> + +#include "efistub.h" + +static efi_guid_t loader_entry_guid = LINUX_EFI_LOADER_ENTRY_GUID; + +static const struct efi_hd_dev_path * +efi_bli_find_hd_node(const struct efi_dev_path *path) +{ + const struct efi_dev_path *node; + u16 node_len; + + for (node = path; + node->header.type != EFI_DEV_END_PATH && + node->header.type != EFI_DEV_END_PATH2; + node = (const void *)node + node_len) { + node_len = get_unaligned_le16(&node->header.length); + + if (node_len < sizeof(node->header)) + return NULL; + + if (node->header.type != EFI_DEV_MEDIA || + node->header.sub_type != EFI_DEV_MEDIA_HARD_DRIVE) + continue; + + if (node_len < sizeof(node->hd)) + return NULL; + + if (node->hd.partition_format != EFI_HD_PARTITION_FORMAT_GPT || + node->hd.signature_type != EFI_HD_SIGNATURE_TYPE_GUID) + continue; + + return &node->hd; + } + + return NULL; +} + +static void efi_bli_populate_loader_part_uuid(const efi_loaded_image_t *image) +{ + static efi_guid_t device_path_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; + efi_char16_t partuuid[UUID_STRING_LEN + 1]; + const struct efi_hd_dev_path *hd_node; + const struct efi_dev_path *path; + + if (efi_bs_call(handle_protocol, efi_table_attr(image, device_handle), + &device_path_guid, (void **)&path) != EFI_SUCCESS) + return; + + hd_node = efi_bli_find_hd_node(path); + if (!hd_node) + return; + + if (efi_snprintf(partuuid, ARRAY_SIZE(partuuid), "%pUl", + &hd_node->signature) != UUID_STRING_LEN) + return; + + set_efi_var(L"LoaderDevicePartUUID", &loader_entry_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(partuuid), partuuid); +} + +void efi_bli_set_variables(const efi_loaded_image_t *image) +{ + static efi_char16_t loader_info[] = L"Linux EFI stub " UTS_RELEASE; + unsigned long size = 0; + + if (!image) + return; + + if (get_efi_var(L"LoaderInfo", &loader_entry_guid, + NULL, &size, NULL) != EFI_NOT_FOUND) + return; + + if (set_efi_var(L"LoaderInfo", &loader_entry_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(loader_info), loader_info) != EFI_SUCCESS) + return; + + efi_bli_populate_loader_part_uuid(image); +} diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c index aa85e910fe59..efa3ab8ad1c6 100644 --- a/drivers/firmware/efi/libstub/efi-stub-entry.c +++ b/drivers/firmware/efi/libstub/efi-stub-entry.c @@ -35,13 +35,12 @@ struct sysfb_display_info *alloc_primary_display(void) efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_system_table_t *systab) { + static efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; efi_loaded_image_t *image; efi_status_t status; unsigned long image_addr; unsigned long image_size = 0; /* addr/point and size pairs for memory management*/ - char *cmdline_ptr = NULL; - efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; unsigned long reserve_addr = 0; unsigned long reserve_size = 0; @@ -63,6 +62,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, return status; } + char *cmdline_ptr __free(efi_pool) = NULL; status = efi_handle_cmdline(image, &cmdline_ptr); if (status != EFI_SUCCESS) return status; diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index f27f2e1f0019..8e43eb3f418b 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -12,6 +12,7 @@ #include <linux/efi.h> #include <linux/kernel.h> #include <linux/overflow.h> +#include <linux/ucs2_string.h> #include <asm/efi.h> #include <asm/setup.h> @@ -248,6 +249,7 @@ static efi_status_t efi_measure_tagged_event(unsigned long load_addr, unsigned long load_size, enum efistub_event_type event) { + static efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; union { efi_status_t (__efiapi *hash_log_extend_event)(void *, u64, efi_physical_addr_t, @@ -257,7 +259,6 @@ static efi_status_t efi_measure_tagged_event(unsigned long load_addr, struct efistub_measured_event *evt __free(efi_pool) = NULL; int size = struct_size(evt, tagged_event.tagged_event_data, events[event].event_data_len); - efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; efi_tcg2_protocol_t *tcg2 = NULL; union efistub_event ev; efi_status_t status; @@ -276,7 +277,7 @@ static efi_status_t efi_measure_tagged_event(unsigned long load_addr, method.hash_log_extend_event = (void *)efi_table_attr(tcg2, hash_log_extend_event); } else { - efi_guid_t cc_guid = EFI_CC_MEASUREMENT_PROTOCOL_GUID; + static efi_guid_t cc_guid = EFI_CC_MEASUREMENT_PROTOCOL_GUID; efi_cc_protocol_t *cc = NULL; efi_bs_call(locate_protocol, &cc_guid, NULL, (void **)&cc); @@ -334,81 +335,56 @@ char *efi_convert_cmdline(efi_loaded_image_t *image) { const efi_char16_t *options = efi_table_attr(image, load_options); u32 options_size = efi_table_attr(image, load_options_size); - int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ - unsigned long cmdline_addr = 0; - const efi_char16_t *s2; - bool in_quote = false; + unsigned long options_chars = 0; + unsigned long cmdline_bytes; efi_status_t status; - u32 options_chars; + char *cmdline_addr; if (options_size > 0) efi_measure_tagged_event((unsigned long)options, options_size, EFISTUB_EVT_LOAD_OPTIONS); efi_apply_loadoptions_quirk((const void **)&options, &options_size); - options_chars = options_size / sizeof(efi_char16_t); - - if (options) { - s2 = options; - while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { - efi_char16_t c = *s2++; - - if (c < 0x80) { - if (c == L'\0' || c == L'\n') - break; - if (c == L'"') - in_quote = !in_quote; - else if (!in_quote && isspace((char)c)) - safe_options_bytes = options_bytes; - - options_bytes++; - continue; - } - - /* - * Get the number of UTF-8 bytes corresponding to a - * UTF-16 character. - * The first part handles everything in the BMP. - */ - options_bytes += 2 + (c >= 0x800); - /* - * Add one more byte for valid surrogate pairs. Invalid - * surrogates will be replaced with 0xfffd and take up - * only 3 bytes. - */ - if ((c & 0xfc00) == 0xd800) { - /* - * If the very last word is a high surrogate, - * we must ignore it since we can't access the - * low surrogate. - */ - if (!options_chars) { - options_bytes -= 3; - } else if ((*s2 & 0xfc00) == 0xdc00) { - options_bytes++; - options_chars--; - s2++; - } - } - } - if (options_bytes >= COMMAND_LINE_SIZE) { - options_bytes = safe_options_bytes; - efi_err("Command line is too long: truncated to %d bytes\n", - options_bytes); - } - } + if (options) + options_chars = ucs2_strnlen(options, + options_size / sizeof(efi_char16_t)); - options_bytes++; /* NUL termination */ + /* Each UCS-2 char takes up at most 3 UTF-8 bytes */ + cmdline_bytes = min(3 * options_chars, COMMAND_LINE_SIZE - 1) + 3; - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, cmdline_bytes, (void **)&cmdline_addr); if (status != EFI_SUCCESS) return NULL; - snprintf((char *)cmdline_addr, options_bytes, "%.*ls", - options_bytes - 1, options); + if (ucs2_as_utf8_l(cmdline_addr, options, options_chars, + cmdline_bytes) >= COMMAND_LINE_SIZE) { + /* + * The output fills up the entire buffer, and may have been + * truncated. Work backwards through the buffer to find a safe + * truncation point (i.e., a blank character not inside a + * quoted string). + */ + int safe_pos[2] = {}; + int in_quote = 0; + + for (int i = COMMAND_LINE_SIZE - 1; i >= 0; i--) { + char c = cmdline_addr[i]; + + if (!c) + return cmdline_addr; + else if (c == '"') + in_quote ^= 1; + else if (!safe_pos[in_quote] && isspace(c)) + safe_pos[in_quote] = i; + } + + efi_err("Command line is too long: truncated to %d bytes\n", + safe_pos[in_quote]); + cmdline_addr[safe_pos[in_quote]] = '\0'; + } - return (char *)cmdline_addr; + return cmdline_addr; } /** @@ -552,7 +528,7 @@ static efi_status_t efi_load_initrd_dev_path(struct linux_efi_initrd *initrd, unsigned long max) { - efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; + static efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; efi_device_path_protocol_t *dp; efi_load_file2_protocol_t *lf2; efi_handle_t handle; @@ -614,7 +590,7 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image, unsigned long hard_limit, const struct linux_efi_initrd **out) { - efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; + static efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; efi_status_t status = EFI_SUCCESS; struct linux_efi_initrd initrd, *tbl; @@ -725,7 +701,7 @@ efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) void efi_remap_image(unsigned long image_base, unsigned alloc_size, unsigned long code_size) { - efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + static efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; efi_memory_attribute_protocol_t *memattr; efi_status_t status; u64 attr; diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 42d6073bcd06..6db10590f89b 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -79,10 +79,18 @@ err_free_primary_display: static void install_memreserve_table(void) { + static efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID; struct linux_efi_memreserve *rsv; - efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID; efi_status_t status; + /* + * If an earlier boot stage installed a memreserve table already, e.g., + * a kexec loader passing on the persistent reservations made by the + * previous kernel, use it as is: installing a new one would replace it. + */ + if (get_efi_config_table(memreserve_table_guid)) + return; + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv), (void **)&rsv); if (status != EFI_SUCCESS) { @@ -165,6 +173,7 @@ efi_status_t efi_stub_common(efi_handle_t handle, dpy = setup_primary_display(); efi_retrieve_eventlog(); + efi_bli_set_variables(image); /* Ask the firmware to clear memory on unclean shutdown */ efi_enable_reset_attack_mitigation(); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index fd91fc15ec81..f5eec8c12894 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -224,8 +224,8 @@ typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *); * efi_set_event_at() - add event to events array * * @events: array of UEFI events - * @ids: index where to put the event in the array - * @event: event to add to the aray + * @idx: index where to put the event in the array + * @event: event to add to the array * * boottime->wait_for_event() takes an array of events as input. * Provide a helper to set it up correctly for mixed mode. @@ -1072,17 +1072,21 @@ efi_status_t efi_random_alloc(unsigned long size, unsigned long align, int memory_type, unsigned long alloc_min, unsigned long alloc_max); +void efi_bli_set_variables(const efi_loaded_image_t *image); + efi_status_t efi_random_get_seed(void); efi_status_t check_platform_features(void); void *get_efi_config_table(efi_guid_t guid); -/* NOTE: These functions do not print a trailing newline after the string */ void efi_char16_puts(efi_char16_t *); -void efi_puts(const char *str); + +int efi_vsnprintf(efi_char16_t *buf, size_t size, const char *fmt, va_list ap, + bool crlf); __printf(1, 2) int efi_printk(char const *fmt, ...); +__printf(3, 4) int efi_snprintf(efi_char16_t *buf, size_t size, const char *fmt, ...); void efi_free(unsigned long size, unsigned long addr); DEFINE_FREE(efi_pool, void *, if (_T) efi_bs_call(free_pool, _T)); diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 23b3543d3041..f01450b49975 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -229,6 +229,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, u32 desc_ver; efi_status_t status; struct exit_boot_struct priv; + unsigned long fdt_size_allocated = 0; unsigned long fdt_addr = 0; unsigned long fdt_size = 0; @@ -251,12 +252,13 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, if (strstr(cmdline_ptr, "dtb=")) efi_err("Ignoring DTB from command line.\n"); } else { - status = efi_load_dtb(image, &fdt_addr, &fdt_size); + status = efi_load_dtb(image, &fdt_addr, &fdt_size_allocated); if (status != EFI_SUCCESS && status != EFI_NOT_READY) { efi_err("Failed to load device tree!\n"); goto fail; } + fdt_size = fdt_size_allocated; } if (fdt_addr) { @@ -334,7 +336,7 @@ fail_free_new_fdt: efi_free(MAX_FDT_SIZE, *new_fdt_addr); fail: - efi_free(fdt_size, fdt_addr); + efi_free(fdt_size_allocated, fdt_addr); if (!efi_novamap) efi_bs_call(free_pool, priv.runtime_map); diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index bd626d55dcbc..b2601e284695 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -29,6 +29,8 @@ */ #define EFI_READ_CHUNK_SIZE SZ_1M +static efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + struct finfo { efi_file_info_t info; efi_char16_t filename[MAX_FILENAME_SIZE]; @@ -39,7 +41,7 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, efi_file_protocol_t **handle, unsigned long *file_size) { - efi_guid_t info_guid = EFI_FILE_INFO_ID; + static efi_guid_t info_guid = EFI_FILE_INFO_ID; efi_file_protocol_t *fh; unsigned long info_sz; efi_status_t status; @@ -74,7 +76,6 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, static efi_status_t efi_open_volume(efi_loaded_image_t *image, efi_file_protocol_t **fh) { - efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; efi_simple_file_system_protocol_t *io; efi_status_t status; @@ -128,9 +129,8 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, static efi_status_t efi_open_device_path(efi_file_protocol_t **volume, struct finfo *fi) { - efi_guid_t text_to_dp_guid = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; + static efi_guid_t text_to_dp_guid = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; static efi_device_path_from_text_protocol_t *text_to_dp = NULL; - efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; efi_device_path_protocol_t *initrd_dp; efi_simple_file_system_protocol_t *io; struct efi_file_path_dev_path *fpath; diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 80dc8cfeb33e..b800a6c2290c 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -24,6 +24,8 @@ enum efi_cmdline_option { EFI_CMDLINE_LIST }; +static efi_guid_t graphics_output_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + static struct { enum efi_cmdline_option option; union { @@ -309,12 +311,12 @@ static u32 choose_mode_list(efi_graphics_output_protocol_t *gop) efi_status_t status; efi_printk("Available graphics modes are 0-%u\n", max_mode-1); - efi_puts(" * = current mode\n" - " - = unusable mode\n"); + efi_char16_puts(L" * = current mode\r\n" + " - = unusable mode\r\n"); choose_mode(gop, match_list, (void *)cur_mode); - efi_puts("\nPress any key to continue (or wait 10 seconds)\n"); + efi_char16_puts(L"\r\nPress any key to continue (or wait 10 seconds)\r\n"); status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key); if (status != EFI_SUCCESS && status != EFI_TIMEOUT) { efi_err("Unable to read key, continuing in 10 seconds\n"); @@ -425,6 +427,7 @@ static void setup_edid_info(struct edid_info *edid, u32 gop_size_of_edid, u8 *go static efi_handle_t find_handle_with_primary_gop(unsigned long num, const efi_handle_t handles[], efi_graphics_output_protocol_t **found_gop) { + static efi_guid_t console_out_device_guid = EFI_CONSOLE_OUT_DEVICE_GUID; efi_graphics_output_protocol_t *first_gop; efi_handle_t h, first_gop_handle; @@ -439,8 +442,7 @@ static efi_handle_t find_handle_with_primary_gop(unsigned long num, const efi_ha efi_graphics_output_mode_info_t *info; void *dummy = NULL; - status = efi_bs_call(handle_protocol, h, - &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, + status = efi_bs_call(handle_protocol, h, &graphics_output_guid, (void **)&gop); if (status != EFI_SUCCESS) continue; @@ -462,7 +464,7 @@ static efi_handle_t find_handle_with_primary_gop(unsigned long num, const efi_ha * don't bother looking any further. */ status = efi_bs_call(handle_protocol, h, - &EFI_CONSOLE_OUT_DEVICE_GUID, &dummy); + &console_out_device_guid, &dummy); if (status == EFI_SUCCESS) { if (found_gop) *found_gop = gop; @@ -480,6 +482,8 @@ static efi_handle_t find_handle_with_primary_gop(unsigned long num, const efi_ha efi_status_t efi_setup_graphics(struct screen_info *si, struct edid_info *edid) { + static efi_guid_t edid_active_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID; + static efi_guid_t edid_discovered_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID; efi_handle_t *handles __free(efi_pool) = NULL; efi_handle_t handle; efi_graphics_output_protocol_t *gop; @@ -487,8 +491,7 @@ efi_status_t efi_setup_graphics(struct screen_info *si, struct edid_info *edid) unsigned long num; status = efi_bs_call(locate_handle_buffer, EFI_LOCATE_BY_PROTOCOL, - &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, &num, - &handles); + &graphics_output_guid, NULL, &num, &handles); if (status != EFI_SUCCESS) return status; @@ -510,14 +513,14 @@ efi_status_t efi_setup_graphics(struct screen_info *si, struct edid_info *edid) u32 gop_size_of_edid = 0; u8 *gop_edid = NULL; - status = efi_bs_call(handle_protocol, handle, &EFI_EDID_ACTIVE_PROTOCOL_GUID, + status = efi_bs_call(handle_protocol, handle, &edid_active_guid, (void **)&active_edid); if (status == EFI_SUCCESS) { gop_size_of_edid = efi_table_attr(active_edid, size_of_edid); gop_edid = efi_table_attr(active_edid, edid); } else { status = efi_bs_call(handle_protocol, handle, - &EFI_EDID_DISCOVERED_PROTOCOL_GUID, + &edid_discovered_guid, (void **)&discovered_edid); if (status == EFI_SUCCESS) { gop_size_of_edid = efi_table_attr(discovered_edid, size_of_edid); diff --git a/drivers/firmware/efi/libstub/kaslr.c b/drivers/firmware/efi/libstub/kaslr.c index 4bc963e999eb..8f8dc335b3b3 100644 --- a/drivers/firmware/efi/libstub/kaslr.c +++ b/drivers/firmware/efi/libstub/kaslr.c @@ -18,7 +18,7 @@ */ u32 efi_kaslr_get_phys_seed(efi_handle_t image_handle) { - efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; + static efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; void *p; if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE)) @@ -59,8 +59,7 @@ static bool check_image_region(u64 base, u64 size) { struct efi_boot_memmap *map __free(efi_pool) = NULL; efi_status_t status; - bool ret = false; - int map_offset; + unsigned long map_offset; status = efi_get_memory_map(&map, false); if (status != EFI_SUCCESS) @@ -74,18 +73,16 @@ static bool check_image_region(u64 base, u64 size) * Find the region that covers base, and return whether * it covers base+size bytes. */ - if (base >= md->phys_addr && base < end) { - ret = (base + size) <= end; - break; - } + if (base >= md->phys_addr && base < end) + return (base + size) <= end; } - return ret; + return false; } /** * efi_kaslr_relocate_kernel() - Relocate the kernel (random if KASLR enabled) - * @image_addr: Pointer to the current kernel location + * @image_addr: Pointer to the current kernel location * @reserve_addr: Pointer to the relocated kernel location * @reserve_size: Size of the relocated kernel * @kernel_size: Size of the text + data @@ -94,8 +91,8 @@ static bool check_image_region(u64 base, u64 size) * @phys_seed: Random seed used for the relocation * * If KASLR is not enabled, this function relocates the kernel to a fixed - * address (or leave it as its current location). If KASLR is enabled, the - * kernel physical location is randomized using the seed in parameter. + * address or leaves it at its current location. If KASLR is enabled, the + * kernel's physical location is randomized using the supplied seed. * * Return: status code, EFI_SUCCESS if relocation is successful */ diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 59f3f83de50c..fec561e3a792 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -20,10 +20,10 @@ efi_status_t efi_get_memory_map(struct efi_boot_memmap **map, bool install_cfg_tbl) { + static efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; struct efi_boot_memmap tmp, *m __free(efi_pool) = NULL; int memtype = install_cfg_tbl ? EFI_ACPI_RECLAIM_MEMORY : EFI_LOADER_DATA; - efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; efi_status_t status; unsigned long size; diff --git a/drivers/firmware/efi/libstub/pci.c b/drivers/firmware/efi/libstub/pci.c index 1dccf77958d3..5daa7a0a0e87 100644 --- a/drivers/firmware/efi/libstub/pci.c +++ b/drivers/firmware/efi/libstub/pci.c @@ -15,7 +15,7 @@ void efi_pci_disable_bridge_busmaster(void) { - efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; + static efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; efi_handle_t *pci_handle __free(efi_pool) = NULL; unsigned long pci_handle_num; efi_handle_t handle; diff --git a/drivers/firmware/efi/libstub/printk.c b/drivers/firmware/efi/libstub/printk.c index bc599212c05d..0a18cfe32528 100644 --- a/drivers/firmware/efi/libstub/printk.c +++ b/drivers/firmware/efi/libstub/printk.c @@ -23,98 +23,20 @@ void efi_char16_puts(efi_char16_t *str) output_string, str); } -static -u32 utf8_to_utf32(const u8 **s8) -{ - u32 c32; - u8 c0, cx; - size_t clen, i; - - c0 = cx = *(*s8)++; - /* - * The position of the most-significant 0 bit gives us the length of - * a multi-octet encoding. - */ - for (clen = 0; cx & 0x80; ++clen) - cx <<= 1; - /* - * If the 0 bit is in position 8, this is a valid single-octet - * encoding. If the 0 bit is in position 7 or positions 1-3, the - * encoding is invalid. - * In either case, we just return the first octet. - */ - if (clen < 2 || clen > 4) - return c0; - /* Get the bits from the first octet. */ - c32 = cx >> clen--; - for (i = 0; i < clen; ++i) { - /* Trailing octets must have 10 in most significant bits. */ - cx = (*s8)[i] ^ 0x80; - if (cx & 0xc0) - return c0; - c32 = (c32 << 6) | cx; - } - /* - * Check for validity: - * - The character must be in the Unicode range. - * - It must not be a surrogate. - * - It must be encoded using the correct number of octets. - */ - if (c32 > 0x10ffff || - (c32 & 0xf800) == 0xd800 || - clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) - return c0; - *s8 += clen; - return c32; -} - -/** - * efi_puts() - Write a UTF-8 encoded string to the console - * @str: UTF-8 encoded string - */ -void efi_puts(const char *str) -{ - efi_char16_t buf[128]; - size_t pos = 0, lim = ARRAY_SIZE(buf); - const u8 *s8 = (const u8 *)str; - u32 c32; - - while (*s8) { - if (*s8 == '\n') - buf[pos++] = L'\r'; - c32 = utf8_to_utf32(&s8); - if (c32 < 0x10000) { - /* Characters in plane 0 use a single word. */ - buf[pos++] = c32; - } else { - /* - * Characters in other planes encode into a surrogate - * pair. - */ - buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); - buf[pos++] = 0xdc00 + (c32 & 0x3ff); - } - if (*s8 == '\0' || pos >= lim - 2) { - buf[pos] = L'\0'; - efi_char16_puts(buf); - pos = 0; - } - } -} - /** * efi_printk() - Print a kernel message * @fmt: format string * * The first letter of the format string is used to determine the logging level * of the message. If the level is less then the current EFI logging level, the - * message is suppressed. The message will be truncated to 255 bytes. + * message is suppressed. The message will be truncated to 255 characters + * (ignoring surrogates). * * Return: number of printed characters */ int efi_printk(const char *fmt, ...) { - char printf_buf[256]; + efi_char16_t printf_buf[256]; va_list args; int printed; int loglevel = printk_get_level(fmt); @@ -136,17 +58,18 @@ int efi_printk(const char *fmt, ...) return 0; if (loglevel >= 0) - efi_puts("EFI stub: "); + efi_char16_puts(L"EFI stub: "); fmt = printk_skip_level(fmt); va_start(args, fmt); - printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); + printed = efi_vsnprintf(printf_buf, ARRAY_SIZE(printf_buf), fmt, args, + true); va_end(args); - efi_puts(printf_buf); - if (printed >= sizeof(printf_buf)) { - efi_puts("[Message truncated]\n"); + efi_char16_puts(printf_buf); + if (printed >= ARRAY_SIZE(printf_buf)) { + efi_char16_puts(L"[Message truncated]\r\n"); return -1; } diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index 7109b8a2dcba..d63262d36e47 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -8,6 +8,8 @@ #include "efistub.h" +static efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; + typedef union efi_rng_protocol efi_rng_protocol_t; union efi_rng_protocol { @@ -38,7 +40,6 @@ union efi_rng_protocol { */ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) { - efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; efi_status_t status; efi_rng_protocol_t *rng = NULL; @@ -64,9 +65,8 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) */ efi_status_t efi_random_get_seed(void) { - efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; - efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; - efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; + static efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; + static efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; struct linux_efi_random_seed *prev_seed, *seed = NULL; int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE; unsigned long nv_seed_size = 0, offset = 0; diff --git a/drivers/firmware/efi/libstub/riscv-stub.c b/drivers/firmware/efi/libstub/riscv-stub.c index e7d9204baee3..725b634c5179 100644 --- a/drivers/firmware/efi/libstub/riscv-stub.c +++ b/drivers/firmware/efi/libstub/riscv-stub.c @@ -37,17 +37,14 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, kernel_codesize = __init_text_end - _start; kernel_memsize = kernel_size + (_end - _edata); *image_addr = (unsigned long)_start; - *image_size = kernel_memsize; - *reserve_size = *image_size; + *reserve_size = kernel_memsize; status = efi_kaslr_relocate_kernel(image_addr, reserve_addr, reserve_size, kernel_size, kernel_codesize, kernel_memsize, efi_kaslr_get_phys_seed(image_handle)); - if (status != EFI_SUCCESS) { + if (status != EFI_SUCCESS) efi_err("Failed to relocate kernel\n"); - *image_size = 0; - } return status; } diff --git a/drivers/firmware/efi/libstub/riscv.c b/drivers/firmware/efi/libstub/riscv.c index f66f33ceb99e..49db47c05bfe 100644 --- a/drivers/firmware/efi/libstub/riscv.c +++ b/drivers/firmware/efi/libstub/riscv.c @@ -45,7 +45,7 @@ static int get_boot_hartid_from_fdt(void) static efi_status_t get_boot_hartid_from_efi(void) { - efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; + static efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; struct riscv_efi_boot_protocol *boot_protocol; efi_status_t status; diff --git a/drivers/firmware/efi/libstub/smbios.c b/drivers/firmware/efi/libstub/smbios.c index f31410d7e7e1..efbbfc3c2c0d 100644 --- a/drivers/firmware/efi/libstub/smbios.c +++ b/drivers/firmware/efi/libstub/smbios.c @@ -35,12 +35,13 @@ union efi_smbios_protocol { const struct efi_smbios_record *efi_get_smbios_record(u8 type) { + static efi_guid_t smbios_guid = EFI_SMBIOS_PROTOCOL_GUID; struct efi_smbios_record *record; efi_smbios_protocol_t *smbios; efi_status_t status; u16 handle = 0xfffe; - status = efi_bs_call(locate_protocol, &EFI_SMBIOS_PROTOCOL_GUID, NULL, + status = efi_bs_call(locate_protocol, &smbios_guid, NULL, (void **)&smbios) ?: efi_call_proto(smbios, get_next, &handle, &type, &record, NULL); if (status != EFI_SUCCESS) diff --git a/drivers/firmware/efi/libstub/tpm.c b/drivers/firmware/efi/libstub/tpm.c index a5c6c4f163fc..73f001114732 100644 --- a/drivers/firmware/efi/libstub/tpm.c +++ b/drivers/firmware/efi/libstub/tpm.c @@ -28,8 +28,8 @@ static const efi_char16_t efi_MemoryOverWriteRequest_name[] = */ void efi_enable_reset_attack_mitigation(void) { + static efi_guid_t var_guid = MEMORY_ONLY_RESET_CONTROL_GUID; u8 val = 1; - efi_guid_t var_guid = MEMORY_ONLY_RESET_CONTROL_GUID; efi_status_t status; unsigned long datasize = 0; @@ -52,7 +52,7 @@ static void efi_retrieve_tcg2_eventlog(int version, efi_physical_addr_t log_loca efi_bool_t truncated, struct efi_tcg2_final_events_table *final_events_table) { - efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID; + static efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID; efi_status_t status; struct linux_efi_tpm_eventlog *log_tbl = NULL; unsigned long first_entry_addr, last_entry_addr; @@ -150,9 +150,9 @@ err_free: void efi_retrieve_eventlog(void) { + static efi_guid_t tpm2_guid = EFI_TCG2_PROTOCOL_GUID; struct efi_tcg2_final_events_table *final_events_table = NULL; efi_physical_addr_t log_location = 0, log_last_entry = 0; - efi_guid_t tpm2_guid = EFI_TCG2_PROTOCOL_GUID; int version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; efi_tcg2_protocol_t *tpm2 = NULL; efi_bool_t truncated; @@ -173,7 +173,7 @@ void efi_retrieve_eventlog(void) get_efi_config_table(EFI_TCG2_FINAL_EVENTS_TABLE_GUID); } } else { - efi_guid_t cc_guid = EFI_CC_MEASUREMENT_PROTOCOL_GUID; + static efi_guid_t cc_guid = EFI_CC_MEASUREMENT_PROTOCOL_GUID; efi_cc_protocol_t *cc = NULL; status = efi_bs_call(locate_protocol, &cc_guid, NULL, (void **)&cc); diff --git a/drivers/firmware/efi/libstub/unaccepted_memory.c b/drivers/firmware/efi/libstub/unaccepted_memory.c index 757dbe734a47..2a7eac7fef86 100644 --- a/drivers/firmware/efi/libstub/unaccepted_memory.c +++ b/drivers/firmware/efi/libstub/unaccepted_memory.c @@ -9,7 +9,7 @@ struct efi_unaccepted_memory *unaccepted_table; efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc, struct efi_boot_memmap *map) { - efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID; + static efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID; u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size; efi_status_t status; int i; diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index 71c71c222346..7fd6589f44ef 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -14,10 +14,14 @@ #include <linux/compiler.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <linux/kernel.h> #include <linux/limits.h> #include <linux/string.h> #include <linux/types.h> +#include <linux/ucs2_string.h> + +#include "efistub.h" static int skip_atoi(const char **s) @@ -109,6 +113,9 @@ char *put_dec(char *end, unsigned long long n) return p; } +/* we are called with base 8, 10 or 16, only, thus don't need "G..." */ +static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + static char *number(char *end, unsigned long long num, int base, char locase) { @@ -117,9 +124,6 @@ char *number(char *end, unsigned long long num, int base, char locase) * produces same digits or (maybe lowercased) letters */ - /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ - static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ - switch (base) { case 10: if (num != 0) @@ -140,6 +144,29 @@ char *number(char *end, unsigned long long num, int base, char locase) return end; } +static char *guid_to_str(const efi_guid_t *guid, char *out, char locase) +{ + static const u8 guid_index[UUID_SIZE] = { + 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15, + }; + + for (int i = 0, p = 0; i < ARRAY_SIZE(guid_index); i++) { + u8 byte = guid->b[guid_index[i]]; + + out[p++] = locase | digits[byte >> 4]; + out[p++] = locase | digits[byte & 0xf]; + + switch (i) { + case 3: + case 5: + case 7: + case 9: + out[p++] = '-'; + } + } + return out; +} + #define ZEROPAD 1 /* pad with zero */ #define SIGN 2 /* unsigned/signed long */ #define PLUS 4 /* show plus */ @@ -239,58 +266,6 @@ char get_sign(long long *num, int flags) return 0; } -static -size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen) -{ - size_t len, clen; - - for (len = 0; len < maxlen && *s16; len += clen) { - u16 c0 = *s16++; - - /* First, get the length for a BMP character */ - clen = 1 + (c0 >= 0x80) + (c0 >= 0x800); - if (len + clen > maxlen) - break; - /* - * If this is a high surrogate, and we're already at maxlen, we - * can't include the character if it's a valid surrogate pair. - * Avoid accessing one extra word just to check if it's valid - * or not. - */ - if ((c0 & 0xfc00) == 0xd800) { - if (len + clen == maxlen) - break; - if ((*s16 & 0xfc00) == 0xdc00) { - ++s16; - ++clen; - } - } - } - - return len; -} - -static -u32 utf16_to_utf32(const u16 **s16) -{ - u16 c0, c1; - - c0 = *(*s16)++; - /* not a surrogate */ - if ((c0 & 0xf800) != 0xd800) - return c0; - /* invalid: low surrogate instead of high */ - if (c0 & 0x0400) - return 0xfffd; - c1 = **s16; - /* invalid: missing low surrogate */ - if ((c1 & 0xfc00) != 0xdc00) - return 0xfffd; - /* valid surrogate pair */ - ++(*s16); - return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1; -} - #define PUTC(c) \ do { \ if (pos < size) \ @@ -298,10 +273,10 @@ do { \ ++pos; \ } while (0); -int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) +int efi_vsnprintf(efi_char16_t *buf, size_t size, const char *fmt, va_list ap, + bool crlf) { - /* The maximum space required is to print a 64-bit number in octal */ - char tmp[(sizeof(unsigned long long) * 8 + 2) / 3]; + char tmp[UUID_STRING_LEN]; char *tmp_end = &tmp[ARRAY_SIZE(tmp)]; long long num; int base; @@ -336,6 +311,8 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) for (pos = 0; *fmt; ++fmt) { if (*fmt != '%' || *++fmt == '%') { + if (crlf && *fmt == '\n') + PUTC('\r'); PUTC(*fmt); continue; } @@ -400,7 +377,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) else if (qualifier == 'l') { wstring: flags |= WIDE; - precision = len = utf16s_utf8nlen((const u16 *)s, precision); + precision = len = ucs2_strnlen((const u16 *)s, precision); goto output; } precision = len = strnlen(s, precision); @@ -412,6 +389,14 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) break; case 'p': + if (fmt[1] == 'U' && (fmt[2] | 0x20) == 'l') { + flags &= LEFT; + s = guid_to_str(va_arg(args, efi_guid_t *), tmp, fmt[2] & 0x20); + precision = len = UUID_STRING_LEN; + fmt += 2; + goto output; + } + if (precision < 0) precision = 2 * sizeof(void *); fallthrough; @@ -505,36 +490,9 @@ output: if (flags & WIDE) { const u16 *ws = (const u16 *)s; - while (len-- > 0) { - u32 c32 = utf16_to_utf32(&ws); - u8 *s8; - size_t clen; - - if (c32 < 0x80) { - PUTC(c32); - continue; - } - - /* Number of trailing octets */ - clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000); - - len -= clen; - s8 = (u8 *)&buf[pos]; - - /* Avoid writing partial character */ - PUTC('\0'); - pos += clen; - if (pos >= size) - continue; - - /* Set high bits of leading octet */ - *s8 = (0xf00 >> 1) >> clen; - /* Write trailing octets in reverse order */ - for (s8 += clen; clen; --clen, c32 >>= 6) - *s8-- = 0x80 | (c32 & 0x3f); - /* Set low bits of leading octet */ - *s8 |= c32; - } + if (pos < size) + memcpy(&buf[pos], ws, min(len, size - pos) * sizeof(*ws)); + pos += len; } else { while (len-- > 0) PUTC(*s++); @@ -552,13 +510,13 @@ fail: return pos; } -int snprintf(char *buf, size_t size, const char *fmt, ...) +int efi_snprintf(efi_char16_t *buf, size_t size, const char *fmt, ...) { va_list args; int i; va_start(args, fmt); - i = vsnprintf(buf, size, fmt, args); + i = efi_vsnprintf(buf, size, fmt, args, false); va_end(args); return i; } diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 80556a7e7552..253dd3ba8fab 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -116,9 +116,9 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) */ static void setup_efi_pci(struct boot_params *params) { + static efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; efi_status_t status; efi_handle_t *pci_handle __free(efi_pool) = NULL; - efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; struct setup_data *data; unsigned long num; efi_handle_t h; @@ -157,7 +157,7 @@ static void setup_efi_pci(struct boot_params *params) static void retrieve_apple_device_properties(struct boot_params *boot_params) { - efi_guid_t guid = APPLE_PROPERTIES_PROTOCOL_GUID; + static efi_guid_t guid = APPLE_PROPERTIES_PROTOCOL_GUID; struct setup_data *data, *new; efi_status_t status; u32 size = 0; @@ -337,6 +337,7 @@ static bool apple_match_product_name(void) static void apple_set_os(void) { + static efi_guid_t apple_set_os_guid = APPLE_SET_OS_PROTOCOL_GUID; struct { unsigned long version; efi_status_t (__efiapi *set_os_version)(const char *); @@ -347,7 +348,7 @@ static void apple_set_os(void) if (!efi_is_64bit() || !apple_match_product_name()) return; - status = efi_bs_call(locate_protocol, &APPLE_SET_OS_PROTOCOL_GUID, NULL, + status = efi_bs_call(locate_protocol, &apple_set_os_guid, NULL, (void **)&set_os); if (status != EFI_SUCCESS) return; @@ -446,7 +447,7 @@ efi_status_t efi_adjust_memory_range_protection(unsigned long start, static void setup_unaccepted_memory(void) { - efi_guid_t mem_acceptance_proto = OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL_GUID; + static efi_guid_t mem_acceptance_proto = OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL_GUID; sev_memory_acceptance_protocol_t *proto; efi_status_t status; @@ -509,7 +510,7 @@ static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status) static efi_status_t efi_allocate_bootparams(efi_handle_t handle, struct boot_params **bp) { - efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; + static efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; struct boot_params *boot_params; struct setup_header *hdr; efi_status_t status; @@ -650,7 +651,7 @@ setup_e820(struct boot_params *params, struct setup_data *e820ext, u32 e820ext_s } if (nr_entries == ARRAY_SIZE(params->e820_table)) { - u32 need = (nr_desc - i) * sizeof(struct e820_entry) + + u32 need = (nr_desc - i) * sizeof(struct boot_e820_entry) + sizeof(struct setup_data); if (!e820ext || e820ext_size < need) @@ -686,7 +687,7 @@ static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext, unsigned long size; size = sizeof(struct setup_data) + - sizeof(struct e820_entry) * nr_desc; + sizeof(struct boot_e820_entry) * nr_desc; if (*e820ext) { efi_bs_call(free_pool, *e820ext); @@ -911,12 +912,13 @@ static void __noreturn enter_kernel(unsigned long kernel_addr, * return. On failure, it will exit to the firmware via efi_exit() instead of * returning. */ +MIXED_EXTERN void __noreturn efi_stub_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg, struct boot_params *boot_params) { - efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + static efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; const struct linux_efi_initrd *initrd = NULL; unsigned long kernel_entry; struct setup_header *hdr; @@ -1016,6 +1018,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle, efi_random_get_seed(); efi_retrieve_eventlog(); + efi_bli_set_variables(image); setup_graphics(boot_params); @@ -1053,6 +1056,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, } #ifdef CONFIG_EFI_HANDOVER_PROTOCOL +MIXED_EXTERN void efi_handover_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg, struct boot_params *boot_params) { diff --git a/drivers/firmware/efi/libstub/x86-stub.h b/drivers/firmware/efi/libstub/x86-stub.h index 1c20e99a6494..d955ac7cc694 100644 --- a/drivers/firmware/efi/libstub/x86-stub.h +++ b/drivers/firmware/efi/libstub/x86-stub.h @@ -8,6 +8,22 @@ extern const u16 trampoline_ljmp_imm_offset; efi_status_t efi_adjust_memory_range_protection(unsigned long start, unsigned long size); +#ifdef CONFIG_EFI_MIXED +#define MIXED_EXTERN asmlinkage + +struct boot_params; + +asmlinkage void __noreturn efi_stub_entry(efi_handle_t handle, + efi_system_table_t *sys_table_arg, + struct boot_params *boot_params); +asmlinkage void efi_handover_entry(efi_handle_t handle, + efi_system_table_t *sys_table_arg, + struct boot_params *boot_params); + +#else +#define MIXED_EXTERN static +#endif + #ifdef CONFIG_X86_64 efi_status_t efi_setup_5level_paging(void); void efi_5level_switch(void); diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 4b76f74c56da..4a5ffe164873 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -34,6 +34,7 @@ struct sysfb_display_info *alloc_primary_display(void) asmlinkage efi_status_t __efiapi efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) { + static efi_guid_t loaded_image_guid = LOADED_IMAGE_PROTOCOL_GUID; char *cmdline_ptr __free(efi_pool) = NULL; unsigned long image_base, alloc_size; efi_loaded_image_t *image; @@ -42,7 +43,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) WRITE_ONCE(efi_system_table, systab); status = efi_bs_call(handle_protocol, handle, - &LOADED_IMAGE_PROTOCOL_GUID, (void **)&image); + &loaded_image_guid, (void **)&image); if (status != EFI_SUCCESS) { efi_err("Failed to locate parent's loaded image protocol\n"); return status; diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 2344b9d1e81f..eea4f32e6474 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -127,6 +127,12 @@ struct efi_runtime_work efi_rts_work; #define EFI_RTS_TIMEOUT (120 * HZ) /* + * Set when __efi_queue_work() gives up waiting for a call that is wedged in + * firmware. EFI runtime services are disabled for good at that point. + */ +static bool efi_rts_abandoned; + +/* * efi_queue_work: Queue EFI runtime service call and wait for completion * @_rts: EFI runtime service function identifier * @_args: Arguments to pass to the EFI runtime service @@ -331,12 +337,42 @@ static void __nocfi efi_call_rts(struct work_struct *work) * function identifier into efi_rts_work->efi_rts_id */ pr_err("Requested executing invalid EFI Runtime Service.\n"); + break; + +#if IS_ENABLED(CONFIG_LKDTM) + case EFI_LKDTM_RUNTIME_RO_TEST: + /* + * Call GetTime() with the output argument pointing to + * executable memory, which should be mapped read-only in the + * EFI page tables. + */ + status = efi_call_virt(get_time, + (void *)efi.runtime->set_virtual_address_map, + NULL); + break; + case EFI_LKDTM_RUNTIME_NX_TEST: + /* + * Call efi.runtime, which points to a data object in a EFI + * runtime region, as if it were a function pointer. + */ + { + struct { efi_status_t (*f)(int); } *s = (void *)&efi.runtime; + + status = arch_efi_call_virt(s, f, 0); + } + break; +#endif } efi_call_virt_check_flags(flags, efi_rts_work.caller); arch_efi_call_virt_teardown(); - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + /* + * EFI runtime services may also have been disabled because the + * firmware faulted, but the caller is still waiting for us in that + * case. Only park the worker if the caller has given up on it. + */ + if (READ_ONCE(efi_rts_abandoned)) efi_rts_park_worker(); efi_rts_work.status = status; @@ -373,6 +409,7 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id, EFI_RTS_TIMEOUT)) { pr_err("EFI runtime service %d wedged in firmware; disabling EFI runtime services\n", id); + WRITE_ONCE(efi_rts_abandoned, true); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return EFI_ABORTED; } @@ -612,6 +649,34 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, return status; } +#if IS_ENABLED(CONFIG_LKDTM) +static efi_status_t efi_lkdtm_rt_test(enum efi_rts_ids id) +{ + efi_status_t status; + + if (!efi_enabled(EFI_RUNTIME_SERVICES) || !efi_enabled(EFI_MEM_ATTR)) + return EFI_NOT_READY; + + switch (id) { + case EFI_LKDTM_RUNTIME_NX_TEST: + break; + case EFI_LKDTM_RUNTIME_RO_TEST: + if (efi.get_time && + efi_rt_services_supported(EFI_RT_SUPPORTED_GET_TIME)) + break; + fallthrough; + default: + return EFI_UNSUPPORTED; + } + + if (down_interruptible(&efi_runtime_lock)) + return EFI_NOT_READY; + status = __efi_queue_work(id, NULL); + up(&efi_runtime_lock); + return status; +} +#endif + void __init efi_native_runtime_setup(void) { efi.get_time = virt_efi_get_time; @@ -628,6 +693,10 @@ void __init efi_native_runtime_setup(void) efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nb; efi.update_capsule = virt_efi_update_capsule; efi.query_capsule_caps = virt_efi_query_capsule_caps; + +#if IS_ENABLED(CONFIG_LKDTM) + efi.lkdtm_rt_test = efi_lkdtm_rt_test; +#endif } #ifdef CONFIG_ACPI_PRMT @@ -652,3 +721,31 @@ void efi_runtime_assert_lock_held(void) { WARN_ON(efi_runtime_lock_owner != current); } + +efi_status_t efi_runtime_set_enable_flag(bool enable) +{ + static bool runtime_disabled; + efi_status_t ret = EFI_NOT_READY; + + if (down_interruptible(&efi_runtime_lock)) + return EFI_ABORTED; + + if (enable) { + /* It could be enabled only if it is disabled here */ + if (runtime_disabled) { + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); + runtime_disabled = false; + ret = EFI_SUCCESS; + } + } else { + if (efi_enabled(EFI_RUNTIME_SERVICES)) { + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); + runtime_disabled = true; + ret = EFI_SUCCESS; + } + } + + up(&efi_runtime_lock); + + return ret; +} diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index d82c47fb9f03..2761685cf4f8 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -841,6 +841,37 @@ static void __maybe_unused lkdtm_EFI_RUNTIME_CRASH(void) status); } +static void efi_rt_test(enum efi_rts_ids id, const char *desc) +{ + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + pr_err("XFAIL: EFI runtime services have been disabled\n"); + return; + } else if (!efi_enabled(EFI_MEM_ATTR)) { + pr_err("XFAIL: EFI memory attributes table not provided by firmware\n"); + return; + } else if (!efi.lkdtm_rt_test) { + pr_err("XFAIL: Only supported with native EFI runtime services\n"); + return; + } + + efi_status_t status = efi.lkdtm_rt_test(id); + if (status == EFI_UNSUPPORTED) + pr_err("XFAIL: EFI runtime service not implemented by firmware\n"); + else if (status != EFI_ABORTED || efi_enabled(EFI_RUNTIME_SERVICES)) + pr_err("FAIL: EFI %s memory not caught (%#lx)\n", + desc, status); +} + +static void __maybe_unused lkdtm_EFI_RUNTIME_WRITE_TO_RO(void) +{ + efi_rt_test(EFI_LKDTM_RUNTIME_RO_TEST, "write to RO"); +} + +static void __maybe_unused lkdtm_EFI_RUNTIME_BRANCH_TO_NX(void) +{ + efi_rt_test(EFI_LKDTM_RUNTIME_NX_TEST, "branch to NX"); +} + static struct crashtype crashtypes[] = { CRASHTYPE(PANIC), CRASHTYPE(PANIC_STOP_IRQOFF), @@ -876,6 +907,8 @@ static struct crashtype crashtypes[] = { CRASHTYPE(CORRUPT_PAC), #ifdef CONFIG_EFI CRASHTYPE(EFI_RUNTIME_CRASH), + CRASHTYPE(EFI_RUNTIME_WRITE_TO_RO), + CRASHTYPE(EFI_RUNTIME_BRANCH_TO_NX), #endif }; diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h index f913b6824289..f5c1a25be996 100644 --- a/fs/efivarfs/internal.h +++ b/fs/efivarfs/internal.h @@ -11,6 +11,7 @@ struct efivarfs_mount_opts { kuid_t uid; kgid_t gid; + bool nostatfs; }; struct efivarfs_fs_info { diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 8d33f11db2a1..12746ced6dc6 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -74,6 +74,8 @@ static int efivarfs_show_options(struct seq_file *m, struct dentry *root) if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) seq_printf(m, ",gid=%u", from_kgid_munged(&init_user_ns, opts->gid)); + if (opts->nostatfs) + seq_puts(m, ",nostatfs"); return 0; } @@ -82,13 +84,18 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf) const u32 attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + struct efivarfs_fs_info *sfi = dentry->d_sb->s_fs_info; u64 storage_space, remaining_space, max_variable_size; u64 id = huge_encode_dev(dentry->d_sb->s_dev); efi_status_t status; - /* Some UEFI firmware does not implement QueryVariableInfo() */ + /* + * With nostatfs, or on firmware that does not implement + * QueryVariableInfo(), report zero used/available. + */ storage_space = remaining_space = 0; - if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) { + if (!sfi->mount_opts.nostatfs && + efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) { static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 5); static u64 storage, remaining; static DEFINE_SPINLOCK(lock); @@ -323,12 +330,13 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, } enum { - Opt_uid, Opt_gid, + Opt_uid, Opt_gid, Opt_statfs, }; static const struct fs_parameter_spec efivarfs_parameters[] = { fsparam_uid("uid", Opt_uid), fsparam_gid("gid", Opt_gid), + fsparam_flag_no("statfs", Opt_statfs), {}, }; @@ -350,6 +358,9 @@ static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *para case Opt_gid: opts->gid = result.gid; break; + case Opt_statfs: + opts->nostatfs = result.negated; + break; default: return -EINVAL; } @@ -402,11 +413,17 @@ static int efivarfs_get_tree(struct fs_context *fc) static int efivarfs_reconfigure(struct fs_context *fc) { + struct efivarfs_fs_info *sfi = fc->root->d_sb->s_fs_info; + struct efivarfs_fs_info *new_sfi = fc->s_fs_info; + if (!efivar_supports_writes() && !(fc->sb_flags & SB_RDONLY)) { pr_err("Firmware does not support SetVariableRT. Can not remount with rw\n"); return -EINVAL; } + /* statfs()/show_options() read nostatfs locklessly; benign race. */ + data_race(sfi->mount_opts.nostatfs = new_sfi->mount_opts.nostatfs); + return 0; } @@ -524,8 +541,17 @@ static int efivarfs_init_fs_context(struct fs_context *fc) if (!sfi) return -ENOMEM; - sfi->mount_opts.uid = GLOBAL_ROOT_UID; - sfi->mount_opts.gid = GLOBAL_ROOT_GID; + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { + /* nostatfs is writable via remount; keep it if not respecified. */ + struct efivarfs_fs_info *old = fc->root->d_sb->s_fs_info; + + sfi->mount_opts.nostatfs = old->mount_opts.nostatfs; + } else { + sfi->mount_opts.uid = GLOBAL_ROOT_UID; + sfi->mount_opts.gid = GLOBAL_ROOT_GID; + /* QueryVariableInfo() stalls the CPU; default nostatfs on PREEMPT_RT. */ + sfi->mount_opts.nostatfs = IS_ENABLED(CONFIG_PREEMPT_RT); + } fc->s_fs_info = sfi; fc->ops = &efivarfs_context_ops; diff --git a/include/linux/efi.h b/include/linux/efi.h index aa15ff88539b..cc1df1577d5f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -631,6 +631,8 @@ typedef struct { extern unsigned long __ro_after_init efi_rng_seed; /* RNG Seed table */ +enum efi_rts_ids; + /* * All runtime access to EFI goes through this structure: */ @@ -650,6 +652,7 @@ extern struct efi { unsigned long mokvar_table; /* MOK variable config table */ unsigned long coco_secret; /* Confidential computing secret table */ unsigned long unaccepted; /* Unaccepted memory table */ + unsigned long mem_reserve; /* Persistent memory reservations */ efi_get_time_t *get_time; efi_set_time_t *set_time; @@ -666,6 +669,10 @@ extern struct efi { efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_reset_system_t *reset_system; +#if IS_ENABLED(CONFIG_LKDTM) + efi_status_t (*lkdtm_rt_test)(enum efi_rts_ids id); +#endif + struct efi_memory_map memmap; unsigned long flags; } efi; @@ -957,6 +964,17 @@ extern int efi_status_to_err(efi_status_t status); #define EFI_DEV_END_INSTANCE 0x01 #define EFI_DEV_END_ENTIRE 0xFF +enum efi_hd_partition_format { + EFI_HD_PARTITION_FORMAT_MBR = 1, + EFI_HD_PARTITION_FORMAT_GPT, +}; + +enum efi_hd_signature_type { + EFI_HD_SIGNATURE_TYPE_NONE, + EFI_HD_SIGNATURE_TYPE_MBR, + EFI_HD_SIGNATURE_TYPE_GUID, +}; + struct efi_generic_dev_path { u8 type; u8 sub_type; @@ -988,6 +1006,16 @@ struct efi_rel_offset_dev_path { u64 ending_offset; } __packed; +struct efi_hd_dev_path { + struct efi_generic_dev_path header; + u32 partition_number; + u64 partition_start; + u64 partition_size; + efi_guid_t signature; + u8 partition_format; + u8 signature_type; +} __packed; + struct efi_mem_mapped_dev_path { struct efi_generic_dev_path header; u32 memory_type; @@ -1007,6 +1035,7 @@ struct efi_dev_path { struct efi_pci_dev_path pci; struct efi_vendor_dev_path vendor; struct efi_rel_offset_dev_path rel_offset; + struct efi_hd_dev_path hd; }; } __packed; @@ -1109,6 +1138,7 @@ extern void efi_call_virt_check_flags(unsigned long flags, const void *caller); extern unsigned long efi_call_virt_save_flags(void); void efi_runtime_assert_lock_held(void); +efi_status_t efi_runtime_set_enable_flag(bool enable); enum efi_secureboot_mode { efi_secureboot_mode_unset, @@ -1230,6 +1260,10 @@ enum efi_rts_ids { EFI_UPDATE_CAPSULE, EFI_QUERY_CAPSULE_CAPS, EFI_ACPI_PRM_HANDLER, +#if IS_ENABLED(CONFIG_LKDTM) + EFI_LKDTM_RUNTIME_RO_TEST, + EFI_LKDTM_RUNTIME_NX_TEST, +#endif }; union efi_rts_args; @@ -1330,7 +1364,8 @@ bool efi_config_table_is_usable(const efi_guid_t *guid, unsigned long table) return xen_efi_config_table_is_usable(guid, table); } -umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n); +umode_t efi_attr_is_visible(struct kobject *kobj, const struct attribute *attr, + int n); int ovmf_log_probe(unsigned long ovmf_debug_log_table); diff --git a/include/linux/ucs2_string.h b/include/linux/ucs2_string.h index c499ae809c7d..74f23ca5a967 100644 --- a/include/linux/ucs2_string.h +++ b/include/linux/ucs2_string.h @@ -14,7 +14,16 @@ ssize_t ucs2_strscpy(ucs2_char_t *dst, const ucs2_char_t *src, size_t count); int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len); unsigned long ucs2_utf8size(const ucs2_char_t *src); +unsigned long +ucs2_as_utf8_l(u8 *dest, const ucs2_char_t *src, unsigned long limit, + unsigned long maxlength); + +static inline unsigned long ucs2_as_utf8(u8 *dest, const ucs2_char_t *src, - unsigned long maxlength); + unsigned long maxlength) +{ + return ucs2_as_utf8_l(dest, src, ucs2_strnlen(src, maxlength), + maxlength); +} #endif /* _LINUX_UCS2_STRING_H_ */ diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c index 1f7dd4eb640a..32e167332840 100644 --- a/lib/ucs2_string.c +++ b/lib/ucs2_string.c @@ -51,20 +51,18 @@ EXPORT_SYMBOL(ucs2_strsize); */ ssize_t ucs2_strscpy(ucs2_char_t *dst, const ucs2_char_t *src, size_t count) { - long res; - /* * Ensure that we have a valid amount of space. We need to store at * least one NUL-character. */ - if (count == 0 || WARN_ON_ONCE(count > INT_MAX / sizeof(*dst))) + if (count == 0 || count > SSIZE_MAX) return -E2BIG; /* * Copy at most 'count' characters, return early if we find a * NUL-terminator. */ - for (res = 0; res < count; res++) { + for (ssize_t res = 0; res < count; res++) { ucs2_char_t c; c = src[res]; @@ -125,18 +123,19 @@ ucs2_utf8size(const ucs2_char_t *src) EXPORT_SYMBOL(ucs2_utf8size); /* - * copy at most maxlength bytes of whole utf8 characters to dest from the - * ucs2 string src. + * Copy at most @limit whole utf8 characters to @dest from the ucs2 string + * @src, using no more than @maxlength bytes of buffer space. * - * The return value is the number of characters copied, not including the - * final NUL character. + * The return value is the number of bytes copied, not including the final NUL + * character. No NUL character will be appended if the output length equals + * @maxlength. */ unsigned long -ucs2_as_utf8(u8 *dest, const ucs2_char_t *src, unsigned long maxlength) +ucs2_as_utf8_l(u8 *dest, const ucs2_char_t *src, unsigned long limit, + unsigned long maxlength) { unsigned int i; unsigned long j = 0; - unsigned long limit = ucs2_strnlen(src, maxlength); for (i = 0; maxlength && i < limit; i++) { u16 c = src[i]; @@ -163,7 +162,9 @@ ucs2_as_utf8(u8 *dest, const ucs2_char_t *src, unsigned long maxlength) dest[j] = '\0'; return j; } -EXPORT_SYMBOL(ucs2_as_utf8); +EXPORT_SYMBOL(ucs2_as_utf8_l); +#ifndef __DISABLE_EXPORTS MODULE_DESCRIPTION("UCS2 string handling"); MODULE_LICENSE("GPL v2"); +#endif |
