xref: /openbmc/linux/arch/sh/kernel/cpu/sh3/entry.S (revision 6ee73861)
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#ifndef CONFIG_CPU_SUBTYPE_SHX3
301	bra	handle_exception
302	 sts	pr, k3		! save original pr value in k3
303#else
304	mov.l	1f, k4
305	mov.l	@k4, k4
306
307	! Is EXPEVT larger than 0x800?
308	mov	#0x8, k0
309	shll8	k0
310	cmp/hs	k0, k4
311	bf	0f
312
313	! then add 0x580 (k2 is 0xd80 or 0xda0)
314	mov	#0x58, k0
315	shll2	k0
316	shll2	k0
317	add	k0, k4
3180:
319	! Setup stack and save DSP context (k0 contains original r15 on return)
320	bsr	prepare_stack
321	 nop
322
323	! Save registers / Switch to bank 0
324	mov		k4, k2		! keep vector in k2
325	mov.l	1f, k4		! SR bits to clear in k4
326	bsr	save_regs	! needs original pr value in k3
327	 nop
328
329	bra	handle_exception_special
330	 nop
331
332	.align	2
3331:	.long	EXPEVT
334#endif
335
336! prepare_stack()
337! - roll back gRB
338! - switch to kernel stack
339! k0 returns original sp (after roll back)
340! k1 trashed
341! k2 trashed
342
343prepare_stack:
344#ifdef CONFIG_GUSA
345	! Check for roll back gRB (User and Kernel)
346	mov	r15, k0
347	shll	k0
348	bf/s	1f
349	 shll	k0
350	bf/s	1f
351	 stc	spc, k1
352	stc	r0_bank, k0
353	cmp/hs	k0, k1		! test k1 (saved PC) >= k0 (saved r0)
354	bt/s	2f
355	 stc	r1_bank, k1
356
357	add	#-2, k0
358	add	r15, k0
359	ldc	k0, spc		! PC = saved r0 + r15 - 2
3602:	mov	k1, r15		! SP = r1
3611:
362#endif
363	! Switch to kernel stack if needed
364	stc	ssr, k0		! Is it from kernel space?
365	shll	k0		! Check MD bit (bit30) by shifting it into...
366	shll	k0		!       ...the T bit
367	bt/s	1f		! It's a kernel to kernel transition.
368	 mov	r15, k0		! save original stack to k0
369	/* User space to kernel */
370	mov	#(THREAD_SIZE >> 10), k1
371	shll8	k1		! k1 := THREAD_SIZE
372	shll2	k1
373	add	current, k1
374	mov	k1, r15		! change to kernel stack
375	!
3761:
377	rts
378	 nop
379
380!
381! 0x400: Instruction and Data TLB miss exception vector
382!
383	.balign 	1024,0,1024
384tlb_miss:
385	sts	pr, k3		! save original pr value in k3
386
387handle_exception:
388	mova	exception_data, k0
389
390	! Setup stack and save DSP context (k0 contains original r15 on return)
391	bsr	prepare_stack
392	 PREF(k0)
393
394	! Save registers / Switch to bank 0
395	mov.l	5f, k2		! vector register address
396	mov.l	1f, k4		! SR bits to clear in k4
397	bsr	save_regs	! needs original pr value in k3
398	 mov.l	@k2, k2		! read out vector and keep in k2
399
400handle_exception_special:
401	! Setup return address and jump to exception handler
402	mov.l	7f, r9		! fetch return address
403	stc	r2_bank, r0	! k2 (vector)
404	mov.l	6f, r10
405	shlr2	r0
406	shlr	r0
407	mov.l	@(r0, r10), r10
408	jmp	@r10
409	 lds	r9, pr		! put return address in pr
410
411	.align	L1_CACHE_SHIFT
412
413! save_regs()
414! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack
415! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack
416! - switch bank
417! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
418! k0 contains original stack pointer*
419! k1 trashed
420! k3 passes original pr*
421! k4 passes SR bitmask
422! BL=1 on entry, on exit BL=0.
423
424ENTRY(save_regs)
425	mov	#-1, r1
426	mov.l	k1, @-r15	! set TRA (default: -1)
427	sts.l	macl, @-r15
428	sts.l	mach, @-r15
429	stc.l	gbr, @-r15
430	stc.l	ssr, @-r15
431	mov.l	k3, @-r15	! original pr in k3
432	stc.l	spc, @-r15
433
434	mov.l	k0, @-r15	! original stack pointer in k0
435	mov.l	r14, @-r15
436	mov.l	r13, @-r15
437	mov.l	r12, @-r15
438	mov.l	r11, @-r15
439	mov.l	r10, @-r15
440	mov.l	r9, @-r15
441	mov.l	r8, @-r15
442
443	mov.l	0f, k3		! SR bits to set in k3
444
445	! fall-through
446
447! save_low_regs()
448! - modify SR for bank switch
449! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
450! k3 passes bits to set in SR
451! k4 passes bits to clear in SR
452
453ENTRY(save_low_regs)
454	stc	sr, r8
455	or	k3, r8
456	and	k4, r8
457	ldc	r8, sr
458
459	mov.l	r7, @-r15
460	mov.l	r6, @-r15
461	mov.l	r5, @-r15
462	mov.l	r4, @-r15
463	mov.l	r3, @-r15
464	mov.l	r2, @-r15
465	mov.l	r1, @-r15
466	rts
467	 mov.l	r0, @-r15
468
469!
470! 0x600: Interrupt / NMI vector
471!
472	.balign 	512,0,512
473ENTRY(handle_interrupt)
474	sts	pr, k3		! save original pr value in k3
475	mova	exception_data, k0
476
477	! Setup stack and save DSP context (k0 contains original r15 on return)
478	bsr	prepare_stack
479	 PREF(k0)
480
481	! Save registers / Switch to bank 0
482	mov.l	1f, k4		! SR bits to clear in k4
483	bsr	save_regs	! needs original pr value in k3
484	 mov	#-1, k2		! default vector kept in k2
485
486	setup_frame_reg
487
488	stc	sr, r0	! get status register
489	shlr2	r0
490	and	#0x3c, r0
491	cmp/eq	#0x3c, r0
492	bf	9f
493	TRACE_IRQS_OFF
4949:
495
496	! Setup return address and jump to do_IRQ
497	mov.l	4f, r9		! fetch return address
498	lds	r9, pr		! put return address in pr
499	mov.l	2f, r4
500	mov.l	3f, r9
501	mov.l	@r4, r4		! pass INTEVT vector as arg0
502
503	shlr2	r4
504	shlr	r4
505	mov	r4, r0		! save vector->jmp table offset for later
506
507	shlr2	r4		! vector to IRQ# conversion
508	add	#-0x10, r4
509
510	cmp/pz	r4		! is it a valid IRQ?
511	bt	10f
512
513	/*
514	 * We got here as a result of taking the INTEVT path for something
515	 * that isn't a valid hard IRQ, therefore we bypass the do_IRQ()
516	 * path and special case the event dispatch instead.  This is the
517	 * expected path for the NMI (and any other brilliantly implemented
518	 * exception), which effectively wants regular exception dispatch
519	 * but is unfortunately reported through INTEVT rather than
520	 * EXPEVT.  Grr.
521	 */
522	mov.l	6f, r9
523	mov.l	@(r0, r9), r9
524	jmp	@r9
525	 mov	r15, r8		! trap handlers take saved regs in r8
526
52710:
528	jmp	@r9		! Off to do_IRQ() we go.
529	 mov	r15, r5		! pass saved registers as arg1
530
531ENTRY(exception_none)
532	rts
533	 nop
534
535	.align	L1_CACHE_SHIFT
536exception_data:
5370:	.long	0x000080f0	! FD=1, IMASK=15
5381:	.long	0xcfffffff	! RB=0, BL=0
5392:	.long	INTEVT
5403:	.long	do_IRQ
5414:	.long	ret_from_irq
5425:	.long	EXPEVT
5436:	.long	exception_handling_table
5447:	.long	ret_from_exception
545