diff options
Diffstat (limited to 'arch/x86/platform/efi/quirks.c')
-rw-r--r-- | arch/x86/platform/efi/quirks.c | 127 |
1 files changed, 114 insertions, 13 deletions
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 89d1146f5a6f..10aca63a50d7 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c | |||
@@ -164,6 +164,75 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size, | |||
164 | EXPORT_SYMBOL_GPL(efi_query_variable_store); | 164 | EXPORT_SYMBOL_GPL(efi_query_variable_store); |
165 | 165 | ||
166 | /* | 166 | /* |
167 | * The UEFI specification makes it clear that the operating system is | ||
168 | * free to do whatever it wants with boot services code after | ||
169 | * ExitBootServices() has been called. Ignoring this recommendation a | ||
170 | * significant bunch of EFI implementations continue calling into boot | ||
171 | * services code (SetVirtualAddressMap). In order to work around such | ||
172 | * buggy implementations we reserve boot services region during EFI | ||
173 | * init and make sure it stays executable. Then, after | ||
174 | * SetVirtualAddressMap(), it is discarded. | ||
175 | * | ||
176 | * However, some boot services regions contain data that is required | ||
177 | * by drivers, so we need to track which memory ranges can never be | ||
178 | * freed. This is done by tagging those regions with the | ||
179 | * EFI_MEMORY_RUNTIME attribute. | ||
180 | * | ||
181 | * Any driver that wants to mark a region as reserved must use | ||
182 | * efi_mem_reserve() which will insert a new EFI memory descriptor | ||
183 | * into efi.memmap (splitting existing regions if necessary) and tag | ||
184 | * it with EFI_MEMORY_RUNTIME. | ||
185 | */ | ||
186 | void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) | ||
187 | { | ||
188 | phys_addr_t new_phys, new_size; | ||
189 | struct efi_mem_range mr; | ||
190 | efi_memory_desc_t md; | ||
191 | int num_entries; | ||
192 | void *new; | ||
193 | |||
194 | if (efi_mem_desc_lookup(addr, &md)) { | ||
195 | pr_err("Failed to lookup EFI memory descriptor for %pa\n", &addr); | ||
196 | return; | ||
197 | } | ||
198 | |||
199 | if (addr + size > md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT)) { | ||
200 | pr_err("Region spans EFI memory descriptors, %pa\n", &addr); | ||
201 | return; | ||
202 | } | ||
203 | |||
204 | size += addr % EFI_PAGE_SIZE; | ||
205 | size = round_up(size, EFI_PAGE_SIZE); | ||
206 | addr = round_down(addr, EFI_PAGE_SIZE); | ||
207 | |||
208 | mr.range.start = addr; | ||
209 | mr.range.end = addr + size - 1; | ||
210 | mr.attribute = md.attribute | EFI_MEMORY_RUNTIME; | ||
211 | |||
212 | num_entries = efi_memmap_split_count(&md, &mr.range); | ||
213 | num_entries += efi.memmap.nr_map; | ||
214 | |||
215 | new_size = efi.memmap.desc_size * num_entries; | ||
216 | |||
217 | new_phys = memblock_alloc(new_size, 0); | ||
218 | if (!new_phys) { | ||
219 | pr_err("Could not allocate boot services memmap\n"); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | new = early_memremap(new_phys, new_size); | ||
224 | if (!new) { | ||
225 | pr_err("Failed to map new boot services memmap\n"); | ||
226 | return; | ||
227 | } | ||
228 | |||
229 | efi_memmap_insert(&efi.memmap, new, &mr); | ||
230 | early_memunmap(new, new_size); | ||
231 | |||
232 | efi_memmap_install(new_phys, num_entries); | ||
233 | } | ||
234 | |||
235 | /* | ||
167 | * Helper function for efi_reserve_boot_services() to figure out if we | 236 | * Helper function for efi_reserve_boot_services() to figure out if we |
168 | * can free regions in efi_free_boot_services(). | 237 | * can free regions in efi_free_boot_services(). |
169 | * | 238 | * |
@@ -184,15 +253,6 @@ static bool can_free_region(u64 start, u64 size) | |||
184 | return true; | 253 | return true; |
185 | } | 254 | } |
186 | 255 | ||
187 | /* | ||
188 | * The UEFI specification makes it clear that the operating system is free to do | ||
189 | * whatever it wants with boot services code after ExitBootServices() has been | ||
190 | * called. Ignoring this recommendation a significant bunch of EFI implementations | ||
191 | * continue calling into boot services code (SetVirtualAddressMap). In order to | ||
192 | * work around such buggy implementations we reserve boot services region during | ||
193 | * EFI init and make sure it stays executable. Then, after SetVirtualAddressMap(), it | ||
194 | * is discarded. | ||
195 | */ | ||
196 | void __init efi_reserve_boot_services(void) | 256 | void __init efi_reserve_boot_services(void) |
197 | { | 257 | { |
198 | efi_memory_desc_t *md; | 258 | efi_memory_desc_t *md; |
@@ -249,7 +309,10 @@ void __init efi_reserve_boot_services(void) | |||
249 | 309 | ||
250 | void __init efi_free_boot_services(void) | 310 | void __init efi_free_boot_services(void) |
251 | { | 311 | { |
312 | phys_addr_t new_phys, new_size; | ||
252 | efi_memory_desc_t *md; | 313 | efi_memory_desc_t *md; |
314 | int num_entries = 0; | ||
315 | void *new, *new_md; | ||
253 | 316 | ||
254 | for_each_efi_memory_desc(md) { | 317 | for_each_efi_memory_desc(md) { |
255 | unsigned long long start = md->phys_addr; | 318 | unsigned long long start = md->phys_addr; |
@@ -257,12 +320,16 @@ void __init efi_free_boot_services(void) | |||
257 | size_t rm_size; | 320 | size_t rm_size; |
258 | 321 | ||
259 | if (md->type != EFI_BOOT_SERVICES_CODE && | 322 | if (md->type != EFI_BOOT_SERVICES_CODE && |
260 | md->type != EFI_BOOT_SERVICES_DATA) | 323 | md->type != EFI_BOOT_SERVICES_DATA) { |
324 | num_entries++; | ||
261 | continue; | 325 | continue; |
326 | } | ||
262 | 327 | ||
263 | /* Do not free, someone else owns it: */ | 328 | /* Do not free, someone else owns it: */ |
264 | if (md->attribute & EFI_MEMORY_RUNTIME) | 329 | if (md->attribute & EFI_MEMORY_RUNTIME) { |
330 | num_entries++; | ||
265 | continue; | 331 | continue; |
332 | } | ||
266 | 333 | ||
267 | /* | 334 | /* |
268 | * Nasty quirk: if all sub-1MB memory is used for boot | 335 | * Nasty quirk: if all sub-1MB memory is used for boot |
@@ -287,7 +354,41 @@ void __init efi_free_boot_services(void) | |||
287 | free_bootmem_late(start, size); | 354 | free_bootmem_late(start, size); |
288 | } | 355 | } |
289 | 356 | ||
290 | efi_unmap_memmap(); | 357 | new_size = efi.memmap.desc_size * num_entries; |
358 | new_phys = memblock_alloc(new_size, 0); | ||
359 | if (!new_phys) { | ||
360 | pr_err("Failed to allocate new EFI memmap\n"); | ||
361 | return; | ||
362 | } | ||
363 | |||
364 | new = memremap(new_phys, new_size, MEMREMAP_WB); | ||
365 | if (!new) { | ||
366 | pr_err("Failed to map new EFI memmap\n"); | ||
367 | return; | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Build a new EFI memmap that excludes any boot services | ||
372 | * regions that are not tagged EFI_MEMORY_RUNTIME, since those | ||
373 | * regions have now been freed. | ||
374 | */ | ||
375 | new_md = new; | ||
376 | for_each_efi_memory_desc(md) { | ||
377 | if (!(md->attribute & EFI_MEMORY_RUNTIME) && | ||
378 | (md->type == EFI_BOOT_SERVICES_CODE || | ||
379 | md->type == EFI_BOOT_SERVICES_DATA)) | ||
380 | continue; | ||
381 | |||
382 | memcpy(new_md, md, efi.memmap.desc_size); | ||
383 | new_md += efi.memmap.desc_size; | ||
384 | } | ||
385 | |||
386 | memunmap(new); | ||
387 | |||
388 | if (efi_memmap_install(new_phys, num_entries)) { | ||
389 | pr_err("Could not install new EFI memmap\n"); | ||
390 | return; | ||
391 | } | ||
291 | } | 392 | } |
292 | 393 | ||
293 | /* | 394 | /* |
@@ -365,7 +466,7 @@ void __init efi_apply_memmap_quirks(void) | |||
365 | */ | 466 | */ |
366 | if (!efi_runtime_supported()) { | 467 | if (!efi_runtime_supported()) { |
367 | pr_info("Setup done, disabling due to 32/64-bit mismatch\n"); | 468 | pr_info("Setup done, disabling due to 32/64-bit mismatch\n"); |
368 | efi_unmap_memmap(); | 469 | efi_memmap_unmap(); |
369 | } | 470 | } |
370 | 471 | ||
371 | /* UV2+ BIOS has a fix for this issue. UV1 still needs the quirk. */ | 472 | /* UV2+ BIOS has a fix for this issue. UV1 still needs the quirk. */ |