From: James S. <jsi...@us...> - 2001-10-22 20:43:32
|
Update of /cvsroot/linux-mips/linux/arch/mips/mm In directory usw-pr-cvs1:/tmp/cvs-serv2286 Added Files: fault.c pg-r3k.c pg-r4k.S tlb-r3k.c tlb-r4k.c tlbex-r3k.S tlbex-r4k.S Log Message: New files for TLB handling. --- NEW FILE: pg-r3k.c --- /* * Copyright (C) 2001 Ralf Baechle (ra...@gn...) */ #include <asm/page.h> /* page functions */ void r3k_clear_page(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" ".set\tnoat\n\t" "addiu\t$1,%0,%2\n" "1:\tsw\t$0,(%0)\n\t" "sw\t$0,4(%0)\n\t" "sw\t$0,8(%0)\n\t" "sw\t$0,12(%0)\n\t" "addiu\t%0,32\n\t" "sw\t$0,-16(%0)\n\t" "sw\t$0,-12(%0)\n\t" "sw\t$0,-8(%0)\n\t" "bne\t$1,%0,1b\n\t" "sw\t$0,-4(%0)\n\t" ".set\tat\n\t" ".set\treorder" : "=r" (page) : "0" (page), "I" (PAGE_SIZE) : "memory"); } void r3k_copy_page(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; __asm__ __volatile__( ".set\tnoreorder\n\t" ".set\tnoat\n\t" "addiu\t$1,%0,%8\n" "1:\tlw\t%2,(%1)\n\t" "lw\t%3,4(%1)\n\t" "lw\t%4,8(%1)\n\t" "lw\t%5,12(%1)\n\t" "sw\t%2,(%0)\n\t" "sw\t%3,4(%0)\n\t" "sw\t%4,8(%0)\n\t" "sw\t%5,12(%0)\n\t" "lw\t%2,16(%1)\n\t" "lw\t%3,20(%1)\n\t" "lw\t%4,24(%1)\n\t" "lw\t%5,28(%1)\n\t" "sw\t%2,16(%0)\n\t" "sw\t%3,20(%0)\n\t" "sw\t%4,24(%0)\n\t" "sw\t%5,28(%0)\n\t" "addiu\t%0,64\n\t" "addiu\t%1,64\n\t" "lw\t%2,-32(%1)\n\t" "lw\t%3,-28(%1)\n\t" "lw\t%4,-24(%1)\n\t" "lw\t%5,-20(%1)\n\t" "sw\t%2,-32(%0)\n\t" "sw\t%3,-28(%0)\n\t" "sw\t%4,-24(%0)\n\t" "sw\t%5,-20(%0)\n\t" "lw\t%2,-16(%1)\n\t" "lw\t%3,-12(%1)\n\t" "lw\t%4,-8(%1)\n\t" "lw\t%5,-4(%1)\n\t" "sw\t%2,-16(%0)\n\t" "sw\t%3,-12(%0)\n\t" "sw\t%4,-8(%0)\n\t" "bne\t$1,%0,1b\n\t" "sw\t%5,-4(%0)\n\t" ".set\tat\n\t" ".set\treorder" : "=r" (dummy1), "=r" (dummy2), "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) : "0" (to), "1" (from), "I" (PAGE_SIZE)); } --- NEW FILE: pg-r4k.S --- /* * 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. * * r4xx0.c: R4000 processor variant specific MMU/Cache routines. * * Copyright (C) 1996 David S. Miller (dm...@en...) * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ra...@gn... */ #include <asm/addrspace.h> #include <asm/asm.h> #include <asm/regdef.h> #include <asm/cacheops.h> #include <asm/mipsregs.h> #define PAGE_SIZE 0x1000 .text .set mips3 .set noreorder .set nomacro .set noat /* * Zero an entire page. Basically a simple unrolled loop should do the * job but we want more performance by saving memory bus bandwidth. We * have five flavours of the routine available for: * * - 16byte cachelines and no second level cache * - 32byte cachelines second level cache * - a version which handles the buggy R4600 v1.x * - a version which handles the buggy R4600 v2.0 * - Finally a last version without fancy cache games for the SC and MC * versions of R4000 and R4400. */ LEAF(r4k_clear_page_d16) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_D, (a0) sd zero, (a0) sd zero, 8(a0) cache Create_Dirty_Excl_D, 16(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 cache Create_Dirty_Excl_D, -32(a0) sd zero, -32(a0) sd zero, -24(a0) cache Create_Dirty_Excl_D, -16(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_d16) LEAF(r4k_clear_page_d32) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_D, (a0) sd zero, (a0) sd zero, 8(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 cache Create_Dirty_Excl_D, -32(a0) sd zero, -32(a0) sd zero, -24(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_d32) /* * This flavour of r4k_clear_page is for the R4600 V1.x. Cite from the * IDT R4600 V1.7 errata: * * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D, * Hit_Invalidate_D and Create_Dirty_Excl_D should only be * executed if there is no other dcache activity. If the dcache is * accessed for another instruction immeidately preceding when these * cache instructions are executing, it is possible that the dcache * tag match outputs used by these cache instructions will be * incorrect. These cache instructions should be preceded by at least * four instructions that are not any kind of load or store * instruction. * * This is not allowed: lw * nop * nop * nop * cache Hit_Writeback_Invalidate_D * * This is allowed: lw * nop * nop * nop * nop * cache Hit_Writeback_Invalidate_D */ LEAF(r4k_clear_page_r4600_v1) addiu AT, a0, PAGE_SIZE 1: nop nop nop nop cache Create_Dirty_Excl_D, (a0) sd zero, (a0) sd zero, 8(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 nop nop nop cache Create_Dirty_Excl_D, -32(a0) sd zero, -32(a0) sd zero, -24(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_r4600_v1) LEAF(r4k_clear_page_r4600_v2) mfc0 a1, CP0_STATUS ori AT, a1, 1 xori AT, 1 mtc0 AT, CP0_STATUS nop nop nop .set volatile la AT, KSEG1 lw zero, (AT) .set novolatile addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_D, (a0) sd zero, (a0) sd zero, 8(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 cache Create_Dirty_Excl_D, -32(a0) sd zero, -32(a0) sd zero, -24(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) mfc0 AT, CP0_STATUS # __restore_flags andi a1, 1 ori AT, 1 xori AT, 1 or a1, AT mtc0 a1, CP0_STATUS nop nop nop jr ra END(r4k_clear_page_r4600_v2) /* * The next 4 versions are optimized for all possible scache configurations * of the SC / MC versions of R4000 and R4400 ... * * Todo: For even better performance we should have a routine optimized for * every legal combination of dcache / scache linesize. When I (Ralf) tried * this the kernel crashed shortly after mounting the root filesystem. CPU * bug? Weirdo cache instruction semantics? */ LEAF(r4k_clear_page_s16) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) sd zero, (a0) sd zero, 8(a0) cache Create_Dirty_Excl_SD, 16(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 cache Create_Dirty_Excl_SD, -32(a0) sd zero, -32(a0) sd zero, -24(a0) cache Create_Dirty_Excl_SD, -16(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_s16) LEAF(r4k_clear_page_s32) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) sd zero, (a0) sd zero, 8(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 cache Create_Dirty_Excl_SD, -32(a0) sd zero, -32(a0) sd zero, -24(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_s32) LEAF(r4k_clear_page_s64) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) sd zero, (a0) sd zero, 8(a0) sd zero, 16(a0) sd zero, 24(a0) addiu a0, 64 sd zero, -32(a0) sd zero, -24(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_s64) LEAF(r4k_clear_page_s128) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) sd zero, (a0) sd zero, 8(a0) sd zero, 16(a0) sd zero, 24(a0) sd zero, 32(a0) sd zero, 40(a0) sd zero, 48(a0) sd zero, 56(a0) addiu a0, 128 sd zero, -64(a0) sd zero, -56(a0) sd zero, -48(a0) sd zero, -40(a0) sd zero, -32(a0) sd zero, -24(a0) sd zero, -16(a0) bne AT, a0, 1b sd zero, -8(a0) jr ra END(r4k_clear_page_s128) /* * This is still inefficient. We only can do better if we know the * virtual address where the copy will be accessed. */ LEAF(r4k_copy_page_d16) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_D, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) cache Create_Dirty_Excl_D, 16(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) cache Create_Dirty_Excl_D, 32(a0) addiu a0, 64 addiu a1, 64 lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) cache Create_Dirty_Excl_D, -16(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_d16) LEAF(r4k_copy_page_d32) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_D, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) cache Create_Dirty_Excl_D, 32(a0) addiu a0, 64 addiu a1, 64 lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_d32) /* * Again a special version for the R4600 V1.x */ LEAF(r4k_copy_page_r4600_v1) addiu AT, a0, PAGE_SIZE 1: nop nop nop nop cache Create_Dirty_Excl_D, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) nop nop nop nop cache Create_Dirty_Excl_D, 32(a0) addiu a0, 64 addiu a1, 64 lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_r4600_v1) LEAF(r4k_copy_page_r4600_v2) mfc0 v1, CP0_STATUS ori AT, v1, 1 xori AT, 1 mtc0 AT, CP0_STATUS nop nop nop addiu AT, a0, PAGE_SIZE 1: nop nop nop nop cache Create_Dirty_Excl_D, (a0) lw t1, (a1) lw t0, 4(a1) lw a3, 8(a1) lw a2, 12(a1) sw t1, (a0) sw t0, 4(a0) sw a3, 8(a0) sw a2, 12(a0) lw t1, 16(a1) lw t0, 20(a1) lw a3, 24(a1) lw a2, 28(a1) sw t1, 16(a0) sw t0, 20(a0) sw a3, 24(a0) sw a2, 28(a0) nop nop nop nop cache Create_Dirty_Excl_D, 32(a0) addiu a0, 64 addiu a1, 64 lw t1, -32(a1) lw t0, -28(a1) lw a3, -24(a1) lw a2, -20(a1) sw t1, -32(a0) sw t0, -28(a0) sw a3, -24(a0) sw a2, -20(a0) lw t1, -16(a1) lw t0, -12(a1) lw a3, -8(a1) lw a2, -4(a1) sw t1, -16(a0) sw t0, -12(a0) sw a3, -8(a0) bne AT, a0, 1b sw a2, -4(a0) mfc0 AT, CP0_STATUS # __restore_flags andi v1, 1 ori AT, 1 xori AT, 1 or v1, AT mtc0 v1, CP0_STATUS nop nop nop jr ra END(r4k_copy_page_r4600_v2) /* * These are for R4000SC / R4400MC */ LEAF(r4k_copy_page_s16) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) cache Create_Dirty_Excl_SD, 16(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) cache Create_Dirty_Excl_SD, 32(a0) addiu a0, 64 addiu a1, 64 lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) cache Create_Dirty_Excl_SD, -16(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_s16) LEAF(r4k_copy_page_s32) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) cache Create_Dirty_Excl_SD, 32(a0) addiu a0, 64 addiu a1, 64 lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_s32) LEAF(r4k_copy_page_s64) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) addiu a0, 64 addiu a1, 64 lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_s64) LEAF(r4k_copy_page_s128) addiu AT, a0, PAGE_SIZE 1: cache Create_Dirty_Excl_SD, (a0) lw a3, (a1) lw a2, 4(a1) lw v1, 8(a1) lw v0, 12(a1) sw a3, (a0) sw a2, 4(a0) sw v1, 8(a0) sw v0, 12(a0) lw a3, 16(a1) lw a2, 20(a1) lw v1, 24(a1) lw v0, 28(a1) sw a3, 16(a0) sw a2, 20(a0) sw v1, 24(a0) sw v0, 28(a0) lw a3, 32(a1) lw a2, 36(a1) lw v1, 40(a1) lw v0, 44(a1) sw a3, 32(a0) sw a2, 36(a0) sw v1, 40(a0) sw v0, 44(a0) lw a3, 48(a1) lw a2, 52(a1) lw v1, 56(a1) lw v0, 60(a1) sw a3, 48(a0) sw a2, 52(a0) sw v1, 56(a0) sw v0, 60(a0) addiu a0, 128 addiu a1, 128 lw a3, -64(a1) lw a2, -60(a1) lw v1, -56(a1) lw v0, -52(a1) sw a3, -64(a0) sw a2, -60(a0) sw v1, -56(a0) sw v0, -52(a0) lw a3, -48(a1) lw a2, -44(a1) lw v1, -40(a1) lw v0, -36(a1) sw a3, -48(a0) sw a2, -44(a0) sw v1, -40(a0) sw v0, -36(a0) lw a3, -32(a1) lw a2, -28(a1) lw v1, -24(a1) lw v0, -20(a1) sw a3, -32(a0) sw a2, -28(a0) sw v1, -24(a0) sw v0, -20(a0) lw a3, -16(a1) lw a2, -12(a1) lw v1, -8(a1) lw v0, -4(a1) sw a3, -16(a0) sw a2, -12(a0) sw v1, -8(a0) bne AT, a0, 1b sw v0, -4(a0) jr ra END(r4k_copy_page_s128) --- NEW FILE: tlb-r3k.c --- /* * r2300.c: R2000 and R3000 specific mmu/cache code. * * Copyright (C) 1996 David S. Miller (dm...@en...) * * with a lot of changes to make this thing work for R3000s * Tx39XX R4k style caches added. HK * Copyright (C) 1998, 1999, 2000 Harald Koerfgen * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> #include <asm/system.h> #include <asm/isadep.h> #include <asm/io.h> #include <asm/wbflush.h> #include <asm/bootinfo.h> #include <asm/cpu.h> #undef DEBUG_TLB /* TLB operations. */ void local_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 = 8; entry < mips_cpu.tlbsize; 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); } void local_flush_tlb_mm(struct mm_struct *mm) { if (mm->context != 0) { unsigned long flags; #ifdef DEBUG_TLB printk("[tlbmm<%lu>]", (unsigned long) mm->context); #endif save_and_cli(flags); get_new_cpu_mmu_context(mm, smp_processor_id()); if (mm == current->active_mm) set_entryhi(mm->context & 0xfc0); restore_flags(flags); } } void local_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<%lu,0x%08lx,0x%08lx>]", (mm->context & 0xfc0), start, end); #endif save_and_cli(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if (size <= mips_cpu.tlbsize) { 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_cpu_mmu_context(mm, smp_processor_id()); if (mm == current->active_mm) set_entryhi(mm->context & 0xfc0); } restore_flags(flags); } } void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { if (vma->vm_mm->context != 0) { unsigned long flags; int oldpid, newpid, idx; #ifdef DEBUG_TLB printk("[tlbpage<%lu,0x%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); } } void update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { unsigned long flags; pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; int idx, pid; /* * Handle debugger faulting in for debugee. */ if (current->active_mm != vma->vm_mm) return; 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=%lu tlbpid=%d\n", (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 } set_entryhi(pid); restore_flags(flags); } void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, unsigned long entryhi, unsigned long pagemask) { unsigned long flags; unsigned long old_ctx; static unsigned long wired = 0; if (wired < 8) { __save_and_cli(flags); old_ctx = get_entryhi() & 0xfc0; set_entrylo0(entrylo0); set_entryhi(entryhi); set_index(wired); wired++; tlb_write_indexed(); set_entryhi(old_ctx); local_flush_tlb_all(); __restore_flags(flags); } } --- NEW FILE: tlb-r4k.c --- /* * 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. * * r4xx0.c: R4000 processor variant specific MMU/Cache routines. * * Copyright (C) 1996 David S. Miller (dm...@en...) * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ra...@gn... * * To do: * * - this code is a overbloated pig * - many of the bug workarounds are not efficient at all, but at * least they are functional ... */ #include <linux/init.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/cpu.h> #include <asm/bootinfo.h> #include <asm/mmu_context.h> #include <asm/pgtable.h> #include <asm/system.h> #undef DEBUG_TLB #undef DEBUG_TLBUPDATE extern char except_vec0_nevada, except_vec0_r4000, except_vec0_r4600; /* CP0 hazard avoidance. */ #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ "nop; nop; nop; nop; nop; nop;\n\t" \ ".set reorder\n\t") void local_flush_tlb_all(void) { unsigned long flags; unsigned long old_ctx; int entry; #ifdef DEBUG_TLB printk("[tlball]"); #endif __save_and_cli(flags); /* Save old context and create impossible VPN2 value */ old_ctx = (get_entryhi() & 0xff); set_entryhi(KSEG0); set_entrylo0(0); set_entrylo1(0); BARRIER; entry = get_wired(); /* Blast 'em all away. */ while (entry < mips_cpu.tlbsize) { set_index(entry); BARRIER; tlb_write_indexed(); BARRIER; entry++; } BARRIER; set_entryhi(old_ctx); __restore_flags(flags); } void local_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_cpu_mmu_context(mm, smp_processor_id()); if (mm == current->active_mm) set_entryhi(mm->context & 0xff); __restore_flags(flags); } } void local_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 & 0xff), start, end); #endif __save_and_cli(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (size + 1) >> 1; if (size <= mips_cpu.tlbsize/2) { int oldpid = (get_entryhi() & 0xff); int newpid = (mm->context & 0xff); start &= (PAGE_MASK << 1); end += ((PAGE_SIZE << 1) - 1); end &= (PAGE_MASK << 1); while (start < end) { int idx; set_entryhi(start | newpid); start += (PAGE_SIZE << 1); BARRIER; tlb_probe(); BARRIER; idx = get_index(); set_entrylo0(0); set_entrylo1(0); set_entryhi(KSEG0); BARRIER; if(idx < 0) continue; tlb_write_indexed(); BARRIER; } set_entryhi(oldpid); } else { get_new_cpu_mmu_context(mm, smp_processor_id()); if (mm == current->active_mm) set_entryhi(mm->context & 0xff); } __restore_flags(flags); } } void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { 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 & 0xff); page &= (PAGE_MASK << 1); __save_and_cli(flags); oldpid = (get_entryhi() & 0xff); set_entryhi(page | newpid); BARRIER; tlb_probe(); BARRIER; idx = get_index(); set_entrylo0(0); set_entrylo1(0); set_entryhi(KSEG0); if(idx < 0) goto finish; BARRIER; tlb_write_indexed(); finish: BARRIER; set_entryhi(oldpid); __restore_flags(flags); } } /* We will need multiple versions of update_mmu_cache(), one that just * updates the TLB with the new pte(s), and another which also checks * for the R4k "end of page" hardware bug and does the needy. */ void update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { unsigned long flags; pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; int idx, pid; /* * Handle debugger faulting in for debugee. */ if (current->active_mm != vma->vm_mm) return; pid = get_entryhi() & 0xff; #ifdef DEBUG_TLB if((pid != (vma->vm_mm->context & 0xff)) || (vma->vm_mm->context == 0)) { printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n", (int) (vma->vm_mm->context & 0xff), pid); } #endif __save_and_cli(flags); address &= (PAGE_MASK << 1); set_entryhi(address | (pid)); pgdp = pgd_offset(vma->vm_mm, address); BARRIER; tlb_probe(); BARRIER; pmdp = pmd_offset(pgdp, address); idx = get_index(); ptep = pte_offset(pmdp, address); BARRIER; set_entrylo0(pte_val(*ptep++) >> 6); set_entrylo1(pte_val(*ptep) >> 6); set_entryhi(address | (pid)); BARRIER; if (idx < 0) { tlb_write_random(); } else { tlb_write_indexed(); } BARRIER; set_entryhi(pid); BARRIER; __restore_flags(flags); } #if 0 static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, unsigned long address, pte_t pte) { unsigned long flags; pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; int idx; __save_and_cli(flags); address &= (PAGE_MASK << 1); set_entryhi(address | (get_entryhi() & 0xff)); 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++) >> 6); set_entrylo1(pte_val(*ptep) >> 6); BARRIER; if (idx < 0) tlb_write_random(); else tlb_write_indexed(); BARRIER; __restore_flags(flags); } #endif void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, unsigned long entryhi, unsigned long pagemask) { unsigned long flags; unsigned long wired; unsigned long old_pagemask; unsigned long old_ctx; __save_and_cli(flags); /* Save old context and create impossible VPN2 value */ old_ctx = get_entryhi() & 0xff; old_pagemask = get_pagemask(); wired = get_wired(); set_wired(wired + 1); set_index(wired); BARRIER; set_pagemask(pagemask); set_entryhi(entryhi); set_entrylo0(entrylo0); set_entrylo1(entrylo1); BARRIER; tlb_write_indexed(); BARRIER; set_entryhi(old_ctx); BARRIER; set_pagemask(old_pagemask); local_flush_tlb_all(); __restore_flags(flags); } /* * Used for loading TLB entries before trap_init() has started, when we * don't actually want to add a wired entry which remains throughout the * lifetime of the system */ static int temp_tlb_entry __initdata; __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, unsigned long entryhi, unsigned long pagemask) { int ret = 0; unsigned long flags; unsigned long wired; unsigned long old_pagemask; unsigned long old_ctx; __save_and_cli(flags); /* Save old context and create impossible VPN2 value */ old_ctx = get_entryhi() & 0xff; old_pagemask = get_pagemask(); wired = get_wired(); if (--temp_tlb_entry < wired) { printk(KERN_WARNING "No TLB space left for add_temporary_entry\n"); ret = -ENOSPC; goto out; } set_index(temp_tlb_entry); BARRIER; set_pagemask(pagemask); set_entryhi(entryhi); set_entrylo0(entrylo0); set_entrylo1(entrylo1); BARRIER; tlb_write_indexed(); BARRIER; set_entryhi(old_ctx); BARRIER; set_pagemask(old_pagemask); out: __restore_flags(flags); return ret; } void __init r4k_tlb_init(void) { /* * You should never change this register: * - On R4600 1.7 the tlbp never hits for pages smaller than * the value in the c0_pagemask register. * - The entire mm handling assumes the c0_pagemask register to * be set for 4kb pages. */ set_pagemask(PM_4K); write_32bit_cp0_register(CP0_WIRED, 0); temp_tlb_entry = mips_cpu.tlbsize - 1; printk("TLB has %d entries.\n", mips_cpu.tlbsize); local_flush_tlb_all(); if ((mips_cpu.options & MIPS_CPU_4KEX) && (mips_cpu.options & MIPS_CPU_4KTLB)) { if (mips_cpu.cputype == CPU_NEVADA) memcpy((void *)KSEG0, &except_vec0_nevada, 0x80); else if (mips_cpu.cputype == CPU_R4600) memcpy((void *)KSEG0, &except_vec0_r4600, 0x80); else memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); flush_icache_range(KSEG0, KSEG0 + 0x80); } } --- NEW FILE: tlbex-r3k.S --- /* * TLB exception handling code for R2000/R3000. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse * * Multi-CPU abstraction reworking: * Copyright (C) 1996 David S. Miller (dm...@en...) * * Further modifications to make this work: * Copyright (c) 1998 Harald Koerfgen * Copyright (c) 1998, 1999 Gleb Raiko & Vladimir Roganov * Copyright (c) 2001 Ralf Baechle * Copyright (c) 2001 MIPS Technologies, Inc. */ #include <linux/init.h> #include <asm/asm.h> #include <asm/current.h> #include <asm/bootinfo.h> #include <asm/cachectl.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> #include <asm/regdef.h> #include <asm/segment.h> #include <asm/stackframe.h> #define TLB_OPTIMIZE /* If you are paranoid, disable this. */ .text .set mips1 .set noreorder __INIT /* TLB refill, R[23]00 version */ LEAF(except_vec0_r2300) .set noat .set mips1 mfc0 k0, CP0_BADVADDR lw k1, pgd_current # get pgd pointer srl k0, k0, 22 sll k0, k0, 2 addu k1, k1, k0 mfc0 k0, CP0_CONTEXT lw k1, (k1) and k0, k0, 0xffc addu k1, k1, k0 lw k0, (k1) nop mtc0 k0, CP0_ENTRYLO0 mfc0 k1, CP0_EPC tlbwr jr k1 rfe END(except_vec0_r2300) __FINIT /* 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; \ lw ptr, pgd_current; \ srl pte, pte, 22; \ sll pte, pte, 2; \ addu ptr, ptr, pte; \ 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_exception; \ 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); /* * The index register may have the probe fail bit set, * because we would trap on access kseg2, i.e. without refill. */ #define TLB_WRITE(reg) \ mfc0 reg, CP0_INDEX; \ nop; \ bltz reg, 1f; \ nop; \ tlbwi; \ j 2f; \ nop; \ 1: tlbwr; \ 2: #define RET(reg) \ mfc0 reg, CP0_EPC; \ nop; \ jr reg; \ rfe .set noreorder .align 5 NESTED(handle_tlbl, PT_SIZE, sp) .set noat #ifdef TLB_OPTIMIZE /* Test present bit in entry. */ LOAD_PTE(k0, k1) tlbp PTE_PRESENT(k0, k1, nopage_tlbl) PTE_MAKEVALID(k0, k1) PTE_RELOAD(k1) TLB_WRITE(k0) RET(k0) nopage_tlbl: #endif DO_FAULT(0) END(handle_tlbl) NESTED(handle_tlbs, PT_SIZE, sp) .set noat #ifdef TLB_OPTIMIZE LOAD_PTE(k0, k1) tlbp # find faulting entry PTE_WRITABLE(k0, k1, nopage_tlbs) PTE_MAKEWRITE(k0, k1) PTE_RELOAD(k1) TLB_WRITE(k0) RET(k0) nopage_tlbs: #endif DO_FAULT(1) END(handle_tlbs) .align 5 NESTED(handle_mod, PT_SIZE, sp) .set noat #ifdef TLB_OPTIMIZE LOAD_PTE(k0, k1) tlbp # find faulting entry 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) tlbwi RET(k0) #endif nowrite_mod: DO_FAULT(1) END(handle_mod) --- NEW FILE: tlbex-r4k.S --- /* * TLB exception handling code for r4k. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse * * Multi-cpu abstraction and reworking: * Copyright (C) 1996 David S. Miller (dm...@en...) * * Carsten Langgaard, car...@mi... * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. */ #include <linux/init.h> #include <asm/asm.h> #include <asm/current.h> #include <asm/offset.h> #include <asm/bootinfo.h> #include <asm/cachectl.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> #include <asm/regdef.h> #include <asm/stackframe.h> #define TLB_OPTIMIZE /* If you are paranoid, disable this. */ __INIT /* * These handlers much be written in a relocatable manner * because based upon the cpu type an arbitrary one of the * following pieces of code will be copied to the KSEG0 * vector location. */ /* TLB refill, EXL == 0, R4xx0, non-R4600 version */ .set noreorder .set noat LEAF(except_vec0_r4000) .set mips3 #ifdef CONFIG_SMP mfc0 k1, CP0_CONTEXT la k0, pgd_current srl k1, 23 sll k1, 2 addu k1, k0, k1 lw k1, (k1) #else lw k1, pgd_current # get pgd pointer #endif mfc0 k0, CP0_BADVADDR # Get faulting address srl k0, k0, 22 # get pgd only bits sll k0, k0, 2 addu k1, k1, k0 # add in pgd offset mfc0 k0, CP0_CONTEXT # get context reg lw k1, (k1) #if defined(CONFIG_CPU_VR41XX) srl k0, k0, 3 # get pte offset #else srl k0, k0, 1 # get pte offset #endif and k0, k0, 0xff8 addu k1, k1, k0 # add in offset lw k0, 0(k1) # get even pte lw k1, 4(k1) # get odd pte srl k0, k0, 6 # convert to entrylo0 mtc0 k0, CP0_ENTRYLO0 # load it srl k1, k1, 6 # convert to entrylo1 mtc0 k1, CP0_ENTRYLO1 # load it b 1f tlbwr # write random tlb entry 1: nop eret # return from trap END(except_vec0_r4000) /* TLB refill, EXL == 0, R4600 version */ LEAF(except_vec0_r4600) .set mips3 mfc0 k0, CP0_BADVADDR srl k0, k0, 22 lw k1, pgd_current # get pgd pointer sll k0, k0, 2 addu k1, k1, k0 mfc0 k0, CP0_CONTEXT lw k1, (k1) srl k0, k0, 1 and k0, k0, 0xff8 addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) srl k0, k0, 6 mtc0 k0, CP0_ENTRYLO0 srl k1, k1, 6 mtc0 k1, CP0_ENTRYLO1 nop tlbwr nop eret END(except_vec0_r4600) /* TLB refill, EXL == 0, R52x0 "Nevada" version */ /* * This version has a bug workaround for the Nevada. It seems * as if under certain circumstances the move from cp0_context * might produce a bogus result when the mfc0 instruction and * it's consumer are in a different cacheline or a load instruction, * probably any memory reference, is between them. This is * potencially slower than the R4000 version, so we use this * special version. */ .set noreorder .set noat LEAF(except_vec0_nevada) .set mips3 mfc0 k0, CP0_BADVADDR # Get faulting address srl k0, k0, 22 # get pgd only bits lw k1, pgd_current # get pgd pointer sll k0, k0, 2 addu k1, k1, k0 # add in pgd offset lw k1, (k1) mfc0 k0, CP0_CONTEXT # get context reg srl k0, k0, 1 # get pte offset and k0, k0, 0xff8 addu k1, k1, k0 # add in offset lw k0, 0(k1) # get even pte lw k1, 4(k1) # get odd pte srl k0, k0, 6 # convert to entrylo0 mtc0 k0, CP0_ENTRYLO0 # load it srl k1, k1, 6 # convert to entrylo1 mtc0 k1, CP0_ENTRYLO1 # load it nop # QED specified nops nop tlbwr # write random tlb entry nop # traditional nop eret # return from trap END(except_vec0_nevada) /* TLB refill, EXL == 0, R4[40]00/R5000 badvaddr hwbug version */ LEAF(except_vec0_r45k_bvahwbug) .set mips3 mfc0 k0, CP0_BADVADDR srl k0, k0, 22 lw k1, pgd_current # get pgd pointer sll k0, k0, 2 addu k1, k1, k0 mfc0 k0, CP0_CONTEXT lw k1, (k1) srl k0, k0, 1 and k0, k0, 0xff8 addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) nop /* XXX */ tlbp srl k0, k0, 6 mtc0 k0, CP0_ENTRYLO0 srl k1, k1, 6 mfc0 k0, CP0_INDEX mtc0 k1, CP0_ENTRYLO1 bltzl k0, 1f tlbwr 1: nop eret END(except_vec0_r45k_bvahwbug) #ifdef CONFIG_SMP /* TLB refill, EXL == 0, R4000 MP badvaddr hwbug version */ LEAF(except_vec0_r4k_mphwbug) .set mips3 mfc0 k0, CP0_BADVADDR srl k0, k0, 22 lw k1, pgd_current # get pgd pointer sll k0, k0, 2 addu k1, k1, k0 mfc0 k0, CP0_CONTEXT lw k1, (k1) srl k0, k0, 1 and k0, k0, 0xff8 addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) nop /* XXX */ tlbp srl k0, k0, 6 mtc0 k0, CP0_ENTRYLO0 srl k1, k1, 6 mfc0 k0, CP0_INDEX mtc0 k1, CP0_ENTRYLO1 bltzl k0, 1f tlbwr 1: nop eret END(except_vec0_r4k_mphwbug) #endif /* TLB refill, EXL == 0, R4000 UP 250MHZ entrylo[01] hwbug version */ LEAF(except_vec0_r4k_250MHZhwbug) .set mips3 mfc0 k0, CP0_BADVADDR srl k0, k0, 22 lw k1, pgd_current # get pgd pointer sll k0, k0, 2 addu k1, k1, k0 mfc0 k0, CP0_CONTEXT lw k1, (k1) srl k0, k0, 1 and k0, k0, 0xff8 addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) srl k0, k0, 6 mtc0 zero, CP0_ENTRYLO0 mtc0 k0, CP0_ENTRYLO0 srl k1, k1, 6 mtc0 zero, CP0_ENTRYLO1 mtc0 k1, CP0_ENTRYLO1 b 1f tlbwr 1: nop eret END(except_vec0_r4k_250MHZhwbug) #ifdef CONFIG_SMP /* TLB refill, EXL == 0, R4000 MP 250MHZ entrylo[01]+badvaddr bug version */ LEAF(except_vec0_r4k_MP250MHZhwbug) .set mips3 mfc0 k0, CP0_BADVADDR srl k0, k0, 22 lw k1, pgd_current # get pgd pointer sll k0, k0, 2 addu k1, k1, k0 mfc0 k0, CP0_CONTEXT lw k1, (k1) srl k0, k0, 1 and k0, k0, 0xff8 addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) nop /* XXX */ tlbp srl k0, k0, 6 mtc0 zero, CP0_ENTRYLO0 mtc0 k0, CP0_ENTRYLO0 mfc0 k0, CP0_INDEX srl k1, k1, 6 mtc0 zero, CP0_ENTRYLO1 mtc0 k1, CP0_ENTRYLO1 bltzl k0, 1f tlbwr 1: nop eret END(except_vec0_r4k_MP250MHZhwbug) #endif __FINIT /* * 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. */ #ifdef CONFIG_SMP #define GET_PGD(scratch, ptr) \ mfc0 ptr, CP0_CONTEXT; \ la scratch, pgd_current;\ srl ptr, 23; \ sll ptr, 2; \ addu ptr, scratch, ptr; \ lw ptr, (ptr); #else #define GET_PGD(scratch, ptr) \ lw ptr, pgd_current; #endif #define LOAD_PTE(pte, ptr) \ GET_PGD(pte, ptr) \ mfc0 pte, CP0_BADVADDR; \ srl pte, pte, 22; \ sll pte, pte, 2; \ addu ptr, ptr, pte; \ mfc0 pte, CP0_BADVADDR; \ lw ptr, (ptr); \ srl pte, pte, 10; \ and pte, pte, 0xffc; \ addu ptr, ptr, pte; \ lw pte, (ptr); /* 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, tmp) \ ori ptr, ptr, 0x4; \ xori ptr, ptr, 0x4; \ lw tmp, 4(ptr); \ lw ptr, 0(ptr); \ srl tmp, tmp, 6; \ mtc0 tmp, CP0_ENTRYLO1; \ srl ptr, ptr, 6; \ mtc0 ptr, CP0_ENTRYLO0; #define DO_FAULT(write) \ .set noat; \ SAVE_ALL; \ mfc0 a2, CP0_BADVADDR; \ STI; \ .set at; \ move a0, sp; \ jal do_page_fault; \ li a1, write; \ j ret_from_exception; \ nop; \ .set noat; /* 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; \ lw pte, (ptr); /* 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; \ lw pte, (ptr); /* 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); .set noreorder /* * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: * 2. A timing hazard exists for the TLBP instruction. * * stalling_instruction * TLBP * * The JTLB is being read for the TLBP throughout the stall generated by the * previous instruction. This is not really correct as the stalling instruction * can modify the address used to access the JTLB. The failure symptom is that * the TLBP instruction will use an address created for the stalling instruction * and not the address held in C0_ENHI and thus report the wrong results. * * The software work-around is to not allow the instruction preceding the TLBP * to stall - make it an NOP or some other instruction guaranteed not to stall. * * Errata 2 will not be fixed. This errata is also on the R5000. * * As if we MIPS hackers wouldn't know how to nop pipelines happy ... */ #define R5K_HAZARD nop /* * Note for many R4k variants tlb probes cannot be executed out * of the instruction cache else you get bogus results. */ .align 5 NESTED(handle_tlbl, PT_SIZE, sp) .set noat invalid_tlbl: #ifdef TLB_OPTIMIZE /* Test present bit in entry. */ LOAD_PTE(k0, k1) R5K_HAZARD tlbp PTE_PRESENT(k0, k1, nopage_tlbl) PTE_MAKEVALID(k0, k1) PTE_RELOAD(k1, k0) nop b 1f tlbwi 1: nop .set mips3 eret .set mips0 #endif nopage_tlbl: DO_FAULT(0) END(handle_tlbl) .align 5 NESTED(handle_tlbs, PT_SIZE, sp) .set noat #ifdef TLB_OPTIMIZE LOAD_PTE(k0, k1) R5K_HAZARD tlbp # find faulting entry PTE_WRITABLE(k0, k1, nopage_tlbs) PTE_MAKEWRITE(k0, k1) PTE_RELOAD(k1, k0) nop b 1f tlbwi 1: nop .set mips3 eret .set mips0 #endif nopage_tlbs: DO_FAULT(1) END(handle_tlbs) .align 5 NESTED(handle_mod, PT_SIZE, sp) .set noat #ifdef TLB_OPTIMIZE LOAD_PTE(k0, k1) R5K_HAZARD tlbp # find faulting entry andi k0, k0, _PAGE_WRITE beqz k0, nowrite_mod lw k0, (k1) /* Present and writable bits set, set accessed and dirty bits. */ PTE_MAKEWRITE(k0, k1) #if 0 ori k0, k0, (_PAGE_ACCESSED | _PAGE_DIRTY) sw k0, (k1) #endif /* Now reload the entry into the tlb. */ PTE_RELOAD(k1, k0) nop b 1f tlbwi 1: nop .set mips3 eret .set mips0 #endif nowrite_mod: DO_FAULT(1) END(handle_mod) |