xref: /openbmc/linux/arch/arc/mm/tlbex.S (revision af958a38)
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/mmu.h>
43#include <asm/pgtable.h>
44#include <asm/arcregs.h>
45#include <asm/cache.h>
46#include <asm/processor.h>
47#include <asm/tlb-mmu1.h>
48
49;-----------------------------------------------------------------
50; ARC700 Exception Handling doesn't auto-switch stack and it only provides
51; ONE scratch AUX reg "ARC_REG_SCRATCH_DATA0"
52;
53; For Non-SMP, the scratch AUX reg is repurposed to cache task PGD, so a
54; "global" is used to free-up FIRST core reg to be able to code the rest of
55; exception prologue (IRQ auto-disabled on Exceptions, so it's IRQ-safe).
56; Since the Fast Path TLB Miss handler is coded with 4 regs, the remaining 3
57; need to be saved as well by extending the "global" to be 4 words. Hence
58;	".size   ex_saved_reg1, 16"
59; [All of this dance is to avoid stack switching for each TLB Miss, since we
60; only need to save only a handful of regs, as opposed to complete reg file]
61;
62; For ARC700 SMP, the "global" obviously can't be used for free up the FIRST
63; core reg as it will not be SMP safe.
64; Thus scratch AUX reg is used (and no longer used to cache task PGD).
65; To save the rest of 3 regs - per cpu, the global is made "per-cpu".
66; Epilogue thus has to locate the "per-cpu" storage for regs.
67; To avoid cache line bouncing the per-cpu global is aligned/sized per
68; L1_CACHE_SHIFT, despite fundamentally needing to be 12 bytes only. Hence
69;	".size   ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)"
70
71; As simple as that....
72;--------------------------------------------------------------------------
73
74; scratch memory to save [r0-r3] used to code TLB refill Handler
75ARCFP_DATA ex_saved_reg1
76	.align 1 << L1_CACHE_SHIFT
77	.type   ex_saved_reg1, @object
78#ifdef CONFIG_SMP
79	.size   ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)
80ex_saved_reg1:
81	.zero (CONFIG_NR_CPUS << L1_CACHE_SHIFT)
82#else
83	.size   ex_saved_reg1, 16
84ex_saved_reg1:
85	.zero 16
86#endif
87
88.macro TLBMISS_FREEUP_REGS
89#ifdef CONFIG_SMP
90	sr  r0, [ARC_REG_SCRATCH_DATA0]	; freeup r0 to code with
91	GET_CPU_ID  r0			; get to per cpu scratch mem,
92	lsl r0, r0, L1_CACHE_SHIFT	; cache line wide per cpu
93	add r0, @ex_saved_reg1, r0
94#else
95	st    r0, [@ex_saved_reg1]
96	mov_s r0, @ex_saved_reg1
97#endif
98	st_s  r1, [r0, 4]
99	st_s  r2, [r0, 8]
100	st_s  r3, [r0, 12]
101
102	; VERIFY if the ASID in MMU-PID Reg is same as
103	; one in Linux data structures
104
105	tlb_paranoid_check_asm
106.endm
107
108.macro TLBMISS_RESTORE_REGS
109#ifdef CONFIG_SMP
110	GET_CPU_ID  r0			; get to per cpu scratch mem
111	lsl r0, r0, L1_CACHE_SHIFT	; each is cache line wide
112	add r0, @ex_saved_reg1, r0
113	ld_s  r3, [r0,12]
114	ld_s  r2, [r0, 8]
115	ld_s  r1, [r0, 4]
116	lr    r0, [ARC_REG_SCRATCH_DATA0]
117#else
118	mov_s r0, @ex_saved_reg1
119	ld_s  r3, [r0,12]
120	ld_s  r2, [r0, 8]
121	ld_s  r1, [r0, 4]
122	ld_s  r0, [r0]
123#endif
124.endm
125
126;============================================================================
127;  Troubleshooting Stuff
128;============================================================================
129
130; Linux keeps ASID (Address Space ID) in task->active_mm->context.asid
131; When Creating TLB Entries, instead of doing 3 dependent loads from memory,
132; we use the MMU PID Reg to get current ASID.
133; In bizzare scenrios SW and HW ASID can get out-of-sync which is trouble.
134; So we try to detect this in TLB Mis shandler
135
136.macro tlb_paranoid_check_asm
137
138#ifdef CONFIG_ARC_DBG_TLB_PARANOIA
139
140	GET_CURR_TASK_ON_CPU  r3
141	ld r0, [r3, TASK_ACT_MM]
142	ld r0, [r0, MM_CTXT+MM_CTXT_ASID]
143	breq r0, 0, 55f	; Error if no ASID allocated
144
145	lr r1, [ARC_REG_PID]
146	and r1, r1, 0xFF
147
148	and r2, r0, 0xFF	; MMU PID bits only for comparison
149	breq r1, r2, 5f
150
15155:
152	; Error if H/w and S/w ASID don't match, but NOT if in kernel mode
153	lr  r2, [erstatus]
154	bbit0 r2, STATUS_U_BIT, 5f
155
156	; We sure are in troubled waters, Flag the error, but to do so
157	; need to switch to kernel mode stack to call error routine
158	GET_TSK_STACK_BASE   r3, sp
159
160	; Call printk to shoutout aloud
161	mov r2, 1
162	j print_asid_mismatch
163
1645:	; ASIDs match so proceed normally
165	nop
166
167#endif
168
169.endm
170
171;============================================================================
172;TLB Miss handling Code
173;============================================================================
174
175;-----------------------------------------------------------------------------
176; This macro does the page-table lookup for the faulting address.
177; OUT: r0 = PTE faulted on, r1 = ptr to PTE, r2 = Faulting V-address
178.macro LOAD_FAULT_PTE
179
180	lr  r2, [efa]
181
182#ifndef CONFIG_SMP
183	lr  r1, [ARC_REG_SCRATCH_DATA0] ; current pgd
184#else
185	GET_CURR_TASK_ON_CPU  r1
186	ld  r1, [r1, TASK_ACT_MM]
187	ld  r1, [r1, MM_PGD]
188#endif
189
190	lsr     r0, r2, PGDIR_SHIFT     ; Bits for indexing into PGD
191	ld.as   r1, [r1, r0]            ; PGD entry corresp to faulting addr
192	and.f   r1, r1, PAGE_MASK       ; Ignoring protection and other flags
193	;   contains Ptr to Page Table
194	bz.d    do_slow_path_pf         ; if no Page Table, do page fault
195
196	; Get the PTE entry: The idea is
197	; (1) x = addr >> PAGE_SHIFT 	-> masks page-off bits from @fault-addr
198	; (2) y = x & (PTRS_PER_PTE - 1) -> to get index
199	; (3) z = pgtbl[y]
200	; To avoid the multiply by in end, we do the -2, <<2 below
201
202	lsr     r0, r2, (PAGE_SHIFT - 2)
203	and     r0, r0, ( (PTRS_PER_PTE - 1) << 2)
204	ld.aw   r0, [r1, r0]            ; get PTE and PTE ptr for fault addr
205#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
206	and.f 0, r0, _PAGE_PRESENT
207	bz   1f
208	ld   r3, [num_pte_not_present]
209	add  r3, r3, 1
210	st   r3, [num_pte_not_present]
2111:
212#endif
213
214.endm
215
216;-----------------------------------------------------------------
217; Convert Linux PTE entry into TLB entry
218; A one-word PTE entry is programmed as two-word TLB Entry [PD0:PD1] in mmu
219; IN: r0 = PTE, r1 = ptr to PTE
220
221.macro CONV_PTE_TO_TLB
222	and    r3, r0, PTE_BITS_RWX	;       r w x
223	lsl    r2, r3, 3		; r w x 0 0 0 (GLOBAL, kernel only)
224	and.f  0,  r0, _PAGE_GLOBAL
225	or.z   r2, r2, r3		; r w x r w x (!GLOBAL, user page)
226
227	and r3, r0, PTE_BITS_NON_RWX_IN_PD1 ; Extract PFN+cache bits from PTE
228	or  r3, r3, r2
229
230	sr  r3, [ARC_REG_TLBPD1]    	; these go in PD1
231
232	and r2, r0, PTE_BITS_IN_PD0 ; Extract other PTE flags: (V)alid, (G)lb
233
234	lr  r3,[ARC_REG_TLBPD0]     ; MMU prepares PD0 with vaddr and asid
235
236	or  r3, r3, r2              ; S | vaddr | {sasid|asid}
237	sr  r3,[ARC_REG_TLBPD0]     ; rewrite PD0
238.endm
239
240;-----------------------------------------------------------------
241; Commit the TLB entry into MMU
242
243.macro COMMIT_ENTRY_TO_MMU
244
245	/* Get free TLB slot: Set = computed from vaddr, way = random */
246	sr  TLBGetIndex, [ARC_REG_TLBCOMMAND]
247
248	/* Commit the Write */
249#if (CONFIG_ARC_MMU_VER >= 2)   /* introduced in v2 */
250	sr TLBWriteNI, [ARC_REG_TLBCOMMAND]
251#else
252	sr TLBWrite, [ARC_REG_TLBCOMMAND]
253#endif
254.endm
255
256
257ARCFP_CODE	;Fast Path Code, candidate for ICCM
258
259;-----------------------------------------------------------------------------
260; I-TLB Miss Exception Handler
261;-----------------------------------------------------------------------------
262
263ENTRY(EV_TLBMissI)
264
265	TLBMISS_FREEUP_REGS
266
267#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
268	ld  r0, [@numitlb]
269	add r0, r0, 1
270	st  r0, [@numitlb]
271#endif
272
273	;----------------------------------------------------------------
274	; Get the PTE corresponding to V-addr accessed, r2 is setup with EFA
275	LOAD_FAULT_PTE
276
277	;----------------------------------------------------------------
278	; VERIFY_PTE: Check if PTE permissions approp for executing code
279	cmp_s   r2, VMALLOC_START
280	mov_s   r2, (_PAGE_PRESENT | _PAGE_EXECUTE)
281	or.hs   r2, r2, _PAGE_GLOBAL
282
283	and     r3, r0, r2  ; Mask out NON Flag bits from PTE
284	xor.f   r3, r3, r2  ; check ( ( pte & flags_test ) == flags_test )
285	bnz     do_slow_path_pf
286
287	; Let Linux VM know that the page was accessed
288	or      r0, r0, _PAGE_ACCESSED  ; set Accessed Bit
289	st_s    r0, [r1]                ; Write back PTE
290
291	CONV_PTE_TO_TLB
292	COMMIT_ENTRY_TO_MMU
293	TLBMISS_RESTORE_REGS
294	rtie
295
296END(EV_TLBMissI)
297
298;-----------------------------------------------------------------------------
299; D-TLB Miss Exception Handler
300;-----------------------------------------------------------------------------
301
302ENTRY(EV_TLBMissD)
303
304	TLBMISS_FREEUP_REGS
305
306#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
307	ld  r0, [@numdtlb]
308	add r0, r0, 1
309	st  r0, [@numdtlb]
310#endif
311
312	;----------------------------------------------------------------
313	; Get the PTE corresponding to V-addr accessed
314	; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE, r2 = EFA
315	LOAD_FAULT_PTE
316
317	;----------------------------------------------------------------
318	; VERIFY_PTE: Chk if PTE permissions approp for data access (R/W/R+W)
319
320	cmp_s	r2, VMALLOC_START
321	mov_s   r2, _PAGE_PRESENT	; common bit for K/U PTE
322	or.hs	r2, r2, _PAGE_GLOBAL	; kernel PTE only
323
324	; Linux PTE [RWX] bits are semantically overloaded:
325	; -If PAGE_GLOBAL set, they refer to kernel-only flags (vmalloc)
326	; -Otherwise they are user-mode permissions, and those are exactly
327	;  same for kernel mode as well (e.g. copy_(to|from)_user)
328
329	lr      r3, [ecr]
330	btst_s  r3, ECR_C_BIT_DTLB_LD_MISS	; Read Access
331	or.nz   r2, r2, _PAGE_READ      	; chk for Read flag in PTE
332	btst_s  r3, ECR_C_BIT_DTLB_ST_MISS	; Write Access
333	or.nz   r2, r2, _PAGE_WRITE     	; chk for Write flag in PTE
334	; Above laddering takes care of XCHG access (both R and W)
335
336	; By now, r2 setup with all the Flags we need to check in PTE
337	and     r3, r0, r2              ; Mask out NON Flag bits from PTE
338	brne.d  r3, r2, do_slow_path_pf ; is ((pte & flags_test) == flags_test)
339
340	;----------------------------------------------------------------
341	; UPDATE_PTE: Let Linux VM know that page was accessed/dirty
342	lr      r3, [ecr]
343	or      r0, r0, _PAGE_ACCESSED        ; Accessed bit always
344	btst_s  r3,  ECR_C_BIT_DTLB_ST_MISS   ; See if it was a Write Access ?
345	or.nz   r0, r0, _PAGE_MODIFIED        ; if Write, set Dirty bit as well
346	st_s    r0, [r1]                      ; Write back PTE
347
348	CONV_PTE_TO_TLB
349
350#if (CONFIG_ARC_MMU_VER == 1)
351	; MMU with 2 way set assoc J-TLB, needs some help in pathetic case of
352	; memcpy where 3 parties contend for 2 ways, ensuing a livelock.
353	; But only for old MMU or one with Metal Fix
354	TLB_WRITE_HEURISTICS
355#endif
356
357	COMMIT_ENTRY_TO_MMU
358	TLBMISS_RESTORE_REGS
359	rtie
360
361;-------- Common routine to call Linux Page Fault Handler -----------
362do_slow_path_pf:
363
364	; Restore the 4-scratch regs saved by fast path miss handler
365	TLBMISS_RESTORE_REGS
366
367	; Slow path TLB Miss handled as a regular ARC Exception
368	; (stack switching / save the complete reg-file).
369	EXCEPTION_PROLOGUE
370
371	; ------- setup args for Linux Page fault Hanlder ---------
372	mov_s r1, sp
373	lr    r0, [efa]
374
375	; We don't want exceptions to be disabled while the fault is handled.
376	; Now that we have saved the context we return from exception hence
377	; exceptions get re-enable
378
379	FAKE_RET_FROM_EXCPN  r9
380
381	bl  do_page_fault
382	b   ret_from_exception
383
384END(EV_TLBMissD)
385