From patchwork Mon Jul 14 18:40:48 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Salter X-Patchwork-Id: 4548591 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 160ACC0514 for ; Mon, 14 Jul 2014 18:43:28 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 11D3E20149 for ; Mon, 14 Jul 2014 18:43:27 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 0560A2012B for ; Mon, 14 Jul 2014 18:43:26 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1X6lBo-0008Ka-P0; Mon, 14 Jul 2014 18:41:28 +0000 Received: from mx1.redhat.com ([209.132.183.28]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1X6lBm-0008FI-JG for linux-arm-kernel@lists.infradead.org; Mon, 14 Jul 2014 18:41:27 +0000 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s6EIeoH2029472 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 14 Jul 2014 14:40:50 -0400 Received: from [10.3.113.32] (ovpn-113-32.phx2.redhat.com [10.3.113.32]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s6EIen29025157; Mon, 14 Jul 2014 14:40:49 -0400 Message-ID: <1405363248.25580.12.camel@deneb.redhat.com> Subject: Re: [PATCH] efi/arm64: efistub: don't abort if base of DRAM is occupied From: Mark Salter To: Ard Biesheuvel Date: Mon, 14 Jul 2014 14:40:48 -0400 In-Reply-To: <1405351521-12010-1-git-send-email-ard.biesheuvel@linaro.org> References: <1405351521-12010-1-git-send-email-ard.biesheuvel@linaro.org> Organization: Red Hat, Inc Mime-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140714_114126_683264_2E017B0E X-CRM114-Status: GOOD ( 34.84 ) X-Spam-Score: -5.0 (-----) Cc: linux-efi@vger.kernel.org, catalin.marinas@arm.com, leif.lindholm@linaro.org, roy.franz@linaro.org, matt.fleming@intel.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On Mon, 2014-07-14 at 17:25 +0200, Ard Biesheuvel wrote: > If we fail to relocate the kernel Image to its preferred offset of TEXT_OFFSET > bytes above the base of DRAM, accept the lowest alternative mapping available > instead of aborting. We may lose a bit of memory at the low end, but we can > still proceed normally otherwise. This breaks APM Mustang because the spin-table holding pen for secondary CPUs is marked as reserved memory in the TEXT_OFFSET area and the kernel placement using your patch makes it unreachable by kernel. Here is a patch I've been working with to solve the same problem: From: Mark Salter Date: Thu, 10 Jul 2014 09:25:30 -0400 Subject: [PATCH] arm64/efi: try to handle firmware located below kernel The rule for arm64 kernel image placement is that it must be located TEXT_OFFSET bytes past a 2MiB boundary. The kernel itself will use the TEXT_OFFSET sized area for initial page tables but that area is not part of the kernel image itself. The current EFI stub code finds the base of DRAM from the EFI memmap and relocates the kernel to dram_base+TEXT_OFFSET. This assumes that the low memory is not being used and the kernel relocation simply fails if the base memory allocation fails. At least one vendor has firmware which occupies memory near dram_base so kernel relocations always fail. This patch attempts to work with such firmware by searching the EFI memmap for the lowest available memory which may be used for the kernel image. There are several pitfalls remaining which may lead to boot failure: * The stub does not allocate the TEXT_OFFSET region, so it is required that the firmware not be using that area for anything which may interfere or overlap with the initial kernel page tables. We can't simply include that area in our search for available memory because firmware using the spin-table method for booting secondary CPUs may place the CPU pen in an out of the way part of that region and mark it as reserved memory. * The current code requires FDT to be placed within first 512MiB of DRAM (with the kernel below it). This requirement can be removed in the future, but would involve changes to generic stub code shared by other architectures. Signed-off-by: Mark Salter --- arch/arm64/kernel/efi-stub.c | 45 +++++++++++++++++++++++++++++++++++++------- arch/arm64/kernel/efi.c | 19 ++++++++++++++++--- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c index 60e98a63..f5da27f 100644 --- a/arch/arm64/kernel/efi-stub.c +++ b/arch/arm64/kernel/efi-stub.c @@ -54,21 +54,53 @@ static efi_status_t handle_kernel_image(efi_system_table_t *sys_table, efi_loaded_image_t *image) { efi_status_t status; - unsigned long kernel_size, kernel_memsize = 0; + unsigned long kernel_size, kernel_memsize; + unsigned long desired_base = dram_base + TEXT_OFFSET; + unsigned long desired_end; + unsigned long map_size; + struct efi_memory_map map; + efi_memory_desc_t *md; /* Relocate the image, if required. */ kernel_size = _edata - _text; - if (*image_addr != (dram_base + TEXT_OFFSET)) { - kernel_memsize = kernel_size + (_end - _edata); + kernel_memsize = kernel_size + (_end - _edata); + + desired_end = desired_base + kernel_size; + + /* find lowest available address for kernel to live */ + status = efi_get_memory_map(sys_table, (efi_memory_desc_t **)&map.map, + &map_size, &map.desc_size, NULL, NULL); + if (status == EFI_SUCCESS) { + map.map_end = map.map + map_size; + for_each_efi_memory_desc(&map, md) { + unsigned long start, end, offset; + if (!(md->attribute & EFI_MEMORY_WB)) + continue; + if (md->type != EFI_CONVENTIONAL_MEMORY) + continue; + start = md->phys_addr; + end = start + (md->num_pages << EFI_PAGE_SHIFT); + offset = start & (SZ_2M - 1); + if (offset < TEXT_OFFSET) + start += (TEXT_OFFSET - offset); + else if (offset > TEXT_OFFSET) + start = ALIGN(start, SZ_2M) + TEXT_OFFSET; + if (start < end && (start + kernel_memsize) <= end) { + desired_base = start; + break; + } + } + } + + if (*image_addr != desired_base) { status = efi_relocate_kernel(sys_table, image_addr, kernel_size, kernel_memsize, - dram_base + TEXT_OFFSET, - PAGE_SIZE); + desired_base, PAGE_SIZE); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to relocate kernel\n"); return status; } - if (*image_addr != (dram_base + TEXT_OFFSET)) { + if (*image_addr != desired_base) { pr_efi_err(sys_table, "Failed to alloc kernel memory\n"); efi_free(sys_table, kernel_memsize, *image_addr); return EFI_ERROR; @@ -76,6 +108,5 @@ static efi_status_t handle_kernel_image(efi_system_table_t *sys_table, *image_size = kernel_memsize; } - return EFI_SUCCESS; } diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 453b7f8..2bc6469 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -180,9 +180,18 @@ static __init void reserve_regions(void) if (is_reserve_region(md) || md->type == EFI_BOOT_SERVICES_CODE || md->type == EFI_BOOT_SERVICES_DATA) { - memblock_reserve(paddr, size); - if (uefi_debug) - pr_cont("*"); + if (paddr < PHYS_OFFSET) { + if ((paddr + size) > PHYS_OFFSET) { + size -= (PHYS_OFFSET - paddr); + memblock_reserve(PHYS_OFFSET, size); + if (uefi_debug) + pr_cont("**"); + } + } else { + memblock_reserve(paddr, size); + if (uefi_debug) + pr_cont("*"); + } } if (uefi_debug) @@ -273,6 +282,10 @@ static void __init free_boot_services(void) continue; } + /* Don't free anything below kernel */ + if (md->phys_addr < PHYS_OFFSET) + continue; + /* * We want to free memory from this region. */