Commit | Line | Data |
---|---|---|
5f97f7f9 HS |
1 | /* |
2 | * Copyright (C) 2004-2006 Atmel Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | /* | |
10 | * This file contains the low-level entry-points into the kernel, that is, | |
11 | * exception handlers, debug trap handlers, interrupt handlers and the | |
12 | * system call handler. | |
13 | */ | |
14 | #include <linux/errno.h> | |
15 | ||
16 | #include <asm/asm.h> | |
17 | #include <asm/hardirq.h> | |
18 | #include <asm/irq.h> | |
19 | #include <asm/ocd.h> | |
20 | #include <asm/page.h> | |
21 | #include <asm/pgtable.h> | |
22 | #include <asm/ptrace.h> | |
23 | #include <asm/sysreg.h> | |
24 | #include <asm/thread_info.h> | |
25 | #include <asm/unistd.h> | |
26 | ||
27 | #ifdef CONFIG_PREEMPT | |
28 | # define preempt_stop mask_interrupts | |
29 | #else | |
30 | # define preempt_stop | |
31 | # define fault_resume_kernel fault_restore_all | |
32 | #endif | |
33 | ||
34 | #define __MASK(x) ((1 << (x)) - 1) | |
35 | #define IRQ_MASK ((__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) | \ | |
36 | (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)) | |
37 | ||
38 | .section .ex.text,"ax",@progbits | |
39 | .align 2 | |
40 | exception_vectors: | |
41 | bral handle_critical | |
42 | .align 2 | |
43 | bral handle_critical | |
44 | .align 2 | |
45 | bral do_bus_error_write | |
46 | .align 2 | |
47 | bral do_bus_error_read | |
48 | .align 2 | |
49 | bral do_nmi_ll | |
50 | .align 2 | |
51 | bral handle_address_fault | |
52 | .align 2 | |
53 | bral handle_protection_fault | |
54 | .align 2 | |
55 | bral handle_debug | |
56 | .align 2 | |
57 | bral do_illegal_opcode_ll | |
58 | .align 2 | |
59 | bral do_illegal_opcode_ll | |
60 | .align 2 | |
61 | bral do_illegal_opcode_ll | |
62 | .align 2 | |
63 | bral do_fpe_ll | |
64 | .align 2 | |
65 | bral do_illegal_opcode_ll | |
66 | .align 2 | |
67 | bral handle_address_fault | |
68 | .align 2 | |
69 | bral handle_address_fault | |
70 | .align 2 | |
71 | bral handle_protection_fault | |
72 | .align 2 | |
73 | bral handle_protection_fault | |
74 | .align 2 | |
75 | bral do_dtlb_modified | |
76 | ||
5f97f7f9 HS |
77 | #define tlbmiss_save pushm r0-r3 |
78 | #define tlbmiss_restore popm r0-r3 | |
79 | ||
d704fb0c | 80 | .org 0x50 |
5f97f7f9 HS |
81 | .global itlb_miss |
82 | itlb_miss: | |
83 | tlbmiss_save | |
84 | rjmp tlb_miss_common | |
85 | ||
d704fb0c | 86 | .org 0x60 |
5f97f7f9 HS |
87 | dtlb_miss_read: |
88 | tlbmiss_save | |
89 | rjmp tlb_miss_common | |
90 | ||
d704fb0c | 91 | .org 0x70 |
5f97f7f9 HS |
92 | dtlb_miss_write: |
93 | tlbmiss_save | |
94 | ||
95 | .global tlb_miss_common | |
d704fb0c | 96 | .align 2 |
5f97f7f9 | 97 | tlb_miss_common: |
c0c3e816 HS |
98 | mfsr r0, SYSREG_TLBEAR |
99 | mfsr r1, SYSREG_PTBR | |
5f97f7f9 | 100 | |
cfd23e93 HS |
101 | /* |
102 | * First level lookup: The PGD contains virtual pointers to | |
103 | * the second-level page tables, but they may be NULL if not | |
104 | * present. | |
105 | */ | |
5f97f7f9 | 106 | pgtbl_lookup: |
c0c3e816 HS |
107 | lsr r2, r0, PGDIR_SHIFT |
108 | ld.w r3, r1[r2 << 2] | |
109 | bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT | |
cfd23e93 HS |
110 | cp.w r3, 0 |
111 | breq page_table_not_present | |
5f97f7f9 HS |
112 | |
113 | /* Second level lookup */ | |
c0c3e816 HS |
114 | ld.w r2, r3[r1 << 2] |
115 | mfsr r0, SYSREG_TLBARLO | |
116 | bld r2, _PAGE_BIT_PRESENT | |
5f97f7f9 HS |
117 | brcc page_not_present |
118 | ||
119 | /* Mark the page as accessed */ | |
c0c3e816 HS |
120 | sbr r2, _PAGE_BIT_ACCESSED |
121 | st.w r3[r1 << 2], r2 | |
5f97f7f9 HS |
122 | |
123 | /* Drop software flags */ | |
c0c3e816 HS |
124 | andl r2, _PAGE_FLAGS_HARDWARE_MASK & 0xffff |
125 | mtsr SYSREG_TLBELO, r2 | |
5f97f7f9 HS |
126 | |
127 | /* Figure out which entry we want to replace */ | |
c0c3e816 | 128 | mfsr r1, SYSREG_MMUCR |
5f97f7f9 HS |
129 | clz r2, r0 |
130 | brcc 1f | |
c0c3e816 HS |
131 | mov r3, -1 /* All entries have been accessed, */ |
132 | mov r2, 0 /* so start at 0 */ | |
133 | mtsr SYSREG_TLBARLO, r3 /* and reset TLBAR */ | |
5f97f7f9 | 134 | |
c0c3e816 HS |
135 | 1: bfins r1, r2, SYSREG_DRP_OFFSET, SYSREG_DRP_SIZE |
136 | mtsr SYSREG_MMUCR, r1 | |
5f97f7f9 HS |
137 | tlbw |
138 | ||
139 | tlbmiss_restore | |
140 | rete | |
141 | ||
cfd23e93 HS |
142 | /* The slow path of the TLB miss handler */ |
143 | .align 2 | |
144 | page_table_not_present: | |
a9a934f2 HS |
145 | /* Do we need to synchronize with swapper_pg_dir? */ |
146 | bld r0, 31 | |
147 | brcs sync_with_swapper_pg_dir | |
148 | ||
cfd23e93 HS |
149 | page_not_present: |
150 | tlbmiss_restore | |
151 | sub sp, 4 | |
152 | stmts --sp, r0-lr | |
8d29b7b9 | 153 | call save_full_context_ex |
cfd23e93 HS |
154 | mfsr r12, SYSREG_ECR |
155 | mov r11, sp | |
8d29b7b9 | 156 | call do_page_fault |
cfd23e93 HS |
157 | rjmp ret_from_exception |
158 | ||
a9a934f2 HS |
159 | .align 2 |
160 | sync_with_swapper_pg_dir: | |
161 | /* | |
162 | * If swapper_pg_dir contains a non-NULL second-level page | |
163 | * table pointer, copy it into the current PGD. If not, we | |
164 | * must handle it as a full-blown page fault. | |
165 | * | |
166 | * Jumping back to pgtbl_lookup causes an unnecessary lookup, | |
167 | * but it is guaranteed to be a cache hit, it won't happen | |
168 | * very often, and we absolutely do not want to sacrifice any | |
169 | * performance in the fast path in order to improve this. | |
170 | */ | |
171 | mov r1, lo(swapper_pg_dir) | |
172 | orh r1, hi(swapper_pg_dir) | |
173 | ld.w r3, r1[r2 << 2] | |
174 | cp.w r3, 0 | |
175 | breq page_not_present | |
176 | mfsr r1, SYSREG_PTBR | |
177 | st.w r1[r2 << 2], r3 | |
178 | rjmp pgtbl_lookup | |
179 | ||
180 | /* | |
181 | * We currently have two bytes left at this point until we | |
182 | * crash into the system call handler... | |
183 | * | |
184 | * Don't worry, the assembler will let us know. | |
185 | */ | |
186 | ||
5f97f7f9 HS |
187 | |
188 | /* --- System Call --- */ | |
189 | ||
d704fb0c | 190 | .org 0x100 |
5f97f7f9 | 191 | system_call: |
a7e30b8d PR |
192 | #ifdef CONFIG_PREEMPT |
193 | mask_interrupts | |
194 | #endif | |
5f97f7f9 HS |
195 | pushm r12 /* r12_orig */ |
196 | stmts --sp, r0-lr | |
a7e30b8d | 197 | |
5f97f7f9 HS |
198 | mfsr r0, SYSREG_RAR_SUP |
199 | mfsr r1, SYSREG_RSR_SUP | |
a7e30b8d PR |
200 | #ifdef CONFIG_PREEMPT |
201 | unmask_interrupts | |
202 | #endif | |
203 | zero_fp | |
5f97f7f9 HS |
204 | stm --sp, r0-r1 |
205 | ||
206 | /* check for syscall tracing */ | |
207 | get_thread_info r0 | |
208 | ld.w r1, r0[TI_flags] | |
209 | bld r1, TIF_SYSCALL_TRACE | |
210 | brcs syscall_trace_enter | |
211 | ||
212 | syscall_trace_cont: | |
213 | cp.w r8, NR_syscalls | |
214 | brhs syscall_badsys | |
215 | ||
216 | lddpc lr, syscall_table_addr | |
217 | ld.w lr, lr[r8 << 2] | |
218 | mov r8, r5 /* 5th argument (6th is pushed by stub) */ | |
219 | icall lr | |
220 | ||
221 | .global syscall_return | |
222 | syscall_return: | |
223 | get_thread_info r0 | |
224 | mask_interrupts /* make sure we don't miss an interrupt | |
225 | setting need_resched or sigpending | |
226 | between sampling and the rets */ | |
227 | ||
228 | /* Store the return value so that the correct value is loaded below */ | |
229 | stdsp sp[REG_R12], r12 | |
230 | ||
231 | ld.w r1, r0[TI_flags] | |
232 | andl r1, _TIF_ALLWORK_MASK, COH | |
233 | brne syscall_exit_work | |
234 | ||
235 | syscall_exit_cont: | |
236 | popm r8-r9 | |
237 | mtsr SYSREG_RAR_SUP, r8 | |
238 | mtsr SYSREG_RSR_SUP, r9 | |
239 | ldmts sp++, r0-lr | |
240 | sub sp, -4 /* r12_orig */ | |
241 | rets | |
242 | ||
243 | .align 2 | |
244 | syscall_table_addr: | |
245 | .long sys_call_table | |
246 | ||
247 | syscall_badsys: | |
248 | mov r12, -ENOSYS | |
249 | rjmp syscall_return | |
250 | ||
251 | .global ret_from_fork | |
252 | ret_from_fork: | |
8d29b7b9 | 253 | call schedule_tail |
5f97f7f9 HS |
254 | |
255 | /* check for syscall tracing */ | |
256 | get_thread_info r0 | |
257 | ld.w r1, r0[TI_flags] | |
258 | andl r1, _TIF_ALLWORK_MASK, COH | |
259 | brne syscall_exit_work | |
260 | rjmp syscall_exit_cont | |
261 | ||
262 | syscall_trace_enter: | |
263 | pushm r8-r12 | |
8d29b7b9 | 264 | call syscall_trace |
5f97f7f9 HS |
265 | popm r8-r12 |
266 | rjmp syscall_trace_cont | |
267 | ||
268 | syscall_exit_work: | |
269 | bld r1, TIF_SYSCALL_TRACE | |
270 | brcc 1f | |
271 | unmask_interrupts | |
8d29b7b9 | 272 | call syscall_trace |
5f97f7f9 HS |
273 | mask_interrupts |
274 | ld.w r1, r0[TI_flags] | |
275 | ||
276 | 1: bld r1, TIF_NEED_RESCHED | |
277 | brcc 2f | |
278 | unmask_interrupts | |
8d29b7b9 | 279 | call schedule |
5f97f7f9 HS |
280 | mask_interrupts |
281 | ld.w r1, r0[TI_flags] | |
282 | rjmp 1b | |
283 | ||
284 | 2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | |
285 | tst r1, r2 | |
286 | breq 3f | |
287 | unmask_interrupts | |
288 | mov r12, sp | |
289 | mov r11, r0 | |
8d29b7b9 | 290 | call do_notify_resume |
5f97f7f9 HS |
291 | mask_interrupts |
292 | ld.w r1, r0[TI_flags] | |
293 | rjmp 1b | |
294 | ||
295 | 3: bld r1, TIF_BREAKPOINT | |
296 | brcc syscall_exit_cont | |
2507bc13 | 297 | rjmp enter_monitor_mode |
5f97f7f9 | 298 | |
5f97f7f9 | 299 | /* This function expects to find offending PC in SYSREG_RAR_EX */ |
2507bc13 HS |
300 | .type save_full_context_ex, @function |
301 | .align 2 | |
5f97f7f9 | 302 | save_full_context_ex: |
2507bc13 HS |
303 | mfsr r11, SYSREG_RAR_EX |
304 | sub r9, pc, . - debug_trampoline | |
5f97f7f9 | 305 | mfsr r8, SYSREG_RSR_EX |
2507bc13 HS |
306 | cp.w r9, r11 |
307 | breq 3f | |
5f97f7f9 HS |
308 | mov r12, r8 |
309 | andh r8, (MODE_MASK >> 16), COH | |
5f97f7f9 HS |
310 | brne 2f |
311 | ||
312 | 1: pushm r11, r12 /* PC and SR */ | |
313 | unmask_exceptions | |
314 | ret r12 | |
315 | ||
316 | 2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR) | |
317 | stdsp sp[4], r10 /* replace saved SP */ | |
318 | rjmp 1b | |
319 | ||
2507bc13 HS |
320 | /* |
321 | * The debug handler set up a trampoline to make us | |
322 | * automatically enter monitor mode upon return, but since | |
323 | * we're saving the full context, we must assume that the | |
324 | * exception handler might want to alter the return address | |
325 | * and/or status register. So we need to restore the original | |
326 | * context and enter monitor mode manually after the exception | |
327 | * has been handled. | |
328 | */ | |
329 | 3: get_thread_info r8 | |
330 | ld.w r11, r8[TI_rar_saved] | |
331 | ld.w r12, r8[TI_rsr_saved] | |
332 | rjmp 1b | |
333 | .size save_full_context_ex, . - save_full_context_ex | |
334 | ||
5f97f7f9 HS |
335 | /* Low-level exception handlers */ |
336 | handle_critical: | |
9e3f544d HS |
337 | /* |
338 | * AT32AP700x errata: | |
339 | * | |
340 | * After a Java stack overflow or underflow trap, any CPU | |
341 | * memory access may cause erratic behavior. This will happen | |
342 | * when the four least significant bits of the JOSP system | |
343 | * register contains any value between 9 and 15 (inclusive). | |
344 | * | |
345 | * Possible workarounds: | |
346 | * - Don't use the Java Extension Module | |
347 | * - Ensure that the stack overflow and underflow trap | |
348 | * handlers do not do any memory access or trigger any | |
349 | * exceptions before the overflow/underflow condition is | |
350 | * cleared (by incrementing or decrementing the JOSP) | |
351 | * - Make sure that JOSP does not contain any problematic | |
352 | * value before doing any exception or interrupt | |
353 | * processing. | |
354 | * - Set up a critical exception handler which writes a | |
355 | * known-to-be-safe value, e.g. 4, to JOSP before doing | |
356 | * any further processing. | |
357 | * | |
358 | * We'll use the last workaround for now since we cannot | |
359 | * guarantee that user space processes don't use Java mode. | |
360 | * Non-well-behaving userland will be terminated with extreme | |
361 | * prejudice. | |
362 | */ | |
363 | #ifdef CONFIG_CPU_AT32AP700X | |
364 | /* | |
365 | * There's a chance we can't touch memory, so temporarily | |
366 | * borrow PTBR to save the stack pointer while we fix things | |
367 | * up... | |
368 | */ | |
369 | mtsr SYSREG_PTBR, sp | |
370 | mov sp, 4 | |
371 | mtsr SYSREG_JOSP, sp | |
372 | mfsr sp, SYSREG_PTBR | |
373 | sub pc, -2 | |
374 | ||
375 | /* Push most of pt_regs on stack. We'll do the rest later */ | |
5998a3cf | 376 | sub sp, 4 |
9e3f544d HS |
377 | pushm r0-r12 |
378 | ||
379 | /* PTBR mirrors current_thread_info()->task->active_mm->pgd */ | |
380 | get_thread_info r0 | |
381 | ld.w r1, r0[TI_task] | |
382 | ld.w r2, r1[TSK_active_mm] | |
383 | ld.w r3, r2[MM_pgd] | |
384 | mtsr SYSREG_PTBR, r3 | |
385 | #else | |
386 | sub sp, 4 | |
387 | pushm r0-r12 | |
388 | #endif | |
389 | sub r0, sp, -(14 * 4) | |
390 | mov r1, lr | |
391 | mfsr r2, SYSREG_RAR_EX | |
392 | mfsr r3, SYSREG_RSR_EX | |
393 | pushm r0-r3 | |
394 | ||
5f97f7f9 HS |
395 | mfsr r12, SYSREG_ECR |
396 | mov r11, sp | |
8d29b7b9 | 397 | call do_critical_exception |
5f97f7f9 HS |
398 | |
399 | /* We should never get here... */ | |
400 | bad_return: | |
401 | sub r12, pc, (. - 1f) | |
402 | bral panic | |
403 | .align 2 | |
404 | 1: .asciz "Return from critical exception!" | |
405 | ||
406 | .align 1 | |
407 | do_bus_error_write: | |
408 | sub sp, 4 | |
409 | stmts --sp, r0-lr | |
8d29b7b9 | 410 | call save_full_context_ex |
5f97f7f9 HS |
411 | mov r11, 1 |
412 | rjmp 1f | |
413 | ||
414 | do_bus_error_read: | |
415 | sub sp, 4 | |
416 | stmts --sp, r0-lr | |
8d29b7b9 | 417 | call save_full_context_ex |
5f97f7f9 HS |
418 | mov r11, 0 |
419 | 1: mfsr r12, SYSREG_BEAR | |
420 | mov r10, sp | |
8d29b7b9 | 421 | call do_bus_error |
5f97f7f9 HS |
422 | rjmp ret_from_exception |
423 | ||
424 | .align 1 | |
425 | do_nmi_ll: | |
426 | sub sp, 4 | |
427 | stmts --sp, r0-lr | |
92b728c1 HS |
428 | mfsr r9, SYSREG_RSR_NMI |
429 | mfsr r8, SYSREG_RAR_NMI | |
430 | bfextu r0, r9, MODE_SHIFT, 3 | |
431 | brne 2f | |
432 | ||
433 | 1: pushm r8, r9 /* PC and SR */ | |
5f97f7f9 HS |
434 | mfsr r12, SYSREG_ECR |
435 | mov r11, sp | |
8d29b7b9 | 436 | call do_nmi |
92b728c1 HS |
437 | popm r8-r9 |
438 | mtsr SYSREG_RAR_NMI, r8 | |
439 | tst r0, r0 | |
440 | mtsr SYSREG_RSR_NMI, r9 | |
441 | brne 3f | |
442 | ||
443 | ldmts sp++, r0-lr | |
444 | sub sp, -4 /* skip r12_orig */ | |
445 | rete | |
446 | ||
447 | 2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR) | |
448 | stdsp sp[4], r10 /* replace saved SP */ | |
449 | rjmp 1b | |
450 | ||
451 | 3: popm lr | |
452 | sub sp, -4 /* skip sp */ | |
453 | popm r0-r12 | |
454 | sub sp, -4 /* skip r12_orig */ | |
455 | rete | |
5f97f7f9 HS |
456 | |
457 | handle_address_fault: | |
458 | sub sp, 4 | |
459 | stmts --sp, r0-lr | |
8d29b7b9 | 460 | call save_full_context_ex |
5f97f7f9 HS |
461 | mfsr r12, SYSREG_ECR |
462 | mov r11, sp | |
8d29b7b9 | 463 | call do_address_exception |
5f97f7f9 HS |
464 | rjmp ret_from_exception |
465 | ||
466 | handle_protection_fault: | |
467 | sub sp, 4 | |
468 | stmts --sp, r0-lr | |
8d29b7b9 | 469 | call save_full_context_ex |
5f97f7f9 HS |
470 | mfsr r12, SYSREG_ECR |
471 | mov r11, sp | |
8d29b7b9 | 472 | call do_page_fault |
5f97f7f9 HS |
473 | rjmp ret_from_exception |
474 | ||
475 | .align 1 | |
476 | do_illegal_opcode_ll: | |
477 | sub sp, 4 | |
478 | stmts --sp, r0-lr | |
8d29b7b9 | 479 | call save_full_context_ex |
5f97f7f9 HS |
480 | mfsr r12, SYSREG_ECR |
481 | mov r11, sp | |
8d29b7b9 | 482 | call do_illegal_opcode |
5f97f7f9 HS |
483 | rjmp ret_from_exception |
484 | ||
485 | do_dtlb_modified: | |
486 | pushm r0-r3 | |
487 | mfsr r1, SYSREG_TLBEAR | |
488 | mfsr r0, SYSREG_PTBR | |
489 | lsr r2, r1, PGDIR_SHIFT | |
490 | ld.w r0, r0[r2 << 2] | |
491 | lsl r1, (32 - PGDIR_SHIFT) | |
492 | lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT | |
493 | ||
494 | /* Translate to virtual address in P1 */ | |
495 | andl r0, 0xf000 | |
496 | sbr r0, 31 | |
497 | add r2, r0, r1 << 2 | |
498 | ld.w r3, r2[0] | |
499 | sbr r3, _PAGE_BIT_DIRTY | |
500 | mov r0, r3 | |
501 | st.w r2[0], r3 | |
502 | ||
503 | /* The page table is up-to-date. Update the TLB entry as well */ | |
504 | andl r0, lo(_PAGE_FLAGS_HARDWARE_MASK) | |
505 | mtsr SYSREG_TLBELO, r0 | |
506 | ||
507 | /* MMUCR[DRP] is updated automatically, so let's go... */ | |
508 | tlbw | |
509 | ||
510 | popm r0-r3 | |
511 | rete | |
512 | ||
513 | do_fpe_ll: | |
514 | sub sp, 4 | |
515 | stmts --sp, r0-lr | |
8d29b7b9 | 516 | call save_full_context_ex |
5f97f7f9 HS |
517 | unmask_interrupts |
518 | mov r12, 26 | |
519 | mov r11, sp | |
8d29b7b9 | 520 | call do_fpe |
5f97f7f9 HS |
521 | rjmp ret_from_exception |
522 | ||
523 | ret_from_exception: | |
524 | mask_interrupts | |
525 | lddsp r4, sp[REG_SR] | |
2507bc13 | 526 | |
5f97f7f9 HS |
527 | andh r4, (MODE_MASK >> 16), COH |
528 | brne fault_resume_kernel | |
529 | ||
530 | get_thread_info r0 | |
531 | ld.w r1, r0[TI_flags] | |
532 | andl r1, _TIF_WORK_MASK, COH | |
533 | brne fault_exit_work | |
534 | ||
535 | fault_resume_user: | |
536 | popm r8-r9 | |
537 | mask_exceptions | |
538 | mtsr SYSREG_RAR_EX, r8 | |
539 | mtsr SYSREG_RSR_EX, r9 | |
540 | ldmts sp++, r0-lr | |
541 | sub sp, -4 | |
542 | rete | |
543 | ||
544 | fault_resume_kernel: | |
545 | #ifdef CONFIG_PREEMPT | |
546 | get_thread_info r0 | |
547 | ld.w r2, r0[TI_preempt_count] | |
548 | cp.w r2, 0 | |
549 | brne 1f | |
550 | ld.w r1, r0[TI_flags] | |
551 | bld r1, TIF_NEED_RESCHED | |
552 | brcc 1f | |
553 | lddsp r4, sp[REG_SR] | |
554 | bld r4, SYSREG_GM_OFFSET | |
555 | brcs 1f | |
8d29b7b9 | 556 | call preempt_schedule_irq |
5f97f7f9 HS |
557 | 1: |
558 | #endif | |
559 | ||
560 | popm r8-r9 | |
561 | mask_exceptions | |
562 | mfsr r1, SYSREG_SR | |
563 | mtsr SYSREG_RAR_EX, r8 | |
564 | mtsr SYSREG_RSR_EX, r9 | |
565 | popm lr | |
566 | sub sp, -4 /* ignore SP */ | |
567 | popm r0-r12 | |
568 | sub sp, -4 /* ignore r12_orig */ | |
569 | rete | |
570 | ||
571 | irq_exit_work: | |
572 | /* Switch to exception mode so that we can share the same code. */ | |
573 | mfsr r8, SYSREG_SR | |
574 | cbr r8, SYSREG_M0_OFFSET | |
575 | orh r8, hi(SYSREG_BIT(M1) | SYSREG_BIT(M2)) | |
576 | mtsr SYSREG_SR, r8 | |
577 | sub pc, -2 | |
578 | get_thread_info r0 | |
579 | ld.w r1, r0[TI_flags] | |
580 | ||
581 | fault_exit_work: | |
582 | bld r1, TIF_NEED_RESCHED | |
583 | brcc 1f | |
584 | unmask_interrupts | |
8d29b7b9 | 585 | call schedule |
5f97f7f9 HS |
586 | mask_interrupts |
587 | ld.w r1, r0[TI_flags] | |
588 | rjmp fault_exit_work | |
589 | ||
590 | 1: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | |
591 | tst r1, r2 | |
592 | breq 2f | |
593 | unmask_interrupts | |
594 | mov r12, sp | |
595 | mov r11, r0 | |
8d29b7b9 | 596 | call do_notify_resume |
5f97f7f9 HS |
597 | mask_interrupts |
598 | ld.w r1, r0[TI_flags] | |
599 | rjmp fault_exit_work | |
600 | ||
601 | 2: bld r1, TIF_BREAKPOINT | |
602 | brcc fault_resume_user | |
2507bc13 HS |
603 | rjmp enter_monitor_mode |
604 | ||
605 | .section .kprobes.text, "ax", @progbits | |
606 | .type handle_debug, @function | |
607 | handle_debug: | |
608 | sub sp, 4 /* r12_orig */ | |
609 | stmts --sp, r0-lr | |
610 | mfsr r8, SYSREG_RAR_DBG | |
611 | mfsr r9, SYSREG_RSR_DBG | |
612 | unmask_exceptions | |
613 | pushm r8-r9 | |
614 | bfextu r9, r9, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | |
615 | brne debug_fixup_regs | |
616 | ||
617 | .Ldebug_fixup_cont: | |
618 | #ifdef CONFIG_TRACE_IRQFLAGS | |
8d29b7b9 | 619 | call trace_hardirqs_off |
2507bc13 HS |
620 | #endif |
621 | mov r12, sp | |
8d29b7b9 | 622 | call do_debug |
2507bc13 HS |
623 | mov sp, r12 |
624 | ||
625 | lddsp r2, sp[REG_SR] | |
626 | bfextu r3, r2, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | |
627 | brne debug_resume_kernel | |
628 | ||
629 | get_thread_info r0 | |
630 | ld.w r1, r0[TI_flags] | |
631 | mov r2, _TIF_DBGWORK_MASK | |
632 | tst r1, r2 | |
633 | brne debug_exit_work | |
634 | ||
635 | bld r1, TIF_SINGLE_STEP | |
636 | brcc 1f | |
637 | mfdr r4, OCD_DC | |
638 | sbr r4, OCD_DC_SS_BIT | |
639 | mtdr OCD_DC, r4 | |
640 | ||
641 | 1: popm r10,r11 | |
642 | mask_exceptions | |
643 | mtsr SYSREG_RSR_DBG, r11 | |
644 | mtsr SYSREG_RAR_DBG, r10 | |
645 | #ifdef CONFIG_TRACE_IRQFLAGS | |
8d29b7b9 | 646 | call trace_hardirqs_on |
2507bc13 HS |
647 | 1: |
648 | #endif | |
649 | ldmts sp++, r0-lr | |
650 | sub sp, -4 | |
651 | retd | |
652 | .size handle_debug, . - handle_debug | |
653 | ||
654 | /* Mode of the trapped context is in r9 */ | |
655 | .type debug_fixup_regs, @function | |
656 | debug_fixup_regs: | |
657 | mfsr r8, SYSREG_SR | |
658 | mov r10, r8 | |
659 | bfins r8, r9, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | |
660 | mtsr SYSREG_SR, r8 | |
5f97f7f9 HS |
661 | sub pc, -2 |
662 | stdsp sp[REG_LR], lr | |
2507bc13 | 663 | mtsr SYSREG_SR, r10 |
5f97f7f9 | 664 | sub pc, -2 |
2507bc13 HS |
665 | sub r8, sp, -FRAME_SIZE_FULL |
666 | stdsp sp[REG_SP], r8 | |
667 | rjmp .Ldebug_fixup_cont | |
668 | .size debug_fixup_regs, . - debug_fixup_regs | |
5f97f7f9 | 669 | |
2507bc13 HS |
670 | .type debug_resume_kernel, @function |
671 | debug_resume_kernel: | |
672 | mask_exceptions | |
5f97f7f9 HS |
673 | popm r10, r11 |
674 | mtsr SYSREG_RAR_DBG, r10 | |
675 | mtsr SYSREG_RSR_DBG, r11 | |
320516b7 HS |
676 | #ifdef CONFIG_TRACE_IRQFLAGS |
677 | bld r11, SYSREG_GM_OFFSET | |
678 | brcc 1f | |
8d29b7b9 | 679 | call trace_hardirqs_on |
320516b7 HS |
680 | 1: |
681 | #endif | |
682 | mfsr r2, SYSREG_SR | |
2507bc13 HS |
683 | mov r1, r2 |
684 | bfins r2, r3, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | |
320516b7 | 685 | mtsr SYSREG_SR, r2 |
5f97f7f9 HS |
686 | sub pc, -2 |
687 | popm lr | |
2507bc13 | 688 | mtsr SYSREG_SR, r1 |
5f97f7f9 HS |
689 | sub pc, -2 |
690 | sub sp, -4 /* skip SP */ | |
691 | popm r0-r12 | |
692 | sub sp, -4 | |
693 | retd | |
2507bc13 | 694 | .size debug_resume_kernel, . - debug_resume_kernel |
5f97f7f9 | 695 | |
2507bc13 HS |
696 | .type debug_exit_work, @function |
697 | debug_exit_work: | |
5f97f7f9 | 698 | /* |
2507bc13 HS |
699 | * We must return from Monitor Mode using a retd, and we must |
700 | * not schedule since that involves the D bit in SR getting | |
701 | * cleared by something other than the debug hardware. This | |
702 | * may cause undefined behaviour according to the Architecture | |
703 | * manual. | |
704 | * | |
705 | * So we fix up the return address and status and return to a | |
706 | * stub below in Exception mode. From there, we can follow the | |
707 | * normal exception return path. | |
708 | * | |
709 | * The real return address and status registers are stored on | |
710 | * the stack in the way the exception return path understands, | |
711 | * so no need to fix anything up there. | |
5f97f7f9 | 712 | */ |
2507bc13 HS |
713 | sub r8, pc, . - fault_exit_work |
714 | mtsr SYSREG_RAR_DBG, r8 | |
715 | mov r9, 0 | |
716 | orh r9, hi(SR_EM | SR_GM | MODE_EXCEPTION) | |
717 | mtsr SYSREG_RSR_DBG, r9 | |
718 | sub pc, -2 | |
5f97f7f9 | 719 | retd |
2507bc13 | 720 | .size debug_exit_work, . - debug_exit_work |
5f97f7f9 HS |
721 | |
722 | .set rsr_int0, SYSREG_RSR_INT0 | |
723 | .set rsr_int1, SYSREG_RSR_INT1 | |
724 | .set rsr_int2, SYSREG_RSR_INT2 | |
725 | .set rsr_int3, SYSREG_RSR_INT3 | |
726 | .set rar_int0, SYSREG_RAR_INT0 | |
727 | .set rar_int1, SYSREG_RAR_INT1 | |
728 | .set rar_int2, SYSREG_RAR_INT2 | |
729 | .set rar_int3, SYSREG_RAR_INT3 | |
730 | ||
731 | .macro IRQ_LEVEL level | |
732 | .type irq_level\level, @function | |
733 | irq_level\level: | |
734 | sub sp, 4 /* r12_orig */ | |
735 | stmts --sp,r0-lr | |
736 | mfsr r8, rar_int\level | |
737 | mfsr r9, rsr_int\level | |
a7e30b8d PR |
738 | |
739 | #ifdef CONFIG_PREEMPT | |
740 | sub r11, pc, (. - system_call) | |
741 | cp.w r11, r8 | |
742 | breq 4f | |
743 | #endif | |
744 | ||
5f97f7f9 HS |
745 | pushm r8-r9 |
746 | ||
747 | mov r11, sp | |
748 | mov r12, \level | |
749 | ||
8d29b7b9 | 750 | call do_IRQ |
5f97f7f9 HS |
751 | |
752 | lddsp r4, sp[REG_SR] | |
19b7ce8b HCE |
753 | bfextu r4, r4, SYSREG_M0_OFFSET, 3 |
754 | cp.w r4, MODE_SUPERVISOR >> SYSREG_M0_OFFSET | |
755 | breq 2f | |
756 | cp.w r4, MODE_USER >> SYSREG_M0_OFFSET | |
5f97f7f9 | 757 | #ifdef CONFIG_PREEMPT |
19b7ce8b | 758 | brne 3f |
5f97f7f9 HS |
759 | #else |
760 | brne 1f | |
761 | #endif | |
762 | ||
763 | get_thread_info r0 | |
764 | ld.w r1, r0[TI_flags] | |
765 | andl r1, _TIF_WORK_MASK, COH | |
766 | brne irq_exit_work | |
767 | ||
320516b7 HS |
768 | 1: |
769 | #ifdef CONFIG_TRACE_IRQFLAGS | |
8d29b7b9 | 770 | call trace_hardirqs_on |
320516b7 HS |
771 | #endif |
772 | popm r8-r9 | |
5f97f7f9 HS |
773 | mtsr rar_int\level, r8 |
774 | mtsr rsr_int\level, r9 | |
775 | ldmts sp++,r0-lr | |
776 | sub sp, -4 /* ignore r12_orig */ | |
777 | rete | |
778 | ||
a7e30b8d PR |
779 | #ifdef CONFIG_PREEMPT |
780 | 4: mask_interrupts | |
781 | mfsr r8, rsr_int\level | |
782 | sbr r8, 16 | |
783 | mtsr rsr_int\level, r8 | |
784 | ldmts sp++, r0-lr | |
785 | sub sp, -4 /* ignore r12_orig */ | |
786 | rete | |
787 | #endif | |
788 | ||
19b7ce8b HCE |
789 | 2: get_thread_info r0 |
790 | ld.w r1, r0[TI_flags] | |
791 | bld r1, TIF_CPU_GOING_TO_SLEEP | |
5f97f7f9 | 792 | #ifdef CONFIG_PREEMPT |
19b7ce8b HCE |
793 | brcc 3f |
794 | #else | |
795 | brcc 1b | |
796 | #endif | |
797 | sub r1, pc, . - cpu_idle_skip_sleep | |
798 | stdsp sp[REG_PC], r1 | |
799 | #ifdef CONFIG_PREEMPT | |
800 | 3: get_thread_info r0 | |
5f97f7f9 HS |
801 | ld.w r2, r0[TI_preempt_count] |
802 | cp.w r2, 0 | |
803 | brne 1b | |
804 | ld.w r1, r0[TI_flags] | |
805 | bld r1, TIF_NEED_RESCHED | |
806 | brcc 1b | |
807 | lddsp r4, sp[REG_SR] | |
808 | bld r4, SYSREG_GM_OFFSET | |
809 | brcs 1b | |
8d29b7b9 | 810 | call preempt_schedule_irq |
5f97f7f9 | 811 | #endif |
19b7ce8b | 812 | rjmp 1b |
5f97f7f9 HS |
813 | .endm |
814 | ||
815 | .section .irq.text,"ax",@progbits | |
816 | ||
817 | .global irq_level0 | |
818 | .global irq_level1 | |
819 | .global irq_level2 | |
820 | .global irq_level3 | |
821 | IRQ_LEVEL 0 | |
822 | IRQ_LEVEL 1 | |
823 | IRQ_LEVEL 2 | |
824 | IRQ_LEVEL 3 | |
2507bc13 HS |
825 | |
826 | .section .kprobes.text, "ax", @progbits | |
827 | .type enter_monitor_mode, @function | |
828 | enter_monitor_mode: | |
829 | /* | |
830 | * We need to enter monitor mode to do a single step. The | |
831 | * monitor code will alter the return address so that we | |
832 | * return directly to the user instead of returning here. | |
833 | */ | |
834 | breakpoint | |
835 | rjmp breakpoint_failed | |
836 | ||
837 | .size enter_monitor_mode, . - enter_monitor_mode | |
838 | ||
839 | .type debug_trampoline, @function | |
840 | .global debug_trampoline | |
841 | debug_trampoline: | |
842 | /* | |
843 | * Save the registers on the stack so that the monitor code | |
844 | * can find them easily. | |
845 | */ | |
846 | sub sp, 4 /* r12_orig */ | |
847 | stmts --sp, r0-lr | |
848 | get_thread_info r0 | |
849 | ld.w r8, r0[TI_rar_saved] | |
850 | ld.w r9, r0[TI_rsr_saved] | |
851 | pushm r8-r9 | |
852 | ||
853 | /* | |
854 | * The monitor code will alter the return address so we don't | |
855 | * return here. | |
856 | */ | |
857 | breakpoint | |
858 | rjmp breakpoint_failed | |
859 | .size debug_trampoline, . - debug_trampoline | |
860 | ||
861 | .type breakpoint_failed, @function | |
862 | breakpoint_failed: | |
863 | /* | |
864 | * Something went wrong. Perhaps the debug hardware isn't | |
865 | * enabled? | |
866 | */ | |
867 | lda.w r12, msg_breakpoint_failed | |
868 | mov r11, sp | |
869 | mov r10, 9 /* SIGKILL */ | |
870 | call die | |
871 | 1: rjmp 1b | |
872 | ||
873 | msg_breakpoint_failed: | |
874 | .asciz "Failed to enter Debug Mode" |