1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2020 Western Digital Corporation or its affiliates. |
4 | * Adapted from arch/arm64/kernel/efi.c |
5 | */ |
6 | |
7 | #include <linux/efi.h> |
8 | #include <linux/init.h> |
9 | |
10 | #include <asm/efi.h> |
11 | #include <asm/pgtable.h> |
12 | #include <asm/pgtable-bits.h> |
13 | |
14 | /* |
15 | * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be |
16 | * executable, everything else can be mapped with the XN bits |
17 | * set. Also take the new (optional) RO/XP bits into account. |
18 | */ |
19 | static __init pgprot_t efimem_to_pgprot_map(efi_memory_desc_t *md) |
20 | { |
21 | u64 attr = md->attribute; |
22 | u32 type = md->type; |
23 | |
24 | if (type == EFI_MEMORY_MAPPED_IO) |
25 | return PAGE_KERNEL; |
26 | |
27 | /* R-- */ |
28 | if ((attr & (EFI_MEMORY_XP | EFI_MEMORY_RO)) == |
29 | (EFI_MEMORY_XP | EFI_MEMORY_RO)) |
30 | return PAGE_KERNEL_READ; |
31 | |
32 | /* R-X */ |
33 | if (attr & EFI_MEMORY_RO) |
34 | return PAGE_KERNEL_READ_EXEC; |
35 | |
36 | /* RW- */ |
37 | if (((attr & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP)) == |
38 | EFI_MEMORY_XP) || |
39 | type != EFI_RUNTIME_SERVICES_CODE) |
40 | return PAGE_KERNEL; |
41 | |
42 | /* RWX */ |
43 | return PAGE_KERNEL_EXEC; |
44 | } |
45 | |
46 | int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md) |
47 | { |
48 | pgprot_t prot = __pgprot(pgprot_val(efimem_to_pgprot_map(md)) & |
49 | ~(_PAGE_GLOBAL)); |
50 | int i; |
51 | |
52 | /* RISC-V maps one page at a time */ |
53 | for (i = 0; i < md->num_pages; i++) |
54 | create_pgd_mapping(mm->pgd, md->virt_addr + i * PAGE_SIZE, |
55 | md->phys_addr + i * PAGE_SIZE, |
56 | PAGE_SIZE, prot); |
57 | return 0; |
58 | } |
59 | |
60 | static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data) |
61 | { |
62 | efi_memory_desc_t *md = data; |
63 | pte_t pte = ptep_get(ptep); |
64 | unsigned long val; |
65 | |
66 | if (md->attribute & EFI_MEMORY_RO) { |
67 | val = pte_val(pte) & ~_PAGE_WRITE; |
68 | val |= _PAGE_READ; |
69 | pte = __pte(val); |
70 | } |
71 | if (md->attribute & EFI_MEMORY_XP) { |
72 | val = pte_val(pte) & ~_PAGE_EXEC; |
73 | pte = __pte(val); |
74 | } |
75 | set_pte(ptep, pte); |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | int __init efi_set_mapping_permissions(struct mm_struct *mm, |
81 | efi_memory_desc_t *md, |
82 | bool ignored) |
83 | { |
84 | BUG_ON(md->type != EFI_RUNTIME_SERVICES_CODE && |
85 | md->type != EFI_RUNTIME_SERVICES_DATA); |
86 | |
87 | /* |
88 | * Calling apply_to_page_range() is only safe on regions that are |
89 | * guaranteed to be mapped down to pages. Since we are only called |
90 | * for regions that have been mapped using efi_create_mapping() above |
91 | * (and this is checked by the generic Memory Attributes table parsing |
92 | * routines), there is no need to check that again here. |
93 | */ |
94 | return apply_to_page_range(mm, address: md->virt_addr, |
95 | size: md->num_pages << EFI_PAGE_SHIFT, |
96 | fn: set_permissions, data: md); |
97 | } |
98 | |