summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/filesystems/efivarfs.rst12
-rw-r--r--arch/x86/platform/efi/efi.c13
-rw-r--r--drivers/firmware/efi/capsule-loader.c6
-rw-r--r--drivers/firmware/efi/capsule.c4
-rw-r--r--drivers/firmware/efi/efi.c93
-rw-r--r--drivers/firmware/efi/libstub/Makefile44
-rw-r--r--drivers/firmware/efi/libstub/bli.c86
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-entry.c4
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-helper.c108
-rw-r--r--drivers/firmware/efi/libstub/efi-stub.c11
-rw-r--r--drivers/firmware/efi/libstub/efistub.h12
-rw-r--r--drivers/firmware/efi/libstub/fdt.c6
-rw-r--r--drivers/firmware/efi/libstub/file.c8
-rw-r--r--drivers/firmware/efi/libstub/gop.c23
-rw-r--r--drivers/firmware/efi/libstub/kaslr.c19
-rw-r--r--drivers/firmware/efi/libstub/mem.c2
-rw-r--r--drivers/firmware/efi/libstub/pci.c2
-rw-r--r--drivers/firmware/efi/libstub/printk.c95
-rw-r--r--drivers/firmware/efi/libstub/random.c8
-rw-r--r--drivers/firmware/efi/libstub/riscv-stub.c7
-rw-r--r--drivers/firmware/efi/libstub/riscv.c2
-rw-r--r--drivers/firmware/efi/libstub/smbios.c3
-rw-r--r--drivers/firmware/efi/libstub/tpm.c8
-rw-r--r--drivers/firmware/efi/libstub/unaccepted_memory.c2
-rw-r--r--drivers/firmware/efi/libstub/vsprintf.c140
-rw-r--r--drivers/firmware/efi/libstub/x86-stub.c20
-rw-r--r--drivers/firmware/efi/libstub/x86-stub.h16
-rw-r--r--drivers/firmware/efi/libstub/zboot.c3
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c99
-rw-r--r--drivers/misc/lkdtm/bugs.c33
-rw-r--r--fs/efivarfs/internal.h1
-rw-r--r--fs/efivarfs/super.c36
-rw-r--r--include/linux/efi.h37
-rw-r--r--include/linux/ucs2_string.h11
-rw-r--r--lib/ucs2_string.c23
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