Commit | Line | Data |
---|---|---|
baf4326e | 1 | /* |
58862699 | 2 | * arch/sh/kernel/cpu/sh3/entry.S |
1da177e4 LT |
3 | * |
4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka | |
baf4326e | 5 | * Copyright (C) 2003 - 2006 Paul Mundt |
1da177e4 LT |
6 | * |
7 | * This file is subject to the terms and conditions of the GNU General Public | |
8 | * License. See the file "COPYING" in the main directory of this archive | |
9 | * for more details. | |
1da177e4 | 10 | */ |
1da177e4 | 11 | #include <linux/sys.h> |
711fa809 | 12 | #include <linux/errno.h> |
1da177e4 | 13 | #include <linux/linkage.h> |
1da177e4 LT |
14 | #include <asm/asm-offsets.h> |
15 | #include <asm/thread_info.h> | |
db2e1fa3 | 16 | #include <asm/unistd.h> |
f15cbe6f | 17 | #include <cpu/mmu_context.h> |
1efe4ce3 | 18 | #include <asm/page.h> |
1d015cf0 | 19 | #include <asm/cache.h> |
1da177e4 | 20 | |
1da177e4 LT |
21 | ! NOTE: |
22 | ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address | |
23 | ! to be jumped is too far, but it causes illegal slot exception. | |
24 | ||
25 | /* | |
26 | * entry.S contains the system-call and fault low-level handling routines. | |
27 | * This also contains the timer-interrupt handler, as well as all interrupts | |
28 | * and faults that can result in a task-switch. | |
29 | * | |
30 | * NOTE: This code handles signal-recognition, which happens every time | |
31 | * after a timer-interrupt and after each system call. | |
32 | * | |
33 | * NOTE: This code uses a convention that instructions in the delay slot | |
34 | * of a transfer-control instruction are indented by an extra space, thus: | |
35 | * | |
36 | * jmp @k0 ! control-transfer instruction | |
37 | * ldc k1, ssr ! delay slot | |
38 | * | |
39 | * Stack layout in 'ret_from_syscall': | |
40 | * ptrace needs to have all regs on the stack. | |
41 | * if the order here is changed, it needs to be | |
42 | * updated in ptrace.c and ptrace.h | |
43 | * | |
44 | * r0 | |
45 | * ... | |
46 | * r15 = stack pointer | |
47 | * spc | |
48 | * pr | |
49 | * ssr | |
50 | * gbr | |
51 | * mach | |
52 | * macl | |
53 | * syscall # | |
54 | * | |
55 | */ | |
ab6e570b | 56 | #if defined(CONFIG_KGDB) |
1da177e4 LT |
57 | NMI_VEC = 0x1c0 ! Must catch early for debounce |
58 | #endif | |
59 | ||
60 | /* Offsets to the stack */ | |
61 | OFF_R0 = 0 /* Return value. New ABI also arg4 */ | |
62 | OFF_R1 = 4 /* New ABI: arg5 */ | |
63 | OFF_R2 = 8 /* New ABI: arg6 */ | |
64 | OFF_R3 = 12 /* New ABI: syscall_nr */ | |
65 | OFF_R4 = 16 /* New ABI: arg0 */ | |
66 | OFF_R5 = 20 /* New ABI: arg1 */ | |
67 | OFF_R6 = 24 /* New ABI: arg2 */ | |
68 | OFF_R7 = 28 /* New ABI: arg3 */ | |
69 | OFF_SP = (15*4) | |
70 | OFF_PC = (16*4) | |
71 | OFF_SR = (16*4+8) | |
72 | OFF_TRA = (16*4+6*4) | |
73 | ||
74 | ||
75 | #define k0 r0 | |
76 | #define k1 r1 | |
77 | #define k2 r2 | |
78 | #define k3 r3 | |
79 | #define k4 r4 | |
80 | ||
1da177e4 LT |
81 | #define g_imask r6 /* r6_bank1 */ |
82 | #define k_g_imask r6_bank /* r6_bank1 */ | |
83 | #define current r7 /* r7_bank1 */ | |
84 | ||
de398406 YS |
85 | #include <asm/entry-macros.S> |
86 | ||
1da177e4 LT |
87 | /* |
88 | * Kernel mode register usage: | |
89 | * k0 scratch | |
90 | * k1 scratch | |
91 | * k2 scratch (Exception code) | |
92 | * k3 scratch (Return address) | |
93 | * k4 scratch | |
94 | * k5 reserved | |
95 | * k6 Global Interrupt Mask (0--15 << 4) | |
96 | * k7 CURRENT_THREAD_INFO (pointer to current thread info) | |
97 | */ | |
98 | ||
99 | ! | |
100 | ! TLB Miss / Initial Page write exception handling | |
101 | ! _and_ | |
102 | ! TLB hits, but the access violate the protection. | |
103 | ! It can be valid access, such as stack grow and/or C-O-W. | |
104 | ! | |
105 | ! | |
106 | ! Find the pmd/pte entry and loadtlb | |
107 | ! If it's not found, cause address error (SEGV) | |
108 | ! | |
109 | ! Although this could be written in assembly language (and it'd be faster), | |
110 | ! this first version depends *much* on C implementation. | |
111 | ! | |
112 | ||
1da177e4 LT |
113 | #if defined(CONFIG_MMU) |
114 | .align 2 | |
115 | ENTRY(tlb_miss_load) | |
116 | bra call_dpf | |
117 | mov #0, r5 | |
118 | ||
119 | .align 2 | |
120 | ENTRY(tlb_miss_store) | |
121 | bra call_dpf | |
122 | mov #1, r5 | |
123 | ||
124 | .align 2 | |
125 | ENTRY(initial_page_write) | |
126 | bra call_dpf | |
127 | mov #1, r5 | |
128 | ||
129 | .align 2 | |
130 | ENTRY(tlb_protection_violation_load) | |
131 | bra call_dpf | |
132 | mov #0, r5 | |
133 | ||
134 | .align 2 | |
135 | ENTRY(tlb_protection_violation_store) | |
136 | bra call_dpf | |
137 | mov #1, r5 | |
138 | ||
139 | call_dpf: | |
140 | mov.l 1f, r0 | |
db2e1fa3 PM |
141 | mov r5, r8 |
142 | mov.l @r0, r6 | |
143 | mov r6, r9 | |
144 | mov.l 2f, r0 | |
145 | sts pr, r10 | |
146 | jsr @r0 | |
147 | mov r15, r4 | |
148 | ! | |
149 | tst r0, r0 | |
150 | bf/s 0f | |
151 | lds r10, pr | |
152 | rts | |
153 | nop | |
c347d12c | 154 | 0: mov.l 3f, r0 |
db2e1fa3 PM |
155 | mov r9, r6 |
156 | mov r8, r5 | |
1da177e4 | 157 | jmp @r0 |
db2e1fa3 | 158 | mov r15, r4 |
1da177e4 LT |
159 | |
160 | .align 2 | |
161 | 1: .long MMU_TEA | |
db2e1fa3 | 162 | 2: .long __do_page_fault |
1da177e4 LT |
163 | 3: .long do_page_fault |
164 | ||
165 | .align 2 | |
166 | ENTRY(address_error_load) | |
167 | bra call_dae | |
168 | mov #0,r5 ! writeaccess = 0 | |
169 | ||
170 | .align 2 | |
171 | ENTRY(address_error_store) | |
172 | bra call_dae | |
173 | mov #1,r5 ! writeaccess = 1 | |
174 | ||
175 | .align 2 | |
176 | call_dae: | |
177 | mov.l 1f, r0 | |
178 | mov.l @r0, r6 ! address | |
179 | mov.l 2f, r0 | |
180 | jmp @r0 | |
181 | mov r15, r4 ! regs | |
182 | ||
183 | .align 2 | |
184 | 1: .long MMU_TEA | |
185 | 2: .long do_address_error | |
186 | #endif /* CONFIG_MMU */ | |
187 | ||
1da177e4 LT |
188 | #if defined(CONFIG_SH_STANDARD_BIOS) |
189 | /* Unwind the stack and jmp to the debug entry */ | |
f413d0d9 | 190 | ENTRY(sh_bios_handler) |
1dd22722 MD |
191 | mov.l 1f, r8 |
192 | bsr restore_regs | |
193 | nop | |
194 | ||
195 | lds k2, pr ! restore pr | |
196 | mov k4, r15 | |
1da177e4 LT |
197 | ! |
198 | mov.l 2f, k0 | |
199 | mov.l @k0, k0 | |
200 | jmp @k0 | |
1dd22722 | 201 | ldc k3, ssr |
1da177e4 LT |
202 | .align 2 |
203 | 1: .long 0x300000f0 | |
204 | 2: .long gdb_vbr_vector | |
205 | #endif /* CONFIG_SH_STANDARD_BIOS */ | |
206 | ||
1dd22722 MD |
207 | ! restore_regs() |
208 | ! - restore r0, r1, r2, r3, r4, r5, r6, r7 from the stack | |
209 | ! - switch bank | |
210 | ! - restore r8, r9, r10, r11, r12, r13, r14, r15 from the stack | |
211 | ! - restore spc, pr*, ssr, gbr, mach, macl, skip default tra | |
212 | ! k2 returns original pr | |
213 | ! k3 returns original sr | |
214 | ! k4 returns original stack pointer | |
215 | ! r8 passes SR bitmask, overwritten with restored data on return | |
216 | ! r9 trashed | |
217 | ! BL=0 on entry, on exit BL=1 (depending on r8). | |
218 | ||
2ef7f0da | 219 | ENTRY(restore_regs) |
1da177e4 LT |
220 | mov.l @r15+, r0 |
221 | mov.l @r15+, r1 | |
222 | mov.l @r15+, r2 | |
223 | mov.l @r15+, r3 | |
224 | mov.l @r15+, r4 | |
225 | mov.l @r15+, r5 | |
226 | mov.l @r15+, r6 | |
227 | mov.l @r15+, r7 | |
228 | ! | |
1dd22722 MD |
229 | stc sr, r9 |
230 | or r8, r9 | |
231 | ldc r9, sr | |
1da177e4 LT |
232 | ! |
233 | mov.l @r15+, r8 | |
234 | mov.l @r15+, r9 | |
235 | mov.l @r15+, r10 | |
236 | mov.l @r15+, r11 | |
237 | mov.l @r15+, r12 | |
238 | mov.l @r15+, r13 | |
239 | mov.l @r15+, r14 | |
240 | mov.l @r15+, k4 ! original stack pointer | |
241 | ldc.l @r15+, spc | |
1dd22722 | 242 | mov.l @r15+, k2 ! original PR |
1da177e4 LT |
243 | mov.l @r15+, k3 ! original SR |
244 | ldc.l @r15+, gbr | |
245 | lds.l @r15+, mach | |
246 | lds.l @r15+, macl | |
1dd22722 MD |
247 | rts |
248 | add #4, r15 ! Skip syscall number | |
249 | ||
250 | restore_all: | |
251 | mov.l 7f, r8 | |
252 | bsr restore_regs | |
253 | nop | |
254 | ||
255 | lds k2, pr ! restore pr | |
1da177e4 | 256 | ! |
1da177e4 LT |
257 | ! Calculate new SR value |
258 | mov k3, k2 ! original SR value | |
de398406 YS |
259 | mov #0xf0, k1 |
260 | extu.b k1, k1 | |
261 | not k1, k1 | |
1d015cf0 | 262 | and k1, k2 ! Mask original SR value |
1da177e4 LT |
263 | ! |
264 | mov k3, k0 ! Calculate IMASK-bits | |
265 | shlr2 k0 | |
266 | and #0x3c, k0 | |
267 | cmp/eq #0x3c, k0 | |
268 | bt/s 6f | |
269 | shll2 k0 | |
270 | mov g_imask, k0 | |
271 | ! | |
272 | 6: or k0, k2 ! Set the IMASK-bits | |
273 | ldc k2, ssr | |
274 | ! | |
ab6e570b | 275 | #if defined(CONFIG_KGDB) |
1da177e4 | 276 | ! Clear in_nmi |
6ae5e8d7 | 277 | mov.l 6f, k0 |
1da177e4 LT |
278 | mov #0, k1 |
279 | mov.b k1, @k0 | |
280 | #endif | |
1da177e4 LT |
281 | mov k4, r15 |
282 | rte | |
283 | nop | |
284 | ||
285 | .align 2 | |
1da177e4 | 286 | 5: .long 0x00001000 ! DSP |
ab6e570b | 287 | #ifdef CONFIG_KGDB |
370ac91a | 288 | 6: .long in_nmi |
c596b1a3 | 289 | #endif |
1da177e4 | 290 | 7: .long 0x30000000 |
1da177e4 | 291 | |
de398406 | 292 | ! common exception handler |
716067f2 | 293 | #include "../../entry-common.S" |
de398406 | 294 | |
1da177e4 LT |
295 | ! Exception Vector Base |
296 | ! | |
297 | ! Should be aligned page boundary. | |
298 | ! | |
299 | .balign 4096,0,4096 | |
300 | ENTRY(vbr_base) | |
301 | .long 0 | |
1d015cf0 MD |
302 | ! |
303 | ! 0x100: General exception vector | |
1da177e4 LT |
304 | ! |
305 | .balign 256,0,256 | |
306 | general_exception: | |
1d015cf0 MD |
307 | #ifndef CONFIG_CPU_SUBTYPE_SHX3 |
308 | bra handle_exception | |
309 | sts pr, k3 ! save original pr value in k3 | |
310 | #else | |
311 | mov.l 1f, k4 | |
312 | mov.l @k4, k4 | |
2b1bd1ac PM |
313 | |
314 | ! Is EXPEVT larger than 0x800? | |
315 | mov #0x8, k0 | |
316 | shll8 k0 | |
1d015cf0 | 317 | cmp/hs k0, k4 |
2b1bd1ac PM |
318 | bf 0f |
319 | ||
320 | ! then add 0x580 (k2 is 0xd80 or 0xda0) | |
321 | mov #0x58, k0 | |
322 | shll2 k0 | |
323 | shll2 k0 | |
1d015cf0 | 324 | add k0, k4 |
2b1bd1ac | 325 | 0: |
1d015cf0 | 326 | ! Setup stack and save DSP context (k0 contains original r15 on return) |
01ab1039 | 327 | bsr prepare_stack |
2b1bd1ac | 328 | nop |
9b3a53ab | 329 | |
1d015cf0 | 330 | ! Save registers / Switch to bank 0 |
7a516280 | 331 | mov k4, k2 ! keep vector in k2 |
2ef7f0da | 332 | mov.l 1f, k4 ! SR bits to clear in k4 |
1d015cf0 | 333 | bsr save_regs ! needs original pr value in k3 |
2ef7f0da | 334 | nop |
1d015cf0 MD |
335 | |
336 | bra handle_exception_special | |
1da177e4 | 337 | nop |
1da177e4 LT |
338 | |
339 | .align 2 | |
340 | 1: .long EXPEVT | |
1d015cf0 | 341 | #endif |
1da177e4 | 342 | |
01ab1039 | 343 | ! prepare_stack() |
1d015cf0 MD |
344 | ! - roll back gRB |
345 | ! - switch to kernel stack | |
1d015cf0 MD |
346 | ! k0 returns original sp (after roll back) |
347 | ! k1 trashed | |
348 | ! k2 trashed | |
1efe4ce3 | 349 | |
01ab1039 | 350 | prepare_stack: |
1efe4ce3 SM |
351 | #ifdef CONFIG_GUSA |
352 | ! Check for roll back gRB (User and Kernel) | |
353 | mov r15, k0 | |
354 | shll k0 | |
355 | bf/s 1f | |
356 | shll k0 | |
357 | bf/s 1f | |
358 | stc spc, k1 | |
359 | stc r0_bank, k0 | |
360 | cmp/hs k0, k1 ! test k1 (saved PC) >= k0 (saved r0) | |
361 | bt/s 2f | |
362 | stc r1_bank, k1 | |
363 | ||
364 | add #-2, k0 | |
365 | add r15, k0 | |
366 | ldc k0, spc ! PC = saved r0 + r15 - 2 | |
367 | 2: mov k1, r15 ! SP = r1 | |
368 | 1: | |
369 | #endif | |
1d015cf0 | 370 | ! Switch to kernel stack if needed |
1da177e4 LT |
371 | stc ssr, k0 ! Is it from kernel space? |
372 | shll k0 ! Check MD bit (bit30) by shifting it into... | |
373 | shll k0 ! ...the T bit | |
374 | bt/s 1f ! It's a kernel to kernel transition. | |
375 | mov r15, k0 ! save original stack to k0 | |
376 | /* User space to kernel */ | |
510c72ad | 377 | mov #(THREAD_SIZE >> 10), k1 |
a6a31139 | 378 | shll8 k1 ! k1 := THREAD_SIZE |
510c72ad | 379 | shll2 k1 |
1da177e4 LT |
380 | add current, k1 |
381 | mov k1, r15 ! change to kernel stack | |
382 | ! | |
1d015cf0 | 383 | 1: |
1d015cf0 MD |
384 | rts |
385 | nop | |
01ab1039 | 386 | |
1d015cf0 MD |
387 | ! |
388 | ! 0x400: Instruction and Data TLB miss exception vector | |
389 | ! | |
390 | .balign 1024,0,1024 | |
391 | tlb_miss: | |
392 | sts pr, k3 ! save original pr value in k3 | |
baf4326e | 393 | |
1d015cf0 | 394 | handle_exception: |
0197f21c MD |
395 | mova exception_data, k0 |
396 | ||
1d015cf0 | 397 | ! Setup stack and save DSP context (k0 contains original r15 on return) |
01ab1039 | 398 | bsr prepare_stack |
0197f21c | 399 | PREF(k0) |
1d015cf0 MD |
400 | |
401 | ! Save registers / Switch to bank 0 | |
402 | mov.l 5f, k2 ! vector register address | |
2ef7f0da | 403 | mov.l 1f, k4 ! SR bits to clear in k4 |
1d015cf0 MD |
404 | bsr save_regs ! needs original pr value in k3 |
405 | mov.l @k2, k2 ! read out vector and keep in k2 | |
406 | ||
407 | handle_exception_special: | |
408 | ! Setup return address and jump to exception handler | |
409 | mov.l 7f, r9 ! fetch return address | |
410 | stc r2_bank, r0 ! k2 (vector) | |
411 | mov.l 6f, r10 | |
412 | shlr2 r0 | |
413 | shlr r0 | |
414 | mov.l @(r0, r10), r10 | |
415 | jmp @r10 | |
416 | lds r9, pr ! put return address in pr | |
417 | ||
418 | .align L1_CACHE_SHIFT | |
419 | ||
420 | ! save_regs() | |
4f099ebb | 421 | ! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack |
1d015cf0 MD |
422 | ! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack |
423 | ! - switch bank | |
424 | ! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack | |
425 | ! k0 contains original stack pointer* | |
426 | ! k1 trashed | |
1d015cf0 | 427 | ! k3 passes original pr* |
2ef7f0da | 428 | ! k4 passes SR bitmask |
1d015cf0 MD |
429 | ! BL=1 on entry, on exit BL=0. |
430 | ||
2ef7f0da | 431 | ENTRY(save_regs) |
1d015cf0 | 432 | mov #-1, r1 |
1d015cf0 | 433 | mov.l k1, @-r15 ! set TRA (default: -1) |
1da177e4 LT |
434 | sts.l macl, @-r15 |
435 | sts.l mach, @-r15 | |
436 | stc.l gbr, @-r15 | |
437 | stc.l ssr, @-r15 | |
1d015cf0 | 438 | mov.l k3, @-r15 ! original pr in k3 |
1da177e4 | 439 | stc.l spc, @-r15 |
1d015cf0 MD |
440 | |
441 | mov.l k0, @-r15 ! original stack pointer in k0 | |
1da177e4 LT |
442 | mov.l r14, @-r15 |
443 | mov.l r13, @-r15 | |
444 | mov.l r12, @-r15 | |
445 | mov.l r11, @-r15 | |
446 | mov.l r10, @-r15 | |
447 | mov.l r9, @-r15 | |
448 | mov.l r8, @-r15 | |
1d015cf0 MD |
449 | |
450 | mov.l 0f, k3 ! SR bits to set in k3 | |
1d015cf0 | 451 | |
2ef7f0da MD |
452 | ! fall-through |
453 | ||
454 | ! save_low_regs() | |
455 | ! - modify SR for bank switch | |
456 | ! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack | |
457 | ! k3 passes bits to set in SR | |
458 | ! k4 passes bits to clear in SR | |
459 | ||
460 | ENTRY(save_low_regs) | |
1d015cf0 MD |
461 | stc sr, r8 |
462 | or k3, r8 | |
463 | and k4, r8 | |
464 | ldc r8, sr | |
465 | ||
1da177e4 LT |
466 | mov.l r7, @-r15 |
467 | mov.l r6, @-r15 | |
468 | mov.l r5, @-r15 | |
469 | mov.l r4, @-r15 | |
470 | mov.l r3, @-r15 | |
471 | mov.l r2, @-r15 | |
472 | mov.l r1, @-r15 | |
baf4326e | 473 | rts |
1d015cf0 | 474 | mov.l r0, @-r15 |
1da177e4 | 475 | |
1d015cf0 MD |
476 | ! |
477 | ! 0x600: Interrupt / NMI vector | |
478 | ! | |
479 | .balign 512,0,512 | |
480 | ENTRY(handle_interrupt) | |
481 | #if defined(CONFIG_KGDB) | |
482 | mov.l 2f, k2 | |
483 | ! Debounce (filter nested NMI) | |
484 | mov.l @k2, k0 | |
485 | mov.l 9f, k1 | |
486 | cmp/eq k1, k0 | |
487 | bf 11f | |
488 | mov.l 10f, k1 | |
489 | tas.b @k1 | |
490 | bt 11f | |
491 | rte | |
492 | nop | |
1da177e4 | 493 | .align 2 |
1d015cf0 MD |
494 | 9: .long NMI_VEC |
495 | 10: .long in_nmi | |
496 | 11: | |
497 | #endif /* defined(CONFIG_KGDB) */ | |
498 | sts pr, k3 ! save original pr value in k3 | |
0197f21c | 499 | mova exception_data, k0 |
1da177e4 | 500 | |
1d015cf0 | 501 | ! Setup stack and save DSP context (k0 contains original r15 on return) |
01ab1039 | 502 | bsr prepare_stack |
0197f21c | 503 | PREF(k0) |
baf4326e | 504 | |
1d015cf0 | 505 | ! Save registers / Switch to bank 0 |
2ef7f0da | 506 | mov.l 1f, k4 ! SR bits to clear in k4 |
1d015cf0 MD |
507 | bsr save_regs ! needs original pr value in k3 |
508 | mov #-1, k2 ! default vector kept in k2 | |
509 | ||
510 | ! Setup return address and jump to do_IRQ | |
511 | mov.l 4f, r9 ! fetch return address | |
512 | lds r9, pr ! put return address in pr | |
513 | mov.l 2f, r4 | |
514 | mov.l 3f, r9 | |
515 | mov.l @r4, r4 ! pass INTEVT vector as arg0 | |
516 | jmp @r9 | |
517 | mov r15, r5 ! pass saved registers as arg1 | |
baf4326e | 518 | |
1da177e4 LT |
519 | ENTRY(exception_none) |
520 | rts | |
521 | nop | |
1d015cf0 MD |
522 | |
523 | .align L1_CACHE_SHIFT | |
524 | exception_data: | |
525 | 0: .long 0x000080f0 ! FD=1, IMASK=15 | |
526 | 1: .long 0xcfffffff ! RB=0, BL=0 | |
527 | 2: .long INTEVT | |
528 | 3: .long do_IRQ | |
529 | 4: .long ret_from_irq | |
530 | 5: .long EXPEVT | |
531 | 6: .long exception_handling_table | |
532 | 7: .long ret_from_exception |