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