linux/drivers/firmware/efi/riscv-runtime.c
Ard Biesheuvel 37926f9630 efi: runtime: Don't assume virtual mappings are missing if VA == PA == 0
The generic EFI stub can be instructed to avoid SetVirtualAddressMap(),
and simply run with the firmware's 1:1 mapping. In this case, it
populates the virtual address fields of the runtime regions in the
memory map with the physical address of each region, so that the mapping
code has to be none the wiser. Only if SetVirtualAddressMap() fails, the
virtual addresses are wiped and the kernel code knows that the regions
cannot be mapped.

However, wiping amounts to setting it to zero, and if a runtime region
happens to live at physical address 0, its valid 1:1 mapped virtual
address could be mistaken for a wiped field, resulting on loss of access
to the EFI services at runtime.

So let's only assume that VA == 0 means 'no runtime services' if the
region in question does not live at PA 0x0.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
2022-10-21 11:09:41 +02:00

144 lines
3.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Extensible Firmware Interface
*
* Copyright (C) 2020 Western Digital Corporation or its affiliates.
*
* Based on Extensible Firmware Interface Specification version 2.4
* Adapted from drivers/firmware/efi/arm-runtime.c
*
*/
#include <linux/dmi.h>
#include <linux/efi.h>
#include <linux/io.h>
#include <linux/memblock.h>
#include <linux/mm_types.h>
#include <linux/preempt.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/pgtable.h>
#include <asm/cacheflush.h>
#include <asm/efi.h>
#include <asm/mmu.h>
#include <asm/pgalloc.h>
static bool __init efi_virtmap_init(void)
{
efi_memory_desc_t *md;
efi_mm.pgd = pgd_alloc(&efi_mm);
mm_init_cpumask(&efi_mm);
init_new_context(NULL, &efi_mm);
for_each_efi_memory_desc(md) {
phys_addr_t phys = md->phys_addr;
int ret;
if (!(md->attribute & EFI_MEMORY_RUNTIME))
continue;
if (md->virt_addr == U64_MAX)
return false;
ret = efi_create_mapping(&efi_mm, md);
if (ret) {
pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
&phys, ret);
return false;
}
}
if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions))
return false;
return true;
}
/*
* Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
* non-early mapping of the UEFI system table and virtual mappings for all
* EFI_MEMORY_RUNTIME regions.
*/
static int __init riscv_enable_runtime_services(void)
{
u64 mapsize;
if (!efi_enabled(EFI_BOOT)) {
pr_info("EFI services will not be available.\n");
return 0;
}
efi_memmap_unmap();
mapsize = efi.memmap.desc_size * efi.memmap.nr_map;
if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
pr_err("Failed to remap EFI memory map\n");
return 0;
}
if (efi_soft_reserve_enabled()) {
efi_memory_desc_t *md;
for_each_efi_memory_desc(md) {
int md_size = md->num_pages << EFI_PAGE_SHIFT;
struct resource *res;
if (!(md->attribute & EFI_MEMORY_SP))
continue;
res = kzalloc(sizeof(*res), GFP_KERNEL);
if (WARN_ON(!res))
break;
res->start = md->phys_addr;
res->end = md->phys_addr + md_size - 1;
res->name = "Soft Reserved";
res->flags = IORESOURCE_MEM;
res->desc = IORES_DESC_SOFT_RESERVED;
insert_resource(&iomem_resource, res);
}
}
if (efi_runtime_disabled()) {
pr_info("EFI runtime services will be disabled.\n");
return 0;
}
if (efi_enabled(EFI_RUNTIME_SERVICES)) {
pr_info("EFI runtime services access via paravirt.\n");
return 0;
}
pr_info("Remapping and enabling EFI services.\n");
if (!efi_virtmap_init()) {
pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
return -ENOMEM;
}
/* Set up runtime services function pointers */
efi_native_runtime_setup();
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return 0;
}
early_initcall(riscv_enable_runtime_services);
void efi_virtmap_load(void)
{
preempt_disable();
switch_mm(current->active_mm, &efi_mm, NULL);
}
void efi_virtmap_unload(void)
{
switch_mm(&efi_mm, current->active_mm, NULL);
preempt_enable();
}