From patchwork Thu Nov 6 14:13:20 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 40288 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ee0-f71.google.com (mail-ee0-f71.google.com [74.125.83.71]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 7E3E024237 for ; Thu, 6 Nov 2014 14:16:36 +0000 (UTC) Received: by mail-ee0-f71.google.com with SMTP id e51sf2190596eek.10 for ; Thu, 06 Nov 2014 06:16:35 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:subject:date:message-id :in-reply-to:references:cc:precedence:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:mime-version:sender :errors-to:x-original-sender:x-original-authentication-results :mailing-list:content-type:content-transfer-encoding; bh=5FQqTyL9/c/SQjanbHPtkQUxhTPOODi8fBFe+PEAXDo=; b=B+wGdxefgUnZDJ1JRnW3GRU/Gu3Bcg5sYqAP0/AyZT6oNjNXT+DfSyrVJoAnBZBcvB dZAAHXlk9jJR8ywHbj6FrJnSB/7+hKmlweLUygFqaa5kUE/TY8rtHuMqaYY2TgHaWead SLk//BWeGYdQsSx6cSa9ANeTP4FODaJs//+VyOKXb2B9Fn4t/xdcmO/AphmWnou+wjxo mIMZhl1WZUBn73YuuCuVb7O3upAu5tfjoWYSTY3iMfpm8F8fnJnoO/Zpnsdf10KRF6Dj B8ioo4xFtiHbbQw3fx7dE74bo9YxE8m5eVelyg2nVkov1c81rL/mzbCeQFS9M2HxVXQJ H+EA== X-Gm-Message-State: ALoCoQn6zsgXrssu89ILUR6u9OLZUYyN1UDjW7Pl2qKBKYrJDbPVv4zXRpo6X7GusL3hBrJ2CTfO X-Received: by 10.112.89.195 with SMTP id bq3mr797033lbb.9.1415283395740; Thu, 06 Nov 2014 06:16:35 -0800 (PST) X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.29.170 with SMTP id l10ls75465lah.18.gmail; Thu, 06 Nov 2014 06:16:35 -0800 (PST) X-Received: by 10.112.199.40 with SMTP id jh8mr5343503lbc.5.1415283395424; Thu, 06 Nov 2014 06:16:35 -0800 (PST) Received: from mail-lb0-f173.google.com (mail-lb0-f173.google.com. [209.85.217.173]) by mx.google.com with ESMTPS id s10si11676013lal.38.2014.11.06.06.16.35 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 06 Nov 2014 06:16:35 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.173 as permitted sender) client-ip=209.85.217.173; Received: by mail-lb0-f173.google.com with SMTP id n15so980284lbi.18 for ; Thu, 06 Nov 2014 06:16:35 -0800 (PST) X-Received: by 10.112.77.74 with SMTP id q10mr5293314lbw.66.1415283395300; Thu, 06 Nov 2014 06:16:35 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.184.201 with SMTP id ew9csp49920lbc; Thu, 6 Nov 2014 06:16:34 -0800 (PST) X-Received: by 10.70.102.164 with SMTP id fp4mr4712974pdb.96.1415283390522; Thu, 06 Nov 2014 06:16:30 -0800 (PST) Received: from bombadil.infradead.org (bombadil.infradead.org. [2001:1868:205::9]) by mx.google.com with ESMTPS id q5si5976700pdm.153.2014.11.06.06.16.29 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 06 Nov 2014 06:16:30 -0800 (PST) Received-SPF: none (google.com: linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org does not designate permitted sender hosts) client-ip=2001:1868:205::9; 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 1XmNpk-0005KW-Be; Thu, 06 Nov 2014 14:14:44 +0000 Received: from mail-wi0-f172.google.com ([209.85.212.172]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XmNpM-0005BL-EC for linux-arm-kernel@lists.infradead.org; Thu, 06 Nov 2014 14:14:21 +0000 Received: by mail-wi0-f172.google.com with SMTP id bs8so1598439wib.11 for ; Thu, 06 Nov 2014 06:13:58 -0800 (PST) X-Received: by 10.194.81.70 with SMTP id y6mr5709641wjx.113.1415283238180; Thu, 06 Nov 2014 06:13:58 -0800 (PST) Received: from ards-macbook-pro.local (cag06-7-83-153-85-71.fbx.proxad.net. [83.153.85.71]) by mx.google.com with ESMTPSA id mw7sm8450413wib.14.2014.11.06.06.13.56 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 06 Nov 2014 06:13:57 -0800 (PST) From: Ard Biesheuvel To: leif.lindholm@linaro.org, roy.franz@linaro.org, linux-arm-kernel@lists.infradead.org, mark.rutland@arm.com, msalter@redhat.com, dyoung@redhat.com, linux-efi@vger.kernel.org, matt.fleming@intel.com, will.deacon@arm.com, catalin.marinas@arm.com, grant.likely@linaro.org Subject: [PATCH v2 04/10] efi: add common infrastructure for stub-installed virtual mapping Date: Thu, 6 Nov 2014 15:13:20 +0100 Message-Id: <1415283206-14713-5-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1415283206-14713-1-git-send-email-ard.biesheuvel@linaro.org> References: <1415283206-14713-1-git-send-email-ard.biesheuvel@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141106_061420_796941_A28A034B X-CRM114-Status: GOOD ( 25.94 ) X-Spam-Score: -0.7 (/) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-0.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.172 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) [209.85.212.172 listed in wl.mailspike.net] -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders Cc: Ard Biesheuvel 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+patch=linaro.org@lists.infradead.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ard.biesheuvel@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.173 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 This introduces the common infrastructure to be shared between arm64 and ARM that wires up the UEFI memory map into system RAM discovery, virtual mappings for Runtime Services and aligning cache attributes between kernel and userland (/dev/mem) mappings for regions owned by UEFI. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 3 + drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/virtmap.c | 224 +++++++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 15 ++- 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/efi/virtmap.c diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index f712d47f30d8..c71498180e67 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -60,6 +60,9 @@ config EFI_RUNTIME_WRAPPERS config EFI_ARMSTUB bool +config EFI_VIRTMAP + bool + endmenu config UEFI_CPER diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index aef6a95adef5..3fd26c0ad40b 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_UEFI_CPER) += cper.o obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o obj-$(CONFIG_EFI_ARM_STUB) += libstub/ +obj-$(CONFIG_EFI_VIRTMAP) += virtmap.o diff --git a/drivers/firmware/efi/virtmap.c b/drivers/firmware/efi/virtmap.c new file mode 100644 index 000000000000..11670186f8e6 --- /dev/null +++ b/drivers/firmware/efi/virtmap.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Ard Biesheuvel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss; + +static struct mm_struct efi_mm = { + .mm_rb = RB_ROOT, + .pgd = efi_pgd, + .mm_users = ATOMIC_INIT(2), + .mm_count = ATOMIC_INIT(1), + .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem), + .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), + .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), + INIT_MM_CONTEXT(efi_mm) +}; + +void efi_virtmap_load(void) +{ + WARN_ON(preemptible()); + efi_set_pgd(&efi_mm); +} + +void efi_virtmap_unload(void) +{ + efi_set_pgd(current->active_mm); +} + +static pgprot_t efi_md_access_prot(efi_memory_desc_t *md, pgprot_t prot) +{ + if (md->attribute & EFI_MEMORY_WB) + return prot; + if (md->attribute & (EFI_MEMORY_WT|EFI_MEMORY_WC)) + return pgprot_writecombine(prot); + return pgprot_device(prot); +} + +void __init efi_virtmap_init(void) +{ + efi_memory_desc_t *md; + + if (!efi_enabled(EFI_BOOT)) + return; + + for_each_efi_memory_desc(&memmap, md) { + u64 paddr, npages, size; + pgprot_t prot; + + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (WARN(md->virt_addr == 0, + "UEFI virtual mapping incomplete or missing -- no entry found for 0x%llx\n", + md->phys_addr)) + return; + + paddr = md->phys_addr; + npages = md->num_pages; + memrange_efi_to_native(&paddr, &npages); + size = npages << PAGE_SHIFT; + + pr_info(" EFI remap 0x%012llx => %p\n", + md->phys_addr, (void *)md->virt_addr); + + /* + * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be + * executable, everything else can be mapped with the XN bits + * set. + */ + if (md->type == EFI_RUNTIME_SERVICES_CODE) + prot = efi_md_access_prot(md, PAGE_KERNEL_EXEC); + else + prot = efi_md_access_prot(md, PAGE_KERNEL); + + create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size, prot); + } + set_bit(EFI_VIRTMAP, &efi.flags); +} + +/* + * Return true for RAM regions that are available for general use. + */ +bool efi_mem_is_usable_region(efi_memory_desc_t *md) +{ + switch (md->type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_CONVENTIONAL_MEMORY: + return md->attribute & EFI_MEMORY_WB; + default: + break; + } + return false; +} + +/* + * Translate a EFI virtual address into a physical address: this is necessary, + * as some data members of the EFI system table are virtually remapped after + * SetVirtualAddressMap() has been called. + */ +phys_addr_t efi_to_phys(unsigned long addr) +{ + efi_memory_desc_t *md; + + for_each_efi_memory_desc(&memmap, md) { + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + /* no virtual mapping has been installed by the stub */ + break; + if (md->virt_addr <= addr && + (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT)) + return md->phys_addr + addr - md->virt_addr; + } + return addr; +} + +static efi_memory_desc_t *efi_memory_desc(phys_addr_t addr) +{ + efi_memory_desc_t *md; + + for_each_efi_memory_desc(&memmap, md) + if (md->phys_addr <= addr && + (addr - md->phys_addr) < (md->num_pages << EFI_PAGE_SHIFT)) + return md; + return NULL; +} + +bool efi_mem_access_prot(unsigned long pfn, pgprot_t *prot) +{ + efi_memory_desc_t *md = efi_memory_desc(pfn << PAGE_SHIFT); + + if (!md) + return false; + + *prot = efi_md_access_prot(md, *prot); + return true; +} + +bool efi_mem_access_allowed(unsigned long pfn, unsigned long size, + bool writable) +{ + static const u64 memtype_mask = EFI_MEMORY_UC | EFI_MEMORY_WC | + EFI_MEMORY_WT | EFI_MEMORY_WB | + EFI_MEMORY_UCE; + phys_addr_t addr = pfn << PAGE_SHIFT; + efi_memory_desc_t *md; + + /* + * To keep things reasonable and simple, let's only allow mappings + * that are either entirely disjoint from the UEFI memory map, or + * are completely covered by it. + */ + md = efi_memory_desc(addr); + if (!md) { + /* + * 'addr' is not covered by the UEFI memory map, so no other + * UEFI memory map entries should intersect with the range + */ + for_each_efi_memory_desc(&memmap, md) + if (min(addr + size, md->phys_addr + + (md->num_pages << EFI_PAGE_SHIFT)) + > max(addr, md->phys_addr)) + return false; + return true; + } else { + /* + * Iterate over physically adjacent UEFI memory map entries. + * (This would be a lot easier if the memory map were sorted, + * and while it is in most cases, it is not mandated by the + * UEFI spec so we cannot rely on it.) + */ + u64 attr = md->attribute & memtype_mask; + unsigned long md_size; + + do { + /* + * All regions must have the same memory type + * attributes. + */ + if (attr != (md->attribute & memtype_mask)) + return false; + + /* + * Under CONFIG_STRICT_DEVMEM, don't allow any of + * the regions with special significance to UEFI + * to be mapped read-write. In case of unusable or + * MMIO memory, don't allow read-only access either. + */ + if (IS_ENABLED(CONFIG_STRICT_DEVMEM) && + (md->type == EFI_UNUSABLE_MEMORY || + md->type == EFI_MEMORY_MAPPED_IO || + (!efi_mem_is_usable_region(md) && writable))) + return false; + + /* + * We are done if this entry covers + * the remainder of the region. + */ + md_size = md->num_pages << EFI_PAGE_SHIFT; + if (md->phys_addr + md_size >= addr + size) + return true; + } while ((md = efi_memory_desc(md->phys_addr + md_size))); + } + return false; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 2dc8577032b3..e97abb226541 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -941,7 +941,8 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_MEMMAP 4 /* Can we use EFI memory map? */ #define EFI_64BIT 5 /* Is the firmware 64-bit? */ #define EFI_PARAVIRT 6 /* Access is via a paravirt interface */ -#define EFI_ARCH_1 7 /* First arch-specific bit */ +#define EFI_VIRTMAP 7 /* Use virtmap installed by the stub */ +#define EFI_ARCH_1 8 /* First arch-specific bit */ #ifdef CONFIG_EFI /* @@ -952,6 +953,7 @@ static inline bool efi_enabled(int feature) return test_bit(feature, &efi.flags) != 0; } extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused); +extern void efi_virtmap_init(void); #else static inline bool efi_enabled(int feature) { @@ -959,6 +961,7 @@ static inline bool efi_enabled(int feature) } static inline void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {} +static inline void efi_virtmap_init(void) {} #endif /* @@ -1250,4 +1253,14 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, efi_status_t efi_parse_options(char *cmdline); bool efi_runtime_disabled(void); + +phys_addr_t efi_to_phys(unsigned long addr); +bool efi_mem_is_usable_region(efi_memory_desc_t *md); +bool efi_mem_access_prot(unsigned long pfn, pgprot_t *prot); +bool efi_mem_access_allowed(unsigned long pfn, unsigned long size, + bool writable); + +void efi_virtmap_load(void); +void efi_virtmap_unload(void); + #endif /* _LINUX_EFI_H */