| /* | |
| * linux/arch/cris/mm/fault.c | |
| * | |
| * Low level bus fault handler | |
| * | |
| * | |
| * Copyright (C) 2000-2007 Axis Communications AB | |
| * | |
| * Authors: Bjorn Wesen | |
| * | |
| */ | |
| #include <linux/mm.h> | |
| #include <asm/uaccess.h> | |
| #include <asm/pgtable.h> | |
| #include <arch/svinto.h> | |
| #include <asm/mmu_context.h> | |
| /* debug of low-level TLB reload */ | |
| #undef DEBUG | |
| #ifdef DEBUG | |
| #define D(x) x | |
| #else | |
| #define D(x) | |
| #endif | |
| extern const struct exception_table_entry | |
| *search_exception_tables(unsigned long addr); | |
| asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs, | |
| int protection, int writeaccess); | |
| /* fast TLB-fill fault handler | |
| * this is called from entry.S with interrupts disabled | |
| */ | |
| void | |
| handle_mmu_bus_fault(struct pt_regs *regs) | |
| { | |
| int cause; | |
| int select; | |
| #ifdef DEBUG | |
| int index; | |
| int page_id; | |
| int acc, inv; | |
| #endif | |
| pgd_t* pgd = (pgd_t*)per_cpu(current_pgd, smp_processor_id()); | |
| pmd_t *pmd; | |
| pte_t pte; | |
| int miss, we, writeac; | |
| unsigned long address; | |
| unsigned long flags; | |
| cause = *R_MMU_CAUSE; | |
| address = cause & PAGE_MASK; /* get faulting address */ | |
| select = *R_TLB_SELECT; | |
| #ifdef DEBUG | |
| page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); | |
| acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); | |
| inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); | |
| index = IO_EXTRACT(R_TLB_SELECT, index, select); | |
| #endif | |
| miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); | |
| we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); | |
| writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause); | |
| D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n", | |
| regs->irp, address, miss, inv, we, acc, index, page_id)); | |
| /* leave it to the MM system fault handler */ | |
| if (miss) | |
| do_page_fault(address, regs, 0, writeac); | |
| else | |
| do_page_fault(address, regs, 1, we); | |
| /* Reload TLB with new entry to avoid an extra miss exception. | |
| * do_page_fault may have flushed the TLB so we have to restore | |
| * the MMU registers. | |
| */ | |
| local_irq_save(flags); | |
| pmd = (pmd_t *)(pgd + pgd_index(address)); | |
| if (pmd_none(*pmd)) | |
| goto exit; | |
| pte = *pte_offset_kernel(pmd, address); | |
| if (!pte_present(pte)) | |
| goto exit; | |
| *R_TLB_SELECT = select; | |
| *R_TLB_HI = cause; | |
| *R_TLB_LO = pte_val(pte); | |
| exit: | |
| local_irq_restore(flags); | |
| } |