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_a |
