Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.13 596 lines 15 kB view raw
1/* $Id: fault.c,v 1.122 2001/11/17 07:19:26 davem Exp $ 2 * fault.c: Page fault handlers for the Sparc. 3 * 4 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) 5 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) 6 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 7 */ 8 9#include <asm/head.h> 10 11#include <linux/string.h> 12#include <linux/types.h> 13#include <linux/sched.h> 14#include <linux/ptrace.h> 15#include <linux/mman.h> 16#include <linux/threads.h> 17#include <linux/kernel.h> 18#include <linux/signal.h> 19#include <linux/mm.h> 20#include <linux/smp.h> 21#include <linux/smp_lock.h> 22#include <linux/interrupt.h> 23#include <linux/module.h> 24 25#include <asm/system.h> 26#include <asm/segment.h> 27#include <asm/page.h> 28#include <asm/pgtable.h> 29#include <asm/memreg.h> 30#include <asm/openprom.h> 31#include <asm/oplib.h> 32#include <asm/smp.h> 33#include <asm/traps.h> 34#include <asm/kdebug.h> 35#include <asm/uaccess.h> 36 37#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0])) 38 39extern int prom_node_root; 40 41/* At boot time we determine these two values necessary for setting 42 * up the segment maps and page table entries (pte's). 43 */ 44 45int num_segmaps, num_contexts; 46int invalid_segment; 47 48/* various Virtual Address Cache parameters we find at boot time... */ 49 50int vac_size, vac_linesize, vac_do_hw_vac_flushes; 51int vac_entries_per_context, vac_entries_per_segment; 52int vac_entries_per_page; 53 54/* Nice, simple, prom library does all the sweating for us. ;) */ 55int prom_probe_memory (void) 56{ 57 register struct linux_mlist_v0 *mlist; 58 register unsigned long bytes, base_paddr, tally; 59 register int i; 60 61 i = 0; 62 mlist= *prom_meminfo()->v0_available; 63 bytes = tally = mlist->num_bytes; 64 base_paddr = (unsigned long) mlist->start_adr; 65 66 sp_banks[0].base_addr = base_paddr; 67 sp_banks[0].num_bytes = bytes; 68 69 while (mlist->theres_more != (void *) 0){ 70 i++; 71 mlist = mlist->theres_more; 72 bytes = mlist->num_bytes; 73 tally += bytes; 74 if (i > SPARC_PHYS_BANKS-1) { 75 printk ("The machine has more banks than " 76 "this kernel can support\n" 77 "Increase the SPARC_PHYS_BANKS " 78 "setting (currently %d)\n", 79 SPARC_PHYS_BANKS); 80 i = SPARC_PHYS_BANKS-1; 81 break; 82 } 83 84 sp_banks[i].base_addr = (unsigned long) mlist->start_adr; 85 sp_banks[i].num_bytes = mlist->num_bytes; 86 } 87 88 i++; 89 sp_banks[i].base_addr = 0xdeadbeef; 90 sp_banks[i].num_bytes = 0; 91 92 /* Now mask all bank sizes on a page boundary, it is all we can 93 * use anyways. 94 */ 95 for(i=0; sp_banks[i].num_bytes != 0; i++) 96 sp_banks[i].num_bytes &= PAGE_MASK; 97 98 return tally; 99} 100 101/* Traverse the memory lists in the prom to see how much physical we 102 * have. 103 */ 104unsigned long 105probe_memory(void) 106{ 107 int total; 108 109 total = prom_probe_memory(); 110 111 /* Oh man, much nicer, keep the dirt in promlib. */ 112 return total; 113} 114 115extern void sun4c_complete_all_stores(void); 116 117/* Whee, a level 15 NMI interrupt memory error. Let's have fun... */ 118asmlinkage void sparc_lvl15_nmi(struct pt_regs *regs, unsigned long serr, 119 unsigned long svaddr, unsigned long aerr, 120 unsigned long avaddr) 121{ 122 sun4c_complete_all_stores(); 123 printk("FAULT: NMI received\n"); 124 printk("SREGS: Synchronous Error %08lx\n", serr); 125 printk(" Synchronous Vaddr %08lx\n", svaddr); 126 printk(" Asynchronous Error %08lx\n", aerr); 127 printk(" Asynchronous Vaddr %08lx\n", avaddr); 128 if (sun4c_memerr_reg) 129 printk(" Memory Parity Error %08lx\n", *sun4c_memerr_reg); 130 printk("REGISTER DUMP:\n"); 131 show_regs(regs); 132 prom_halt(); 133} 134 135static void unhandled_fault(unsigned long, struct task_struct *, 136 struct pt_regs *) __attribute__ ((noreturn)); 137 138static void unhandled_fault(unsigned long address, struct task_struct *tsk, 139 struct pt_regs *regs) 140{ 141 if((unsigned long) address < PAGE_SIZE) { 142 printk(KERN_ALERT 143 "Unable to handle kernel NULL pointer dereference\n"); 144 } else { 145 printk(KERN_ALERT "Unable to handle kernel paging request " 146 "at virtual address %08lx\n", address); 147 } 148 printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n", 149 (tsk->mm ? tsk->mm->context : tsk->active_mm->context)); 150 printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n", 151 (tsk->mm ? (unsigned long) tsk->mm->pgd : 152 (unsigned long) tsk->active_mm->pgd)); 153 die_if_kernel("Oops", regs); 154} 155 156asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, 157 unsigned long address) 158{ 159 struct pt_regs regs; 160 unsigned long g2; 161 unsigned int insn; 162 int i; 163 164 i = search_extables_range(ret_pc, &g2); 165 switch (i) { 166 case 3: 167 /* load & store will be handled by fixup */ 168 return 3; 169 170 case 1: 171 /* store will be handled by fixup, load will bump out */ 172 /* for _to_ macros */ 173 insn = *((unsigned int *) pc); 174 if ((insn >> 21) & 1) 175 return 1; 176 break; 177 178 case 2: 179 /* load will be handled by fixup, store will bump out */ 180 /* for _from_ macros */ 181 insn = *((unsigned int *) pc); 182 if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) 183 return 2; 184 break; 185 186 default: 187 break; 188 }; 189 190 memset(&regs, 0, sizeof (regs)); 191 regs.pc = pc; 192 regs.npc = pc + 4; 193 __asm__ __volatile__( 194 "rd %%psr, %0\n\t" 195 "nop\n\t" 196 "nop\n\t" 197 "nop\n" : "=r" (regs.psr)); 198 unhandled_fault(address, current, &regs); 199 200 /* Not reached */ 201 return 0; 202} 203 204extern unsigned long safe_compute_effective_address(struct pt_regs *, 205 unsigned int); 206 207static unsigned long compute_si_addr(struct pt_regs *regs, int text_fault) 208{ 209 unsigned int insn; 210 211 if (text_fault) 212 return regs->pc; 213 214 if (regs->psr & PSR_PS) { 215 insn = *(unsigned int *) regs->pc; 216 } else { 217 __get_user(insn, (unsigned int *) regs->pc); 218 } 219 220 return safe_compute_effective_address(regs, insn); 221} 222 223asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, 224 unsigned long address) 225{ 226 struct vm_area_struct *vma; 227 struct task_struct *tsk = current; 228 struct mm_struct *mm = tsk->mm; 229 unsigned int fixup; 230 unsigned long g2; 231 siginfo_t info; 232 int from_user = !(regs->psr & PSR_PS); 233 234 if(text_fault) 235 address = regs->pc; 236 237 /* 238 * We fault-in kernel-space virtual memory on-demand. The 239 * 'reference' page table is init_mm.pgd. 240 * 241 * NOTE! We MUST NOT take any locks for this case. We may 242 * be in an interrupt or a critical region, and should 243 * only copy the information from the master page table, 244 * nothing more. 245 */ 246 if (!ARCH_SUN4C_SUN4 && address >= TASK_SIZE) 247 goto vmalloc_fault; 248 249 info.si_code = SEGV_MAPERR; 250 251 /* 252 * If we're in an interrupt or have no user 253 * context, we must not take the fault.. 254 */ 255 if (in_atomic() || !mm) 256 goto no_context; 257 258 down_read(&mm->mmap_sem); 259 260 /* 261 * The kernel referencing a bad kernel pointer can lock up 262 * a sun4c machine completely, so we must attempt recovery. 263 */ 264 if(!from_user && address >= PAGE_OFFSET) 265 goto bad_area; 266 267 vma = find_vma(mm, address); 268 if(!vma) 269 goto bad_area; 270 if(vma->vm_start <= address) 271 goto good_area; 272 if(!(vma->vm_flags & VM_GROWSDOWN)) 273 goto bad_area; 274 if(expand_stack(vma, address)) 275 goto bad_area; 276 /* 277 * Ok, we have a good vm_area for this memory access, so 278 * we can handle it.. 279 */ 280good_area: 281 info.si_code = SEGV_ACCERR; 282 if(write) { 283 if(!(vma->vm_flags & VM_WRITE)) 284 goto bad_area; 285 } else { 286 /* Allow reads even for write-only mappings */ 287 if(!(vma->vm_flags & (VM_READ | VM_EXEC))) 288 goto bad_area; 289 } 290 291 /* 292 * If for any reason at all we couldn't handle the fault, 293 * make sure we exit gracefully rather than endlessly redo 294 * the fault. 295 */ 296 switch (handle_mm_fault(mm, vma, address, write)) { 297 case VM_FAULT_SIGBUS: 298 goto do_sigbus; 299 case VM_FAULT_OOM: 300 goto out_of_memory; 301 case VM_FAULT_MAJOR: 302 current->maj_flt++; 303 break; 304 case VM_FAULT_MINOR: 305 default: 306 current->min_flt++; 307 break; 308 } 309 up_read(&mm->mmap_sem); 310 return; 311 312 /* 313 * Something tried to access memory that isn't in our memory map.. 314 * Fix it, but check if it's kernel or user first.. 315 */ 316bad_area: 317 up_read(&mm->mmap_sem); 318 319bad_area_nosemaphore: 320 /* User mode accesses just cause a SIGSEGV */ 321 if(from_user) { 322#if 0 323 printk("Fault whee %s [%d]: segfaults at %08lx pc=%08lx\n", 324 tsk->comm, tsk->pid, address, regs->pc); 325#endif 326 info.si_signo = SIGSEGV; 327 info.si_errno = 0; 328 /* info.si_code set above to make clear whether 329 this was a SEGV_MAPERR or SEGV_ACCERR fault. */ 330 info.si_addr = (void __user *)compute_si_addr(regs, text_fault); 331 info.si_trapno = 0; 332 force_sig_info (SIGSEGV, &info, tsk); 333 return; 334 } 335 336 /* Is this in ex_table? */ 337no_context: 338 g2 = regs->u_regs[UREG_G2]; 339 if (!from_user && (fixup = search_extables_range(regs->pc, &g2))) { 340 if (fixup > 10) { /* Values below are reserved for other things */ 341 extern const unsigned __memset_start[]; 342 extern const unsigned __memset_end[]; 343 extern const unsigned __csum_partial_copy_start[]; 344 extern const unsigned __csum_partial_copy_end[]; 345 346#ifdef DEBUG_EXCEPTIONS 347 printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address); 348 printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n", 349 regs->pc, fixup, g2); 350#endif 351 if ((regs->pc >= (unsigned long)__memset_start && 352 regs->pc < (unsigned long)__memset_end) || 353 (regs->pc >= (unsigned long)__csum_partial_copy_start && 354 regs->pc < (unsigned long)__csum_partial_copy_end)) { 355 regs->u_regs[UREG_I4] = address; 356 regs->u_regs[UREG_I5] = regs->pc; 357 } 358 regs->u_regs[UREG_G2] = g2; 359 regs->pc = fixup; 360 regs->npc = regs->pc + 4; 361 return; 362 } 363 } 364 365 unhandled_fault (address, tsk, regs); 366 do_exit(SIGKILL); 367 368/* 369 * We ran out of memory, or some other thing happened to us that made 370 * us unable to handle the page fault gracefully. 371 */ 372out_of_memory: 373 up_read(&mm->mmap_sem); 374 printk("VM: killing process %s\n", tsk->comm); 375 if (from_user) 376 do_exit(SIGKILL); 377 goto no_context; 378 379do_sigbus: 380 up_read(&mm->mmap_sem); 381 info.si_signo = SIGBUS; 382 info.si_errno = 0; 383 info.si_code = BUS_ADRERR; 384 info.si_addr = (void __user *) compute_si_addr(regs, text_fault); 385 info.si_trapno = 0; 386 force_sig_info (SIGBUS, &info, tsk); 387 if (!from_user) 388 goto no_context; 389 390vmalloc_fault: 391 { 392 /* 393 * Synchronize this task's top level page-table 394 * with the 'reference' page table. 395 */ 396 int offset = pgd_index(address); 397 pgd_t *pgd, *pgd_k; 398 pmd_t *pmd, *pmd_k; 399 400 pgd = tsk->active_mm->pgd + offset; 401 pgd_k = init_mm.pgd + offset; 402 403 if (!pgd_present(*pgd)) { 404 if (!pgd_present(*pgd_k)) 405 goto bad_area_nosemaphore; 406 pgd_val(*pgd) = pgd_val(*pgd_k); 407 return; 408 } 409 410 pmd = pmd_offset(pgd, address); 411 pmd_k = pmd_offset(pgd_k, address); 412 413 if (pmd_present(*pmd) || !pmd_present(*pmd_k)) 414 goto bad_area_nosemaphore; 415 *pmd = *pmd_k; 416 return; 417 } 418} 419 420asmlinkage void do_sun4c_fault(struct pt_regs *regs, int text_fault, int write, 421 unsigned long address) 422{ 423 extern void sun4c_update_mmu_cache(struct vm_area_struct *, 424 unsigned long,pte_t); 425 extern pte_t *sun4c_pte_offset_kernel(pmd_t *,unsigned long); 426 struct task_struct *tsk = current; 427 struct mm_struct *mm = tsk->mm; 428 pgd_t *pgdp; 429 pte_t *ptep; 430 431 if (text_fault) { 432 address = regs->pc; 433 } else if (!write && 434 !(regs->psr & PSR_PS)) { 435 unsigned int insn, __user *ip; 436 437 ip = (unsigned int __user *)regs->pc; 438 if (!get_user(insn, ip)) { 439 if ((insn & 0xc1680000) == 0xc0680000) 440 write = 1; 441 } 442 } 443 444 if (!mm) { 445 /* We are oopsing. */ 446 do_sparc_fault(regs, text_fault, write, address); 447 BUG(); /* P3 Oops already, you bitch */ 448 } 449 450 pgdp = pgd_offset(mm, address); 451 ptep = sun4c_pte_offset_kernel((pmd_t *) pgdp, address); 452 453 if (pgd_val(*pgdp)) { 454 if (write) { 455 if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) 456 == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) { 457 unsigned long flags; 458 459 *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED | 460 _SUN4C_PAGE_MODIFIED | 461 _SUN4C_PAGE_VALID | 462 _SUN4C_PAGE_DIRTY); 463 464 local_irq_save(flags); 465 if (sun4c_get_segmap(address) != invalid_segment) { 466 sun4c_put_pte(address, pte_val(*ptep)); 467 local_irq_restore(flags); 468 return; 469 } 470 local_irq_restore(flags); 471 } 472 } else { 473 if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) 474 == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) { 475 unsigned long flags; 476 477 *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED | 478 _SUN4C_PAGE_VALID); 479 480 local_irq_save(flags); 481 if (sun4c_get_segmap(address) != invalid_segment) { 482 sun4c_put_pte(address, pte_val(*ptep)); 483 local_irq_restore(flags); 484 return; 485 } 486 local_irq_restore(flags); 487 } 488 } 489 } 490 491 /* This conditional is 'interesting'. */ 492 if (pgd_val(*pgdp) && !(write && !(pte_val(*ptep) & _SUN4C_PAGE_WRITE)) 493 && (pte_val(*ptep) & _SUN4C_PAGE_VALID)) 494 /* Note: It is safe to not grab the MMAP semaphore here because 495 * we know that update_mmu_cache() will not sleep for 496 * any reason (at least not in the current implementation) 497 * and therefore there is no danger of another thread getting 498 * on the CPU and doing a shrink_mmap() on this vma. 499 */ 500 sun4c_update_mmu_cache (find_vma(current->mm, address), address, 501 *ptep); 502 else 503 do_sparc_fault(regs, text_fault, write, address); 504} 505 506/* This always deals with user addresses. */ 507inline void force_user_fault(unsigned long address, int write) 508{ 509 struct vm_area_struct *vma; 510 struct task_struct *tsk = current; 511 struct mm_struct *mm = tsk->mm; 512 siginfo_t info; 513 514 info.si_code = SEGV_MAPERR; 515 516#if 0 517 printk("wf<pid=%d,wr=%d,addr=%08lx>\n", 518 tsk->pid, write, address); 519#endif 520 down_read(&mm->mmap_sem); 521 vma = find_vma(mm, address); 522 if(!vma) 523 goto bad_area; 524 if(vma->vm_start <= address) 525 goto good_area; 526 if(!(vma->vm_flags & VM_GROWSDOWN)) 527 goto bad_area; 528 if(expand_stack(vma, address)) 529 goto bad_area; 530good_area: 531 info.si_code = SEGV_ACCERR; 532 if(write) { 533 if(!(vma->vm_flags & VM_WRITE)) 534 goto bad_area; 535 } else { 536 if(!(vma->vm_flags & (VM_READ | VM_EXEC))) 537 goto bad_area; 538 } 539 switch (handle_mm_fault(mm, vma, address, write)) { 540 case VM_FAULT_SIGBUS: 541 case VM_FAULT_OOM: 542 goto do_sigbus; 543 } 544 up_read(&mm->mmap_sem); 545 return; 546bad_area: 547 up_read(&mm->mmap_sem); 548#if 0 549 printk("Window whee %s [%d]: segfaults at %08lx\n", 550 tsk->comm, tsk->pid, address); 551#endif 552 info.si_signo = SIGSEGV; 553 info.si_errno = 0; 554 /* info.si_code set above to make clear whether 555 this was a SEGV_MAPERR or SEGV_ACCERR fault. */ 556 info.si_addr = (void __user *) address; 557 info.si_trapno = 0; 558 force_sig_info (SIGSEGV, &info, tsk); 559 return; 560 561do_sigbus: 562 up_read(&mm->mmap_sem); 563 info.si_signo = SIGBUS; 564 info.si_errno = 0; 565 info.si_code = BUS_ADRERR; 566 info.si_addr = (void __user *) address; 567 info.si_trapno = 0; 568 force_sig_info (SIGBUS, &info, tsk); 569} 570 571void window_overflow_fault(void) 572{ 573 unsigned long sp; 574 575 sp = current_thread_info()->rwbuf_stkptrs[0]; 576 if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) 577 force_user_fault(sp + 0x38, 1); 578 force_user_fault(sp, 1); 579} 580 581void window_underflow_fault(unsigned long sp) 582{ 583 if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) 584 force_user_fault(sp + 0x38, 0); 585 force_user_fault(sp, 0); 586} 587 588void window_ret_fault(struct pt_regs *regs) 589{ 590 unsigned long sp; 591 592 sp = regs->u_regs[UREG_FP]; 593 if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) 594 force_user_fault(sp + 0x38, 0); 595 force_user_fault(sp, 0); 596}