forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.5 KiB
96 lines
2.5 KiB
// 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); |
|
}
|
|
|