Commit | Line | Data |
---|---|---|
de398406 YS |
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 | ||
de398406 YS |
57 | |
58 | .align 2 | |
59 | ENTRY(exception_error) | |
60 | ! | |
afbfb52e | 61 | #ifdef CONFIG_TRACE_IRQFLAGS |
f413d0d9 | 62 | mov.l 2f, r0 |
afbfb52e PM |
63 | jsr @r0 |
64 | nop | |
65 | #endif | |
de398406 | 66 | sti |
f413d0d9 | 67 | mov.l 1f, r0 |
de398406 YS |
68 | jmp @r0 |
69 | nop | |
70 | ||
de398406 | 71 | .align 2 |
f413d0d9 | 72 | 1: .long do_exception_error |
afbfb52e | 73 | #ifdef CONFIG_TRACE_IRQFLAGS |
f413d0d9 | 74 | 2: .long trace_hardirqs_on |
afbfb52e | 75 | #endif |
de398406 YS |
76 | |
77 | .align 2 | |
78 | ret_from_exception: | |
79 | preempt_stop() | |
afbfb52e PM |
80 | #ifdef CONFIG_TRACE_IRQFLAGS |
81 | mov.l 4f, r0 | |
82 | jsr @r0 | |
83 | nop | |
84 | #endif | |
de398406 YS |
85 | ENTRY(ret_from_irq) |
86 | ! | |
87 | mov #OFF_SR, r0 | |
88 | mov.l @(r0,r15), r0 ! get status register | |
89 | shll r0 | |
90 | shll r0 ! kernel space? | |
91 | get_current_thread_info r8, r0 | |
92 | bt resume_kernel ! Yes, it's from kernel, go back soon | |
93 | ||
94 | #ifdef CONFIG_PREEMPT | |
95 | bra resume_userspace | |
96 | nop | |
97 | ENTRY(resume_kernel) | |
98 | mov.l @(TI_PRE_COUNT,r8), r0 ! current_thread_info->preempt_count | |
99 | tst r0, r0 | |
100 | bf noresched | |
101 | need_resched: | |
102 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
103 | tst #_TIF_NEED_RESCHED, r0 ! need_resched set? | |
104 | bt noresched | |
105 | ||
106 | mov #OFF_SR, r0 | |
107 | mov.l @(r0,r15), r0 ! get status register | |
108 | and #0xf0, r0 ! interrupts off (exception path)? | |
109 | cmp/eq #0xf0, r0 | |
110 | bt noresched | |
111 | ||
112 | mov.l 1f, r0 | |
113 | mov.l r0, @(TI_PRE_COUNT,r8) | |
114 | ||
afbfb52e PM |
115 | #ifdef CONFIG_TRACE_IRQFLAGS |
116 | mov.l 3f, r0 | |
117 | jsr @r0 | |
118 | nop | |
119 | #endif | |
de398406 YS |
120 | sti |
121 | mov.l 2f, r0 | |
122 | jsr @r0 | |
123 | nop | |
124 | mov #0, r0 | |
125 | mov.l r0, @(TI_PRE_COUNT,r8) | |
126 | cli | |
afbfb52e PM |
127 | #ifdef CONFIG_TRACE_IRQFLAGS |
128 | mov.l 4f, r0 | |
129 | jsr @r0 | |
130 | nop | |
131 | #endif | |
de398406 YS |
132 | |
133 | bra need_resched | |
134 | nop | |
afbfb52e | 135 | |
de398406 YS |
136 | noresched: |
137 | bra __restore_all | |
138 | nop | |
139 | ||
140 | .align 2 | |
141 | 1: .long PREEMPT_ACTIVE | |
142 | 2: .long schedule | |
afbfb52e PM |
143 | #ifdef CONFIG_TRACE_IRQFLAGS |
144 | 3: .long trace_hardirqs_on | |
145 | 4: .long trace_hardirqs_off | |
146 | #endif | |
de398406 YS |
147 | #endif |
148 | ||
149 | ENTRY(resume_userspace) | |
150 | ! r8: current_thread_info | |
151 | cli | |
afbfb52e PM |
152 | #ifdef CONFIG_TRACE_IRQFLAGS |
153 | mov.l 5f, r0 | |
154 | jsr @r0 | |
155 | nop | |
156 | #endif | |
de398406 YS |
157 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags |
158 | tst #_TIF_WORK_MASK, r0 | |
159 | bt/s __restore_all | |
160 | tst #_TIF_NEED_RESCHED, r0 | |
161 | ||
162 | .align 2 | |
163 | work_pending: | |
164 | ! r0: current_thread_info->flags | |
165 | ! r8: current_thread_info | |
166 | ! t: result of "tst #_TIF_NEED_RESCHED, r0" | |
167 | bf/s work_resched | |
168 | tst #(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0 | |
169 | work_notifysig: | |
170 | bt/s __restore_all | |
171 | mov r15, r4 | |
172 | mov r12, r5 ! set arg1(save_r0) | |
173 | mov r0, r6 | |
174 | mov.l 2f, r1 | |
175 | mov.l 3f, r0 | |
176 | jmp @r1 | |
177 | lds r0, pr | |
178 | work_resched: | |
179 | #ifndef CONFIG_PREEMPT | |
180 | ! gUSA handling | |
181 | mov.l @(OFF_SP,r15), r0 ! get user space stack pointer | |
182 | mov r0, r1 | |
183 | shll r0 | |
184 | bf/s 1f | |
185 | shll r0 | |
186 | bf/s 1f | |
187 | mov #OFF_PC, r0 | |
188 | ! SP >= 0xc0000000 : gUSA mark | |
189 | mov.l @(r0,r15), r2 ! get user space PC (program counter) | |
190 | mov.l @(OFF_R0,r15), r3 ! end point | |
191 | cmp/hs r3, r2 ! r2 >= r3? | |
192 | bt 1f | |
193 | add r3, r1 ! rewind point #2 | |
194 | mov.l r1, @(r0,r15) ! reset PC to rewind point #2 | |
195 | ! | |
196 | 1: | |
197 | #endif | |
198 | mov.l 1f, r1 | |
199 | jsr @r1 ! schedule | |
200 | nop | |
201 | cli | |
afbfb52e PM |
202 | #ifdef CONFIG_TRACE_IRQFLAGS |
203 | mov.l 5f, r0 | |
204 | jsr @r0 | |
205 | nop | |
206 | #endif | |
de398406 YS |
207 | ! |
208 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
209 | tst #_TIF_WORK_MASK, r0 | |
210 | bt __restore_all | |
211 | bra work_pending | |
212 | tst #_TIF_NEED_RESCHED, r0 | |
213 | ||
214 | .align 2 | |
215 | 1: .long schedule | |
216 | 2: .long do_notify_resume | |
217 | 3: .long restore_all | |
afbfb52e PM |
218 | #ifdef CONFIG_TRACE_IRQFLAGS |
219 | 4: .long trace_hardirqs_on | |
220 | 5: .long trace_hardirqs_off | |
221 | #endif | |
de398406 YS |
222 | |
223 | .align 2 | |
224 | syscall_exit_work: | |
225 | ! r0: current_thread_info->flags | |
226 | ! r8: current_thread_info | |
9432f968 | 227 | tst #_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP, r0 |
de398406 YS |
228 | bt/s work_pending |
229 | tst #_TIF_NEED_RESCHED, r0 | |
afbfb52e PM |
230 | #ifdef CONFIG_TRACE_IRQFLAGS |
231 | mov.l 5f, r0 | |
232 | jsr @r0 | |
233 | nop | |
234 | #endif | |
de398406 YS |
235 | sti |
236 | ! XXX setup arguments... | |
237 | mov.l 4f, r0 ! do_syscall_trace | |
238 | jsr @r0 | |
239 | nop | |
240 | bra resume_userspace | |
241 | nop | |
242 | ||
243 | .align 2 | |
244 | syscall_trace_entry: | |
245 | ! Yes it is traced. | |
246 | ! XXX setup arguments... | |
247 | mov.l 4f, r11 ! Call do_syscall_trace which notifies | |
248 | jsr @r11 ! superior (will chomp R[0-7]) | |
249 | nop | |
250 | ! Reload R0-R4 from kernel stack, where the | |
251 | ! parent may have modified them using | |
252 | ! ptrace(POKEUSR). (Note that R0-R2 are | |
253 | ! used by the system call handler directly | |
254 | ! from the kernel stack anyway, so don't need | |
255 | ! to be reloaded here.) This allows the parent | |
256 | ! to rewrite system calls and args on the fly. | |
257 | mov.l @(OFF_R4,r15), r4 ! arg0 | |
258 | mov.l @(OFF_R5,r15), r5 | |
259 | mov.l @(OFF_R6,r15), r6 | |
260 | mov.l @(OFF_R7,r15), r7 ! arg3 | |
261 | mov.l @(OFF_R3,r15), r3 ! syscall_nr | |
e0969e0c | 262 | ! |
de398406 YS |
263 | mov.l 2f, r10 ! Number of syscalls |
264 | cmp/hs r10, r3 | |
265 | bf syscall_call | |
266 | mov #-ENOSYS, r0 | |
267 | bra syscall_exit | |
268 | mov.l r0, @(OFF_R0,r15) ! Return value | |
269 | ||
270 | __restore_all: | |
afbfb52e | 271 | mov.l 1f, r0 |
de398406 YS |
272 | jmp @r0 |
273 | nop | |
274 | ||
275 | .align 2 | |
276 | 1: .long restore_all | |
277 | ||
e0969e0c SM |
278 | .align 2 |
279 | syscall_badsys: ! Bad syscall number | |
280 | mov #-ENOSYS, r0 | |
281 | bra resume_userspace | |
282 | mov.l r0, @(OFF_R0,r15) ! Return value | |
f413d0d9 PM |
283 | |
284 | /* | |
285 | * The main debug trap handler. | |
286 | * | |
287 | * r8=TRA (not the trap number!) | |
288 | * | |
289 | * Note: This assumes that the trapa value is left in its original | |
290 | * form (without the shlr2 shift) so the calculation for the jump | |
291 | * call table offset remains a simple in place mask. | |
292 | */ | |
293 | debug_trap: | |
294 | mov r8, r0 | |
295 | and #(0xf << 2), r0 | |
296 | mov.l 1f, r8 | |
297 | add r0, r8 | |
298 | mov.l @r8, r8 | |
299 | jmp @r8 | |
300 | nop | |
301 | ||
302 | .align 2 | |
303 | 1: .long debug_trap_table | |
e0969e0c | 304 | |
de398406 YS |
305 | /* |
306 | * Syscall interface: | |
307 | * | |
308 | * Syscall #: R3 | |
309 | * Arguments #0 to #3: R4--R7 | |
310 | * Arguments #4 to #6: R0, R1, R2 | |
f413d0d9 | 311 | * TRA: (number of arguments + ABI revision) x 4 |
de398406 YS |
312 | * |
313 | * This code also handles delegating other traps to the BIOS/gdb stub | |
314 | * according to: | |
315 | * | |
316 | * Trap number | |
f413d0d9 PM |
317 | * (TRA>>2) Purpose |
318 | * -------- ------- | |
319 | * 0x00-0x0f original SH-3/4 syscall ABI (not in general use). | |
320 | * 0x10-0x1f general SH-3/4 syscall ABI. | |
321 | * 0x20-0x2f syscall ABI for SH-2 parts. | |
322 | * 0x30-0x3f debug traps used by the kernel. | |
323 | * 0x40-0xff Not supported by all parts, so left unhandled. | |
de398406 YS |
324 | * |
325 | * Note: When we're first called, the TRA value must be shifted | |
326 | * right 2 bits in order to get the value that was used as the "trapa" | |
327 | * argument. | |
328 | */ | |
329 | ||
330 | .align 2 | |
331 | .globl ret_from_fork | |
332 | ret_from_fork: | |
333 | mov.l 1f, r8 | |
334 | jsr @r8 | |
335 | mov r0, r4 | |
336 | bra syscall_exit | |
337 | nop | |
338 | .align 2 | |
339 | 1: .long schedule_tail | |
f413d0d9 PM |
340 | |
341 | /* | |
342 | * The poorly named main trapa decode and dispatch routine, for | |
343 | * system calls and debug traps through their respective jump tables. | |
344 | */ | |
de398406 YS |
345 | ENTRY(system_call) |
346 | #if !defined(CONFIG_CPU_SH2) | |
347 | mov.l 1f, r9 | |
348 | mov.l @r9, r8 ! Read from TRA (Trap Address) Register | |
349 | #endif | |
f413d0d9 PM |
350 | /* |
351 | * Check the trap type | |
352 | */ | |
353 | mov #((0x20 << 2) - 1), r9 | |
de398406 | 354 | cmp/hi r9, r8 |
f413d0d9 | 355 | bt/s debug_trap ! it's a debug trap.. |
de398406 YS |
356 | mov #OFF_TRA, r9 |
357 | add r15, r9 | |
de398406 | 358 | mov.l r8, @r9 ! set TRA value to tra |
afbfb52e PM |
359 | #ifdef CONFIG_TRACE_IRQFLAGS |
360 | mov.l 5f, r10 | |
361 | jsr @r10 | |
362 | nop | |
363 | #endif | |
de398406 | 364 | sti |
afbfb52e | 365 | |
de398406 | 366 | ! |
e0969e0c | 367 | get_current_thread_info r8, r10 |
de398406 YS |
368 | mov.l @(TI_FLAGS,r8), r8 |
369 | mov #_TIF_SYSCALL_TRACE, r10 | |
370 | tst r10, r8 | |
371 | bf syscall_trace_entry | |
372 | ! | |
e0969e0c SM |
373 | mov.l 2f, r8 ! Number of syscalls |
374 | cmp/hs r8, r3 | |
375 | bt syscall_badsys | |
376 | ! | |
de398406 | 377 | syscall_call: |
e0969e0c | 378 | shll2 r3 ! x4 |
de398406 | 379 | mov.l 3f, r8 ! Load the address of sys_call_table |
e0969e0c SM |
380 | add r8, r3 |
381 | mov.l @r3, r8 | |
de398406 YS |
382 | jsr @r8 ! jump to specific syscall handler |
383 | nop | |
384 | mov.l @(OFF_R0,r15), r12 ! save r0 | |
385 | mov.l r0, @(OFF_R0,r15) ! save the return value | |
386 | ! | |
387 | syscall_exit: | |
388 | cli | |
afbfb52e PM |
389 | #ifdef CONFIG_TRACE_IRQFLAGS |
390 | mov.l 6f, r0 | |
391 | jsr @r0 | |
392 | nop | |
393 | #endif | |
de398406 YS |
394 | ! |
395 | get_current_thread_info r8, r0 | |
396 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
397 | tst #_TIF_ALLWORK_MASK, r0 | |
398 | bf syscall_exit_work | |
399 | bra __restore_all | |
400 | nop | |
401 | .align 2 | |
402 | #if !defined(CONFIG_CPU_SH2) | |
403 | 1: .long TRA | |
404 | #endif | |
405 | 2: .long NR_syscalls | |
406 | 3: .long sys_call_table | |
407 | 4: .long do_syscall_trace | |
afbfb52e PM |
408 | #ifdef CONFIG_TRACE_IRQFLAGS |
409 | 5: .long trace_hardirqs_on | |
410 | 6: .long trace_hardirqs_off | |
411 | #endif |