aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi/memattr.c
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 /drivers/firmware/efi/memattr.c
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>
Diffstat (limited to 'drivers/firmware/efi/memattr.c')
-rw-r--r--drivers/firmware/efi/memattr.c182
1 files changed, 182 insertions, 0 deletions
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}