diff options
Diffstat (limited to 'arch/sh/kernel/entry-common.S')
-rw-r--r-- | arch/sh/kernel/entry-common.S | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S new file mode 100644 index 000000000000..29136a35d7c7 --- /dev/null +++ b/arch/sh/kernel/entry-common.S | |||
@@ -0,0 +1,433 @@ | |||
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 | ||