Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * linux/arch/arm/kernel/smp_tlb.c 4 : : * 5 : : * Copyright (C) 2002 ARM Limited, All Rights Reserved. 6 : : */ 7 : : #include <linux/preempt.h> 8 : : #include <linux/smp.h> 9 : : #include <linux/uaccess.h> 10 : : 11 : : #include <asm/smp_plat.h> 12 : : #include <asm/tlbflush.h> 13 : : #include <asm/mmu_context.h> 14 : : 15 : : /**********************************************************************/ 16 : : 17 : : /* 18 : : * TLB operations 19 : : */ 20 : : struct tlb_args { 21 : : struct vm_area_struct *ta_vma; 22 : : unsigned long ta_start; 23 : : unsigned long ta_end; 24 : : }; 25 : : 26 : 0 : static inline void ipi_flush_tlb_all(void *ignored) 27 : : { 28 : : local_flush_tlb_all(); 29 : 0 : } 30 : : 31 : 0 : static inline void ipi_flush_tlb_mm(void *arg) 32 : : { 33 : : struct mm_struct *mm = (struct mm_struct *)arg; 34 : : 35 : : local_flush_tlb_mm(mm); 36 : 0 : } 37 : : 38 : 0 : static inline void ipi_flush_tlb_page(void *arg) 39 : : { 40 : : struct tlb_args *ta = (struct tlb_args *)arg; 41 : : unsigned int __ua_flags = uaccess_save_and_enable(); 42 : : 43 : 0 : local_flush_tlb_page(ta->ta_vma, ta->ta_start); 44 : : 45 : : uaccess_restore(__ua_flags); 46 : 0 : } 47 : : 48 : 0 : static inline void ipi_flush_tlb_kernel_page(void *arg) 49 : : { 50 : : struct tlb_args *ta = (struct tlb_args *)arg; 51 : : 52 : 0 : local_flush_tlb_kernel_page(ta->ta_start); 53 : 0 : } 54 : : 55 : 0 : static inline void ipi_flush_tlb_range(void *arg) 56 : : { 57 : : struct tlb_args *ta = (struct tlb_args *)arg; 58 : : unsigned int __ua_flags = uaccess_save_and_enable(); 59 : : 60 : 0 : local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); 61 : : 62 : : uaccess_restore(__ua_flags); 63 : 0 : } 64 : : 65 : 0 : static inline void ipi_flush_tlb_kernel_range(void *arg) 66 : : { 67 : : struct tlb_args *ta = (struct tlb_args *)arg; 68 : : 69 : 0 : local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); 70 : 0 : } 71 : : 72 : 0 : static inline void ipi_flush_bp_all(void *ignored) 73 : : { 74 : : local_flush_bp_all(); 75 : 0 : } 76 : : 77 : : #ifdef CONFIG_ARM_ERRATA_798181 78 : : bool (*erratum_a15_798181_handler)(void); 79 : : 80 : : static bool erratum_a15_798181_partial(void) 81 : : { 82 : : asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (0)); 83 : : dsb(ish); 84 : : return false; 85 : : } 86 : : 87 : : static bool erratum_a15_798181_broadcast(void) 88 : : { 89 : : asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (0)); 90 : : dsb(ish); 91 : : return true; 92 : : } 93 : : 94 : : void erratum_a15_798181_init(void) 95 : : { 96 : : unsigned int midr = read_cpuid_id(); 97 : : unsigned int revidr = read_cpuid(CPUID_REVIDR); 98 : : 99 : : /* Brahma-B15 r0p0..r0p2 affected 100 : : * Cortex-A15 r0p0..r3p3 w/o ECO fix affected 101 : : * Fixes applied to A15 with respect to the revision and revidr are: 102 : : * 103 : : * r0p0-r2p1: No fixes applied 104 : : * r2p2,r2p3: 105 : : * REVIDR[4]: 798181 Moving a virtual page that is being accessed 106 : : * by an active process can lead to unexpected behavior 107 : : * REVIDR[9]: Not defined 108 : : * r2p4,r3p0,r3p1,r3p2: 109 : : * REVIDR[4]: 798181 Moving a virtual page that is being accessed 110 : : * by an active process can lead to unexpected behavior 111 : : * REVIDR[9]: 798181 Moving a virtual page that is being accessed 112 : : * by an active process can lead to unexpected behavior 113 : : * - This is an update to a previously released ECO. 114 : : * r3p3: 115 : : * REVIDR[4]: Reserved 116 : : * REVIDR[9]: 798181 Moving a virtual page that is being accessed 117 : : * by an active process can lead to unexpected behavior 118 : : * - This is an update to a previously released ECO. 119 : : * 120 : : * Handling: 121 : : * REVIDR[9] set -> No WA 122 : : * REVIDR[4] set, REVIDR[9] cleared -> Partial WA 123 : : * Both cleared -> Full WA 124 : : */ 125 : : if ((midr & 0xff0ffff0) == 0x420f00f0 && midr <= 0x420f00f2) { 126 : : erratum_a15_798181_handler = erratum_a15_798181_broadcast; 127 : : } else if ((midr & 0xff0ffff0) == 0x410fc0f0 && midr < 0x412fc0f2) { 128 : : erratum_a15_798181_handler = erratum_a15_798181_broadcast; 129 : : } else if ((midr & 0xff0ffff0) == 0x410fc0f0 && midr < 0x412fc0f4) { 130 : : if (revidr & 0x10) 131 : : erratum_a15_798181_handler = 132 : : erratum_a15_798181_partial; 133 : : else 134 : : erratum_a15_798181_handler = 135 : : erratum_a15_798181_broadcast; 136 : : } else if ((midr & 0xff0ffff0) == 0x410fc0f0 && midr < 0x413fc0f3) { 137 : : if ((revidr & 0x210) == 0) 138 : : erratum_a15_798181_handler = 139 : : erratum_a15_798181_broadcast; 140 : : else if (revidr & 0x10) 141 : : erratum_a15_798181_handler = 142 : : erratum_a15_798181_partial; 143 : : } else if ((midr & 0xff0ffff0) == 0x410fc0f0 && midr < 0x414fc0f0) { 144 : : if ((revidr & 0x200) == 0) 145 : : erratum_a15_798181_handler = 146 : : erratum_a15_798181_partial; 147 : : } 148 : : } 149 : : #endif 150 : : 151 : : static void ipi_flush_tlb_a15_erratum(void *arg) 152 : : { 153 : : dmb(); 154 : : } 155 : : 156 : : static void broadcast_tlb_a15_erratum(void) 157 : : { 158 : : if (!erratum_a15_798181()) 159 : : return; 160 : : 161 : : smp_call_function(ipi_flush_tlb_a15_erratum, NULL, 1); 162 : : } 163 : : 164 : : static void broadcast_tlb_mm_a15_erratum(struct mm_struct *mm) 165 : : { 166 : : int this_cpu; 167 : : cpumask_t mask = { CPU_BITS_NONE }; 168 : : 169 : : if (!erratum_a15_798181()) 170 : : return; 171 : : 172 : : this_cpu = get_cpu(); 173 : : a15_erratum_get_cpumask(this_cpu, mm, &mask); 174 : : smp_call_function_many(&mask, ipi_flush_tlb_a15_erratum, NULL, 1); 175 : : put_cpu(); 176 : : } 177 : : 178 : 0 : void flush_tlb_all(void) 179 : : { 180 : 0 : if (tlb_ops_need_broadcast()) 181 : 0 : on_each_cpu(ipi_flush_tlb_all, NULL, 1); 182 : : else 183 : : __flush_tlb_all(); 184 : : broadcast_tlb_a15_erratum(); 185 : 0 : } 186 : : 187 : 3 : void flush_tlb_mm(struct mm_struct *mm) 188 : : { 189 : 3 : if (tlb_ops_need_broadcast()) 190 : 0 : on_each_cpu_mask(mm_cpumask(mm), ipi_flush_tlb_mm, mm, 1); 191 : : else 192 : : __flush_tlb_mm(mm); 193 : : broadcast_tlb_mm_a15_erratum(mm); 194 : 3 : } 195 : : 196 : 3 : void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) 197 : : { 198 : 3 : if (tlb_ops_need_broadcast()) { 199 : : struct tlb_args ta; 200 : 0 : ta.ta_vma = vma; 201 : 0 : ta.ta_start = uaddr; 202 : 0 : on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page, 203 : : &ta, 1); 204 : : } else 205 : 3 : __flush_tlb_page(vma, uaddr); 206 : : broadcast_tlb_mm_a15_erratum(vma->vm_mm); 207 : 3 : } 208 : : 209 : 0 : void flush_tlb_kernel_page(unsigned long kaddr) 210 : : { 211 : 0 : if (tlb_ops_need_broadcast()) { 212 : : struct tlb_args ta; 213 : 0 : ta.ta_start = kaddr; 214 : 0 : on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); 215 : : } else 216 : : __flush_tlb_kernel_page(kaddr); 217 : : broadcast_tlb_a15_erratum(); 218 : 0 : } 219 : : 220 : 3 : void flush_tlb_range(struct vm_area_struct *vma, 221 : : unsigned long start, unsigned long end) 222 : : { 223 : 3 : if (tlb_ops_need_broadcast()) { 224 : : struct tlb_args ta; 225 : 0 : ta.ta_vma = vma; 226 : 0 : ta.ta_start = start; 227 : 0 : ta.ta_end = end; 228 : 0 : on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range, 229 : : &ta, 1); 230 : : } else 231 : 3 : local_flush_tlb_range(vma, start, end); 232 : : broadcast_tlb_mm_a15_erratum(vma->vm_mm); 233 : 3 : } 234 : : 235 : 3 : void flush_tlb_kernel_range(unsigned long start, unsigned long end) 236 : : { 237 : 3 : if (tlb_ops_need_broadcast()) { 238 : : struct tlb_args ta; 239 : 0 : ta.ta_start = start; 240 : 0 : ta.ta_end = end; 241 : 0 : on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); 242 : : } else 243 : 3 : local_flush_tlb_kernel_range(start, end); 244 : : broadcast_tlb_a15_erratum(); 245 : 3 : } 246 : : 247 : 0 : void flush_bp_all(void) 248 : : { 249 : 0 : if (tlb_ops_need_broadcast()) 250 : 0 : on_each_cpu(ipi_flush_bp_all, NULL, 1); 251 : : else 252 : : __flush_bp_all(); 253 : 0 : }