aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2016-12-24 08:59:23 -0500
committerIngo Molnar <mingo@kernel.org>2016-12-28 03:23:32 -0500
commitabfb7b686a3e5be27bf81db62f9c5c895b76f5d1 (patch)
treee2aa96d0faf8bd1bc7ba7c94c152d7b4a8ceb6ef
parent2d706e790f0508dff4fb72eca9b4892b79757feb (diff)
efi/libstub/arm*: Pass latest memory map to the kernel
As reported by James Morse, the current libstub code involving the annotated memory map only works somewhat correctly by accident, due to the fact that a pool allocation happens to be reused immediately, retaining its former contents on most implementations of the UEFI boot services. Instead of juggling memory maps, which makes the code more complex than it needs to be, simply put placeholder values into the FDT for the memory map parameters, and only write the actual values after ExitBootServices() has been called. Reported-by: James Morse <james.morse@arm.com> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: <stable@vger.kernel.org> Cc: Jeffrey Hugo <jhugo@codeaurora.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Matt Fleming <matt@codeblueprint.co.uk> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-arm-kernel@lists.infradead.org Cc: linux-efi@vger.kernel.org Fixes: ed9cc156c42f ("efi/libstub: Use efi_exit_boot_services() in FDT") Link: http://lkml.kernel.org/r/1482587963-20183-2-git-send-email-ard.biesheuvel@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--drivers/firmware/efi/libstub/efistub.h8
-rw-r--r--drivers/firmware/efi/libstub/fdt.c87
2 files changed, 56 insertions, 39 deletions
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index b98824e3800a..0e2a96b12cb3 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle);
39 39
40unsigned long get_dram_base(efi_system_table_t *sys_table_arg); 40unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
41 41
42efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
43 unsigned long orig_fdt_size,
44 void *fdt, int new_fdt_size, char *cmdline_ptr,
45 u64 initrd_addr, u64 initrd_size,
46 efi_memory_desc_t *memory_map,
47 unsigned long map_size, unsigned long desc_size,
48 u32 desc_ver);
49
50efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, 42efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
51 void *handle, 43 void *handle,
52 unsigned long *new_fdt_addr, 44 unsigned long *new_fdt_addr,
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index a6a93116a8f0..921dfa047202 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -16,13 +16,10 @@
16 16
17#include "efistub.h" 17#include "efistub.h"
18 18
19efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, 19static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
20 unsigned long orig_fdt_size, 20 unsigned long orig_fdt_size,
21 void *fdt, int new_fdt_size, char *cmdline_ptr, 21 void *fdt, int new_fdt_size, char *cmdline_ptr,
22 u64 initrd_addr, u64 initrd_size, 22 u64 initrd_addr, u64 initrd_size)
23 efi_memory_desc_t *memory_map,
24 unsigned long map_size, unsigned long desc_size,
25 u32 desc_ver)
26{ 23{
27 int node, num_rsv; 24 int node, num_rsv;
28 int status; 25 int status;
@@ -101,25 +98,23 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
101 if (status) 98 if (status)
102 goto fdt_set_fail; 99 goto fdt_set_fail;
103 100
104 fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); 101 fdt_val64 = U64_MAX; /* placeholder */
105 status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", 102 status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
106 &fdt_val64, sizeof(fdt_val64)); 103 &fdt_val64, sizeof(fdt_val64));
107 if (status) 104 if (status)
108 goto fdt_set_fail; 105 goto fdt_set_fail;
109 106
110 fdt_val32 = cpu_to_fdt32(map_size); 107 fdt_val32 = U32_MAX; /* placeholder */
111 status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", 108 status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
112 &fdt_val32, sizeof(fdt_val32)); 109 &fdt_val32, sizeof(fdt_val32));
113 if (status) 110 if (status)
114 goto fdt_set_fail; 111 goto fdt_set_fail;
115 112
116 fdt_val32 = cpu_to_fdt32(desc_size);
117 status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", 113 status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
118 &fdt_val32, sizeof(fdt_val32)); 114 &fdt_val32, sizeof(fdt_val32));
119 if (status) 115 if (status)
120 goto fdt_set_fail; 116 goto fdt_set_fail;
121 117
122 fdt_val32 = cpu_to_fdt32(desc_ver);
123 status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", 118 status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
124 &fdt_val32, sizeof(fdt_val32)); 119 &fdt_val32, sizeof(fdt_val32));
125 if (status) 120 if (status)
@@ -148,6 +143,43 @@ fdt_set_fail:
148 return EFI_LOAD_ERROR; 143 return EFI_LOAD_ERROR;
149} 144}
150 145
146static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
147{
148 int node = fdt_path_offset(fdt, "/chosen");
149 u64 fdt_val64;
150 u32 fdt_val32;
151 int err;
152
153 if (node < 0)
154 return EFI_LOAD_ERROR;
155
156 fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
157 err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start",
158 &fdt_val64, sizeof(fdt_val64));
159 if (err)
160 return EFI_LOAD_ERROR;
161
162 fdt_val32 = cpu_to_fdt32(*map->map_size);
163 err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size",
164 &fdt_val32, sizeof(fdt_val32));
165 if (err)
166 return EFI_LOAD_ERROR;
167
168 fdt_val32 = cpu_to_fdt32(*map->desc_size);
169 err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size",
170 &fdt_val32, sizeof(fdt_val32));
171 if (err)
172 return EFI_LOAD_ERROR;
173
174 fdt_val32 = cpu_to_fdt32(*map->desc_ver);
175 err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver",
176 &fdt_val32, sizeof(fdt_val32));
177 if (err)
178 return EFI_LOAD_ERROR;
179
180 return EFI_SUCCESS;
181}
182
151#ifndef EFI_FDT_ALIGN 183#ifndef EFI_FDT_ALIGN
152#define EFI_FDT_ALIGN EFI_PAGE_SIZE 184#define EFI_FDT_ALIGN EFI_PAGE_SIZE
153#endif 185#endif
@@ -243,20 +275,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
243 goto fail; 275 goto fail;
244 } 276 }
245 277
246 /*
247 * Now that we have done our final memory allocation (and free)
248 * we can get the memory map key needed for
249 * exit_boot_services().
250 */
251 status = efi_get_memory_map(sys_table, &map);
252 if (status != EFI_SUCCESS)
253 goto fail_free_new_fdt;
254
255 status = update_fdt(sys_table, 278 status = update_fdt(sys_table,
256 (void *)fdt_addr, fdt_size, 279 (void *)fdt_addr, fdt_size,
257 (void *)*new_fdt_addr, new_fdt_size, 280 (void *)*new_fdt_addr, new_fdt_size,
258 cmdline_ptr, initrd_addr, initrd_size, 281 cmdline_ptr, initrd_addr, initrd_size);
259 memory_map, map_size, desc_size, desc_ver);
260 282
261 /* Succeeding the first time is the expected case. */ 283 /* Succeeding the first time is the expected case. */
262 if (status == EFI_SUCCESS) 284 if (status == EFI_SUCCESS)
@@ -266,20 +288,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
266 /* 288 /*
267 * We need to allocate more space for the new 289 * We need to allocate more space for the new
268 * device tree, so free existing buffer that is 290 * device tree, so free existing buffer that is
269 * too small. Also free memory map, as we will need 291 * too small.
270 * to get new one that reflects the free/alloc we do
271 * on the device tree buffer.
272 */ 292 */
273 efi_free(sys_table, new_fdt_size, *new_fdt_addr); 293 efi_free(sys_table, new_fdt_size, *new_fdt_addr);
274 sys_table->boottime->free_pool(memory_map);
275 new_fdt_size += EFI_PAGE_SIZE; 294 new_fdt_size += EFI_PAGE_SIZE;
276 } else { 295 } else {
277 pr_efi_err(sys_table, "Unable to construct new device tree.\n"); 296 pr_efi_err(sys_table, "Unable to construct new device tree.\n");
278 goto fail_free_mmap; 297 goto fail_free_new_fdt;
279 } 298 }
280 } 299 }
281 300
282 sys_table->boottime->free_pool(memory_map);
283 priv.runtime_map = runtime_map; 301 priv.runtime_map = runtime_map;
284 priv.runtime_entry_count = &runtime_entry_count; 302 priv.runtime_entry_count = &runtime_entry_count;
285 status = efi_exit_boot_services(sys_table, handle, &map, &priv, 303 status = efi_exit_boot_services(sys_table, handle, &map, &priv,
@@ -288,6 +306,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
288 if (status == EFI_SUCCESS) { 306 if (status == EFI_SUCCESS) {
289 efi_set_virtual_address_map_t *svam; 307 efi_set_virtual_address_map_t *svam;
290 308
309 status = update_fdt_memmap((void *)*new_fdt_addr, &map);
310 if (status != EFI_SUCCESS) {
311 /*
312 * The kernel won't get far without the memory map, but
313 * may still be able to print something meaningful so
314 * return success here.
315 */
316 return EFI_SUCCESS;
317 }
318
291 /* Install the new virtual address map */ 319 /* Install the new virtual address map */
292 svam = sys_table->runtime->set_virtual_address_map; 320 svam = sys_table->runtime->set_virtual_address_map;
293 status = svam(runtime_entry_count * desc_size, desc_size, 321 status = svam(runtime_entry_count * desc_size, desc_size,
@@ -319,9 +347,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
319 347
320 pr_efi_err(sys_table, "Exit boot services failed.\n"); 348 pr_efi_err(sys_table, "Exit boot services failed.\n");
321 349
322fail_free_mmap:
323 sys_table->boottime->free_pool(memory_map);
324
325fail_free_new_fdt: 350fail_free_new_fdt:
326 efi_free(sys_table, new_fdt_size, *new_fdt_addr); 351 efi_free(sys_table, new_fdt_size, *new_fdt_addr);
327 352