xref: /linux/arch/sh/kernel/entry-common.S (revision 606d099cdd1080bbb50ea50dc52d98252f8f10a1)
1/* $Id: entry.S,v 1.37 2004/06/11 13:02:46 doyu Exp $
2 *
3 *  linux/arch/sh/entry.S
4 *
5 *  Copyright (C) 1999, 2000, 2002  Niibe Yutaka
6 *  Copyright (C) 2003  Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License.  See the file "COPYING" in the main directory of this archive
10 * for more details.
11 *
12 */
13
14! NOTE:
15! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
16! to be jumped is too far, but it causes illegal slot exception.
17
18/*
19 * entry.S contains the system-call and fault low-level handling routines.
20 * This also contains the timer-interrupt handler, as well as all interrupts
21 * and faults that can result in a task-switch.
22 *
23 * NOTE: This code handles signal-recognition, which happens every time
24 * after a timer-interrupt and after each system call.
25 *
26 * NOTE: This code uses a convention that instructions in the delay slot
27 * of a transfer-control instruction are indented by an extra space, thus:
28 *
29 *    jmp	@k0	    ! control-transfer instruction
30 *     ldc	k1, ssr     ! delay slot
31 *
32 * Stack layout in 'ret_from_syscall':
33 * 	ptrace needs to have all regs on the stack.
34 *	if the order here is changed, it needs to be
35 *	updated in ptrace.c and ptrace.h
36 *
37 *	r0
38 *      ...
39 *	r15 = stack pointer
40 *	spc
41 *	pr
42 *	ssr
43 *	gbr
44 *	mach
45 *	macl
46 *	syscall #
47 *
48 */
49
50#if defined(CONFIG_PREEMPT)
51#  define preempt_stop()	cli
52#else
53#  define preempt_stop()
54#  define resume_kernel		__restore_all
55#endif
56
57#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
58! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present.
59! If both are configured, handle the debug traps (breakpoints) in SW,
60! but still allow BIOS traps to FW.
61
62	.align	2
63debug_kernel:
64#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB)
65	/* Force BIOS call to FW (debug_trap put TRA in r8) */
66	mov	r8,r0
67	shlr2	r0
68	cmp/eq	#0x3f,r0
69	bt	debug_kernel_fw
70#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */
71
72debug_enter:
73#if defined(CONFIG_SH_KGDB)
74	/* Jump to kgdb, pass stacked regs as arg */
75debug_kernel_sw:
76	mov.l	3f, r0
77	jmp	@r0
78	 mov	r15, r4
79	.align	2
803:	.long	kgdb_handle_exception
81#endif /* CONFIG_SH_KGDB */
82
83#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
84
85
86	.align	2
87debug_trap:
88#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
89	mov	#OFF_SR, r0
90	mov.l	@(r0,r15), r0		! get status register
91	shll	r0
92	shll	r0			! kernel space?
93	bt/s	debug_kernel
94#endif
95	 mov.l	@r15, r0		! Restore R0 value
96	mov.l	1f, r8
97	jmp	@r8
98	 nop
99
100	.align	2
101ENTRY(exception_error)
102	!
103#ifdef CONFIG_TRACE_IRQFLAGS
104	mov.l	3f, r0
105	jsr	@r0
106	 nop
107#endif
108	sti
109	mov.l	2f, r0
110	jmp	@r0
111	 nop
112
113!
114	.align	2
1151:	.long	break_point_trap_software
1162:	.long	do_exception_error
117#ifdef CONFIG_TRACE_IRQFLAGS
1183:	.long	trace_hardirqs_on
119#endif
120
121	.align	2
122ret_from_exception:
123	preempt_stop()
124#ifdef CONFIG_TRACE_IRQFLAGS
125	mov.l	4f, r0
126	jsr	@r0
127	 nop
128#endif
129ENTRY(ret_from_irq)
130	!
131	mov	#OFF_SR, r0
132	mov.l	@(r0,r15), r0	! get status register
133	shll	r0
134	shll	r0		! kernel space?
135	get_current_thread_info r8, r0
136	bt	resume_kernel	! Yes, it's from kernel, go back soon
137
138#ifdef CONFIG_PREEMPT
139	bra	resume_userspace
140	 nop
141ENTRY(resume_kernel)
142	mov.l	@(TI_PRE_COUNT,r8), r0	! current_thread_info->preempt_count
143	tst	r0, r0
144	bf	noresched
145need_resched:
146	mov.l	@(TI_FLAGS,r8), r0	! current_thread_info->flags
147	tst	#_TIF_NEED_RESCHED, r0	! need_resched set?
148	bt	noresched
149
150	mov	#OFF_SR, r0
151	mov.l	@(r0,r15), r0		! get status register
152	and	#0xf0, r0		! interrupts off (exception path)?
153	cmp/eq	#0xf0, r0
154	bt	noresched
155
156	mov.l	1f, r0
157	mov.l	r0, @(TI_PRE_COUNT,r8)
158
159#ifdef CONFIG_TRACE_IRQFLAGS
160	mov.l	3f, r0
161	jsr	@r0
162	 nop
163#endif
164	sti
165	mov.l	2f, r0
166	jsr	@r0
167	 nop
168	mov	#0, r0
169	mov.l	r0, @(TI_PRE_COUNT,r8)
170	cli
171#ifdef CONFIG_TRACE_IRQFLAGS
172	mov.l	4f, r0
173	jsr	@r0
174	 nop
175#endif
176
177	bra	need_resched
178	 nop
179
180noresched:
181	bra	__restore_all
182	 nop
183
184	.align 2
1851:	.long	PREEMPT_ACTIVE
1862:	.long	schedule
187#ifdef CONFIG_TRACE_IRQFLAGS
1883:	.long	trace_hardirqs_on
1894:	.long	trace_hardirqs_off
190#endif
191#endif
192
193ENTRY(resume_userspace)
194	! r8: current_thread_info
195	cli
196#ifdef CONFIG_TRACE_IRQFLAGS
197	mov.l	5f, r0
198	jsr	@r0
199	 nop
200#endif
201	mov.l	@(TI_FLAGS,r8), r0		! current_thread_info->flags
202	tst	#_TIF_WORK_MASK, r0
203	bt/s	__restore_all
204	 tst	#_TIF_NEED_RESCHED, r0
205
206	.align	2
207work_pending:
208	! r0: current_thread_info->flags
209	! r8: current_thread_info
210	! t:  result of "tst	#_TIF_NEED_RESCHED, r0"
211	bf/s	work_resched
212	 tst	#(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0
213work_notifysig:
214	bt/s	__restore_all
215	 mov	r15, r4
216	mov	r12, r5		! set arg1(save_r0)
217	mov	r0, r6
218	mov.l	2f, r1
219	mov.l	3f, r0
220	jmp	@r1
221	 lds	r0, pr
222work_resched:
223#ifndef CONFIG_PREEMPT
224	! gUSA handling
225	mov.l	@(OFF_SP,r15), r0	! get user space stack pointer
226	mov	r0, r1
227	shll	r0
228	bf/s	1f
229	 shll	r0
230	bf/s	1f
231	 mov	#OFF_PC, r0
232	! 				  SP >= 0xc0000000 : gUSA mark
233	mov.l	@(r0,r15), r2		! get user space PC (program counter)
234	mov.l	@(OFF_R0,r15), r3	! end point
235	cmp/hs	r3, r2			! r2 >= r3?
236	bt	1f
237	add	r3, r1			! rewind point #2
238	mov.l	r1, @(r0,r15)		! reset PC to rewind point #2
239	!
2401:
241#endif
242	mov.l	1f, r1
243	jsr	@r1				! schedule
244	 nop
245	cli
246#ifdef CONFIG_TRACE_IRQFLAGS
247	mov.l	5f, r0
248	jsr	@r0
249	 nop
250#endif
251	!
252	mov.l	@(TI_FLAGS,r8), r0		! current_thread_info->flags
253	tst	#_TIF_WORK_MASK, r0
254	bt	__restore_all
255	bra	work_pending
256	 tst	#_TIF_NEED_RESCHED, r0
257
258	.align	2
2591:	.long	schedule
2602:	.long	do_notify_resume
2613:	.long	restore_all
262#ifdef CONFIG_TRACE_IRQFLAGS
2634:	.long	trace_hardirqs_on
2645:	.long	trace_hardirqs_off
265#endif
266
267	.align	2
268syscall_exit_work:
269	! r0: current_thread_info->flags
270	! r8: current_thread_info
271	tst	#_TIF_SYSCALL_TRACE, r0
272	bt/s	work_pending
273	 tst	#_TIF_NEED_RESCHED, r0
274#ifdef CONFIG_TRACE_IRQFLAGS
275	mov.l	5f, r0
276	jsr	@r0
277	 nop
278#endif
279	sti
280	! XXX setup arguments...
281	mov.l	4f, r0			! do_syscall_trace
282	jsr	@r0
283	 nop
284	bra	resume_userspace
285	 nop
286
287	.align	2
288syscall_trace_entry:
289	!                     	Yes it is traced.
290	! XXX setup arguments...
291	mov.l	4f, r11		! Call do_syscall_trace which notifies
292	jsr	@r11	    	! superior (will chomp R[0-7])
293	 nop
294	!			Reload R0-R4 from kernel stack, where the
295	!   	    	    	parent may have modified them using
296	!   	    	    	ptrace(POKEUSR).  (Note that R0-R2 are
297	!   	    	    	used by the system call handler directly
298	!   	    	    	from the kernel stack anyway, so don't need
299	!   	    	    	to be reloaded here.)  This allows the parent
300	!   	    	    	to rewrite system calls and args on the fly.
301	mov.l	@(OFF_R4,r15), r4   ! arg0
302	mov.l	@(OFF_R5,r15), r5
303	mov.l	@(OFF_R6,r15), r6
304	mov.l	@(OFF_R7,r15), r7   ! arg3
305	mov.l	@(OFF_R3,r15), r3   ! syscall_nr
306	!
307	mov.l	2f, r10			! Number of syscalls
308	cmp/hs	r10, r3
309	bf	syscall_call
310	mov	#-ENOSYS, r0
311	bra	syscall_exit
312	 mov.l	r0, @(OFF_R0,r15)	! Return value
313
314__restore_all:
315	mov.l	1f, r0
316	jmp	@r0
317	 nop
318
319	.align	2
3201:	.long	restore_all
321
322	.align	2
323not_syscall_tra:
324	bra	debug_trap
325	 nop
326
327	.align	2
328syscall_badsys:			! Bad syscall number
329	mov	#-ENOSYS, r0
330	bra	resume_userspace
331	 mov.l	r0, @(OFF_R0,r15)	! Return value
332
333
334/*
335 * Syscall interface:
336 *
337 *	Syscall #: R3
338 *	Arguments #0 to #3: R4--R7
339 *	Arguments #4 to #6: R0, R1, R2
340 *	TRA: (number of arguments + 0x10) x 4
341 *
342 * This code also handles delegating other traps to the BIOS/gdb stub
343 * according to:
344 *
345 * Trap number
346 * (TRA>>2) 	    Purpose
347 * -------- 	    -------
348 * 0x0-0xf  	    old syscall ABI
349 * 0x10-0x1f  	    new syscall ABI
350 * 0x20-0xff  	    delegated through debug_trap to BIOS/gdb stub.
351 *
352 * Note: When we're first called, the TRA value must be shifted
353 * right 2 bits in order to get the value that was used as the "trapa"
354 * argument.
355 */
356
357	.align	2
358	.globl	ret_from_fork
359ret_from_fork:
360	mov.l	1f, r8
361	jsr	@r8
362	 mov	r0, r4
363	bra	syscall_exit
364	 nop
365	.align	2
3661:	.long	schedule_tail
367	!
368ENTRY(system_call)
369#if !defined(CONFIG_CPU_SH2)
370	mov.l	1f, r9
371	mov.l	@r9, r8		! Read from TRA (Trap Address) Register
372#endif
373	!
374	! Is the trap argument >= 0x20? (TRA will be >= 0x80)
375	mov	#0x7f, r9
376	cmp/hi	r9, r8
377	bt/s	not_syscall_tra
378	 mov	#OFF_TRA, r9
379	add	r15, r9
380	mov.l	r8, @r9			! set TRA value to tra
381#ifdef CONFIG_TRACE_IRQFLAGS
382	mov.l	5f, r10
383	jsr	@r10
384	 nop
385#endif
386	sti
387
388	!
389	get_current_thread_info r8, r10
390	mov.l	@(TI_FLAGS,r8), r8
391	mov	#_TIF_SYSCALL_TRACE, r10
392	tst	r10, r8
393	bf	syscall_trace_entry
394	!
395	mov.l	2f, r8			! Number of syscalls
396	cmp/hs	r8, r3
397	bt	syscall_badsys
398	!
399syscall_call:
400	shll2	r3		! x4
401	mov.l	3f, r8		! Load the address of sys_call_table
402	add	r8, r3
403	mov.l	@r3, r8
404	jsr	@r8	    	! jump to specific syscall handler
405	 nop
406	mov.l	@(OFF_R0,r15), r12		! save r0
407	mov.l	r0, @(OFF_R0,r15)		! save the return value
408	!
409syscall_exit:
410	cli
411#ifdef CONFIG_TRACE_IRQFLAGS
412	mov.l	6f, r0
413	jsr	@r0
414	 nop
415#endif
416	!
417	get_current_thread_info r8, r0
418	mov.l	@(TI_FLAGS,r8), r0		! current_thread_info->flags
419	tst	#_TIF_ALLWORK_MASK, r0
420	bf	syscall_exit_work
421	bra	__restore_all
422	 nop
423	.align	2
424#if !defined(CONFIG_CPU_SH2)
4251:	.long	TRA
426#endif
4272:	.long	NR_syscalls
4283:	.long	sys_call_table
4294:	.long	do_syscall_trace
430#ifdef CONFIG_TRACE_IRQFLAGS
4315:	.long	trace_hardirqs_on
4326:	.long	trace_hardirqs_off
433#endif
434