From patchwork Thu May 14 14:22:53 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Salter X-Patchwork-Id: 6406101 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 159C39F318 for ; Thu, 14 May 2015 14:25:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0F39E2034A for ; Thu, 14 May 2015 14:25:58 +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 C994320270 for ; Thu, 14 May 2015 14:25:56 +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 1Ysu2x-0003Jc-W5; Thu, 14 May 2015 14:23:36 +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 1Ysu2p-00039m-IV for linux-arm-kernel@lists.infradead.org; Thu, 14 May 2015 14:23:28 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (Postfix) with ESMTPS id 4B0E7B5E6B; Thu, 14 May 2015 14:23:05 +0000 (UTC) Received: from deneb.redhat.com (ovpn-113-53.phx2.redhat.com [10.3.113.53]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t4EEN4GS030899; Thu, 14 May 2015 10:23:04 -0400 From: Mark Salter To: Will Deacon , Catalin Marinas Subject: [PATCH] arm64: support ACPI tables outside of kernel RAM Date: Thu, 14 May 2015 10:22:53 -0400 Message-Id: <1431613373-10928-1-git-send-email-msalter@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150514_072327_886992_34DB7B49 X-CRM114-Status: GOOD ( 21.51 ) X-Spam-Score: -5.0 (-----) Cc: Matt Fleming , Ard Biesheuvel , linux-kernel@vger.kernel.org, Hanjun Guo , Mark Salter , 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: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_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 There is no guarantee that ACPI tables will be located in RAM linearly mapped by the kernel. This could be because UEFI placed them below the kernel image or because mem= places them beyond the reach of the linear kernel mapping. Even though these tables are outside the linear mapped RAM, they still need to be accessed as normal memory in order to support unaligned accesses from ACPI code. In this case, the page_is_ram() test in acpi_os_ioremap() is not sufficient. Additionally, if the table spans multiple pages, it may fall partially within the linear map and partially without. If the table overlaps the end of the linear map, the test for whether or not to use the existing mapping in ioremap_cache() could lead to a panic when ACPI code tries to access the part beyond the end of the linear map. This patch attempts to address these problems. Cc: Hanjun Guo Cc: Ard Biesheuvel Cc: Matt Fleming Signed-off-by: Mark Salter --- arch/arm64/include/asm/acpi.h | 6 ++- arch/arm64/include/asm/efi.h | 2 + arch/arm64/kernel/acpi.c | 13 ++++++ arch/arm64/kernel/efi.c | 95 +++++++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/setup.c | 1 + arch/arm64/mm/ioremap.c | 3 +- 6 files changed, 118 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 59c05d8..0849533 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -20,11 +20,15 @@ /* Basic configuration for ACPI */ #ifdef CONFIG_ACPI +extern int page_is_acpi_ram(unsigned long pfn); + /* ACPI table mapping after acpi_gbl_permanent_mmap is set */ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) { - if (!page_is_ram(phys >> PAGE_SHIFT)) + unsigned long pfn = phys >> PAGE_SHIFT; + + if (!page_is_ram(pfn) && !page_is_acpi_ram(pfn)) return ioremap(phys, size); return ioremap_cache(phys, size); diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index ef57220..3d5c12a 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -6,8 +6,10 @@ #ifdef CONFIG_EFI extern void efi_init(void); +extern void efi_request_acpi_resources(void); #else #define efi_init() +#define efi_request_acpi_resources() #endif #define efi_call_virt(f, ...) \ diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 8b83955..2c85ca0 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -343,3 +343,16 @@ void __init acpi_gic_init(void) early_acpi_os_unmap_memory((char *)table, tbl_size); } + +static int __is_acpi_ram(u64 start, u64 end, void *arg) +{ + return 1; +} + +int page_is_acpi_ram(unsigned long pfn) +{ + u64 addr = (u64) pfn << PAGE_SHIFT; + + return walk_iomem_res("ACPI RAM", IORESOURCE_MEM | IORESOURCE_BUSY, + addr, addr, NULL, __is_acpi_ram) == 1; +} diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index ab21e0d..2d914d7 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -204,6 +204,101 @@ static __init void reserve_regions(void) set_bit(EFI_MEMMAP, &efi.flags); } +#ifdef CONFIG_ACPI +static void __init __insert_resource(struct resource *res) +{ + struct resource *r, *conflict; + + r = alloc_bootmem_low(sizeof(*r)); + *r = *res; + + /* + * When inserting a resource, there will be a conflict + * only if the resource being inserted partially overlaps + * an existing resource. If the resource being inserted + * is entirely within an existing resource, it becomes + * a child of that resource with no conflict. So if we + * do get a conflict, split the one resource into two + * resources: one inside the conflict and one outside. + */ + conflict = insert_resource_conflict(&iomem_resource, r); + if (conflict) { + struct resource *tmp; + + tmp = alloc_bootmem_low(sizeof(*tmp)); + *tmp = *r; + + if (r->start >= conflict->start) { + r->start = conflict->end + 1; + tmp->end = conflict->end; + } else { + r->end = conflict->start - 1; + tmp->start = conflict->start; + } + insert_resource(&iomem_resource, tmp); + insert_resource(&iomem_resource, r); + } +} + +/* + * Create /proc/iomem resources for any ACPI regions in RAM. + */ +void __init efi_request_acpi_resources(void) +{ + struct resource res; + efi_memory_desc_t *md; + u64 start, end, npages; + unsigned long mapsize = memmap.map_end - memmap.map; + void *map; + + map = early_memremap((resource_size_t)memmap.phys_map, mapsize); + if (map == NULL) + return; + memmap.map = map; + memmap.map_end = map + mapsize; + + memset(&res, 0, sizeof(res)); + res.name = "ACPI RAM"; + res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + + for_each_efi_memory_desc(&memmap, md) { + if (!is_normal_ram(md) || + !(md->type == EFI_ACPI_RECLAIM_MEMORY || + md->type == EFI_ACPI_MEMORY_NVS)) + continue; + + start = md->phys_addr; + npages = md->num_pages; + memrange_efi_to_native(&start, &npages); + end = start + (npages << PAGE_SHIFT) - 1; + + if (res.end == 0) { + res.start = start; + res.end = end; + continue; + } + + if (start >= res.start && (start - 1) <= res.end) { + /* overlaps (or adjacent to) end of old region */ + if (end > res.end) + res.end = end; + } else if (end >= (res.start - 1) && end <= res.end) { + /* overlaps (or adjacent to) start of old region */ + if (start < res.start) + res.start = start; + } else { + __insert_resource(&res); + res.start = start; + res.end = end; + } + } + if (res.end) + __insert_resource(&res); + + early_memunmap(memmap.map, mapsize); +} +#endif + void __init efi_init(void) { struct efi_fdt_params params; diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 7475313..a438a0c 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -413,6 +413,7 @@ void __init setup_arch(char **cmdline_p) of_smp_init_cpus(); #endif } else { + efi_request_acpi_resources(); psci_acpi_init(); acpi_init_cpus(); } diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c index 01e88c8..d62e701 100644 --- a/arch/arm64/mm/ioremap.c +++ b/arch/arm64/mm/ioremap.c @@ -96,7 +96,8 @@ EXPORT_SYMBOL(__iounmap); void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size) { /* For normal memory we already have a cacheable mapping. */ - if (pfn_valid(__phys_to_pfn(phys_addr))) + if (pfn_valid(__phys_to_pfn(phys_addr)) && + pfn_valid(__phys_to_pfn(phys_addr + size - 1))) return (void __iomem *)__phys_to_virt(phys_addr); return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL),