URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [ppc/] [mm/] [fault.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * ARCH/ppc/mm/fault.c * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Ported to PPC by Gary Thomas */ #include <linux/signal.h> #include <linux/sched.h> #include <linux/head.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/types.h> #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> #include <asm/page.h> #include <asm/pgtable.h> extern void die_if_kernel(char *, struct pt_regs *, long); extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long); #undef SHOW_FAULTS #undef NOISY_INSTRFAULT #undef NOISY_DATAFAULT #define NEWMM 1 void DataAccessException(struct pt_regs *regs) { pgd_t *dir; pmd_t *pmd; pte_t *pte; int tries, mode = 0; #ifdef NOISY_DATAFAULT printk("Data fault on %x\n",regs->dar); #endif if (user_mode(regs)) mode |= 0x04; if (regs->dsisr & 0x02000000) mode |= 0x02; /* Load/store */ if (regs->dsisr & 0x08000000) mode |= 0x01; /* Protection violation */ if (mode & 0x01) { #ifdef NOISY_DATAFAULT printk("Write Protect fault\n "); #endif do_page_fault(regs, regs->dar, mode); #ifdef NOISY_DATAFAULT printk("Write Protect fault handled\n"); #endif return; } for (tries = 0; tries < 1; tries++) { dir = pgd_offset(current->mm, regs->dar & PAGE_MASK); if (dir) { pmd = pmd_offset(dir, regs->dar & PAGE_MASK); if (pmd && pmd_present(*pmd)) { pte = pte_offset(pmd, regs->dar & PAGE_MASK); if (pte && pte_present(*pte)) { #if NOISY_DATAFAULT printk("Page mapped - PTE: %x[%x]\n", pte, *(long *)pte); #endif MMU_hash_page(¤t->tss, regs->dar & PAGE_MASK, pte); /*MMU_hash_page2(current->mm, regs->dar & PAGE_MASK, pte);*/ return; } } } else { #if NOISY_DATAFAULT printk("No PGD\n"); #endif } do_page_fault(regs, regs->dar, mode); } } void InstructionAccessException(struct pt_regs *regs) { pgd_t *dir; pmd_t *pmd; pte_t *pte; int tries, mode = 0; #if NOISY_INSTRFAULT printk("Instr fault on %x\n",regs->dar); #endif #ifdef NEWMM if (!user_mode(regs)) { panic("InstructionAcessException in kernel mode. PC %x addr %x", regs->nip, regs->dar); } #endif if (user_mode(regs)) mode |= 0x04; if (regs->dsisr & 0x02000000) mode |= 0x02; /* Load/store */ if (regs->dsisr & 0x08000000) mode |= 0x01; /* Protection violation */ if (mode & 0x01) { do_page_fault(regs, regs->dar, mode); return; } for (tries = 0; tries < 1; tries++) { dir = pgd_offset(current->mm, regs->dar & PAGE_MASK); if (dir) { pmd = pmd_offset(dir, regs->dar & PAGE_MASK); if (pmd && pmd_present(*pmd)) { pte = pte_offset(pmd, regs->dar & PAGE_MASK); if (pte && pte_present(*pte)) { MMU_hash_page(¤t->tss, regs->dar & PAGE_MASK, pte); /* MMU_hash_page2(current->mm, regs->dar & PAGE_MASK, pte);*/ return; } } } else { } do_page_fault(regs, regs->dar, mode); } } /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate * routines. * * The error_code parameter just the same as in the i386 version: * * bit 0 == 0 means no page found, 1 means protection fault * bit 1 == 0 means read, 1 means write * bit 2 == 0 means kernel, 1 means user-mode */ void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code) { struct vm_area_struct * vma; unsigned long page; vma = find_vma(current, address); if (!vma) { goto bad_area; } if (vma->vm_start <= address){ goto good_area; } if (!(vma->vm_flags & VM_GROWSDOWN)) { goto bad_area; } if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) { goto bad_area; } vma->vm_offset -= vma->vm_start - (address & PAGE_MASK); vma->vm_start = (address & PAGE_MASK); good_area: /* a write */ if (error_code & 2) { if (!(vma->vm_flags & VM_WRITE)) { goto bad_area; } /* a read */ } else { /* protection fault */ if (error_code & 1) { goto bad_area; } if (!(vma->vm_flags & (VM_READ | VM_EXEC))) { goto bad_area; } } handle_mm_fault(vma, address, error_code & 2); flush_page(address); /* Flush & Invalidate cache - note: address is OK now */ return; bad_area: if (user_mode(regs)) { /* printk("Bad User Area: Addr %x PC %x Task %x pid %d %s\n", address,regs->nip, current,current->pid,current->comm);*/ send_sig(SIGSEGV, current, 1); return; } panic("KERNEL access of bad area PC %x address %x vm_flags %x\n", regs->nip,address,vma->vm_flags); } va_to_phys(unsigned long address) { pgd_t *dir; pmd_t *pmd; pte_t *pte; dir = pgd_offset(current->mm, address & PAGE_MASK); if (dir) { pmd = pmd_offset(dir, address & PAGE_MASK); if (pmd && pmd_present(*pmd)) { pte = pte_offset(pmd, address & PAGE_MASK); if (pte && pte_present(*pte)) { return(pte_page(*pte) | (address & ~(PAGE_MASK-1))); } } else { return (0); } } else { return (0); } return (0); } /* * See if an address should be valid in the current context. */ valid_addr(unsigned long addr) { struct vm_area_struct * vma; for (vma = current->mm->mmap ; ; vma = vma->vm_next) { if (!vma) { return (0); } if (vma->vm_end > addr) break; } if (vma->vm_start <= addr) { return (1); } return (0); }