From patchwork Thu Mar 16 10:56:16 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 95389 Delivered-To: patch@linaro.org Received: by 10.140.89.134 with SMTP id v6csp757540qgd; Thu, 16 Mar 2017 03:59:11 -0700 (PDT) X-Received: by 10.98.221.141 with SMTP id w135mr9377830pff.109.1489661951592; Thu, 16 Mar 2017 03:59:11 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l3si300617pln.127.2017.03.16.03.59.11; Thu, 16 Mar 2017 03:59:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-efi-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752077AbdCPK6t (ORCPT + 2 others); Thu, 16 Mar 2017 06:58:49 -0400 Received: from mail-wr0-f181.google.com ([209.85.128.181]:36316 "EHLO mail-wr0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751357AbdCPK54 (ORCPT ); Thu, 16 Mar 2017 06:57:56 -0400 Received: by mail-wr0-f181.google.com with SMTP id u108so29105540wrb.3 for ; Thu, 16 Mar 2017 03:56:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=EPgvLo5GB6tXyXDadqzcVYplU+tm5qptBa8BVDFA6VM=; b=YymIBG8RimSLl/3UjgIEU7tVE9ANQUmBJfw7Mqmw3fm705ISsAv7cRYJGkRf7r1PIp 8Xos0my6Hvc48YzLK3GRlk8ODrXkRlhcN01/m6lqfkj9jPCYJG8WvVqr/dFigJGv54rT WbgWGzqPKjRQQ8JRKxYxKlhIUxwN38MB9X96E= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=EPgvLo5GB6tXyXDadqzcVYplU+tm5qptBa8BVDFA6VM=; b=EQoJj+f54wF+xau2OX7dTp/8VLL1u2RNd13++1rBUqrixwoJyNKXsVxM6bWiBVO1S8 COWOZp1gawRnt4lPHG6tUS56C8cBcgGFmaYhwWFt3rU9GVIFc6RuCRAh0Hxqm9+TFYTg NqxhgaY+FnYHNgfjC6zRvlc8EDJMjbYZJ0br0KwMK3T7qEv4CHre9nAYgiz3q8++SbZj P0hpJtxE6lCGJxWB2so9+udo0DHfRcejvwljFL2wM/3bcdFosZZAZN9AL64M4h96JUFx TEkSrASmyMO6v7HpIsYl/2BYfZOZGr1xCLKgzYA59fHrA/y9lp10Iew8gmzATgHKO+OG BeUQ== X-Gm-Message-State: AFeK/H27nSeH3DaRARdedeubaap7JaTbpjyghx3glhJGUuR7jP2BHOw/e2L2zIcouM2xmJFW X-Received: by 10.223.165.17 with SMTP id i17mr7968409wrb.70.1489661795419; Thu, 16 Mar 2017 03:56:35 -0700 (PDT) Received: from localhost.localdomain ([105.144.205.163]) by smtp.gmail.com with ESMTPSA id u145sm3857943wmu.1.2017.03.16.03.56.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 16 Mar 2017 03:56:34 -0700 (PDT) From: Ard Biesheuvel To: linux-efi@vger.kernel.org Cc: leif.lindholm@linaro.org, eugene@hp.com, rfranz@cavium.com, Ard Biesheuvel Subject: [PATCH] efi/arm32-stub: allow boottime allocations in the vmlinux region Date: Thu, 16 Mar 2017 10:56:16 +0000 Message-Id: <1489661776-12946-1-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.7.4 Sender: linux-efi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-efi@vger.kernel.org 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 Signed-off-by: Ard Biesheuvel --- 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. drivers/firmware/efi/libstub/arm32-stub.c | 137 +++++++++++++++++--- 1 file changed, 117 insertions(+), 20 deletions(-) -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Reviewed-by: Leif Lindholm Reviewed-by: Eugene Cohen Reviewed-by: Roy Franz 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? */ + 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