xref: /linux/arch/sh/kernel/entry-common.S (revision b454cc6636d254fbf6049b73e9560aee76fb04a3)
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#ifdef CONFIG_SH_STANDARD_BIOS
83	bra	debug_kernel_fw
84	 nop
85#endif
86#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
87
88	.align	2
89debug_trap:
90#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
91	mov	r8, r0
92	shlr2	r0
93	cmp/eq	#0x3f, r0		! sh_bios() trap
94	bf	1f
95#ifdef CONFIG_SH_KGDB
96	cmp/eq	#0xff, r0		! XXX: KGDB trap, fix for SH-2.
97	bf	1f
98#endif
99	mov	#OFF_SR, r0
100	mov.l	@(r0,r15), r0		! get status register
101	shll	r0
102	shll	r0			! kernel space?
103	bt/s	debug_kernel
1041:
105#endif
106	 mov.l	@r15, r0		! Restore R0 value
107	mov.l	1f, r8
108	jmp	@r8
109	 nop
110
111	.align	2
112ENTRY(exception_error)
113	!
114#ifdef CONFIG_TRACE_IRQFLAGS
115	mov.l	3f, r0
116	jsr	@r0
117	 nop
118#endif
119	sti
120	mov.l	2f, r0
121	jmp	@r0
122	 nop
123
124!
125	.align	2
1261:	.long	break_point_trap_software
1272:	.long	do_exception_error
128#ifdef CONFIG_TRACE_IRQFLAGS
1293:	.long	trace_hardirqs_on
130#endif
131
132	.align	2
133ret_from_exception:
134	preempt_stop()
135#ifdef CONFIG_TRACE_IRQFLAGS
136	mov.l	4f, r0
137	jsr	@r0
138	 nop
139#endif
140ENTRY(ret_from_irq)
141	!
142	mov	#OFF_SR, r0
143	mov.l	@(r0,r15), r0	! get status register
144	shll	r0
145	shll	r0		! kernel space?
146	get_current_thread_info r8, r0
147	bt	resume_kernel	! Yes, it's from kernel, go back soon
148
149#ifdef CONFIG_PREEMPT
150	bra	resume_userspace
151	 nop
152ENTRY(resume_kernel)
153	mov.l	@(TI_PRE_COUNT,r8), r0	! current_thread_info->preempt_count
154	tst	r0, r0
155	bf	noresched
156need_resched:
157	mov.l	@(TI_FLAGS,r8), r0	! current_thread_info->flags
158	tst	#_TIF_NEED_RESCHED, r0	! need_resched set?
159	bt	noresched
160
161	mov	#OFF_SR, r0
162	mov.l	@(r0,r15), r0		! get status register
163	and	#0xf0, r0		! interrupts off (exception path)?
164	cmp/eq	#0xf0, r0
165	bt	noresched
166
167	mov.l	1f, r0
168	mov.l	r0, @(TI_PRE_COUNT,r8)
169
170#ifdef CONFIG_TRACE_IRQFLAGS
171	mov.l	3f, r0
172	jsr	@r0
173	 nop
174#endif
175	sti
176	mov.l	2f, r0
177	jsr	@r0
178	 nop
179	mov	#0, r0
180	mov.l	r0, @(TI_PRE_COUNT,r8)
181	cli
182#ifdef CONFIG_TRACE_IRQFLAGS
183	mov.l	4f, r0
184	jsr	@r0
185	 nop
186#endif
187
188	bra	need_resched
189	 nop
190
191noresched:
192	bra	__restore_all
193	 nop
194
195	.align 2
1961:	.long	PREEMPT_ACTIVE
1972:	.long	schedule
198#ifdef CONFIG_TRACE_IRQFLAGS
1993:	.long	trace_hardirqs_on
2004:	.long	trace_hardirqs_off
201#endif
202#endif
203
204ENTRY(resume_userspace)
205	! r8: current_thread_info
206	cli
207#ifdef CONFIG_TRACE_IRQFLAGS
208	mov.l	5f, r0
209	jsr	@r0
210	 nop
211#endif
212	mov.l	@(TI_FLAGS,r8), r0		! current_thread_info->flags
213	tst	#_TIF_WORK_MASK, r0
214	bt/s	__restore_all
215	 tst	#_TIF_NEED_RESCHED, r0
216
217	.align	2
218work_pending:
219	! r0: current_thread_info->flags
220	! r8: current_thread_info
221	! t:  result of "tst	#_TIF_NEED_RESCHED, r0"
222	bf/s	work_resched
223	 tst	#(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0
224work_notifysig:
225	bt/s	__restore_all
226	 mov	r15, r4
227	mov	r12, r5		! set arg1(save_r0)
228	mov	r0, r6
229	mov.l	2f, r1
230	mov.l	3f, r0
231	jmp	@r1
232	 lds	r0, pr
233work_resched:
234#ifndef CONFIG_PREEMPT
235	! gUSA handling
236	mov.l	@(OFF_SP,r15), r0	! get user space stack pointer
237	mov	r0, r1
238	shll	r0
239	bf/s	1f
240	 shll	r0
241	bf/s	1f
242	 mov	#OFF_PC, r0
243	! 				  SP >= 0xc0000000 : gUSA mark
244	mov.l	@(r0,r15), r2		! get user space PC (program counter)
245	mov.l	@(OFF_R0,r15), r3	! end point
246	cmp/hs	r3, r2			! r2 >= r3?
247	bt	1f
248	add	r3, r1			! rewind point #2
249	mov.l	r1, @(r0,r15)		! reset PC to rewind point #2
250	!
2511:
252#endif
253	mov.l	1f, r1
254	jsr	@r1				! schedule
255	 nop
256	cli
257#ifdef CONFIG_TRACE_IRQFLAGS
258	mov.l	5f, r0
259	jsr	@r0
260	 nop
261#endif
262	!
263	mov.l	@(TI_FLAGS,r8), r0		! current_thread_info->flags
264	tst	#_TIF_WORK_MASK, r0
265	bt	__restore_all
266	bra	work_pending
267	 tst	#_TIF_NEED_RESCHED, r0
268
269	.align	2
2701:	.long	schedule
2712:	.long	do_notify_resume
2723:	.long	restore_all
273#ifdef CONFIG_TRACE_IRQFLAGS
2744:	.long	trace_hardirqs_on
2755:	.long	trace_hardirqs_off
276#endif
277
278	.align	2
279syscall_exit_work:
280	! r0: current_thread_info->flags
281	! r8: current_thread_info
282	tst	#_TIF_SYSCALL_TRACE, r0
283	bt/s	work_pending
284	 tst	#_TIF_NEED_RESCHED, r0
285#ifdef CONFIG_TRACE_IRQFLAGS
286	mov.l	5f, r0
287	jsr	@r0
288	 nop
289#endif
290	sti
291	! XXX setup arguments...
292	mov.l	4f, r0			! do_syscall_trace
293	jsr	@r0
294	 nop
295	bra	resume_userspace
296	 nop
297
298	.align	2
299syscall_trace_entry:
300	!                     	Yes it is traced.
301	! XXX setup arguments...
302	mov.l	4f, r11		! Call do_syscall_trace which notifies
303	jsr	@r11	    	! superior (will chomp R[0-7])
304	 nop
305	!			Reload R0-R4 from kernel stack, where the
306	!   	    	    	parent may have modified them using
307	!   	    	    	ptrace(POKEUSR).  (Note that R0-R2 are
308	!   	    	    	used by the system call handler directly
309	!   	    	    	from the kernel stack anyway, so don't need
310	!   	    	    	to be reloaded here.)  This allows the parent
311	!   	    	    	to rewrite system calls and args on the fly.
312	mov.l	@(OFF_R4,r15), r4   ! arg0
313	mov.l	@(OFF_R5,r15), r5
314	mov.l	@(OFF_R6,r15), r6
315	mov.l	@(OFF_R7,r15), r7   ! arg3
316	mov.l	@(OFF_R3,r15), r3   ! syscall_nr
317	!
318	mov.l	2f, r10			! Number of syscalls
319	cmp/hs	r10, r3
320	bf	syscall_call
321	mov	#-ENOSYS, r0
322	bra	syscall_exit
323	 mov.l	r0, @(OFF_R0,r15)	! Return value
324
325__restore_all:
326	mov.l	1f, r0
327	jmp	@r0
328	 nop
329
330	.align	2
3311:	.long	restore_all
332
333	.align	2
334not_syscall_tra:
335	bra	debug_trap
336	 nop
337
338	.align	2
339syscall_badsys:			! Bad syscall number
340	mov	#-ENOSYS, r0
341	bra	resume_userspace
342	 mov.l	r0, @(OFF_R0,r15)	! Return value
343
344
345/*
346 * Syscall interface:
347 *
348 *	Syscall #: R3
349 *	Arguments #0 to #3: R4--R7
350 *	Arguments #4 to #6: R0, R1, R2
351 *	TRA: (number of arguments + 0x10) x 4
352 *
353 * This code also handles delegating other traps to the BIOS/gdb stub
354 * according to:
355 *
356 * Trap number
357 * (TRA>>2) 	    Purpose
358 * -------- 	    -------
359 * 0x0-0xf  	    old syscall ABI
360 * 0x10-0x1f  	    new syscall ABI
361 * 0x20-0xff  	    delegated through debug_trap to BIOS/gdb stub.
362 *
363 * Note: When we're first called, the TRA value must be shifted
364 * right 2 bits in order to get the value that was used as the "trapa"
365 * argument.
366 */
367
368	.align	2
369	.globl	ret_from_fork
370ret_from_fork:
371	mov.l	1f, r8
372	jsr	@r8
373	 mov	r0, r4
374	bra	syscall_exit
375	 nop
376	.align	2
3771:	.long	schedule_tail
378	!
379ENTRY(system_call)
380#if !defined(CONFIG_CPU_SH2)
381	mov.l	1f, r9
382	mov.l	@r9, r8		! Read from TRA (Trap Address) Register
383#endif
384	!
385	! Is the trap argument >= 0x20? (TRA will be >= 0x80)
386	mov	#0x7f, r9
387	cmp/hi	r9, r8
388	bt/s	not_syscall_tra
389	 mov	#OFF_TRA, r9
390	add	r15, r9
391	mov.l	r8, @r9			! set TRA value to tra
392#ifdef CONFIG_TRACE_IRQFLAGS
393	mov.l	5f, r10
394	jsr	@r10
395	 nop
396#endif
397	sti
398
399	!
400	get_current_thread_info r8, r10
401	mov.l	@(TI_FLAGS,r8), r8
402	mov	#_TIF_SYSCALL_TRACE, r10
403	tst	r10, r8
404	bf	syscall_trace_entry
405	!
406	mov.l	2f, r8			! Number of syscalls
407	cmp/hs	r8, r3
408	bt	syscall_badsys
409	!
410syscall_call:
411	shll2	r3		! x4
412	mov.l	3f, r8		! Load the address of sys_call_table
413	add	r8, r3
414	mov.l	@r3, r8
415	jsr	@r8	    	! jump to specific syscall handler
416	 nop
417	mov.l	@(OFF_R0,r15), r12		! save r0
418	mov.l	r0, @(OFF_R0,r15)		! save the return value
419	!
420syscall_exit:
421	cli
422#ifdef CONFIG_TRACE_IRQFLAGS
423	mov.l	6f, r0
424	jsr	@r0
425	 nop
426#endif
427	!
428	get_current_thread_info r8, r0
429	mov.l	@(TI_FLAGS,r8), r0		! current_thread_info->flags
430	tst	#_TIF_ALLWORK_MASK, r0
431	bf	syscall_exit_work
432	bra	__restore_all
433	 nop
434	.align	2
435#if !defined(CONFIG_CPU_SH2)
4361:	.long	TRA
437#endif
4382:	.long	NR_syscalls
4393:	.long	sys_call_table
4404:	.long	do_syscall_trace
441#ifdef CONFIG_TRACE_IRQFLAGS
4425:	.long	trace_hardirqs_on
4436:	.long	trace_hardirqs_off
444#endif
445