Commit abfb7b68 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Ingo Molnar

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: default avatarJames Morse <james.morse@arm.com>
Signed-off-by: default avatarArd 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: ed9cc156 ("efi/libstub: Use efi_exit_boot_services() in FDT")
Link: http://lkml.kernel.org/r/1482587963-20183-2-git-send-email-ard.biesheuvel@linaro.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 2d706e79
...@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle); ...@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle);
unsigned long get_dram_base(efi_system_table_t *sys_table_arg); unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
unsigned long orig_fdt_size,
void *fdt, int new_fdt_size, char *cmdline_ptr,
u64 initrd_addr, u64 initrd_size,
efi_memory_desc_t *memory_map,
unsigned long map_size, unsigned long desc_size,
u32 desc_ver);
efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
void *handle, void *handle,
unsigned long *new_fdt_addr, unsigned long *new_fdt_addr,
......
...@@ -16,13 +16,10 @@ ...@@ -16,13 +16,10 @@
#include "efistub.h" #include "efistub.h"
efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
unsigned long orig_fdt_size, unsigned long orig_fdt_size,
void *fdt, int new_fdt_size, char *cmdline_ptr, void *fdt, int new_fdt_size, char *cmdline_ptr,
u64 initrd_addr, u64 initrd_size, u64 initrd_addr, u64 initrd_size)
efi_memory_desc_t *memory_map,
unsigned long map_size, unsigned long desc_size,
u32 desc_ver)
{ {
int node, num_rsv; int node, num_rsv;
int status; int status;
...@@ -101,25 +98,23 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, ...@@ -101,25 +98,23 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); fdt_val64 = U64_MAX; /* placeholder */
status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
&fdt_val64, sizeof(fdt_val64)); &fdt_val64, sizeof(fdt_val64));
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val32 = cpu_to_fdt32(map_size); fdt_val32 = U32_MAX; /* placeholder */
status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
&fdt_val32, sizeof(fdt_val32)); &fdt_val32, sizeof(fdt_val32));
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val32 = cpu_to_fdt32(desc_size);
status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
&fdt_val32, sizeof(fdt_val32)); &fdt_val32, sizeof(fdt_val32));
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val32 = cpu_to_fdt32(desc_ver);
status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
&fdt_val32, sizeof(fdt_val32)); &fdt_val32, sizeof(fdt_val32));
if (status) if (status)
...@@ -148,6 +143,43 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, ...@@ -148,6 +143,43 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
return EFI_LOAD_ERROR; return EFI_LOAD_ERROR;
} }
static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
{
int node = fdt_path_offset(fdt, "/chosen");
u64 fdt_val64;
u32 fdt_val32;
int err;
if (node < 0)
return EFI_LOAD_ERROR;
fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start",
&fdt_val64, sizeof(fdt_val64));
if (err)
return EFI_LOAD_ERROR;
fdt_val32 = cpu_to_fdt32(*map->map_size);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size",
&fdt_val32, sizeof(fdt_val32));
if (err)
return EFI_LOAD_ERROR;
fdt_val32 = cpu_to_fdt32(*map->desc_size);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size",
&fdt_val32, sizeof(fdt_val32));
if (err)
return EFI_LOAD_ERROR;
fdt_val32 = cpu_to_fdt32(*map->desc_ver);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver",
&fdt_val32, sizeof(fdt_val32));
if (err)
return EFI_LOAD_ERROR;
return EFI_SUCCESS;
}
#ifndef EFI_FDT_ALIGN #ifndef EFI_FDT_ALIGN
#define EFI_FDT_ALIGN EFI_PAGE_SIZE #define EFI_FDT_ALIGN EFI_PAGE_SIZE
#endif #endif
...@@ -243,20 +275,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -243,20 +275,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
goto fail; goto fail;
} }
/*
* Now that we have done our final memory allocation (and free)
* we can get the memory map key needed for
* exit_boot_services().
*/
status = efi_get_memory_map(sys_table, &map);
if (status != EFI_SUCCESS)
goto fail_free_new_fdt;
status = update_fdt(sys_table, status = update_fdt(sys_table,
(void *)fdt_addr, fdt_size, (void *)fdt_addr, fdt_size,
(void *)*new_fdt_addr, new_fdt_size, (void *)*new_fdt_addr, new_fdt_size,
cmdline_ptr, initrd_addr, initrd_size, cmdline_ptr, initrd_addr, initrd_size);
memory_map, map_size, desc_size, desc_ver);
/* Succeeding the first time is the expected case. */ /* Succeeding the first time is the expected case. */
if (status == EFI_SUCCESS) if (status == EFI_SUCCESS)
...@@ -266,20 +288,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -266,20 +288,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
/* /*
* We need to allocate more space for the new * We need to allocate more space for the new
* device tree, so free existing buffer that is * device tree, so free existing buffer that is
* too small. Also free memory map, as we will need * too small.
* to get new one that reflects the free/alloc we do
* on the device tree buffer.
*/ */
efi_free(sys_table, new_fdt_size, *new_fdt_addr); efi_free(sys_table, new_fdt_size, *new_fdt_addr);
sys_table->boottime->free_pool(memory_map);
new_fdt_size += EFI_PAGE_SIZE; new_fdt_size += EFI_PAGE_SIZE;
} else { } else {
pr_efi_err(sys_table, "Unable to construct new device tree.\n"); pr_efi_err(sys_table, "Unable to construct new device tree.\n");
goto fail_free_mmap; goto fail_free_new_fdt;
} }
} }
sys_table->boottime->free_pool(memory_map);
priv.runtime_map = runtime_map; priv.runtime_map = runtime_map;
priv.runtime_entry_count = &runtime_entry_count; priv.runtime_entry_count = &runtime_entry_count;
status = efi_exit_boot_services(sys_table, handle, &map, &priv, 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,6 +306,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
if (status == EFI_SUCCESS) { if (status == EFI_SUCCESS) {
efi_set_virtual_address_map_t *svam; efi_set_virtual_address_map_t *svam;
status = update_fdt_memmap((void *)*new_fdt_addr, &map);
if (status != EFI_SUCCESS) {
/*
* The kernel won't get far without the memory map, but
* may still be able to print something meaningful so
* return success here.
*/
return EFI_SUCCESS;
}
/* Install the new virtual address map */ /* Install the new virtual address map */
svam = sys_table->runtime->set_virtual_address_map; svam = sys_table->runtime->set_virtual_address_map;
status = svam(runtime_entry_count * desc_size, desc_size, 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,9 +347,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
pr_efi_err(sys_table, "Exit boot services failed.\n"); pr_efi_err(sys_table, "Exit boot services failed.\n");
fail_free_mmap:
sys_table->boottime->free_pool(memory_map);
fail_free_new_fdt: fail_free_new_fdt:
efi_free(sys_table, new_fdt_size, *new_fdt_addr); efi_free(sys_table, new_fdt_size, *new_fdt_addr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment