xref: /openbmc/linux/arch/sh/kernel/cpu/sh3/entry.S (revision e8e0929d)
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	sti
156
157	mov.l	3f, r0
158	mov.l	4f, r1
159	mov	r15, r4
160	jmp	@r0
161	 lds	r1, pr
162
163	.align 2
1641:	.long	MMU_TEA
1652:	.long	handle_tlbmiss
1663:	.long	do_page_fault
1674:	.long	ret_from_exception
168
169	.align	2
170ENTRY(address_error_load)
171	bra	call_dae
172	 mov	#0,r5		! writeaccess = 0
173
174	.align	2
175ENTRY(address_error_store)
176	bra	call_dae
177	 mov	#1,r5		! writeaccess = 1
178
179	.align	2
180call_dae:
181	mov.l	1f, r0
182	mov.l	@r0, r6		! address
183	mov.l	2f, r0
184	jmp	@r0
185	 mov	r15, r4		! regs
186
187	.align 2
1881:	.long	MMU_TEA
1892:	.long   do_address_error
190#endif /* CONFIG_MMU */
191
192#if defined(CONFIG_SH_STANDARD_BIOS)
193	/* Unwind the stack and jmp to the debug entry */
194ENTRY(sh_bios_handler)
195	mov.l	1f, r8
196	bsr	restore_regs
197	 nop
198
199	lds	k2, pr			! restore pr
200	mov	k4, r15
201	!
202	mov.l	2f, k0
203	mov.l	@k0, k0
204	jmp	@k0
205	 ldc	k3, ssr
206	.align	2
2071:	.long	0x300000f0
2082:	.long	gdb_vbr_vector
209#endif /* CONFIG_SH_STANDARD_BIOS */
210
211! restore_regs()
212! - restore r0, r1, r2, r3, r4, r5, r6, r7 from the stack
213! - switch bank
214! - restore r8, r9, r10, r11, r12, r13, r14, r15 from the stack
215! - restore spc, pr*, ssr, gbr, mach, macl, skip default tra
216! k2 returns original pr
217! k3 returns original sr
218! k4 returns original stack pointer
219! r8 passes SR bitmask, overwritten with restored data on return
220! r9 trashed
221! BL=0 on entry, on exit BL=1 (depending on r8).
222
223ENTRY(restore_regs)
224	mov.l	@r15+, r0
225	mov.l	@r15+, r1
226	mov.l	@r15+, r2
227	mov.l	@r15+, r3
228	mov.l	@r15+, r4
229	mov.l	@r15+, r5
230	mov.l	@r15+, r6
231	mov.l	@r15+, r7
232	!
233	stc	sr, r9
234	or	r8, r9
235	ldc	r9, sr
236	!
237	mov.l	@r15+, r8
238	mov.l	@r15+, r9
239	mov.l	@r15+, r10
240	mov.l	@r15+, r11
241	mov.l	@r15+, r12
242	mov.l	@r15+, r13
243	mov.l	@r15+, r14
244	mov.l	@r15+, k4		! original stack pointer
245	ldc.l	@r15+, spc
246	mov.l	@r15+, k2		! original PR
247	mov.l	@r15+, k3		! original SR
248	ldc.l	@r15+, gbr
249	lds.l	@r15+, mach
250	lds.l	@r15+, macl
251	rts
252	 add	#4, r15			! Skip syscall number
253
254restore_all:
255	mov.l	7f, r8
256	bsr	restore_regs
257	 nop
258
259	lds	k2, pr			! restore pr
260	!
261	! Calculate new SR value
262	mov	k3, k2			! original SR value
263	mov	#0xfffffff0, k1
264	extu.b	k1, k1
265	not	k1, k1
266	and	k1, k2			! Mask original SR value
267	!
268	mov	k3, k0			! Calculate IMASK-bits
269	shlr2	k0
270	and	#0x3c, k0
271	cmp/eq	#0x3c, k0
272	bt/s	6f
273	 shll2	k0
274	mov	g_imask, k0
275	!
2766:	or	k0, k2			! Set the IMASK-bits
277	ldc	k2, ssr
278	!
279	mov	k4, r15
280	rte
281	 nop
282
283	.align	2
2845:	.long	0x00001000	! DSP
2857:	.long	0x30000000
286
287! common exception handler
288#include "../../entry-common.S"
289
290! Exception Vector Base
291!
292!	Should be aligned page boundary.
293!
294	.balign 	4096,0,4096
295ENTRY(vbr_base)
296	.long	0
297!
298! 0x100: General exception vector
299!
300	.balign 	256,0,256
301general_exception:
302#ifndef CONFIG_CPU_SUBTYPE_SHX3
303	bra	handle_exception
304	 sts	pr, k3		! save original pr value in k3
305#else
306	mov.l	1f, k4
307	mov.l	@k4, k4
308
309	! Is EXPEVT larger than 0x800?
310	mov	#0x8, k0
311	shll8	k0
312	cmp/hs	k0, k4
313	bf	0f
314
315	! then add 0x580 (k2 is 0xd80 or 0xda0)
316	mov	#0x58, k0
317	shll2	k0
318	shll2	k0
319	add	k0, k4
3200:
321	! Setup stack and save DSP context (k0 contains original r15 on return)
322	bsr	prepare_stack
323	 nop
324
325	! Save registers / Switch to bank 0
326	mov		k4, k2		! keep vector in k2
327	mov.l	1f, k4		! SR bits to clear in k4
328	bsr	save_regs	! needs original pr value in k3
329	 nop
330
331	bra	handle_exception_special
332	 nop
333
334	.align	2
3351:	.long	EXPEVT
336#endif
337
338! prepare_stack()
339! - roll back gRB
340! - switch to kernel stack
341! k0 returns original sp (after roll back)
342! k1 trashed
343! k2 trashed
344
345prepare_stack:
346#ifdef CONFIG_GUSA
347	! Check for roll back gRB (User and Kernel)
348	mov	r15, k0
349	shll	k0
350	bf/s	1f
351	 shll	k0
352	bf/s	1f
353	 stc	spc, k1
354	stc	r0_bank, k0
355	cmp/hs	k0, k1		! test k1 (saved PC) >= k0 (saved r0)
356	bt/s	2f
357	 stc	r1_bank, k1
358
359	add	#-2, k0
360	add	r15, k0
361	ldc	k0, spc		! PC = saved r0 + r15 - 2
3622:	mov	k1, r15		! SP = r1
3631:
364#endif
365	! Switch to kernel stack if needed
366	stc	ssr, k0		! Is it from kernel space?
367	shll	k0		! Check MD bit (bit30) by shifting it into...
368	shll	k0		!       ...the T bit
369	bt/s	1f		! It's a kernel to kernel transition.
370	 mov	r15, k0		! save original stack to k0
371	/* User space to kernel */
372	mov	#(THREAD_SIZE >> 10), k1
373	shll8	k1		! k1 := THREAD_SIZE
374	shll2	k1
375	add	current, k1
376	mov	k1, r15		! change to kernel stack
377	!
3781:
379	rts
380	 nop
381
382!
383! 0x400: Instruction and Data TLB miss exception vector
384!
385	.balign 	1024,0,1024
386tlb_miss:
387	sts	pr, k3		! save original pr value in k3
388
389handle_exception:
390	mova	exception_data, k0
391
392	! Setup stack and save DSP context (k0 contains original r15 on return)
393	bsr	prepare_stack
394	 PREF(k0)
395
396	! Save registers / Switch to bank 0
397	mov.l	5f, k2		! vector register address
398	mov.l	1f, k4		! SR bits to clear in k4
399	bsr	save_regs	! needs original pr value in k3
400	 mov.l	@k2, k2		! read out vector and keep in k2
401
402handle_exception_special:
403	! Setup return address and jump to exception handler
404	mov.l	7f, r9		! fetch return address
405	stc	r2_bank, r0	! k2 (vector)
406	mov.l	6f, r10
407	shlr2	r0
408	shlr	r0
409	mov.l	@(r0, r10), r10
410	jmp	@r10
411	 lds	r9, pr		! put return address in pr
412
413	.align	L1_CACHE_SHIFT
414
415! save_regs()
416! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack
417! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack
418! - switch bank
419! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
420! k0 contains original stack pointer*
421! k1 trashed
422! k3 passes original pr*
423! k4 passes SR bitmask
424! BL=1 on entry, on exit BL=0.
425
426ENTRY(save_regs)
427	mov	#-1, r1
428	mov.l	k1, @-r15	! set TRA (default: -1)
429	sts.l	macl, @-r15
430	sts.l	mach, @-r15
431	stc.l	gbr, @-r15
432	stc.l	ssr, @-r15
433	mov.l	k3, @-r15	! original pr in k3
434	stc.l	spc, @-r15
435
436	mov.l	k0, @-r15	! original stack pointer in k0
437	mov.l	r14, @-r15
438	mov.l	r13, @-r15
439	mov.l	r12, @-r15
440	mov.l	r11, @-r15
441	mov.l	r10, @-r15
442	mov.l	r9, @-r15
443	mov.l	r8, @-r15
444
445	mov.l	0f, k3		! SR bits to set in k3
446
447	! fall-through
448
449! save_low_regs()
450! - modify SR for bank switch
451! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
452! k3 passes bits to set in SR
453! k4 passes bits to clear in SR
454
455ENTRY(save_low_regs)
456	stc	sr, r8
457	or	k3, r8
458	and	k4, r8
459	ldc	r8, sr
460
461	mov.l	r7, @-r15
462	mov.l	r6, @-r15
463	mov.l	r5, @-r15
464	mov.l	r4, @-r15
465	mov.l	r3, @-r15
466	mov.l	r2, @-r15
467	mov.l	r1, @-r15
468	rts
469	 mov.l	r0, @-r15
470
471!
472! 0x600: Interrupt / NMI vector
473!
474	.balign 	512,0,512
475ENTRY(handle_interrupt)
476	sts	pr, k3		! save original pr value in k3
477	mova	exception_data, k0
478
479	! Setup stack and save DSP context (k0 contains original r15 on return)
480	bsr	prepare_stack
481	 PREF(k0)
482
483	! Save registers / Switch to bank 0
484	mov.l	1f, k4		! SR bits to clear in k4
485	bsr	save_regs	! needs original pr value in k3
486	 mov	#-1, k2		! default vector kept in k2
487
488	setup_frame_reg
489
490	stc	sr, r0	! get status register
491	shlr2	r0
492	and	#0x3c, r0
493	cmp/eq	#0x3c, r0
494	bf	9f
495	TRACE_IRQS_OFF
4969:
497
498	! Setup return address and jump to do_IRQ
499	mov.l	4f, r9		! fetch return address
500	lds	r9, pr		! put return address in pr
501	mov.l	2f, r4
502	mov.l	3f, r9
503	mov.l	@r4, r4		! pass INTEVT vector as arg0
504
505	shlr2	r4
506	shlr	r4
507	mov	r4, r0		! save vector->jmp table offset for later
508
509	shlr2	r4		! vector to IRQ# conversion
510	add	#-0x10, r4
511
512	cmp/pz	r4		! is it a valid IRQ?
513	bt	10f
514
515	/*
516	 * We got here as a result of taking the INTEVT path for something
517	 * that isn't a valid hard IRQ, therefore we bypass the do_IRQ()
518	 * path and special case the event dispatch instead.  This is the
519	 * expected path for the NMI (and any other brilliantly implemented
520	 * exception), which effectively wants regular exception dispatch
521	 * but is unfortunately reported through INTEVT rather than
522	 * EXPEVT.  Grr.
523	 */
524	mov.l	6f, r9
525	mov.l	@(r0, r9), r9
526	jmp	@r9
527	 mov	r15, r8		! trap handlers take saved regs in r8
528
52910:
530	jmp	@r9		! Off to do_IRQ() we go.
531	 mov	r15, r5		! pass saved registers as arg1
532
533ENTRY(exception_none)
534	rts
535	 nop
536
537	.align	L1_CACHE_SHIFT
538exception_data:
5390:	.long	0x000080f0	! FD=1, IMASK=15
5401:	.long	0xcfffffff	! RB=0, BL=0
5412:	.long	INTEVT
5423:	.long	do_IRQ
5434:	.long	ret_from_irq
5445:	.long	EXPEVT
5456:	.long	exception_handling_table
5467:	.long	ret_from_exception
547