diff options
author | Sai Praneeth <sai.praneeth.prakhya@intel.com> | 2018-03-12 05:43:55 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-03-12 06:05:05 -0400 |
commit | 03781e40890c18bdea40092355b61431d0073c1d (patch) | |
tree | 4a7c7b0f467cfb1cd6d4d94384350d3f66ee32d4 | |
parent | 3ede3417f8d59fff5c07339b310b343468c81b07 (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.h | 25 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi_64.c | 41 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi_thunk_64.S | 2 |
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 | */ |
75 | struct efi_scratch { | 77 | struct 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); | |||
145 | extern void __init efi_apply_memmap_quirks(void); | 139 | extern void __init efi_apply_memmap_quirks(void); |
146 | extern int __init efi_reuse_config(u64 tables, int nr_tables); | 140 | extern int __init efi_reuse_config(u64 tables, int nr_tables); |
147 | 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); | ||
148 | 143 | ||
149 | struct efi_setup_data { | 144 | struct 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 | */ | ||
627 | void 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 |
631 | extern efi_status_t efi64_thunk(u32, ...); | 637 | extern 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. |