forked from pop-os/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This patch adds EFI runtime service support for RISC-V. Signed-off-by: Atish Patra <atish.patra@wdc.com> [ardb: - Remove the page check] Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Acked-by: Ard Biesheuvel <ardb@kernel.org> Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
- Loading branch information
1 parent
d707174
commit b91540d
Showing
11 changed files
with
287 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,4 +55,6 @@ obj-$(CONFIG_KGDB) += kgdb.o | |
|
||
obj-$(CONFIG_JUMP_LABEL) += jump_label.o | ||
|
||
obj-$(CONFIG_EFI) += efi.o | ||
|
||
clean: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* Copyright (C) 2020 Western Digital Corporation or its affiliates. | ||
* Adapted from arch/arm64/kernel/efi.c | ||
*/ | ||
|
||
#include <linux/efi.h> | ||
#include <linux/init.h> | ||
|
||
#include <asm/efi.h> | ||
#include <asm/pgtable.h> | ||
#include <asm/pgtable-bits.h> | ||
|
||
/* | ||
* Only regions of type EFI_RUNTIME_SERVICES_CODE need to be | ||
* executable, everything else can be mapped with the XN bits | ||
* set. Also take the new (optional) RO/XP bits into account. | ||
*/ | ||
static __init pgprot_t efimem_to_pgprot_map(efi_memory_desc_t *md) | ||
{ | ||
u64 attr = md->attribute; | ||
u32 type = md->type; | ||
|
||
if (type == EFI_MEMORY_MAPPED_IO) | ||
return PAGE_KERNEL; | ||
|
||
/* R-- */ | ||
if ((attr & (EFI_MEMORY_XP | EFI_MEMORY_RO)) == | ||
(EFI_MEMORY_XP | EFI_MEMORY_RO)) | ||
return PAGE_KERNEL_READ; | ||
|
||
/* R-X */ | ||
if (attr & EFI_MEMORY_RO) | ||
return PAGE_KERNEL_READ_EXEC; | ||
|
||
/* RW- */ | ||
if (((attr & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP)) == | ||
EFI_MEMORY_XP) || | ||
type != EFI_RUNTIME_SERVICES_CODE) | ||
return PAGE_KERNEL; | ||
|
||
/* RWX */ | ||
return PAGE_KERNEL_EXEC; | ||
} | ||
|
||
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md) | ||
{ | ||
pgprot_t prot = __pgprot(pgprot_val(efimem_to_pgprot_map(md)) & | ||
~(_PAGE_GLOBAL)); | ||
int i; | ||
|
||
/* RISC-V maps one page at a time */ | ||
for (i = 0; i < md->num_pages; i++) | ||
create_pgd_mapping(mm->pgd, md->virt_addr + i * PAGE_SIZE, | ||
md->phys_addr + i * PAGE_SIZE, | ||
PAGE_SIZE, prot); | ||
return 0; | ||
} | ||
|
||
static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data) | ||
{ | ||
efi_memory_desc_t *md = data; | ||
pte_t pte = READ_ONCE(*ptep); | ||
unsigned long val; | ||
|
||
if (md->attribute & EFI_MEMORY_RO) { | ||
val = pte_val(pte) & ~_PAGE_WRITE; | ||
val = pte_val(pte) | _PAGE_READ; | ||
pte = __pte(val); | ||
} | ||
if (md->attribute & EFI_MEMORY_XP) { | ||
val = pte_val(pte) & ~_PAGE_EXEC; | ||
pte = __pte(val); | ||
} | ||
set_pte(ptep, pte); | ||
|
||
return 0; | ||
} | ||
|
||
int __init efi_set_mapping_permissions(struct mm_struct *mm, | ||
efi_memory_desc_t *md) | ||
{ | ||
BUG_ON(md->type != EFI_RUNTIME_SERVICES_CODE && | ||
md->type != EFI_RUNTIME_SERVICES_DATA); | ||
|
||
/* | ||
* Calling apply_to_page_range() is only safe on regions that are | ||
* guaranteed to be mapped down to pages. Since we are only called | ||
* for regions that have been mapped using efi_create_mapping() above | ||
* (and this is checked by the generic Memory Attributes table parsing | ||
* routines), there is no need to check that again here. | ||
*/ | ||
return apply_to_page_range(mm, md->virt_addr, | ||
md->num_pages << EFI_PAGE_SHIFT, | ||
set_permissions, md); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// 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 == 0) | ||
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(); | ||
} |