aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2014-10-20 10:27:26 -0400
committerArd Biesheuvel <ard.biesheuvel@linaro.org>2015-01-12 11:29:12 -0500
commitf3cdfd239da56a4cea75a2920dc326f0f45f67e3 (patch)
tree71eef379af8b2aac7232d21bdcbb4ae390ae9ba5 /drivers/firmware
parent1bd0abb0c924a8b28c6466cdd6bb34ea053541dc (diff)
arm64/efi: move SetVirtualAddressMap() to UEFI stub
In order to support kexec, the kernel needs to be able to deal with the state of the UEFI firmware after SetVirtualAddressMap() has been called. To avoid having separate code paths for non-kexec and kexec, let's move the call to SetVirtualAddressMap() to the stub: this will guarantee us that it will only be called once (since the stub is not executed during kexec), and ensures that the UEFI state is identical between kexec and normal boot. This implies that the layout of the virtual mapping needs to be created by the stub as well. All regions are rounded up to a naturally aligned multiple of 64 KB (for compatibility with 64k pages kernels) and recorded in the UEFI memory map. The kernel proper reads those values and installs the mappings in a dedicated set of page tables that are swapped in during UEFI Runtime Services calls. Acked-by: Leif Lindholm <leif.lindholm@linaro.org> Acked-by: Matt Fleming <matt.fleming@intel.com> Tested-by: Leif Lindholm <leif.lindholm@linaro.org> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efi/libstub/arm-stub.c59
-rw-r--r--drivers/firmware/efi/libstub/efistub.h4
-rw-r--r--drivers/firmware/efi/libstub/fdt.c62
3 files changed, 122 insertions, 3 deletions
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c
index eb48a1a1a576..e2432b39b6df 100644
--- a/drivers/firmware/efi/libstub/arm-stub.c
+++ b/drivers/firmware/efi/libstub/arm-stub.c
@@ -295,3 +295,62 @@ fail_free_image:
295fail: 295fail:
296 return EFI_ERROR; 296 return EFI_ERROR;
297} 297}
298
299/*
300 * This is the base address at which to start allocating virtual memory ranges
301 * for UEFI Runtime Services. This is in the low TTBR0 range so that we can use
302 * any allocation we choose, and eliminate the risk of a conflict after kexec.
303 * The value chosen is the largest non-zero power of 2 suitable for this purpose
304 * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can
305 * be mapped efficiently.
306 */
307#define EFI_RT_VIRTUAL_BASE 0x40000000
308
309/*
310 * efi_get_virtmap() - create a virtual mapping for the EFI memory map
311 *
312 * This function populates the virt_addr fields of all memory region descriptors
313 * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors
314 * are also copied to @runtime_map, and their total count is returned in @count.
315 */
316void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
317 unsigned long desc_size, efi_memory_desc_t *runtime_map,
318 int *count)
319{
320 u64 efi_virt_base = EFI_RT_VIRTUAL_BASE;
321 efi_memory_desc_t *out = runtime_map;
322 int l;
323
324 for (l = 0; l < map_size; l += desc_size) {
325 efi_memory_desc_t *in = (void *)memory_map + l;
326 u64 paddr, size;
327
328 if (!(in->attribute & EFI_MEMORY_RUNTIME))
329 continue;
330
331 /*
332 * Make the mapping compatible with 64k pages: this allows
333 * a 4k page size kernel to kexec a 64k page size kernel and
334 * vice versa.
335 */
336 paddr = round_down(in->phys_addr, SZ_64K);
337 size = round_up(in->num_pages * EFI_PAGE_SIZE +
338 in->phys_addr - paddr, SZ_64K);
339
340 /*
341 * Avoid wasting memory on PTEs by choosing a virtual base that
342 * is compatible with section mappings if this region has the
343 * appropriate size and physical alignment. (Sections are 2 MB
344 * on 4k granule kernels)
345 */
346 if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M)
347 efi_virt_base = round_up(efi_virt_base, SZ_2M);
348
349 in->virt_addr = efi_virt_base + in->phys_addr - paddr;
350 efi_virt_base += size;
351
352 memcpy(out, in, desc_size);
353 out = (void *)out + desc_size;
354 ++*count;
355 }
356}
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 304ab295ca1a..2be10984a67a 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -39,4 +39,8 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
39 39
40void *get_fdt(efi_system_table_t *sys_table); 40void *get_fdt(efi_system_table_t *sys_table);
41 41
42void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
43 unsigned long desc_size, efi_memory_desc_t *runtime_map,
44 int *count);
45
42#endif 46#endif
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index c846a9608cbd..91da56c4fd54 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -14,6 +14,8 @@
14#include <linux/libfdt.h> 14#include <linux/libfdt.h>
15#include <asm/efi.h> 15#include <asm/efi.h>
16 16
17#include "efistub.h"
18
17efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, 19efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
18 unsigned long orig_fdt_size, 20 unsigned long orig_fdt_size,
19 void *fdt, int new_fdt_size, char *cmdline_ptr, 21 void *fdt, int new_fdt_size, char *cmdline_ptr,
@@ -193,9 +195,26 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
193 unsigned long map_size, desc_size; 195 unsigned long map_size, desc_size;
194 u32 desc_ver; 196 u32 desc_ver;
195 unsigned long mmap_key; 197 unsigned long mmap_key;
196 efi_memory_desc_t *memory_map; 198 efi_memory_desc_t *memory_map, *runtime_map;
197 unsigned long new_fdt_size; 199 unsigned long new_fdt_size;
198 efi_status_t status; 200 efi_status_t status;
201 int runtime_entry_count = 0;
202
203 /*
204 * Get a copy of the current memory map that we will use to prepare
205 * the input for SetVirtualAddressMap(). We don't have to worry about
206 * subsequent allocations adding entries, since they could not affect
207 * the number of EFI_MEMORY_RUNTIME regions.
208 */
209 status = efi_get_memory_map(sys_table, &runtime_map, &map_size,
210 &desc_size, &desc_ver, &mmap_key);
211 if (status != EFI_SUCCESS) {
212 pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n");
213 return status;
214 }
215
216 pr_efi(sys_table,
217 "Exiting boot services and installing virtual address map...\n");
199 218
200 /* 219 /*
201 * Estimate size of new FDT, and allocate memory for it. We 220 * Estimate size of new FDT, and allocate memory for it. We
@@ -248,12 +267,48 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
248 } 267 }
249 } 268 }
250 269
270 /*
271 * Update the memory map with virtual addresses. The function will also
272 * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
273 * entries so that we can pass it straight into SetVirtualAddressMap()
274 */
275 efi_get_virtmap(memory_map, map_size, desc_size, runtime_map,
276 &runtime_entry_count);
277
251 /* Now we are ready to exit_boot_services.*/ 278 /* Now we are ready to exit_boot_services.*/
252 status = sys_table->boottime->exit_boot_services(handle, mmap_key); 279 status = sys_table->boottime->exit_boot_services(handle, mmap_key);
253 280
281 if (status == EFI_SUCCESS) {
282 efi_set_virtual_address_map_t *svam;
254 283
255 if (status == EFI_SUCCESS) 284 /* Install the new virtual address map */
256 return status; 285 svam = sys_table->runtime->set_virtual_address_map;
286 status = svam(runtime_entry_count * desc_size, desc_size,
287 desc_ver, runtime_map);
288
289 /*
290 * We are beyond the point of no return here, so if the call to
291 * SetVirtualAddressMap() failed, we need to signal that to the
292 * incoming kernel but proceed normally otherwise.
293 */
294 if (status != EFI_SUCCESS) {
295 int l;
296
297 /*
298 * Set the virtual address field of all
299 * EFI_MEMORY_RUNTIME entries to 0. This will signal
300 * the incoming kernel that no virtual translation has
301 * been installed.
302 */
303 for (l = 0; l < map_size; l += desc_size) {
304 efi_memory_desc_t *p = (void *)memory_map + l;
305
306 if (p->attribute & EFI_MEMORY_RUNTIME)
307 p->virt_addr = 0;
308 }
309 }
310 return EFI_SUCCESS;
311 }
257 312
258 pr_efi_err(sys_table, "Exit boot services failed.\n"); 313 pr_efi_err(sys_table, "Exit boot services failed.\n");
259 314
@@ -264,6 +319,7 @@ fail_free_new_fdt:
264 efi_free(sys_table, new_fdt_size, *new_fdt_addr); 319 efi_free(sys_table, new_fdt_size, *new_fdt_addr);
265 320
266fail: 321fail:
322 sys_table->boottime->free_pool(runtime_map);
267 return EFI_LOAD_ERROR; 323 return EFI_LOAD_ERROR;
268} 324}
269 325