aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/kernel-parameters.txt15
-rw-r--r--arch/x86/kernel/setup.c4
-rw-r--r--drivers/firmware/efi/Kconfig22
-rw-r--r--drivers/firmware/efi/Makefile1
-rw-r--r--drivers/firmware/efi/fake_mem.c238
-rw-r--r--include/linux/efi.h6
6 files changed, 285 insertions, 1 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 1d6f0459cd7b..cd5312f24981 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1092,6 +1092,21 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
1092 you are really sure that your UEFI does sane gc and 1092 you are really sure that your UEFI does sane gc and
1093 fulfills the spec otherwise your board may brick. 1093 fulfills the spec otherwise your board may brick.
1094 1094
1095 efi_fake_mem= nn[KMG]@ss[KMG]:aa[,nn[KMG]@ss[KMG]:aa,..] [EFI; X86]
1096 Add arbitrary attribute to specific memory range by
1097 updating original EFI memory map.
1098 Region of memory which aa attribute is added to is
1099 from ss to ss+nn.
1100 If efi_fake_mem=2G@4G:0x10000,2G@0x10a0000000:0x10000
1101 is specified, EFI_MEMORY_MORE_RELIABLE(0x10000)
1102 attribute is added to range 0x100000000-0x180000000 and
1103 0x10a0000000-0x1120000000.
1104
1105 Using this parameter you can do debugging of EFI memmap
1106 related feature. For example, you can do debugging of
1107 Address Range Mirroring feature even if your box
1108 doesn't support it.
1109
1095 eisa_irq_edge= [PARISC,HW] 1110 eisa_irq_edge= [PARISC,HW]
1096 See header of drivers/parisc/eisa.c. 1111 See header of drivers/parisc/eisa.c.
1097 1112
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 80f874bf999e..e3ed628f7db4 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -1104,8 +1104,10 @@ void __init setup_arch(char **cmdline_p)
1104 memblock_set_current_limit(ISA_END_ADDRESS); 1104 memblock_set_current_limit(ISA_END_ADDRESS);
1105 memblock_x86_fill(); 1105 memblock_x86_fill();
1106 1106
1107 if (efi_enabled(EFI_BOOT)) 1107 if (efi_enabled(EFI_BOOT)) {
1108 efi_fake_memmap();
1108 efi_find_mirror(); 1109 efi_find_mirror();
1110 }
1109 1111
1110 /* 1112 /*
1111 * The EFI specification says that boot service code won't be called 1113 * The EFI specification says that boot service code won't be called
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);
diff --git a/include/linux/efi.h b/include/linux/efi.h
index fa5106c2f9f5..4d01c1033fce 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -922,6 +922,12 @@ extern struct kobject *efi_kobj;
922extern int efi_reboot_quirk_mode; 922extern int efi_reboot_quirk_mode;
923extern bool efi_poweroff_required(void); 923extern bool efi_poweroff_required(void);
924 924
925#ifdef CONFIG_EFI_FAKE_MEMMAP
926extern void __init efi_fake_memmap(void);
927#else
928static inline void efi_fake_memmap(void) { }
929#endif
930
925/* Iterate through an efi_memory_map */ 931/* Iterate through an efi_memory_map */
926#define for_each_efi_memory_desc(m, md) \ 932#define for_each_efi_memory_desc(m, md) \
927 for ((md) = (m)->map; \ 933 for ((md) = (m)->map; \