Re: [PATCH] efi/arm32-stub: allow boottime allocations in the vmlinux region

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Mar 16, 2017 at 10:56:16AM +0000, Ard Biesheuvel wrote:
> 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.
> 
> Reported-by: Eugene Cohen <eugene@xxxxxx>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx>
> ---
> 
> NOTE: This patch appears to have uncovered a bug in DxeCore's AllocatePages
> routine. If the first allocate_pages(EFI_ALLOCATE_ADDRESS) call fails, we may
> still end up with a memory map that reflects a kind of limbo state where the
> intended allocation is carved out and partially converted.
> 
> For example, starting from
> 
> 0x000040000000-0x00004007ffff [ConventionalMemory ]
> 0x000040080000-0x00004009ffff [Boot Data          ]
> 0x0000400a0000-0x000047ffffff [ConventionalMemory ]
> 
> the failed allocation of 32 MB of LoaderData @ 0x4000_0000 will result in
> 
> 0x000040000000-0x00004007ffff [Loader Data        ]
> 0x000040080000-0x00004009ffff [Boot Data          ]
> 0x0000400a0000-0x000047ffffff [ConventionalMemory ]
> 
> after which scanning the region for LoaderData regions (which we should reject
> given that they could be freed and replaced with, e.g., runtime services data
> regions) will always fail.
> 
> For this reason, the allocate_pages(EFI_ALLOCATE_ADDRESS) has been modified to
> use EfiBootServicesData instead. In the mean time, I will report this to the
> EDK2 development mailing list.

That feels a little bit eeew, but I can't see it breaking anything.

>  drivers/firmware/efi/libstub/arm32-stub.c | 137 +++++++++++++++++---
>  1 file changed, 117 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> index e1f0b28e1dcb..4e1b6478986e 100644
> --- a/drivers/firmware/efi/libstub/arm32-stub.c
> +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> @@ -63,6 +63,121 @@ void free_screen_info(efi_system_table_t *sys_table_arg, struct screen_info *si)
>  	efi_call_early(free_pool, si);
>  }
>  
> +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)
> +{
> +	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;
> +
> +	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
> +	 * all that prevents any future allocations from conflicting
> +	 * with the kernel. Since we can't tell from the compressed
> +	 * image how much DRAM the kernel actually uses (due to BSS
> +	 * size uncertainty) we allocate the maximum possible size.
> +	 * 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_arg->boottime->allocate_pages(EFI_ALLOCATE_ADDRESS,
> +							 EFI_BOOT_SERVICES_DATA,
> +							 nr_pages, &alloc_addr);
> +	if (status == EFI_SUCCESS) {
> +		*reserve_addr = alloc_addr;
> +		return EFI_SUCCESS;
> +	}
> +
> +	/*
> +	 * 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;
> +
> +		/* does this entry cover the region? */

Nitpick: the logic tests the opposite of what the comment describes.
                /* Skip if entry does not intersect with region */
?

Anyway, that's minor.

Reviewed-by: Leif Lindholm <leif.lindholm@xxxxxxxxxx>

> +		if (start >= dram_base + MAX_UNCOMP_KERNEL_SIZE ||
> +		    end <= dram_base)
> +			continue;
> +
> +		/* ignore types that are released to the OS anyway */
> +		switch (desc->type) {
> +		case EFI_BOOT_SERVICES_CODE:
> +		case EFI_BOOT_SERVICES_DATA:
> +			/* these are safe -- ignore */
> +			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,
> @@ -71,10 +186,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
>  				 unsigned long dram_base,
>  				 efi_loaded_image_t *image)
>  {
> -	unsigned long nr_pages;
>  	efi_status_t status;
> -	/* Use alloc_addr to tranlsate between types */
> -	efi_physical_addr_t alloc_addr;
>  
>  	/*
>  	 * Verify that the DRAM base address is compatible with the ARM
> @@ -85,27 +197,12 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
>  	 */
>  	dram_base = round_up(dram_base, SZ_128M);
>  
> -	/*
> -	 * Reserve memory for the uncompressed kernel image. This is
> -	 * all that prevents any future allocations from conflicting
> -	 * with the kernel. Since we can't tell from the compressed
> -	 * image how much DRAM the kernel actually uses (due to BSS
> -	 * size uncertainty) we allocate the maximum possible size.
> -	 * 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);
> +	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
> -- 
> 2.7.4
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux