diff options
-rw-r--r-- | Documentation/kernel-parameters.txt | 15 | ||||
-rw-r--r-- | arch/x86/kernel/setup.c | 4 | ||||
-rw-r--r-- | drivers/firmware/efi/Kconfig | 22 | ||||
-rw-r--r-- | drivers/firmware/efi/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/efi/fake_mem.c | 238 | ||||
-rw-r--r-- | include/linux/efi.h | 6 |
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 | ||
55 | config 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 | |||
67 | config 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 | |||
55 | config EFI_PARAMS_FROM_FDT | 77 | config 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 | |||
9 | obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o | 9 | obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o |
10 | obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o | 10 | obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o |
11 | obj-$(CONFIG_EFI_STUB) += libstub/ | 11 | obj-$(CONFIG_EFI_STUB) += libstub/ |
12 | obj-$(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 | |||
38 | struct fake_mem { | ||
39 | struct range range; | ||
40 | u64 attribute; | ||
41 | }; | ||
42 | static struct fake_mem fake_mems[EFI_MAX_FAKEMEM]; | ||
43 | static int nr_fake_mem; | ||
44 | |||
45 | static 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 | |||
57 | void __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 | |||
195 | static 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 | |||
238 | early_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; | |||
922 | extern int efi_reboot_quirk_mode; | 922 | extern int efi_reboot_quirk_mode; |
923 | extern bool efi_poweroff_required(void); | 923 | extern bool efi_poweroff_required(void); |
924 | 924 | ||
925 | #ifdef CONFIG_EFI_FAKE_MEMMAP | ||
926 | extern void __init efi_fake_memmap(void); | ||
927 | #else | ||
928 | static 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; \ |