1209243Sjchandra/*- 2209243Sjchandra * Copyright (c) 2004-2010 Juli Mallett <jmallett@FreeBSD.org> 3209243Sjchandra * All rights reserved. 4209243Sjchandra * 5209243Sjchandra * Redistribution and use in source and binary forms, with or without 6209243Sjchandra * modification, are permitted provided that the following conditions 7209243Sjchandra * are met: 8209243Sjchandra * 1. Redistributions of source code must retain the above copyright 9209243Sjchandra * notice, this list of conditions and the following disclaimer. 10209243Sjchandra * 2. Redistributions in binary form must reproduce the above copyright 11209243Sjchandra * notice, this list of conditions and the following disclaimer in the 12209243Sjchandra * documentation and/or other materials provided with the distribution. 13209243Sjchandra * 14209243Sjchandra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15209243Sjchandra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16209243Sjchandra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17209243Sjchandra * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18209243Sjchandra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19209243Sjchandra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20209243Sjchandra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21209243Sjchandra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22209243Sjchandra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23209243Sjchandra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24209243Sjchandra * SUCH DAMAGE. 25209243Sjchandra * 26209243Sjchandra * $FreeBSD$ 27209243Sjchandra */ 28209243Sjchandra 29209243Sjchandra#include "opt_ddb.h" 30209243Sjchandra 31209243Sjchandra#include <sys/param.h> 32209243Sjchandra#include <sys/kernel.h> 33209243Sjchandra#include <sys/systm.h> 34209243Sjchandra#include <sys/pcpu.h> 35209243Sjchandra#include <sys/smp.h> 36209243Sjchandra 37209243Sjchandra#include <vm/vm.h> 38209243Sjchandra#include <vm/vm_page.h> 39209243Sjchandra 40209243Sjchandra#include <machine/pte.h> 41209243Sjchandra#include <machine/tlb.h> 42209243Sjchandra 43209243Sjchandrastruct tlb_state { 44209243Sjchandra unsigned wired; 45209243Sjchandra struct tlb_entry { 46209243Sjchandra register_t entryhi; 47209243Sjchandra register_t entrylo0; 48209243Sjchandra register_t entrylo1; 49209243Sjchandra } entry[MIPS_MAX_TLB_ENTRIES]; 50209243Sjchandra}; 51209243Sjchandra 52209243Sjchandrastatic struct tlb_state tlb_state[MAXCPU]; 53209243Sjchandra 54209243Sjchandra#if 0 55209243Sjchandra/* 56209243Sjchandra * PageMask must increment in steps of 2 bits. 57209243Sjchandra */ 58209243SjchandraCOMPILE_TIME_ASSERT(POPCNT(TLBMASK_MASK) % 2 == 0); 59209243Sjchandra#endif 60209243Sjchandra 61209243Sjchandrastatic inline void 62209243Sjchandratlb_probe(void) 63209243Sjchandra{ 64209243Sjchandra __asm __volatile ("tlbp" : : : "memory"); 65209243Sjchandra mips_cp0_sync(); 66209243Sjchandra} 67209243Sjchandra 68209243Sjchandrastatic inline void 69209243Sjchandratlb_read(void) 70209243Sjchandra{ 71209243Sjchandra __asm __volatile ("tlbr" : : : "memory"); 72209243Sjchandra mips_cp0_sync(); 73209243Sjchandra} 74209243Sjchandra 75209243Sjchandrastatic inline void 76209243Sjchandratlb_write_indexed(void) 77209243Sjchandra{ 78209243Sjchandra __asm __volatile ("tlbwi" : : : "memory"); 79209243Sjchandra mips_cp0_sync(); 80209243Sjchandra} 81209243Sjchandra 82209243Sjchandrastatic inline void 83209243Sjchandratlb_write_random(void) 84209243Sjchandra{ 85209243Sjchandra __asm __volatile ("tlbwr" : : : "memory"); 86209243Sjchandra mips_cp0_sync(); 87209243Sjchandra} 88209243Sjchandra 89209243Sjchandrastatic void tlb_invalidate_one(unsigned); 90209243Sjchandra 91209243Sjchandravoid 92209243Sjchandratlb_insert_wired(unsigned i, vm_offset_t va, pt_entry_t pte0, pt_entry_t pte1) 93209243Sjchandra{ 94209645Sjchandra register_t asid; 95209243Sjchandra register_t s; 96209243Sjchandra 97209243Sjchandra va &= ~PAGE_MASK; 98209243Sjchandra 99209243Sjchandra s = intr_disable(); 100209243Sjchandra asid = mips_rd_entryhi() & TLBHI_ASID_MASK; 101209243Sjchandra 102209243Sjchandra mips_wr_index(i); 103209243Sjchandra mips_wr_pagemask(0); 104209243Sjchandra mips_wr_entryhi(TLBHI_ENTRY(va, 0)); 105209243Sjchandra mips_wr_entrylo0(pte0); 106209243Sjchandra mips_wr_entrylo1(pte1); 107209243Sjchandra tlb_write_indexed(); 108209243Sjchandra 109209243Sjchandra mips_wr_entryhi(asid); 110209243Sjchandra intr_restore(s); 111209243Sjchandra} 112209243Sjchandra 113209243Sjchandravoid 114209243Sjchandratlb_invalidate_address(struct pmap *pmap, vm_offset_t va) 115209243Sjchandra{ 116209645Sjchandra register_t asid; 117209243Sjchandra register_t s; 118209243Sjchandra int i; 119209243Sjchandra 120209243Sjchandra va &= ~PAGE_MASK; 121209243Sjchandra 122209243Sjchandra s = intr_disable(); 123209243Sjchandra asid = mips_rd_entryhi() & TLBHI_ASID_MASK; 124209243Sjchandra 125209243Sjchandra mips_wr_pagemask(0); 126209243Sjchandra mips_wr_entryhi(TLBHI_ENTRY(va, pmap_asid(pmap))); 127209243Sjchandra tlb_probe(); 128209243Sjchandra i = mips_rd_index(); 129209243Sjchandra if (i >= 0) 130209243Sjchandra tlb_invalidate_one(i); 131209243Sjchandra 132209243Sjchandra mips_wr_entryhi(asid); 133209243Sjchandra intr_restore(s); 134209243Sjchandra} 135209243Sjchandra 136209243Sjchandravoid 137209243Sjchandratlb_invalidate_all(void) 138209243Sjchandra{ 139209645Sjchandra register_t asid; 140209243Sjchandra register_t s; 141209243Sjchandra unsigned i; 142209243Sjchandra 143209243Sjchandra s = intr_disable(); 144209243Sjchandra asid = mips_rd_entryhi() & TLBHI_ASID_MASK; 145209243Sjchandra 146209243Sjchandra for (i = mips_rd_wired(); i < num_tlbentries; i++) 147209243Sjchandra tlb_invalidate_one(i); 148209243Sjchandra 149209243Sjchandra mips_wr_entryhi(asid); 150209243Sjchandra intr_restore(s); 151209243Sjchandra} 152209243Sjchandra 153209243Sjchandravoid 154209243Sjchandratlb_invalidate_all_user(struct pmap *pmap) 155209243Sjchandra{ 156209645Sjchandra register_t asid; 157209243Sjchandra register_t s; 158209243Sjchandra unsigned i; 159209243Sjchandra 160209243Sjchandra s = intr_disable(); 161209243Sjchandra asid = mips_rd_entryhi() & TLBHI_ASID_MASK; 162209243Sjchandra 163209243Sjchandra for (i = mips_rd_wired(); i < num_tlbentries; i++) { 164209243Sjchandra register_t uasid; 165209243Sjchandra 166209243Sjchandra mips_wr_index(i); 167209243Sjchandra tlb_read(); 168209243Sjchandra 169209243Sjchandra uasid = mips_rd_entryhi() & TLBHI_ASID_MASK; 170209243Sjchandra if (pmap == NULL) { 171209243Sjchandra /* 172209243Sjchandra * Invalidate all non-kernel entries. 173209243Sjchandra */ 174209243Sjchandra if (uasid == 0) 175209243Sjchandra continue; 176209243Sjchandra } else { 177209243Sjchandra /* 178209243Sjchandra * Invalidate this pmap's entries. 179209243Sjchandra */ 180209243Sjchandra if (uasid != pmap_asid(pmap)) 181209243Sjchandra continue; 182209243Sjchandra } 183209243Sjchandra tlb_invalidate_one(i); 184209243Sjchandra } 185209243Sjchandra 186209243Sjchandra mips_wr_entryhi(asid); 187209243Sjchandra intr_restore(s); 188209243Sjchandra} 189209243Sjchandra 190209243Sjchandra/* XXX Only if DDB? */ 191209243Sjchandravoid 192209243Sjchandratlb_save(void) 193209243Sjchandra{ 194209243Sjchandra unsigned i, cpu; 195209243Sjchandra 196209243Sjchandra cpu = PCPU_GET(cpuid); 197209243Sjchandra 198209243Sjchandra tlb_state[cpu].wired = mips_rd_wired(); 199209243Sjchandra for (i = 0; i < num_tlbentries; i++) { 200209243Sjchandra mips_wr_index(i); 201209243Sjchandra tlb_read(); 202209243Sjchandra 203209243Sjchandra tlb_state[cpu].entry[i].entryhi = mips_rd_entryhi(); 204209243Sjchandra tlb_state[cpu].entry[i].entrylo0 = mips_rd_entrylo0(); 205209243Sjchandra tlb_state[cpu].entry[i].entrylo1 = mips_rd_entrylo1(); 206209243Sjchandra } 207209243Sjchandra} 208209243Sjchandra 209209243Sjchandravoid 210209243Sjchandratlb_update(struct pmap *pmap, vm_offset_t va, pt_entry_t pte) 211209243Sjchandra{ 212209645Sjchandra register_t asid; 213209243Sjchandra register_t s; 214209243Sjchandra int i; 215209243Sjchandra 216209243Sjchandra va &= ~PAGE_MASK; 217209243Sjchandra pte &= ~TLBLO_SWBITS_MASK; 218209243Sjchandra 219209243Sjchandra s = intr_disable(); 220209243Sjchandra asid = mips_rd_entryhi() & TLBHI_ASID_MASK; 221209243Sjchandra 222209243Sjchandra mips_wr_pagemask(0); 223209243Sjchandra mips_wr_entryhi(TLBHI_ENTRY(va, pmap_asid(pmap))); 224209243Sjchandra tlb_probe(); 225209243Sjchandra i = mips_rd_index(); 226209243Sjchandra if (i >= 0) { 227209243Sjchandra tlb_read(); 228209243Sjchandra 229209243Sjchandra if ((va & PAGE_SIZE) == 0) { 230209243Sjchandra mips_wr_entrylo0(pte); 231209243Sjchandra } else { 232209243Sjchandra mips_wr_entrylo1(pte); 233209243Sjchandra } 234209243Sjchandra tlb_write_indexed(); 235209243Sjchandra } 236209243Sjchandra 237209243Sjchandra mips_wr_entryhi(asid); 238209243Sjchandra intr_restore(s); 239209243Sjchandra} 240209243Sjchandra 241209243Sjchandrastatic void 242209243Sjchandratlb_invalidate_one(unsigned i) 243209243Sjchandra{ 244209243Sjchandra /* XXX an invalid ASID? */ 245209243Sjchandra mips_wr_entryhi(TLBHI_ENTRY(MIPS_KSEG0_START + (2 * i * PAGE_SIZE), 0)); 246209243Sjchandra mips_wr_entrylo0(0); 247209243Sjchandra mips_wr_entrylo1(0); 248209243Sjchandra mips_wr_pagemask(0); 249209243Sjchandra mips_wr_index(i); 250209243Sjchandra tlb_write_indexed(); 251209243Sjchandra} 252209243Sjchandra 253209243Sjchandra#ifdef DDB 254209243Sjchandra#include <ddb/ddb.h> 255209243Sjchandra 256209243SjchandraDB_SHOW_COMMAND(tlb, ddb_dump_tlb) 257209243Sjchandra{ 258209243Sjchandra register_t ehi, elo0, elo1; 259209243Sjchandra unsigned i, cpu; 260209243Sjchandra 261209243Sjchandra /* 262209243Sjchandra * XXX 263209243Sjchandra * The worst conversion from hex to decimal ever. 264209243Sjchandra */ 265209243Sjchandra if (have_addr) 266209243Sjchandra cpu = ((addr >> 4) % 16) * 10 + (addr % 16); 267209243Sjchandra else 268209243Sjchandra cpu = PCPU_GET(cpuid); 269209243Sjchandra 270209243Sjchandra if (cpu < 0 || cpu >= mp_ncpus) { 271209243Sjchandra db_printf("Invalid CPU %u\n", cpu); 272209243Sjchandra return; 273209243Sjchandra } 274209243Sjchandra 275209243Sjchandra if (cpu == PCPU_GET(cpuid)) 276209243Sjchandra tlb_save(); 277209243Sjchandra 278209243Sjchandra db_printf("Beginning TLB dump for CPU %u...\n", cpu); 279209243Sjchandra for (i = 0; i < num_tlbentries; i++) { 280209243Sjchandra if (i == tlb_state[cpu].wired) { 281209243Sjchandra if (i != 0) 282209243Sjchandra db_printf("^^^ WIRED ENTRIES ^^^\n"); 283209243Sjchandra else 284209243Sjchandra db_printf("(No wired entries.)\n"); 285209243Sjchandra } 286209243Sjchandra 287209243Sjchandra /* XXX PageMask. */ 288209243Sjchandra ehi = tlb_state[cpu].entry[i].entryhi; 289209243Sjchandra elo0 = tlb_state[cpu].entry[i].entrylo0; 290209243Sjchandra elo1 = tlb_state[cpu].entry[i].entrylo1; 291209243Sjchandra 292209243Sjchandra if (elo0 == 0 && elo1 == 0) 293209243Sjchandra continue; 294209243Sjchandra 295209243Sjchandra db_printf("#%u\t=> %jx\n", i, (intmax_t)ehi); 296209243Sjchandra db_printf(" Lo0\t%jx\t(%#jx)\n", (intmax_t)elo0, (intmax_t)TLBLO_PTE_TO_PA(elo0)); 297209243Sjchandra db_printf(" Lo1\t%jx\t(%#jx)\n", (intmax_t)elo1, (intmax_t)TLBLO_PTE_TO_PA(elo1)); 298209243Sjchandra } 299209243Sjchandra db_printf("Finished.\n"); 300209243Sjchandra} 301209243Sjchandra#endif 302