aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi
diff options
context:
space:
mode:
authorTaku Izumi <izumi.taku@jp.fujitsu.com>2015-09-30 10:01:56 -0400
committerMatt Fleming <matt.fleming@intel.com>2015-10-12 09:20:09 -0400
commit0f96a99dab366333439e110d6ad253bc7c557c09 (patch)
tree9c5df8a648c1ca4ba54cf29ce5fc305bec4d02e2 /drivers/firmware/efi
parent0bbea1ce98ed44779736ecc54ca9cdbca2b95544 (diff)
efi: Add "efi_fake_mem" boot option
This patch introduces new boot option named "efi_fake_mem". By specifying this parameter, you can add arbitrary attribute to specific memory range. This is useful for debugging of Address Range Mirroring feature. For example, if "efi_fake_mem=2G@4G:0x10000,2G@0x10a0000000:0x10000" is specified, the original (firmware provided) EFI memmap will be updated so that the specified memory regions have EFI_MEMORY_MORE_RELIABLE attribute (0x10000): <original> efi: mem36: [Conventional Memory| | | | | | |WB|WT|WC|UC] range=[0x0000000100000000-0x00000020a0000000) (129536MB) <updated> efi: mem36: [Conventional Memory| |MR| | | | |WB|WT|WC|UC] range=[0x0000000100000000-0x0000000180000000) (2048MB) efi: mem37: [Conventional Memory| | | | | | |WB|WT|WC|UC] range=[0x0000000180000000-0x00000010a0000000) (61952MB) efi: mem38: [Conventional Memory| |MR| | | | |WB|WT|WC|UC] range=[0x00000010a0000000-0x0000001120000000) (2048MB) efi: mem39: [Conventional Memory| | | | | | |WB|WT|WC|UC] range=[0x0000001120000000-0x00000020a0000000) (63488MB) And you will find that the following message is output: efi: Memory: 4096M/131455M mirrored memory Signed-off-by: Taku Izumi <izumi.taku@jp.fujitsu.com> Cc: Tony Luck <tony.luck@intel.com> Cc: Xishi Qiu <qiuxishi@huawei.com> Cc: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware/efi')
-rw-r--r--drivers/firmware/efi/Kconfig22
-rw-r--r--drivers/firmware/efi/Makefile1
-rw-r--r--drivers/firmware/efi/fake_mem.c238
3 files changed, 261 insertions, 0 deletions
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 54071c148340..1de6f0ed5077 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -52,6 +52,28 @@ config EFI_RUNTIME_MAP
52 52
53 See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. 53 See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map.
54 54
55config EFI_FAKE_MEMMAP
56 bool "Enable EFI fake memory map"
57 depends on EFI && X86
58 default n
59 help
60 Saying Y here will enable "efi_fake_mem" boot option.
61 By specifying this parameter, you can add arbitrary attribute
62 to specific memory range by updating original (firmware provided)
63 EFI memmap.
64 This is useful for debugging of EFI memmap related feature.
65 e.g. Address Range Mirroring feature.
66
67config EFI_MAX_FAKE_MEM
68 int "maximum allowable number of ranges in efi_fake_mem boot option"
69 depends on EFI_FAKE_MEMMAP
70 range 1 128
71 default 8
72 help
73 Maximum allowable number of ranges in efi_fake_mem boot option.
74 Ranges can be set up to this value using comma-separated list.
75 The default value is 8.
76
55config EFI_PARAMS_FROM_FDT 77config EFI_PARAMS_FROM_FDT
56 bool 78 bool
57 help 79 help
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index 6fd3da938717..c24f00569acb 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_UEFI_CPER) += cper.o
9obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o 9obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
10obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o 10obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
11obj-$(CONFIG_EFI_STUB) += libstub/ 11obj-$(CONFIG_EFI_STUB) += libstub/
12obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c
new file mode 100644
index 000000000000..32bcb14df2c8
--- /dev/null
+++ b/drivers/firmware/efi/fake_mem.c
@@ -0,0 +1,238 @@
1/*
2 * fake_mem.c
3 *
4 * Copyright (C) 2015 FUJITSU LIMITED
5 * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
6 *
7 * This code introduces new boot option named "efi_fake_mem"
8 * By specifying this parameter, you can add arbitrary attribute to
9 * specific memory range by updating original (firmware provided) EFI
10 * memmap.
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms and conditions of the GNU General Public License,
14 * version 2, as published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * this program; if not, see <http://www.gnu.org/licenses/>.
23 *
24 * The full GNU General Public License is included in this distribution in
25 * the file called "COPYING".
26 */
27
28#include <linux/kernel.h>
29#include <linux/efi.h>
30#include <linux/init.h>
31#include <linux/memblock.h>
32#include <linux/types.h>
33#include <linux/sort.h>
34#include <asm/efi.h>
35
36#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
37
38struct fake_mem {
39 struct range range;
40 u64 attribute;
41};
42static struct fake_mem fake_mems[EFI_MAX_FAKEMEM];
43static int nr_fake_mem;
44
45static int __init cmp_fake_mem(const void *x1, const void *x2)
46{
47 const struct fake_mem *m1 = x1;
48 const struct fake_mem *m2 = x2;
49
50 if (m1->range.start < m2->range.start)
51 return -1;
52 if (m1->range.start > m2->range.start)
53 return 1;
54 return 0;
55}
56
57void __init efi_fake_memmap(void)
58{
59 u64 start, end, m_start, m_end, m_attr;
60 int new_nr_map = memmap.nr_map;
61 efi_memory_desc_t *md;
62 u64 new_memmap_phy;
63 void *new_memmap;
64 void *old, *new;
65 int i;
66
67 if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP))
68 return;
69
70 /* count up the number of EFI memory descriptor */
71 for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) {
72 md = old;
73 start = md->phys_addr;
74 end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
75
76 for (i = 0; i < nr_fake_mem; i++) {
77 /* modifying range */
78 m_start = fake_mems[i].range.start;
79 m_end = fake_mems[i].range.end;
80
81 if (m_start <= start) {
82 /* split into 2 parts */
83 if (start < m_end && m_end < end)
84 new_nr_map++;
85 }
86 if (start < m_start && m_start < end) {
87 /* split into 3 parts */
88 if (m_end < end)
89 new_nr_map += 2;
90 /* split into 2 parts */
91 if (end <= m_end)
92 new_nr_map++;
93 }
94 }
95 }
96
97 /* allocate memory for new EFI memmap */
98 new_memmap_phy = memblock_alloc(memmap.desc_size * new_nr_map,
99 PAGE_SIZE);
100 if (!new_memmap_phy)
101 return;
102
103 /* create new EFI memmap */
104 new_memmap = early_memremap(new_memmap_phy,
105 memmap.desc_size * new_nr_map);
106 if (!new_memmap) {
107 memblock_free(new_memmap_phy, memmap.desc_size * new_nr_map);
108 return;
109 }
110
111 for (old = memmap.map, new = new_memmap;
112 old < memmap.map_end;
113 old += memmap.desc_size, new += memmap.desc_size) {
114
115 /* copy original EFI memory descriptor */
116 memcpy(new, old, memmap.desc_size);
117 md = new;
118 start = md->phys_addr;
119 end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
120
121 for (i = 0; i < nr_fake_mem; i++) {
122 /* modifying range */
123 m_start = fake_mems[i].range.start;
124 m_end = fake_mems[i].range.end;
125 m_attr = fake_mems[i].attribute;
126
127 if (m_start <= start && end <= m_end)
128 md->attribute |= m_attr;
129
130 if (m_start <= start &&
131 (start < m_end && m_end < end)) {
132 /* first part */
133 md->attribute |= m_attr;
134 md->num_pages = (m_end - md->phys_addr + 1) >>
135 EFI_PAGE_SHIFT;
136 /* latter part */
137 new += memmap.desc_size;
138 memcpy(new, old, memmap.desc_size);
139 md = new;
140 md->phys_addr = m_end + 1;
141 md->num_pages = (end - md->phys_addr + 1) >>
142 EFI_PAGE_SHIFT;
143 }
144
145 if ((start < m_start && m_start < end) && m_end < end) {
146 /* first part */
147 md->num_pages = (m_start - md->phys_addr) >>
148 EFI_PAGE_SHIFT;
149 /* middle part */
150 new += memmap.desc_size;
151 memcpy(new, old, memmap.desc_size);
152 md = new;
153 md->attribute |= m_attr;
154 md->phys_addr = m_start;
155 md->num_pages = (m_end - m_start + 1) >>
156 EFI_PAGE_SHIFT;
157 /* last part */
158 new += memmap.desc_size;
159 memcpy(new, old, memmap.desc_size);
160 md = new;
161 md->phys_addr = m_end + 1;
162 md->num_pages = (end - m_end) >>
163 EFI_PAGE_SHIFT;
164 }
165
166 if ((start < m_start && m_start < end) &&
167 (end <= m_end)) {
168 /* first part */
169 md->num_pages = (m_start - md->phys_addr) >>
170 EFI_PAGE_SHIFT;
171 /* latter part */
172 new += memmap.desc_size;
173 memcpy(new, old, memmap.desc_size);
174 md = new;
175 md->phys_addr = m_start;
176 md->num_pages = (end - md->phys_addr + 1) >>
177 EFI_PAGE_SHIFT;
178 md->attribute |= m_attr;
179 }
180 }
181 }
182
183 /* swap into new EFI memmap */
184 efi_unmap_memmap();
185 memmap.map = new_memmap;
186 memmap.phys_map = (void *)new_memmap_phy;
187 memmap.nr_map = new_nr_map;
188 memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size;
189 set_bit(EFI_MEMMAP, &efi.flags);
190
191 /* print new EFI memmap */
192 efi_print_memmap();
193}
194
195static int __init setup_fake_mem(char *p)
196{
197 u64 start = 0, mem_size = 0, attribute = 0;
198 int i;
199
200 if (!p)
201 return -EINVAL;
202
203 while (*p != '\0') {
204 mem_size = memparse(p, &p);
205 if (*p == '@')
206 start = memparse(p+1, &p);
207 else
208 break;
209
210 if (*p == ':')
211 attribute = simple_strtoull(p+1, &p, 0);
212 else
213 break;
214
215 if (nr_fake_mem >= EFI_MAX_FAKEMEM)
216 break;
217
218 fake_mems[nr_fake_mem].range.start = start;
219 fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
220 fake_mems[nr_fake_mem].attribute = attribute;
221 nr_fake_mem++;
222
223 if (*p == ',')
224 p++;
225 }
226
227 sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem),
228 cmp_fake_mem, NULL);
229
230 for (i = 0; i < nr_fake_mem; i++)
231 pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
232 fake_mems[i].attribute, fake_mems[i].range.start,
233 fake_mems[i].range.end);
234
235 return *p == '\0' ? 0 : -EINVAL;
236}
237
238early_param("efi_fake_mem", setup_fake_mem);