📄 tlb.cc
字号:
#include <string.h>#include "koala.hh"// Reset the Translation Lookaside Buffer.voidKoala::dump_tlb(){ for (size_t i = 0; i < tlb_size; ++i) { log("TLB [%d]: 0x%016x : 0x%016x-0x%016x", i, tlb[i].hi, tlb[i].lo[0], tlb[i].lo[1]); }} voidKoala::reset_tlb(){ // Clear the TLB map. memset(tlb_map, 0, sizeof(tlb_map)); // Add all TLB entries to the last (otherwise unused) TLB chain. for (size_t i = 0; i < tlb_size; ++i) { tlb[i].asid = invalid_asid; tlb[i].index = i; tlb[i].hash = tlb_map_size; if (tlb_map[tlb_map_size]) tlb_map[tlb_map_size]->prev = &tlb[i]; tlb[i].next = tlb_map[tlb_map_size]; tlb[i].prev = 0; tlb_map[tlb_map_size] = &tlb[i]; }}// Set a TLB entry from PageMask, EntryHi, EntryLo0 and EntryLo1.voidKoala::set_tlb_entry(int n){ int hash = tlb_hash(cp0[EntryHi]); if ((unsigned int)n >= tlb_size) return; // msg("TLB Insert: EntryHi = %016lx, EntryLo0 = %016lx, EntryLo1 = %016lx, PageMask = %016lx", // cp0[EntryHi], cp0[EntryLo0], cp0[EntryLo1], cp0[PageMask]); // Remove entry from the current TLB hash chain. if (tlb[n].next) tlb[n].next->prev = tlb[n].prev; if (tlb[n].prev) tlb[n].prev->next = tlb[n].next; else tlb_map[tlb[n].hash] = tlb[n].next; // Compute the page mask. UInt64 pagemask = set_bits(cp0[PageMask], 12, 0); // Set the new entry. tlb[n].hi = cp0[EntryHi]; tlb[n].lo[0] = cp0[EntryLo0]; tlb[n].lo[1] = cp0[EntryLo1]; tlb[n].mask = ~pagemask; tlb[n].oddbit = pagemask ^ (pagemask >> 1); if (bit(cp0[EntryLo0], EntryLo_G) & bit(cp0[EntryLo1], EntryLo_G)) tlb[n].asid = global_asid; else tlb[n].asid = bits(cp0[EntryHi], 7, 0) /* asid */; tlb[n].index = n; tlb[n].hash = hash; // Add the entry to the new TLB hash chain. if (tlb_map[hash]) tlb_map[hash]->prev = &tlb[n]; tlb[n].next = tlb_map[hash]; tlb[n].prev = 0; tlb_map[hash] = &tlb[n];}// The TLB lookup routine. MIPS TLBs are fully-associative, so we use a hash// table to look them up. Dynamic allocation of the TLB nodes isn't needed as// the number of entries is fixed. This is used only by translate_vaddr() and// the TLBP instruction. It returns a pointer to the TLB entry or 0 on a TLB// miss.Koala::TLBEntry *Koala::probe_tlb(VA va){ int hash = tlb_hash(va); for (TLBEntry* e = tlb_map[hash]; e; e = e->next) { if (asid_match(asid, e->asid) && va_match(va, e->hi, e->mask)) { return e; } } return 0;}// Perform a full TLB lookup.//// WARNING: Both R4600/R4700 have a four-entry DTLB that use a pseudo-LRU// replacement (the least-recently used entry of the least-recenty used half// is selected for replacement). A DTLB miss results in one-cycle slip in the// A pipeline stage. This feature is currently not simulated.Koala::PAKoala::translate_vaddr(VA va, int type){ UInt32 region_type; // one of SR_UX, SR_SX or SR_KX // Decode the virtual address. if (vaddr_region(va) == vaddr_region(xkuseg)) { // The user region is valid in every mode, but we still have // to handle the 32/64 bit mode differences. if (bits(va, 63, (mode & xmode) ? vaddr_width : 31)) { process_address_error(type, va); return 0; // shut up the compiler } else { // kuseg becomes an unmapped, uncached region if ERL is set. if (bit(cp0[SR], SR_ERL) && !bits(va, 63, 32)) return make_physical_address(va, uncached); // Proceed with the TLB lookup. region_type = SR_UX; } } else if (vaddr_region(va) == vaddr_region(xksseg)) { // The 64 bit supervisor segment is accessible only in the 64 bit // supervisor or kernel mode. if ((mode & (cmode|umode)) || bits(va, 61, vaddr_width)) { process_address_error(type, va); return 0; // shut up the compiler } else { // Proceed with the TLB lookup. region_type = SR_SX; } } else if (vaddr_region(va) == vaddr_region(xkphys)) { // The 64 bit unmapped segment; accessible only in 64 bit kernel mode. // Bits paddr_width to 58 must be zero. int ca = coherency_algorithm(va); if ((mode & (cmode|umode|smode)) || bits(va, 58, paddr_width) || ca < noncoherent_write_through || ca > update_on_write) { process_address_error(type, va); return 0; // shut up the compiler } else { // The physical address is already encoded. return va; } } else { // This segment includes the 64 bit kernel segment and the // compatibility segments. Only CKSSEG may be accessed in the // supervisor mode; all others must be accessed in the kernel mode. if (va >= ckseg0) { int region = vaddr_compat_region(va); if ((mode & (umode|smode)) && ((mode & umode) || region != vaddr_compat_region(cksseg))) { process_address_error(type, va); return 0; // shut up the compiler } else if (region < vaddr_compat_region(cksseg)) { // Access unmapped segments ckseg0 and ckseg1. int ca = (region == vaddr_compat_region(ckseg1)) ? uncached : bits(cp0[Config], Config_K0_Last, Config_K0_First); return make_physical_address(bits(va, 28, 0), ca); } else { // One of ckseg3 or cksseg. region_type = (region < vaddr_compat_region(ckseg3)) ? SR_SX : SR_KX; } } else if ((mode & xmode) == 0 || va >= xkseg_end) { // xkseg is accessible only in the 64 bit kernel mode. process_address_error(type, va); return 0; // shut up the compiler } else { // Proceed with the TLB lookup. region_type = SR_KX; } } // Now, we are ready to probe the TLB. TLBEntry *entry = probe_tlb(va); if (!entry) process_tlb_refill(type, va, bit(cp0[SR], region_type)); // TLB miss // Filter TLB entries marked invalid. UInt32 lo = (va & entry->oddbit) ? entry->lo[1] : entry->lo[0]; if (!bit(lo, EntryLo_V)) process_tlb_invalid(type, va); // TLB invalid. // Fiter TLB entries marked read-only. if (type == data_store && !bit(lo, EntryLo_D)) process_tlb_modified(va); // TLB Modified exception. // Finally, retrieve the mapping. UInt64 mask = entry->oddbit - 1; PA pa = (va & mask) | ((PA(lo) << 6) & ~mask); int ca = bits(lo, EntryLo_C_Last, EntryLo_C_First); return make_physical_address(pa, ca);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -