xref: /linux/arch/sh/kernel/cpu/sh3/entry.S (revision ac6a0cf6716bb46813d0161024c66c2af66e53d1)
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#if defined(CONFIG_KGDB)
57NMI_VEC = 0x1c0			! Must catch early for debounce
58#endif
59
60/* Offsets to the stack */
61OFF_R0  =  0		/* Return value. New ABI also arg4 */
62OFF_R1  =  4     	/* New ABI: arg5 */
63OFF_R2  =  8     	/* New ABI: arg6 */
64OFF_R3  =  12     	/* New ABI: syscall_nr */
65OFF_R4  =  16     	/* New ABI: arg0 */
66OFF_R5  =  20     	/* New ABI: arg1 */
67OFF_R6  =  24     	/* New ABI: arg2 */
68OFF_R7  =  28     	/* New ABI: arg3 */
69OFF_SP	=  (15*4)
70OFF_PC  =  (16*4)
71OFF_SR	=  (16*4+8)
72OFF_TRA	=  (16*4+6*4)
73
74
75#define k0	r0
76#define k1	r1
77#define k2	r2
78#define k3	r3
79#define k4	r4
80
81#define g_imask		r6	/* r6_bank1 */
82#define k_g_imask	r6_bank	/* r6_bank1 */
83#define current		r7	/* r7_bank1 */
84
85#include <asm/entry-macros.S>
86
87/*
88 * Kernel mode register usage:
89 *	k0	scratch
90 *	k1	scratch
91 *	k2	scratch (Exception code)
92 *	k3	scratch (Return address)
93 *	k4	scratch
94 *	k5	reserved
95 *	k6	Global Interrupt Mask (0--15 << 4)
96 *	k7	CURRENT_THREAD_INFO (pointer to current thread info)
97 */
98
99!
100! TLB Miss / Initial Page write exception handling
101!			_and_
102! TLB hits, but the access violate the protection.
103! It can be valid access, such as stack grow and/or C-O-W.
104!
105!
106! Find the pmd/pte entry and loadtlb
107! If it's not found, cause address error (SEGV)
108!
109! Although this could be written in assembly language (and it'd be faster),
110! this first version depends *much* on C implementation.
111!
112
113#if defined(CONFIG_MMU)
114	.align	2
115ENTRY(tlb_miss_load)
116	bra	call_handle_tlbmiss
117	 mov	#0, r5
118
119	.align	2
120ENTRY(tlb_miss_store)
121	bra	call_handle_tlbmiss
122	 mov	#1, r5
123
124	.align	2
125ENTRY(initial_page_write)
126	bra	call_handle_tlbmiss
127	 mov	#2, r5
128
129	.align	2
130ENTRY(tlb_protection_violation_load)
131	bra	call_do_page_fault
132	 mov	#0, r5
133
134	.align	2
135ENTRY(tlb_protection_violation_store)
136	bra	call_do_page_fault
137	 mov	#1, r5
138
139call_handle_tlbmiss:
140	setup_frame_reg
141	mov.l	1f, r0
142	mov	r5, r8
143	mov.l	@r0, r6
144	mov.l	2f, r0
145	sts	pr, r10
146	jsr	@r0
147	 mov	r15, r4
148	!
149	tst	r0, r0
150	bf/s	0f
151	 lds	r10, pr
152	rts
153	 nop
1540:
155	mov	r8, r5
156call_do_page_fault:
157	mov.l	1f, r0
158	mov.l	@r0, r6
159
160	sti
161
162	mov.l	3f, r0
163	mov.l	4f, r1
164	mov	r15, r4
165	jmp	@r0
166	 lds	r1, pr
167
168	.align 2
1691:	.long	MMU_TEA
1702:	.long	handle_tlbmiss
1713:	.long	do_page_fault
1724:	.long	ret_from_exception
173
174	.align	2
175ENTRY(address_error_load)
176	bra	call_dae
177	 mov	#0,r5		! writeaccess = 0
178
179	.align	2
180ENTRY(address_error_store)
181	bra	call_dae
182	 mov	#1,r5		! writeaccess = 1
183
184	.align	2
185call_dae:
186	mov.l	1f, r0
187	mov.l	@r0, r6		! address
188	mov.l	2f, r0
189	jmp	@r0
190	 mov	r15, r4		! regs
191
192	.align 2
1931:	.long	MMU_TEA
1942:	.long   do_address_error
195#endif /* CONFIG_MMU */
196
197#if defined(CONFIG_SH_STANDARD_BIOS)
198	/* Unwind the stack and jmp to the debug entry */
199ENTRY(sh_bios_handler)
200	mov.l	1f, r8
201	bsr	restore_regs
202	 nop
203
204	lds	k2, pr			! restore pr
205	mov	k4, r15
206	!
207	mov.l	2f, k0
208	mov.l	@k0, k0
209	jmp	@k0
210	 ldc	k3, ssr
211	.align	2
2121:	.long	0x300000f0
2132:	.long	gdb_vbr_vector
214#endif /* CONFIG_SH_STANDARD_BIOS */
215
216! restore_regs()
217! - restore r0, r1, r2, r3, r4, r5, r6, r7 from the stack
218! - switch bank
219! - restore r8, r9, r10, r11, r12, r13, r14, r15 from the stack
220! - restore spc, pr*, ssr, gbr, mach, macl, skip default tra
221! k2 returns original pr
222! k3 returns original sr
223! k4 returns original stack pointer
224! r8 passes SR bitmask, overwritten with restored data on return
225! r9 trashed
226! BL=0 on entry, on exit BL=1 (depending on r8).
227
228ENTRY(restore_regs)
229	mov.l	@r15+, r0
230	mov.l	@r15+, r1
231	mov.l	@r15+, r2
232	mov.l	@r15+, r3
233	mov.l	@r15+, r4
234	mov.l	@r15+, r5
235	mov.l	@r15+, r6
236	mov.l	@r15+, r7
237	!
238	stc	sr, r9
239	or	r8, r9
240	ldc	r9, sr
241	!
242	mov.l	@r15+, r8
243	mov.l	@r15+, r9
244	mov.l	@r15+, r10
245	mov.l	@r15+, r11
246	mov.l	@r15+, r12
247	mov.l	@r15+, r13
248	mov.l	@r15+, r14
249	mov.l	@r15+, k4		! original stack pointer
250	ldc.l	@r15+, spc
251	mov.l	@r15+, k2		! original PR
252	mov.l	@r15+, k3		! original SR
253	ldc.l	@r15+, gbr
254	lds.l	@r15+, mach
255	lds.l	@r15+, macl
256	rts
257	 add	#4, r15			! Skip syscall number
258
259restore_all:
260	mov.l	7f, r8
261	bsr	restore_regs
262	 nop
263
264	lds	k2, pr			! restore pr
265	!
266	! Calculate new SR value
267	mov	k3, k2			! original SR value
268	mov	#0xfffffff0, k1
269	extu.b	k1, k1
270	not	k1, k1
271	and	k1, k2			! Mask original SR value
272	!
273	mov	k3, k0			! Calculate IMASK-bits
274	shlr2	k0
275	and	#0x3c, k0
276	cmp/eq	#0x3c, k0
277	bt/s	6f
278	 shll2	k0
279	mov	g_imask, k0
280	!
2816:	or	k0, k2			! Set the IMASK-bits
282	ldc	k2, ssr
283	!
284#if defined(CONFIG_KGDB)
285	! Clear in_nmi
286	mov.l	6f, k0
287	mov	#0, k1
288	mov.b	k1, @k0
289#endif
290	mov	k4, r15
291	rte
292	 nop
293
294	.align	2
2955:	.long	0x00001000	! DSP
296#ifdef CONFIG_KGDB
2976:	.long	in_nmi
298#endif
2997:	.long	0x30000000
300
301! common exception handler
302#include "../../entry-common.S"
303
304! Exception Vector Base
305!
306!	Should be aligned page boundary.
307!
308	.balign 	4096,0,4096
309ENTRY(vbr_base)
310	.long	0
311!
312! 0x100: General exception vector
313!
314	.balign 	256,0,256
315general_exception:
316#ifndef CONFIG_CPU_SUBTYPE_SHX3
317	bra	handle_exception
318	 sts	pr, k3		! save original pr value in k3
319#else
320	mov.l	1f, k4
321	mov.l	@k4, k4
322
323	! Is EXPEVT larger than 0x800?
324	mov	#0x8, k0
325	shll8	k0
326	cmp/hs	k0, k4
327	bf	0f
328
329	! then add 0x580 (k2 is 0xd80 or 0xda0)
330	mov	#0x58, k0
331	shll2	k0
332	shll2	k0
333	add	k0, k4
3340:
335	! Setup stack and save DSP context (k0 contains original r15 on return)
336	bsr	prepare_stack
337	 nop
338
339	! Save registers / Switch to bank 0
340	mov		k4, k2		! keep vector in k2
341	mov.l	1f, k4		! SR bits to clear in k4
342	bsr	save_regs	! needs original pr value in k3
343	 nop
344
345	bra	handle_exception_special
346	 nop
347
348	.align	2
3491:	.long	EXPEVT
350#endif
351
352! prepare_stack()
353! - roll back gRB
354! - switch to kernel stack
355! k0 returns original sp (after roll back)
356! k1 trashed
357! k2 trashed
358
359prepare_stack:
360#ifdef CONFIG_GUSA
361	! Check for roll back gRB (User and Kernel)
362	mov	r15, k0
363	shll	k0
364	bf/s	1f
365	 shll	k0
366	bf/s	1f
367	 stc	spc, k1
368	stc	r0_bank, k0
369	cmp/hs	k0, k1		! test k1 (saved PC) >= k0 (saved r0)
370	bt/s	2f
371	 stc	r1_bank, k1
372
373	add	#-2, k0
374	add	r15, k0
375	ldc	k0, spc		! PC = saved r0 + r15 - 2
3762:	mov	k1, r15		! SP = r1
3771:
378#endif
379	! Switch to kernel stack if needed
380	stc	ssr, k0		! Is it from kernel space?
381	shll	k0		! Check MD bit (bit30) by shifting it into...
382	shll	k0		!       ...the T bit
383	bt/s	1f		! It's a kernel to kernel transition.
384	 mov	r15, k0		! save original stack to k0
385	/* User space to kernel */
386	mov	#(THREAD_SIZE >> 10), k1
387	shll8	k1		! k1 := THREAD_SIZE
388	shll2	k1
389	add	current, k1
390	mov	k1, r15		! change to kernel stack
391	!
3921:
393	rts
394	 nop
395
396!
397! 0x400: Instruction and Data TLB miss exception vector
398!
399	.balign 	1024,0,1024
400tlb_miss:
401	sts	pr, k3		! save original pr value in k3
402
403handle_exception:
404	mova	exception_data, k0
405
406	! Setup stack and save DSP context (k0 contains original r15 on return)
407	bsr	prepare_stack
408	 PREF(k0)
409
410	! Save registers / Switch to bank 0
411	mov.l	5f, k2		! vector register address
412	mov.l	1f, k4		! SR bits to clear in k4
413	bsr	save_regs	! needs original pr value in k3
414	 mov.l	@k2, k2		! read out vector and keep in k2
415
416handle_exception_special:
417	! Setup return address and jump to exception handler
418	mov.l	7f, r9		! fetch return address
419	stc	r2_bank, r0	! k2 (vector)
420	mov.l	6f, r10
421	shlr2	r0
422	shlr	r0
423	mov.l	@(r0, r10), r10
424	jmp	@r10
425	 lds	r9, pr		! put return address in pr
426
427	.align	L1_CACHE_SHIFT
428
429! save_regs()
430! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack
431! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack
432! - switch bank
433! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
434! k0 contains original stack pointer*
435! k1 trashed
436! k3 passes original pr*
437! k4 passes SR bitmask
438! BL=1 on entry, on exit BL=0.
439
440ENTRY(save_regs)
441	mov	#-1, r1
442	mov.l	k1, @-r15	! set TRA (default: -1)
443	sts.l	macl, @-r15
444	sts.l	mach, @-r15
445	stc.l	gbr, @-r15
446	stc.l	ssr, @-r15
447	mov.l	k3, @-r15	! original pr in k3
448	stc.l	spc, @-r15
449
450	mov.l	k0, @-r15	! original stack pointer in k0
451	mov.l	r14, @-r15
452	mov.l	r13, @-r15
453	mov.l	r12, @-r15
454	mov.l	r11, @-r15
455	mov.l	r10, @-r15
456	mov.l	r9, @-r15
457	mov.l	r8, @-r15
458
459	mov.l	0f, k3		! SR bits to set in k3
460
461	! fall-through
462
463! save_low_regs()
464! - modify SR for bank switch
465! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
466! k3 passes bits to set in SR
467! k4 passes bits to clear in SR
468
469ENTRY(save_low_regs)
470	stc	sr, r8
471	or	k3, r8
472	and	k4, r8
473	ldc	r8, sr
474
475	mov.l	r7, @-r15
476	mov.l	r6, @-r15
477	mov.l	r5, @-r15
478	mov.l	r4, @-r15
479	mov.l	r3, @-r15
480	mov.l	r2, @-r15
481	mov.l	r1, @-r15
482	rts
483	 mov.l	r0, @-r15
484
485!
486! 0x600: Interrupt / NMI vector
487!
488	.balign 	512,0,512
489ENTRY(handle_interrupt)
490#if defined(CONFIG_KGDB)
491	mov.l	2f, k2
492	! Debounce (filter nested NMI)
493	mov.l	@k2, k0
494	mov.l	9f, k1
495	cmp/eq	k1, k0
496	bf	11f
497	mov.l	10f, k1
498	tas.b	@k1
499	bt	11f
500	rte
501	 nop
502	.align	2
5039:	.long	NMI_VEC
50410:	.long	in_nmi
50511:
506#endif /* defined(CONFIG_KGDB) */
507	sts	pr, k3		! save original pr value in k3
508	mova	exception_data, k0
509
510	! Setup stack and save DSP context (k0 contains original r15 on return)
511	bsr	prepare_stack
512	 PREF(k0)
513
514	! Save registers / Switch to bank 0
515	mov.l	1f, k4		! SR bits to clear in k4
516	bsr	save_regs	! needs original pr value in k3
517	 mov	#-1, k2		! default vector kept in k2
518
519	setup_frame_reg
520
521	stc	sr, r0	! get status register
522	shlr2	r0
523	and	#0x3c, r0
524	cmp/eq	#0x3c, r0
525	bf	9f
526	TRACE_IRQS_OFF
5279:
528
529	! Setup return address and jump to do_IRQ
530	mov.l	4f, r9		! fetch return address
531	lds	r9, pr		! put return address in pr
532	mov.l	2f, r4
533	mov.l	3f, r9
534	mov.l	@r4, r4		! pass INTEVT vector as arg0
535	jmp	@r9
536	 mov	r15, r5		! pass saved registers as arg1
537
538ENTRY(exception_none)
539	rts
540	 nop
541
542	.align	L1_CACHE_SHIFT
543exception_data:
5440:	.long	0x000080f0	! FD=1, IMASK=15
5451:	.long	0xcfffffff	! RB=0, BL=0
5462:	.long	INTEVT
5473:	.long	do_IRQ
5484:	.long	ret_from_irq
5495:	.long	EXPEVT
5506:	.long	exception_handling_table
5517:	.long	ret_from_exception
552