Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
f1f3347d | 2 | /* |
288ff7de | 3 | * TLB Management (flush/create/diagnostics) for MMUv3 and MMUv4 |
f1f3347d VG |
4 | * |
5 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | |
6 | * | |
f1f3347d VG |
7 | */ |
8 | ||
9 | #include <linux/module.h> | |
483e9bcb | 10 | #include <linux/bug.h> |
589ee628 IM |
11 | #include <linux/mm_types.h> |
12 | ||
f1f3347d | 13 | #include <asm/arcregs.h> |
d79e678d | 14 | #include <asm/setup.h> |
f1f3347d | 15 | #include <asm/mmu_context.h> |
da1677b0 | 16 | #include <asm/mmu.h> |
f1f3347d VG |
17 | |
18 | /* A copy of the ASID from the PID reg is kept in asid_cache */ | |
63eca94c | 19 | DEFINE_PER_CPU(unsigned int, asid_cache) = MM_CTXT_FIRST_CYCLE; |
cc562d2e | 20 | |
72d861f2 VG |
21 | static struct cpuinfo_arc_mmu { |
22 | unsigned int ver, pg_sz_k, s_pg_sz_m, pae, sets, ways; | |
23 | } mmuinfo; | |
b5ddb6d5 | 24 | |
d79e678d VG |
25 | /* |
26 | * Utility Routine to erase a J-TLB entry | |
483e9bcb | 27 | * Caller needs to setup Index Reg (manually or via getIndex) |
d79e678d | 28 | */ |
483e9bcb | 29 | static inline void __tlb_entry_erase(void) |
d79e678d VG |
30 | { |
31 | write_aux_reg(ARC_REG_TLBPD1, 0); | |
5a364c2a VG |
32 | |
33 | if (is_pae40_enabled()) | |
34 | write_aux_reg(ARC_REG_TLBPD1HI, 0); | |
35 | ||
d79e678d VG |
36 | write_aux_reg(ARC_REG_TLBPD0, 0); |
37 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); | |
38 | } | |
39 | ||
1355ea2e VG |
40 | static void utlb_invalidate(void) |
41 | { | |
1355ea2e | 42 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBIVUTLB); |
1355ea2e VG |
43 | } |
44 | ||
288ff7de | 45 | #ifdef CONFIG_ARC_MMU_V3 |
d7a512bf | 46 | |
483e9bcb | 47 | static inline unsigned int tlb_entry_lkup(unsigned long vaddr_n_asid) |
d79e678d VG |
48 | { |
49 | unsigned int idx; | |
50 | ||
d79e678d | 51 | write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid); |
483e9bcb | 52 | |
d79e678d VG |
53 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBProbe); |
54 | idx = read_aux_reg(ARC_REG_TLBINDEX); | |
55 | ||
483e9bcb VG |
56 | return idx; |
57 | } | |
58 | ||
59 | static void tlb_entry_erase(unsigned int vaddr_n_asid) | |
60 | { | |
61 | unsigned int idx; | |
62 | ||
63 | /* Locate the TLB entry for this vaddr + ASID */ | |
64 | idx = tlb_entry_lkup(vaddr_n_asid); | |
65 | ||
d79e678d VG |
66 | /* No error means entry found, zero it out */ |
67 | if (likely(!(idx & TLB_LKUP_ERR))) { | |
68 | __tlb_entry_erase(); | |
483e9bcb | 69 | } else { |
d79e678d | 70 | /* Duplicate entry error */ |
483e9bcb VG |
71 | WARN(idx == TLB_DUP_ERR, "Probe returned Dup PD for %x\n", |
72 | vaddr_n_asid); | |
d79e678d VG |
73 | } |
74 | } | |
75 | ||
366440ee | 76 | static void tlb_entry_insert(unsigned int pd0, phys_addr_t pd1) |
483e9bcb VG |
77 | { |
78 | unsigned int idx; | |
79 | ||
80 | /* | |
81 | * First verify if entry for this vaddr+ASID already exists | |
82 | * This also sets up PD0 (vaddr, ASID..) for final commit | |
83 | */ | |
84 | idx = tlb_entry_lkup(pd0); | |
85 | ||
86 | /* | |
87 | * If Not already present get a free slot from MMU. | |
88 | * Otherwise, Probe would have located the entry and set INDEX Reg | |
89 | * with existing location. This will cause Write CMD to over-write | |
90 | * existing entry with new PD0 and PD1 | |
91 | */ | |
92 | if (likely(idx & TLB_LKUP_ERR)) | |
93 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBGetIndex); | |
94 | ||
95 | /* setup the other half of TLB entry (pfn, rwx..) */ | |
96 | write_aux_reg(ARC_REG_TLBPD1, pd1); | |
97 | ||
98 | /* | |
99 | * Commit the Entry to MMU | |
7423cc0c | 100 | * It doesn't sound safe to use the TLBWriteNI cmd here |
483e9bcb VG |
101 | * which doesn't flush uTLBs. I'd rather be safe than sorry. |
102 | */ | |
103 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); | |
104 | } | |
105 | ||
288ff7de | 106 | #else /* MMUv4 */ |
d7a512bf | 107 | |
d7a512bf VG |
108 | static void tlb_entry_erase(unsigned int vaddr_n_asid) |
109 | { | |
110 | write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid | _PAGE_PRESENT); | |
111 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBDeleteEntry); | |
112 | } | |
113 | ||
366440ee | 114 | static void tlb_entry_insert(unsigned int pd0, phys_addr_t pd1) |
d7a512bf VG |
115 | { |
116 | write_aux_reg(ARC_REG_TLBPD0, pd0); | |
5a364c2a | 117 | |
366440ee VG |
118 | if (!is_pae40_enabled()) { |
119 | write_aux_reg(ARC_REG_TLBPD1, pd1); | |
120 | } else { | |
121 | write_aux_reg(ARC_REG_TLBPD1, pd1 & 0xFFFFFFFF); | |
5a364c2a | 122 | write_aux_reg(ARC_REG_TLBPD1HI, (u64)pd1 >> 32); |
366440ee | 123 | } |
5a364c2a | 124 | |
d7a512bf VG |
125 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBInsertEntry); |
126 | } | |
127 | ||
128 | #endif | |
129 | ||
d79e678d VG |
130 | /* |
131 | * Un-conditionally (without lookup) erase the entire MMU contents | |
132 | */ | |
133 | ||
134 | noinline void local_flush_tlb_all(void) | |
135 | { | |
72d861f2 | 136 | struct cpuinfo_arc_mmu *mmu = &mmuinfo; |
d79e678d VG |
137 | unsigned long flags; |
138 | unsigned int entry; | |
b598e17f | 139 | int num_tlb = mmu->sets * mmu->ways; |
d79e678d VG |
140 | |
141 | local_irq_save(flags); | |
142 | ||
143 | /* Load PD0 and PD1 with template for a Blank Entry */ | |
144 | write_aux_reg(ARC_REG_TLBPD1, 0); | |
5a364c2a VG |
145 | |
146 | if (is_pae40_enabled()) | |
147 | write_aux_reg(ARC_REG_TLBPD1HI, 0); | |
148 | ||
d79e678d VG |
149 | write_aux_reg(ARC_REG_TLBPD0, 0); |
150 | ||
b598e17f | 151 | for (entry = 0; entry < num_tlb; entry++) { |
d79e678d VG |
152 | /* write this entry to the TLB */ |
153 | write_aux_reg(ARC_REG_TLBINDEX, entry); | |
1355ea2e | 154 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBWriteNI); |
d79e678d VG |
155 | } |
156 | ||
fe6c1b86 VG |
157 | if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { |
158 | const int stlb_idx = 0x800; | |
159 | ||
160 | /* Blank sTLB entry */ | |
161 | write_aux_reg(ARC_REG_TLBPD0, _PAGE_HW_SZ); | |
162 | ||
163 | for (entry = stlb_idx; entry < stlb_idx + 16; entry++) { | |
164 | write_aux_reg(ARC_REG_TLBINDEX, entry); | |
1355ea2e | 165 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBWriteNI); |
fe6c1b86 VG |
166 | } |
167 | } | |
168 | ||
d79e678d VG |
169 | utlb_invalidate(); |
170 | ||
171 | local_irq_restore(flags); | |
172 | } | |
173 | ||
174 | /* | |
5f840df5 | 175 | * Flush the entire MM for userland. The fastest way is to move to Next ASID |
d79e678d VG |
176 | */ |
177 | noinline void local_flush_tlb_mm(struct mm_struct *mm) | |
178 | { | |
179 | /* | |
180 | * Small optimisation courtesy IA64 | |
181 | * flush_mm called during fork,exit,munmap etc, multiple times as well. | |
182 | * Only for fork( ) do we need to move parent to a new MMU ctxt, | |
183 | * all other cases are NOPs, hence this check. | |
184 | */ | |
185 | if (atomic_read(&mm->mm_users) == 0) | |
186 | return; | |
187 | ||
188 | /* | |
3daa48d1 VG |
189 | * - Move to a new ASID, but only if the mm is still wired in |
190 | * (Android Binder ended up calling this for vma->mm != tsk->mm, | |
191 | * causing h/w - s/w ASID to get out of sync) | |
192 | * - Also get_new_mmu_context() new implementation allocates a new | |
193 | * ASID only if it is not allocated already - so unallocate first | |
d79e678d | 194 | */ |
3daa48d1 VG |
195 | destroy_context(mm); |
196 | if (current->mm == mm) | |
d79e678d VG |
197 | get_new_mmu_context(mm); |
198 | } | |
199 | ||
200 | /* | |
201 | * Flush a Range of TLB entries for userland. | |
202 | * @start is inclusive, while @end is exclusive | |
203 | * Difference between this and Kernel Range Flush is | |
204 | * -Here the fastest way (if range is too large) is to move to next ASID | |
205 | * without doing any explicit Shootdown | |
5f840df5 | 206 | * -In case of kernel Flush, entry has to be shot down explicitly |
d79e678d VG |
207 | */ |
208 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | |
209 | unsigned long end) | |
210 | { | |
63eca94c | 211 | const unsigned int cpu = smp_processor_id(); |
d79e678d | 212 | unsigned long flags; |
d79e678d VG |
213 | |
214 | /* If range @start to @end is more than 32 TLB entries deep, | |
ebfc2fd8 | 215 | * it's better to move to a new ASID rather than searching for |
d79e678d VG |
216 | * individual entries and then shooting them down |
217 | * | |
218 | * The calc above is rough, doesn't account for unaligned parts, | |
219 | * since this is heuristics based anyways | |
220 | */ | |
221 | if (unlikely((end - start) >= PAGE_SIZE * 32)) { | |
222 | local_flush_tlb_mm(vma->vm_mm); | |
223 | return; | |
224 | } | |
225 | ||
226 | /* | |
227 | * @start moved to page start: this alone suffices for checking | |
228 | * loop end condition below, w/o need for aligning @end to end | |
229 | * e.g. 2000 to 4001 will anyhow loop twice | |
230 | */ | |
231 | start &= PAGE_MASK; | |
232 | ||
233 | local_irq_save(flags); | |
d79e678d | 234 | |
63eca94c | 235 | if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) { |
d79e678d | 236 | while (start < end) { |
63eca94c | 237 | tlb_entry_erase(start | hw_pid(vma->vm_mm, cpu)); |
d79e678d VG |
238 | start += PAGE_SIZE; |
239 | } | |
240 | } | |
241 | ||
d79e678d VG |
242 | local_irq_restore(flags); |
243 | } | |
244 | ||
245 | /* Flush the kernel TLB entries - vmalloc/modules (Global from MMU perspective) | |
246 | * @start, @end interpreted as kvaddr | |
247 | * Interestingly, shared TLB entries can also be flushed using just | |
248 | * @start,@end alone (interpreted as user vaddr), although technically SASID | |
249 | * is also needed. However our smart TLbProbe lookup takes care of that. | |
250 | */ | |
251 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | |
252 | { | |
253 | unsigned long flags; | |
254 | ||
255 | /* exactly same as above, except for TLB entry not taking ASID */ | |
256 | ||
257 | if (unlikely((end - start) >= PAGE_SIZE * 32)) { | |
258 | local_flush_tlb_all(); | |
259 | return; | |
260 | } | |
261 | ||
262 | start &= PAGE_MASK; | |
263 | ||
264 | local_irq_save(flags); | |
265 | while (start < end) { | |
266 | tlb_entry_erase(start); | |
267 | start += PAGE_SIZE; | |
268 | } | |
269 | ||
d79e678d VG |
270 | local_irq_restore(flags); |
271 | } | |
272 | ||
273 | /* | |
274 | * Delete TLB entry in MMU for a given page (??? address) | |
275 | * NOTE One TLB entry contains translation for single PAGE | |
276 | */ | |
277 | ||
278 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | |
279 | { | |
63eca94c | 280 | const unsigned int cpu = smp_processor_id(); |
d79e678d VG |
281 | unsigned long flags; |
282 | ||
283 | /* Note that it is critical that interrupts are DISABLED between | |
284 | * checking the ASID and using it flush the TLB entry | |
285 | */ | |
286 | local_irq_save(flags); | |
287 | ||
63eca94c VG |
288 | if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) { |
289 | tlb_entry_erase((page & PAGE_MASK) | hw_pid(vma->vm_mm, cpu)); | |
d79e678d VG |
290 | } |
291 | ||
292 | local_irq_restore(flags); | |
293 | } | |
cc562d2e | 294 | |
5ea72a90 VG |
295 | #ifdef CONFIG_SMP |
296 | ||
297 | struct tlb_args { | |
298 | struct vm_area_struct *ta_vma; | |
299 | unsigned long ta_start; | |
300 | unsigned long ta_end; | |
301 | }; | |
302 | ||
303 | static inline void ipi_flush_tlb_page(void *arg) | |
304 | { | |
305 | struct tlb_args *ta = arg; | |
306 | ||
307 | local_flush_tlb_page(ta->ta_vma, ta->ta_start); | |
308 | } | |
309 | ||
310 | static inline void ipi_flush_tlb_range(void *arg) | |
311 | { | |
312 | struct tlb_args *ta = arg; | |
313 | ||
314 | local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); | |
315 | } | |
316 | ||
c7119d56 VG |
317 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
318 | static inline void ipi_flush_pmd_tlb_range(void *arg) | |
319 | { | |
320 | struct tlb_args *ta = arg; | |
321 | ||
322 | local_flush_pmd_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); | |
323 | } | |
324 | #endif | |
325 | ||
5ea72a90 VG |
326 | static inline void ipi_flush_tlb_kernel_range(void *arg) |
327 | { | |
328 | struct tlb_args *ta = (struct tlb_args *)arg; | |
329 | ||
330 | local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); | |
331 | } | |
332 | ||
333 | void flush_tlb_all(void) | |
334 | { | |
335 | on_each_cpu((smp_call_func_t)local_flush_tlb_all, NULL, 1); | |
336 | } | |
337 | ||
338 | void flush_tlb_mm(struct mm_struct *mm) | |
339 | { | |
340 | on_each_cpu_mask(mm_cpumask(mm), (smp_call_func_t)local_flush_tlb_mm, | |
341 | mm, 1); | |
342 | } | |
343 | ||
344 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) | |
345 | { | |
346 | struct tlb_args ta = { | |
347 | .ta_vma = vma, | |
348 | .ta_start = uaddr | |
349 | }; | |
350 | ||
351 | on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page, &ta, 1); | |
352 | } | |
353 | ||
354 | void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | |
355 | unsigned long end) | |
356 | { | |
357 | struct tlb_args ta = { | |
358 | .ta_vma = vma, | |
359 | .ta_start = start, | |
360 | .ta_end = end | |
361 | }; | |
362 | ||
363 | on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range, &ta, 1); | |
364 | } | |
365 | ||
c7119d56 VG |
366 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
367 | void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, | |
368 | unsigned long end) | |
369 | { | |
370 | struct tlb_args ta = { | |
371 | .ta_vma = vma, | |
372 | .ta_start = start, | |
373 | .ta_end = end | |
374 | }; | |
375 | ||
376 | on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_pmd_tlb_range, &ta, 1); | |
377 | } | |
378 | #endif | |
379 | ||
5ea72a90 VG |
380 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) |
381 | { | |
382 | struct tlb_args ta = { | |
383 | .ta_start = start, | |
384 | .ta_end = end | |
385 | }; | |
386 | ||
387 | on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); | |
388 | } | |
389 | #endif | |
390 | ||
cc562d2e VG |
391 | /* |
392 | * Routine to create a TLB entry | |
393 | */ | |
4d369680 | 394 | static void create_tlb(struct vm_area_struct *vma, unsigned long vaddr, pte_t *ptep) |
cc562d2e VG |
395 | { |
396 | unsigned long flags; | |
483e9bcb | 397 | unsigned int asid_or_sasid, rwx; |
5a364c2a | 398 | unsigned long pd0; |
366440ee | 399 | phys_addr_t pd1; |
cc562d2e VG |
400 | |
401 | /* | |
402 | * create_tlb() assumes that current->mm == vma->mm, since | |
403 | * -it ASID for TLB entry is fetched from MMU ASID reg (valid for curr) | |
404 | * -completes the lazy write to SASID reg (again valid for curr tsk) | |
405 | * | |
406 | * Removing the assumption involves | |
407 | * -Using vma->mm->context{ASID,SASID}, as opposed to MMU reg. | |
cc562d2e VG |
408 | * -More importantly it makes this handler inconsistent with fast-path |
409 | * TLB Refill handler which always deals with "current" | |
410 | * | |
ebfc2fd8 | 411 | * Let's see the use cases when current->mm != vma->mm and we land here |
cc562d2e VG |
412 | * 1. execve->copy_strings()->__get_user_pages->handle_mm_fault |
413 | * Here VM wants to pre-install a TLB entry for user stack while | |
414 | * current->mm still points to pre-execve mm (hence the condition). | |
415 | * However the stack vaddr is soon relocated (randomization) and | |
416 | * move_page_tables() tries to undo that TLB entry. | |
417 | * Thus not creating TLB entry is not any worse. | |
418 | * | |
419 | * 2. ptrace(POKETEXT) causes a CoW - debugger(current) inserting a | |
420 | * breakpoint in debugged task. Not creating a TLB now is not | |
421 | * performance critical. | |
422 | * | |
423 | * Both the cases above are not good enough for code churn. | |
424 | */ | |
425 | if (current->active_mm != vma->vm_mm) | |
426 | return; | |
427 | ||
428 | local_irq_save(flags); | |
429 | ||
28b4af72 | 430 | vaddr &= PAGE_MASK; |
cc562d2e VG |
431 | |
432 | /* update this PTE credentials */ | |
433 | pte_val(*ptep) |= (_PAGE_PRESENT | _PAGE_ACCESSED); | |
434 | ||
d091fcb9 | 435 | /* Create HW TLB(PD0,PD1) from PTE */ |
cc562d2e VG |
436 | |
437 | /* ASID for this task */ | |
438 | asid_or_sasid = read_aux_reg(ARC_REG_PID) & 0xff; | |
439 | ||
28b4af72 | 440 | pd0 = vaddr | asid_or_sasid | (pte_val(*ptep) & PTE_BITS_IN_PD0); |
cc562d2e | 441 | |
64b703ef VG |
442 | /* |
443 | * ARC MMU provides fully orthogonal access bits for K/U mode, | |
444 | * however Linux only saves 1 set to save PTE real-estate | |
445 | * Here we convert 3 PTE bits into 6 MMU bits: | |
446 | * -Kernel only entries have Kr Kw Kx 0 0 0 | |
447 | * -User entries have mirrored K and U bits | |
448 | */ | |
449 | rwx = pte_val(*ptep) & PTE_BITS_RWX; | |
450 | ||
451 | if (pte_val(*ptep) & _PAGE_GLOBAL) | |
452 | rwx <<= 3; /* r w x => Kr Kw Kx 0 0 0 */ | |
453 | else | |
454 | rwx |= (rwx << 3); /* r w x => Kr Kw Kx Ur Uw Ux */ | |
455 | ||
483e9bcb | 456 | pd1 = rwx | (pte_val(*ptep) & PTE_BITS_NON_RWX_IN_PD1); |
cc562d2e | 457 | |
483e9bcb | 458 | tlb_entry_insert(pd0, pd1); |
cc562d2e VG |
459 | |
460 | local_irq_restore(flags); | |
461 | } | |
462 | ||
eacd0e95 VG |
463 | /* |
464 | * Called at the end of pagefault, for a userspace mapped page | |
465 | * -pre-install the corresponding TLB entry into MMU | |
4102b533 VG |
466 | * -Finalize the delayed D-cache flush of kernel mapping of page due to |
467 | * flush_dcache_page(), copy_user_page() | |
468 | * | |
469 | * Note that flush (when done) involves both WBACK - so physical page is | |
470 | * in sync as well as INV - so any non-congruent aliases don't remain | |
cc562d2e | 471 | */ |
ac4cfacc MWO |
472 | void update_mmu_cache_range(struct vm_fault *vmf, struct vm_area_struct *vma, |
473 | unsigned long vaddr_unaligned, pte_t *ptep, unsigned int nr) | |
cc562d2e | 474 | { |
24603fdd | 475 | unsigned long vaddr = vaddr_unaligned & PAGE_MASK; |
c5f756d8 | 476 | phys_addr_t paddr = pte_val(*ptep) & PAGE_MASK_PHYS; |
29b93c68 | 477 | struct page *page = pfn_to_page(pte_pfn(*ptep)); |
24603fdd VG |
478 | |
479 | create_tlb(vma, vaddr, ptep); | |
cc562d2e | 480 | |
6732c0e4 | 481 | if (page == ZERO_PAGE(0)) |
29b93c68 | 482 | return; |
29b93c68 | 483 | |
4102b533 | 484 | /* |
6732c0e4 VG |
485 | * For executable pages, since icache doesn't snoop dcache, any |
486 | * dirty K-mapping of a code page needs to be wback+inv so that | |
487 | * icache fetch by userspace sees code correctly. | |
4102b533 | 488 | */ |
6732c0e4 | 489 | if (vma->vm_flags & VM_EXEC) { |
ac4cfacc MWO |
490 | struct folio *folio = page_folio(page); |
491 | int dirty = !test_and_set_bit(PG_dc_clean, &folio->flags); | |
eacd0e95 | 492 | if (dirty) { |
ac4cfacc MWO |
493 | unsigned long offset = offset_in_folio(folio, paddr); |
494 | nr = folio_nr_pages(folio); | |
495 | paddr -= offset; | |
496 | vaddr -= offset; | |
61a16348 | 497 | /* wback + inv dcache lines (K-mapping) */ |
ac4cfacc | 498 | __flush_dcache_pages(paddr, paddr, nr); |
4102b533 | 499 | |
61a16348 | 500 | /* invalidate any existing icache lines (U-mapping) */ |
4102b533 | 501 | if (vma->vm_flags & VM_EXEC) |
ac4cfacc | 502 | __inv_icache_pages(paddr, vaddr, nr); |
eacd0e95 | 503 | } |
24603fdd | 504 | } |
cc562d2e VG |
505 | } |
506 | ||
fe6c1b86 VG |
507 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
508 | ||
509 | /* | |
510 | * MMUv4 in HS38x cores supports Super Pages which are basis for Linux THP | |
511 | * support. | |
512 | * | |
513 | * Normal and Super pages can co-exist (ofcourse not overlap) in TLB with a | |
7423cc0c | 514 | * new bit "SZ" in TLB page descriptor to distinguish between them. |
fe6c1b86 VG |
515 | * Super Page size is configurable in hardware (4K to 16M), but fixed once |
516 | * RTL builds. | |
517 | * | |
5f840df5 | 518 | * The exact THP size a Linux configuration will support is a function of: |
fe6c1b86 VG |
519 | * - MMU page size (typical 8K, RTL fixed) |
520 | * - software page walker address split between PGD:PTE:PFN (typical | |
521 | * 11:8:13, but can be changed with 1 line) | |
522 | * So for above default, THP size supported is 8K * (2^8) = 2M | |
523 | * | |
524 | * Default Page Walker is 2 levels, PGD:PTE:PFN, which in THP regime | |
525 | * reduces to 1 level (as PTE is folded into PGD and canonically referred | |
526 | * to as PMD). | |
527 | * Thus THP PMD accessors are implemented in terms of PTE (just like sparc) | |
528 | */ | |
529 | ||
530 | void update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr, | |
531 | pmd_t *pmd) | |
532 | { | |
533 | pte_t pte = __pte(pmd_val(*pmd)); | |
ac4cfacc | 534 | update_mmu_cache_range(NULL, vma, addr, &pte, HPAGE_PMD_NR); |
fe6c1b86 VG |
535 | } |
536 | ||
c7119d56 VG |
537 | void local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, |
538 | unsigned long end) | |
722fe8fd VG |
539 | { |
540 | unsigned int cpu; | |
541 | unsigned long flags; | |
542 | ||
543 | local_irq_save(flags); | |
544 | ||
545 | cpu = smp_processor_id(); | |
546 | ||
547 | if (likely(asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID)) { | |
548 | unsigned int asid = hw_pid(vma->vm_mm, cpu); | |
549 | ||
550 | /* No need to loop here: this will always be for 1 Huge Page */ | |
551 | tlb_entry_erase(start | _PAGE_HW_SZ | asid); | |
552 | } | |
553 | ||
554 | local_irq_restore(flags); | |
555 | } | |
556 | ||
fe6c1b86 VG |
557 | #endif |
558 | ||
5f840df5 | 559 | /* Read the Cache Build Configuration Registers, Decode them and save into |
cc562d2e VG |
560 | * the cpuinfo structure for later use. |
561 | * No Validation is done here, simply read/convert the BCRs | |
562 | */ | |
fad84e39 | 563 | int arc_mmu_mumbojumbo(int c, char *buf, int len) |
cc562d2e | 564 | { |
72d861f2 VG |
565 | struct cpuinfo_arc_mmu *mmu = &mmuinfo; |
566 | unsigned int bcr, u_dtlb, u_itlb, sasid; | |
567 | struct bcr_mmu_3 *mmu3; | |
568 | struct bcr_mmu_4 *mmu4; | |
569 | char super_pg[64] = ""; | |
570 | int n = 0; | |
d7a512bf | 571 | |
72d861f2 VG |
572 | bcr = read_aux_reg(ARC_REG_MMU_BCR); |
573 | mmu->ver = (bcr >> 24); | |
cc562d2e | 574 | |
288ff7de | 575 | if (is_isa_arcompact() && mmu->ver == 3) { |
72d861f2 | 576 | mmu3 = (struct bcr_mmu_3 *)&bcr; |
288ff7de VG |
577 | mmu->pg_sz_k = 1 << (mmu3->pg_sz - 1); |
578 | mmu->sets = 1 << mmu3->sets; | |
579 | mmu->ways = 1 << mmu3->ways; | |
72d861f2 VG |
580 | u_dtlb = mmu3->u_dtlb; |
581 | u_itlb = mmu3->u_itlb; | |
582 | sasid = mmu3->sasid; | |
d7a512bf | 583 | } else { |
72d861f2 | 584 | mmu4 = (struct bcr_mmu_4 *)&bcr; |
d7a512bf VG |
585 | mmu->pg_sz_k = 1 << (mmu4->sz0 - 1); |
586 | mmu->s_pg_sz_m = 1 << (mmu4->sz1 - 11); | |
587 | mmu->sets = 64 << mmu4->n_entry; | |
588 | mmu->ways = mmu4->n_ways * 2; | |
72d861f2 VG |
589 | u_dtlb = mmu4->u_dtlb * 4; |
590 | u_itlb = mmu4->u_itlb * 4; | |
591 | sasid = mmu4->sasid; | |
592 | mmu->pae = mmu4->pae; | |
cc562d2e | 593 | } |
cc562d2e | 594 | |
72d861f2 VG |
595 | if (mmu->s_pg_sz_m) |
596 | scnprintf(super_pg, 64, "/%dM%s", | |
597 | mmu->s_pg_sz_m, | |
598 | IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) ? " (THP enabled)":""); | |
af617428 | 599 | |
af617428 | 600 | n += scnprintf(buf + n, len - n, |
72d861f2 VG |
601 | "MMU [v%x]\t: %dk%s, swalk %d lvl, JTLB %dx%d, uDTLB %d, uITLB %d%s%s%s\n", |
602 | mmu->ver, mmu->pg_sz_k, super_pg, CONFIG_PGTABLE_LEVELS, | |
603 | mmu->sets, mmu->ways, | |
604 | u_dtlb, u_itlb, | |
605 | IS_AVAIL1(sasid, ", SASID"), | |
606 | IS_AVAIL2(mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40)); | |
af617428 | 607 | |
fad84e39 | 608 | return n; |
af617428 VG |
609 | } |
610 | ||
b5ddb6d5 VG |
611 | int pae40_exist_but_not_enab(void) |
612 | { | |
72d861f2 | 613 | return mmuinfo.pae && !is_pae40_enabled(); |
b5ddb6d5 VG |
614 | } |
615 | ||
ce759956 | 616 | void arc_mmu_init(void) |
cc562d2e | 617 | { |
72d861f2 | 618 | struct cpuinfo_arc_mmu *mmu = &mmuinfo; |
92d44128 | 619 | int compat = 0; |
af617428 | 620 | |
15ca68a9 | 621 | /* |
5f840df5 | 622 | * Can't be done in processor.h due to header include dependencies |
15ca68a9 NC |
623 | */ |
624 | BUILD_BUG_ON(!IS_ALIGNED((CONFIG_ARC_KVADDR_SIZE << 20), PMD_SIZE)); | |
625 | ||
8bcf2c48 NC |
626 | /* |
627 | * stack top size sanity check, | |
5f840df5 | 628 | * Can't be done in processor.h due to header include dependencies |
8bcf2c48 NC |
629 | */ |
630 | BUILD_BUG_ON(!IS_ALIGNED(STACK_TOP, PMD_SIZE)); | |
631 | ||
92d44128 VG |
632 | /* |
633 | * Ensure that MMU features assumed by kernel exist in hardware. | |
288ff7de VG |
634 | * - For older ARC700 cpus, only v3 supported |
635 | * - For HS cpus, v4 was baseline and v5 is backwards compatible | |
636 | * (will run older software). | |
af617428 | 637 | */ |
288ff7de | 638 | if (is_isa_arcompact() && mmu->ver == 3) |
92d44128 | 639 | compat = 1; |
288ff7de | 640 | else if (is_isa_arcv2() && mmu->ver >= 4) |
92d44128 VG |
641 | compat = 1; |
642 | ||
288ff7de VG |
643 | if (!compat) |
644 | panic("MMU ver %d doesn't match kernel built for\n", mmu->ver); | |
af617428 | 645 | |
40b552d9 | 646 | if (mmu->pg_sz_k != TO_KB(PAGE_SIZE)) |
af617428 VG |
647 | panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE)); |
648 | ||
6ce18798 VG |
649 | if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && |
650 | mmu->s_pg_sz_m != TO_MB(HPAGE_PMD_SIZE)) | |
651 | panic("MMU Super pg size != Linux HPAGE_PMD_SIZE (%luM)\n", | |
652 | (unsigned long)TO_MB(HPAGE_PMD_SIZE)); | |
653 | ||
5a364c2a VG |
654 | if (IS_ENABLED(CONFIG_ARC_HAS_PAE40) && !mmu->pae) |
655 | panic("Hardware doesn't support PAE40\n"); | |
656 | ||
89d0d424 VG |
657 | /* Enable the MMU with ASID 0 */ |
658 | mmu_setup_asid(NULL, 0); | |
41195d23 | 659 | |
89d0d424 VG |
660 | /* cache the pgd pointer in MMU SCRATCH reg (ARCv2 only) */ |
661 | mmu_setup_pgd(NULL, swapper_pg_dir); | |
b5ddb6d5 VG |
662 | |
663 | if (pae40_exist_but_not_enab()) | |
664 | write_aux_reg(ARC_REG_TLBPD1HI, 0); | |
cc562d2e VG |
665 | } |
666 | ||
667 | /* | |
668 | * TLB Programmer's Model uses Linear Indexes: 0 to {255, 511} for 128 x {2,4} | |
669 | * The mapping is Column-first. | |
670 | * --------------------- ----------- | |
671 | * |way0|way1|way2|way3| |way0|way1| | |
672 | * --------------------- ----------- | |
673 | * [set0] | 0 | 1 | 2 | 3 | | 0 | 1 | | |
674 | * [set1] | 4 | 5 | 6 | 7 | | 2 | 3 | | |
675 | * ~ ~ ~ ~ | |
676 | * [set127] | 508| 509| 510| 511| | 254| 255| | |
677 | * --------------------- ----------- | |
678 | * For normal operations we don't(must not) care how above works since | |
679 | * MMU cmd getIndex(vaddr) abstracts that out. | |
680 | * However for walking WAYS of a SET, we need to know this | |
681 | */ | |
682 | #define SET_WAY_TO_IDX(mmu, set, way) ((set) * mmu->ways + (way)) | |
683 | ||
684 | /* Handling of Duplicate PD (TLB entry) in MMU. | |
685 | * -Could be due to buggy customer tapeouts or obscure kernel bugs | |
686 | * -MMU complaints not at the time of duplicate PD installation, but at the | |
687 | * time of lookup matching multiple ways. | |
688 | * -Ideally these should never happen - but if they do - workaround by deleting | |
689 | * the duplicate one. | |
690 | * -Knob to be verbose abt it.(TODO: hook them up to debugfs) | |
691 | */ | |
5f840df5 | 692 | volatile int dup_pd_silent; /* Be silent abt it or complain (default) */ |
cc562d2e VG |
693 | |
694 | void do_tlb_overlap_fault(unsigned long cause, unsigned long address, | |
695 | struct pt_regs *regs) | |
696 | { | |
72d861f2 | 697 | struct cpuinfo_arc_mmu *mmu = &mmuinfo; |
8840e14c | 698 | unsigned long flags; |
89c92142 VG |
699 | int set, n_ways = mmu->ways; |
700 | ||
701 | n_ways = min(n_ways, 4); | |
702 | BUG_ON(mmu->ways > 4); | |
cc562d2e VG |
703 | |
704 | local_irq_save(flags); | |
705 | ||
cc562d2e VG |
706 | /* loop thru all sets of TLB */ |
707 | for (set = 0; set < mmu->sets; set++) { | |
708 | ||
8840e14c | 709 | int is_valid, way; |
89c92142 | 710 | unsigned int pd0[4]; |
8840e14c | 711 | |
cc562d2e | 712 | /* read out all the ways of current set */ |
89c92142 | 713 | for (way = 0, is_valid = 0; way < n_ways; way++) { |
cc562d2e VG |
714 | write_aux_reg(ARC_REG_TLBINDEX, |
715 | SET_WAY_TO_IDX(mmu, set, way)); | |
716 | write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead); | |
717 | pd0[way] = read_aux_reg(ARC_REG_TLBPD0); | |
cc562d2e | 718 | is_valid |= pd0[way] & _PAGE_PRESENT; |
8840e14c | 719 | pd0[way] &= PAGE_MASK; |
cc562d2e VG |
720 | } |
721 | ||
722 | /* If all the WAYS in SET are empty, skip to next SET */ | |
723 | if (!is_valid) | |
724 | continue; | |
725 | ||
726 | /* Scan the set for duplicate ways: needs a nested loop */ | |
89c92142 | 727 | for (way = 0; way < n_ways - 1; way++) { |
8840e14c VG |
728 | |
729 | int n; | |
730 | ||
cc562d2e VG |
731 | if (!pd0[way]) |
732 | continue; | |
733 | ||
89c92142 | 734 | for (n = way + 1; n < n_ways; n++) { |
8840e14c VG |
735 | if (pd0[way] != pd0[n]) |
736 | continue; | |
737 | ||
738 | if (!dup_pd_silent) | |
739 | pr_info("Dup TLB PD0 %08x @ set %d ways %d,%d\n", | |
740 | pd0[way], set, way, n); | |
741 | ||
742 | /* | |
743 | * clear entry @way and not @n. | |
744 | * This is critical to our optimised loop | |
745 | */ | |
746 | pd0[way] = 0; | |
747 | write_aux_reg(ARC_REG_TLBINDEX, | |
cc562d2e | 748 | SET_WAY_TO_IDX(mmu, set, way)); |
8840e14c | 749 | __tlb_entry_erase(); |
cc562d2e VG |
750 | } |
751 | } | |
752 | } | |
753 | ||
754 | local_irq_restore(flags); | |
755 | } |