xref: /openbmc/linux/arch/sh/kernel/cpu/sh3/entry.S (revision 7dd65feb)
1/*
2 * arch/sh/kernel/cpu/sh3/entry.S
3 *
4 *  Copyright (C) 1999, 2000, 2002  Niibe Yutaka
5 *  Copyright (C) 2003 - 2006  Paul Mundt
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License.  See the file "COPYING" in the main directory of this archive
9 * for more details.
10 */
11#include <linux/sys.h>
12#include <linux/errno.h>
13#include <linux/linkage.h>
14#include <asm/asm-offsets.h>
15#include <asm/thread_info.h>
16#include <asm/unistd.h>
17#include <cpu/mmu_context.h>
18#include <asm/page.h>
19#include <asm/cache.h>
20
21! NOTE:
22! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
23! to be jumped is too far, but it causes illegal slot exception.
24
25/*
26 * entry.S contains the system-call and fault low-level handling routines.
27 * This also contains the timer-interrupt handler, as well as all interrupts
28 * and faults that can result in a task-switch.
29 *
30 * NOTE: This code handles signal-recognition, which happens every time
31 * after a timer-interrupt and after each system call.
32 *
33 * NOTE: This code uses a convention that instructions in the delay slot
34 * of a transfer-control instruction are indented by an extra space, thus:
35 *
36 *    jmp	@k0	    ! control-transfer instruction
37 *     ldc	k1, ssr     ! delay slot
38 *
39 * Stack layout in 'ret_from_syscall':
40 * 	ptrace needs to have all regs on the stack.
41 *	if the order here is changed, it needs to be
42 *	updated in ptrace.c and ptrace.h
43 *
44 *	r0
45 *      ...
46 *	r15 = stack pointer
47 *	spc
48 *	pr
49 *	ssr
50 *	gbr
51 *	mach
52 *	macl
53 *	syscall #
54 *
55 */
56/* Offsets to the stack */
57OFF_R0  =  0		/* Return value. New ABI also arg4 */
58OFF_R1  =  4     	/* New ABI: arg5 */
59OFF_R2  =  8     	/* New ABI: arg6 */
60OFF_R3  =  12     	/* New ABI: syscall_nr */
61OFF_R4  =  16     	/* New ABI: arg0 */
62OFF_R5  =  20     	/* New ABI: arg1 */
63OFF_R6  =  24     	/* New ABI: arg2 */
64OFF_R7  =  28     	/* New ABI: arg3 */
65OFF_SP	=  (15*4)
66OFF_PC  =  (16*4)
67OFF_SR	=  (16*4+8)
68OFF_TRA	=  (16*4+6*4)
69
70#define k0	r0
71#define k1	r1
72#define k2	r2
73#define k3	r3
74#define k4	r4
75
76#define g_imask		r6	/* r6_bank1 */
77#define k_g_imask	r6_bank	/* r6_bank1 */
78#define current		r7	/* r7_bank1 */
79
80#include <asm/entry-macros.S>
81
82/*
83 * Kernel mode register usage:
84 *	k0	scratch
85 *	k1	scratch
86 *	k2	scratch (Exception code)
87 *	k3	scratch (Return address)
88 *	k4	scratch
89 *	k5	reserved
90 *	k6	Global Interrupt Mask (0--15 << 4)
91 *	k7	CURRENT_THREAD_INFO (pointer to current thread info)
92 */
93
94!
95! TLB Miss / Initial Page write exception handling
96!			_and_
97! TLB hits, but the access violate the protection.
98! It can be valid access, such as stack grow and/or C-O-W.
99!
100!
101! Find the pmd/pte entry and loadtlb
102! If it's not found, cause address error (SEGV)
103!
104! Although this could be written in assembly language (and it'd be faster),
105! this first version depends *much* on C implementation.
106!
107
108#if defined(CONFIG_MMU)
109	.align	2
110ENTRY(tlb_miss_load)
111	bra	call_handle_tlbmiss
112	 mov	#0, r5
113
114	.align	2
115ENTRY(tlb_miss_store)
116	bra	call_handle_tlbmiss
117	 mov	#1, r5
118
119	.align	2
120ENTRY(initial_page_write)
121	bra	call_handle_tlbmiss
122	 mov	#2, r5
123
124	.align	2
125ENTRY(tlb_protection_violation_load)
126	bra	call_do_page_fault
127	 mov	#0, r5
128
129	.align	2
130ENTRY(tlb_protection_violation_store)
131	bra	call_do_page_fault
132	 mov	#1, r5
133
134call_handle_tlbmiss:
135	setup_frame_reg
136	mov.l	1f, r0
137	mov	r5, r8
138	mov.l	@r0, r6
139	mov.l	2f, r0
140	sts	pr, r10
141	jsr	@r0
142	 mov	r15, r4
143	!
144	tst	r0, r0
145	bf/s	0f
146	 lds	r10, pr
147	rts
148	 nop
1490:
150	mov	r8, r5
151call_do_page_fault:
152	mov.l	1f, r0
153	mov.l	@r0, r6
154
155	mov.l	3f, r0
156	mov.l	4f, r1
157	mov	r15, r4
158	jmp	@r0
159	 lds	r1, pr
160
161	.align 2
1621:	.long	MMU_TEA
1632:	.long	handle_tlbmiss
1643:	.long	do_page_fault
1654:	.long	ret_from_exception
166
167	.align	2
168ENTRY(address_error_load)
169	bra	call_dae
170	 mov	#0,r5		! writeaccess = 0
171
172	.align	2
173ENTRY(address_error_store)
174	bra	call_dae
175	 mov	#1,r5		! writeaccess = 1
176
177	.align	2
178call_dae:
179	mov.l	1f, r0
180	mov.l	@r0, r6		! address
181	mov.l	2f, r0
182	jmp	@r0
183	 mov	r15, r4		! regs
184
185	.align 2
1861:	.long	MMU_TEA
1872:	.long   do_address_error
188#endif /* CONFIG_MMU */
189
190#if defined(CONFIG_SH_STANDARD_BIOS)
191	/* Unwind the stack and jmp to the debug entry */
192ENTRY(sh_bios_handler)
193	mov.l	1f, r8
194	bsr	restore_regs
195	 nop
196
197	lds	k2, pr			! restore pr
198	mov	k4, r15
199	!
200	mov.l	2f, k0
201	mov.l	@k0, k0
202	jmp	@k0
203	 ldc	k3, ssr
204	.align	2
2051:	.long	0x300000f0
2062:	.long	gdb_vbr_vector
207#endif /* CONFIG_SH_STANDARD_BIOS */
208
209! restore_regs()
210! - restore r0, r1, r2, r3, r4, r5, r6, r7 from the stack
211! - switch bank
212! - restore r8, r9, r10, r11, r12, r13, r14, r15 from the stack
213! - restore spc, pr*, ssr, gbr, mach, macl, skip default tra
214! k2 returns original pr
215! k3 returns original sr
216! k4 returns original stack pointer
217! r8 passes SR bitmask, overwritten with restored data on return
218! r9 trashed
219! BL=0 on entry, on exit BL=1 (depending on r8).
220
221ENTRY(restore_regs)
222	mov.l	@r15+, r0
223	mov.l	@r15+, r1
224	mov.l	@r15+, r2
225	mov.l	@r15+, r3
226	mov.l	@r15+, r4
227	mov.l	@r15+, r5
228	mov.l	@r15+, r6
229	mov.l	@r15+, r7
230	!
231	stc	sr, r9
232	or	r8, r9
233	ldc	r9, sr
234	!
235	mov.l	@r15+, r8
236	mov.l	@r15+, r9
237	mov.l	@r15+, r10
238	mov.l	@r15+, r11
239	mov.l	@r15+, r12
240	mov.l	@r15+, r13
241	mov.l	@r15+, r14
242	mov.l	@r15+, k4		! original stack pointer
243	ldc.l	@r15+, spc
244	mov.l	@r15+, k2		! original PR
245	mov.l	@r15+, k3		! original SR
246	ldc.l	@r15+, gbr
247	lds.l	@r15+, mach
248	lds.l	@r15+, macl
249	rts
250	 add	#4, r15			! Skip syscall number
251
252restore_all:
253	mov.l	7f, r8
254	bsr	restore_regs
255	 nop
256
257	lds	k2, pr			! restore pr
258	!
259	! Calculate new SR value
260	mov	k3, k2			! original SR value
261	mov	#0xfffffff0, k1
262	extu.b	k1, k1
263	not	k1, k1
264	and	k1, k2			! Mask original SR value
265	!
266	mov	k3, k0			! Calculate IMASK-bits
267	shlr2	k0
268	and	#0x3c, k0
269	cmp/eq	#0x3c, k0
270	bt/s	6f
271	 shll2	k0
272	mov	g_imask, k0
273	!
2746:	or	k0, k2			! Set the IMASK-bits
275	ldc	k2, ssr
276	!
277	mov	k4, r15
278	rte
279	 nop
280
281	.align	2
2825:	.long	0x00001000	! DSP
2837:	.long	0x30000000
284
285! common exception handler
286#include "../../entry-common.S"
287
288! Exception Vector Base
289!
290!	Should be aligned page boundary.
291!
292	.balign 	4096,0,4096
293ENTRY(vbr_base)
294	.long	0
295!
296! 0x100: General exception vector
297!
298	.balign 	256,0,256
299general_exception:
300	bra	handle_exception
301	 sts	pr, k3		! save original pr value in k3
302
303! prepare_stack()
304! - roll back gRB
305! - switch to kernel stack
306! k0 returns original sp (after roll back)
307! k1 trashed
308! k2 trashed
309
310prepare_stack:
311#ifdef CONFIG_GUSA
312	! Check for roll back gRB (User and Kernel)
313	mov	r15, k0
314	shll	k0
315	bf/s	1f
316	 shll	k0
317	bf/s	1f
318	 stc	spc, k1
319	stc	r0_bank, k0
320	cmp/hs	k0, k1		! test k1 (saved PC) >= k0 (saved r0)
321	bt/s	2f
322	 stc	r1_bank, k1
323
324	add	#-2, k0
325	add	r15, k0
326	ldc	k0, spc		! PC = saved r0 + r15 - 2
3272:	mov	k1, r15		! SP = r1
3281:
329#endif
330	! Switch to kernel stack if needed
331	stc	ssr, k0		! Is it from kernel space?
332	shll	k0		! Check MD bit (bit30) by shifting it into...
333	shll	k0		!       ...the T bit
334	bt/s	1f		! It's a kernel to kernel transition.
335	 mov	r15, k0		! save original stack to k0
336	/* User space to kernel */
337	mov	#(THREAD_SIZE >> 10), k1
338	shll8	k1		! k1 := THREAD_SIZE
339	shll2	k1
340	add	current, k1
341	mov	k1, r15		! change to kernel stack
342	!
3431:
344	rts
345	 nop
346
347!
348! 0x400: Instruction and Data TLB miss exception vector
349!
350	.balign 	1024,0,1024
351tlb_miss:
352	sts	pr, k3		! save original pr value in k3
353
354handle_exception:
355	mova	exception_data, k0
356
357	! Setup stack and save DSP context (k0 contains original r15 on return)
358	bsr	prepare_stack
359	 PREF(k0)
360
361	! Save registers / Switch to bank 0
362	mov.l	5f, k2		! vector register address
363	mov.l	1f, k4		! SR bits to clear in k4
364	bsr	save_regs	! needs original pr value in k3
365	 mov.l	@k2, k2		! read out vector and keep in k2
366
367handle_exception_special:
368	! Setup return address and jump to exception handler
369	mov.l	7f, r9		! fetch return address
370	stc	r2_bank, r0	! k2 (vector)
371	mov.l	6f, r10
372	shlr2	r0
373	shlr	r0
374	mov.l	@(r0, r10), r10
375	jmp	@r10
376	 lds	r9, pr		! put return address in pr
377
378	.align	L1_CACHE_SHIFT
379
380! save_regs()
381! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack
382! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack
383! - switch bank
384! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
385! k0 contains original stack pointer*
386! k1 trashed
387! k3 passes original pr*
388! k4 passes SR bitmask
389! BL=1 on entry, on exit BL=0.
390
391ENTRY(save_regs)
392	mov	#-1, r1
393	mov.l	k1, @-r15	! set TRA (default: -1)
394	sts.l	macl, @-r15
395	sts.l	mach, @-r15
396	stc.l	gbr, @-r15
397	stc.l	ssr, @-r15
398	mov.l	k3, @-r15	! original pr in k3
399	stc.l	spc, @-r15
400
401	mov.l	k0, @-r15	! original stack pointer in k0
402	mov.l	r14, @-r15
403	mov.l	r13, @-r15
404	mov.l	r12, @-r15
405	mov.l	r11, @-r15
406	mov.l	r10, @-r15
407	mov.l	r9, @-r15
408	mov.l	r8, @-r15
409
410	mov.l	0f, k3		! SR bits to set in k3
411
412	! fall-through
413
414! save_low_regs()
415! - modify SR for bank switch
416! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
417! k3 passes bits to set in SR
418! k4 passes bits to clear in SR
419
420ENTRY(save_low_regs)
421	stc	sr, r8
422	or	k3, r8
423	and	k4, r8
424	ldc	r8, sr
425
426	mov.l	r7, @-r15
427	mov.l	r6, @-r15
428	mov.l	r5, @-r15
429	mov.l	r4, @-r15
430	mov.l	r3, @-r15
431	mov.l	r2, @-r15
432	mov.l	r1, @-r15
433	rts
434	 mov.l	r0, @-r15
435
436!
437! 0x600: Interrupt / NMI vector
438!
439	.balign 	512,0,512
440ENTRY(handle_interrupt)
441	sts	pr, k3		! save original pr value in k3
442	mova	exception_data, k0
443
444	! Setup stack and save DSP context (k0 contains original r15 on return)
445	bsr	prepare_stack
446	 PREF(k0)
447
448	! Save registers / Switch to bank 0
449	mov.l	1f, k4		! SR bits to clear in k4
450	bsr	save_regs	! needs original pr value in k3
451	 mov	#-1, k2		! default vector kept in k2
452
453	setup_frame_reg
454
455	stc	sr, r0	! get status register
456	shlr2	r0
457	and	#0x3c, r0
458	cmp/eq	#0x3c, r0
459	bf	9f
460	TRACE_IRQS_OFF
4619:
462
463	! Setup return address and jump to do_IRQ
464	mov.l	4f, r9		! fetch return address
465	lds	r9, pr		! put return address in pr
466	mov.l	2f, r4
467	mov.l	3f, r9
468	mov.l	@r4, r4		! pass INTEVT vector as arg0
469
470	shlr2	r4
471	shlr	r4
472	mov	r4, r0		! save vector->jmp table offset for later
473
474	shlr2	r4		! vector to IRQ# conversion
475	add	#-0x10, r4
476
477	cmp/pz	r4		! is it a valid IRQ?
478	bt	10f
479
480	/*
481	 * We got here as a result of taking the INTEVT path for something
482	 * that isn't a valid hard IRQ, therefore we bypass the do_IRQ()
483	 * path and special case the event dispatch instead.  This is the
484	 * expected path for the NMI (and any other brilliantly implemented
485	 * exception), which effectively wants regular exception dispatch
486	 * but is unfortunately reported through INTEVT rather than
487	 * EXPEVT.  Grr.
488	 */
489	mov.l	6f, r9
490	mov.l	@(r0, r9), r9
491	jmp	@r9
492	 mov	r15, r8		! trap handlers take saved regs in r8
493
49410:
495	jmp	@r9		! Off to do_IRQ() we go.
496	 mov	r15, r5		! pass saved registers as arg1
497
498ENTRY(exception_none)
499	rts
500	 nop
501
502	.align	L1_CACHE_SHIFT
503exception_data:
5040:	.long	0x000080f0	! FD=1, IMASK=15
5051:	.long	0xcfffffff	! RB=0, BL=0
5062:	.long	INTEVT
5073:	.long	do_IRQ
5084:	.long	ret_from_irq
5095:	.long	EXPEVT
5106:	.long	exception_handling_table
5117:	.long	ret_from_exception
512