diff options
30 files changed, 389 insertions, 116 deletions
diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h index b95241b1ca65..78282ced5038 100644 --- a/arch/arm/include/asm/efi.h +++ b/arch/arm/include/asm/efi.h @@ -20,7 +20,7 @@ void efi_init(void); void arm_efi_init(void); int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md); -int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); +int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md, bool); #define arch_efi_call_virt_setup() efi_virtmap_load() #define arch_efi_call_virt_teardown() efi_virtmap_unload() diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c index 882104f43b3b..e2b9d2618c67 100644 --- a/arch/arm/kernel/efi.c +++ b/arch/arm/kernel/efi.c @@ -23,7 +23,8 @@ static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data) } int __init efi_set_mapping_permissions(struct mm_struct *mm, - efi_memory_desc_t *md) + efi_memory_desc_t *md, + bool ignored) { unsigned long base, size; @@ -71,7 +72,7 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md) * If stricter permissions were specified, apply them now. */ if (md->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP)) - return efi_set_mapping_permissions(mm, md); + return efi_set_mapping_permissions(mm, md, false); return 0; } diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index acaa39f6381a..f86b157a5da3 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -27,13 +27,14 @@ bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg) #endif int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md); -int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); +int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md, + bool has_bti); #define arch_efi_call_virt_setup() \ ({ \ efi_virtmap_load(); \ __efi_fpsimd_begin(); \ - spin_lock(&efi_rt_lock); \ + raw_spin_lock(&efi_rt_lock); \ }) #undef arch_efi_call_virt @@ -42,12 +43,12 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); #define arch_efi_call_virt_teardown() \ ({ \ - spin_unlock(&efi_rt_lock); \ + raw_spin_unlock(&efi_rt_lock); \ __efi_fpsimd_end(); \ efi_virtmap_unload(); \ }) -extern spinlock_t efi_rt_lock; +extern raw_spinlock_t efi_rt_lock; extern u64 *efi_rt_stack_top; efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index b273900f4566..baab8dd3ead3 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -97,22 +97,34 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md) return 0; } +struct set_perm_data { + const efi_memory_desc_t *md; + bool has_bti; +}; + static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data) { - efi_memory_desc_t *md = data; + struct set_perm_data *spd = data; + const efi_memory_desc_t *md = spd->md; pte_t pte = READ_ONCE(*ptep); if (md->attribute & EFI_MEMORY_RO) pte = set_pte_bit(pte, __pgprot(PTE_RDONLY)); if (md->attribute & EFI_MEMORY_XP) pte = set_pte_bit(pte, __pgprot(PTE_PXN)); + else if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) && + system_supports_bti() && spd->has_bti) + pte = set_pte_bit(pte, __pgprot(PTE_GP)); set_pte(ptep, pte); return 0; } int __init efi_set_mapping_permissions(struct mm_struct *mm, - efi_memory_desc_t *md) + efi_memory_desc_t *md, + bool has_bti) { + struct set_perm_data data = { md, has_bti }; + BUG_ON(md->type != EFI_RUNTIME_SERVICES_CODE && md->type != EFI_RUNTIME_SERVICES_DATA); @@ -128,7 +140,7 @@ int __init efi_set_mapping_permissions(struct mm_struct *mm, */ return apply_to_page_range(mm, md->virt_addr, md->num_pages << EFI_PAGE_SHIFT, - set_permissions, md); + set_permissions, &data); } /* @@ -146,7 +158,7 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f) return s; } -DEFINE_SPINLOCK(efi_rt_lock); +DEFINE_RAW_SPINLOCK(efi_rt_lock); asmlinkage u64 *efi_rt_stack_top __ro_after_init; diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index a6dd3e90755c..901dfd9bf04c 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/kexec.h> #include <linux/delay.h> +#include <linux/efi.h> #include <linux/init.h> #include <linux/sched/signal.h> #include <linux/sched/debug.h> @@ -34,6 +35,7 @@ #include <asm/cpufeature.h> #include <asm/daifflags.h> #include <asm/debug-monitors.h> +#include <asm/efi.h> #include <asm/esr.h> #include <asm/exception.h> #include <asm/extable.h> @@ -491,6 +493,10 @@ void do_el0_bti(struct pt_regs *regs) void do_el1_bti(struct pt_regs *regs, unsigned long esr) { + if (efi_runtime_fixup_exception(regs, "BTI violation")) { + regs->pstate &= ~PSR_BTYPE_MASK; + return; + } die("Oops - BTI", regs, esr); } diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 31149e41f9be..21dfa4aa35bb 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -525,7 +525,7 @@ efi_init (void) */ if (efi_systab == NULL) panic("Whoa! Can't find EFI system table.\n"); - if (efi_systab_check_header(&efi_systab->hdr, 1)) + if (efi_systab_check_header(&efi_systab->hdr)) panic("Whoa! EFI system table signature incorrect\n"); efi_systab_report_header(&efi_systab->hdr, efi_systab->fw_vendor); diff --git a/arch/riscv/include/asm/efi.h b/arch/riscv/include/asm/efi.h index 47d3ab0fcc36..29e9a0d84b16 100644 --- a/arch/riscv/include/asm/efi.h +++ b/arch/riscv/include/asm/efi.h @@ -19,7 +19,7 @@ extern void efi_init(void); #endif int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md); -int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); +int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md, bool); #define arch_efi_call_virt_setup() ({ \ sync_kernel_mappings(efi_mm.pgd); \ diff --git a/arch/riscv/kernel/efi.c b/arch/riscv/kernel/efi.c index 1aa540350abd..aa6209a74c83 100644 --- a/arch/riscv/kernel/efi.c +++ b/arch/riscv/kernel/efi.c @@ -78,7 +78,8 @@ static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data) } int __init efi_set_mapping_permissions(struct mm_struct *mm, - efi_memory_desc_t *md) + efi_memory_desc_t *md, + bool ignored) { BUG_ON(md->type != EFI_RUNTIME_SERVICES_CODE && md->type != EFI_RUNTIME_SERVICES_DATA); diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index a63154e049d7..419280d263d2 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -106,6 +106,8 @@ static inline void efi_fpu_end(void) extern asmlinkage u64 __efi_call(void *fp, ...); +extern bool efi_disable_ibt_for_runtime; + #define efi_call(...) ({ \ __efi_nargs_check(efi_call, 7, __VA_ARGS__); \ __efi_call(__VA_ARGS__); \ @@ -121,7 +123,7 @@ extern asmlinkage u64 __efi_call(void *fp, ...); #undef arch_efi_call_virt #define arch_efi_call_virt(p, f, args...) ({ \ - u64 ret, ibt = ibt_save(); \ + u64 ret, ibt = ibt_save(efi_disable_ibt_for_runtime); \ ret = efi_call((void *)p->f, args); \ ibt_restore(ibt); \ ret; \ @@ -335,6 +337,16 @@ static inline u32 efi64_convert_status(efi_status_t status) #define __efi64_argmap_open_volume(prot, file) \ ((prot), efi64_zero_upper(file)) +/* Memory Attribute Protocol */ +#define __efi64_argmap_get_memory_attributes(protocol, phys, size, flags) \ + ((protocol), __efi64_split(phys), __efi64_split(size), (flags)) + +#define __efi64_argmap_set_memory_attributes(protocol, phys, size, flags) \ + ((protocol), __efi64_split(phys), __efi64_split(size), __efi64_split(flags)) + +#define __efi64_argmap_clear_memory_attributes(protocol, phys, size, flags) \ + ((protocol), __efi64_split(phys), __efi64_split(size), __efi64_split(flags)) + /* * The macros below handle the plumbing for the argument mapping. To add a * mapping for a specific EFI method, simply define a macro diff --git a/arch/x86/include/asm/ibt.h b/arch/x86/include/asm/ibt.h index 9b08082a5d9f..baae6b4fea23 100644 --- a/arch/x86/include/asm/ibt.h +++ b/arch/x86/include/asm/ibt.h @@ -74,7 +74,7 @@ static inline bool is_endbr(u32 val) return val == gen_endbr(); } -extern __noendbr u64 ibt_save(void); +extern __noendbr u64 ibt_save(bool disable); extern __noendbr void ibt_restore(u64 save); #else /* __ASSEMBLY__ */ @@ -100,7 +100,7 @@ extern __noendbr void ibt_restore(u64 save); static inline bool is_endbr(u32 val) { return false; } -static inline u64 ibt_save(void) { return 0; } +static inline u64 ibt_save(bool disable) { return 0; } static inline void ibt_restore(u64 save) { } #else /* __ASSEMBLY__ */ diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 60e330cdbd17..c6c15ce1952f 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -609,7 +609,7 @@ static long __apm_bios_call(void *_call) apm_irq_save(flags); firmware_restrict_branch_speculation_start(); - ibt = ibt_save(); + ibt = ibt_save(true); APM_DO_SAVE_SEGS; apm_bios_call_asm(call->func, call->ebx, call->ecx, &call->eax, &call->ebx, &call->ecx, &call->edx, @@ -690,7 +690,7 @@ static long __apm_bios_call_simple(void *_call) apm_irq_save(flags); firmware_restrict_branch_speculation_start(); - ibt = ibt_save(); + ibt = ibt_save(true); APM_DO_SAVE_SEGS; error = apm_bios_call_simple_asm(call->func, call->ebx, call->ecx, &call->eax); diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a394bbba7a4b..8cd4126d8253 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -571,13 +571,14 @@ __setup("nopku", setup_disable_pku); #ifdef CONFIG_X86_KERNEL_IBT -__noendbr u64 ibt_save(void) +__noendbr u64 ibt_save(bool disable) { u64 msr = 0; if (cpu_feature_enabled(X86_FEATURE_IBT)) { rdmsrl(MSR_IA32_S_CET, msr); - wrmsrl(MSR_IA32_S_CET, msr & ~CET_ENDBR_EN); + if (disable) + wrmsrl(MSR_IA32_S_CET, msr & ~CET_ENDBR_EN); } return msr; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 55d9caf66401..f3f2d87cce1b 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -380,7 +380,7 @@ static int __init efi_systab_init(unsigned long phys) return -ENOMEM; } - ret = efi_systab_check_header(hdr, 1); + ret = efi_systab_check_header(hdr); if (ret) { early_memunmap(p, size); return ret; diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index b36596bf0fc3..232acf418cfb 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -389,10 +389,15 @@ static int __init efi_update_mappings(efi_memory_desc_t *md, unsigned long pf) return err1 || err2; } -static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *md) +bool efi_disable_ibt_for_runtime __ro_after_init = true; + +static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *md, + bool has_ibt) { unsigned long pf = 0; + efi_disable_ibt_for_runtime |= !has_ibt; + if (md->attribute & EFI_MEMORY_XP) pf |= _PAGE_NX; @@ -414,6 +419,7 @@ void __init efi_runtime_update_mappings(void) * exists, since it is intended to supersede EFI_PROPERTIES_TABLE. */ if (efi_enabled(EFI_MEM_ATTR)) { + efi_disable_ibt_for_runtime = false; efi_memattr_apply_permissions(NULL, efi_update_mem_attr); return; } diff --git a/drivers/firmware/efi/cper_cxl.c b/drivers/firmware/efi/cper_cxl.c index 53e435c4f310..a55771b99a97 100644 --- a/drivers/firmware/efi/cper_cxl.c +++ b/drivers/firmware/efi/cper_cxl.c @@ -9,7 +9,6 @@ #include <linux/cper.h> #include "cper_cxl.h" -#include <linux/cxl_err.h> #define PROT_ERR_VALID_AGENT_TYPE BIT_ULL(0) #define PROT_ERR_VALID_AGENT_ADDRESS BIT_ULL(1) @@ -19,6 +18,17 @@ #define PROT_ERR_VALID_DVSEC BIT_ULL(5) #define PROT_ERR_VALID_ERROR_LOG BIT_ULL(6) +/* CXL RAS Capability Structure, CXL v3.0 sec 8.2.4.16 */ +struct cxl_ras_capability_regs { + u32 uncor_status; + u32 uncor_mask; + u32 uncor_severity; + u32 cor_status; + u32 cor_mask; + u32 cap_control; + u32 header_log[16]; +}; + static const char * const prot_err_agent_type_strs[] = { "Restricted CXL Device", "Restricted CXL Host Downstream Port", diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c index 4d6c5327471a..f54e6fdf08e2 100644 --- a/drivers/firmware/efi/earlycon.c +++ b/drivers/firmware/efi/earlycon.c @@ -10,11 +10,14 @@ #include <linux/kernel.h> #include <linux/serial_core.h> #include <linux/screen_info.h> +#include <linux/string.h> #include <asm/early_ioremap.h> static const struct console *earlycon_console __initdata; static const struct font_desc *font; +static u16 cur_line_y, max_line_y; +static u32 efi_x_array[1024]; static u32 efi_x, efi_y; static u64 fb_base; static bool fb_wb; @@ -85,9 +88,17 @@ static void efi_earlycon_clear_scanline(unsigned int y) static void efi_earlycon_scroll_up(void) { unsigned long *dst, *src; + u16 maxlen = 0; u16 len; u32 i, height; + /* Find the cached maximum x coordinate */ + for (i = 0; i < max_line_y; i++) { + if (efi_x_array[i] > maxlen) + maxlen = efi_x_array[i]; + } + maxlen *= 4; + len = screen_info.lfb_linelength; height = screen_info.lfb_height; @@ -102,7 +113,7 @@ static void efi_earlycon_scroll_up(void) return; } - memmove(dst, src, len); + memmove(dst, src, maxlen); efi_earlycon_unmap(src, len); efi_earlycon_unmap(dst, len); @@ -135,6 +146,7 @@ static void efi_earlycon_write(struct console *con, const char *str, unsigned int num) { struct screen_info *si; + u32 cur_efi_x = efi_x; unsigned int len; const char *s; void *dst; @@ -143,16 +155,10 @@ efi_earlycon_write(struct console *con, const char *str, unsigned int num) len = si->lfb_linelength; while (num) { - unsigned int linemax; - unsigned int h, count = 0; - - for (s = str; *s && *s != '\n'; s++) { - if (count == num) - break; - count++; - } + unsigned int linemax = (si->lfb_width - efi_x) / font->width; + unsigned int h, count; - linemax = (si->lfb_width - efi_x) / font->width; + count = strnchrnul(str, num, '\n') - str; if (count > linemax) count = linemax; @@ -181,6 +187,7 @@ efi_earlycon_write(struct console *con, const char *str, unsigned int num) str += count; if (num > 0 && *s == '\n') { + cur_efi_x = efi_x; efi_x = 0; efi_y += font->height; str++; @@ -188,6 +195,7 @@ efi_earlycon_write(struct console *con, const char *str, unsigned int num) } if (efi_x + font->width > si->lfb_width) { + cur_efi_x = efi_x; efi_x = 0; efi_y += font->height; } @@ -195,6 +203,9 @@ efi_earlycon_write(struct console *con, const char *str, unsigned int num) if (efi_y + font->height > si->lfb_height) { u32 i; + efi_x_array[cur_line_y] = cur_efi_x; + cur_line_y = (cur_line_y + 1) % max_line_y; + efi_y -= font->height; efi_earlycon_scroll_up(); @@ -235,7 +246,15 @@ static int __init efi_earlycon_setup(struct earlycon_device *device, if (!font) return -ENODEV; - efi_y = rounddown(yres, font->height) - font->height; + /* Fill the cache with maximum possible value of x coordinate */ + memset32(efi_x_array, rounddown(xres, font->width), ARRAY_SIZE(efi_x_array)); + efi_y = rounddown(yres, font->height); + + /* Make sure we have cache for the x coordinate for the full screen */ + max_line_y = efi_y / font->height + 1; + cur_line_y = 0; + + efi_y -= font->height; for (i = 0; i < (yres - efi_y) / font->height; i++) efi_earlycon_scroll_up(); diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index 1639159493e3..2c16080e1f71 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -92,7 +92,7 @@ static int __init uefi_init(u64 efi_system_table) if (IS_ENABLED(CONFIG_64BIT)) set_bit(EFI_64BIT, &efi.flags); - retval = efi_systab_check_header(&systab->hdr, 2); + retval = efi_systab_check_header(&systab->hdr); if (retval) goto out; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 1e0b016fdc2b..abeff7dc0b58 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -187,8 +187,27 @@ static const struct attribute_group efi_subsys_attr_group = { static struct efivars generic_efivars; static struct efivar_operations generic_ops; +static bool generic_ops_supported(void) +{ + unsigned long name_size; + efi_status_t status; + efi_char16_t name; + efi_guid_t guid; + + name_size = sizeof(name); + + status = efi.get_next_variable(&name_size, &name, &guid); + if (status == EFI_UNSUPPORTED) + return false; + + return true; +} + static int generic_ops_register(void) { + if (!generic_ops_supported()) + return 0; + generic_ops.get_variable = efi.get_variable; generic_ops.get_next_variable = efi.get_next_variable; generic_ops.query_variable_store = efi_query_variable_store; @@ -197,11 +216,14 @@ static int generic_ops_register(void) generic_ops.set_variable = efi.set_variable; generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking; } - return efivars_register(&generic_efivars, &generic_ops, efi_kobj); + return efivars_register(&generic_efivars, &generic_ops); } static void generic_ops_unregister(void) { + if (!generic_ops.get_variable) + return; + efivars_unregister(&generic_efivars); } @@ -481,7 +503,7 @@ void __init efi_find_mirror(void) * and if so, populate the supplied memory descriptor with the appropriate * data. */ -int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) +int __efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) { efi_memory_desc_t *md; @@ -499,6 +521,12 @@ int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) u64 size; u64 end; + /* skip bogus entries (including empty ones) */ + if ((md->phys_addr & (EFI_PAGE_SIZE - 1)) || + (md->num_pages <= 0) || + (md->num_pages > (U64_MAX - md->phys_addr) >> EFI_PAGE_SHIFT)) + continue; + size = md->num_pages << EFI_PAGE_SHIFT; end = md->phys_addr + size; if (phys_addr >= md->phys_addr && phys_addr < end) { @@ -509,6 +537,9 @@ int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) return -ENOENT; } +extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) + __weak __alias(__efi_mem_desc_lookup); + /* * Calculate the highest address of an efi memory descriptor. */ @@ -535,6 +566,10 @@ void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {} */ void __init efi_mem_reserve(phys_addr_t addr, u64 size) { + /* efi_mem_reserve() does not work under Xen */ + if (WARN_ON_ONCE(efi_enabled(EFI_PARAVIRT))) + return; + if (!memblock_is_region_reserved(addr, size)) memblock_reserve(addr, size); @@ -583,13 +618,20 @@ static __init int match_config_table(const efi_guid_t *guid, int i; for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { - if (!efi_guidcmp(*guid, table_types[i].guid)) { - *(table_types[i].ptr) = table; + if (efi_guidcmp(*guid, table_types[i].guid)) + continue; + + if (!efi_config_table_is_usable(guid, table)) { if (table_types[i].name[0]) - pr_cont("%s=0x%lx ", + pr_cont("(%s=0x%lx unusable) ", table_types[i].name, table); return 1; } + + *(table_types[i].ptr) = table; + if (table_types[i].name[0]) + pr_cont("%s=0x%lx ", table_types[i].name, table); + return 1; } return 0; @@ -720,20 +762,13 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, return 0; } -int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, - int min_major_version) +int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr) { if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) { pr_err("System table signature incorrect!\n"); return -EINVAL; } - if ((systab_hdr->revision >> 16) < min_major_version) - pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n", - systab_hdr->revision >> 16, - systab_hdr->revision & 0xffff, - min_major_version); - return 0; } @@ -764,6 +799,7 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, char vendor[100] = "unknown"; const efi_char16_t *c16; size_t i; + u16 rev; c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t)); if (c16) { @@ -774,10 +810,14 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t)); } - pr_info("EFI v%u.%.02u by %s\n", - systab_hdr->revision >> 16, - systab_hdr->revision & 0xffff, - vendor); + rev = (u16)systab_hdr->revision; + pr_info("EFI v%u.%u", systab_hdr->revision >> 16, rev / 10); + + rev %= 10; + if (rev) + pr_cont(".%u", rev); + + pr_cont(" by %s\n", vendor); if (IS_ENABLED(CONFIG_X86_64) && systab_hdr->revision > EFI_1_10_SYSTEM_TABLE_REVISION && diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index 2a2f52b017e7..87729c365be1 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -247,7 +247,7 @@ void __init efi_esrt_init(void) int rc; phys_addr_t end; - if (!efi_enabled(EFI_MEMMAP)) + if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT)) return; pr_debug("esrt-init: loading.\n"); @@ -258,20 +258,15 @@ void __init efi_esrt_init(void) if (rc < 0 || (!(md.attribute & EFI_MEMORY_RUNTIME) && md.type != EFI_BOOT_SERVICES_DATA && - md.type != EFI_RUNTIME_SERVICES_DATA)) { + md.type != EFI_RUNTIME_SERVICES_DATA && + md.type != EFI_ACPI_RECLAIM_MEMORY && + md.type != EFI_ACPI_MEMORY_NVS)) { pr_warn("ESRT header is not in the memory map.\n"); return; } - max = efi_mem_desc_end(&md); - if (max < efi.esrt) { - pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n", - (void *)efi.esrt, (void *)max); - return; - } - + max = efi_mem_desc_end(&md) - efi.esrt; size = sizeof(*esrt); - max -= efi.esrt; if (max < size) { pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n", diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index f5a4bdacac64..1e0203d74691 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -651,3 +651,70 @@ efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) return status; } + +/** + * efi_remap_image - Remap a loaded image with the appropriate permissions + * for code and data + * + * @image_base: the base of the image in memory + * @alloc_size: the size of the area in memory occupied by the image + * @code_size: the size of the leading part of the image containing code + * and read-only data + * + * efi_remap_image() uses the EFI memory attribute protocol to remap the code + * region of the loaded image read-only/executable, and the remainder + * read-write/non-executable. The code region is assumed to start at the base + * of the image, and will therefore cover the PE/COFF header as well. + */ +void efi_remap_image(unsigned long image_base, unsigned alloc_size, + unsigned long code_size) +{ + efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + efi_memory_attribute_protocol_t *memattr; + efi_status_t status; + u64 attr; + + /* + * If the firmware implements the EFI_MEMORY_ATTRIBUTE_PROTOCOL, let's + * invoke it to remap the text/rodata region of the decompressed image + * as read-only and the data/bss region as non-executable. + */ + status = efi_bs_call(locate_protocol, &guid, NULL, (void **)&memattr); + if (status != EFI_SUCCESS) + return; + + // Get the current attributes for the entire region + status = memattr->get_memory_attributes(memattr, image_base, + alloc_size, &attr); + if (status != EFI_SUCCESS) { + efi_warn("Failed to retrieve memory attributes for image region: 0x%lx\n", + status); + return; + } + + // Mark the code region as read-only + status = memattr->set_memory_attributes(memattr, image_base, code_size, + EFI_MEMORY_RO); + if (status != EFI_SUCCESS) { + efi_warn("Failed to remap code region read-only\n"); + return; + } + + // If the entire region was already mapped as non-exec, clear the + // attribute from the code region. Otherwise, set it on the data + // region. + if (attr & EFI_MEMORY_XP) { + status = memattr->clear_memory_attributes(memattr, image_base, + code_size, + EFI_MEMORY_XP); + if (status != EFI_SUCCESS) + efi_warn("Failed to remap code region executable\n"); + } else { + status = memattr->set_memory_attributes(memattr, + image_base + code_size, + alloc_size - code_size, + EFI_MEMORY_XP); + if (status != EFI_SUCCESS) + efi_warn("Failed to remap data region non-executable\n"); + } +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 5b8f2c411ed8..6bd3bb86d967 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -442,6 +442,26 @@ union efi_dxe_services_table { } mixed_mode; }; +typedef union efi_memory_attribute_protocol efi_memory_attribute_protocol_t; + +union efi_memory_attribute_protocol { + struct { + efi_status_t (__efiapi *get_memory_attributes)( + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64 *); + + efi_status_t (__efiapi *set_memory_attributes)( + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64); + + efi_status_t (__efiapi *clear_memory_attributes)( + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64); + }; + struct { + u32 get_memory_attributes; + u32 set_memory_attributes; + u32 clear_memory_attributes; + } mixed_mode; +}; + typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t; union efi_uga_draw_protocol { @@ -1076,4 +1096,7 @@ struct efi_smbios_type1_record { const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize); +void efi_remap_image(unsigned long image_base, unsigned alloc_size, + unsigned long code_size); + #endif diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 66be5fdc6b58..ba234e062a1a 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -137,6 +137,8 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) efi_cache_sync_image(image_base, alloc_size, code_size); + efi_remap_image(image_base, alloc_size, code_size); + status = efi_stub_common(handle, image, image_base, cmdline_ptr); free_image: diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c index f178b2984dfb..ab85bf8e165a 100644 --- a/drivers/firmware/efi/memattr.c +++ b/drivers/firmware/efi/memattr.c @@ -129,6 +129,7 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm, efi_memattr_perm_setter fn) { efi_memory_attributes_table_t *tbl; + bool has_bti = false; int i, ret; if (tbl_size <= sizeof(*tbl)) @@ -150,6 +151,10 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm, return -ENOMEM; } + if (tbl->version > 1 && + (tbl->flags & EFI_MEMORY_ATTRIBUTES_FLAGS_RT_FORWARD_CONTROL_FLOW_GUARD)) + has_bti = true; + if (efi_enabled(EFI_DBG)) pr_info("Processing EFI Memory Attributes table:\n"); @@ -169,7 +174,7 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm, efi_md_typeattr_format(buf, sizeof(buf), &md)); if (valid) { - ret = fn(mm, &md); + ret = fn(mm, &md, has_bti); if (ret) pr_err("Error updating mappings, skipping subsequent md's\n"); } diff --git a/drivers/firmware/efi/sysfb_efi.c b/drivers/firmware/efi/sysfb_efi.c index 7882d4b3f2be..f06fdacc9bc8 100644 --- a/drivers/firmware/efi/sysfb_efi.c +++ b/drivers/firmware/efi/sysfb_efi.c @@ -264,6 +264,14 @@ static const struct dmi_system_id efifb_dmi_swap_width_height[] __initconst = { "Lenovo ideapad D330-10IGM"), }, }, + { + /* Lenovo IdeaPad Duet 3 10IGL5 with 1200x1920 portrait screen */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, + "IdeaPad Duet 3 10IGL5"), + }, + }, {}, }; diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 0ba9f18312f5..bd75b87f5fc1 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -6,6 +6,8 @@ * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> */ +#define pr_fmt(fmt) "efivars: " fmt + #include <linux/types.h> #include <linux/sizes.h> #include <linux/errno.h> @@ -40,45 +42,47 @@ static efi_status_t check_var_size(bool nonblocking, u32 attributes, } /** - * efivars_kobject - get the kobject for the registered efivars + * efivar_is_available - check if efivars is available * - * If efivars_register() has not been called we return NULL, - * otherwise return the kobject used at registration time. + * @return true iff evivars is currently registered */ -struct kobject *efivars_kobject(void) +bool efivar_is_available(void) { - if (!__efivars) - return NULL; - - return __efivars->kobject; + return __efivars != NULL; } -EXPORT_SYMBOL_GPL(efivars_kobject); +EXPORT_SYMBOL_GPL(efivar_is_available); /** * efivars_register - register an efivars * @efivars: efivars to register * @ops: efivars operations - * @kobject: @efivars-specific kobject * * Only a single efivars can be registered at any time. */ int efivars_register(struct efivars *efivars, - const struct efivar_operations *ops, - struct kobject *kobject) + const struct efivar_operations *ops) { + int rv; + if (down_interruptible(&efivars_lock)) return -EINTR; + if (__efivars) { + pr_warn("efivars already registered\n"); + rv = -EBUSY; + goto out; + } + efivars->ops = ops; - efivars->kobject = kobject; __efivars = efivars; pr_info("Registered efivars operations\n"); - + rv = 0; +out: up(&efivars_lock); - return 0; + return rv; } EXPORT_SYMBOL_GPL(efivars_register); @@ -97,7 +101,7 @@ int efivars_unregister(struct efivars *efivars) return -EINTR; if (!__efivars) { - printk(KERN_ERR "efivars not registered\n"); + pr_err("efivars not registered\n"); rv = -EINVAL; goto out; } @@ -117,7 +121,7 @@ out: } EXPORT_SYMBOL_GPL(efivars_unregister); -int efivar_supports_writes(void) +bool efivar_supports_writes(void) { return __efivars && __efivars->ops->set_variable; } diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 871bedf533a8..96ea1fa76d35 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -1030,7 +1030,7 @@ static __init int gsmi_init(void) } #ifdef CONFIG_EFI - ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); + ret = efivars_register(&efivars, &efivar_ops); if (ret) { printk(KERN_INFO "gsmi: Failed to register efivars\n"); sysfs_remove_files(gsmi_kobj, gsmi_attrs); diff --git a/drivers/xen/efi.c b/drivers/xen/efi.c index d1ff2186ebb4..fb321cd6415a 100644 --- a/drivers/xen/efi.c +++ b/drivers/xen/efi.c @@ -26,6 +26,7 @@ #include <xen/interface/xen.h> #include <xen/interface/platform.h> +#include <xen/page.h> #include <xen/xen.h> #include <xen/xen-ops.h> @@ -292,3 +293,63 @@ void __init xen_efi_runtime_setup(void) efi.get_next_high_mono_count = xen_efi_get_next_high_mono_count; efi.reset_system = xen_efi_reset_system; } + +int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) +{ + static_assert(XEN_PAGE_SHIFT == EFI_PAGE_SHIFT, + "Mismatch between EFI_PAGE_SHIFT and XEN_PAGE_SHIFT"); + struct xen_platform_op op; + union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; + int rc; + + if (!efi_enabled(EFI_PARAVIRT) || efi_enabled(EFI_MEMMAP)) + return __efi_mem_desc_lookup(phys_addr, out_md); + phys_addr &= ~(u64)(EFI_PAGE_SIZE - 1); + op = (struct xen_platform_op) { + .cmd = XENPF_firmware_info, + .u.firmware_info = { + .type = XEN_FW_EFI_INFO, + .index = XEN_FW_EFI_MEM_INFO, + .u.efi_info.mem.addr = phys_addr, + .u.efi_info.mem.size = U64_MAX - phys_addr, + }, + }; + + rc = HYPERVISOR_platform_op(&op); + if (rc) { + pr_warn("Failed to lookup header 0x%llx in Xen memory map: error %d\n", + phys_addr, rc); + } + + out_md->phys_addr = info->mem.addr; + out_md->num_pages = info->mem.size >> EFI_PAGE_SHIFT; + out_md->type = info->mem.type; + out_md->attribute = info->mem.attr; + + return 0; +} + +bool __init xen_efi_config_table_is_usable(const efi_guid_t *guid, + unsigned long table) +{ + efi_memory_desc_t md; + int rc; + + if (!efi_enabled(EFI_PARAVIRT)) + return true; + + rc = efi_mem_desc_lookup(table, &md); + if (rc) + return false; + + switch (md.type) { + case EFI_RUNTIME_SERVICES_CODE: + case EFI_RUNTIME_SERVICES_DATA: + case EFI_ACPI_RECLAIM_MEMORY: + case EFI_ACPI_MEMORY_NVS: + case EFI_RESERVED_TYPE: + return true; + default: + return false; + } +} diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 07e82e246666..482d612b716b 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -194,6 +194,9 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc) struct dentry *root; int err; + if (!efivar_is_available()) + return -EOPNOTSUPP; + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; @@ -243,6 +246,9 @@ static void efivarfs_kill_sb(struct super_block *sb) { kill_litter_super(sb); + if (!efivar_is_available()) + return; + /* Remove all entries and destroy */ efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL); } @@ -256,9 +262,6 @@ static struct file_system_type efivarfs_type = { static __init int efivarfs_init(void) { - if (!efivars_kobject()) - return -ENODEV; - return register_filesystem(&efivarfs_type); } diff --git a/include/linux/cxl_err.h b/include/linux/cxl_err.h deleted file mode 100644 index 629e1bdeda44..000000000000 --- a/include/linux/cxl_err.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2022 Advanced Micro Devices, Inc. - * - * Author: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com> - */ - -#ifndef LINUX_CXL_ERR_H -#define LINUX_CXL_ERR_H - -/* CXL RAS Capability Structure, CXL v3.1 sec 8.2.4.16 */ -struct cxl_ras_capability_regs { - u32 uncor_status; - u32 uncor_mask; - u32 uncor_severity; - u32 cor_status; - u32 cor_mask; - u32 cap_control; - u32 header_log[16]; -}; - -#endif //__CXL_ERR_ diff --git a/include/linux/efi.h b/include/linux/efi.h index 98598bd1d2fa..04a733f0ba95 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -391,6 +391,7 @@ void efi_native_runtime_setup(void); #define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9) #define EFI_DXE_SERVICES_TABLE_GUID EFI_GUID(0x05ad34ba, 0x6f02, 0x4214, 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9) #define EFI_SMBIOS_PROTOCOL_GUID EFI_GUID(0x03583ff6, 0xcb36, 0x4940, 0x94, 0x7e, 0xb9, 0xb3, 0x9f, 0x4a, 0xfa, 0xf7) +#define EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID EFI_GUID(0xf4560cf6, 0x40ec, 0x4b4a, 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89) #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) @@ -583,11 +584,15 @@ typedef struct { #define EFI_INVALID_TABLE_ADDR (~0UL) +// BIT0 implies that Runtime code includes the forward control flow guard +// instruction, such as X86 CET-IBT or ARM BTI. +#define EFI_MEMORY_ATTRIBUTES_FLAGS_RT_FORWARD_CONTROL_FLOW_GUARD 0x1 + typedef struct { u32 version; u32 num_entries; u32 desc_size; - u32 reserved; + u32 flags; efi_memory_desc_t entry[0]; } efi_memory_attributes_table_t; @@ -721,8 +726,7 @@ static inline void efi_esrt_init(void) { } extern int efi_config_parse_tables(const efi_config_table_t *config_tables, int count, const efi_config_table_type_t *arch_tables); -extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr, - int min_major_version); +extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr); extern void efi_systab_report_header(const efi_table_hdr_t *systab_hdr, unsigned long fw_vendor); extern u64 efi_get_iobase (void); @@ -732,6 +736,7 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int __init efi_uart_console_only (void); extern u64 efi_mem_desc_end(efi_memory_desc_t *md); extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md); +extern int __efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md); extern void efi_mem_reserve(phys_addr_t addr, u64 size); extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); extern void efi_initialize_iomem_resources(struct resource *code_resource, @@ -751,7 +756,7 @@ extern unsigned long efi_mem_attr_table; * argument in the page tables referred to by the * first argument. */ -typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *); +typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *, bool); extern int efi_memattr_init(void); extern int efi_memattr_apply_permissions(struct mm_struct *mm, @@ -1040,7 +1045,6 @@ struct efivar_operations { struct efivars { struct kset *kset; - struct kobject *kobject; const struct efivar_operations *ops; }; @@ -1054,12 +1058,16 @@ struct efivars { #define EFI_VAR_NAME_LEN 1024 int efivars_register(struct efivars *efivars, - const struct efivar_operations *ops, - struct kobject *kobject); + const struct efivar_operations *ops); int efivars_unregister(struct efivars *efivars); -struct kobject *efivars_kobject(void); -int efivar_supports_writes(void); +#ifdef CONFIG_EFI +bool efivar_is_available(void); +#else +static inline bool efivar_is_available(void) { return false; } +#endif + +bool efivar_supports_writes(void); int efivar_lock(void); int efivar_trylock(void); @@ -1319,4 +1327,14 @@ struct linux_efi_initrd { /* Header of a populated EFI secret area */ #define EFI_SECRET_TABLE_HEADER_GUID EFI_GUID(0x1e74f542, 0x71dd, 0x4d66, 0x96, 0x3e, 0xef, 0x42, 0x87, 0xff, 0x17, 0x3b) +bool xen_efi_config_table_is_usable(const efi_guid_t *guid, unsigned long table); + +static inline +bool efi_config_table_is_usable(const efi_guid_t *guid, unsigned long table) +{ + if (!IS_ENABLED(CONFIG_XEN_EFI)) + return true; + return xen_efi_config_table_is_usable(guid, table); +} + #endif /* _LINUX_EFI_H */ |