diff options
Diffstat (limited to 'arch/sparc/mm/fault_32.c')
-rw-r--r-- | arch/sparc/mm/fault_32.c | 207 |
1 files changed, 45 insertions, 162 deletions
diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c index df3155a17991..f46cf6be3370 100644 --- a/arch/sparc/mm/fault_32.c +++ b/arch/sparc/mm/fault_32.c | |||
@@ -24,29 +24,19 @@ | |||
24 | 24 | ||
25 | #include <asm/page.h> | 25 | #include <asm/page.h> |
26 | #include <asm/pgtable.h> | 26 | #include <asm/pgtable.h> |
27 | #include <asm/memreg.h> | ||
28 | #include <asm/openprom.h> | 27 | #include <asm/openprom.h> |
29 | #include <asm/oplib.h> | 28 | #include <asm/oplib.h> |
30 | #include <asm/smp.h> | 29 | #include <asm/smp.h> |
31 | #include <asm/traps.h> | 30 | #include <asm/traps.h> |
32 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
33 | 32 | ||
34 | extern int prom_node_root; | ||
35 | |||
36 | int show_unhandled_signals = 1; | 33 | int show_unhandled_signals = 1; |
37 | 34 | ||
38 | /* At boot time we determine these two values necessary for setting | 35 | /* At boot time we determine these two values necessary for setting |
39 | * up the segment maps and page table entries (pte's). | 36 | * up the segment maps and page table entries (pte's). |
40 | */ | 37 | */ |
41 | 38 | ||
42 | int num_segmaps, num_contexts; | 39 | int num_contexts; |
43 | int invalid_segment; | ||
44 | |||
45 | /* various Virtual Address Cache parameters we find at boot time... */ | ||
46 | |||
47 | int vac_size, vac_linesize, vac_do_hw_vac_flushes; | ||
48 | int vac_entries_per_context, vac_entries_per_segment; | ||
49 | int vac_entries_per_page; | ||
50 | 40 | ||
51 | /* Return how much physical memory we have. */ | 41 | /* Return how much physical memory we have. */ |
52 | unsigned long probe_memory(void) | 42 | unsigned long probe_memory(void) |
@@ -60,55 +50,36 @@ unsigned long probe_memory(void) | |||
60 | return total; | 50 | return total; |
61 | } | 51 | } |
62 | 52 | ||
63 | extern void sun4c_complete_all_stores(void); | ||
64 | |||
65 | /* Whee, a level 15 NMI interrupt memory error. Let's have fun... */ | ||
66 | asmlinkage void sparc_lvl15_nmi(struct pt_regs *regs, unsigned long serr, | ||
67 | unsigned long svaddr, unsigned long aerr, | ||
68 | unsigned long avaddr) | ||
69 | { | ||
70 | sun4c_complete_all_stores(); | ||
71 | printk("FAULT: NMI received\n"); | ||
72 | printk("SREGS: Synchronous Error %08lx\n", serr); | ||
73 | printk(" Synchronous Vaddr %08lx\n", svaddr); | ||
74 | printk(" Asynchronous Error %08lx\n", aerr); | ||
75 | printk(" Asynchronous Vaddr %08lx\n", avaddr); | ||
76 | if (sun4c_memerr_reg) | ||
77 | printk(" Memory Parity Error %08lx\n", *sun4c_memerr_reg); | ||
78 | printk("REGISTER DUMP:\n"); | ||
79 | show_regs(regs); | ||
80 | prom_halt(); | ||
81 | } | ||
82 | |||
83 | static void unhandled_fault(unsigned long, struct task_struct *, | 53 | static void unhandled_fault(unsigned long, struct task_struct *, |
84 | struct pt_regs *) __attribute__ ((noreturn)); | 54 | struct pt_regs *) __attribute__ ((noreturn)); |
85 | 55 | ||
86 | static void unhandled_fault(unsigned long address, struct task_struct *tsk, | 56 | static void __noreturn unhandled_fault(unsigned long address, |
87 | struct pt_regs *regs) | 57 | struct task_struct *tsk, |
58 | struct pt_regs *regs) | ||
88 | { | 59 | { |
89 | if((unsigned long) address < PAGE_SIZE) { | 60 | if ((unsigned long) address < PAGE_SIZE) { |
90 | printk(KERN_ALERT | 61 | printk(KERN_ALERT |
91 | "Unable to handle kernel NULL pointer dereference\n"); | 62 | "Unable to handle kernel NULL pointer dereference\n"); |
92 | } else { | 63 | } else { |
93 | printk(KERN_ALERT "Unable to handle kernel paging request " | 64 | printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx\n", |
94 | "at virtual address %08lx\n", address); | 65 | address); |
95 | } | 66 | } |
96 | printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n", | 67 | printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n", |
97 | (tsk->mm ? tsk->mm->context : tsk->active_mm->context)); | 68 | (tsk->mm ? tsk->mm->context : tsk->active_mm->context)); |
98 | printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n", | 69 | printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n", |
99 | (tsk->mm ? (unsigned long) tsk->mm->pgd : | 70 | (tsk->mm ? (unsigned long) tsk->mm->pgd : |
100 | (unsigned long) tsk->active_mm->pgd)); | 71 | (unsigned long) tsk->active_mm->pgd)); |
101 | die_if_kernel("Oops", regs); | 72 | die_if_kernel("Oops", regs); |
102 | } | 73 | } |
103 | 74 | ||
104 | asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, | 75 | asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, |
105 | unsigned long address) | 76 | unsigned long address) |
106 | { | 77 | { |
107 | struct pt_regs regs; | 78 | struct pt_regs regs; |
108 | unsigned long g2; | 79 | unsigned long g2; |
109 | unsigned int insn; | 80 | unsigned int insn; |
110 | int i; | 81 | int i; |
111 | 82 | ||
112 | i = search_extables_range(ret_pc, &g2); | 83 | i = search_extables_range(ret_pc, &g2); |
113 | switch (i) { | 84 | switch (i) { |
114 | case 3: | 85 | case 3: |
@@ -128,14 +99,14 @@ asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, | |||
128 | /* for _from_ macros */ | 99 | /* for _from_ macros */ |
129 | insn = *((unsigned int *) pc); | 100 | insn = *((unsigned int *) pc); |
130 | if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) | 101 | if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) |
131 | return 2; | 102 | return 2; |
132 | break; | 103 | break; |
133 | 104 | ||
134 | default: | 105 | default: |
135 | break; | 106 | break; |
136 | } | 107 | } |
137 | 108 | ||
138 | memset(®s, 0, sizeof (regs)); | 109 | memset(®s, 0, sizeof(regs)); |
139 | regs.pc = pc; | 110 | regs.pc = pc; |
140 | regs.npc = pc + 4; | 111 | regs.npc = pc + 4; |
141 | __asm__ __volatile__( | 112 | __asm__ __volatile__( |
@@ -198,11 +169,10 @@ static unsigned long compute_si_addr(struct pt_regs *regs, int text_fault) | |||
198 | if (text_fault) | 169 | if (text_fault) |
199 | return regs->pc; | 170 | return regs->pc; |
200 | 171 | ||
201 | if (regs->psr & PSR_PS) { | 172 | if (regs->psr & PSR_PS) |
202 | insn = *(unsigned int *) regs->pc; | 173 | insn = *(unsigned int *) regs->pc; |
203 | } else { | 174 | else |
204 | __get_user(insn, (unsigned int *) regs->pc); | 175 | __get_user(insn, (unsigned int *) regs->pc); |
205 | } | ||
206 | 176 | ||
207 | return safe_compute_effective_address(regs, insn); | 177 | return safe_compute_effective_address(regs, insn); |
208 | } | 178 | } |
@@ -228,7 +198,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, | |||
228 | unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | | 198 | unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | |
229 | (write ? FAULT_FLAG_WRITE : 0)); | 199 | (write ? FAULT_FLAG_WRITE : 0)); |
230 | 200 | ||
231 | if(text_fault) | 201 | if (text_fault) |
232 | address = regs->pc; | 202 | address = regs->pc; |
233 | 203 | ||
234 | /* | 204 | /* |
@@ -241,36 +211,32 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, | |||
241 | * nothing more. | 211 | * nothing more. |
242 | */ | 212 | */ |
243 | code = SEGV_MAPERR; | 213 | code = SEGV_MAPERR; |
244 | if (!ARCH_SUN4C && address >= TASK_SIZE) | 214 | if (address >= TASK_SIZE) |
245 | goto vmalloc_fault; | 215 | goto vmalloc_fault; |
246 | 216 | ||
247 | /* | 217 | /* |
248 | * If we're in an interrupt or have no user | 218 | * If we're in an interrupt or have no user |
249 | * context, we must not take the fault.. | 219 | * context, we must not take the fault.. |
250 | */ | 220 | */ |
251 | if (in_atomic() || !mm) | 221 | if (in_atomic() || !mm) |
252 | goto no_context; | 222 | goto no_context; |
253 | 223 | ||
254 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); | 224 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); |
255 | 225 | ||
256 | retry: | 226 | retry: |
257 | down_read(&mm->mmap_sem); | 227 | down_read(&mm->mmap_sem); |
258 | 228 | ||
259 | /* | 229 | if (!from_user && address >= PAGE_OFFSET) |
260 | * The kernel referencing a bad kernel pointer can lock up | ||
261 | * a sun4c machine completely, so we must attempt recovery. | ||
262 | */ | ||
263 | if(!from_user && address >= PAGE_OFFSET) | ||
264 | goto bad_area; | 230 | goto bad_area; |
265 | 231 | ||
266 | vma = find_vma(mm, address); | 232 | vma = find_vma(mm, address); |
267 | if(!vma) | 233 | if (!vma) |
268 | goto bad_area; | 234 | goto bad_area; |
269 | if(vma->vm_start <= address) | 235 | if (vma->vm_start <= address) |
270 | goto good_area; | 236 | goto good_area; |
271 | if(!(vma->vm_flags & VM_GROWSDOWN)) | 237 | if (!(vma->vm_flags & VM_GROWSDOWN)) |
272 | goto bad_area; | 238 | goto bad_area; |
273 | if(expand_stack(vma, address)) | 239 | if (expand_stack(vma, address)) |
274 | goto bad_area; | 240 | goto bad_area; |
275 | /* | 241 | /* |
276 | * Ok, we have a good vm_area for this memory access, so | 242 | * Ok, we have a good vm_area for this memory access, so |
@@ -278,12 +244,12 @@ retry: | |||
278 | */ | 244 | */ |
279 | good_area: | 245 | good_area: |
280 | code = SEGV_ACCERR; | 246 | code = SEGV_ACCERR; |
281 | if(write) { | 247 | if (write) { |
282 | if(!(vma->vm_flags & VM_WRITE)) | 248 | if (!(vma->vm_flags & VM_WRITE)) |
283 | goto bad_area; | 249 | goto bad_area; |
284 | } else { | 250 | } else { |
285 | /* Allow reads even for write-only mappings */ | 251 | /* Allow reads even for write-only mappings */ |
286 | if(!(vma->vm_flags & (VM_READ | VM_EXEC))) | 252 | if (!(vma->vm_flags & (VM_READ | VM_EXEC))) |
287 | goto bad_area; | 253 | goto bad_area; |
288 | } | 254 | } |
289 | 255 | ||
@@ -349,14 +315,16 @@ no_context: | |||
349 | g2 = regs->u_regs[UREG_G2]; | 315 | g2 = regs->u_regs[UREG_G2]; |
350 | if (!from_user) { | 316 | if (!from_user) { |
351 | fixup = search_extables_range(regs->pc, &g2); | 317 | fixup = search_extables_range(regs->pc, &g2); |
352 | if (fixup > 10) { /* Values below are reserved for other things */ | 318 | /* Values below 10 are reserved for other things */ |
319 | if (fixup > 10) { | ||
353 | extern const unsigned __memset_start[]; | 320 | extern const unsigned __memset_start[]; |
354 | extern const unsigned __memset_end[]; | 321 | extern const unsigned __memset_end[]; |
355 | extern const unsigned __csum_partial_copy_start[]; | 322 | extern const unsigned __csum_partial_copy_start[]; |
356 | extern const unsigned __csum_partial_copy_end[]; | 323 | extern const unsigned __csum_partial_copy_end[]; |
357 | 324 | ||
358 | #ifdef DEBUG_EXCEPTIONS | 325 | #ifdef DEBUG_EXCEPTIONS |
359 | printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address); | 326 | printk("Exception: PC<%08lx> faddr<%08lx>\n", |
327 | regs->pc, address); | ||
360 | printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n", | 328 | printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n", |
361 | regs->pc, fixup, g2); | 329 | regs->pc, fixup, g2); |
362 | #endif | 330 | #endif |
@@ -364,7 +332,7 @@ no_context: | |||
364 | regs->pc < (unsigned long)__memset_end) || | 332 | regs->pc < (unsigned long)__memset_end) || |
365 | (regs->pc >= (unsigned long)__csum_partial_copy_start && | 333 | (regs->pc >= (unsigned long)__csum_partial_copy_start && |
366 | regs->pc < (unsigned long)__csum_partial_copy_end)) { | 334 | regs->pc < (unsigned long)__csum_partial_copy_end)) { |
367 | regs->u_regs[UREG_I4] = address; | 335 | regs->u_regs[UREG_I4] = address; |
368 | regs->u_regs[UREG_I5] = regs->pc; | 336 | regs->u_regs[UREG_I5] = regs->pc; |
369 | } | 337 | } |
370 | regs->u_regs[UREG_G2] = g2; | 338 | regs->u_regs[UREG_G2] = g2; |
@@ -373,8 +341,8 @@ no_context: | |||
373 | return; | 341 | return; |
374 | } | 342 | } |
375 | } | 343 | } |
376 | 344 | ||
377 | unhandled_fault (address, tsk, regs); | 345 | unhandled_fault(address, tsk, regs); |
378 | do_exit(SIGKILL); | 346 | do_exit(SIGKILL); |
379 | 347 | ||
380 | /* | 348 | /* |
@@ -420,97 +388,12 @@ vmalloc_fault: | |||
420 | 388 | ||
421 | if (pmd_present(*pmd) || !pmd_present(*pmd_k)) | 389 | if (pmd_present(*pmd) || !pmd_present(*pmd_k)) |
422 | goto bad_area_nosemaphore; | 390 | goto bad_area_nosemaphore; |
391 | |||
423 | *pmd = *pmd_k; | 392 | *pmd = *pmd_k; |
424 | return; | 393 | return; |
425 | } | 394 | } |
426 | } | 395 | } |
427 | 396 | ||
428 | asmlinkage void do_sun4c_fault(struct pt_regs *regs, int text_fault, int write, | ||
429 | unsigned long address) | ||
430 | { | ||
431 | extern void sun4c_update_mmu_cache(struct vm_area_struct *, | ||
432 | unsigned long,pte_t *); | ||
433 | extern pte_t *sun4c_pte_offset_kernel(pmd_t *,unsigned long); | ||
434 | struct task_struct *tsk = current; | ||
435 | struct mm_struct *mm = tsk->mm; | ||
436 | pgd_t *pgdp; | ||
437 | pte_t *ptep; | ||
438 | |||
439 | if (text_fault) { | ||
440 | address = regs->pc; | ||
441 | } else if (!write && | ||
442 | !(regs->psr & PSR_PS)) { | ||
443 | unsigned int insn, __user *ip; | ||
444 | |||
445 | ip = (unsigned int __user *)regs->pc; | ||
446 | if (!get_user(insn, ip)) { | ||
447 | if ((insn & 0xc1680000) == 0xc0680000) | ||
448 | write = 1; | ||
449 | } | ||
450 | } | ||
451 | |||
452 | if (!mm) { | ||
453 | /* We are oopsing. */ | ||
454 | do_sparc_fault(regs, text_fault, write, address); | ||
455 | BUG(); /* P3 Oops already, you bitch */ | ||
456 | } | ||
457 | |||
458 | pgdp = pgd_offset(mm, address); | ||
459 | ptep = sun4c_pte_offset_kernel((pmd_t *) pgdp, address); | ||
460 | |||
461 | if (pgd_val(*pgdp)) { | ||
462 | if (write) { | ||
463 | if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) | ||
464 | == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) { | ||
465 | unsigned long flags; | ||
466 | |||
467 | *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED | | ||
468 | _SUN4C_PAGE_MODIFIED | | ||
469 | _SUN4C_PAGE_VALID | | ||
470 | _SUN4C_PAGE_DIRTY); | ||
471 | |||
472 | local_irq_save(flags); | ||
473 | if (sun4c_get_segmap(address) != invalid_segment) { | ||
474 | sun4c_put_pte(address, pte_val(*ptep)); | ||
475 | local_irq_restore(flags); | ||
476 | return; | ||
477 | } | ||
478 | local_irq_restore(flags); | ||
479 | } | ||
480 | } else { | ||
481 | if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) | ||
482 | == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) { | ||
483 | unsigned long flags; | ||
484 | |||
485 | *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED | | ||
486 | _SUN4C_PAGE_VALID); | ||
487 | |||
488 | local_irq_save(flags); | ||
489 | if (sun4c_get_segmap(address) != invalid_segment) { | ||
490 | sun4c_put_pte(address, pte_val(*ptep)); | ||
491 | local_irq_restore(flags); | ||
492 | return; | ||
493 | } | ||
494 | local_irq_restore(flags); | ||
495 | } | ||
496 | } | ||
497 | } | ||
498 | |||
499 | /* This conditional is 'interesting'. */ | ||
500 | if (pgd_val(*pgdp) && !(write && !(pte_val(*ptep) & _SUN4C_PAGE_WRITE)) | ||
501 | && (pte_val(*ptep) & _SUN4C_PAGE_VALID)) | ||
502 | /* Note: It is safe to not grab the MMAP semaphore here because | ||
503 | * we know that update_mmu_cache() will not sleep for | ||
504 | * any reason (at least not in the current implementation) | ||
505 | * and therefore there is no danger of another thread getting | ||
506 | * on the CPU and doing a shrink_mmap() on this vma. | ||
507 | */ | ||
508 | sun4c_update_mmu_cache (find_vma(current->mm, address), address, | ||
509 | ptep); | ||
510 | else | ||
511 | do_sparc_fault(regs, text_fault, write, address); | ||
512 | } | ||
513 | |||
514 | /* This always deals with user addresses. */ | 397 | /* This always deals with user addresses. */ |
515 | static void force_user_fault(unsigned long address, int write) | 398 | static void force_user_fault(unsigned long address, int write) |
516 | { | 399 | { |
@@ -523,21 +406,21 @@ static void force_user_fault(unsigned long address, int write) | |||
523 | 406 | ||
524 | down_read(&mm->mmap_sem); | 407 | down_read(&mm->mmap_sem); |
525 | vma = find_vma(mm, address); | 408 | vma = find_vma(mm, address); |
526 | if(!vma) | 409 | if (!vma) |
527 | goto bad_area; | 410 | goto bad_area; |
528 | if(vma->vm_start <= address) | 411 | if (vma->vm_start <= address) |
529 | goto good_area; | 412 | goto good_area; |
530 | if(!(vma->vm_flags & VM_GROWSDOWN)) | 413 | if (!(vma->vm_flags & VM_GROWSDOWN)) |
531 | goto bad_area; | 414 | goto bad_area; |
532 | if(expand_stack(vma, address)) | 415 | if (expand_stack(vma, address)) |
533 | goto bad_area; | 416 | goto bad_area; |
534 | good_area: | 417 | good_area: |
535 | code = SEGV_ACCERR; | 418 | code = SEGV_ACCERR; |
536 | if(write) { | 419 | if (write) { |
537 | if(!(vma->vm_flags & VM_WRITE)) | 420 | if (!(vma->vm_flags & VM_WRITE)) |
538 | goto bad_area; | 421 | goto bad_area; |
539 | } else { | 422 | } else { |
540 | if(!(vma->vm_flags & (VM_READ | VM_EXEC))) | 423 | if (!(vma->vm_flags & (VM_READ | VM_EXEC))) |
541 | goto bad_area; | 424 | goto bad_area; |
542 | } | 425 | } |
543 | switch (handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0)) { | 426 | switch (handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0)) { |
@@ -568,7 +451,7 @@ void window_overflow_fault(void) | |||
568 | unsigned long sp; | 451 | unsigned long sp; |
569 | 452 | ||
570 | sp = current_thread_info()->rwbuf_stkptrs[0]; | 453 | sp = current_thread_info()->rwbuf_stkptrs[0]; |
571 | if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) | 454 | if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) |
572 | force_user_fault(sp + 0x38, 1); | 455 | force_user_fault(sp + 0x38, 1); |
573 | force_user_fault(sp, 1); | 456 | force_user_fault(sp, 1); |
574 | 457 | ||
@@ -577,7 +460,7 @@ void window_overflow_fault(void) | |||
577 | 460 | ||
578 | void window_underflow_fault(unsigned long sp) | 461 | void window_underflow_fault(unsigned long sp) |
579 | { | 462 | { |
580 | if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) | 463 | if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) |
581 | force_user_fault(sp + 0x38, 0); | 464 | force_user_fault(sp + 0x38, 0); |
582 | force_user_fault(sp, 0); | 465 | force_user_fault(sp, 0); |
583 | 466 | ||
@@ -589,7 +472,7 @@ void window_ret_fault(struct pt_regs *regs) | |||
589 | unsigned long sp; | 472 | unsigned long sp; |
590 | 473 | ||
591 | sp = regs->u_regs[UREG_FP]; | 474 | sp = regs->u_regs[UREG_FP]; |
592 | if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) | 475 | if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) |
593 | force_user_fault(sp + 0x38, 0); | 476 | force_user_fault(sp + 0x38, 0); |
594 | force_user_fault(sp, 0); | 477 | force_user_fault(sp, 0); |
595 | 478 | ||