ARM: spectre-v2: per-CPU vtables to work around big.Little systems

In big.Little systems, some CPUs require the Spectre workarounds in
paths such as the context switch, but other CPUs do not. In order
to handle these differences, we need per-CPU vtables.

We are unable to use the kernel's per-CPU variables to support this
as per-CPU is not initialised at times when we need access to the
vtables, so we have to use an array indexed by logical CPU number.

We use an array-of-pointers to avoid having function pointers in
the kernel's read/write .data section.

Reviewed-by: Julien Thierry <julien.thierry@arm.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

Changed files
+61 -15
arch
arm
include
kernel
mm
+23
arch/arm/include/asm/proc-fns.h
··· 104 104 #else 105 105 106 106 extern struct processor processor; 107 + #if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR) 108 + #include <linux/smp.h> 109 + /* 110 + * This can't be a per-cpu variable because we need to access it before 111 + * per-cpu has been initialised. We have a couple of functions that are 112 + * called in a pre-emptible context, and so can't use smp_processor_id() 113 + * there, hence PROC_TABLE(). We insist in init_proc_vtable() that the 114 + * function pointers for these are identical across all CPUs. 115 + */ 116 + extern struct processor *cpu_vtable[]; 117 + #define PROC_VTABLE(f) cpu_vtable[smp_processor_id()]->f 118 + #define PROC_TABLE(f) cpu_vtable[0]->f 119 + static inline void init_proc_vtable(const struct processor *p) 120 + { 121 + unsigned int cpu = smp_processor_id(); 122 + *cpu_vtable[cpu] = *p; 123 + WARN_ON_ONCE(cpu_vtable[cpu]->dcache_clean_area != 124 + cpu_vtable[0]->dcache_clean_area); 125 + WARN_ON_ONCE(cpu_vtable[cpu]->set_pte_ext != 126 + cpu_vtable[0]->set_pte_ext); 127 + } 128 + #else 107 129 #define PROC_VTABLE(f) processor.f 108 130 #define PROC_TABLE(f) processor.f 109 131 static inline void init_proc_vtable(const struct processor *p) 110 132 { 111 133 processor = *p; 112 134 } 135 + #endif 113 136 114 137 #define cpu_proc_init PROC_VTABLE(_proc_init) 115 138 #define cpu_check_bugs PROC_VTABLE(check_bugs)
+5
arch/arm/kernel/setup.c
··· 115 115 116 116 #ifdef MULTI_CPU 117 117 struct processor processor __ro_after_init; 118 + #if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR) 119 + struct processor *cpu_vtable[NR_CPUS] = { 120 + [0] = &processor, 121 + }; 122 + #endif 118 123 #endif 119 124 #ifdef MULTI_TLB 120 125 struct cpu_tlb_fns cpu_tlb __ro_after_init;
+31
arch/arm/kernel/smp.c
··· 42 42 #include <asm/mmu_context.h> 43 43 #include <asm/pgtable.h> 44 44 #include <asm/pgalloc.h> 45 + #include <asm/procinfo.h> 45 46 #include <asm/processor.h> 46 47 #include <asm/sections.h> 47 48 #include <asm/tlbflush.h> ··· 103 102 #endif 104 103 } 105 104 105 + #if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR) 106 + static int secondary_biglittle_prepare(unsigned int cpu) 107 + { 108 + if (!cpu_vtable[cpu]) 109 + cpu_vtable[cpu] = kzalloc(sizeof(*cpu_vtable[cpu]), GFP_KERNEL); 110 + 111 + return cpu_vtable[cpu] ? 0 : -ENOMEM; 112 + } 113 + 114 + static void secondary_biglittle_init(void) 115 + { 116 + init_proc_vtable(lookup_processor(read_cpuid_id())->proc); 117 + } 118 + #else 119 + static int secondary_biglittle_prepare(unsigned int cpu) 120 + { 121 + return 0; 122 + } 123 + 124 + static void secondary_biglittle_init(void) 125 + { 126 + } 127 + #endif 128 + 106 129 int __cpu_up(unsigned int cpu, struct task_struct *idle) 107 130 { 108 131 int ret; 109 132 110 133 if (!smp_ops.smp_boot_secondary) 111 134 return -ENOSYS; 135 + 136 + ret = secondary_biglittle_prepare(cpu); 137 + if (ret) 138 + return ret; 112 139 113 140 /* 114 141 * We need to tell the secondary core where to find ··· 388 359 { 389 360 struct mm_struct *mm = &init_mm; 390 361 unsigned int cpu; 362 + 363 + secondary_biglittle_init(); 391 364 392 365 /* 393 366 * The identity mapping is uncached (strongly ordered), so
+2 -15
arch/arm/mm/proc-v7-bugs.c
··· 52 52 case ARM_CPU_PART_CORTEX_A17: 53 53 case ARM_CPU_PART_CORTEX_A73: 54 54 case ARM_CPU_PART_CORTEX_A75: 55 - if (processor.switch_mm != cpu_v7_bpiall_switch_mm) 56 - goto bl_error; 57 55 per_cpu(harden_branch_predictor_fn, cpu) = 58 56 harden_branch_predictor_bpiall; 59 57 spectre_v2_method = "BPIALL"; ··· 59 61 60 62 case ARM_CPU_PART_CORTEX_A15: 61 63 case ARM_CPU_PART_BRAHMA_B15: 62 - if (processor.switch_mm != cpu_v7_iciallu_switch_mm) 63 - goto bl_error; 64 64 per_cpu(harden_branch_predictor_fn, cpu) = 65 65 harden_branch_predictor_iciallu; 66 66 spectre_v2_method = "ICIALLU"; ··· 84 88 ARM_SMCCC_ARCH_WORKAROUND_1, &res); 85 89 if ((int)res.a0 != 0) 86 90 break; 87 - if (processor.switch_mm != cpu_v7_hvc_switch_mm && cpu) 88 - goto bl_error; 89 91 per_cpu(harden_branch_predictor_fn, cpu) = 90 92 call_hvc_arch_workaround_1; 91 - processor.switch_mm = cpu_v7_hvc_switch_mm; 93 + cpu_do_switch_mm = cpu_v7_hvc_switch_mm; 92 94 spectre_v2_method = "hypervisor"; 93 95 break; 94 96 ··· 95 101 ARM_SMCCC_ARCH_WORKAROUND_1, &res); 96 102 if ((int)res.a0 != 0) 97 103 break; 98 - if (processor.switch_mm != cpu_v7_smc_switch_mm && cpu) 99 - goto bl_error; 100 104 per_cpu(harden_branch_predictor_fn, cpu) = 101 105 call_smc_arch_workaround_1; 102 - processor.switch_mm = cpu_v7_smc_switch_mm; 106 + cpu_do_switch_mm = cpu_v7_smc_switch_mm; 103 107 spectre_v2_method = "firmware"; 104 108 break; 105 109 ··· 111 119 if (spectre_v2_method) 112 120 pr_info("CPU%u: Spectre v2: using %s workaround\n", 113 121 smp_processor_id(), spectre_v2_method); 114 - return; 115 - 116 - bl_error: 117 - pr_err("CPU%u: Spectre v2: incorrect context switching function, system vulnerable\n", 118 - cpu); 119 122 } 120 123 #else 121 124 static void cpu_v7_spectre_init(void)