xref: /linux/arch/sh/kernel/cpu/sh3/entry.S (revision 606d099cdd1080bbb50ea50dc52d98252f8f10a1)
1/*
2 * arch/sh/kernel/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 <asm/cpu/mmu_context.h>
18#include <asm/pgtable.h>
19#include <asm/page.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_NMI)
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_dpf
117	 mov	#0, r5
118
119	.align	2
120ENTRY(tlb_miss_store)
121	bra	call_dpf
122	 mov	#1, r5
123
124	.align	2
125ENTRY(initial_page_write)
126	bra	call_dpf
127	 mov	#1, r5
128
129	.align	2
130ENTRY(tlb_protection_violation_load)
131	bra	call_dpf
132	 mov	#0, r5
133
134	.align	2
135ENTRY(tlb_protection_violation_store)
136	bra	call_dpf
137	 mov	#1, r5
138
139call_dpf:
140	mov.l	1f, r0
141 	mov.l	@r0, r6		! address
142	mov.l	3f, r0
143
144	jmp	@r0
145 	 mov	r15, r4		! regs
146
147	.align 2
1481:	.long	MMU_TEA
1493:	.long	do_page_fault
150
151	.align	2
152ENTRY(address_error_load)
153	bra	call_dae
154	 mov	#0,r5		! writeaccess = 0
155
156	.align	2
157ENTRY(address_error_store)
158	bra	call_dae
159	 mov	#1,r5		! writeaccess = 1
160
161	.align	2
162call_dae:
163	mov.l	1f, r0
164	mov.l	@r0, r6		! address
165	mov.l	2f, r0
166	jmp	@r0
167	 mov	r15, r4		! regs
168
169	.align 2
1701:	.long	MMU_TEA
1712:	.long   do_address_error
172#endif /* CONFIG_MMU */
173
174#if defined(CONFIG_SH_STANDARD_BIOS)
175	/* Unwind the stack and jmp to the debug entry */
176debug_kernel_fw:
177	mov.l	@r15+, r0
178	mov.l	@r15+, r1
179	mov.l	@r15+, r2
180	mov.l	@r15+, r3
181	mov.l	@r15+, r4
182	mov.l	@r15+, r5
183	mov.l	@r15+, r6
184	mov.l	@r15+, r7
185	stc	sr, r8
186	mov.l	1f, r9			! BL =1, RB=1, IMASK=0x0F
187	or	r9, r8
188	ldc	r8, sr			! here, change the register bank
189	mov.l	@r15+, r8
190	mov.l	@r15+, r9
191	mov.l	@r15+, r10
192	mov.l	@r15+, r11
193	mov.l	@r15+, r12
194	mov.l	@r15+, r13
195	mov.l	@r15+, r14
196	mov.l	@r15+, k0
197	ldc.l	@r15+, spc
198	lds.l	@r15+, pr
199	mov.l	@r15+, k1
200	ldc.l	@r15+, gbr
201	lds.l	@r15+, mach
202	lds.l	@r15+, macl
203	mov	k0, r15
204	!
205	mov.l	2f, k0
206	mov.l	@k0, k0
207	jmp	@k0
208	 ldc	k1, ssr
209	.align	2
2101:	.long	0x300000f0
2112:	.long	gdb_vbr_vector
212#endif /* CONFIG_SH_STANDARD_BIOS */
213
214restore_all:
215	mov.l	@r15+, r0
216	mov.l	@r15+, r1
217	mov.l	@r15+, r2
218	mov.l	@r15+, r3
219	mov.l	@r15+, r4
220	mov.l	@r15+, r5
221	mov.l	@r15+, r6
222	mov.l	@r15+, r7
223	!
224	stc	sr, r8
225	mov.l	7f, r9
226	or	r9, r8			! BL =1, RB=1
227	ldc	r8, sr			! here, change the register bank
228	!
229	mov.l	@r15+, r8
230	mov.l	@r15+, r9
231	mov.l	@r15+, r10
232	mov.l	@r15+, r11
233	mov.l	@r15+, r12
234	mov.l	@r15+, r13
235	mov.l	@r15+, r14
236	mov.l	@r15+, k4		! original stack pointer
237	ldc.l	@r15+, spc
238	lds.l	@r15+, pr
239	mov.l	@r15+, k3		! original SR
240	ldc.l	@r15+, gbr
241	lds.l	@r15+, mach
242	lds.l	@r15+, macl
243	add	#4, r15			! Skip syscall number
244	!
245#ifdef CONFIG_SH_DSP
246	mov.l	@r15+, k0		! DSP mode marker
247	mov.l	5f, k1
248	cmp/eq	k0, k1			! Do we have a DSP stack frame?
249	bf	skip_restore
250
251	stc	sr, k0			! Enable CPU DSP mode
252	or	k1, k0			! (within kernel it may be disabled)
253	ldc	k0, sr
254	mov	r2, k0			! Backup r2
255
256	! Restore DSP registers from stack
257	mov	r15, r2
258	movs.l	@r2+, a1
259	movs.l	@r2+, a0g
260	movs.l	@r2+, a1g
261	movs.l	@r2+, m0
262	movs.l	@r2+, m1
263	mov	r2, r15
264
265	lds.l	@r15+, a0
266	lds.l	@r15+, x0
267	lds.l	@r15+, x1
268	lds.l	@r15+, y0
269	lds.l	@r15+, y1
270	lds.l	@r15+, dsr
271	ldc.l	@r15+, rs
272	ldc.l	@r15+, re
273	ldc.l	@r15+, mod
274
275	mov	k0, r2			! Restore r2
276skip_restore:
277#endif
278	!
279	! Calculate new SR value
280	mov	k3, k2			! original SR value
281	mov	#0xf0, k1
282	extu.b	k1, k1
283	not	k1, k1
284	and	k1, k2			! Mask orignal SR value
285	!
286	mov	k3, k0			! Calculate IMASK-bits
287	shlr2	k0
288	and	#0x3c, k0
289	cmp/eq	#0x3c, k0
290	bt/s	6f
291	 shll2	k0
292	mov	g_imask, k0
293	!
2946:	or	k0, k2			! Set the IMASK-bits
295	ldc	k2, ssr
296	!
297#if defined(CONFIG_KGDB_NMI)
298	! Clear in_nmi
299	mov.l	6f, k0
300	mov	#0, k1
301	mov.b	k1, @k0
302#endif
303	mov.l	@r15+, k2		! restore EXPEVT
304	mov	k4, r15
305	rte
306	 nop
307
308	.align	2
3095:	.long	0x00001000	! DSP
3107:	.long	0x30000000
311
312! common exception handler
313#include "../../entry-common.S"
314
315! Exception Vector Base
316!
317!	Should be aligned page boundary.
318!
319	.balign 	4096,0,4096
320ENTRY(vbr_base)
321	.long	0
322!
323	.balign 	256,0,256
324general_exception:
325	mov.l	1f, k2
326	mov.l	2f, k3
327	bra	handle_exception
328	 mov.l	@k2, k2
329	.align	2
3301:	.long	EXPEVT
3312:	.long	ret_from_exception
332!
333!
334
335/* This code makes some assumptions to improve performance.
336 * Make sure they are stil true. */
337#if PTRS_PER_PGD != PTRS_PER_PTE
338#error PGD and PTE sizes don't match
339#endif
340
341/* gas doesn't flag impossible values for mov #immediate as an error */
342#if (_PAGE_PRESENT >> 2) > 0x7f
343#error cannot load PAGE_PRESENT as an immediate
344#endif
345#if _PAGE_DIRTY > 0x7f
346#error cannot load PAGE_DIRTY as an immediate
347#endif
348#if (_PAGE_PRESENT << 2) != _PAGE_ACCESSED
349#error cannot derive PAGE_ACCESSED from PAGE_PRESENT
350#endif
351
352#if defined(CONFIG_CPU_SH4)
353#define ldmmupteh(r)	mov.l	8f, r
354#else
355#define ldmmupteh(r)	mov	#MMU_PTEH, r
356#endif
357
358	.balign 	1024,0,1024
359tlb_miss:
360#ifdef COUNT_EXCEPTIONS
361	! Increment the counts
362	mov.l	9f, k1
363	mov.l	@k1, k2
364	add	#1, k2
365	mov.l	k2, @k1
366#endif
367
368	! k0 scratch
369	! k1 pgd and pte pointers
370	! k2 faulting address
371	! k3 pgd and pte index masks
372	! k4 shift
373
374	! Load up the pgd entry (k1)
375
376	ldmmupteh(k0)			!  9 LS (latency=2)	MMU_PTEH
377
378	mov.w	4f, k3			!  8 LS (latency=2)	(PTRS_PER_PGD-1) << 2
379	mov	#-(PGDIR_SHIFT-2), k4	!  6 EX
380
381	mov.l	@(MMU_TEA-MMU_PTEH,k0), k2	! 18 LS (latency=2)
382
383	mov.l	@(MMU_TTB-MMU_PTEH,k0), k1	! 18 LS (latency=2)
384
385	mov	k2, k0			!   5 MT (latency=0)
386	shld	k4, k0			!  99 EX
387
388	and	k3, k0			!  78 EX
389
390	mov.l	@(k0, k1), k1		!  21 LS (latency=2)
391	mov	#-(PAGE_SHIFT-2), k4	!   6 EX
392
393	! Load up the pte entry (k2)
394
395	mov	k2, k0			!   5 MT (latency=0)
396	shld	k4, k0			!  99 EX
397
398	tst	k1, k1			!  86 MT
399
400	bt	20f			! 110 BR
401
402	and	k3, k0			!  78 EX
403	mov.w	5f, k4			!   8 LS (latency=2)	_PAGE_PRESENT
404
405	mov.l	@(k0, k1), k2		!  21 LS (latency=2)
406	add	k0, k1			!  49 EX
407
408#ifdef CONFIG_CPU_HAS_PTEA
409	! Test the entry for present and _PAGE_ACCESSED
410
411	mov	#-28, k3		!   6 EX
412	mov	k2, k0			!   5 MT (latency=0)
413
414	tst	k4, k2			!  68 MT
415	shld	k3, k0			!  99 EX
416
417	bt	20f			! 110 BR
418
419	! Set PTEA register
420	! MMU_PTEA = ((pteval >> 28) & 0xe) | (pteval & 0x1)
421	!
422	! k0=pte>>28, k1=pte*, k2=pte, k3=<unused>, k4=_PAGE_PRESENT
423
424	and	#0xe, k0		!  79 EX
425
426	mov	k0, k3			!   5 MT (latency=0)
427	mov	k2, k0			!   5 MT (latency=0)
428
429	and	#1, k0			!  79 EX
430
431	or	k0, k3			!  82 EX
432
433	ldmmupteh(k0)			!   9 LS (latency=2)
434	shll2	k4			! 101 EX		_PAGE_ACCESSED
435
436	tst	k4, k2			!  68 MT
437
438	mov.l	k3, @(MMU_PTEA-MMU_PTEH,k0)	! 27 LS
439
440	mov.l	7f, k3			!   9 LS (latency=2)	_PAGE_FLAGS_HARDWARE_MASK
441
442	! k0=MMU_PTEH, k1=pte*, k2=pte, k3=_PAGE_FLAGS_HARDWARE, k4=_PAGE_ACCESSED
443#else
444
445	! Test the entry for present and _PAGE_ACCESSED
446
447	mov.l	7f, k3			!   9 LS (latency=2)	_PAGE_FLAGS_HARDWARE_MASK
448	tst	k4, k2			!  68 MT
449
450	shll2	k4			! 101 EX		_PAGE_ACCESSED
451	ldmmupteh(k0)			!   9 LS (latency=2)
452
453	bt	20f			! 110 BR
454	tst	k4, k2			!  68 MT
455
456	! k0=MMU_PTEH, k1=pte*, k2=pte, k3=_PAGE_FLAGS_HARDWARE, k4=_PAGE_ACCESSED
457
458#endif
459
460	! Set up the entry
461
462	and	k2, k3			!  78 EX
463	bt/s	10f			! 108 BR
464
465	 mov.l	k3, @(MMU_PTEL-MMU_PTEH,k0)	! 27 LS
466
467	ldtlb				! 128 CO
468
469	! At least one instruction between ldtlb and rte
470	nop				! 119 NOP
471
472	rte				! 126 CO
473
474	 nop				! 119 NOP
475
476
47710:	or	k4, k2			!  82 EX
478
479	ldtlb				! 128 CO
480
481	! At least one instruction between ldtlb and rte
482	mov.l	k2, @k1			!  27 LS
483
484	rte				! 126 CO
485
486	! Note we cannot execute mov here, because it is executed after
487	! restoring SSR, so would be executed in user space.
488	 nop				! 119 NOP
489
490
491	.align 5
492	! Once cache line if possible...
4931:	.long	swapper_pg_dir
4944:	.short	(PTRS_PER_PGD-1) << 2
4955:	.short	_PAGE_PRESENT
4967:	.long	_PAGE_FLAGS_HARDWARE_MASK
4978:	.long	MMU_PTEH
498#ifdef COUNT_EXCEPTIONS
4999:	.long	exception_count_miss
500#endif
501
502	! Either pgd or pte not present
50320:	mov.l	1f, k2
504	mov.l	4f, k3
505	bra	handle_exception
506	 mov.l	@k2, k2
507!
508	.balign 	512,0,512
509interrupt:
510	mov.l	2f, k2
511	mov.l	3f, k3
512#if defined(CONFIG_KGDB_NMI)
513	! Debounce (filter nested NMI)
514	mov.l	@k2, k0
515	mov.l	5f, k1
516	cmp/eq	k1, k0
517	bf	0f
518	mov.l	6f, k1
519	tas.b	@k1
520	bt	0f
521	rte
522	 nop
523	.align	2
5245:	.long	NMI_VEC
5256:	.long	in_nmi
5260:
527#endif /* defined(CONFIG_KGDB_NMI) */
528	bra	handle_exception
529	 mov	#-1, k2		! interrupt exception marker
530
531	.align	2
5321:	.long	EXPEVT
5332:	.long	INTEVT
5343:	.long	ret_from_irq
5354:	.long	ret_from_exception
536
537!
538!
539	.align	2
540ENTRY(handle_exception)
541	! Using k0, k1 for scratch registers (r0_bank1, r1_bank),
542	! save all registers onto stack.
543	!
544	stc	ssr, k0		! Is it from kernel space?
545	shll	k0		! Check MD bit (bit30) by shifting it into...
546	shll	k0		!       ...the T bit
547	bt/s	1f		! It's a kernel to kernel transition.
548	 mov	r15, k0		! save original stack to k0
549	/* User space to kernel */
550	mov	#(THREAD_SIZE >> 10), k1
551	shll8	k1		! k1 := THREAD_SIZE
552	shll2	k1
553	add	current, k1
554	mov	k1, r15		! change to kernel stack
555	!
5561:	mov.l	2f, k1
557	!
558#ifdef CONFIG_SH_DSP
559	mov.l	r2, @-r15		! Save r2, we need another reg
560	stc	sr, k4
561	mov.l	1f, r2
562	tst	r2, k4			! Check if in DSP mode
563	mov.l	@r15+, r2		! Restore r2 now
564	bt/s	skip_save
565	 mov	#0, k4			! Set marker for no stack frame
566
567	mov	r2, k4			! Backup r2 (in k4) for later
568
569	! Save DSP registers on stack
570	stc.l	mod, @-r15
571	stc.l	re, @-r15
572	stc.l	rs, @-r15
573	sts.l	dsr, @-r15
574	sts.l	y1, @-r15
575	sts.l	y0, @-r15
576	sts.l	x1, @-r15
577	sts.l	x0, @-r15
578	sts.l	a0, @-r15
579
580	! GAS is broken, does not generate correct "movs.l Ds,@-As" instr.
581
582	! FIXME: Make sure that this is still the case with newer toolchains,
583	! as we're not at all interested in supporting ancient toolchains at
584	! this point. -- PFM.
585
586	mov	r15, r2
587	.word	0xf653			! movs.l	a1, @-r2
588	.word	0xf6f3			! movs.l	a0g, @-r2
589	.word	0xf6d3			! movs.l	a1g, @-r2
590	.word	0xf6c3			! movs.l	m0, @-r2
591	.word	0xf6e3			! movs.l	m1, @-r2
592	mov	r2, r15
593
594	mov	k4, r2			! Restore r2
595	mov.l	1f, k4			! Force DSP stack frame
596skip_save:
597	mov.l	k4, @-r15		! Push DSP mode marker onto stack
598#endif
599	! Save the user registers on the stack.
600	mov.l	k2, @-r15	! EXPEVT
601
602	mov	#-1, k4
603	mov.l	k4, @-r15	! set TRA (default: -1)
604	!
605	sts.l	macl, @-r15
606	sts.l	mach, @-r15
607	stc.l	gbr, @-r15
608	stc.l	ssr, @-r15
609	sts.l	pr, @-r15
610	stc.l	spc, @-r15
611	!
612	lds	k3, pr		! Set the return address to pr
613	!
614	mov.l	k0, @-r15	! save orignal stack
615	mov.l	r14, @-r15
616	mov.l	r13, @-r15
617	mov.l	r12, @-r15
618	mov.l	r11, @-r15
619	mov.l	r10, @-r15
620	mov.l	r9, @-r15
621	mov.l	r8, @-r15
622	!
623	stc	sr, r8		! Back to normal register bank, and
624	or	k1, r8		! Block all interrupts
625	mov.l	3f, k1
626	and	k1, r8		! ...
627	ldc	r8, sr		! ...changed here.
628	!
629	mov.l	r7, @-r15
630	mov.l	r6, @-r15
631	mov.l	r5, @-r15
632	mov.l	r4, @-r15
633	mov.l	r3, @-r15
634	mov.l	r2, @-r15
635	mov.l	r1, @-r15
636	mov.l	r0, @-r15
637
638	/*
639	 * This gets a bit tricky.. in the INTEVT case we don't want to use
640	 * the VBR offset as a destination in the jump call table, since all
641	 * of the destinations are the same. In this case, (interrupt) sets
642	 * a marker in r2 (now r2_bank since SR.RB changed), which we check
643	 * to determine the exception type. For all other exceptions, we
644	 * forcibly read EXPEVT from memory and fix up the jump address, in
645	 * the interrupt exception case we jump to do_IRQ() and defer the
646	 * INTEVT read until there. As a bonus, we can also clean up the SR.RB
647	 * checks that do_IRQ() was doing..
648	 */
649	stc	r2_bank, r8
650	cmp/pz	r8
651	bf	interrupt_exception
652	shlr2	r8
653	shlr	r8
654
655#ifdef COUNT_EXCEPTIONS
656	mov.l	5f, r9
657	add	r8, r9
658	mov.l	@r9, r10
659	add	#1, r10
660	mov.l	r10, @r9
661#endif
662
663	mov.l	4f, r9
664	add	r8, r9
665	mov.l	@r9, r9
666	jmp	@r9
667	 nop
668	rts
669	 nop
670
671	.align	2
6721:	.long	0x00001000	! DSP=1
6732:	.long	0x000080f0	! FD=1, IMASK=15
6743:	.long	0xcfffffff	! RB=0, BL=0
6754:	.long	exception_handling_table
676#ifdef COUNT_EXCEPTIONS
6775:	.long	exception_count_table
678#endif
679
680interrupt_exception:
681	mov.l	1f, r9
682	jmp	@r9
683	 nop
684	rts
685	 nop
686
687	.align 2
6881:	.long	do_IRQ
689
690	.align	2
691ENTRY(exception_none)
692	rts
693	 nop
694