aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi/libstub/fdt.c
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/efi/libstub/fdt.c
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/efi/libstub/fdt.c')
-rw-r--r--drivers/firmware/efi/libstub/fdt.c62
1 files changed, 59 insertions, 3 deletions
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