linux/drivers/firmware/efi/memattr.c
Linus Torvalds 06e1a81c48 A healthy mix of EFI contributions this time:
- Performance tweaks for efifb earlycon by Andy
 
 - Preparatory refactoring and cleanup work in the efivar layer by Johan,
   which is needed to accommodate the Snapdragon arm64 laptops that
   expose their EFI variable store via a TEE secure world API.
 
 - 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 contributed by
   Evgeniy and wire it up in the EFI zboot code. 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 by 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 by Darrell.
 -----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE+9lifEBpyUIVN1cpw08iOZLZjyQFAmPzuwsACgkQw08iOZLZ
 jyS7dwwAm95DlDxFIQi4FmTm2mqJws9PyDrkfaAK1CoyqCgeOLQT2FkVolgr8jne
 pwpwCTXtYP8y0BZvdQEIjpAq/BHKaD3GJSPfl7lo+pnUu68PpsFWaV6EdT33KKfj
 QeF0MnUvrqUeTFI77D+S0ZW2zxdo9eCcahF3TPA52/bEiiDHWBF8Qm9VHeQGklik
 zoXA15ft3mgITybgjEA0ncGrVZiBMZrYoMvbdkeoedfw02GN/eaQn8d2iHBtTDEh
 3XNlo7ONX0v50cjt0yvwFEA0AKo0o7R1cj+ziKH/bc4KjzIiCbINhy7blroSq+5K
 YMlnPHuj8Nhv3I+MBdmn/nxRCQeQsE4RfRru04hfNfdcqjAuqwcBvRXvVnjWKZHl
 CmUYs+p/oqxrQ4BjiHfw0JKbXRsgbFI6o3FeeLH9kzI9IDUPpqu3Ma814FVok9Ai
 zbOCrJf5tEtg5tIavcUESEMBuHjEafqzh8c7j7AAqbaNjlihsqosDy9aYoarEi5M
 f/tLec86
 =+pOz
 -----END PGP SIGNATURE-----

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
  ...
2023-02-23 14:41:48 -08:00

185 lines
4.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
*/
#define pr_fmt(fmt) "efi: memattr: " fmt
#include <linux/efi.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/memblock.h>
#include <asm/early_ioremap.h>
static int __initdata tbl_size;
unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR;
/*
* Reserve the memory associated with the Memory Attributes configuration
* table, if it exists.
*/
int __init efi_memattr_init(void)
{
efi_memory_attributes_table_t *tbl;
if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR)
return 0;
tbl = early_memremap(efi_mem_attr_table, sizeof(*tbl));
if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi_mem_attr_table);
return -ENOMEM;
}
if (tbl->version > 2) {
pr_warn("Unexpected EFI Memory Attributes table version %d\n",
tbl->version);
goto unmap;
}
tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
memblock_reserve(efi_mem_attr_table, tbl_size);
set_bit(EFI_MEM_ATTR, &efi.flags);
unmap:
early_memunmap(tbl, sizeof(*tbl));
return 0;
}
/*
* Returns a copy @out of the UEFI memory descriptor @in if it is covered
* entirely by a UEFI memory map entry with matching attributes. The virtual
* address of @out is set according to the matching entry that was found.
*/
static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out)
{
u64 in_paddr = in->phys_addr;
u64 in_size = in->num_pages << EFI_PAGE_SHIFT;
efi_memory_desc_t *md;
*out = *in;
if (in->type != EFI_RUNTIME_SERVICES_CODE &&
in->type != EFI_RUNTIME_SERVICES_DATA) {
pr_warn("Entry type should be RuntimeServiceCode/Data\n");
return false;
}
if (PAGE_SIZE > EFI_PAGE_SIZE &&
(!PAGE_ALIGNED(in->phys_addr) ||
!PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) {
/*
* Since arm64 may execute with page sizes of up to 64 KB, the
* UEFI spec mandates that RuntimeServices memory regions must
* be 64 KB aligned. We need to validate this here since we will
* not be able to tighten permissions on such regions without
* affecting adjacent regions.
*/
pr_warn("Entry address region misaligned\n");
return false;
}
for_each_efi_memory_desc(md) {
u64 md_paddr = md->phys_addr;
u64 md_size = md->num_pages << EFI_PAGE_SHIFT;
if (!(md->attribute & EFI_MEMORY_RUNTIME))
continue;
if (md->virt_addr == 0 && md->phys_addr != 0) {
/* no virtual mapping has been installed by the stub */
break;
}
if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
continue;
/*
* This entry covers the start of @in, check whether
* it covers the end as well.
*/
if (md_paddr + md_size < in_paddr + in_size) {
pr_warn("Entry covers multiple EFI memory map regions\n");
return false;
}
if (md->type != in->type) {
pr_warn("Entry type deviates from EFI memory map region type\n");
return false;
}
out->virt_addr = in_paddr + (md->virt_addr - md_paddr);
return true;
}
pr_warn("No matching entry found in the EFI memory map\n");
return false;
}
/*
* To be called after the EFI page tables have been populated. If a memory
* attributes table is available, its contents will be used to update the
* mappings with tightened permissions as described by the table.
* This requires the UEFI memory map to have already been populated with
* virtual addresses.
*/
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))
return 0;
/*
* We need the EFI memory map to be setup so we can use it to
* lookup the virtual addresses of all entries in the of EFI
* Memory Attributes table. If it isn't available, this
* function should not be called.
*/
if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
return 0;
tbl = memremap(efi_mem_attr_table, tbl_size, MEMREMAP_WB);
if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi_mem_attr_table);
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");
for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) {
efi_memory_desc_t md;
unsigned long size;
bool valid;
char buf[64];
valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size,
&md);
size = md.num_pages << EFI_PAGE_SHIFT;
if (efi_enabled(EFI_DBG) || !valid)
pr_info("%s 0x%012llx-0x%012llx %s\n",
valid ? "" : "!", md.phys_addr,
md.phys_addr + size - 1,
efi_md_typeattr_format(buf, sizeof(buf), &md));
if (valid) {
ret = fn(mm, &md, has_bti);
if (ret)
pr_err("Error updating mappings, skipping subsequent md's\n");
}
}
memunmap(tbl);
return ret;
}