Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/efi-stub.c
51324 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* EFI stub implementation that is shared by arm and arm64 architectures.
4
* This should be #included by the EFI stub implementation files.
5
*
6
* Copyright (C) 2013,2014 Linaro Limited
7
* Roy Franz <[email protected]
8
* Copyright (C) 2013 Red Hat, Inc.
9
* Mark Salter <[email protected]>
10
*/
11
12
#include <linux/efi.h>
13
#include <linux/sysfb.h>
14
#include <asm/efi.h>
15
16
#include "efistub.h"
17
18
/*
19
* This is the base address at which to start allocating virtual memory ranges
20
* for UEFI Runtime Services.
21
*
22
* For ARM/ARM64:
23
* This is in the low TTBR0 range so that we can use
24
* any allocation we choose, and eliminate the risk of a conflict after kexec.
25
* The value chosen is the largest non-zero power of 2 suitable for this purpose
26
* both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can
27
* be mapped efficiently.
28
* Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split,
29
* map everything below 1 GB. (512 MB is a reasonable upper bound for the
30
* entire footprint of the UEFI runtime services memory regions)
31
*
32
* For RISC-V:
33
* There is no specific reason for which, this address (512MB) can't be used
34
* EFI runtime virtual address for RISC-V. It also helps to use EFI runtime
35
* services on both RV32/RV64. Keep the same runtime virtual address for RISC-V
36
* as well to minimize the code churn.
37
*/
38
#define EFI_RT_VIRTUAL_BASE SZ_512M
39
40
/*
41
* Some architectures map the EFI regions into the kernel's linear map using a
42
* fixed offset.
43
*/
44
#ifndef EFI_RT_VIRTUAL_OFFSET
45
#define EFI_RT_VIRTUAL_OFFSET 0
46
#endif
47
48
static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
49
static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
50
51
void __weak free_primary_display(struct sysfb_display_info *dpy)
52
{ }
53
54
static struct sysfb_display_info *setup_primary_display(void)
55
{
56
struct sysfb_display_info *dpy;
57
struct screen_info *screen = NULL;
58
struct edid_info *edid = NULL;
59
efi_status_t status;
60
61
dpy = alloc_primary_display();
62
if (!dpy)
63
return NULL;
64
screen = &dpy->screen;
65
#if defined(CONFIG_FIRMWARE_EDID)
66
edid = &dpy->edid;
67
#endif
68
69
status = efi_setup_graphics(screen, edid);
70
if (status != EFI_SUCCESS)
71
goto err_free_primary_display;
72
73
return dpy;
74
75
err_free_primary_display:
76
free_primary_display(dpy);
77
return NULL;
78
}
79
80
static void install_memreserve_table(void)
81
{
82
struct linux_efi_memreserve *rsv;
83
efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
84
efi_status_t status;
85
86
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
87
(void **)&rsv);
88
if (status != EFI_SUCCESS) {
89
efi_err("Failed to allocate memreserve entry!\n");
90
return;
91
}
92
93
rsv->next = 0;
94
rsv->size = 0;
95
atomic_set(&rsv->count, 0);
96
97
status = efi_bs_call(install_configuration_table,
98
&memreserve_table_guid, rsv);
99
if (status != EFI_SUCCESS)
100
efi_err("Failed to install memreserve config table!\n");
101
}
102
103
static u32 get_supported_rt_services(void)
104
{
105
const efi_rt_properties_table_t *rt_prop_table;
106
u32 supported = EFI_RT_SUPPORTED_ALL;
107
108
rt_prop_table = get_efi_config_table(EFI_RT_PROPERTIES_TABLE_GUID);
109
if (rt_prop_table)
110
supported &= rt_prop_table->runtime_services_supported;
111
112
return supported;
113
}
114
115
efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr)
116
{
117
char *cmdline __free(efi_pool) = NULL;
118
efi_status_t status;
119
120
/*
121
* Get the command line from EFI, using the LOADED_IMAGE
122
* protocol. We are going to copy the command line into the
123
* device tree, so this can be allocated anywhere.
124
*/
125
cmdline = efi_convert_cmdline(image);
126
if (!cmdline) {
127
efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
128
return EFI_OUT_OF_RESOURCES;
129
}
130
131
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) {
132
status = efi_parse_options(cmdline);
133
if (status != EFI_SUCCESS) {
134
efi_err("Failed to parse EFI load options\n");
135
return status;
136
}
137
}
138
139
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
140
IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
141
cmdline[0] == 0) {
142
status = efi_parse_options(CONFIG_CMDLINE);
143
if (status != EFI_SUCCESS) {
144
efi_err("Failed to parse built-in command line\n");
145
return status;
146
}
147
}
148
149
*cmdline_ptr = no_free_ptr(cmdline);
150
return EFI_SUCCESS;
151
}
152
153
efi_status_t efi_stub_common(efi_handle_t handle,
154
efi_loaded_image_t *image,
155
unsigned long image_addr,
156
char *cmdline_ptr)
157
{
158
struct sysfb_display_info *dpy;
159
efi_status_t status;
160
161
status = check_platform_features();
162
if (status != EFI_SUCCESS)
163
return status;
164
165
dpy = setup_primary_display();
166
167
efi_retrieve_eventlog();
168
169
/* Ask the firmware to clear memory on unclean shutdown */
170
efi_enable_reset_attack_mitigation();
171
172
efi_load_initrd(image, ULONG_MAX, efi_get_max_initrd_addr(image_addr),
173
NULL);
174
175
efi_random_get_seed();
176
177
/* force efi_novamap if SetVirtualAddressMap() is unsupported */
178
efi_novamap |= !(get_supported_rt_services() &
179
EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP);
180
181
install_memreserve_table();
182
183
status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr);
184
185
free_primary_display(dpy);
186
187
return status;
188
}
189
190
/*
191
* efi_allocate_virtmap() - create a pool allocation for the virtmap
192
*
193
* Create an allocation that is of sufficient size to hold all the memory
194
* descriptors that will be passed to SetVirtualAddressMap() to inform the
195
* firmware about the virtual mapping that will be used under the OS to call
196
* into the firmware.
197
*/
198
efi_status_t efi_alloc_virtmap(efi_memory_desc_t **virtmap,
199
unsigned long *desc_size, u32 *desc_ver)
200
{
201
unsigned long size, mmap_key;
202
efi_status_t status;
203
204
/*
205
* Use the size of the current memory map as an upper bound for the
206
* size of the buffer we need to pass to SetVirtualAddressMap() to
207
* cover all EFI_MEMORY_RUNTIME regions.
208
*/
209
size = 0;
210
status = efi_bs_call(get_memory_map, &size, NULL, &mmap_key, desc_size,
211
desc_ver);
212
if (status != EFI_BUFFER_TOO_SMALL)
213
return EFI_LOAD_ERROR;
214
215
return efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
216
(void **)virtmap);
217
}
218
219
/*
220
* efi_get_virtmap() - create a virtual mapping for the EFI memory map
221
*
222
* This function populates the virt_addr fields of all memory region descriptors
223
* in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors
224
* are also copied to @runtime_map, and their total count is returned in @count.
225
*/
226
void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
227
unsigned long desc_size, efi_memory_desc_t *runtime_map,
228
int *count)
229
{
230
u64 efi_virt_base = virtmap_base;
231
efi_memory_desc_t *in, *out = runtime_map;
232
int l;
233
234
*count = 0;
235
236
for (l = 0; l < map_size; l += desc_size) {
237
u64 paddr, size;
238
239
in = (void *)memory_map + l;
240
if (!(in->attribute & EFI_MEMORY_RUNTIME))
241
continue;
242
243
paddr = in->phys_addr;
244
size = in->num_pages * EFI_PAGE_SIZE;
245
246
in->virt_addr = in->phys_addr + EFI_RT_VIRTUAL_OFFSET;
247
if (efi_novamap) {
248
continue;
249
}
250
251
/*
252
* Make the mapping compatible with 64k pages: this allows
253
* a 4k page size kernel to kexec a 64k page size kernel and
254
* vice versa.
255
*/
256
if (!flat_va_mapping) {
257
258
paddr = round_down(in->phys_addr, SZ_64K);
259
size += in->phys_addr - paddr;
260
261
/*
262
* Avoid wasting memory on PTEs by choosing a virtual
263
* base that is compatible with section mappings if this
264
* region has the appropriate size and physical
265
* alignment. (Sections are 2 MB on 4k granule kernels)
266
*/
267
if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M)
268
efi_virt_base = round_up(efi_virt_base, SZ_2M);
269
else
270
efi_virt_base = round_up(efi_virt_base, SZ_64K);
271
272
in->virt_addr += efi_virt_base - paddr;
273
efi_virt_base += size;
274
}
275
276
memcpy(out, in, desc_size);
277
out = (void *)out + desc_size;
278
++*count;
279
}
280
}
281
282