[tip:efi/core] x86/efi: Mark can_free_region() as an __init function
Commit-ID: 8fe55212aacfce9b7718de7964b3a3096ec30919 Gitweb: https://git.kernel.org/tip/8fe55212aacfce9b7718de7964b3a3096ec30919 Author: Sai Praneeth Prakhya AuthorDate: Sat, 2 Feb 2019 10:41:10 +0100 Committer: Ingo Molnar CommitDate: Mon, 4 Feb 2019 08:19:22 +0100 x86/efi: Mark can_free_region() as an __init function can_free_region() is called only once during boot, by efi_reserve_boot_services(). Hence, mark it as an __init function. Signed-off-by: Sai Praneeth Prakhya Signed-off-by: Ard Biesheuvel Cc: AKASHI Takahiro Cc: Alexander Graf Cc: Bjorn Andersson Cc: Borislav Petkov Cc: Heinrich Schuchardt Cc: Jeffrey Hugo Cc: Lee Jones Cc: Leif Lindholm Cc: Linus Torvalds Cc: Matt Fleming Cc: Peter Jones Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-...@vger.kernel.org Link: http://lkml.kernel.org/r/20190202094119.13230-2-ard.biesheu...@linaro.org Signed-off-by: Ingo Molnar --- arch/x86/platform/efi/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 17456a1d3f04..9ce85e605052 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -304,7 +304,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) * - Not within any part of the kernel * - Not the BIOS reserved area (E820_TYPE_RESERVED, E820_TYPE_NVS, etc) */ -static bool can_free_region(u64 start, u64 size) +static __init bool can_free_region(u64 start, u64 size) { if (start + size > __pa_symbol(_text) && start <= __pa_symbol(_end)) return false;
[tip:efi/core] x86/efi: Don't unmap EFI boot services code/data regions for EFI_OLD_MEMMAP and EFI_MIXED_MODE
Commit-ID: 1debf0958fa27b7c469dbf22754929ec59a7c0e7 Gitweb: https://git.kernel.org/tip/1debf0958fa27b7c469dbf22754929ec59a7c0e7 Author: Sai Praneeth Prakhya AuthorDate: Fri, 21 Dec 2018 18:22:34 -0800 Committer: Ingo Molnar CommitDate: Sat, 22 Dec 2018 20:58:30 +0100 x86/efi: Don't unmap EFI boot services code/data regions for EFI_OLD_MEMMAP and EFI_MIXED_MODE The following commit: d5052a7130a6 ("x86/efi: Unmap EFI boot services code/data regions from efi_pgd") forgets to take two EFI modes into consideration, namely EFI_OLD_MEMMAP and EFI_MIXED_MODE: - EFI_OLD_MEMMAP is a legacy way of mapping EFI regions into swapper_pg_dir using ioremap() and init_memory_mapping(). This feature can be enabled by passing "efi=old_map" as kernel command line argument. But, efi_unmap_pages() unmaps EFI boot services code/data regions *only* from efi_pgd and hence cannot be used for unmapping EFI boot services code/data regions from swapper_pg_dir. Introduce a temporary fix to not unmap EFI boot services code/data regions when EFI_OLD_MEMMAP is enabled while working on a real fix. - EFI_MIXED_MODE is another feature where a 64-bit kernel runs on a 64-bit platform crippled by a 32-bit firmware. To support EFI_MIXED_MODE, all RAM (i.e. namely EFI regions like EFI_CONVENTIONAL_MEMORY, EFI_LOADER_, EFI_BOOT_SERVICES_ and EFI_RUNTIME_CODE/DATA regions) is mapped into efi_pgd all the time to facilitate EFI runtime calls access it's arguments in 1:1 mode. Hence, don't unmap EFI boot services code/data regions when booted in mixed mode. Signed-off-by: Sai Praneeth Prakhya Acked-by: Ard Biesheuvel Cc: Andy Lutomirski Cc: Bhupesh Sharma Cc: Borislav Petkov Cc: Dave Hansen Cc: Dave Hansen Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Rik van Riel Cc: Thomas Gleixner Cc: linux-...@vger.kernel.org Link: http://lkml.kernel.org/r/20181222022234.7573-1-sai.praneeth.prak...@intel.com Signed-off-by: Ingo Molnar --- arch/x86/platform/efi/quirks.c | 16 1 file changed, 16 insertions(+) diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 09e811b9da26..17456a1d3f04 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -380,6 +380,22 @@ static void __init efi_unmap_pages(efi_memory_desc_t *md) u64 pa = md->phys_addr; u64 va = md->virt_addr; + /* +* To Do: Remove this check after adding functionality to unmap EFI boot +* services code/data regions from direct mapping area because +* "efi=old_map" maps EFI regions in swapper_pg_dir. +*/ + if (efi_enabled(EFI_OLD_MEMMAP)) + return; + + /* +* EFI mixed mode has all RAM mapped to access arguments while making +* EFI runtime calls, hence don't unmap EFI boot services code/data +* regions. +*/ + if (!efi_is_native()) + return; + if (kernel_unmap_pages_in_pgd(pgd, pa, md->num_pages)) pr_err("Failed to unmap 1:1 mapping for 0x%llx\n", pa);
[tip:efi/core] x86/efi: Move efi__boot_services() to arch/x86
Commit-ID: 47c33a095e1fae376d74b4160a0d73c1a4e73969 Gitweb: https://git.kernel.org/tip/47c33a095e1fae376d74b4160a0d73c1a4e73969 Author: Sai Praneeth Prakhya AuthorDate: Thu, 29 Nov 2018 18:12:25 +0100 Committer: Ingo Molnar CommitDate: Fri, 30 Nov 2018 09:10:31 +0100 x86/efi: Move efi__boot_services() to arch/x86 efi__boot_services() are x86 specific quirks and as such should be in asm/efi.h, so move them from linux/efi.h. Also, call efi_free_boot_services() from __efi_enter_virtual_mode() as it is x86 specific call and ideally shouldn't be part of init/main.c Signed-off-by: Sai Praneeth Prakhya Signed-off-by: Ard Biesheuvel Acked-by: Thomas Gleixner Cc: Andy Lutomirski Cc: Arend van Spriel Cc: Bhupesh Sharma Cc: Borislav Petkov Cc: Dave Hansen Cc: Eric Snowberg Cc: Hans de Goede Cc: Joe Perches Cc: Jon Hunter Cc: Julien Thierry Cc: Linus Torvalds Cc: Marc Zyngier Cc: Matt Fleming Cc: Nathan Chancellor Cc: Peter Zijlstra Cc: Sedat Dilek Cc: YiFei Zhu Cc: linux-...@vger.kernel.org Link: http://lkml.kernel.org/r/20181129171230.18699-7-ard.biesheu...@linaro.org Signed-off-by: Ingo Molnar --- arch/x86/include/asm/efi.h | 2 ++ arch/x86/platform/efi/efi.c | 2 ++ include/linux/efi.h | 3 --- init/main.c | 4 4 files changed, 4 insertions(+), 7 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); extern void efi_delete_dummy_variable(void); extern void efi_switch_mm(struct mm_struct *mm); extern void efi_recover_from_page_fault(unsigned long phys_addr); +extern void efi_free_boot_services(void); +extern void efi_reserve_boot_services(void); struct efi_setup_data { u64 fw_vendor; 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) panic("EFI call to SetVirtualAddressMap() failed!"); } + efi_free_boot_services(); + /* * Now that EFI is in virtual mode, update the function * pointers in the runtime service table to the new virtual addresses. diff --git a/include/linux/efi.h b/include/linux/efi.h index 100ce4a4aff6..2b3b33c83b05 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); extern void efi_gettimeofday (struct timespec64 *ts); extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ #ifdef CONFIG_X86 -extern void efi_free_boot_services(void); extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size, bool nonblocking); extern void efi_find_mirror(void); #else -static inline void efi_free_boot_services(void) {} static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned long size, @@ -1046,7 +1044,6 @@ extern void efi_mem_reserve(phys_addr_t addr, u64 size); extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); -extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params); extern struct kobject *efi_kobj; diff --git a/init/main.c b/init/main.c index ee147103ba1b..ccefcd8e855f 100644 --- a/init/main.c +++ b/init/main.c @@ -737,10 +737,6 @@ asmlinkage __visible void __init start_kernel(void) arch_post_acpi_subsys_init(); sfi_init_late(); - if (efi_enabled(EFI_RUNTIME_SERVICES)) { - efi_free_boot_services(); - } - /* Do the rest non-__init'ed, we're now alive */ arch_call_rest_init(); }
[tip:efi/core] x86/efi: Unmap EFI boot services code/data regions from efi_pgd
Commit-ID: 08cfb38f3ef49cfd1bba11a00401451606477d80 Gitweb: https://git.kernel.org/tip/08cfb38f3ef49cfd1bba11a00401451606477d80 Author: Sai Praneeth Prakhya AuthorDate: Thu, 29 Nov 2018 18:12:24 +0100 Committer: Ingo Molnar CommitDate: Fri, 30 Nov 2018 09:10:30 +0100 x86/efi: Unmap EFI boot services code/data regions from efi_pgd efi_free_boot_services(), as the name suggests, frees EFI boot services code/data regions but forgets to unmap these regions from efi_pgd. This means that any code that's running in efi_pgd address space (e.g: any EFI runtime service) would still be able to access these regions but the contents of these regions would have long been over written by someone else. So, it's important to unmap these regions. Hence, introduce efi_unmap_pages() to unmap these regions from efi_pgd. After unmapping EFI boot services code/data regions, any illegal access by buggy firmware to these regions would result in page fault which will be handled by EFI specific fault handler. Signed-off-by: Sai Praneeth Prakhya Signed-off-by: Ard Biesheuvel Acked-by: Thomas Gleixner Cc: Andy Lutomirski Cc: Arend van Spriel Cc: Bhupesh Sharma Cc: Borislav Petkov Cc: Dave Hansen Cc: Eric Snowberg Cc: Hans de Goede Cc: Joe Perches Cc: Jon Hunter Cc: Julien Thierry Cc: Linus Torvalds Cc: Marc Zyngier Cc: Matt Fleming Cc: Nathan Chancellor Cc: Peter Zijlstra Cc: Sedat Dilek Cc: YiFei Zhu Cc: linux-...@vger.kernel.org Link: http://lkml.kernel.org/r/20181129171230.18699-6-ard.biesheu...@linaro.org Signed-off-by: Ingo Molnar --- arch/x86/platform/efi/quirks.c | 25 + 1 file changed, 25 insertions(+) diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 95e77a667ba5..09e811b9da26 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -369,6 +369,24 @@ void __init efi_reserve_boot_services(void) } } +/* + * Apart from having VA mappings for EFI boot services code/data regions, + * (duplicate) 1:1 mappings were also created as a quirk for buggy firmware. So, + * unmap both 1:1 and VA mappings. + */ +static void __init efi_unmap_pages(efi_memory_desc_t *md) +{ + pgd_t *pgd = efi_mm.pgd; + u64 pa = md->phys_addr; + u64 va = md->virt_addr; + + if (kernel_unmap_pages_in_pgd(pgd, pa, md->num_pages)) + pr_err("Failed to unmap 1:1 mapping for 0x%llx\n", pa); + + if (kernel_unmap_pages_in_pgd(pgd, va, md->num_pages)) + pr_err("Failed to unmap VA mapping for 0x%llx\n", va); +} + void __init efi_free_boot_services(void) { phys_addr_t new_phys, new_size; @@ -393,6 +411,13 @@ void __init efi_free_boot_services(void) continue; } + /* +* Before calling set_virtual_address_map(), EFI boot services +* code/data regions were mapped as a quirk for buggy firmware. +* Unmap them from efi_pgd before freeing them up. +*/ + efi_unmap_pages(md); + /* * Nasty quirk: if all sub-1MB memory is used for boot * services, we can get here without having allocated the
[tip:efi/core] x86/mm/pageattr: Introduce helper function to unmap EFI boot services
Commit-ID: 7e0dabd3010d6041ee0a952c1146b2150a11f1be Gitweb: https://git.kernel.org/tip/7e0dabd3010d6041ee0a952c1146b2150a11f1be Author: Sai Praneeth Prakhya AuthorDate: Thu, 29 Nov 2018 18:12:23 +0100 Committer: Ingo Molnar CommitDate: Fri, 30 Nov 2018 09:10:30 +0100 x86/mm/pageattr: Introduce helper function to unmap EFI boot services Ideally, after kernel assumes control of the platform, firmware shouldn't access EFI boot services code/data regions. But, it's noticed that this is not so true in many x86 platforms. Hence, during boot, kernel reserves EFI boot services code/data regions [1] and maps [2] them to efi_pgd so that call to set_virtual_address_map() doesn't fail. After returning from set_virtual_address_map(), kernel frees the reserved regions [3] but they still remain mapped. Hence, introduce kernel_unmap_pages_in_pgd() which will later be used to unmap EFI boot services code/data regions. While at it modify kernel_map_pages_in_pgd() by: 1. Adding __init modifier because it's always used *only* during boot. 2. Add a warning if it's used after SMP is initialized because it uses __flush_tlb_all() which flushes mappings only on current CPU. Unmapping EFI boot services code/data regions will result in clearing PAGE_PRESENT bit and it shouldn't bother L1TF cases because it's already handled by protnone_mask() at arch/x86/include/asm/pgtable-invert.h. [1] efi_reserve_boot_services() [2] efi_map_region() -> __map_region() -> kernel_map_pages_in_pgd() [3] efi_free_boot_services() Signed-off-by: Sai Praneeth Prakhya Signed-off-by: Ard Biesheuvel Reviewed-by: Thomas Gleixner Cc: Andy Lutomirski Cc: Arend van Spriel Cc: Bhupesh Sharma Cc: Borislav Petkov Cc: Dave Hansen Cc: Eric Snowberg Cc: Hans de Goede Cc: Joe Perches Cc: Jon Hunter Cc: Julien Thierry Cc: Linus Torvalds Cc: Marc Zyngier Cc: Matt Fleming Cc: Nathan Chancellor Cc: Peter Zijlstra Cc: Sedat Dilek Cc: YiFei Zhu Cc: linux-...@vger.kernel.org Link: http://lkml.kernel.org/r/20181129171230.18699-5-ard.biesheu...@linaro.org Signed-off-by: Ingo Molnar --- arch/x86/include/asm/pgtable_types.h | 8 ++-- arch/x86/mm/pageattr.c | 40 ++-- 2 files changed, 44 insertions(+), 4 deletions(-) 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, unsigned int *level); extern pmd_t *lookup_pmd_address(unsigned long address); extern phys_addr_t slow_virt_to_phys(void *__address); -extern int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, - unsigned numpages, unsigned long page_flags); +extern int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, + unsigned long address, + unsigned numpages, + unsigned long page_flags); +extern int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address, + unsigned long numpages); #endif /* !__ASSEMBLY__ */ #endif /* _ASM_X86_PGTABLE_DEFS_H */ diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index db7a10082238..bac35001d896 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -2338,8 +2338,8 @@ bool kernel_page_present(struct page *page) #endif /* CONFIG_DEBUG_PAGEALLOC */ -int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, - unsigned numpages, unsigned long page_flags) +int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, + unsigned numpages, unsigned long page_flags) { int retval = -EINVAL; @@ -2353,6 +2353,8 @@ int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, .flags = 0, }; + WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP"); + if (!(__supported_pte_mask & _PAGE_NX)) goto out; @@ -2374,6 +2376,40 @@ out: return retval; } +/* + * __flush_tlb_all() flushes mappings only on current CPU and hence this + * function shouldn't be used in an SMP environment. Presently, it's used only + * during boot (way before smp_init()) by EFI subsystem and hence is ok. + */ +int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address, +unsigned long numpages) +{ + int retval; + + /* +* The typical sequence for unmapping is to find a pte through +* lookup_address_in_pgd() (ideally, it should never return NULL because +* the address is already mapped) and change it's protections. As pfn is +