X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fcris%2Farch-v10%2Fmm%2Ffault.c;h=fe2615022b9741ca2473300f2fa1d66ac494ec87;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=7a012adc157934cd3519297ed56921b66fd422e3;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/cris/arch-v10/mm/fault.c b/arch/cris/arch-v10/mm/fault.c index 7a012adc1..fe2615022 100644 --- a/arch/cris/arch-v10/mm/fault.c +++ b/arch/cris/arch-v10/mm/fault.c @@ -14,6 +14,7 @@ #include #include #include +#include /* debug of low-level TLB reload */ #undef DEBUG @@ -24,13 +25,11 @@ #define D(x) #endif -extern volatile pgd_t *current_pgd; - 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 error_code); + int protection, int writeaccess); /* fast TLB-fill fault handler * this is called from entry.S with interrupts disabled @@ -39,22 +38,24 @@ asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs, void handle_mmu_bus_fault(struct pt_regs *regs) { - int cause, select; + int cause; + int select; #ifdef DEBUG int index; int page_id; int acc, inv; #endif - int miss, we, writeac; + pgd_t* pgd = (pgd_t*)per_cpu(current_pgd, smp_processor_id()); pmd_t *pmd; pte_t pte; - int errcode; + int miss, we, writeac; unsigned long address; + unsigned long flags; cause = *R_MMU_CAUSE; - select = *R_TLB_SELECT; address = cause & PAGE_MASK; /* get faulting address */ + select = *R_TLB_SELECT; #ifdef DEBUG page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); @@ -66,110 +67,29 @@ handle_mmu_bus_fault(struct pt_regs *regs) we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause); - /* ETRAX 100LX TR89 bugfix: if the second half of an unaligned - * write causes a MMU-fault, it will not be restarted correctly. - * This could happen if a write crosses a page-boundary and the - * second page is not yet COW'ed or even loaded. The workaround - * is to clear the unaligned bit in the CPU status record, so - * that the CPU will rerun both the first and second halves of - * the instruction. This will not have any sideeffects unless - * the first half goes to any device or memory that can't be - * written twice, and which is mapped through the MMU. - * - * We only need to do this for writes. - */ - - if(writeac) - regs->csrinstr &= ~(1 << 5); - - /* Set errcode's R/W flag according to the mode which caused the - * fault - */ - - errcode = writeac << 1; - 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)); - /* for a miss, we need to reload the TLB entry */ - - if (miss) { - /* see if the pte exists at all - * refer through current_pgd, dont use mm->pgd - */ - - pmd = (pmd_t *)(current_pgd + pgd_index(address)); - if (pmd_none(*pmd)) - goto dofault; - if (pmd_bad(*pmd)) { - printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd); - pmd_clear(pmd); - return; - } - pte = *pte_offset_kernel(pmd, address); - if (!pte_present(pte)) - goto dofault; - -#ifdef DEBUG - printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte)); - if (pte_val(pte) & _PAGE_SILENT_WRITE) - printk("Silent-W "); - if (pte_val(pte) & _PAGE_KERNEL) - printk("Kernel "); - if (pte_val(pte) & _PAGE_SILENT_READ) - printk("Silent-R "); - if (pte_val(pte) & _PAGE_GLOBAL) - printk("Global "); - if (pte_val(pte) & _PAGE_PRESENT) - printk("Present "); - if (pte_val(pte) & _PAGE_ACCESSED) - printk("Accessed "); - if (pte_val(pte) & _PAGE_MODIFIED) - printk("Modified "); - if (pte_val(pte) & _PAGE_READ) - printk("Readable "); - if (pte_val(pte) & _PAGE_WRITE) - printk("Writeable "); - printk("\n"); -#endif - - /* load up the chosen TLB entry - * this assumes the pte format is the same as the TLB_LO layout. - * - * the write to R_TLB_LO also writes the vpn and page_id fields from - * R_MMU_CAUSE, which we in this case obviously want to keep - */ - - *R_TLB_LO = pte_val(pte); + /* 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_save_flags(flags); + local_irq_disable(); + pmd = (pmd_t *)(pgd + pgd_index(address)); + if (pmd_none(*pmd)) return; - } - - errcode = 1 | (we << 1); - - dofault: - /* leave it to the MM system fault handler below */ - D(printk("do_page_fault %lx errcode %d\n", address, errcode)); - do_page_fault(address, regs, errcode); -} - -/* Called from arch/cris/mm/fault.c to find fixup code. */ -int -find_fixup_code(struct pt_regs *regs) -{ - const struct exception_table_entry *fixup; - - if ((fixup = search_exception_tables(regs->irp)) != 0) { - /* Adjust the instruction pointer in the stackframe. */ - regs->irp = fixup->fixup; - - /* - * Don't return by restoring the CPU state, so switch - * frame-type. - */ - regs->frametype = CRIS_FRAME_NORMAL; - return 1; - } - - return 0; + pte = *pte_offset_kernel(pmd, address); + if (!pte_present(pte)) + return; + *R_TLB_SELECT = select; + *R_TLB_HI = cause; + *R_TLB_LO = pte_val(pte); + local_irq_restore(flags); }