Commit 06e1a81c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'efi-next-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi

Pull EFI updates from Ard Biesheuvel:
 "A healthy mix of EFI contributions this time:

   - Performance tweaks for efifb earlycon (Andy)

   - Preparatory refactoring and cleanup work in the efivar layer, which
     is needed to accommodate the Snapdragon arm64 laptops that expose
     their EFI variable store via a TEE secure world API (Johan)

   - Enhancements to the EFI memory map handling so that Xen dom0 can
     safely access EFI configuration tables (Demi Marie)

   - Wire up the newly introduced IBT/BTI flag in the EFI memory
     attributes table, so that firmware that is generated with ENDBR/BTI
     landing pads will be mapped with enforcement enabled

   - Clean up how we check and print the EFI revision exposed by the
     firmware

   - Incorporate EFI memory attributes protocol definition and wire it
     up in the EFI zboot code (Evgeniy)

     This ensures that these images can execute under new and stricter
     rules regarding the default memory permissions for EFI page
     allocations (More work is in progress here)

   - CPER header cleanup (Dan Williams)

   - Use a raw spinlock to protect the EFI runtime services stack on
     arm64 to ensure the correct semantics under -rt (Pierre)

   - EFI framebuffer quirk for Lenovo Ideapad (Darrell)"

* tag 'efi-next-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi: (24 commits)
  firmware/efi sysfb_efi: Add quirk for Lenovo IdeaPad Duet 3
  arm64: efi: Make efi_rt_lock a raw_spinlock
  efi: Add mixed-mode thunk recipe for GetMemoryAttributes
  efi: x86: Wire up IBT annotation in memory attributes table
  efi: arm64: Wire up BTI annotation in memory attributes table
  efi: Discover BTI support in runtime services regions
  efi/cper, cxl: Remove cxl_err.h
  efi: Use standard format for printing the EFI revision
  efi: Drop minimum EFI version check at boot
  efi: zboot: Use EFI protocol to remap code/data with the right attributes
  efi/libstub: Add memory attribute protocol definitions
  efi: efivars: prevent double registration
  efi: verify that variable services are supported
  efivarfs: always register filesystem
  efi: efivars: add efivars printk prefix
  efi: Warn if trying to reserve memory under Xen
  efi: Actually enable the ESRT under Xen
  efi: Apply allowlist to EFI configuration tables when running under Xen
  efi: xen: Implement memory descriptor lookup based on hypercall
  efi: memmap: Disregard bogus entries instead of returning them
  ...
parents f2b98d0a e1d44715
......@@ -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()
......
......@@ -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;
}
......
......@@ -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 *, ...);
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -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); \
......
......@@ -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);
......
......@@ -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
......
......@@ -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__ */
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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",
......
......@@ -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();
......
......@@ -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;
......
......@@ -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 &&
......
......@@ -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",
......
......@@ -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");
}
}
......@@ -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
......@@ -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:
......
......@@ -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");
}
......
......@@ -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"),
},
},
{},
};
......
......@@ -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 @@ int efivars_unregister(struct efivars *efivars)
}
EXPORT_SYMBOL_GPL(efivars_unregister);
int efivar_supports_writes(void)
bool efivar_supports_writes(void)
{
return __efivars && __efivars->ops->set_variable;
}
......
......@@ -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);
......
......@@ -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;
}
}
......@@ -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);
}
......
/* 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_
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment