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 | ||
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 | sti | |
104 | mov.l 2f, r0 | |
105 | jmp @r0 | |
106 | nop | |
107 | ||
108 | ! | |
109 | .align 2 | |
110 | 1: .long break_point_trap_software | |
111 | 2: .long do_exception_error | |
112 | ||
113 | .align 2 | |
114 | ret_from_exception: | |
115 | preempt_stop() | |
116 | ENTRY(ret_from_irq) | |
117 | ! | |
118 | mov #OFF_SR, r0 | |
119 | mov.l @(r0,r15), r0 ! get status register | |
120 | shll r0 | |
121 | shll r0 ! kernel space? | |
122 | get_current_thread_info r8, r0 | |
123 | bt resume_kernel ! Yes, it's from kernel, go back soon | |
124 | ||
125 | #ifdef CONFIG_PREEMPT | |
126 | bra resume_userspace | |
127 | nop | |
128 | ENTRY(resume_kernel) | |
129 | mov.l @(TI_PRE_COUNT,r8), r0 ! current_thread_info->preempt_count | |
130 | tst r0, r0 | |
131 | bf noresched | |
132 | need_resched: | |
133 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
134 | tst #_TIF_NEED_RESCHED, r0 ! need_resched set? | |
135 | bt noresched | |
136 | ||
137 | mov #OFF_SR, r0 | |
138 | mov.l @(r0,r15), r0 ! get status register | |
139 | and #0xf0, r0 ! interrupts off (exception path)? | |
140 | cmp/eq #0xf0, r0 | |
141 | bt noresched | |
142 | ||
143 | mov.l 1f, r0 | |
144 | mov.l r0, @(TI_PRE_COUNT,r8) | |
145 | ||
146 | sti | |
147 | mov.l 2f, r0 | |
148 | jsr @r0 | |
149 | nop | |
150 | mov #0, r0 | |
151 | mov.l r0, @(TI_PRE_COUNT,r8) | |
152 | cli | |
153 | ||
154 | bra need_resched | |
155 | nop | |
156 | noresched: | |
157 | bra __restore_all | |
158 | nop | |
159 | ||
160 | .align 2 | |
161 | 1: .long PREEMPT_ACTIVE | |
162 | 2: .long schedule | |
163 | #endif | |
164 | ||
165 | ENTRY(resume_userspace) | |
166 | ! r8: current_thread_info | |
167 | cli | |
168 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
169 | tst #_TIF_WORK_MASK, r0 | |
170 | bt/s __restore_all | |
171 | tst #_TIF_NEED_RESCHED, r0 | |
172 | ||
173 | .align 2 | |
174 | work_pending: | |
175 | ! r0: current_thread_info->flags | |
176 | ! r8: current_thread_info | |
177 | ! t: result of "tst #_TIF_NEED_RESCHED, r0" | |
178 | bf/s work_resched | |
179 | tst #(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0 | |
180 | work_notifysig: | |
181 | bt/s __restore_all | |
182 | mov r15, r4 | |
183 | mov r12, r5 ! set arg1(save_r0) | |
184 | mov r0, r6 | |
185 | mov.l 2f, r1 | |
186 | mov.l 3f, r0 | |
187 | jmp @r1 | |
188 | lds r0, pr | |
189 | work_resched: | |
190 | #ifndef CONFIG_PREEMPT | |
191 | ! gUSA handling | |
192 | mov.l @(OFF_SP,r15), r0 ! get user space stack pointer | |
193 | mov r0, r1 | |
194 | shll r0 | |
195 | bf/s 1f | |
196 | shll r0 | |
197 | bf/s 1f | |
198 | mov #OFF_PC, r0 | |
199 | ! SP >= 0xc0000000 : gUSA mark | |
200 | mov.l @(r0,r15), r2 ! get user space PC (program counter) | |
201 | mov.l @(OFF_R0,r15), r3 ! end point | |
202 | cmp/hs r3, r2 ! r2 >= r3? | |
203 | bt 1f | |
204 | add r3, r1 ! rewind point #2 | |
205 | mov.l r1, @(r0,r15) ! reset PC to rewind point #2 | |
206 | ! | |
207 | 1: | |
208 | #endif | |
209 | mov.l 1f, r1 | |
210 | jsr @r1 ! schedule | |
211 | nop | |
212 | cli | |
213 | ! | |
214 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
215 | tst #_TIF_WORK_MASK, r0 | |
216 | bt __restore_all | |
217 | bra work_pending | |
218 | tst #_TIF_NEED_RESCHED, r0 | |
219 | ||
220 | .align 2 | |
221 | 1: .long schedule | |
222 | 2: .long do_notify_resume | |
223 | 3: .long restore_all | |
224 | ||
225 | .align 2 | |
226 | syscall_exit_work: | |
227 | ! r0: current_thread_info->flags | |
228 | ! r8: current_thread_info | |
229 | tst #_TIF_SYSCALL_TRACE, r0 | |
230 | bt/s work_pending | |
231 | tst #_TIF_NEED_RESCHED, r0 | |
232 | sti | |
233 | ! XXX setup arguments... | |
234 | mov.l 4f, r0 ! do_syscall_trace | |
235 | jsr @r0 | |
236 | nop | |
237 | bra resume_userspace | |
238 | nop | |
239 | ||
240 | .align 2 | |
241 | syscall_trace_entry: | |
242 | ! Yes it is traced. | |
243 | ! XXX setup arguments... | |
244 | mov.l 4f, r11 ! Call do_syscall_trace which notifies | |
245 | jsr @r11 ! superior (will chomp R[0-7]) | |
246 | nop | |
247 | ! Reload R0-R4 from kernel stack, where the | |
248 | ! parent may have modified them using | |
249 | ! ptrace(POKEUSR). (Note that R0-R2 are | |
250 | ! used by the system call handler directly | |
251 | ! from the kernel stack anyway, so don't need | |
252 | ! to be reloaded here.) This allows the parent | |
253 | ! to rewrite system calls and args on the fly. | |
254 | mov.l @(OFF_R4,r15), r4 ! arg0 | |
255 | mov.l @(OFF_R5,r15), r5 | |
256 | mov.l @(OFF_R6,r15), r6 | |
257 | mov.l @(OFF_R7,r15), r7 ! arg3 | |
258 | mov.l @(OFF_R3,r15), r3 ! syscall_nr | |
e0969e0c | 259 | ! |
de398406 YS |
260 | mov.l 2f, r10 ! Number of syscalls |
261 | cmp/hs r10, r3 | |
262 | bf syscall_call | |
263 | mov #-ENOSYS, r0 | |
264 | bra syscall_exit | |
265 | mov.l r0, @(OFF_R0,r15) ! Return value | |
266 | ||
267 | __restore_all: | |
268 | mov.l 1f,r0 | |
269 | jmp @r0 | |
270 | nop | |
271 | ||
272 | .align 2 | |
273 | 1: .long restore_all | |
274 | ||
e0969e0c SM |
275 | .align 2 |
276 | not_syscall_tra: | |
277 | bra debug_trap | |
278 | nop | |
279 | ||
280 | .align 2 | |
281 | syscall_badsys: ! Bad syscall number | |
282 | mov #-ENOSYS, r0 | |
283 | bra resume_userspace | |
284 | mov.l r0, @(OFF_R0,r15) ! Return value | |
285 | ||
286 | ||
de398406 YS |
287 | /* |
288 | * Syscall interface: | |
289 | * | |
290 | * Syscall #: R3 | |
291 | * Arguments #0 to #3: R4--R7 | |
292 | * Arguments #4 to #6: R0, R1, R2 | |
293 | * TRA: (number of arguments + 0x10) x 4 | |
294 | * | |
295 | * This code also handles delegating other traps to the BIOS/gdb stub | |
296 | * according to: | |
297 | * | |
298 | * Trap number | |
299 | * (TRA>>2) Purpose | |
300 | * -------- ------- | |
301 | * 0x0-0xf old syscall ABI | |
302 | * 0x10-0x1f new syscall ABI | |
303 | * 0x20-0xff delegated through debug_trap to BIOS/gdb stub. | |
304 | * | |
305 | * Note: When we're first called, the TRA value must be shifted | |
306 | * right 2 bits in order to get the value that was used as the "trapa" | |
307 | * argument. | |
308 | */ | |
309 | ||
310 | .align 2 | |
311 | .globl ret_from_fork | |
312 | ret_from_fork: | |
313 | mov.l 1f, r8 | |
314 | jsr @r8 | |
315 | mov r0, r4 | |
316 | bra syscall_exit | |
317 | nop | |
318 | .align 2 | |
319 | 1: .long schedule_tail | |
320 | ! | |
321 | ENTRY(system_call) | |
322 | #if !defined(CONFIG_CPU_SH2) | |
323 | mov.l 1f, r9 | |
324 | mov.l @r9, r8 ! Read from TRA (Trap Address) Register | |
325 | #endif | |
326 | ! | |
327 | ! Is the trap argument >= 0x20? (TRA will be >= 0x80) | |
328 | mov #0x7f, r9 | |
329 | cmp/hi r9, r8 | |
e0969e0c | 330 | bt/s not_syscall_tra |
de398406 YS |
331 | mov #OFF_TRA, r9 |
332 | add r15, r9 | |
de398406 YS |
333 | mov.l r8, @r9 ! set TRA value to tra |
334 | sti | |
de398406 | 335 | ! |
e0969e0c | 336 | get_current_thread_info r8, r10 |
de398406 YS |
337 | mov.l @(TI_FLAGS,r8), r8 |
338 | mov #_TIF_SYSCALL_TRACE, r10 | |
339 | tst r10, r8 | |
340 | bf syscall_trace_entry | |
341 | ! | |
e0969e0c SM |
342 | mov.l 2f, r8 ! Number of syscalls |
343 | cmp/hs r8, r3 | |
344 | bt syscall_badsys | |
345 | ! | |
de398406 | 346 | syscall_call: |
e0969e0c | 347 | shll2 r3 ! x4 |
de398406 | 348 | mov.l 3f, r8 ! Load the address of sys_call_table |
e0969e0c SM |
349 | add r8, r3 |
350 | mov.l @r3, r8 | |
de398406 YS |
351 | jsr @r8 ! jump to specific syscall handler |
352 | nop | |
353 | mov.l @(OFF_R0,r15), r12 ! save r0 | |
354 | mov.l r0, @(OFF_R0,r15) ! save the return value | |
355 | ! | |
356 | syscall_exit: | |
357 | cli | |
358 | ! | |
359 | get_current_thread_info r8, r0 | |
360 | mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags | |
361 | tst #_TIF_ALLWORK_MASK, r0 | |
362 | bf syscall_exit_work | |
363 | bra __restore_all | |
364 | nop | |
365 | .align 2 | |
366 | #if !defined(CONFIG_CPU_SH2) | |
367 | 1: .long TRA | |
368 | #endif | |
369 | 2: .long NR_syscalls | |
370 | 3: .long sys_call_table | |
371 | 4: .long do_syscall_trace |