Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: alternatives: use tpidr_el2 on VHE hosts

Now that KVM uses tpidr_el2 in the same way as Linux's cpu_offset in
tpidr_el1, merge the two. This saves KVM from save/restoring tpidr_el1
on VHE hosts, and allows future code to blindly access per-cpu variables
without triggering world-switch.

Signed-off-by: James Morse <james.morse@arm.com>
Reviewed-by: Christoffer Dall <cdall@linaro.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

James Morse and committed by
Catalin Marinas
6d99b689 c97e166e

+49 -6
+2
arch/arm64/include/asm/alternative.h
··· 12 12 #include <linux/stddef.h> 13 13 #include <linux/stringify.h> 14 14 15 + extern int alternatives_applied; 16 + 15 17 struct alt_instr { 16 18 s32 orig_offset; /* offset to original instruction */ 17 19 s32 alt_offset; /* offset to replacement instruction */
+8
arch/arm64/include/asm/assembler.h
··· 254 254 #else 255 255 adr_l \dst, \sym 256 256 #endif 257 + alternative_if_not ARM64_HAS_VIRT_HOST_EXTN 257 258 mrs \tmp, tpidr_el1 259 + alternative_else 260 + mrs \tmp, tpidr_el2 261 + alternative_endif 258 262 add \dst, \dst, \tmp 259 263 .endm 260 264 ··· 269 265 */ 270 266 .macro ldr_this_cpu dst, sym, tmp 271 267 adr_l \dst, \sym 268 + alternative_if_not ARM64_HAS_VIRT_HOST_EXTN 272 269 mrs \tmp, tpidr_el1 270 + alternative_else 271 + mrs \tmp, tpidr_el2 272 + alternative_endif 273 273 ldr \dst, [\dst, \tmp] 274 274 .endm 275 275
+9 -2
arch/arm64/include/asm/percpu.h
··· 16 16 #ifndef __ASM_PERCPU_H 17 17 #define __ASM_PERCPU_H 18 18 19 + #include <asm/alternative.h> 19 20 #include <asm/stack_pointer.h> 20 21 21 22 static inline void set_my_cpu_offset(unsigned long off) 22 23 { 23 - asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory"); 24 + asm volatile(ALTERNATIVE("msr tpidr_el1, %0", 25 + "msr tpidr_el2, %0", 26 + ARM64_HAS_VIRT_HOST_EXTN) 27 + :: "r" (off) : "memory"); 24 28 } 25 29 26 30 static inline unsigned long __my_cpu_offset(void) ··· 35 31 * We want to allow caching the value, so avoid using volatile and 36 32 * instead use a fake stack read to hazard against barrier(). 37 33 */ 38 - asm("mrs %0, tpidr_el1" : "=r" (off) : 34 + asm(ALTERNATIVE("mrs %0, tpidr_el1", 35 + "mrs %0, tpidr_el2", 36 + ARM64_HAS_VIRT_HOST_EXTN) 37 + : "=r" (off) : 39 38 "Q" (*(const unsigned long *)current_stack_pointer)); 40 39 41 40 return off;
+5 -4
arch/arm64/kernel/alternative.c
··· 32 32 #define ALT_ORIG_PTR(a) __ALT_PTR(a, orig_offset) 33 33 #define ALT_REPL_PTR(a) __ALT_PTR(a, alt_offset) 34 34 35 + int alternatives_applied; 36 + 35 37 struct alt_region { 36 38 struct alt_instr *begin; 37 39 struct alt_instr *end; ··· 145 143 */ 146 144 static int __apply_alternatives_multi_stop(void *unused) 147 145 { 148 - static int patched = 0; 149 146 struct alt_region region = { 150 147 .begin = (struct alt_instr *)__alt_instructions, 151 148 .end = (struct alt_instr *)__alt_instructions_end, ··· 152 151 153 152 /* We always have a CPU 0 at this point (__init) */ 154 153 if (smp_processor_id()) { 155 - while (!READ_ONCE(patched)) 154 + while (!READ_ONCE(alternatives_applied)) 156 155 cpu_relax(); 157 156 isb(); 158 157 } else { 159 - BUG_ON(patched); 158 + BUG_ON(alternatives_applied); 160 159 __apply_alternatives(&region, true); 161 160 /* Barriers provided by the cache flushing */ 162 - WRITE_ONCE(patched, 1); 161 + WRITE_ONCE(alternatives_applied, 1); 163 162 } 164 163 165 164 return 0;
+17
arch/arm64/kernel/cpufeature.c
··· 886 886 __setup("kpti=", parse_kpti); 887 887 #endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ 888 888 889 + static int cpu_copy_el2regs(void *__unused) 890 + { 891 + /* 892 + * Copy register values that aren't redirected by hardware. 893 + * 894 + * Before code patching, we only set tpidr_el1, all CPUs need to copy 895 + * this value to tpidr_el2 before we patch the code. Once we've done 896 + * that, freshly-onlined CPUs will set tpidr_el2, so we don't need to 897 + * do anything here. 898 + */ 899 + if (!alternatives_applied) 900 + write_sysreg(read_sysreg(tpidr_el1), tpidr_el2); 901 + 902 + return 0; 903 + } 904 + 889 905 static const struct arm64_cpu_capabilities arm64_features[] = { 890 906 { 891 907 .desc = "GIC system register CPU interface", ··· 971 955 .capability = ARM64_HAS_VIRT_HOST_EXTN, 972 956 .def_scope = SCOPE_SYSTEM, 973 957 .matches = runs_at_el2, 958 + .enable = cpu_copy_el2regs, 974 959 }, 975 960 { 976 961 .desc = "32-bit EL0 Support",
+8
arch/arm64/mm/proc.S
··· 70 70 mrs x8, mdscr_el1 71 71 mrs x9, oslsr_el1 72 72 mrs x10, sctlr_el1 73 + alternative_if_not ARM64_HAS_VIRT_HOST_EXTN 73 74 mrs x11, tpidr_el1 75 + alternative_else 76 + mrs x11, tpidr_el2 77 + alternative_endif 74 78 mrs x12, sp_el0 75 79 stp x2, x3, [x0] 76 80 stp x4, xzr, [x0, #16] ··· 120 116 msr mdscr_el1, x10 121 117 122 118 msr sctlr_el1, x12 119 + alternative_if_not ARM64_HAS_VIRT_HOST_EXTN 123 120 msr tpidr_el1, x13 121 + alternative_else 122 + msr tpidr_el2, x13 123 + alternative_endif 124 124 msr sp_el0, x14 125 125 /* 126 126 * Restore oslsr_el1 by writing oslar_el1