aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2016-04-25 16:06:45 -0400
committerIngo Molnar <mingo@kernel.org>2016-04-28 05:33:54 -0400
commit10f0d2f57705350bbbe5f28e9292ae3905823c3c (patch)
treed828604041ea1b077eefd938a6de549b9b0ccf47
parenta604af075a3226adaff84b7026876f0c6dfe9f52 (diff)
efi: Implement generic support for the Memory Attributes table
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 <ard.biesheuvel@linaro.org> [ Trim pr_*() strings to 80 cols and use EFI consistently. ] Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk> Cc: Borislav Petkov <bp@alien8.de> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Leif Lindholm <leif.lindholm@linaro.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Peter Jones <pjones@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Will Deacon <will.deacon@arm.com> Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/1461614832-17633-14-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--drivers/firmware/efi/Makefile2
-rw-r--r--drivers/firmware/efi/memattr.c182
-rw-r--r--include/linux/efi.h13
3 files changed, 196 insertions, 1 deletions
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 @@
9# 9#
10KASAN_SANITIZE_runtime-wrappers.o := n 10KASAN_SANITIZE_runtime-wrappers.o := n
11 11
12obj-$(CONFIG_EFI) += efi.o vars.o reboot.o 12obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o
13obj-$(CONFIG_EFI_VARS) += efivars.o 13obj-$(CONFIG_EFI_VARS) += efivars.o
14obj-$(CONFIG_EFI_ESRT) += esrt.o 14obj-$(CONFIG_EFI_ESRT) += esrt.o
15obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o 15obj-$(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..236004b9a50d
--- /dev/null
+++ b/drivers/firmware/efi/memattr.c
@@ -0,0 +1,182 @@
1/*
2 * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#define pr_fmt(fmt) "efi: memattr: " fmt
10
11#include <linux/efi.h>
12#include <linux/init.h>
13#include <linux/io.h>
14#include <linux/memblock.h>
15
16#include <asm/early_ioremap.h>
17
18static int __initdata tbl_size;
19
20/*
21 * Reserve the memory associated with the Memory Attributes configuration
22 * table, if it exists.
23 */
24int __init efi_memattr_init(void)
25{
26 efi_memory_attributes_table_t *tbl;
27
28 if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR)
29 return 0;
30
31 tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl));
32 if (!tbl) {
33 pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
34 efi.mem_attr_table);
35 return -ENOMEM;
36 }
37
38 if (tbl->version > 1) {
39 pr_warn("Unexpected EFI Memory Attributes table version %d\n",
40 tbl->version);
41 goto unmap;
42 }
43
44 tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
45 memblock_reserve(efi.mem_attr_table, tbl_size);
46
47unmap:
48 early_memunmap(tbl, sizeof(*tbl));
49 return 0;
50}
51
52/*
53 * Returns a copy @out of the UEFI memory descriptor @in if it is covered
54 * entirely by a UEFI memory map entry with matching attributes. The virtual
55 * address of @out is set according to the matching entry that was found.
56 */
57static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out)
58{
59 u64 in_paddr = in->phys_addr;
60 u64 in_size = in->num_pages << EFI_PAGE_SHIFT;
61 efi_memory_desc_t *md;
62
63 *out = *in;
64
65 if (in->type != EFI_RUNTIME_SERVICES_CODE &&
66 in->type != EFI_RUNTIME_SERVICES_DATA) {
67 pr_warn("Entry type should be RuntimeServiceCode/Data\n");
68 return false;
69 }
70
71 if (!(in->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))) {
72 pr_warn("Entry attributes invalid: RO and XP bits both cleared\n");
73 return false;
74 }
75
76 if (PAGE_SIZE > EFI_PAGE_SIZE &&
77 (!PAGE_ALIGNED(in->phys_addr) ||
78 !PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) {
79 /*
80 * Since arm64 may execute with page sizes of up to 64 KB, the
81 * UEFI spec mandates that RuntimeServices memory regions must
82 * be 64 KB aligned. We need to validate this here since we will
83 * not be able to tighten permissions on such regions without
84 * affecting adjacent regions.
85 */
86 pr_warn("Entry address region misaligned\n");
87 return false;
88 }
89
90 for_each_efi_memory_desc(md) {
91 u64 md_paddr = md->phys_addr;
92 u64 md_size = md->num_pages << EFI_PAGE_SHIFT;
93
94 if (!(md->attribute & EFI_MEMORY_RUNTIME))
95 continue;
96 if (md->virt_addr == 0) {
97 /* no virtual mapping has been installed by the stub */
98 break;
99 }
100
101 if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
102 continue;
103
104 /*
105 * This entry covers the start of @in, check whether
106 * it covers the end as well.
107 */
108 if (md_paddr + md_size < in_paddr + in_size) {
109 pr_warn("Entry covers multiple EFI memory map regions\n");
110 return false;
111 }
112
113 if (md->type != in->type) {
114 pr_warn("Entry type deviates from EFI memory map region type\n");
115 return false;
116 }
117
118 out->virt_addr = in_paddr + (md->virt_addr - md_paddr);
119
120 return true;
121 }
122
123 pr_warn("No matching entry found in the EFI memory map\n");
124 return false;
125}
126
127/*
128 * To be called after the EFI page tables have been populated. If a memory
129 * attributes table is available, its contents will be used to update the
130 * mappings with tightened permissions as described by the table.
131 * This requires the UEFI memory map to have already been populated with
132 * virtual addresses.
133 */
134int __init efi_memattr_apply_permissions(struct mm_struct *mm,
135 efi_memattr_perm_setter fn)
136{
137 efi_memory_attributes_table_t *tbl;
138 int i, ret;
139
140 if (tbl_size <= sizeof(*tbl))
141 return 0;
142
143 /*
144 * We need the EFI memory map to be setup so we can use it to
145 * lookup the virtual addresses of all entries in the of EFI
146 * Memory Attributes table. If it isn't available, this
147 * function should not be called.
148 */
149 if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
150 return 0;
151
152 tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB);
153 if (!tbl) {
154 pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
155 efi.mem_attr_table);
156 return -ENOMEM;
157 }
158
159 if (efi_enabled(EFI_DBG))
160 pr_info("Processing EFI Memory Attributes table:\n");
161
162 for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) {
163 efi_memory_desc_t md;
164 unsigned long size;
165 bool valid;
166 char buf[64];
167
168 valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size,
169 &md);
170 size = md.num_pages << EFI_PAGE_SHIFT;
171 if (efi_enabled(EFI_DBG) || !valid)
172 pr_info("%s 0x%012llx-0x%012llx %s\n",
173 valid ? "" : "!", md.phys_addr,
174 md.phys_addr + size - 1,
175 efi_md_typeattr_format(buf, sizeof(buf), &md));
176
177 if (valid)
178 ret = fn(mm, &md);
179 }
180 memunmap(tbl);
181 return ret;
182}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 81af5feba1f7..e29a31d0fc35 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -969,6 +969,19 @@ extern void __init efi_fake_memmap(void);
969static inline void efi_fake_memmap(void) { } 969static inline void efi_fake_memmap(void) { }
970#endif 970#endif
971 971
972/*
973 * efi_memattr_perm_setter - arch specific callback function passed into
974 * efi_memattr_apply_permissions() that updates the
975 * mapping permissions described by the second
976 * argument in the page tables referred to by the
977 * first argument.
978 */
979typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *);
980
981extern int efi_memattr_init(void);
982extern int efi_memattr_apply_permissions(struct mm_struct *mm,
983 efi_memattr_perm_setter fn);
984
972/* Iterate through an efi_memory_map */ 985/* Iterate through an efi_memory_map */
973#define for_each_efi_memory_desc_in_map(m, md) \ 986#define for_each_efi_memory_desc_in_map(m, md) \
974 for ((md) = (m)->map; \ 987 for ((md) = (m)->map; \