diff options
author | Harald Koerfgen <hkoerfg@web.de> | 1999-04-11 17:13:55 +0000 |
---|---|---|
committer | Harald Koerfgen <hkoerfg@web.de> | 1999-04-11 17:13:55 +0000 |
commit | 24377993f15547b37b6337ad42f89cca3b7c6d52 (patch) | |
tree | fe67f233dfa3bb479a8e8ad37a59aadd11b9e93b /arch/mips | |
parent | 30d8d197749ab4766ed02604af02068a00b32964 (diff) |
R3000 changes which don't affect common code
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/kernel/r2300_fpu.S | 183 | ||||
-rw-r--r-- | arch/mips/kernel/r2300_misc.S | 486 | ||||
-rw-r--r-- | arch/mips/kernel/r2300_switch.S | 186 | ||||
-rw-r--r-- | arch/mips/mm/loadmmu.c | 3 | ||||
-rw-r--r-- | arch/mips/mm/r2300.c | 501 |
5 files changed, 819 insertions, 540 deletions
diff --git a/arch/mips/kernel/r2300_fpu.S b/arch/mips/kernel/r2300_fpu.S index 6c699c74d..de682be45 100644 --- a/arch/mips/kernel/r2300_fpu.S +++ b/arch/mips/kernel/r2300_fpu.S @@ -1,16 +1,17 @@ -/* +/* $Id: $ * r2300_fpu.S: Save/restore floating point context for signal handlers. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1996 by Ralf Baechle + * Copyright (C) 1996, 1998 by Ralf Baechle * * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r2300_fpu.S,v 1.3 1997/12/01 16:54:20 ralf Exp $ + * Further modifications to make this work: + * Copyright (c) 1998 Harald Koerfgen */ #include <asm/asm.h> #include <asm/fpregdef.h> @@ -18,61 +19,55 @@ #include <asm/offset.h> #include <asm/regdef.h> - .set mips3 +#define EX(a,b) \ +9: a,##b; \ + .section __ex_table,"a"; \ + PTR 9b,bad_stack; \ + .previous + .set noreorder + .set mips1 /* Save floating point context */ - .align 5 - LEAF(r2300_save_fp_context) - mfc0 t0,CP0_STATUS - sll t0,t0,2 +LEAF(r2300_save_fp_context) - bgez t0,1f - nop - - cfc1 t0,fcr31 - /* Store the 32 single precision registers */ - swc1 $f0,(SC_FPREGS+0)(a0) - swc1 $f1,(SC_FPREGS+8)(a0) - swc1 $f2,(SC_FPREGS+16)(a0) - swc1 $f3,(SC_FPREGS+24)(a0) - swc1 $f4,(SC_FPREGS+32)(a0) - swc1 $f5,(SC_FPREGS+40)(a0) - swc1 $f6,(SC_FPREGS+48)(a0) - swc1 $f7,(SC_FPREGS+56)(a0) - swc1 $f8,(SC_FPREGS+64)(a0) - swc1 $f9,(SC_FPREGS+72)(a0) - swc1 $f10,(SC_FPREGS+80)(a0) - swc1 $f11,(SC_FPREGS+88)(a0) - swc1 $f12,(SC_FPREGS+96)(a0) - swc1 $f13,(SC_FPREGS+104)(a0) - swc1 $f14,(SC_FPREGS+112)(a0) - swc1 $f15,(SC_FPREGS+120)(a0) - swc1 $f16,(SC_FPREGS+128)(a0) - swc1 $f17,(SC_FPREGS+136)(a0) - swc1 $f18,(SC_FPREGS+144)(a0) - swc1 $f19,(SC_FPREGS+152)(a0) - swc1 $f20,(SC_FPREGS+160)(a0) - swc1 $f21,(SC_FPREGS+168)(a0) - swc1 $f22,(SC_FPREGS+176)(a0) - swc1 $f23,(SC_FPREGS+184)(a0) - swc1 $f24,(SC_FPREGS+192)(a0) - swc1 $f25,(SC_FPREGS+200)(a0) - swc1 $f26,(SC_FPREGS+208)(a0) - swc1 $f27,(SC_FPREGS+216)(a0) - swc1 $f28,(SC_FPREGS+224)(a0) - swc1 $f29,(SC_FPREGS+232)(a0) - swc1 $f30,(SC_FPREGS+240)(a0) - swc1 $f31,(SC_FPREGS+248)(a0) - sw t0,SC_FPC_CSR(a0) + cfc1 t1,fcr31 + EX(swc1 $f0,(SC_FPREGS+0)(a0)) + EX(swc1 $f1,(SC_FPREGS+8)(a0)) + EX(swc1 $f2,(SC_FPREGS+16)(a0)) + EX(swc1 $f3,(SC_FPREGS+24)(a0)) + EX(swc1 $f4,(SC_FPREGS+32)(a0)) + EX(swc1 $f5,(SC_FPREGS+40)(a0)) + EX(swc1 $f6,(SC_FPREGS+48)(a0)) + EX(swc1 $f7,(SC_FPREGS+56)(a0)) + EX(swc1 $f8,(SC_FPREGS+64)(a0)) + EX(swc1 $f9,(SC_FPREGS+72)(a0)) + EX(swc1 $f10,(SC_FPREGS+80)(a0)) + EX(swc1 $f11,(SC_FPREGS+88)(a0)) + EX(swc1 $f12,(SC_FPREGS+96)(a0)) + EX(swc1 $f13,(SC_FPREGS+104)(a0)) + EX(swc1 $f14,(SC_FPREGS+112)(a0)) + EX(swc1 $f15,(SC_FPREGS+120)(a0)) + EX(swc1 $f16,(SC_FPREGS+128)(a0)) + EX(swc1 $f17,(SC_FPREGS+136)(a0)) + EX(swc1 $f18,(SC_FPREGS+144)(a0)) + EX(swc1 $f19,(SC_FPREGS+152)(a0)) + EX(swc1 $f20,(SC_FPREGS+160)(a0)) + EX(swc1 $f21,(SC_FPREGS+168)(a0)) + EX(swc1 $f22,(SC_FPREGS+176)(a0)) + EX(swc1 $f23,(SC_FPREGS+184)(a0)) + EX(swc1 $f24,(SC_FPREGS+192)(a0)) + EX(swc1 $f25,(SC_FPREGS+200)(a0)) + EX(swc1 $f26,(SC_FPREGS+208)(a0)) + EX(swc1 $f27,(SC_FPREGS+216)(a0)) + EX(swc1 $f28,(SC_FPREGS+224)(a0)) + EX(swc1 $f29,(SC_FPREGS+232)(a0)) + EX(swc1 $f30,(SC_FPREGS+240)(a0)) + EX(swc1 $f31,(SC_FPREGS+248)(a0)) + EX(sw t1,SC_FPC_CSR(a0)) cfc1 t0,$0 # implementation/version jr ra .set nomacro - sw t0,SC_FPC_EIR(a0) - .set macro -1: - jr ra - .set nomacro - nop + EX(sw t0,SC_FPC_EIR(a0)) .set macro END(r2300_save_fp_context) @@ -85,56 +80,40 @@ * frame on the current content of c0_status, not on the content of the * stack frame which might have been changed by the user. */ - LEAF(r2300_restore_fp_context) - mfc0 t0,CP0_STATUS - sll t0,t0,2 - - bgez t0,1f - nop - - lw t0,SC_FPC_CSR(a0) - /* Restore the 16 odd double precision registers only - * when enabled in the cp0 status register. - */ - ldc1 $f0,(SC_FPREGS+0)(a0) - ldc1 $f1,(SC_FPREGS+8)(a0) - ldc1 $f2,(SC_FPREGS+16)(a0) - ldc1 $f3,(SC_FPREGS+24)(a0) - ldc1 $f4,(SC_FPREGS+32)(a0) - ldc1 $f5,(SC_FPREGS+40)(a0) - ldc1 $f6,(SC_FPREGS+48)(a0) - ldc1 $f7,(SC_FPREGS+56)(a0) - ldc1 $f8,(SC_FPREGS+64)(a0) - ldc1 $f9,(SC_FPREGS+72)(a0) - ldc1 $f10,(SC_FPREGS+80)(a0) - ldc1 $f11,(SC_FPREGS+88)(a0) - ldc1 $f12,(SC_FPREGS+96)(a0) - ldc1 $f13,(SC_FPREGS+104)(a0) - ldc1 $f14,(SC_FPREGS+112)(a0) - ldc1 $f15,(SC_FPREGS+120)(a0) - ldc1 $f16,(SC_FPREGS+128)(a0) - ldc1 $f17,(SC_FPREGS+136)(a0) - ldc1 $f18,(SC_FPREGS+144)(a0) - ldc1 $f19,(SC_FPREGS+152)(a0) - ldc1 $f20,(SC_FPREGS+160)(a0) - ldc1 $f21,(SC_FPREGS+168)(a0) - ldc1 $f22,(SC_FPREGS+176)(a0) - ldc1 $f23,(SC_FPREGS+184)(a0) - ldc1 $f24,(SC_FPREGS+192)(a0) - ldc1 $f25,(SC_FPREGS+200)(a0) - ldc1 $f26,(SC_FPREGS+208)(a0) - ldc1 $f27,(SC_FPREGS+216)(a0) - ldc1 $f28,(SC_FPREGS+224)(a0) - ldc1 $f29,(SC_FPREGS+232)(a0) - ldc1 $f30,(SC_FPREGS+240)(a0) - ldc1 $f31,(SC_FPREGS+248)(a0) +LEAF(r2300_restore_fp_context) + EX(lw t0,SC_FPC_CSR(a0)) + EX(lwc1 $f0,(SC_FPREGS+0)(a0)) + EX(lwc1 $f1,(SC_FPREGS+8)(a0)) + EX(lwc1 $f2,(SC_FPREGS+16)(a0)) + EX(lwc1 $f3,(SC_FPREGS+24)(a0)) + EX(lwc1 $f4,(SC_FPREGS+32)(a0)) + EX(lwc1 $f5,(SC_FPREGS+40)(a0)) + EX(lwc1 $f6,(SC_FPREGS+48)(a0)) + EX(lwc1 $f7,(SC_FPREGS+56)(a0)) + EX(lwc1 $f8,(SC_FPREGS+64)(a0)) + EX(lwc1 $f9,(SC_FPREGS+72)(a0)) + EX(lwc1 $f10,(SC_FPREGS+80)(a0)) + EX(lwc1 $f11,(SC_FPREGS+88)(a0)) + EX(lwc1 $f12,(SC_FPREGS+96)(a0)) + EX(lwc1 $f13,(SC_FPREGS+104)(a0)) + EX(lwc1 $f14,(SC_FPREGS+112)(a0)) + EX(lwc1 $f15,(SC_FPREGS+120)(a0)) + EX(lwc1 $f16,(SC_FPREGS+128)(a0)) + EX(lwc1 $f17,(SC_FPREGS+136)(a0)) + EX(lwc1 $f18,(SC_FPREGS+144)(a0)) + EX(lwc1 $f19,(SC_FPREGS+152)(a0)) + EX(lwc1 $f20,(SC_FPREGS+160)(a0)) + EX(lwc1 $f21,(SC_FPREGS+168)(a0)) + EX(lwc1 $f22,(SC_FPREGS+176)(a0)) + EX(lwc1 $f23,(SC_FPREGS+184)(a0)) + EX(lwc1 $f24,(SC_FPREGS+192)(a0)) + EX(lwc1 $f25,(SC_FPREGS+200)(a0)) + EX(lwc1 $f26,(SC_FPREGS+208)(a0)) + EX(lwc1 $f27,(SC_FPREGS+216)(a0)) + EX(lwc1 $f28,(SC_FPREGS+224)(a0)) + EX(lwc1 $f29,(SC_FPREGS+232)(a0)) + EX(lwc1 $f30,(SC_FPREGS+240)(a0)) + EX(lwc1 $f31,(SC_FPREGS+248)(a0)) jr ra - .set nomacro ctc1 t0,fcr31 - .set macro -1: - jr ra - .set nomacro - nop - .set macro END(r2300_restore_fp_context) diff --git a/arch/mips/kernel/r2300_misc.S b/arch/mips/kernel/r2300_misc.S index 1a557d456..935e783d7 100644 --- a/arch/mips/kernel/r2300_misc.S +++ b/arch/mips/kernel/r2300_misc.S @@ -5,10 +5,15 @@ * * Multi-cpu abstraction reworking: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * Further modifications to make this work: + * Copyright (c) 1998 Harald Koerfgen + * Copyright (c) 1998 Gleb Raiko & Vladimir Roganov */ #include <linux/config.h> #include <asm/asm.h> +#include <asm/current.h> #include <asm/bootinfo.h> #include <asm/cachectl.h> #include <asm/fpregdef.h> @@ -18,380 +23,175 @@ #include <asm/pgtable.h> #include <asm/processor.h> #include <asm/regdef.h> +#include <asm/segment.h> #include <asm/stackframe.h> .text .set mips1 .set noreorder - .align 5 - NESTED(r2300_handle_tlbl, PT_SIZE, sp) - .set noat - /* Check whether this is a refill or an invalid exception */ - mfc0 k0,CP0_BADVADDR - mfc0 k1,CP0_ENTRYHI - ori k0,0xfff # clear ASID... - xori k0,0xfff # in BadVAddr - andi k1,0xfc0 # get current ASID - or k0,k1 # make new entryhi - mfc0 k1,CP0_ENTRYHI - mtc0 k0,CP0_ENTRYHI - nop # for pipeline - nop - nop - tlbp - nop # for pipeline - nop - mfc0 k0,CP0_INDEX - - bgez k0,invalid_tlbl # bad addr in c0_badvaddr - mtc0 k1,CP0_ENTRYHI - - /* Damn... The next nop is required on the R4400PC V5.0, but - * I don't know why - at least there is no documented - * reason as for the others :-( - * And I haven't tested it as being necessary on R3000 - PMA. - * (The R3000 pipeline has only 5 stages, so it's probably not - * required -- Ralf) - */ - nop - -#ifdef CONF_DEBUG_TLB - /* OK, this is a double fault. Let's see whether this is - * due to an invalid entry in the page_table. - */ - /* used to be dmfc0 */ - mfc0 k0,CP0_BADVADDR - /* FIXME: This srl/sll sequence is as it is for the R4xx0, - * and I suspect that it should be different for - * the R[23]000. PMA - * (No, it's the assembler way to do - * k0 = k0 / PAGE_SIZE; - * k0 = k0 * sizeof(pte_t) - * Acutally the R4xx0 code will have to change when - * switching to 64 bit ... -- Ralf) - */ - srl k0,12 # get PFN? - sll k0,2 - lui k1,%HI(TLBMAP) - addu k0,k1 - lw k1,(k0) - andi k1,(_PAGE_PRESENT|_PAGE_ACCESSED) - bnez k1,reload_pgd_entries - nop - - .set noat - SAVE_ALL - .set at - PRINT("Double fault caused by invalid entries in pgd:\n") - mfc0 a1,CP0_BADVADDR - PRINT("Double fault address : %08lx\n") - mfc0 a1,CP0_EPC - PRINT("c0_epc : %08lx\n") - - jal show_regs - move a0,sp - - jal dump_tlb_nonwired - nop - - mfc0 a0,CP0_BADVADDR +#undef NOTLB_OPTIMIZE /* If you are paranoid, define this. */ + + /* ABUSE of CPP macros 101. */ + + /* After this macro runs, the pte faulted on is + * in register PTE, a ptr into the table in which + * the pte belongs is in PTR. + */ +#define LOAD_PTE(pte, ptr) \ + mfc0 pte, CP0_BADVADDR; \ + _GET_CURRENT(ptr); \ + srl pte, pte, 22; \ + lw ptr, THREAD_PGDIR(ptr); \ + sll pte, pte, 2; \ + addu ptr, pte, ptr; \ + mfc0 pte, CP0_CONTEXT; \ + lw ptr, (ptr); \ + andi pte, pte, 0xffc; \ + addu ptr, ptr, pte; \ + lw pte, (ptr); \ + nop; + + /* This places the even/odd pte pair in the page + * table at PTR into ENTRYLO0 and ENTRYLO1 using + * TMP as a scratch register. + */ +#define PTE_RELOAD(ptr) \ + lw ptr, (ptr) ; \ + nop ; \ + mtc0 ptr, CP0_ENTRYLO0; \ + nop; + +#define DO_FAULT(write) \ + .set noat; \ + .set macro; \ + SAVE_ALL; \ + mfc0 a2, CP0_BADVADDR; \ + STI; \ + .set at; \ + move a0, sp; \ + jal do_page_fault; \ + li a1, write; \ + j ret_from_sys_call; \ + nop; \ + .set noat; \ + .set nomacro; + + /* Check is PTE is present, if not then jump to LABEL. + * PTR points to the page table where this PTE is located, + * when the macro is done executing PTE will be restored + * with it's original value. + */ +#define PTE_PRESENT(pte, ptr, label) \ + andi pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ + xori pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ + bnez pte, label; \ + .set push; \ + .set reorder; \ + lw pte, (ptr); \ + .set pop; + + /* Make PTE valid, store result in PTR. */ +#define PTE_MAKEVALID(pte, ptr) \ + ori pte, pte, (_PAGE_VALID | _PAGE_ACCESSED); \ + sw pte, (ptr); + + /* Check if PTE can be written to, if not branch to LABEL. + * Regardless restore PTE with value from PTR when done. + */ +#define PTE_WRITABLE(pte, ptr, label) \ + andi pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \ + xori pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \ + bnez pte, label; \ + .set push; \ + .set reorder; \ + lw pte, (ptr); \ + .set pop; + + + /* Make PTE writable, update software status bits as well, + * then store at PTR. + */ +#define PTE_MAKEWRITE(pte, ptr) \ + ori pte, pte, (_PAGE_ACCESSED | _PAGE_MODIFIED | \ + _PAGE_VALID | _PAGE_DIRTY); \ + sw pte, (ptr); - jal dump_list_current - nop + .set noreorder - .set noat - STI - .set at - PANIC("Corrupted pagedir") + .align 5 +NESTED(r2300_handle_tlbl, PT_SIZE, sp) .set noat -reload_pgd_entries: -#endif /* CONF_DEBUG_TLB */ - - /* Load missing pair of entries from the pgd and return. */ - mfc0 k1,CP0_CONTEXT - lw k0,(k1) # Never causes nested exception - mfc0 k1,CP0_EPC # get the return PC - srl k0,12 # Convert to EntryLo format - mtc0 k0,CP0_ENTRYLO0 - nop # for pipeline - tlbwr - nop # for pipeline +#ifndef NOTLB_OPTIMIZE + /* Test present bit in entry. */ + LOAD_PTE(k0, k1) + tlbp nop + PTE_PRESENT(k0, k1, nopage_tlbl) + PTE_MAKEVALID(k0, k1) + PTE_RELOAD(k1) + tlbwi nop - /* We don't know whether the original access was read or - * write, so return and see what happens... - */ - jr k1 - rfe - - /* Handle invalid exception - * - * There are two possible causes for an invalid (tlbl) - * exception: - * 1) pages with present bit set but the valid bit clear - * 2) nonexistant pages - * Case one needs fast handling, therefore don't save - * registers yet. - * - * k0 contains c0_index. - */ -invalid_tlbl: -#ifdef CONFIG_TLB_SHUTDOWN - /* Remove entry so we don't need to care later - * For sake of the pipeline the tlbwi insn has been moved down. - * Moving it around is juggling with explosives... - */ - /* FIXME: Why is Ralf setting bit 3 of k1? This may need to - * be changed for R[236]000! PMA - * (The new ENTRYHI value will then point represent a - * inique virtual address outside the 32 bit address - * limit. This is just paranoia to avoid a tlb - * shutdown. This whole part of the routine is probably - * no longer required and can be removed -- Ralf) - */ - lui k1,0x0008 - or k0,k1 - sll k0,12 # make it EntryHi format - mtc0 k0,CP0_ENTRYHI - mtc0 zero,CP0_ENTRYLO0 -#endif - /* Test present bit in entry */ - mfc0 k0,CP0_BADVADDR - /* FIXME: This srl/sll sequence is as it is for the R4xx0, - * and I suspect that it should be different for - * the R[23]000. PMA - * (No, it's the assembler way to do - * k0 = k0 / PAGE_SIZE; - * k0 = k0 * sizeof(pte_t) - * Acutally the R4xx0 code will have to change when - * switching to 64 bit ... -- Ralf) - */ - srl k0,12 - sll k0,2 -#ifdef CONFIG_TLB_SHUTDOWN - tlbwi # do not move! -#endif - lui k1,%HI(TLBMAP) - addu k0,k1 - lw k1,(k0) - andi k1,(_PAGE_PRESENT|_PAGE_READ) - xori k1,(_PAGE_PRESENT|_PAGE_READ) - - bnez k1,nopage_tlbl - lw k1,(k0) - - /* Present and read bits are set -> set valid and accessed bits */ - ori k1,(_PAGE_VALID|_PAGE_ACCESSED) - sw k1,(k0) - mfc0 k1,CP0_EPC + mfc0 k0, CP0_EPC nop - - jr k1 + jr k0 rfe - - /* Page doesn't exist. Lots of work which is less important - * for speed needs to be done, so hand it all over to the - * kernel memory management routines. - */ nopage_tlbl: - SAVE_ALL - mfc0 a2,CP0_BADVADDR - STI - .set at - /* a0 (struct pt_regs *) regs - * a1 (unsigned long) 0 for read access - * a2 (unsigned long) faulting virtual address - */ - move a0,sp - jal do_page_fault - li a1,0 - - j ret_from_sys_call - nop - END(r2300_handle_tlbl) +#endif + DO_FAULT(0) +END(r2300_handle_tlbl) - .text - .align 5 - NESTED(r2300_handle_tlbs, PT_SIZE, sp) +NESTED(r2300_handle_tlbs, PT_SIZE, sp) .set noat - /* It is impossible that is a nested reload exception. - * Therefore this must be a invalid exception. - * Two possible cases: - * 1) Page exists but not dirty. - * 2) Page doesn't exist yet. Hand over to the kernel. - * - * Test whether present bit in entry is set - */ - /* used to be dmfc0 */ - mfc0 k0,CP0_BADVADDR - /* FIXME: This srl/sll sequence is as it is for the R4xx0, - * and I suspect that it should be different for - * the R[23]000. PMA - */ - srl k0,12 - sll k0,2 - lui k1,%HI(TLBMAP) - addu k0,k1 - lw k1,(k0) - tlbp # find faulting entry - andi k1,(_PAGE_PRESENT|_PAGE_WRITE) - xori k1,(_PAGE_PRESENT|_PAGE_WRITE) - bnez k1,nopage_tlbs - lw k1,(k0) - - /* Present and writable bits set: set accessed and dirty bits. */ - ori k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \ - _PAGE_VALID|_PAGE_DIRTY) - sw k1,(k0) - /* Now reload the entry into the TLB */ - /* FIXME: Why has Ralf set bit 2? Should it be different for - * R[23]000? PMA - * (The ori/xori combination actually _clears_ bit 2. - * This is required for the R4xx0 these CPUs always - * map page pairs; a page pair of 4k pages therfore - * has always an address with bit 2 set to zero. -- Ralf) - */ - ori k0,0x0004 - xori k0,0x0004 - lw k0,(k0) - srl k0,12 - mtc0 k0,CP0_ENTRYLO0 - mfc0 k1,CP0_EPC - nop # for pipeline +#ifndef NOTLB_OPTIMIZE + LOAD_PTE(k0, k1) + tlbp # find faulting entry + nop + PTE_WRITABLE(k0, k1, nopage_tlbs) + PTE_MAKEWRITE(k0, k1) + PTE_RELOAD(k1) tlbwi - nop # for pipeline nop + mfc0 k0, CP0_EPC nop - - jr k1 + jr k0 rfe - - /* Page doesn't exist. Lots of work which is less important - * for speed needs to be done, so hand it all over to the - * kernel memory management routines. - */ nopage_tlbs: -nowrite_mod: -#ifdef CONFIG_TLB_SHUTDOWN - /* Remove entry so we don't need to care later */ - mfc0 k0,CP0_INDEX -#ifdef CONF_DEBUG_TLB - bgez k0,2f - nop - /* We got a tlbs exception but found no matching entry in - * the tlb. This should never happen. Paranoia makes us - * check it, though. - */ - SAVE_ALL - jal show_regs - move a0,sp - .set at - mfc0 a1,CP0_BADVADDR - PRINT("c0_badvaddr == %08lx\n") - mfc0 a1,CP0_INDEX - PRINT("c0_index == %08x\n") - mfc0 a1,CP0_ENTRYHI - PRINT("c0_entryhi == %08x\n") - .set noat - STI - .set at - PANIC("Tlbs or tlbm exception with no matching entry in tlb") -1: - j 1b - nop -2: -#endif /* CONF_DEBUG_TLB */ - /* FIXME: Why is Ralf setting bit 3 of k1? This may need to - * be changed for R[236]000! PMA - * (The new ENTRYHI value will then point represent a - * inique virtual address outside the 32 bit address - * limit. This is just paranoia to avoid a tlb - * shutdown. This whole part of the routine is probably - * no longer required and can be removed -- Ralf) - */ - lui k1,0x0008 - or k0,k1 - sll k0,12 - mtc0 k0,CP0_ENTRYHI - mtc0 zero,CP0_ENTRYLO0 - nop # for pipeline - nop # R4000 V2.2 requires 4 NOPs - nop - nop - tlbwi -#endif /* CONFIG_TLB_SHUTDOWN */ - .set noat - SAVE_ALL - mfc0 a2,CP0_BADVADDR - STI - .set at - /* a0 (struct pt_regs *) regs - * a1 (unsigned long) 1 for write access - * a2 (unsigned long) faulting virtual address - */ - move a0,sp - jal do_page_fault - li a1,1 - - j ret_from_sys_call - nop - END(r2300_handle_tlbs) +#endif + DO_FAULT(1) +END(r2300_handle_tlbs) .align 5 - NESTED(r2300_handle_mod, PT_SIZE, sp) +NESTED(r2300_handle_mod, PT_SIZE, sp) .set noat - /* Two possible cases: - * 1) Page is writable but not dirty -> set dirty and return - * 2) Page is not writable -> call C handler - */ - /* used to be dmfc0 */ - mfc0 k0,CP0_BADVADDR - /* FIXME: This srl/sll sequence is as it is for the R4xx0, - * and I suspect that it should be different for - * the R[23]000. PMA - */ - srl k0,12 - sll k0,2 - lui k1,%HI(TLBMAP) - addu k0,k1 - lw k1,(k0) +#ifndef NOTLB_OPTIMIZE + LOAD_PTE(k0, k1) tlbp # find faulting entry - andi k1,_PAGE_WRITE - - beqz k1,nowrite_mod - lw k1,(k0) - - /* Present and writable bits set: set accessed and dirty bits. */ - ori k1,(_PAGE_ACCESSED|_PAGE_DIRTY) - sw k1,(k0) - /* Now reload the entry into the tlb */ - /* FIXME: Why has Ralf set bit 2? Should it be different for - * R[23]000? PMA - * (The ori/xori combination actually _clears_ bit 2. - * This is required for the R4xx0 these CPUs always - * map page pairs; a page pair of 4k pages therfore - * has always an address with bit 2 set to zero. -- Ralf) - */ - ori k0,0x0004 - xori k0,0x0004 - lw k0,(k0) - srl k0,12 - mtc0 k0,CP0_ENTRYLO0 - mfc0 k1,CP0_EPC - nop # for pipeline - nop + andi k0, k0, _PAGE_WRITE + beqz k0, nowrite_mod + .set push + .set reorder + lw k0, (k1) + .set pop + + /* Present and writable bits set, set accessed and dirty bits. */ + PTE_MAKEWRITE(k0, k1) + + /* Now reload the entry into the tlb. */ + PTE_RELOAD(k1) nop tlbwi - nop # for pipeline nop + mfc0 k0, CP0_EPC nop - - jr k1 + jr k0 rfe - END(r2300_handle_mod) - .set at +#endif + +nowrite_mod: + DO_FAULT(1) +END(r2300_handle_mod) diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S index b2b55bf0b..4b8698d3b 100644 --- a/arch/mips/kernel/r2300_switch.S +++ b/arch/mips/kernel/r2300_switch.S @@ -1,12 +1,13 @@ -/* - * r2300_switch.S: R3000/R2000 specific task switching code. +/* $Id: $ + * r2300_switch.S: R2300 specific task switching code. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse * * Multi-cpu abstraction and macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r2300_switch.S,v 1.3 1998/03/27 04:47:55 ralf Exp $ + * Further modifications to make this work: + * Copyright (c) 1998 Harald Koerfgen */ #include <asm/asm.h> #include <asm/bootinfo.h> @@ -15,6 +16,7 @@ #include <asm/fpregdef.h> #include <asm/mipsconfig.h> #include <asm/mipsregs.h> +#include <asm/offset.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> @@ -23,68 +25,132 @@ #include <asm/asmmacro.h> -/* XXX The following is fucking losing... find a better way dave. */ -MODE_ALIAS = 0x00e0 # uncachable, dirty, valid - - .text .set mips1 - .set noreorder -/* - * Code necessary to switch tasks on an Linux/MIPS machine. - * FIXME: We don't need to disable interrupts anymore. - */ .align 5 - LEAF(r2300_resume) - mfc0 t1,CP0_STATUS # Save status register - sw t1,THREAD_STATUS($28) - ori t2,t1,0x1f # Disable interrupts - xori t2,0x1e - mtc0 t2,CP0_STATUS + +LEAF(r2300_resume) + .set reorder + mfc0 t1, CP0_STATUS + .set noreorder + sw t1, THREAD_STATUS($28) CPU_SAVE_NONSCRATCH($28) - sll t2,t1,2 # Save floating point state - bgez t2,1f - sw ra,THREAD_REG31($28) - FPU_SAVE($28, t0) -1: + sw ra, THREAD_REG31($28) + + /* + * The order of restoring the registers takes care of the race + * updating $28, $29 and kernelsp without disabling ints. + */ move $28, a0 - lw t0,THREAD_PGDIR($28) # Switch the root pointer - li t1,TLB_ROOT # get PFN - mtc0 t1,CP0_ENTRYHI - mtc0 zero,CP0_INDEX - srl t0,12 # PFN is 12 bits west - ori t0,MODE_ALIAS # want uncachable, dirty, valid - mtc0 t0,CP0_ENTRYLO0 - lw a2,THREAD_STATUS($28) - tlbwi - - /* Flush TLB. */ - mfc0 t3,CP0_STATUS # disable interrupts... - ori t4,t3,1 - xori t4,1 - mtc0 t4,CP0_STATUS - lw t0,mips_tlb_entries - mtc0 zero,CP0_ENTRYLO0 -1: - subu t0,1 - mtc0 t0,CP0_INDEX - lui t1,0x0008 - or t1,t0,t1 - sll t1,12 - mtc0 t1,CP0_ENTRYHI - bne t2,t0,1b - tlbwi - - ori t1,a2,1 # Restore FPU, pipeline magic - xori t1,1 - mtc0 t1,CP0_STATUS - sll t0,a2,2 - bgez t0,1f - lw ra,THREAD_REG31($28) - FPU_RESTORE($28, t0) -1: CPU_RESTORE_NONSCRATCH($28) addiu t0, $28, KERNEL_STACK_SIZE-32 - sw t0,kernelsp + sw t0, kernelsp + mfc0 t1, CP0_STATUS /* Do we really need this? */ + li a3, 0xff00 + and t1, a3 + lw a2, THREAD_STATUS($28) + nor a3, $0, a3 + and a2, a3 + lw a3, TASK_MM($28) + or a2, t1 + lw a3, MM_CONTEXT(a3) + mtc0 a2, CP0_STATUS + andi a3, 0xfc0 jr ra - mtc0 a2,CP0_STATUS # Restore status register + mtc0 a3, CP0_ENTRYHI END(r2300_resume) + +/* + * Do lazy fpu context switch. Saves FPU context to the process in a0 + * and loads the new context of the current process. + */ + +#define ST_OFF (KERNEL_STACK_SIZE - 32 - PT_SIZE + PT_STATUS) + +LEAF(r2300_lazy_fpu_switch) + mfc0 t0, CP0_STATUS # enable cp1 + li t3, 0x20000000 + or t0, t3 + mtc0 t0, CP0_STATUS + + beqz a0, 2f # Save floating point state + nor t3, zero, t3 + .set reorder + lw t1, ST_OFF(a0) # last thread looses fpu + .set noreorder + and t1, t3 + sw t1, ST_OFF(a0) + swc1 $f0, (THREAD_FPU + 0x00)(a0) + FPU_SAVE(a0, t1) # clobbers t1 + +2: + lwc1 $f0, (THREAD_FPU + 0x00)($28) + .set reorder + FPU_RESTORE($28, t0) # clobbers t0 + jr ra + END(r2300_lazy_fpu_switch) + +/* + * Save a thread's fp context. + */ + .set noreorder +LEAF(r2300_save_fp) + FPU_SAVE(a0, t1) # clobbers t1 + jr ra + swc1 $f0, (THREAD_FPU + 0x00)(a0) + END(r2300_save_fp) + +/* + * Load the FPU with signalling NANS. This bit pattern we're using has + * the property that no matter wether considered as single or as double + * precission represents signaling NANS. + * + * We initialize fcr31 to rounding to nearest, no exceptions. + */ + +#define FPU_DEFAULT 0x00000000 + +LEAF(r2300_init_fpu) + mfc0 t0, CP0_STATUS + li t1, 0x20000000 + or t0, t1 + mtc0 t0, CP0_STATUS + + li t1, FPU_DEFAULT + ctc1 t1, fcr31 + + li t0, -1 + + mtc1 t0, $f0 + mtc1 t0, $f1 + mtc1 t0, $f2 + mtc1 t0, $f3 + mtc1 t0, $f4 + mtc1 t0, $f5 + mtc1 t0, $f6 + mtc1 t0, $f7 + mtc1 t0, $f8 + mtc1 t0, $f9 + mtc1 t0, $f10 + mtc1 t0, $f11 + mtc1 t0, $f12 + mtc1 t0, $f13 + mtc1 t0, $f14 + mtc1 t0, $f15 + mtc1 t0, $f16 + mtc1 t0, $f17 + mtc1 t0, $f18 + mtc1 t0, $f19 + mtc1 t0, $f20 + mtc1 t0, $f21 + mtc1 t0, $f22 + mtc1 t0, $f23 + mtc1 t0, $f24 + mtc1 t0, $f25 + mtc1 t0, $f26 + mtc1 t0, $f27 + mtc1 t0, $f28 + mtc1 t0, $f29 + mtc1 t0, $f30 + jr ra + mtc1 t0, $f31 + END(r2300_init_fpu) diff --git a/arch/mips/mm/loadmmu.c b/arch/mips/mm/loadmmu.c index f8730ad26..82f6793c1 100644 --- a/arch/mips/mm/loadmmu.c +++ b/arch/mips/mm/loadmmu.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: loadmmu.c,v 1.6 1998/03/22 23:27:15 ralf Exp $ + * $Id: loadmmu.c,v 1.7 1998/04/05 11:23:55 ralf Exp $ */ #include <linux/init.h> #include <linux/kernel.h> @@ -66,6 +66,7 @@ __initfunc(void loadmmu(void)) switch(mips_cputype) { case CPU_R2000: case CPU_R3000: + case CPU_R3000A: printk("Loading R[23]00 MMU routines.\n"); ld_mmu_r2300(); break; diff --git a/arch/mips/mm/r2300.c b/arch/mips/mm/r2300.c index 8f8e7ddfa..3c2cec663 100644 --- a/arch/mips/mm/r2300.c +++ b/arch/mips/mm/r2300.c @@ -1,8 +1,13 @@ -/* $Id: r2300.c,v 1.6 1998/08/25 09:14:46 ralf Exp $ - * +/* * r2300.c: R2000 and R3000 specific mmu/cache code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * with a lot of changes to make this thing work for R3000s + * Copyright (C) 1998 Harald Koerfgen + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + * + * $Id: r2300.c,v 1.5 1998/04/05 11:23:55 ralf Exp $ */ #include <linux/init.h> #include <linux/kernel.h> @@ -11,12 +16,40 @@ #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/mmu_context.h> #include <asm/system.h> #include <asm/sgialib.h> -#include <asm/mmu_context.h> +#include <asm/mipsregs.h> +#include <asm/io.h> +/* + * Temporarily disabled + * +#include <asm/wbflush.h> + */ + +/* + * According to the paper written by D. Miller about Linux cache & TLB + * flush implementation, DMA/Driver coherence should be done at the + * driver layer. Thus, normally, we don't need flush dcache for R3000. + * Define this if driver does not handle cache consistency during DMA ops. + */ +#undef DO_DCACHE_FLUSH + +/* + * Unified cache space description structure + */ +static struct cache_space { + unsigned long ca_flags; /* Cache space access flags */ + int size; /* Cache space size */ +} icache, dcache; + +#undef DEBUG_TLB +#undef DEBUG_CACHE extern unsigned long mips_tlb_entries; +#define NTLB_ENTRIES 64 /* Fixed on all R23000 variants... */ + /* page functions */ void r2300_clear_page(unsigned long page) { @@ -94,80 +127,425 @@ static void r2300_copy_page(unsigned long to, unsigned long from) "I" (PAGE_SIZE)); } -/* Cache operations. */ -static inline void r2300_flush_cache_all(void) { } -static void r2300_flush_cache_mm(struct mm_struct *mm) { } +__initfunc(static unsigned long size_cache(unsigned long ca_flags)) +{ + unsigned long flags, status, dummy, size; + volatile unsigned long *p; + + p = (volatile unsigned long *) KSEG0; + + save_and_cli(flags); + + /* isolate cache space */ + write_32bit_cp0_register(CP0_STATUS, (ca_flags|flags)&~ST0_IEC); + + *p = 0xa5a55a5a; + dummy = *p; + status = read_32bit_cp0_register(CP0_STATUS); + + if (dummy != 0xa5a55a5a || (status & (1<<19))) { + size = 0; + } else { + for (size = 512; size <= 0x40000; size <<= 1) + *(p + size) = 0; + *p = -1; + for (size = 512; + (size <= 0x40000) && (*(p + size) == 0); + size <<= 1) + ; + if (size > 0x40000) + size = 0; + } + restore_flags(flags); + + return size * sizeof(*p); +} + +__initfunc(static void probe_dcache(void)) +{ + dcache.size = size_cache(dcache.ca_flags = ST0_DE); + printk("Data cache %dkb\n", dcache.size >> 10); +} + +__initfunc(static void probe_icache(void)) +{ + icache.size = size_cache(icache.ca_flags = ST0_DE|ST0_CE); + printk("Instruction cache %dkb\n", icache.size >> 10); +} + +static inline unsigned long get_phys_page (unsigned long page, + struct mm_struct *mm) +{ + page &= PAGE_MASK; + if (page >= KSEG0 && page < KSEG1) { + /* + * We already have physical address + */ + return page; + } else { + if (!mm) { + printk ("get_phys_page: vaddr without mm\n"); + return 0; + } else { + /* + * Find a physical page using mm_struct + */ + pgd_t *page_dir; + pmd_t *page_middle; + pte_t *page_table, pte; + + unsigned long address = page; + + page_dir = pgd_offset(mm, address); + if (pgd_none(*page_dir)) + return 0; + page_middle = pmd_offset(page_dir, address); + if (pmd_none(*page_middle)) + return 0; + page_table = pte_offset(page_middle, address); + pte = *page_table; + if (!pte_present(pte)) + return 0; + return pte_page(pte); + } + } +} + +static inline void flush_cache_space_page(struct cache_space *space, + unsigned long page) +{ + register unsigned long i, flags, size = space->size; + register volatile unsigned char *p = (volatile unsigned char*) page; + +#ifndef DO_DCACHE_FLUSH + if (space == &dcache) + return; +#endif + if (size > PAGE_SIZE) + size = PAGE_SIZE; + + save_and_cli(flags); + + /* isolate cache space */ + write_32bit_cp0_register(CP0_STATUS, (space->ca_flags|flags)&~ST0_IEC); + + for (i = 0; i < size; i += 64) { + asm ( "sb\t$0,(%0)\n\t" + "sb\t$0,4(%0)\n\t" + "sb\t$0,8(%0)\n\t" + "sb\t$0,12(%0)\n\t" + "sb\t$0,16(%0)\n\t" + "sb\t$0,20(%0)\n\t" + "sb\t$0,24(%0)\n\t" + "sb\t$0,28(%0)\n\t" + "sb\t$0,32(%0)\n\t" + "sb\t$0,36(%0)\n\t" + "sb\t$0,40(%0)\n\t" + "sb\t$0,44(%0)\n\t" + "sb\t$0,48(%0)\n\t" + "sb\t$0,52(%0)\n\t" + "sb\t$0,56(%0)\n\t" + "sb\t$0,60(%0)\n\t" + : : "r" (p) ); + p += 64; + } + + restore_flags(flags); +} + +static inline void flush_cache_space_all(struct cache_space *space) +{ + unsigned long page = KSEG0; + int size = space->size; + +#ifndef DO_DCACHE_FLUSH + if (space == &dcache) + return; +#endif + while(size > 0) { + flush_cache_space_page(space, page); + page += PAGE_SIZE; size -= PAGE_SIZE; + } +} + +static inline void r2300_flush_cache_all(void) +{ + flush_cache_space_all(&dcache); + flush_cache_space_all(&icache); +} + +static void r2300_flush_cache_mm(struct mm_struct *mm) +{ + if(mm->context == 0) + return; +#ifdef DEBUG_CACHE + printk("cmm[%d]", (int)mm->context); +#endif + /* + * This function is called not offen, so it looks + * enough good to flush all caches than scan mm_struct, + * count pages to flush (and, very probably, flush more + * than cache space size :-) + */ + flush_cache_all(); +} + static void r2300_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { + /* + * In general, we need to flush both i- & d- caches here. + * Optimization: if cache space is less than given range, + * it is more quickly to flush all cache than all pages in range. + */ + + unsigned long page; + int icache_done = 0, dcache_done = 0; + + if(mm->context == 0) + return; +#ifdef DEBUG_CACHE + printk("crange[%d]", (int)mm->context); +#endif + if (end - start >= icache.size) { + flush_cache_space_all(&icache); + icache_done = 1; + } + if (end - start >= dcache.size) { + flush_cache_space_all(&dcache); + dcache_done = 1; + } + if (icache_done && dcache_done) + return; + + for (page = start; page < end; page += PAGE_SIZE) { + unsigned long phys_page = get_phys_page(page, mm); + + if (phys_page) { + if (!icache_done) + flush_cache_space_page(&icache, phys_page); + if (!dcache_done) + flush_cache_space_page(&dcache, phys_page); + } + } } static void r2300_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { + struct mm_struct *mm = vma->vm_mm; + + if(mm->context == 0) + return; +#ifdef DEBUG_CACHE + printk("cpage[%d,%08lx]", (int)mm->context, page); +#endif + /* + * User changes page, so we need to check: + * is icache page flush needed ? + * It looks we don't need to flush dcache, + * due it is write-transparent on R3000 + */ + if (vma->vm_flags & VM_EXEC) { + unsigned long phys_page = get_phys_page(page, vma->vm_mm); + if (phys_page) + flush_cache_space_page(&icache, phys_page); + } } static void r2300_flush_page_to_ram(unsigned long page) { - /* XXX What we want to do here is perform a displacement - * XXX flush because there are circumstances where you do - * XXX indeed want to remove stale data from the cache. - * XXX (DMA operations for example, where the cache cannot - * XXX "see" this data get changed.) + /* + * We need to flush both i- & d- caches :-( + */ + unsigned long phys_page = get_phys_page(page, NULL); +#ifdef DEBUG_CACHE + printk("cram[%08lx]", page); +#endif + if (phys_page) { + flush_cache_space_page(&icache, phys_page); + flush_cache_space_page(&dcache, phys_page); + } +} + +static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size) +{ + register unsigned long i, flags; + register volatile unsigned char *p = (volatile unsigned char*) start; + +/* + * Temporarily disabled + wbflush(); */ + + /* + * Invalidate dcache + */ + if (size < 64) + size = 64; + + if (size > dcache.size) + size = dcache.size; + + save_and_cli(flags); + + /* isolate cache space */ + write_32bit_cp0_register(CP0_STATUS, (ST0_DE|flags)&~ST0_IEC); + + for (i = 0; i < size; i += 64) { + asm ( "sb\t$0,(%0)\n\t" + "sb\t$0,4(%0)\n\t" + "sb\t$0,8(%0)\n\t" + "sb\t$0,12(%0)\n\t" + "sb\t$0,16(%0)\n\t" + "sb\t$0,20(%0)\n\t" + "sb\t$0,24(%0)\n\t" + "sb\t$0,28(%0)\n\t" + "sb\t$0,32(%0)\n\t" + "sb\t$0,36(%0)\n\t" + "sb\t$0,40(%0)\n\t" + "sb\t$0,44(%0)\n\t" + "sb\t$0,48(%0)\n\t" + "sb\t$0,52(%0)\n\t" + "sb\t$0,56(%0)\n\t" + "sb\t$0,60(%0)\n\t" + : : "r" (p) ); + p += 64; + } + + restore_flags(flags); } static void r2300_flush_cache_sigtramp(unsigned long page) { + /* + * We need only flush i-cache here + * + * This function receives virtual address (from signal.c), + * but this moment we have needed mm_struct in 'current' + */ + unsigned long phys_page = get_phys_page(page, current->mm); +#ifdef DEBUG_CACHE + printk("csigtramp[%08lx]", page); +#endif + if (phys_page) + flush_cache_space_page(&icache, phys_page); } /* TLB operations. */ static inline void r2300_flush_tlb_all(void) { unsigned long flags; + unsigned long old_ctx; int entry; +#ifdef DEBUG_TLB + printk("[tlball]"); +#endif + save_and_cli(flags); + old_ctx = (get_entryhi() & 0xfc0); write_32bit_cp0_register(CP0_ENTRYLO0, 0); - for(entry = 0; entry < mips_tlb_entries; entry++) { - write_32bit_cp0_register(CP0_INDEX, entry); - write_32bit_cp0_register(CP0_ENTRYHI, ((entry | 0x8) << 12)); + for(entry = 0; entry < NTLB_ENTRIES; entry++) { + write_32bit_cp0_register(CP0_INDEX, entry << 8); + write_32bit_cp0_register(CP0_ENTRYHI, ((entry | 0x80000) << 12)); __asm__ __volatile__("tlbwi"); } + set_entryhi(old_ctx); restore_flags(flags); } static void r2300_flush_tlb_mm(struct mm_struct *mm) { + if(mm->context != 0) { + unsigned long flags; + +#ifdef DEBUG_TLB + printk("[tlbmm<%d>]", mm->context); +#endif + save_and_cli(flags); + get_new_mmu_context(mm, asid_cache); if(mm == current->mm) - r2300_flush_tlb_all(); + set_entryhi(mm->context & 0xfc0); + restore_flags(flags); + } } static void r2300_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { + if(mm->context != 0) { + unsigned long flags; + int size; + +#ifdef DEBUG_TLB + printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xfc0), + start, end); +#endif + save_and_cli(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if(size <= NTLB_ENTRIES) { + int oldpid = (get_entryhi() & 0xfc0); + int newpid = (mm->context & 0xfc0); + + start &= PAGE_MASK; + end += (PAGE_SIZE - 1); + end &= PAGE_MASK; + while(start < end) { + int idx; + + set_entryhi(start | newpid); + start += PAGE_SIZE; + tlb_probe(); + idx = get_index(); + set_entrylo0(0); + set_entryhi(KSEG0); + if(idx < 0) + continue; + tlb_write_indexed(); + } + set_entryhi(oldpid); + } else { + get_new_mmu_context(mm, asid_cache); if(mm == current->mm) - r2300_flush_tlb_all(); + set_entryhi(mm->context & 0xfc0); + } + restore_flags(flags); + } } static void r2300_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { - if(vma->vm_mm == current->mm) - r2300_flush_tlb_all(); + if(vma->vm_mm->context != 0) { + unsigned long flags; + int oldpid, newpid, idx; + +#ifdef DEBUG_TLB + printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page); +#endif + newpid = (vma->vm_mm->context & 0xfc0); + page &= PAGE_MASK; + save_and_cli(flags); + oldpid = (get_entryhi() & 0xfc0); + set_entryhi(page | newpid); + tlb_probe(); + idx = get_index(); + set_entrylo0(0); + set_entryhi(KSEG0); + if(idx < 0) + goto finish; + tlb_write_indexed(); + +finish: + set_entryhi(oldpid); + restore_flags(flags); + } } /* Load a new root pointer into the TLB. */ static void r2300_load_pgd(unsigned long pg_dir) { - unsigned long flags; - - save_and_cli(flags); - write_32bit_cp0_register(CP0_ENTRYHI, TLB_ROOT); - write_32bit_cp0_register(CP0_INDEX, 0); - write_32bit_cp0_register(CP0_ENTRYLO0, ((pg_dir >> 6) | 0x00e0)); - __asm__ __volatile__("tlbwi"); - restore_flags(flags); } /* @@ -199,17 +577,63 @@ static void r2300_pgd_init(unsigned long page) "=r" (dummy2) :"r" ((unsigned long) invalid_pte_table), "0" (page), - "1" (USER_PTRS_PER_PGD/8)); + "1" (PAGE_SIZE/(sizeof(pmd_t)*8))); } static void r2300_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { - r2300_flush_tlb_page(vma, address); - /* - * FIXME: We should also reload a new entry into the TLB to - * avoid unnecessary exceptions. - */ + unsigned long flags; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + int idx, pid; + + pid = (get_entryhi() & 0xfc0); + +#ifdef DEBUG_TLB + if((pid != (vma->vm_mm->context & 0xfc0)) || (vma->vm_mm->context == 0)) { + printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n", + (int) (vma->vm_mm->context & 0xfc0), pid); + } +#endif + + save_and_cli(flags); + address &= PAGE_MASK; + set_entryhi(address | (pid)); + pgdp = pgd_offset(vma->vm_mm, address); + tlb_probe(); + pmdp = pmd_offset(pgdp, address); + idx = get_index(); + ptep = pte_offset(pmdp, address); + set_entrylo0(pte_val(*ptep)); + set_entryhi(address | (pid)); + if(idx < 0) { + tlb_write_random(); +#if 0 + printk("[MISS]"); +#endif + } else { + tlb_write_indexed(); +#if 0 + printk("[HIT]"); +#endif + } +#if 0 + if(!strcmp(current->comm, "args")) { + printk("<"); + for(idx = 0; idx < NTLB_ENTRIES; idx++) { + set_index(idx); + tlb_read(); + address = get_entryhi(); + if((address & 0xfc0) != 0) + printk("[%08lx]", address); + } + printk(">\n"); + } +#endif + set_entryhi(pid); + restore_flags(flags); } static void r2300_show_regs(struct pt_regs * regs) @@ -248,6 +672,7 @@ static void r2300_show_regs(struct pt_regs * regs) static void r2300_add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, unsigned long entryhi, unsigned long pagemask) { +printk("r2300_add_wired_entry"); /* * FIXME, to be done */ @@ -255,14 +680,19 @@ static void r2300_add_wired_entry(unsigned long entrylo0, unsigned long entrylo1 static int r2300_user_mode(struct pt_regs *regs) { - return !(regs->cp0_status & 0x4); + return !(regs->cp0_status & ST0_KUP); } __initfunc(void ld_mmu_r2300(void)) { + printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); + clear_page = r2300_clear_page; copy_page = r2300_copy_page; + probe_icache(); + probe_dcache(); + flush_cache_all = r2300_flush_cache_all; flush_cache_mm = r2300_flush_cache_mm; flush_cache_range = r2300_flush_cache_range; @@ -274,16 +704,19 @@ __initfunc(void ld_mmu_r2300(void)) flush_tlb_mm = r2300_flush_tlb_mm; flush_tlb_range = r2300_flush_tlb_range; flush_tlb_page = r2300_flush_tlb_page; - r3000_asid_setup(); + + dma_cache_wback_inv = r3k_dma_cache_wback_inv; load_pgd = r2300_load_pgd; pgd_init = r2300_pgd_init; update_mmu_cache = r2300_update_mmu_cache; + r3000_asid_setup(); show_regs = r2300_show_regs; add_wired_entry = r2300_add_wired_entry; user_mode = r2300_user_mode; + flush_tlb_all(); } |