1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Extensible Firmware Interface |
4 | * |
5 | * Copyright (C) 2020 Western Digital Corporation or its affiliates. |
6 | * |
7 | * Based on Extensible Firmware Interface Specification version 2.4 |
8 | * Adapted from drivers/firmware/efi/arm-runtime.c |
9 | * |
10 | */ |
11 | |
12 | #include <linux/dmi.h> |
13 | #include <linux/efi.h> |
14 | #include <linux/io.h> |
15 | #include <linux/memblock.h> |
16 | #include <linux/mm_types.h> |
17 | #include <linux/preempt.h> |
18 | #include <linux/rbtree.h> |
19 | #include <linux/rwsem.h> |
20 | #include <linux/sched.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/spinlock.h> |
23 | #include <linux/pgtable.h> |
24 | |
25 | #include <asm/cacheflush.h> |
26 | #include <asm/efi.h> |
27 | #include <asm/mmu.h> |
28 | #include <asm/pgalloc.h> |
29 | |
30 | static bool __init efi_virtmap_init(void) |
31 | { |
32 | efi_memory_desc_t *md; |
33 | |
34 | efi_mm.pgd = pgd_alloc(&efi_mm); |
35 | mm_init_cpumask(mm: &efi_mm); |
36 | init_new_context(NULL, mm: &efi_mm); |
37 | |
38 | for_each_efi_memory_desc(md) { |
39 | phys_addr_t phys = md->phys_addr; |
40 | int ret; |
41 | |
42 | if (!(md->attribute & EFI_MEMORY_RUNTIME)) |
43 | continue; |
44 | if (md->virt_addr == U64_MAX) |
45 | return false; |
46 | |
47 | ret = efi_create_mapping(&efi_mm, md); |
48 | if (ret) { |
49 | pr_warn(" EFI remap %pa: failed to create mapping (%d)\n" , |
50 | &phys, ret); |
51 | return false; |
52 | } |
53 | } |
54 | |
55 | if (efi_memattr_apply_permissions(mm: &efi_mm, fn: efi_set_mapping_permissions)) |
56 | return false; |
57 | |
58 | return true; |
59 | } |
60 | |
61 | /* |
62 | * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., |
63 | * non-early mapping of the UEFI system table and virtual mappings for all |
64 | * EFI_MEMORY_RUNTIME regions. |
65 | */ |
66 | static int __init riscv_enable_runtime_services(void) |
67 | { |
68 | u64 mapsize; |
69 | |
70 | if (!efi_enabled(EFI_BOOT)) { |
71 | pr_info("EFI services will not be available.\n" ); |
72 | return 0; |
73 | } |
74 | |
75 | efi_memmap_unmap(); |
76 | |
77 | mapsize = efi.memmap.desc_size * efi.memmap.nr_map; |
78 | |
79 | if (efi_memmap_init_late(addr: efi.memmap.phys_map, size: mapsize)) { |
80 | pr_err("Failed to remap EFI memory map\n" ); |
81 | return 0; |
82 | } |
83 | |
84 | if (efi_soft_reserve_enabled()) { |
85 | efi_memory_desc_t *md; |
86 | |
87 | for_each_efi_memory_desc(md) { |
88 | u64 md_size = md->num_pages << EFI_PAGE_SHIFT; |
89 | struct resource *res; |
90 | |
91 | if (!(md->attribute & EFI_MEMORY_SP)) |
92 | continue; |
93 | |
94 | res = kzalloc(size: sizeof(*res), GFP_KERNEL); |
95 | if (WARN_ON(!res)) |
96 | break; |
97 | |
98 | res->start = md->phys_addr; |
99 | res->end = md->phys_addr + md_size - 1; |
100 | res->name = "Soft Reserved" ; |
101 | res->flags = IORESOURCE_MEM; |
102 | res->desc = IORES_DESC_SOFT_RESERVED; |
103 | |
104 | insert_resource(parent: &iomem_resource, new: res); |
105 | } |
106 | } |
107 | |
108 | if (efi_runtime_disabled()) { |
109 | pr_info("EFI runtime services will be disabled.\n" ); |
110 | return 0; |
111 | } |
112 | |
113 | if (efi_enabled(EFI_RUNTIME_SERVICES)) { |
114 | pr_info("EFI runtime services access via paravirt.\n" ); |
115 | return 0; |
116 | } |
117 | |
118 | pr_info("Remapping and enabling EFI services.\n" ); |
119 | |
120 | if (!efi_virtmap_init()) { |
121 | pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n" ); |
122 | return -ENOMEM; |
123 | } |
124 | |
125 | /* Set up runtime services function pointers */ |
126 | efi_native_runtime_setup(); |
127 | set_bit(EFI_RUNTIME_SERVICES, addr: &efi.flags); |
128 | |
129 | return 0; |
130 | } |
131 | early_initcall(riscv_enable_runtime_services); |
132 | |
133 | static void efi_virtmap_load(void) |
134 | { |
135 | preempt_disable(); |
136 | switch_mm(current->active_mm, next: &efi_mm, NULL); |
137 | } |
138 | |
139 | static void efi_virtmap_unload(void) |
140 | { |
141 | switch_mm(prev: &efi_mm, current->active_mm, NULL); |
142 | preempt_enable(); |
143 | } |
144 | |
145 | void arch_efi_call_virt_setup(void) |
146 | { |
147 | sync_kernel_mappings(efi_mm.pgd); |
148 | efi_virtmap_load(); |
149 | } |
150 | |
151 | void arch_efi_call_virt_teardown(void) |
152 | { |
153 | efi_virtmap_unload(); |
154 | } |
155 | |