aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSai Praneeth <sai.praneeth.prakhya@intel.com>2018-03-12 05:43:55 -0400
committerIngo Molnar <mingo@kernel.org>2018-03-12 06:05:05 -0400
commit03781e40890c18bdea40092355b61431d0073c1d (patch)
tree4a7c7b0f467cfb1cd6d4d94384350d3f66ee32d4
parent3ede3417f8d59fff5c07339b310b343468c81b07 (diff)
x86/efi: Use efi_switch_mm() rather than manually twiddling with %cr3
Use helper function efi_switch_mm() to switch to/from efi_mm when invoking any UEFI runtime services. Likewise, we need to switch back to previous mm (mm context stolen by efi_mm) after the above calls return successfully. We can use efi_switch_mm() helper function only with x86_64 kernel and "efi=old_map" disabled because, x86_32 and efi=old_map do not use efi_pgd, rather they use swapper_pg_dir. Tested-by: Bhupesh Sharma <bhsharma@redhat.com> [ardb: add #include of sched/task.h for task_lock/_unlock] Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Matt Fleming <matt@codeblueprint.co.uk> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Lee, Chun-Yi <jlee@suse.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Michael S. Tsirkin <mst@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ravi Shankar <ravi.v.shankar@intel.com> Cc: Ricardo Neri <ricardo.neri@intel.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tony Luck <tony.luck@intel.com> Cc: linux-efi@vger.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/efi.h25
-rw-r--r--arch/x86/platform/efi/efi_64.c41
-rw-r--r--arch/x86/platform/efi/efi_thunk_64.S2
3 files changed, 33 insertions, 35 deletions
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index c62443fa7d0a..cec5fae23eb3 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -70,14 +70,13 @@ extern asmlinkage u64 efi_call(void *fp, ...);
70#define efi_call_phys(f, args...) efi_call((f), args) 70#define efi_call_phys(f, args...) efi_call((f), args)
71 71
72/* 72/*
73 * Scratch space used for switching the pagetable in the EFI stub 73 * struct efi_scratch - Scratch space used while switching to/from efi_mm
74 * @phys_stack: stack used during EFI Mixed Mode
75 * @prev_mm: store/restore stolen mm_struct while switching to/from efi_mm
74 */ 76 */
75struct efi_scratch { 77struct efi_scratch {
76 u64 r15; 78 u64 phys_stack;
77 u64 prev_cr3; 79 struct mm_struct *prev_mm;
78 pgd_t *efi_pgt;
79 bool use_pgd;
80 u64 phys_stack;
81} __packed; 80} __packed;
82 81
83#define arch_efi_call_virt_setup() \ 82#define arch_efi_call_virt_setup() \
@@ -87,11 +86,8 @@ struct efi_scratch {
87 __kernel_fpu_begin(); \ 86 __kernel_fpu_begin(); \
88 firmware_restrict_branch_speculation_start(); \ 87 firmware_restrict_branch_speculation_start(); \
89 \ 88 \
90 if (efi_scratch.use_pgd) { \ 89 if (!efi_enabled(EFI_OLD_MEMMAP)) \
91 efi_scratch.prev_cr3 = __read_cr3(); \ 90 efi_switch_mm(&efi_mm); \
92 write_cr3((unsigned long)efi_scratch.efi_pgt); \
93 __flush_tlb_all(); \
94 } \
95}) 91})
96 92
97#define arch_efi_call_virt(p, f, args...) \ 93#define arch_efi_call_virt(p, f, args...) \
@@ -99,10 +95,8 @@ struct efi_scratch {
99 95
100#define arch_efi_call_virt_teardown() \ 96#define arch_efi_call_virt_teardown() \
101({ \ 97({ \
102 if (efi_scratch.use_pgd) { \ 98 if (!efi_enabled(EFI_OLD_MEMMAP)) \
103 write_cr3(efi_scratch.prev_cr3); \ 99 efi_switch_mm(efi_scratch.prev_mm); \
104 __flush_tlb_all(); \
105 } \
106 \ 100 \
107 firmware_restrict_branch_speculation_end(); \ 101 firmware_restrict_branch_speculation_end(); \
108 __kernel_fpu_end(); \ 102 __kernel_fpu_end(); \
@@ -145,6 +139,7 @@ extern void __init efi_dump_pagetable(void);
145extern void __init efi_apply_memmap_quirks(void); 139extern void __init efi_apply_memmap_quirks(void);
146extern int __init efi_reuse_config(u64 tables, int nr_tables); 140extern int __init efi_reuse_config(u64 tables, int nr_tables);
147extern void efi_delete_dummy_variable(void); 141extern void efi_delete_dummy_variable(void);
142extern void efi_switch_mm(struct mm_struct *mm);
148 143
149struct efi_setup_data { 144struct efi_setup_data {
150 u64 fw_vendor; 145 u64 fw_vendor;
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 29b267b8cb63..4f8a9c963fbb 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -34,6 +34,7 @@
34#include <linux/slab.h> 34#include <linux/slab.h>
35#include <linux/ucs2_string.h> 35#include <linux/ucs2_string.h>
36#include <linux/mem_encrypt.h> 36#include <linux/mem_encrypt.h>
37#include <linux/sched/task.h>
37 38
38#include <asm/setup.h> 39#include <asm/setup.h>
39#include <asm/page.h> 40#include <asm/page.h>
@@ -82,9 +83,8 @@ pgd_t * __init efi_call_phys_prolog(void)
82 int n_pgds, i, j; 83 int n_pgds, i, j;
83 84
84 if (!efi_enabled(EFI_OLD_MEMMAP)) { 85 if (!efi_enabled(EFI_OLD_MEMMAP)) {
85 save_pgd = (pgd_t *)__read_cr3(); 86 efi_switch_mm(&efi_mm);
86 write_cr3((unsigned long)efi_scratch.efi_pgt); 87 return NULL;
87 goto out;
88 } 88 }
89 89
90 early_code_mapping_set_exec(1); 90 early_code_mapping_set_exec(1);
@@ -156,8 +156,7 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
156 pud_t *pud; 156 pud_t *pud;
157 157
158 if (!efi_enabled(EFI_OLD_MEMMAP)) { 158 if (!efi_enabled(EFI_OLD_MEMMAP)) {
159 write_cr3((unsigned long)save_pgd); 159 efi_switch_mm(efi_scratch.prev_mm);
160 __flush_tlb_all();
161 return; 160 return;
162 } 161 }
163 162
@@ -348,13 +347,6 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
348 return 0; 347 return 0;
349 348
350 /* 349 /*
351 * Since the PGD is encrypted, set the encryption mask so that when
352 * this value is loaded into cr3 the PGD will be decrypted during
353 * the pagetable walk.
354 */
355 efi_scratch.efi_pgt = (pgd_t *)__sme_pa(pgd);
356
357 /*
358 * It can happen that the physical address of new_memmap lands in memory 350 * It can happen that the physical address of new_memmap lands in memory
359 * which is not mapped in the EFI page table. Therefore we need to go 351 * which is not mapped in the EFI page table. Therefore we need to go
360 * and ident-map those pages containing the map before calling 352 * and ident-map those pages containing the map before calling
@@ -367,8 +359,6 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
367 return 1; 359 return 1;
368 } 360 }
369 361
370 efi_scratch.use_pgd = true;
371
372 /* 362 /*
373 * Certain firmware versions are way too sentimential and still believe 363 * Certain firmware versions are way too sentimential and still believe
374 * they are exclusive and unquestionable owners of the first physical page, 364 * they are exclusive and unquestionable owners of the first physical page,
@@ -627,6 +617,22 @@ void __init efi_dump_pagetable(void)
627#endif 617#endif
628} 618}
629 619
620/*
621 * Makes the calling thread switch to/from efi_mm context. Can be used
622 * for SetVirtualAddressMap() i.e. current->active_mm == init_mm as well
623 * as during efi runtime calls i.e current->active_mm == current_mm.
624 * We are not mm_dropping()/mm_grabbing() any mm, because we are not
625 * losing/creating any references.
626 */
627void efi_switch_mm(struct mm_struct *mm)
628{
629 task_lock(current);
630 efi_scratch.prev_mm = current->active_mm;
631 current->active_mm = mm;
632 switch_mm(efi_scratch.prev_mm, mm, NULL);
633 task_unlock(current);
634}
635
630#ifdef CONFIG_EFI_MIXED 636#ifdef CONFIG_EFI_MIXED
631extern efi_status_t efi64_thunk(u32, ...); 637extern efi_status_t efi64_thunk(u32, ...);
632 638
@@ -680,16 +686,13 @@ efi_status_t efi_thunk_set_virtual_address_map(
680 efi_sync_low_kernel_mappings(); 686 efi_sync_low_kernel_mappings();
681 local_irq_save(flags); 687 local_irq_save(flags);
682 688
683 efi_scratch.prev_cr3 = __read_cr3(); 689 efi_switch_mm(&efi_mm);
684 write_cr3((unsigned long)efi_scratch.efi_pgt);
685 __flush_tlb_all();
686 690
687 func = (u32)(unsigned long)phys_set_virtual_address_map; 691 func = (u32)(unsigned long)phys_set_virtual_address_map;
688 status = efi64_thunk(func, memory_map_size, descriptor_size, 692 status = efi64_thunk(func, memory_map_size, descriptor_size,
689 descriptor_version, virtual_map); 693 descriptor_version, virtual_map);
690 694
691 write_cr3(efi_scratch.prev_cr3); 695 efi_switch_mm(efi_scratch.prev_mm);
692 __flush_tlb_all();
693 local_irq_restore(flags); 696 local_irq_restore(flags);
694 697
695 return status; 698 return status;
diff --git a/arch/x86/platform/efi/efi_thunk_64.S b/arch/x86/platform/efi/efi_thunk_64.S
index 189b218da87c..46c58b08739c 100644
--- a/arch/x86/platform/efi/efi_thunk_64.S
+++ b/arch/x86/platform/efi/efi_thunk_64.S
@@ -33,7 +33,7 @@ ENTRY(efi64_thunk)
33 * Switch to 1:1 mapped 32-bit stack pointer. 33 * Switch to 1:1 mapped 32-bit stack pointer.
34 */ 34 */
35 movq %rsp, efi_saved_sp(%rip) 35 movq %rsp, efi_saved_sp(%rip)
36 movq efi_scratch+25(%rip), %rsp 36 movq efi_scratch(%rip), %rsp
37 37
38 /* 38 /*
39 * Calculate the physical address of the kernel text. 39 * Calculate the physical address of the kernel text.