linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: ard.biesheuvel@linaro.org (Ard Biesheuvel)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v2 04/10] efi: add common infrastructure for stub-installed virtual mapping
Date: Thu,  6 Nov 2014 15:13:20 +0100	[thread overview]
Message-ID: <1415283206-14713-5-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1415283206-14713-1-git-send-email-ard.biesheuvel@linaro.org>

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 <ard.biesheuvel@linaro.org>
---
 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 <ard.biesheuvel@linaro.org>
+ *
+ * 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 <linux/efi.h>
+#include <linux/mm_types.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+
+#include <asm/efi.h>
+#include <asm/pgtable.h>
+#include <asm/mmu_context.h>
+#include <asm/mmu.h>
+
+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 */
-- 
1.8.3.2

  parent reply	other threads:[~2014-11-06 14:13 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-06 14:13 [PATCH v2 00/10] arm64: stable UEFI mappings for kexec Ard Biesheuvel
2014-11-06 14:13 ` [PATCH v2 01/10] arm64/mm: add explicit struct_mm argument to __create_mapping() Ard Biesheuvel
2014-11-06 14:13 ` [PATCH v2 02/10] arm64/mm: add create_pgd_mapping() to create private page tables Ard Biesheuvel
2014-11-07 15:08   ` Steve Capper
2014-11-07 15:12     ` Ard Biesheuvel
2014-11-07 15:21       ` Steve Capper
2014-11-06 14:13 ` [PATCH v2 03/10] efi: split off remapping code from efi_config_init() Ard Biesheuvel
2014-11-06 14:13 ` Ard Biesheuvel [this message]
2014-11-06 14:13 ` [PATCH v2 05/10] arm64/efi: move SetVirtualAddressMap() to UEFI stub Ard Biesheuvel
2014-11-06 14:13 ` [PATCH v2 06/10] arm64/efi: remove free_boot_services() and friends Ard Biesheuvel
2014-11-06 14:13 ` [PATCH v2 07/10] arm64/efi: remove idmap manipulations from UEFI code Ard Biesheuvel
2014-11-06 14:13 ` [PATCH v2 08/10] arm64/efi: use UEFI memory map unconditionally if available Ard Biesheuvel
2014-11-06 14:13 ` [PATCH v2 09/10] arm64/efi: ignore unusable regions instead of reserving them Ard Biesheuvel
2014-11-10  4:11   ` Mark Salter
2014-11-10  7:31     ` Ard Biesheuvel
2014-11-11 15:42       ` Mark Salter
2014-11-11 17:12         ` Mark Salter
2014-11-11 17:44           ` Mark Rutland
2014-11-11 17:55             ` Ard Biesheuvel
2014-11-11 18:39               ` Mark Rutland
2014-11-11 18:23             ` Mark Salter
2014-11-06 14:13 ` [PATCH v2 10/10] arm64/efi: improve /dev/mem mmap() handling under UEFI Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1415283206-14713-5-git-send-email-ard.biesheuvel@linaro.org \
    --to=ard.biesheuvel@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).