From patchwork Wed Mar 30 16:38:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 64725 Delivered-To: patch@linaro.org Received: by 10.112.199.169 with SMTP id jl9csp2720794lbc; Wed, 30 Mar 2016 10:24:55 -0700 (PDT) X-Received: by 10.67.21.167 with SMTP id hl7mr14934424pad.16.1459358695795; Wed, 30 Mar 2016 10:24:55 -0700 (PDT) Return-Path: Received: from bombadil.infradead.org (bombadil.infradead.org. [2001:1868:205::9]) by mx.google.com with ESMTPS id x64si7741214pfi.208.2016.03.30.10.24.55 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 30 Mar 2016 10:24:55 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org designates 2001:1868:205::9 as permitted sender) client-ip=2001:1868:205::9; 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-arm-kernel-bounces+patch=linaro.org@lists.infradead.org designates 2001:1868:205::9 as permitted sender) smtp.mailfrom=linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org 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 1alJqL-0004iR-AB; Wed, 30 Mar 2016 17:23:45 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1alJnE-0007ck-W4 for linux-arm-kernel@bombadil.infradead.org; Wed, 30 Mar 2016 17:20:33 +0000 Received: from mail-wm0-x22f.google.com ([2a00:1450:400c:c09::22f]) by casper.infradead.org with esmtps (Exim 4.85 #2 (Red Hat Linux)) id 1alJ9a-0007X8-Bu for linux-arm-kernel@lists.infradead.org; Wed, 30 Mar 2016 16:39:35 +0000 Received: by mail-wm0-x22f.google.com with SMTP id r72so107244463wmg.0 for ; Wed, 30 Mar 2016 09:39:14 -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:in-reply-to:references; bh=Y3e2VHqZWX1lpzQopDo/mD/5W71OcsyPqMwl9yW6Y/s=; b=NVdf2elRiGfqQcbzoRhiwpC7oJ9fMosb4IRGQVYJQdiSt+k4amk05m2ezBVfETukzR xf1k84EQV3NWsgnQYhpRN/9ynsLehdDxg7v6zM2jOblZSqfGeleO+rVJDXCA5ewlVc2J +ldhDHtZCk3Yw/ZFA/o2yi9zMQn+d1O2MqxwI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Y3e2VHqZWX1lpzQopDo/mD/5W71OcsyPqMwl9yW6Y/s=; b=JNRKb5KZcQm1Y4GYVqcnMT6hbGUvhSuLRgVIAybpX5Jq2fZu93oqot0fT4Q5XpjsDG a1J6/e4LxT/bxtdEO8EX3XDnb2KvK2pEtXjEm6tmEA1/lMUJ8G28meTBaydyE7vBrEaa ldFle4P9LjHmBBFjK9JudfcCaM6rnrVQPjkXke21H3MdhLgqeWt4bqC+liPhZdpGSABv ZK8lTk8KSkax06n3JXcp0B6FtIOzQpCT2RvrfQvkEcHUl9sI4lSAFcvAwa5VMWQixfzh jOTlMZcwHomyY42iA3SzeAAm3FYjbRXLjQxomhU9nMpZcg9dih5izqTJb2X12mwxGooZ 4CXA== X-Gm-Message-State: AD7BkJK/sDpQgjtxUFp2vzcNt37nAQYy0RR/SKBeuj5ukEa2g5M1wb4EKmWgOSNd8LxutjPd X-Received: by 10.194.48.33 with SMTP id i1mr1045857wjn.53.1459355953538; Wed, 30 Mar 2016 09:39:13 -0700 (PDT) Received: from localhost.localdomain ([195.55.142.58]) by smtp.gmail.com with ESMTPSA id l124sm5233151wmf.11.2016.03.30.09.39.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 30 Mar 2016 09:39:12 -0700 (PDT) From: Ard Biesheuvel To: linux-efi@vger.kernel.org, linux-arm-kernel@lists.infradead.org, matt@codeblueprint.co.uk, mark.rutland@arm.com, linux@arm.linux.org.uk Subject: [PATCH v2 4/5] efi: implement generic support for the Memory Attributes table Date: Wed, 30 Mar 2016 18:38:52 +0200 Message-Id: <1459355933-13529-5-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1459355933-13529-1-git-send-email-ard.biesheuvel@linaro.org> References: <1459355933-13529-1-git-send-email-ard.biesheuvel@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160330_173934_490930_D1B0D218 X-CRM114-Status: GOOD ( 30.24 ) X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.4.0 on casper.infradead.org summary: Content analysis details: (-2.7 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [2a00:1450:400c:c09:0:0:0:22f listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ard Biesheuvel , catalin.marinas@arm.com, will.deacon@arm.com, leif.lindholm@linaro.org, sai.praneeth.prakhya@intel.com, pjones@redhat.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org This implements shared support for discovering the presence of the Memory Attributes table, and for parsing and validating its contents. The table is validated against the construction rules in the UEFI spec. Since this is a new table, it makes sense to complain if we encounter a table that does not follow those rules. The parsing and validation routine takes a callback that can be specified per architecture, that gets passed each unique validated region, with the virtual address retrieved from the ordinary memory map. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Makefile | 2 +- drivers/firmware/efi/memattr.c | 170 ++++++++++++++++++++ include/linux/efi.h | 13 ++ 3 files changed, 184 insertions(+), 1 deletion(-) -- 2.5.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 62e654f255f4..d5be62399130 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -9,7 +9,7 @@ # KASAN_SANITIZE_runtime-wrappers.o := n -obj-$(CONFIG_EFI) += efi.o vars.o reboot.o +obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c new file mode 100644 index 000000000000..c55683937f4a --- /dev/null +++ b/drivers/firmware/efi/memattr.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2016 Linaro Ltd. + * + * 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. + */ + +#define pr_fmt(fmt) "efi: " fmt + +#include +#include +#include +#include + +#include + +static int __initdata tbl_size; + +/* + * Reserve the memory associated with the Memory Attributes configuration + * table, if it exists. + */ +int __init efi_memattr_init(void) +{ + efi_memory_attributes_table_t *tbl; + + if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR) + return 0; + + tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl)); + if (!tbl) { + pr_err("Failed to map EFI Memory Attribute table @ 0x%lx\n", + efi.mem_attr_table); + return -ENOMEM; + } + + if (tbl->version > 1) { + pr_warn("Unexpected EFI Memory Attribute table version %d\n", + tbl->version); + tbl_size = 0; + goto unmap; + } + + tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size; + memblock_reserve(efi.mem_attr_table, tbl_size); + +unmap: + early_memunmap(tbl, sizeof(*tbl)); + return 0; +} + +/* + * Returns a copy @out of the UEFI memory descriptor @in if it is covered + * entirely by a UEFI memory map entry with matching attributes. The virtual + * address of @out is set according to the matching entry that was found. + */ +static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out) +{ + u64 in_paddr = in->phys_addr; + u64 in_size = in->num_pages << EFI_PAGE_SHIFT; + efi_memory_desc_t *md; + + if (in->type != EFI_RUNTIME_SERVICES_CODE && + in->type != EFI_RUNTIME_SERVICES_DATA) { + pr_warn("MEMATTR table entry type should be RuntimeServiceCode/Data\n"); + return false; + } + + if (!(in->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))) { + pr_warn("MEMATTR table entry attributes invalid: RO and XP bits both cleared\n"); + return false; + } + + if (PAGE_SIZE > EFI_PAGE_SIZE && + (!PAGE_ALIGNED(in->phys_addr) || + !PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) { + /* + * Since arm64 may execute with page sizes of up to 64 KB, the + * UEFI spec mandates that RuntimeServices memory regions must + * be 64 KB aligned. We need to validate this here since we will + * not be able to tighten permissions on such regions without + * affecting adjacent regions. + */ + pr_warn("MEMATTR table entry misaligned\n"); + return false; + } + + for_each_efi_memory_desc(&memmap, md) { + u64 md_paddr = md->phys_addr; + u64 md_size = md->num_pages << EFI_PAGE_SHIFT; + + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) { + /* no virtual mapping has been installed by the stub */ + break; + } + + if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size) + continue; + + /* + * This entry covers the start of @in, check whether + * it covers the end as well. + */ + if (md_paddr + md_size < in_paddr + in_size) { + pr_warn("MEMATTR table entry covers multiple UEFI memory map regions\n"); + return false; + } + + if (md->type != in->type) { + pr_warn("MEMATTR table entry type deviates from UEFI memory map region type\n"); + return false; + } + + *out = *in; + out->virt_addr = in_paddr + + (md->virt_addr - md->phys_addr); + return true; + } + return false; +} + +/* + * To be called after the EFI page tables have been populated. If a memory + * attributes table is available, its contents will be used to update the + * mappings with tightened permissions as described by the table. + * This requires the UEFI memory map to have already been populated with + * virtual addresses. + */ +int __init efi_memattr_apply_permissions(struct mm_struct *mm, + efi_memattr_perm_setter fn) +{ + efi_memory_attributes_table_t *tbl; + int i, ret; + + if (tbl_size <= sizeof(*tbl)) + return 0; + + tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB); + if (!tbl) { + pr_err("Failed to map EFI Memory Attribute table @ 0x%lx\n", + efi.mem_attr_table); + return -ENOMEM; + } + + if (efi_enabled(EFI_DBG)) + pr_info("Processing UEFI Memory Attributes table:\n"); + + for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) { + efi_memory_desc_t md; + unsigned long size; + bool valid; + char buf[64]; + + valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size, + &md); + size = md.num_pages << EFI_PAGE_SHIFT; + if (efi_enabled(EFI_DBG) || !valid) + pr_info(" 0x%012llx-0x%012llx %s\n", md.phys_addr, + md.phys_addr + size - 1, + efi_md_typeattr_format(buf, sizeof(buf), &md)); + + if (valid) + ret = fn(mm, &md); + } + memunmap(tbl); + return ret; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 346d01ad7cca..277a9bb4e587 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -970,6 +970,19 @@ extern void __init efi_fake_memmap(void); static inline void efi_fake_memmap(void) { } #endif +/* + * efi_memattr_perm_setter - arch specific callback function passed into + * efi_memattr_apply_permissions() that updates the + * mapping permissions described by the second + * argument in the page tables referrred to by the + * first argument + */ +typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *); + +extern int efi_memattr_init(void); +extern int efi_memattr_apply_permissions(struct mm_struct *mm, + efi_memattr_perm_setter fn); + /* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc(m, md) \ for ((md) = (m)->map; \