164b02169498f14addfa4cb0e06caa198f42d79a
[platform/adaptation/renesas_rcar/renesas_kernel.git] / arch / arc / mm / tlbex.S
1 /*
2  * TLB Exception Handling for ARC
3  *
4  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Vineetg: April 2011 :
11  *  -MMU v1: moved out legacy code into a seperate file
12  *  -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore,
13  *      helps avoid a shift when preparing PD0 from PTE
14  *
15  * Vineetg: July 2009
16  *  -For MMU V2, we need not do heuristics at the time of commiting a D-TLB
17  *   entry, so that it doesn't knock out it's I-TLB entry
18  *  -Some more fine tuning:
19  *   bmsk instead of add, asl.cc instead of branch, delay slot utilise etc
20  *
21  * Vineetg: July 2009
22  *  -Practically rewrote the I/D TLB Miss handlers
23  *   Now 40 and 135 instructions a peice as compared to 131 and 449 resp.
24  *   Hence Leaner by 1.5 K
25  *   Used Conditional arithmetic to replace excessive branching
26  *   Also used short instructions wherever possible
27  *
28  * Vineetg: Aug 13th 2008
29  *  -Passing ECR (Exception Cause REG) to do_page_fault( ) for printing
30  *   more information in case of a Fatality
31  *
32  * Vineetg: March 25th Bug #92690
33  *  -Added Debug Code to check if sw-ASID == hw-ASID
34
35  * Rahul Trivedi, Amit Bhor: Codito Technologies 2004
36  */
37
38         .cpu A7
39
40 #include <linux/linkage.h>
41 #include <asm/entry.h>
42 #include <asm/tlb.h>
43 #include <asm/pgtable.h>
44 #include <asm/arcregs.h>
45 #include <asm/cache.h>
46 #include <asm/processor.h>
47 #if (CONFIG_ARC_MMU_VER == 1)
48 #include <asm/tlb-mmu1.h>
49 #endif
50
51 ;--------------------------------------------------------------------------
52 ; scratch memory to save the registers (r0-r3) used to code TLB refill Handler
53 ; For details refer to comments before TLBMISS_FREEUP_REGS below
54 ;--------------------------------------------------------------------------
55
56         .section .data
57         .global ex_saved_reg1
58         .align 1 << L1_CACHE_SHIFT      ; IMP: Must be Cache Line aligned
59         .type   ex_saved_reg1, @object
60         .size   ex_saved_reg1, 16
61 ex_saved_reg1:
62         .zero 16
63
64 ;============================================================================
65 ;  Troubleshooting Stuff
66 ;============================================================================
67
68 ; Linux keeps ASID (Address Space ID) in task->active_mm->context.asid
69 ; When Creating TLB Entries, instead of doing 3 dependent loads from memory,
70 ; we use the MMU PID Reg to get current ASID.
71 ; In bizzare scenrios SW and HW ASID can get out-of-sync which is trouble.
72 ; So we try to detect this in TLB Mis shandler
73
74
75 .macro DBG_ASID_MISMATCH
76
77 #ifdef CONFIG_ARC_DBG_TLB_PARANOIA
78
79         ; make sure h/w ASID is same as s/w ASID
80
81         GET_CURR_TASK_ON_CPU  r3
82         ld r0, [r3, TASK_ACT_MM]
83         ld r0, [r0, MM_CTXT+MM_CTXT_ASID]
84
85         lr r1, [ARC_REG_PID]
86         and r1, r1, 0xFF
87         breq r1, r0, 5f
88
89         ; Error if H/w and S/w ASID don't match, but NOT if in kernel mode
90         lr  r0, [erstatus]
91         bbit0 r0, STATUS_U_BIT, 5f
92
93         ; We sure are in troubled waters, Flag the error, but to do so
94         ; need to switch to kernel mode stack to call error routine
95         GET_TSK_STACK_BASE   r3, sp
96
97         ; Call printk to shoutout aloud
98         mov r0, 1
99         j print_asid_mismatch
100
101 5:   ; ASIDs match so proceed normally
102         nop
103
104 #endif
105
106 .endm
107
108 ;============================================================================
109 ;TLB Miss handling Code
110 ;============================================================================
111
112 ;-----------------------------------------------------------------------------
113 ; This macro does the page-table lookup for the faulting address.
114 ; OUT: r0 = PTE faulted on, r1 = ptr to PTE, r2 = Faulting V-address
115 .macro LOAD_FAULT_PTE
116
117         lr  r2, [efa]
118
119         lr  r1, [ARC_REG_SCRATCH_DATA0] ; current pgd
120
121         lsr     r0, r2, PGDIR_SHIFT     ; Bits for indexing into PGD
122         ld.as   r1, [r1, r0]            ; PGD entry corresp to faulting addr
123         and.f   r1, r1, PAGE_MASK       ; Ignoring protection and other flags
124         ;   contains Ptr to Page Table
125         bz.d    do_slow_path_pf         ; if no Page Table, do page fault
126
127         ; Get the PTE entry: The idea is
128         ; (1) x = addr >> PAGE_SHIFT    -> masks page-off bits from @fault-addr
129         ; (2) y = x & (PTRS_PER_PTE - 1) -> to get index
130         ; (3) z = pgtbl[y]
131         ; To avoid the multiply by in end, we do the -2, <<2 below
132
133         lsr     r0, r2, (PAGE_SHIFT - 2)
134         and     r0, r0, ( (PTRS_PER_PTE - 1) << 2)
135         ld.aw   r0, [r1, r0]            ; get PTE and PTE ptr for fault addr
136 #ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
137         and.f 0, r0, _PAGE_PRESENT
138         bz   1f
139         ld   r2, [num_pte_not_present]
140         add  r2, r2, 1
141         st   r2, [num_pte_not_present]
142 1:
143 #endif
144
145 .endm
146
147 ;-----------------------------------------------------------------
148 ; Convert Linux PTE entry into TLB entry
149 ; A one-word PTE entry is programmed as two-word TLB Entry [PD0:PD1] in mmu
150 ; IN: r0 = PTE, r1 = ptr to PTE
151
152 .macro CONV_PTE_TO_TLB
153         and r3, r0, PTE_BITS_IN_PD1 ; Extract permission flags+PFN from PTE
154         sr  r3, [ARC_REG_TLBPD1]    ; these go in PD1
155
156         and r2, r0, PTE_BITS_IN_PD0 ; Extract other PTE flags: (V)alid, (G)lb
157 #if (CONFIG_ARC_MMU_VER <= 2)   /* Neednot be done with v3 onwards */
158         lsr r2, r2                  ; shift PTE flags to match layout in PD0
159 #endif
160
161         lr  r3,[ARC_REG_TLBPD0]     ; MMU prepares PD0 with vaddr and asid
162
163         or  r3, r3, r2              ; S | vaddr | {sasid|asid}
164         sr  r3,[ARC_REG_TLBPD0]     ; rewrite PD0
165 .endm
166
167 ;-----------------------------------------------------------------
168 ; Commit the TLB entry into MMU
169
170 .macro COMMIT_ENTRY_TO_MMU
171
172         /* Get free TLB slot: Set = computed from vaddr, way = random */
173         sr  TLBGetIndex, [ARC_REG_TLBCOMMAND]
174
175         /* Commit the Write */
176 #if (CONFIG_ARC_MMU_VER >= 2)   /* introduced in v2 */
177         sr TLBWriteNI, [ARC_REG_TLBCOMMAND]
178 #else
179         sr TLBWrite, [ARC_REG_TLBCOMMAND]
180 #endif
181 .endm
182
183 ;-----------------------------------------------------------------
184 ; ARC700 Exception Handling doesn't auto-switch stack and it only provides
185 ; ONE scratch AUX reg "ARC_REG_SCRATCH_DATA0"
186 ;
187 ; For Non-SMP, the scratch AUX reg is repurposed to cache task PGD, so a
188 ; "global" is used to free-up FIRST core reg to be able to code the rest of
189 ; exception prologue (IRQ auto-disabled on Exceptions, so it's IRQ-safe).
190 ; Since the Fast Path TLB Miss handler is coded with 4 regs, the remaining 3
191 ; need to be saved as well by extending the "global" to be 4 words. Hence
192 ;       ".size   ex_saved_reg1, 16"
193 ; [All of this dance is to avoid stack switching for each TLB Miss, since we
194 ; only need to save only a handful of regs, as opposed to complete reg file]
195
196 ; As simple as that....
197
198 .macro TLBMISS_FREEUP_REGS
199         st    r0, [@ex_saved_reg1]
200         mov_s r0, @ex_saved_reg1
201         st_s  r1, [r0, 4]
202         st_s  r2, [r0, 8]
203         st_s  r3, [r0, 12]
204
205         ; VERIFY if the ASID in MMU-PID Reg is same as
206         ; one in Linux data structures
207
208         DBG_ASID_MISMATCH
209 .endm
210
211 ;-----------------------------------------------------------------
212 .macro TLBMISS_RESTORE_REGS
213         mov_s r0, @ex_saved_reg1
214         ld_s  r3, [r0,12]
215         ld_s  r2, [r0, 8]
216         ld_s  r1, [r0, 4]
217         ld_s  r0, [r0]
218 .endm
219
220 .section .text, "ax",@progbits  ;Fast Path Code, candidate for ICCM
221
222 ;-----------------------------------------------------------------------------
223 ; I-TLB Miss Exception Handler
224 ;-----------------------------------------------------------------------------
225
226 ARC_ENTRY EV_TLBMissI
227
228         TLBMISS_FREEUP_REGS
229
230 #ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
231         ld  r0, [@numitlb]
232         add r0, r0, 1
233         st  r0, [@numitlb]
234 #endif
235
236         ;----------------------------------------------------------------
237         ; Get the PTE corresponding to V-addr accessed
238         LOAD_FAULT_PTE
239
240         ;----------------------------------------------------------------
241         ; VERIFY_PTE: Check if PTE permissions approp for executing code
242         cmp_s   r2, VMALLOC_START
243         mov.lo  r2, (_PAGE_PRESENT | _PAGE_READ | _PAGE_EXECUTE)
244         mov.hs  r2, (_PAGE_PRESENT | _PAGE_K_READ | _PAGE_K_EXECUTE)
245
246         and     r3, r0, r2  ; Mask out NON Flag bits from PTE
247         xor.f   r3, r3, r2  ; check ( ( pte & flags_test ) == flags_test )
248         bnz     do_slow_path_pf
249
250         ; Let Linux VM know that the page was accessed
251         or      r0, r0, (_PAGE_PRESENT | _PAGE_ACCESSED)  ; set Accessed Bit
252         st_s    r0, [r1]                                  ; Write back PTE
253
254         CONV_PTE_TO_TLB
255         COMMIT_ENTRY_TO_MMU
256         TLBMISS_RESTORE_REGS
257         rtie
258
259 ARC_EXIT EV_TLBMissI
260
261 ;-----------------------------------------------------------------------------
262 ; D-TLB Miss Exception Handler
263 ;-----------------------------------------------------------------------------
264
265 ARC_ENTRY EV_TLBMissD
266
267         TLBMISS_FREEUP_REGS
268
269 #ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
270         ld  r0, [@numdtlb]
271         add r0, r0, 1
272         st  r0, [@numdtlb]
273 #endif
274
275         ;----------------------------------------------------------------
276         ; Get the PTE corresponding to V-addr accessed
277         ; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE
278         LOAD_FAULT_PTE
279
280         ;----------------------------------------------------------------
281         ; VERIFY_PTE: Chk if PTE permissions approp for data access (R/W/R+W)
282
283         mov_s   r2, 0
284         lr      r3, [ecr]
285         btst_s  r3, ECR_C_BIT_DTLB_LD_MISS      ; Read Access
286         or.nz   r2, r2, _PAGE_READ              ; chk for Read flag in PTE
287         btst_s  r3, ECR_C_BIT_DTLB_ST_MISS      ; Write Access
288         or.nz   r2, r2, _PAGE_WRITE             ; chk for Write flag in PTE
289         ; Above laddering takes care of XCHG access
290         ;   which is both Read and Write
291
292         ; If kernel mode access, ; make _PAGE_xx flags as _PAGE_K_xx
293         ; For copy_(to|from)_user, despite exception taken in kernel mode,
294         ; this code is not hit, because EFA would still be the user mode
295         ; address (EFA < 0x6000_0000).
296         ; This code is for legit kernel mode faults, vmalloc specifically
297         ; (EFA: 0x7000_0000 to 0x7FFF_FFFF)
298
299         lr      r3, [efa]
300         cmp     r3, VMALLOC_START - 1   ; If kernel mode access
301         asl.hi  r2, r2, 3               ; make _PAGE_xx flags as _PAGE_K_xx
302         or      r2, r2, _PAGE_PRESENT   ; Common flag for K/U mode
303
304         ; By now, r2 setup with all the Flags we need to check in PTE
305         and     r3, r0, r2              ; Mask out NON Flag bits from PTE
306         brne.d  r3, r2, do_slow_path_pf ; is ((pte & flags_test) == flags_test)
307
308         ;----------------------------------------------------------------
309         ; UPDATE_PTE: Let Linux VM know that page was accessed/dirty
310         lr      r3, [ecr]
311         or      r0, r0, (_PAGE_PRESENT | _PAGE_ACCESSED) ; Accessed bit always
312         btst_s  r3,  ECR_C_BIT_DTLB_ST_MISS   ; See if it was a Write Access ?
313         or.nz   r0, r0, _PAGE_MODIFIED        ; if Write, set Dirty bit as well
314         st_s    r0, [r1]                      ; Write back PTE
315
316         CONV_PTE_TO_TLB
317
318 #if (CONFIG_ARC_MMU_VER == 1)
319         ; MMU with 2 way set assoc J-TLB, needs some help in pathetic case of
320         ; memcpy where 3 parties contend for 2 ways, ensuing a livelock.
321         ; But only for old MMU or one with Metal Fix
322         TLB_WRITE_HEURISTICS
323 #endif
324
325         COMMIT_ENTRY_TO_MMU
326         TLBMISS_RESTORE_REGS
327         rtie
328
329 ;-------- Common routine to call Linux Page Fault Handler -----------
330 do_slow_path_pf:
331
332         ; Restore the 4-scratch regs saved by fast path miss handler
333         TLBMISS_RESTORE_REGS
334
335         ; Slow path TLB Miss handled as a regular ARC Exception
336         ; (stack switching / save the complete reg-file).
337         ; That requires freeing up r9
338         EXCPN_PROLOG_FREEUP_REG r9
339
340         lr  r9, [erstatus]
341
342         SWITCH_TO_KERNEL_STK
343         SAVE_ALL_SYS
344
345         ; ------- setup args for Linux Page fault Hanlder ---------
346         mov_s r0, sp
347         lr  r2, [efa]
348         lr  r3, [ecr]
349
350         ; Both st and ex imply WRITE access of some sort, hence do_page_fault( )
351         ; invoked with write=1 for DTLB-st/ex Miss and write=0 for ITLB miss or
352         ; DTLB-ld Miss
353         ; DTLB Miss Cause code is ld = 0x01 , st = 0x02, ex = 0x03
354         ; Following code uses that fact that st/ex have one bit in common
355
356         btst_s r3,  ECR_C_BIT_DTLB_ST_MISS
357         mov.z  r1, 0
358         mov.nz r1, 1
359
360         ; We don't want exceptions to be disabled while the fault is handled.
361         ; Now that we have saved the context we return from exception hence
362         ; exceptions get re-enable
363
364         FAKE_RET_FROM_EXCPN  r9
365
366         bl  do_page_fault
367         b   ret_from_exception
368
369 ARC_EXIT EV_TLBMissD
370
371 ARC_ENTRY EV_TLBMissB   ; Bogus entry to measure sz of DTLBMiss hdlr