diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-26 16:38:38 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-26 16:38:38 -0500 |
commit | 684019dd1f0092b4ffce4958c84aff0891deac83 (patch) | |
tree | 65977a5481527c26fe4a027badb9825b71ab9d35 | |
parent | 792bf4d871dea8b69be2aaabdd320d7c6ed15985 (diff) | |
parent | 1debf0958fa27b7c469dbf22754929ec59a7c0e7 (diff) |
Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI updates from Ingo Molnar:
"The main changes in this cycle were:
- Allocate the E820 buffer before doing the
GetMemoryMap/ExitBootServices dance so we don't run out of space
- Clear EFI boot services mappings when freeing the memory
- Harden efivars against callers that invoke it on non-EFI boots
- Reduce the number of memblock reservations resulting from extensive
use of the new efi_mem_reserve_persistent() API
- Other assorted fixes and cleanups"
* 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/efi: Don't unmap EFI boot services code/data regions for EFI_OLD_MEMMAP and EFI_MIXED_MODE
efi: Reduce the amount of memblock reservations for persistent allocations
efi: Permit multiple entries in persistent memreserve data structure
efi/libstub: Disable some warnings for x86{,_64}
x86/efi: Move efi_<reserve/free>_boot_services() to arch/x86
x86/efi: Unmap EFI boot services code/data regions from efi_pgd
x86/mm/pageattr: Introduce helper function to unmap EFI boot services
efi/fdt: Simplify the get_fdt() flow
efi/fdt: Indentation fix
firmware/efi: Add NULL pointer checks in efivars API functions
-rw-r--r-- | arch/x86/include/asm/efi.h | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/pgtable_types.h | 8 | ||||
-rw-r--r-- | arch/x86/mm/pageattr.c | 40 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 2 | ||||
-rw-r--r-- | arch/x86/platform/efi/quirks.c | 41 | ||||
-rw-r--r-- | drivers/firmware/efi/efi.c | 54 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/Makefile | 5 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/arm-stub.c | 2 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/fdt.c | 30 | ||||
-rw-r--r-- | drivers/firmware/efi/vars.c | 99 | ||||
-rw-r--r-- | include/linux/efi.h | 19 | ||||
-rw-r--r-- | init/main.c | 4 |
12 files changed, 242 insertions, 64 deletions
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index eea40d52ca78..d1e64ac80b9c 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h | |||
@@ -141,6 +141,8 @@ extern int __init efi_reuse_config(u64 tables, int nr_tables); | |||
141 | extern void efi_delete_dummy_variable(void); | 141 | extern void efi_delete_dummy_variable(void); |
142 | extern void efi_switch_mm(struct mm_struct *mm); | 142 | extern void efi_switch_mm(struct mm_struct *mm); |
143 | extern void efi_recover_from_page_fault(unsigned long phys_addr); | 143 | extern void efi_recover_from_page_fault(unsigned long phys_addr); |
144 | extern void efi_free_boot_services(void); | ||
145 | extern void efi_reserve_boot_services(void); | ||
144 | 146 | ||
145 | struct efi_setup_data { | 147 | struct efi_setup_data { |
146 | u64 fw_vendor; | 148 | u64 fw_vendor; |
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h index 106b7d0e2dae..d6ff0bbdb394 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h | |||
@@ -564,8 +564,12 @@ extern pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address, | |||
564 | unsigned int *level); | 564 | unsigned int *level); |
565 | extern pmd_t *lookup_pmd_address(unsigned long address); | 565 | extern pmd_t *lookup_pmd_address(unsigned long address); |
566 | extern phys_addr_t slow_virt_to_phys(void *__address); | 566 | extern phys_addr_t slow_virt_to_phys(void *__address); |
567 | extern int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, | 567 | extern int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, |
568 | unsigned numpages, unsigned long page_flags); | 568 | unsigned long address, |
569 | unsigned numpages, | ||
570 | unsigned long page_flags); | ||
571 | extern int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address, | ||
572 | unsigned long numpages); | ||
569 | #endif /* !__ASSEMBLY__ */ | 573 | #endif /* !__ASSEMBLY__ */ |
570 | 574 | ||
571 | #endif /* _ASM_X86_PGTABLE_DEFS_H */ | 575 | #endif /* _ASM_X86_PGTABLE_DEFS_H */ |
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index a1bcde35db4c..61bc7d1800d7 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -2346,8 +2346,8 @@ bool kernel_page_present(struct page *page) | |||
2346 | 2346 | ||
2347 | #endif /* CONFIG_DEBUG_PAGEALLOC */ | 2347 | #endif /* CONFIG_DEBUG_PAGEALLOC */ |
2348 | 2348 | ||
2349 | int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, | 2349 | int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, |
2350 | unsigned numpages, unsigned long page_flags) | 2350 | unsigned numpages, unsigned long page_flags) |
2351 | { | 2351 | { |
2352 | int retval = -EINVAL; | 2352 | int retval = -EINVAL; |
2353 | 2353 | ||
@@ -2361,6 +2361,8 @@ int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, | |||
2361 | .flags = 0, | 2361 | .flags = 0, |
2362 | }; | 2362 | }; |
2363 | 2363 | ||
2364 | WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP"); | ||
2365 | |||
2364 | if (!(__supported_pte_mask & _PAGE_NX)) | 2366 | if (!(__supported_pte_mask & _PAGE_NX)) |
2365 | goto out; | 2367 | goto out; |
2366 | 2368 | ||
@@ -2383,6 +2385,40 @@ out: | |||
2383 | } | 2385 | } |
2384 | 2386 | ||
2385 | /* | 2387 | /* |
2388 | * __flush_tlb_all() flushes mappings only on current CPU and hence this | ||
2389 | * function shouldn't be used in an SMP environment. Presently, it's used only | ||
2390 | * during boot (way before smp_init()) by EFI subsystem and hence is ok. | ||
2391 | */ | ||
2392 | int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address, | ||
2393 | unsigned long numpages) | ||
2394 | { | ||
2395 | int retval; | ||
2396 | |||
2397 | /* | ||
2398 | * The typical sequence for unmapping is to find a pte through | ||
2399 | * lookup_address_in_pgd() (ideally, it should never return NULL because | ||
2400 | * the address is already mapped) and change it's protections. As pfn is | ||
2401 | * the *target* of a mapping, it's not useful while unmapping. | ||
2402 | */ | ||
2403 | struct cpa_data cpa = { | ||
2404 | .vaddr = &address, | ||
2405 | .pfn = 0, | ||
2406 | .pgd = pgd, | ||
2407 | .numpages = numpages, | ||
2408 | .mask_set = __pgprot(0), | ||
2409 | .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW), | ||
2410 | .flags = 0, | ||
2411 | }; | ||
2412 | |||
2413 | WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP"); | ||
2414 | |||
2415 | retval = __change_page_attr_set_clr(&cpa, 0); | ||
2416 | __flush_tlb_all(); | ||
2417 | |||
2418 | return retval; | ||
2419 | } | ||
2420 | |||
2421 | /* | ||
2386 | * The testcases use internal knowledge of the implementation that shouldn't | 2422 | * The testcases use internal knowledge of the implementation that shouldn't |
2387 | * be exposed to the rest of the kernel. Include these directly here. | 2423 | * be exposed to the rest of the kernel. Include these directly here. |
2388 | */ | 2424 | */ |
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 7ae939e353cd..e1cb01a22fa8 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -993,6 +993,8 @@ static void __init __efi_enter_virtual_mode(void) | |||
993 | panic("EFI call to SetVirtualAddressMap() failed!"); | 993 | panic("EFI call to SetVirtualAddressMap() failed!"); |
994 | } | 994 | } |
995 | 995 | ||
996 | efi_free_boot_services(); | ||
997 | |||
996 | /* | 998 | /* |
997 | * Now that EFI is in virtual mode, update the function | 999 | * Now that EFI is in virtual mode, update the function |
998 | * pointers in the runtime service table to the new virtual addresses. | 1000 | * pointers in the runtime service table to the new virtual addresses. |
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 95e77a667ba5..17456a1d3f04 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c | |||
@@ -369,6 +369,40 @@ void __init efi_reserve_boot_services(void) | |||
369 | } | 369 | } |
370 | } | 370 | } |
371 | 371 | ||
372 | /* | ||
373 | * Apart from having VA mappings for EFI boot services code/data regions, | ||
374 | * (duplicate) 1:1 mappings were also created as a quirk for buggy firmware. So, | ||
375 | * unmap both 1:1 and VA mappings. | ||
376 | */ | ||
377 | static void __init efi_unmap_pages(efi_memory_desc_t *md) | ||
378 | { | ||
379 | pgd_t *pgd = efi_mm.pgd; | ||
380 | u64 pa = md->phys_addr; | ||
381 | u64 va = md->virt_addr; | ||
382 | |||
383 | /* | ||
384 | * To Do: Remove this check after adding functionality to unmap EFI boot | ||
385 | * services code/data regions from direct mapping area because | ||
386 | * "efi=old_map" maps EFI regions in swapper_pg_dir. | ||
387 | */ | ||
388 | if (efi_enabled(EFI_OLD_MEMMAP)) | ||
389 | return; | ||
390 | |||
391 | /* | ||
392 | * EFI mixed mode has all RAM mapped to access arguments while making | ||
393 | * EFI runtime calls, hence don't unmap EFI boot services code/data | ||
394 | * regions. | ||
395 | */ | ||
396 | if (!efi_is_native()) | ||
397 | return; | ||
398 | |||
399 | if (kernel_unmap_pages_in_pgd(pgd, pa, md->num_pages)) | ||
400 | pr_err("Failed to unmap 1:1 mapping for 0x%llx\n", pa); | ||
401 | |||
402 | if (kernel_unmap_pages_in_pgd(pgd, va, md->num_pages)) | ||
403 | pr_err("Failed to unmap VA mapping for 0x%llx\n", va); | ||
404 | } | ||
405 | |||
372 | void __init efi_free_boot_services(void) | 406 | void __init efi_free_boot_services(void) |
373 | { | 407 | { |
374 | phys_addr_t new_phys, new_size; | 408 | phys_addr_t new_phys, new_size; |
@@ -394,6 +428,13 @@ void __init efi_free_boot_services(void) | |||
394 | } | 428 | } |
395 | 429 | ||
396 | /* | 430 | /* |
431 | * Before calling set_virtual_address_map(), EFI boot services | ||
432 | * code/data regions were mapped as a quirk for buggy firmware. | ||
433 | * Unmap them from efi_pgd before freeing them up. | ||
434 | */ | ||
435 | efi_unmap_pages(md); | ||
436 | |||
437 | /* | ||
397 | * Nasty quirk: if all sub-1MB memory is used for boot | 438 | * Nasty quirk: if all sub-1MB memory is used for boot |
398 | * services, we can get here without having allocated the | 439 | * services, we can get here without having allocated the |
399 | * real mode trampoline. It's too late to hand boot services | 440 | * real mode trampoline. It's too late to hand boot services |
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 415849bab233..4c46ff6f2242 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c | |||
@@ -602,21 +602,33 @@ int __init efi_apply_persistent_mem_reservations(void) | |||
602 | 602 | ||
603 | while (prsv) { | 603 | while (prsv) { |
604 | struct linux_efi_memreserve *rsv; | 604 | struct linux_efi_memreserve *rsv; |
605 | 605 | u8 *p; | |
606 | /* reserve the entry itself */ | 606 | int i; |
607 | memblock_reserve(prsv, sizeof(*rsv)); | 607 | |
608 | 608 | /* | |
609 | rsv = early_memremap(prsv, sizeof(*rsv)); | 609 | * Just map a full page: that is what we will get |
610 | if (rsv == NULL) { | 610 | * anyway, and it permits us to map the entire entry |
611 | * before knowing its size. | ||
612 | */ | ||
613 | p = early_memremap(ALIGN_DOWN(prsv, PAGE_SIZE), | ||
614 | PAGE_SIZE); | ||
615 | if (p == NULL) { | ||
611 | pr_err("Could not map UEFI memreserve entry!\n"); | 616 | pr_err("Could not map UEFI memreserve entry!\n"); |
612 | return -ENOMEM; | 617 | return -ENOMEM; |
613 | } | 618 | } |
614 | 619 | ||
615 | if (rsv->size) | 620 | rsv = (void *)(p + prsv % PAGE_SIZE); |
616 | memblock_reserve(rsv->base, rsv->size); | 621 | |
622 | /* reserve the entry itself */ | ||
623 | memblock_reserve(prsv, EFI_MEMRESERVE_SIZE(rsv->size)); | ||
624 | |||
625 | for (i = 0; i < atomic_read(&rsv->count); i++) { | ||
626 | memblock_reserve(rsv->entry[i].base, | ||
627 | rsv->entry[i].size); | ||
628 | } | ||
617 | 629 | ||
618 | prsv = rsv->next; | 630 | prsv = rsv->next; |
619 | early_memunmap(rsv, sizeof(*rsv)); | 631 | early_memunmap(p, PAGE_SIZE); |
620 | } | 632 | } |
621 | } | 633 | } |
622 | 634 | ||
@@ -985,7 +997,8 @@ static int __init efi_memreserve_map_root(void) | |||
985 | int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) | 997 | int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) |
986 | { | 998 | { |
987 | struct linux_efi_memreserve *rsv; | 999 | struct linux_efi_memreserve *rsv; |
988 | int rc; | 1000 | unsigned long prsv; |
1001 | int rc, index; | ||
989 | 1002 | ||
990 | if (efi_memreserve_root == (void *)ULONG_MAX) | 1003 | if (efi_memreserve_root == (void *)ULONG_MAX) |
991 | return -ENODEV; | 1004 | return -ENODEV; |
@@ -996,12 +1009,27 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) | |||
996 | return rc; | 1009 | return rc; |
997 | } | 1010 | } |
998 | 1011 | ||
999 | rsv = kmalloc(sizeof(*rsv), GFP_ATOMIC); | 1012 | /* first try to find a slot in an existing linked list entry */ |
1013 | for (prsv = efi_memreserve_root->next; prsv; prsv = rsv->next) { | ||
1014 | rsv = __va(prsv); | ||
1015 | index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size); | ||
1016 | if (index < rsv->size) { | ||
1017 | rsv->entry[index].base = addr; | ||
1018 | rsv->entry[index].size = size; | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | } | ||
1023 | |||
1024 | /* no slot found - allocate a new linked list entry */ | ||
1025 | rsv = (struct linux_efi_memreserve *)__get_free_page(GFP_ATOMIC); | ||
1000 | if (!rsv) | 1026 | if (!rsv) |
1001 | return -ENOMEM; | 1027 | return -ENOMEM; |
1002 | 1028 | ||
1003 | rsv->base = addr; | 1029 | rsv->size = EFI_MEMRESERVE_COUNT(PAGE_SIZE); |
1004 | rsv->size = size; | 1030 | atomic_set(&rsv->count, 1); |
1031 | rsv->entry[0].base = addr; | ||
1032 | rsv->entry[0].size = size; | ||
1005 | 1033 | ||
1006 | spin_lock(&efi_mem_reserve_persistent_lock); | 1034 | spin_lock(&efi_mem_reserve_persistent_lock); |
1007 | rsv->next = efi_memreserve_root->next; | 1035 | rsv->next = efi_memreserve_root->next; |
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index c51627660dbb..d9845099635e 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile | |||
@@ -9,7 +9,10 @@ cflags-$(CONFIG_X86_32) := -march=i386 | |||
9 | cflags-$(CONFIG_X86_64) := -mcmodel=small | 9 | cflags-$(CONFIG_X86_64) := -mcmodel=small |
10 | cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \ | 10 | cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \ |
11 | -fPIC -fno-strict-aliasing -mno-red-zone \ | 11 | -fPIC -fno-strict-aliasing -mno-red-zone \ |
12 | -mno-mmx -mno-sse -fshort-wchar | 12 | -mno-mmx -mno-sse -fshort-wchar \ |
13 | -Wno-pointer-sign \ | ||
14 | $(call cc-disable-warning, address-of-packed-member) \ | ||
15 | $(call cc-disable-warning, gnu) | ||
13 | 16 | ||
14 | # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly | 17 | # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly |
15 | # disable the stackleak plugin | 18 | # disable the stackleak plugin |
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 6640942a1c0d..eee42d5e25ee 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c | |||
@@ -86,8 +86,8 @@ void install_memreserve_table(efi_system_table_t *sys_table_arg) | |||
86 | } | 86 | } |
87 | 87 | ||
88 | rsv->next = 0; | 88 | rsv->next = 0; |
89 | rsv->base = 0; | ||
90 | rsv->size = 0; | 89 | rsv->size = 0; |
90 | atomic_set(&rsv->count, 0); | ||
91 | 91 | ||
92 | status = efi_call_early(install_configuration_table, | 92 | status = efi_call_early(install_configuration_table, |
93 | &memreserve_table_guid, | 93 | &memreserve_table_guid, |
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 0c0d2312f4a8..0dc7b4987cc2 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c | |||
@@ -370,22 +370,24 @@ void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size) | |||
370 | { | 370 | { |
371 | efi_guid_t fdt_guid = DEVICE_TREE_GUID; | 371 | efi_guid_t fdt_guid = DEVICE_TREE_GUID; |
372 | efi_config_table_t *tables; | 372 | efi_config_table_t *tables; |
373 | void *fdt; | ||
374 | int i; | 373 | int i; |
375 | 374 | ||
376 | tables = (efi_config_table_t *) sys_table->tables; | 375 | tables = (efi_config_table_t *)sys_table->tables; |
377 | fdt = NULL; | ||
378 | 376 | ||
379 | for (i = 0; i < sys_table->nr_tables; i++) | 377 | for (i = 0; i < sys_table->nr_tables; i++) { |
380 | if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) { | 378 | void *fdt; |
381 | fdt = (void *) tables[i].table; | 379 | |
382 | if (fdt_check_header(fdt) != 0) { | 380 | if (efi_guidcmp(tables[i].guid, fdt_guid) != 0) |
383 | pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n"); | 381 | continue; |
384 | return NULL; | 382 | |
385 | } | 383 | fdt = (void *)tables[i].table; |
386 | *fdt_size = fdt_totalsize(fdt); | 384 | if (fdt_check_header(fdt) != 0) { |
387 | break; | 385 | pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n"); |
388 | } | 386 | return NULL; |
387 | } | ||
388 | *fdt_size = fdt_totalsize(fdt); | ||
389 | return fdt; | ||
390 | } | ||
389 | 391 | ||
390 | return fdt; | 392 | return NULL; |
391 | } | 393 | } |
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 9336ffdf6e2c..fceaafd67ec6 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c | |||
@@ -318,7 +318,12 @@ EXPORT_SYMBOL_GPL(efivar_variable_is_removable); | |||
318 | static efi_status_t | 318 | static efi_status_t |
319 | check_var_size(u32 attributes, unsigned long size) | 319 | check_var_size(u32 attributes, unsigned long size) |
320 | { | 320 | { |
321 | const struct efivar_operations *fops = __efivars->ops; | 321 | const struct efivar_operations *fops; |
322 | |||
323 | if (!__efivars) | ||
324 | return EFI_UNSUPPORTED; | ||
325 | |||
326 | fops = __efivars->ops; | ||
322 | 327 | ||
323 | if (!fops->query_variable_store) | 328 | if (!fops->query_variable_store) |
324 | return EFI_UNSUPPORTED; | 329 | return EFI_UNSUPPORTED; |
@@ -329,7 +334,12 @@ check_var_size(u32 attributes, unsigned long size) | |||
329 | static efi_status_t | 334 | static efi_status_t |
330 | check_var_size_nonblocking(u32 attributes, unsigned long size) | 335 | check_var_size_nonblocking(u32 attributes, unsigned long size) |
331 | { | 336 | { |
332 | const struct efivar_operations *fops = __efivars->ops; | 337 | const struct efivar_operations *fops; |
338 | |||
339 | if (!__efivars) | ||
340 | return EFI_UNSUPPORTED; | ||
341 | |||
342 | fops = __efivars->ops; | ||
333 | 343 | ||
334 | if (!fops->query_variable_store) | 344 | if (!fops->query_variable_store) |
335 | return EFI_UNSUPPORTED; | 345 | return EFI_UNSUPPORTED; |
@@ -429,13 +439,18 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, | |||
429 | int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), | 439 | int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), |
430 | void *data, bool duplicates, struct list_head *head) | 440 | void *data, bool duplicates, struct list_head *head) |
431 | { | 441 | { |
432 | const struct efivar_operations *ops = __efivars->ops; | 442 | const struct efivar_operations *ops; |
433 | unsigned long variable_name_size = 1024; | 443 | unsigned long variable_name_size = 1024; |
434 | efi_char16_t *variable_name; | 444 | efi_char16_t *variable_name; |
435 | efi_status_t status; | 445 | efi_status_t status; |
436 | efi_guid_t vendor_guid; | 446 | efi_guid_t vendor_guid; |
437 | int err = 0; | 447 | int err = 0; |
438 | 448 | ||
449 | if (!__efivars) | ||
450 | return -EFAULT; | ||
451 | |||
452 | ops = __efivars->ops; | ||
453 | |||
439 | variable_name = kzalloc(variable_name_size, GFP_KERNEL); | 454 | variable_name = kzalloc(variable_name_size, GFP_KERNEL); |
440 | if (!variable_name) { | 455 | if (!variable_name) { |
441 | printk(KERN_ERR "efivars: Memory allocation failed.\n"); | 456 | printk(KERN_ERR "efivars: Memory allocation failed.\n"); |
@@ -583,12 +598,14 @@ static void efivar_entry_list_del_unlock(struct efivar_entry *entry) | |||
583 | */ | 598 | */ |
584 | int __efivar_entry_delete(struct efivar_entry *entry) | 599 | int __efivar_entry_delete(struct efivar_entry *entry) |
585 | { | 600 | { |
586 | const struct efivar_operations *ops = __efivars->ops; | ||
587 | efi_status_t status; | 601 | efi_status_t status; |
588 | 602 | ||
589 | status = ops->set_variable(entry->var.VariableName, | 603 | if (!__efivars) |
590 | &entry->var.VendorGuid, | 604 | return -EINVAL; |
591 | 0, 0, NULL); | 605 | |
606 | status = __efivars->ops->set_variable(entry->var.VariableName, | ||
607 | &entry->var.VendorGuid, | ||
608 | 0, 0, NULL); | ||
592 | 609 | ||
593 | return efi_status_to_err(status); | 610 | return efi_status_to_err(status); |
594 | } | 611 | } |
@@ -607,12 +624,17 @@ EXPORT_SYMBOL_GPL(__efivar_entry_delete); | |||
607 | */ | 624 | */ |
608 | int efivar_entry_delete(struct efivar_entry *entry) | 625 | int efivar_entry_delete(struct efivar_entry *entry) |
609 | { | 626 | { |
610 | const struct efivar_operations *ops = __efivars->ops; | 627 | const struct efivar_operations *ops; |
611 | efi_status_t status; | 628 | efi_status_t status; |
612 | 629 | ||
613 | if (down_interruptible(&efivars_lock)) | 630 | if (down_interruptible(&efivars_lock)) |
614 | return -EINTR; | 631 | return -EINTR; |
615 | 632 | ||
633 | if (!__efivars) { | ||
634 | up(&efivars_lock); | ||
635 | return -EINVAL; | ||
636 | } | ||
637 | ops = __efivars->ops; | ||
616 | status = ops->set_variable(entry->var.VariableName, | 638 | status = ops->set_variable(entry->var.VariableName, |
617 | &entry->var.VendorGuid, | 639 | &entry->var.VendorGuid, |
618 | 0, 0, NULL); | 640 | 0, 0, NULL); |
@@ -650,13 +672,19 @@ EXPORT_SYMBOL_GPL(efivar_entry_delete); | |||
650 | int efivar_entry_set(struct efivar_entry *entry, u32 attributes, | 672 | int efivar_entry_set(struct efivar_entry *entry, u32 attributes, |
651 | unsigned long size, void *data, struct list_head *head) | 673 | unsigned long size, void *data, struct list_head *head) |
652 | { | 674 | { |
653 | const struct efivar_operations *ops = __efivars->ops; | 675 | const struct efivar_operations *ops; |
654 | efi_status_t status; | 676 | efi_status_t status; |
655 | efi_char16_t *name = entry->var.VariableName; | 677 | efi_char16_t *name = entry->var.VariableName; |
656 | efi_guid_t vendor = entry->var.VendorGuid; | 678 | efi_guid_t vendor = entry->var.VendorGuid; |
657 | 679 | ||
658 | if (down_interruptible(&efivars_lock)) | 680 | if (down_interruptible(&efivars_lock)) |
659 | return -EINTR; | 681 | return -EINTR; |
682 | |||
683 | if (!__efivars) { | ||
684 | up(&efivars_lock); | ||
685 | return -EINVAL; | ||
686 | } | ||
687 | ops = __efivars->ops; | ||
660 | if (head && efivar_entry_find(name, vendor, head, false)) { | 688 | if (head && efivar_entry_find(name, vendor, head, false)) { |
661 | up(&efivars_lock); | 689 | up(&efivars_lock); |
662 | return -EEXIST; | 690 | return -EEXIST; |
@@ -687,12 +715,17 @@ static int | |||
687 | efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, | 715 | efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, |
688 | u32 attributes, unsigned long size, void *data) | 716 | u32 attributes, unsigned long size, void *data) |
689 | { | 717 | { |
690 | const struct efivar_operations *ops = __efivars->ops; | 718 | const struct efivar_operations *ops; |
691 | efi_status_t status; | 719 | efi_status_t status; |
692 | 720 | ||
693 | if (down_trylock(&efivars_lock)) | 721 | if (down_trylock(&efivars_lock)) |
694 | return -EBUSY; | 722 | return -EBUSY; |
695 | 723 | ||
724 | if (!__efivars) { | ||
725 | up(&efivars_lock); | ||
726 | return -EINVAL; | ||
727 | } | ||
728 | |||
696 | status = check_var_size_nonblocking(attributes, | 729 | status = check_var_size_nonblocking(attributes, |
697 | size + ucs2_strsize(name, 1024)); | 730 | size + ucs2_strsize(name, 1024)); |
698 | if (status != EFI_SUCCESS) { | 731 | if (status != EFI_SUCCESS) { |
@@ -700,6 +733,7 @@ efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, | |||
700 | return -ENOSPC; | 733 | return -ENOSPC; |
701 | } | 734 | } |
702 | 735 | ||
736 | ops = __efivars->ops; | ||
703 | status = ops->set_variable_nonblocking(name, &vendor, attributes, | 737 | status = ops->set_variable_nonblocking(name, &vendor, attributes, |
704 | size, data); | 738 | size, data); |
705 | 739 | ||
@@ -727,9 +761,13 @@ efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, | |||
727 | int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, | 761 | int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, |
728 | bool block, unsigned long size, void *data) | 762 | bool block, unsigned long size, void *data) |
729 | { | 763 | { |
730 | const struct efivar_operations *ops = __efivars->ops; | 764 | const struct efivar_operations *ops; |
731 | efi_status_t status; | 765 | efi_status_t status; |
732 | 766 | ||
767 | if (!__efivars) | ||
768 | return -EINVAL; | ||
769 | |||
770 | ops = __efivars->ops; | ||
733 | if (!ops->query_variable_store) | 771 | if (!ops->query_variable_store) |
734 | return -ENOSYS; | 772 | return -ENOSYS; |
735 | 773 | ||
@@ -829,13 +867,18 @@ EXPORT_SYMBOL_GPL(efivar_entry_find); | |||
829 | */ | 867 | */ |
830 | int efivar_entry_size(struct efivar_entry *entry, unsigned long *size) | 868 | int efivar_entry_size(struct efivar_entry *entry, unsigned long *size) |
831 | { | 869 | { |
832 | const struct efivar_operations *ops = __efivars->ops; | 870 | const struct efivar_operations *ops; |
833 | efi_status_t status; | 871 | efi_status_t status; |
834 | 872 | ||
835 | *size = 0; | 873 | *size = 0; |
836 | 874 | ||
837 | if (down_interruptible(&efivars_lock)) | 875 | if (down_interruptible(&efivars_lock)) |
838 | return -EINTR; | 876 | return -EINTR; |
877 | if (!__efivars) { | ||
878 | up(&efivars_lock); | ||
879 | return -EINVAL; | ||
880 | } | ||
881 | ops = __efivars->ops; | ||
839 | status = ops->get_variable(entry->var.VariableName, | 882 | status = ops->get_variable(entry->var.VariableName, |
840 | &entry->var.VendorGuid, NULL, size, NULL); | 883 | &entry->var.VendorGuid, NULL, size, NULL); |
841 | up(&efivars_lock); | 884 | up(&efivars_lock); |
@@ -861,12 +904,14 @@ EXPORT_SYMBOL_GPL(efivar_entry_size); | |||
861 | int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes, | 904 | int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes, |
862 | unsigned long *size, void *data) | 905 | unsigned long *size, void *data) |
863 | { | 906 | { |
864 | const struct efivar_operations *ops = __efivars->ops; | ||
865 | efi_status_t status; | 907 | efi_status_t status; |
866 | 908 | ||
867 | status = ops->get_variable(entry->var.VariableName, | 909 | if (!__efivars) |
868 | &entry->var.VendorGuid, | 910 | return -EINVAL; |
869 | attributes, size, data); | 911 | |
912 | status = __efivars->ops->get_variable(entry->var.VariableName, | ||
913 | &entry->var.VendorGuid, | ||
914 | attributes, size, data); | ||
870 | 915 | ||
871 | return efi_status_to_err(status); | 916 | return efi_status_to_err(status); |
872 | } | 917 | } |
@@ -882,14 +927,19 @@ EXPORT_SYMBOL_GPL(__efivar_entry_get); | |||
882 | int efivar_entry_get(struct efivar_entry *entry, u32 *attributes, | 927 | int efivar_entry_get(struct efivar_entry *entry, u32 *attributes, |
883 | unsigned long *size, void *data) | 928 | unsigned long *size, void *data) |
884 | { | 929 | { |
885 | const struct efivar_operations *ops = __efivars->ops; | ||
886 | efi_status_t status; | 930 | efi_status_t status; |
887 | 931 | ||
888 | if (down_interruptible(&efivars_lock)) | 932 | if (down_interruptible(&efivars_lock)) |
889 | return -EINTR; | 933 | return -EINTR; |
890 | status = ops->get_variable(entry->var.VariableName, | 934 | |
891 | &entry->var.VendorGuid, | 935 | if (!__efivars) { |
892 | attributes, size, data); | 936 | up(&efivars_lock); |
937 | return -EINVAL; | ||
938 | } | ||
939 | |||
940 | status = __efivars->ops->get_variable(entry->var.VariableName, | ||
941 | &entry->var.VendorGuid, | ||
942 | attributes, size, data); | ||
893 | up(&efivars_lock); | 943 | up(&efivars_lock); |
894 | 944 | ||
895 | return efi_status_to_err(status); | 945 | return efi_status_to_err(status); |
@@ -921,7 +971,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_get); | |||
921 | int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | 971 | int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, |
922 | unsigned long *size, void *data, bool *set) | 972 | unsigned long *size, void *data, bool *set) |
923 | { | 973 | { |
924 | const struct efivar_operations *ops = __efivars->ops; | 974 | const struct efivar_operations *ops; |
925 | efi_char16_t *name = entry->var.VariableName; | 975 | efi_char16_t *name = entry->var.VariableName; |
926 | efi_guid_t *vendor = &entry->var.VendorGuid; | 976 | efi_guid_t *vendor = &entry->var.VendorGuid; |
927 | efi_status_t status; | 977 | efi_status_t status; |
@@ -940,6 +990,11 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | |||
940 | if (down_interruptible(&efivars_lock)) | 990 | if (down_interruptible(&efivars_lock)) |
941 | return -EINTR; | 991 | return -EINTR; |
942 | 992 | ||
993 | if (!__efivars) { | ||
994 | err = -EINVAL; | ||
995 | goto out; | ||
996 | } | ||
997 | |||
943 | /* | 998 | /* |
944 | * Ensure that the available space hasn't shrunk below the safe level | 999 | * Ensure that the available space hasn't shrunk below the safe level |
945 | */ | 1000 | */ |
@@ -956,6 +1011,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, | |||
956 | } | 1011 | } |
957 | } | 1012 | } |
958 | 1013 | ||
1014 | ops = __efivars->ops; | ||
1015 | |||
959 | status = ops->set_variable(name, vendor, attributes, *size, data); | 1016 | status = ops->set_variable(name, vendor, attributes, *size, data); |
960 | if (status != EFI_SUCCESS) { | 1017 | if (status != EFI_SUCCESS) { |
961 | err = efi_status_to_err(status); | 1018 | err = efi_status_to_err(status); |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 100ce4a4aff6..becd5d76a207 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -1000,13 +1000,11 @@ extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg); | |||
1000 | extern void efi_gettimeofday (struct timespec64 *ts); | 1000 | extern void efi_gettimeofday (struct timespec64 *ts); |
1001 | extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ | 1001 | extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ |
1002 | #ifdef CONFIG_X86 | 1002 | #ifdef CONFIG_X86 |
1003 | extern void efi_free_boot_services(void); | ||
1004 | extern efi_status_t efi_query_variable_store(u32 attributes, | 1003 | extern efi_status_t efi_query_variable_store(u32 attributes, |
1005 | unsigned long size, | 1004 | unsigned long size, |
1006 | bool nonblocking); | 1005 | bool nonblocking); |
1007 | extern void efi_find_mirror(void); | 1006 | extern void efi_find_mirror(void); |
1008 | #else | 1007 | #else |
1009 | static inline void efi_free_boot_services(void) {} | ||
1010 | 1008 | ||
1011 | static inline efi_status_t efi_query_variable_store(u32 attributes, | 1009 | static inline efi_status_t efi_query_variable_store(u32 attributes, |
1012 | unsigned long size, | 1010 | unsigned long size, |
@@ -1046,7 +1044,6 @@ extern void efi_mem_reserve(phys_addr_t addr, u64 size); | |||
1046 | extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); | 1044 | extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); |
1047 | extern void efi_initialize_iomem_resources(struct resource *code_resource, | 1045 | extern void efi_initialize_iomem_resources(struct resource *code_resource, |
1048 | struct resource *data_resource, struct resource *bss_resource); | 1046 | struct resource *data_resource, struct resource *bss_resource); |
1049 | extern void efi_reserve_boot_services(void); | ||
1050 | extern int efi_get_fdt_params(struct efi_fdt_params *params); | 1047 | extern int efi_get_fdt_params(struct efi_fdt_params *params); |
1051 | extern struct kobject *efi_kobj; | 1048 | extern struct kobject *efi_kobj; |
1052 | 1049 | ||
@@ -1715,9 +1712,19 @@ extern struct efi_runtime_work efi_rts_work; | |||
1715 | extern struct workqueue_struct *efi_rts_wq; | 1712 | extern struct workqueue_struct *efi_rts_wq; |
1716 | 1713 | ||
1717 | struct linux_efi_memreserve { | 1714 | struct linux_efi_memreserve { |
1718 | phys_addr_t next; | 1715 | int size; // allocated size of the array |
1719 | phys_addr_t base; | 1716 | atomic_t count; // number of entries used |
1720 | phys_addr_t size; | 1717 | phys_addr_t next; // pa of next struct instance |
1718 | struct { | ||
1719 | phys_addr_t base; | ||
1720 | phys_addr_t size; | ||
1721 | } entry[0]; | ||
1721 | }; | 1722 | }; |
1722 | 1723 | ||
1724 | #define EFI_MEMRESERVE_SIZE(count) (sizeof(struct linux_efi_memreserve) + \ | ||
1725 | (count) * sizeof(((struct linux_efi_memreserve *)0)->entry[0])) | ||
1726 | |||
1727 | #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \ | ||
1728 | / sizeof(((struct linux_efi_memreserve *)0)->entry[0])) | ||
1729 | |||
1723 | #endif /* _LINUX_EFI_H */ | 1730 | #endif /* _LINUX_EFI_H */ |
diff --git a/init/main.c b/init/main.c index a45486330243..954d9b6c62c6 100644 --- a/init/main.c +++ b/init/main.c | |||
@@ -737,10 +737,6 @@ asmlinkage __visible void __init start_kernel(void) | |||
737 | arch_post_acpi_subsys_init(); | 737 | arch_post_acpi_subsys_init(); |
738 | sfi_init_late(); | 738 | sfi_init_late(); |
739 | 739 | ||
740 | if (efi_enabled(EFI_RUNTIME_SERVICES)) { | ||
741 | efi_free_boot_services(); | ||
742 | } | ||
743 | |||
744 | /* Do the rest non-__init'ed, we're now alive */ | 740 | /* Do the rest non-__init'ed, we're now alive */ |
745 | arch_call_rest_init(); | 741 | arch_call_rest_init(); |
746 | } | 742 | } |