forked from Minki/linux
efi/arm32-stub: Allow boot-time allocations in the vmlinux region
The arm32 kernel decompresses itself to the base of DRAM unconditionally, and so it is the EFI stub's job to ensure that the region is available. Currently, we do this by creating an allocation there, and giving up if that fails. However, any boot services regions occupying this area are not an issue, given that the decompressor executes strictly after the stub calls ExitBootServices(). So let's try a bit harder to proceed if the initial allocation fails, and check whether any memory map entries occupying the region may be considered safe. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org> Reviewed-by: Eugene Cohen <eugene@hp.com> Reviewed-by: Roy Franz <roy.franz@cavium.com> 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-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20170404160245.27812-11-ard.biesheuvel@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
b1d1776139
commit
318532bf63
@ -63,27 +63,25 @@ void free_screen_info(efi_system_table_t *sys_table_arg, struct screen_info *si)
|
||||
efi_call_early(free_pool, si);
|
||||
}
|
||||
|
||||
efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
|
||||
unsigned long *image_addr,
|
||||
unsigned long *image_size,
|
||||
unsigned long *reserve_addr,
|
||||
unsigned long *reserve_size,
|
||||
unsigned long dram_base,
|
||||
efi_loaded_image_t *image)
|
||||
static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg,
|
||||
unsigned long dram_base,
|
||||
unsigned long *reserve_addr,
|
||||
unsigned long *reserve_size)
|
||||
{
|
||||
unsigned long nr_pages;
|
||||
efi_status_t status;
|
||||
/* Use alloc_addr to tranlsate between types */
|
||||
efi_physical_addr_t alloc_addr;
|
||||
efi_memory_desc_t *memory_map;
|
||||
unsigned long nr_pages, map_size, desc_size, buff_size;
|
||||
efi_status_t status;
|
||||
unsigned long l;
|
||||
|
||||
/*
|
||||
* Verify that the DRAM base address is compatible with the ARM
|
||||
* boot protocol, which determines the base of DRAM by masking
|
||||
* off the low 27 bits of the address at which the zImage is
|
||||
* loaded. These assumptions are made by the decompressor,
|
||||
* before any memory map is available.
|
||||
*/
|
||||
dram_base = round_up(dram_base, SZ_128M);
|
||||
struct efi_boot_memmap map = {
|
||||
.map = &memory_map,
|
||||
.map_size = &map_size,
|
||||
.desc_size = &desc_size,
|
||||
.desc_ver = NULL,
|
||||
.key_ptr = NULL,
|
||||
.buff_size = &buff_size,
|
||||
};
|
||||
|
||||
/*
|
||||
* Reserve memory for the uncompressed kernel image. This is
|
||||
@ -94,18 +92,128 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
|
||||
* Do this very early, as prints can cause memory allocations
|
||||
* that may conflict with this.
|
||||
*/
|
||||
alloc_addr = dram_base;
|
||||
*reserve_size = MAX_UNCOMP_KERNEL_SIZE;
|
||||
nr_pages = round_up(*reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
|
||||
status = sys_table->boottime->allocate_pages(EFI_ALLOCATE_ADDRESS,
|
||||
EFI_LOADER_DATA,
|
||||
nr_pages, &alloc_addr);
|
||||
alloc_addr = dram_base + MAX_UNCOMP_KERNEL_SIZE;
|
||||
nr_pages = MAX_UNCOMP_KERNEL_SIZE / EFI_PAGE_SIZE;
|
||||
status = efi_call_early(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
|
||||
EFI_BOOT_SERVICES_DATA, nr_pages, &alloc_addr);
|
||||
if (status == EFI_SUCCESS) {
|
||||
if (alloc_addr == dram_base) {
|
||||
*reserve_addr = alloc_addr;
|
||||
*reserve_size = MAX_UNCOMP_KERNEL_SIZE;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
/*
|
||||
* If we end up here, the allocation succeeded but starts below
|
||||
* dram_base. This can only occur if the real base of DRAM is
|
||||
* not a multiple of 128 MB, in which case dram_base will have
|
||||
* been rounded up. Since this implies that a part of the region
|
||||
* was already occupied, we need to fall through to the code
|
||||
* below to ensure that the existing allocations don't conflict.
|
||||
* For this reason, we use EFI_BOOT_SERVICES_DATA above and not
|
||||
* EFI_LOADER_DATA, which we wouldn't able to distinguish from
|
||||
* allocations that we want to disallow.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* If the allocation above failed, we may still be able to proceed:
|
||||
* if the only allocations in the region are of types that will be
|
||||
* released to the OS after ExitBootServices(), the decompressor can
|
||||
* safely overwrite them.
|
||||
*/
|
||||
status = efi_get_memory_map(sys_table_arg, &map);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err(sys_table_arg,
|
||||
"reserve_kernel_base(): Unable to retrieve memory map.\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
for (l = 0; l < map_size; l += desc_size) {
|
||||
efi_memory_desc_t *desc;
|
||||
u64 start, end;
|
||||
|
||||
desc = (void *)memory_map + l;
|
||||
start = desc->phys_addr;
|
||||
end = start + desc->num_pages * EFI_PAGE_SIZE;
|
||||
|
||||
/* Skip if entry does not intersect with region */
|
||||
if (start >= dram_base + MAX_UNCOMP_KERNEL_SIZE ||
|
||||
end <= dram_base)
|
||||
continue;
|
||||
|
||||
switch (desc->type) {
|
||||
case EFI_BOOT_SERVICES_CODE:
|
||||
case EFI_BOOT_SERVICES_DATA:
|
||||
/* Ignore types that are released to the OS anyway */
|
||||
continue;
|
||||
|
||||
case EFI_CONVENTIONAL_MEMORY:
|
||||
/*
|
||||
* Reserve the intersection between this entry and the
|
||||
* region.
|
||||
*/
|
||||
start = max(start, (u64)dram_base);
|
||||
end = min(end, (u64)dram_base + MAX_UNCOMP_KERNEL_SIZE);
|
||||
|
||||
status = efi_call_early(allocate_pages,
|
||||
EFI_ALLOCATE_ADDRESS,
|
||||
EFI_LOADER_DATA,
|
||||
(end - start) / EFI_PAGE_SIZE,
|
||||
&start);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err(sys_table_arg,
|
||||
"reserve_kernel_base(): alloc failed.\n");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case EFI_LOADER_CODE:
|
||||
case EFI_LOADER_DATA:
|
||||
/*
|
||||
* These regions may be released and reallocated for
|
||||
* another purpose (including EFI_RUNTIME_SERVICE_DATA)
|
||||
* at any time during the execution of the OS loader,
|
||||
* so we cannot consider them as safe.
|
||||
*/
|
||||
default:
|
||||
/*
|
||||
* Treat any other allocation in the region as unsafe */
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
status = EFI_SUCCESS;
|
||||
out:
|
||||
efi_call_early(free_pool, memory_map);
|
||||
return status;
|
||||
}
|
||||
|
||||
efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
|
||||
unsigned long *image_addr,
|
||||
unsigned long *image_size,
|
||||
unsigned long *reserve_addr,
|
||||
unsigned long *reserve_size,
|
||||
unsigned long dram_base,
|
||||
efi_loaded_image_t *image)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
/*
|
||||
* Verify that the DRAM base address is compatible with the ARM
|
||||
* boot protocol, which determines the base of DRAM by masking
|
||||
* off the low 27 bits of the address at which the zImage is
|
||||
* loaded. These assumptions are made by the decompressor,
|
||||
* before any memory map is available.
|
||||
*/
|
||||
dram_base = round_up(dram_base, SZ_128M);
|
||||
|
||||
status = reserve_kernel_base(sys_table, dram_base, reserve_addr,
|
||||
reserve_size);
|
||||
if (status != EFI_SUCCESS) {
|
||||
*reserve_size = 0;
|
||||
pr_efi_err(sys_table, "Unable to allocate memory for uncompressed kernel.\n");
|
||||
return status;
|
||||
}
|
||||
*reserve_addr = alloc_addr;
|
||||
|
||||
/*
|
||||
* Relocate the zImage, so that it appears in the lowest 128 MB
|
||||
|
Loading…
Reference in New Issue
Block a user