From patchwork Fri Mar 24 11:33:01 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 95929 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp1194033qgd; Fri, 24 Mar 2017 04:33:12 -0700 (PDT) X-Received: by 10.98.76.140 with SMTP id e12mr8510315pfj.82.1490355191981; Fri, 24 Mar 2017 04:33:11 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i4si2493323plk.92.2017.03.24.04.33.11; Fri, 24 Mar 2017 04:33: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 S1751407AbdCXLdM (ORCPT + 2 others); Fri, 24 Mar 2017 07:33:12 -0400 Received: from mail-wm0-f46.google.com ([74.125.82.46]:35295 "EHLO mail-wm0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965051AbdCXLdL (ORCPT ); Fri, 24 Mar 2017 07:33:11 -0400 Received: by mail-wm0-f46.google.com with SMTP id u132so10623240wmg.0 for ; Fri, 24 Mar 2017 04:33:09 -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=6vzUv3RZRIVsWrhTewfxAxCGgoQPIfam/E9o3mDWCAw=; b=ZGW/dmYJjXeaZ+5e+M6gFI0sOaB3SD6zCvuhSYC/tKzB7/lNXSAxgHGJsnmEwEhPgf vmfZKeCSNd5yoBlfLaCWqOrYst4r64jnnCMOSomvIqOvZeDIdX4HNxe0vWNihVcwZr9Z +kJIA+122Ey2a7+CK4wWzfjU0acgQU2dkmtms= 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=6vzUv3RZRIVsWrhTewfxAxCGgoQPIfam/E9o3mDWCAw=; b=LV7CCQXLWjSwL/7YmDDj0U5ArNDlu9rwH907WwCXE8iD3vIqM/W/9MLMhEfKTU3NBB jdPCw33lGTQ6Vv2hrwUQ3tgkZhSU4rCV1D38sl0Hoi83Xmrze0HCcHkrz7x87BaWX2Dp QmCt3pn4kB/2uhNZ2nBzywN3WKL/Jr2MCdjG8QefP63/6JCIZOYiTzeq3ubVyDw6joxo 0gMgSudkYyM1LjzPomIaSZ4N0IqzfIqsNxmCfVKeZNVC8cICGMcgHLqSdpNFe79eCYvQ kOX16bUZuWN7EeBUQWIv7+oCe33eOEyiRK3vVHZK/pZZMWdrOEi38OiNrv9YKrWqklxV bxxQ== X-Gm-Message-State: AFeK/H2vkStL/KbcfmNlgEGp1HEUoyEybw58WRbT29NAIC7MilPzkCORGxSIrM2WS9Adohhk X-Received: by 10.28.173.2 with SMTP id w2mr2472495wme.117.1490355188821; Fri, 24 Mar 2017 04:33:08 -0700 (PDT) Received: from localhost.localdomain ([196.67.95.24]) by smtp.gmail.com with ESMTPSA id s17sm2558917wrc.25.2017.03.24.04.33.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 24 Mar 2017 04:33:07 -0700 (PDT) From: Ard Biesheuvel To: linux-efi@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, eugene@hp.com, rfranz@cavium.com, leif.lindholm@linaro.org, Ard Biesheuvel , Matt Fleming Subject: [PATCH v2] efi/arm32-stub: allow boottime allocations in the vmlinux region Date: Fri, 24 Mar 2017 11:33:01 +0000 Message-Id: <20170324113301.13636-1-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.9.3 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. Cc: Matt Fleming Reviewed-by: Leif Lindholm Reviewed-by: Eugene Cohen Reviewed-by: Roy Franz Signed-off-by: Ard Biesheuvel --- v2: - use efi_call_early() instead of dereferencing sys_table->boottime directly - use EFI_ALLOCATE_MAX_ADDRESS for the first attempt rather then EFI_ALLOCATE_ADDRESS, which is affected by a EDK2 bug (this could return a base address lower than expected, if dram_base is greater than the real base of DRAM, and part of the region we attempt to reserve is already occupied) drivers/firmware/efi/libstub/arm32-stub.c | 148 +++++++++++++++++--- 1 file changed, 128 insertions(+), 20 deletions(-) -- 2.9.3 -- 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 diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index e1f0b28e1dcb..a4a5687cf8fd 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -63,6 +63,132 @@ 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 + 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, @@ -71,10 +197,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 +208,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