Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/shaggy...
[pandora-kernel.git] / arch / sh / kernel / entry-common.S
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
63 debug_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
72 debug_enter:            
73 #if defined(CONFIG_SH_KGDB)
74         /* Jump to kgdb, pass stacked regs as arg */
75 debug_kernel_sw:
76         mov.l   3f, r0
77         jmp     @r0
78          mov    r15, r4
79         .align  2
80 3:      .long   kgdb_handle_exception
81 #endif /* CONFIG_SH_KGDB */
82
83 #endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
84
85
86         .align  2
87 debug_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
101 ENTRY(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
115 1:      .long   break_point_trap_software
116 2:      .long   do_exception_error
117 #ifdef CONFIG_TRACE_IRQFLAGS
118 3:      .long   trace_hardirqs_on
119 #endif
120
121         .align  2
122 ret_from_exception:
123         preempt_stop()
124 #ifdef CONFIG_TRACE_IRQFLAGS
125         mov.l   4f, r0
126         jsr     @r0
127          nop
128 #endif
129 ENTRY(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
141 ENTRY(resume_kernel)
142         mov.l   @(TI_PRE_COUNT,r8), r0  ! current_thread_info->preempt_count
143         tst     r0, r0
144         bf      noresched
145 need_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
180 noresched:
181         bra     __restore_all
182          nop
183
184         .align 2
185 1:      .long   PREEMPT_ACTIVE
186 2:      .long   schedule
187 #ifdef CONFIG_TRACE_IRQFLAGS
188 3:      .long   trace_hardirqs_on
189 4:      .long   trace_hardirqs_off
190 #endif
191 #endif
192
193 ENTRY(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
207 work_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
213 work_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
222 work_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         !
240 1:
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
259 1:      .long   schedule
260 2:      .long   do_notify_resume
261 3:      .long   restore_all
262 #ifdef CONFIG_TRACE_IRQFLAGS
263 4:      .long   trace_hardirqs_on
264 5:      .long   trace_hardirqs_off
265 #endif
266
267         .align  2
268 syscall_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
288 syscall_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
320 1:      .long   restore_all
321
322         .align  2
323 not_syscall_tra:        
324         bra     debug_trap
325          nop
326
327         .align  2
328 syscall_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
359 ret_from_fork:
360         mov.l   1f, r8
361         jsr     @r8
362          mov    r0, r4
363         bra     syscall_exit
364          nop
365         .align  2
366 1:      .long   schedule_tail
367         !
368 ENTRY(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         !
399 syscall_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         !
409 syscall_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)
425 1:      .long   TRA
426 #endif
427 2:      .long   NR_syscalls
428 3:      .long   sys_call_table
429 4:      .long   do_syscall_trace
430 #ifdef CONFIG_TRACE_IRQFLAGS
431 5:      .long   trace_hardirqs_on
432 6:      .long   trace_hardirqs_off
433 #endif