Pull button into test branch
[linux-drm-fsl-dcu.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 #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
89 debug_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
104 1:
105 #endif
106          mov.l  @r15, r0                ! Restore R0 value
107         mov.l   1f, r8
108         jmp     @r8
109          nop
110
111         .align  2
112 ENTRY(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
126 1:      .long   break_point_trap_software
127 2:      .long   do_exception_error
128 #ifdef CONFIG_TRACE_IRQFLAGS
129 3:      .long   trace_hardirqs_on
130 #endif
131
132         .align  2
133 ret_from_exception:
134         preempt_stop()
135 #ifdef CONFIG_TRACE_IRQFLAGS
136         mov.l   4f, r0
137         jsr     @r0
138          nop
139 #endif
140 ENTRY(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
152 ENTRY(resume_kernel)
153         mov.l   @(TI_PRE_COUNT,r8), r0  ! current_thread_info->preempt_count
154         tst     r0, r0
155         bf      noresched
156 need_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
191 noresched:
192         bra     __restore_all
193          nop
194
195         .align 2
196 1:      .long   PREEMPT_ACTIVE
197 2:      .long   schedule
198 #ifdef CONFIG_TRACE_IRQFLAGS
199 3:      .long   trace_hardirqs_on
200 4:      .long   trace_hardirqs_off
201 #endif
202 #endif
203
204 ENTRY(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
218 work_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
224 work_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
233 work_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         !
251 1:
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
270 1:      .long   schedule
271 2:      .long   do_notify_resume
272 3:      .long   restore_all
273 #ifdef CONFIG_TRACE_IRQFLAGS
274 4:      .long   trace_hardirqs_on
275 5:      .long   trace_hardirqs_off
276 #endif
277
278         .align  2
279 syscall_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
299 syscall_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
331 1:      .long   restore_all
332
333         .align  2
334 not_syscall_tra:        
335         bra     debug_trap
336          nop
337
338         .align  2
339 syscall_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
370 ret_from_fork:
371         mov.l   1f, r8
372         jsr     @r8
373          mov    r0, r4
374         bra     syscall_exit
375          nop
376         .align  2
377 1:      .long   schedule_tail
378         !
379 ENTRY(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         !
410 syscall_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         !
420 syscall_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)
436 1:      .long   TRA
437 #endif
438 2:      .long   NR_syscalls
439 3:      .long   sys_call_table
440 4:      .long   do_syscall_trace
441 #ifdef CONFIG_TRACE_IRQFLAGS
442 5:      .long   trace_hardirqs_on
443 6:      .long   trace_hardirqs_off
444 #endif