]>
Commit | Line | Data |
---|---|---|
b91540d5 AP |
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(&efi_mm); | |
36 | init_new_context(NULL, &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 == 0) | |
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(&efi_mm, 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(efi.memmap.phys_map, 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 | int 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(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(&iomem_resource, 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, &efi.flags); | |
128 | ||
129 | return 0; | |
130 | } | |
131 | early_initcall(riscv_enable_runtime_services); | |
132 | ||
133 | void efi_virtmap_load(void) | |
134 | { | |
135 | preempt_disable(); | |
136 | switch_mm(current->active_mm, &efi_mm, NULL); | |
137 | } | |
138 | ||
139 | void efi_virtmap_unload(void) | |
140 | { | |
141 | switch_mm(&efi_mm, current->active_mm, NULL); | |
142 | preempt_enable(); | |
143 | } |