| /* | |
| * Copyright (c) 2001-2005 The Regents of The University of Michigan | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * Authors: Ali Saidi | |
| */ | |
| #include "arch/sparc/tlb.hh" | |
| #include <cstring> | |
| #include "arch/sparc/asi.hh" | |
| #include "arch/sparc/faults.hh" | |
| #include "arch/sparc/interrupts.hh" | |
| #include "arch/sparc/registers.hh" | |
| #include "base/bitfield.hh" | |
| #include "base/compiler.hh" | |
| #include "base/trace.hh" | |
| #include "cpu/base.hh" | |
| #include "cpu/thread_context.hh" | |
| #include "debug/IPR.hh" | |
| #include "debug/TLB.hh" | |
| #include "mem/packet_access.hh" | |
| #include "mem/request.hh" | |
| #include "sim/full_system.hh" | |
| #include "sim/system.hh" | |
| /* @todo remove some of the magic constants. -- ali | |
| * */ | |
| namespace SparcISA { | |
| TLB::TLB(const Params *p) | |
| : BaseTLB(p), size(p->size), usedEntries(0), lastReplaced(0), | |
| cacheState(0), cacheValid(false) | |
| { | |
| // To make this work you'll have to change the hypervisor and OS | |
| if (size > 64) | |
| fatal("SPARC T1 TLB registers don't support more than 64 TLB entries"); | |
| tlb = new TlbEntry[size]; | |
| std::memset((void *)tlb, 0, sizeof(TlbEntry) * size); | |
| for (int x = 0; x < size; x++) | |
| freeList.push_back(&tlb[x]); | |
| c0_tsb_ps0 = 0; | |
| c0_tsb_ps1 = 0; | |
| c0_config = 0; | |
| cx_tsb_ps0 = 0; | |
| cx_tsb_ps1 = 0; | |
| cx_config = 0; | |
| sfsr = 0; | |
| tag_access = 0; | |
| sfar = 0; | |
| cacheEntry[0] = NULL; | |
| cacheEntry[1] = NULL; | |
| } | |
| void | |
| TLB::clearUsedBits() | |
| { | |
| MapIter i; | |
| for (i = lookupTable.begin(); i != lookupTable.end(); i++) { | |
| TlbEntry *t = i->second; | |
| if (!t->pte.locked()) { | |
| t->used = false; | |
| usedEntries--; | |
| } | |
| } | |
| } | |
| void | |
| TLB::insert(Addr va, int partition_id, int context_id, bool real, | |
| const PageTableEntry& PTE, int entry) | |
| { | |
| MapIter i; | |
| TlbEntry *new_entry = NULL; | |
| // TlbRange tr; | |
| int x; | |
| cacheValid = false; | |
| va &= ~(PTE.size()-1); | |
| /* tr.va = va; | |
| tr.size = PTE.size() - 1; | |
| tr.contextId = context_id; | |
| tr.partitionId = partition_id; | |
| tr.real = real; | |
| */ | |
| DPRINTF(TLB, | |
| "TLB: Inserting Entry; va=%#x pa=%#x pid=%d cid=%d r=%d entryid=%d\n", | |
| va, PTE.paddr(), partition_id, context_id, (int)real, entry); | |
| // Demap any entry that conflicts | |
| for (x = 0; x < size; x++) { | |
| if (tlb[x].range.real == real && | |
| tlb[x].range.partitionId == partition_id && | |
| tlb[x].range.va < va + PTE.size() - 1 && | |
| tlb[x].range.va + tlb[x].range.size >= va && | |
| (real || tlb[x].range.contextId == context_id )) | |
| { | |
| if (tlb[x].valid) { | |
| freeList.push_front(&tlb[x]); | |
| DPRINTF(TLB, "TLB: Conflicting entry %#X , deleting it\n", x); | |
| tlb[x].valid = false; | |
| if (tlb[x].used) { | |
| tlb[x].used = false; | |
| usedEntries--; | |
| } | |
| lookupTable.erase(tlb[x].range); | |
| } | |
| } | |
| } | |
| if (entry != -1) { | |
| assert(entry < size && entry >= 0); | |
| new_entry = &tlb[entry]; | |
| } else { | |
| if (!freeList.empty()) { | |
| new_entry = freeList.front(); | |
| } else { | |
| x = lastReplaced; | |
| do { | |
| ++x; | |
| if (x == size) | |
| x = 0; | |
| if (x == lastReplaced) | |
| goto insertAllLocked; | |
| } while (tlb[x].pte.locked()); | |
| lastReplaced = x; | |
| new_entry = &tlb[x]; | |
| } | |
| } | |
| insertAllLocked: | |
| // Update the last ently if their all locked | |
| if (!new_entry) { | |
| new_entry = &tlb[size-1]; | |
| } | |
| freeList.remove(new_entry); | |
| if (new_entry->valid && new_entry->used) | |
| usedEntries--; | |
| if (new_entry->valid) | |
| lookupTable.erase(new_entry->range); | |
| assert(PTE.valid()); | |
| new_entry->range.va = va; | |
| new_entry->range.size = PTE.size() - 1; | |
| new_entry->range.partitionId = partition_id; | |
| new_entry->range.contextId = context_id; | |
| new_entry->range.real = real; | |
| new_entry->pte = PTE; | |
| new_entry->used = true;; | |
| new_entry->valid = true; | |
| usedEntries++; | |
| i = lookupTable.insert(new_entry->range, new_entry); | |
| assert(i != lookupTable.end()); | |
| // If all entries have their used bit set, clear it on them all, | |
| // but the one we just inserted | |
| if (usedEntries == size) { | |
| clearUsedBits(); | |
| new_entry->used = true; | |
| usedEntries++; | |
| } | |
| } | |
| TlbEntry* | |
| TLB::lookup(Addr va, int partition_id, bool real, int context_id, | |
| bool update_used) | |
| { | |
| MapIter i; | |
| TlbRange tr; | |
| TlbEntry *t; | |
| DPRINTF(TLB, "TLB: Looking up entry va=%#x pid=%d cid=%d r=%d\n", | |
| va, partition_id, context_id, real); | |
| // Assemble full address structure | |
| tr.va = va; | |
| tr.size = 1; | |
| tr.contextId = context_id; | |
| tr.partitionId = partition_id; | |
| tr.real = real; | |
| // Try to find the entry | |
| i = lookupTable.find(tr); | |
| if (i == lookupTable.end()) { | |
| DPRINTF(TLB, "TLB: No valid entry found\n"); | |
| return NULL; | |
| } | |
| // Mark the entries used bit and clear other used bits in needed | |
| t = i->second; | |
| DPRINTF(TLB, "TLB: Valid entry found pa: %#x size: %#x\n", t->pte.paddr(), | |
| t->pte.size()); | |
| // Update the used bits only if this is a real access (not a fake | |
| // one from virttophys() | |
| if (!t->used && update_used) { | |
| t->used = true; | |
| usedEntries++; | |
| if (usedEntries == size) { | |
| clearUsedBits(); | |
| t->used = true; | |
| usedEntries++; | |
| } | |
| } | |
| return t; | |
| } | |
| void | |
| TLB::dumpAll() | |
| { | |
| MapIter i; | |
| for (int x = 0; x < size; x++) { | |
| if (tlb[x].valid) { | |
| DPRINTFN("%4d: %#2x:%#2x %c %#4x %#8x %#8x %#16x\n", | |
| x, tlb[x].range.partitionId, tlb[x].range.contextId, | |
| tlb[x].range.real ? 'R' : ' ', tlb[x].range.size, | |
| tlb[x].range.va, tlb[x].pte.paddr(), tlb[x].pte()); | |
| } | |
| } | |
| } | |
| void | |
| TLB::demapPage(Addr va, int partition_id, bool real, int context_id) | |
| { | |
| TlbRange tr; | |
| MapIter i; | |
| DPRINTF(IPR, "TLB: Demapping Page va=%#x pid=%#d cid=%d r=%d\n", | |
| va, partition_id, context_id, real); | |
| cacheValid = false; | |
| // Assemble full address structure | |
| tr.va = va; | |
| tr.size = 1; | |
| tr.contextId = context_id; | |
| tr.partitionId = partition_id; | |
| tr.real = real; | |
| // Demap any entry that conflicts | |
| i = lookupTable.find(tr); | |
| if (i != lookupTable.end()) { | |
| DPRINTF(IPR, "TLB: Demapped page\n"); | |
| i->second->valid = false; | |
| if (i->second->used) { | |
| i->second->used = false; | |
| usedEntries--; | |
| } | |
| freeList.push_front(i->second); | |
| lookupTable.erase(i); | |
| } | |
| } | |
| void | |
| TLB::demapContext(int partition_id, int context_id) | |
| { | |
| DPRINTF(IPR, "TLB: Demapping Context pid=%#d cid=%d\n", | |
| partition_id, context_id); | |
| cacheValid = false; | |
| for (int x = 0; x < size; x++) { | |
| if (tlb[x].range.contextId == context_id && | |
| tlb[x].range.partitionId == partition_id) { | |
| if (tlb[x].valid) { | |
| freeList.push_front(&tlb[x]); | |
| } | |
| tlb[x].valid = false; | |
| if (tlb[x].used) { | |
| tlb[x].used = false; | |
| usedEntries--; | |
| } | |
| lookupTable.erase(tlb[x].range); | |
| } | |
| } | |
| } | |
| void | |
| TLB::demapAll(int partition_id) | |
| { | |
| DPRINTF(TLB, "TLB: Demapping All pid=%#d\n", partition_id); | |
| cacheValid = false; | |
| for (int x = 0; x < size; x++) { | |
| if (tlb[x].valid && !tlb[x].pte.locked() && | |
| tlb[x].range.partitionId == partition_id) { | |
| freeList.push_front(&tlb[x]); | |
| tlb[x].valid = false; | |
| if (tlb[x].used) { | |
| tlb[x].used = false; | |
| usedEntries--; | |
| } | |
| lookupTable.erase(tlb[x].range); | |
| } | |
| } | |
| } | |
| void | |
| TLB::flushAll() | |
| { | |
| cacheValid = false; | |
| lookupTable.clear(); | |
| for (int x = 0; x < size; x++) { | |
| if (tlb[x].valid) | |
| freeList.push_back(&tlb[x]); | |
| tlb[x].valid = false; | |
| tlb[x].used = false; | |
| } | |
| usedEntries = 0; | |
| } | |
| uint64_t | |
| TLB::TteRead(int entry) | |
| { | |
| if (entry >= size) | |
| panic("entry: %d\n", entry); | |
| assert(entry < size); | |
| if (tlb[entry].valid) | |
| return tlb[entry].pte(); | |
| else | |
| return (uint64_t)-1ll; | |
| } | |
| uint64_t | |
| TLB::TagRead(int entry) | |
| { | |
| assert(entry < size); | |
| uint64_t tag; | |
| if (!tlb[entry].valid) | |
| return (uint64_t)-1ll; | |
| tag = tlb[entry].range.contextId; | |
| tag |= tlb[entry].range.va; | |
| tag |= (uint64_t)tlb[entry].range.partitionId << 61; | |
| tag |= tlb[entry].range.real ? ULL(1) << 60 : 0; | |
| tag |= (uint64_t)~tlb[entry].pte._size() << 56; | |
| return tag; | |
| } | |
| bool | |
| TLB::validVirtualAddress(Addr va, bool am) | |
| { | |
| if (am) | |
| return true; | |
| if (va >= StartVAddrHole && va <= EndVAddrHole) | |
| return false; | |
| return true; | |
| } | |
| void | |
| TLB::writeSfsr(bool write, ContextType ct, bool se, FaultTypes ft, int asi) | |
| { | |
| if (sfsr & 0x1) | |
| sfsr = 0x3; | |
| else | |
| sfsr = 1; | |
| if (write) | |
| sfsr |= 1 << 2; | |
| sfsr |= ct << 4; | |
| if (se) | |
| sfsr |= 1 << 6; | |
| sfsr |= ft << 7; | |
| sfsr |= asi << 16; | |
| } | |
| void | |
| TLB::writeTagAccess(Addr va, int context) | |
| { | |
| DPRINTF(TLB, "TLB: Writing Tag Access: va: %#X ctx: %#X value: %#X\n", | |
| va, context, mbits(va, 63,13) | mbits(context,12,0)); | |
| tag_access = mbits(va, 63,13) | mbits(context,12,0); | |
| } | |
| void | |
| TLB::writeSfsr(Addr a, bool write, ContextType ct, | |
| bool se, FaultTypes ft, int asi) | |
| { | |
| DPRINTF(TLB, "TLB: Fault: A=%#x w=%d ct=%d ft=%d asi=%d\n", | |
| a, (int)write, ct, ft, asi); | |
| TLB::writeSfsr(write, ct, se, ft, asi); | |
| sfar = a; | |
| } | |
| Fault | |
| TLB::translateInst(const RequestPtr &req, ThreadContext *tc) | |
| { | |
| uint64_t tlbdata = tc->readMiscRegNoEffect(MISCREG_TLB_DATA); | |
| Addr vaddr = req->getVaddr(); | |
| TlbEntry *e; | |
| assert(req->getArchFlags() == ASI_IMPLICIT); | |
| DPRINTF(TLB, "TLB: ITB Request to translate va=%#x size=%d\n", | |
| vaddr, req->getSize()); | |
| // Be fast if we can! | |
| if (cacheValid && cacheState == tlbdata) { | |
| if (cacheEntry[0]) { | |
| if (cacheEntry[0]->range.va < vaddr + sizeof(MachInst) && | |
| cacheEntry[0]->range.va + cacheEntry[0]->range.size >= vaddr) { | |
| req->setPaddr(cacheEntry[0]->pte.translate(vaddr)); | |
| return NoFault; | |
| } | |
| } else { | |
| req->setPaddr(vaddr & PAddrImplMask); | |
| return NoFault; | |
| } | |
| } | |
| bool hpriv = bits(tlbdata,0,0); | |
| bool red = bits(tlbdata,1,1); | |
| bool priv = bits(tlbdata,2,2); | |
| bool addr_mask = bits(tlbdata,3,3); | |
| bool lsu_im = bits(tlbdata,4,4); | |
| int part_id = bits(tlbdata,15,8); | |
| int tl = bits(tlbdata,18,16); | |
| int pri_context = bits(tlbdata,47,32); | |
| int context; | |
| ContextType ct; | |
| int asi; | |
| bool real = false; | |
| DPRINTF(TLB, "TLB: priv:%d hpriv:%d red:%d lsuim:%d part_id: %#X\n", | |
| priv, hpriv, red, lsu_im, part_id); | |
| if (tl > 0) { | |
| asi = ASI_N; | |
| ct = Nucleus; | |
| context = 0; | |
| } else { | |
| asi = ASI_P; | |
| ct = Primary; | |
| context = pri_context; | |
| } | |
| if ( hpriv || red ) { | |
| cacheValid = true; | |
| cacheState = tlbdata; | |
| cacheEntry[0] = NULL; | |
| req->setPaddr(vaddr & PAddrImplMask); | |
| return NoFault; | |
| } | |
| // If the access is unaligned trap | |
| if (vaddr & 0x3) { | |
| writeSfsr(false, ct, false, OtherFault, asi); | |
| return std::make_shared<MemAddressNotAligned>(); | |
| } | |
| if (addr_mask) | |
| vaddr = vaddr & VAddrAMask; | |
| if (!validVirtualAddress(vaddr, addr_mask)) { | |
| writeSfsr(false, ct, false, VaOutOfRange, asi); | |
| return std::make_shared<InstructionAccessException>(); | |
| } | |
| if (!lsu_im) { | |
| e = lookup(vaddr, part_id, true); | |
| real = true; | |
| context = 0; | |
| } else { | |
| e = lookup(vaddr, part_id, false, context); | |
| } | |
| if (e == NULL || !e->valid) { | |
| writeTagAccess(vaddr, context); | |
| if (real) { | |
| return std::make_shared<InstructionRealTranslationMiss>(); | |
| } else { | |
| if (FullSystem) | |
| return std::make_shared<FastInstructionAccessMMUMiss>(); | |
| else | |
| return std::make_shared<FastInstructionAccessMMUMiss>( | |
| req->getVaddr()); | |
| } | |
| } | |
| // were not priviledged accesing priv page | |
| if (!priv && e->pte.priv()) { | |
| writeTagAccess(vaddr, context); | |
| writeSfsr(false, ct, false, PrivViolation, asi); | |
| return std::make_shared<InstructionAccessException>(); | |
| } | |
| // cache translation date for next translation | |
| cacheValid = true; | |
| cacheState = tlbdata; | |
| cacheEntry[0] = e; | |
| req->setPaddr(e->pte.translate(vaddr)); | |
| DPRINTF(TLB, "TLB: %#X -> %#X\n", vaddr, req->getPaddr()); | |
| return NoFault; | |
| } | |
| Fault | |
| TLB::translateData(const RequestPtr &req, ThreadContext *tc, bool write) | |
| { | |
| /* | |
| * @todo this could really use some profiling and fixing to make | |
| * it faster! | |
| */ | |
| uint64_t tlbdata = tc->readMiscRegNoEffect(MISCREG_TLB_DATA); | |
| Addr vaddr = req->getVaddr(); | |
| Addr size = req->getSize(); | |
| ASI asi; | |
| asi = (ASI)req->getArchFlags(); | |
| bool implicit = false; | |
| bool hpriv = bits(tlbdata,0,0); | |
| bool unaligned = vaddr & (size - 1); | |
| DPRINTF(TLB, "TLB: DTB Request to translate va=%#x size=%d asi=%#x\n", | |
| vaddr, size, asi); | |
| if (lookupTable.size() != 64 - freeList.size()) | |
| panic("Lookup table size: %d tlb size: %d\n", lookupTable.size(), | |
| freeList.size()); | |
| if (asi == ASI_IMPLICIT) | |
| implicit = true; | |
| // Only use the fast path here if there doesn't need to be an unaligned | |
| // trap later | |
| if (!unaligned) { | |
| if (hpriv && implicit) { | |
| req->setPaddr(vaddr & PAddrImplMask); | |
| return NoFault; | |
| } | |
| // Be fast if we can! | |
| if (cacheValid && cacheState == tlbdata) { | |
| if (cacheEntry[0]) { | |
| TlbEntry *ce = cacheEntry[0]; | |
| Addr ce_va = ce->range.va; | |
| if (cacheAsi[0] == asi && | |
| ce_va < vaddr + size && ce_va + ce->range.size > vaddr && | |
| (!write || ce->pte.writable())) { | |
| req->setPaddr(ce->pte.translate(vaddr)); | |
| if (ce->pte.sideffect() || (ce->pte.paddr() >> 39) & 1) { | |
| req->setFlags( | |
| Request::UNCACHEABLE | Request::STRICT_ORDER); | |
| } | |
| DPRINTF(TLB, "TLB: %#X -> %#X\n", vaddr, req->getPaddr()); | |
| return NoFault; | |
| } // if matched | |
| } // if cache entry valid | |
| if (cacheEntry[1]) { | |
| TlbEntry *ce = cacheEntry[1]; | |
| Addr ce_va = ce->range.va; | |
| if (cacheAsi[1] == asi && | |
| ce_va < vaddr + size && ce_va + ce->range.size > vaddr && | |
| (!write || ce->pte.writable())) { | |
| req->setPaddr(ce->pte.translate(vaddr)); | |
| if (ce->pte.sideffect() || (ce->pte.paddr() >> 39) & 1) { | |
| req->setFlags( | |
| Request::UNCACHEABLE | Request::STRICT_ORDER); | |
| } | |
| DPRINTF(TLB, "TLB: %#X -> %#X\n", vaddr, req->getPaddr()); | |
| return NoFault; | |
| } // if matched | |
| } // if cache entry valid | |
| } | |
| } | |
| bool red = bits(tlbdata,1,1); | |
| bool priv = bits(tlbdata,2,2); | |
| bool addr_mask = bits(tlbdata,3,3); | |
| bool lsu_dm = bits(tlbdata,5,5); | |
| int part_id = bits(tlbdata,15,8); | |
| int tl = bits(tlbdata,18,16); | |
| int pri_context = bits(tlbdata,47,32); | |
| int sec_context = bits(tlbdata,63,48); | |
| bool real = false; | |
| ContextType ct = Primary; | |
| int context = 0; | |
| TlbEntry *e; | |
| DPRINTF(TLB, "TLB: priv:%d hpriv:%d red:%d lsudm:%d part_id: %#X\n", | |
| priv, hpriv, red, lsu_dm, part_id); | |
| if (implicit) { | |
| if (tl > 0) { | |
| asi = ASI_N; | |
| ct = Nucleus; | |
| context = 0; | |
| } else { | |
| asi = ASI_P; | |
| ct = Primary; | |
| context = pri_context; | |
| } | |
| } else { | |
| // We need to check for priv level/asi priv | |
| if (!priv && !hpriv && !asiIsUnPriv(asi)) { | |
| // It appears that context should be Nucleus in these cases? | |
| writeSfsr(vaddr, write, Nucleus, false, IllegalAsi, asi); | |
| return std::make_shared<PrivilegedAction>(); | |
| } | |
| if (!hpriv && asiIsHPriv(asi)) { | |
| writeSfsr(vaddr, write, Nucleus, false, IllegalAsi, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| if (asiIsPrimary(asi)) { | |
| context = pri_context; | |
| ct = Primary; | |
| } else if (asiIsSecondary(asi)) { | |
| context = sec_context; | |
| ct = Secondary; | |
| } else if (asiIsNucleus(asi)) { | |
| ct = Nucleus; | |
| context = 0; | |
| } else { // ???? | |
| ct = Primary; | |
| context = pri_context; | |
| } | |
| } | |
| if (!implicit && asi != ASI_P && asi != ASI_S) { | |
| if (asiIsLittle(asi)) | |
| panic("Little Endian ASIs not supported\n"); | |
| //XXX It's unclear from looking at the documentation how a no fault | |
| // load differs from a regular one, other than what happens concerning | |
| // nfo and e bits in the TTE | |
| // if (asiIsNoFault(asi)) | |
| // panic("No Fault ASIs not supported\n"); | |
| if (asiIsPartialStore(asi)) | |
| panic("Partial Store ASIs not supported\n"); | |
| if (asiIsCmt(asi)) | |
| panic("Cmt ASI registers not implmented\n"); | |
| if (asiIsInterrupt(asi)) | |
| goto handleIntRegAccess; | |
| if (asiIsMmu(asi)) | |
| goto handleMmuRegAccess; | |
| if (asiIsScratchPad(asi)) | |
| goto handleScratchRegAccess; | |
| if (asiIsQueue(asi)) | |
| goto handleQueueRegAccess; | |
| if (asiIsSparcError(asi)) | |
| goto handleSparcErrorRegAccess; | |
| if (!asiIsReal(asi) && !asiIsNucleus(asi) && !asiIsAsIfUser(asi) && | |
| !asiIsTwin(asi) && !asiIsBlock(asi) && !asiIsNoFault(asi)) | |
| panic("Accessing ASI %#X. Should we?\n", asi); | |
| } | |
| // If the asi is unaligned trap | |
| if (unaligned) { | |
| writeSfsr(vaddr, false, ct, false, OtherFault, asi); | |
| return std::make_shared<MemAddressNotAligned>(); | |
| } | |
| if (addr_mask) | |
| vaddr = vaddr & VAddrAMask; | |
| if (!validVirtualAddress(vaddr, addr_mask)) { | |
| writeSfsr(vaddr, false, ct, true, VaOutOfRange, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| if ((!lsu_dm && !hpriv && !red) || asiIsReal(asi)) { | |
| real = true; | |
| context = 0; | |
| } | |
| if (hpriv && (implicit || (!asiIsAsIfUser(asi) && !asiIsReal(asi)))) { | |
| req->setPaddr(vaddr & PAddrImplMask); | |
| return NoFault; | |
| } | |
| e = lookup(vaddr, part_id, real, context); | |
| if (e == NULL || !e->valid) { | |
| writeTagAccess(vaddr, context); | |
| DPRINTF(TLB, "TLB: DTB Failed to find matching TLB entry\n"); | |
| if (real) { | |
| return std::make_shared<DataRealTranslationMiss>(); | |
| } else { | |
| if (FullSystem) | |
| return std::make_shared<FastDataAccessMMUMiss>(); | |
| else | |
| return std::make_shared<FastDataAccessMMUMiss>( | |
| req->getVaddr()); | |
| } | |
| } | |
| if (!priv && e->pte.priv()) { | |
| writeTagAccess(vaddr, context); | |
| writeSfsr(vaddr, write, ct, e->pte.sideffect(), PrivViolation, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| if (write && !e->pte.writable()) { | |
| writeTagAccess(vaddr, context); | |
| writeSfsr(vaddr, write, ct, e->pte.sideffect(), OtherFault, asi); | |
| return std::make_shared<FastDataAccessProtection>(); | |
| } | |
| if (e->pte.nofault() && !asiIsNoFault(asi)) { | |
| writeTagAccess(vaddr, context); | |
| writeSfsr(vaddr, write, ct, e->pte.sideffect(), LoadFromNfo, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| if (e->pte.sideffect() && asiIsNoFault(asi)) { | |
| writeTagAccess(vaddr, context); | |
| writeSfsr(vaddr, write, ct, e->pte.sideffect(), SideEffect, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| if (e->pte.sideffect() || (e->pte.paddr() >> 39) & 1) | |
| req->setFlags(Request::UNCACHEABLE | Request::STRICT_ORDER); | |
| // cache translation date for next translation | |
| cacheState = tlbdata; | |
| if (!cacheValid) { | |
| cacheEntry[1] = NULL; | |
| cacheEntry[0] = NULL; | |
| } | |
| if (cacheEntry[0] != e && cacheEntry[1] != e) { | |
| cacheEntry[1] = cacheEntry[0]; | |
| cacheEntry[0] = e; | |
| cacheAsi[1] = cacheAsi[0]; | |
| cacheAsi[0] = asi; | |
| if (implicit) | |
| cacheAsi[0] = (ASI)0; | |
| } | |
| cacheValid = true; | |
| req->setPaddr(e->pte.translate(vaddr)); | |
| DPRINTF(TLB, "TLB: %#X -> %#X\n", vaddr, req->getPaddr()); | |
| return NoFault; | |
| /** Normal flow ends here. */ | |
| handleIntRegAccess: | |
| if (!hpriv) { | |
| writeSfsr(vaddr, write, Primary, true, IllegalAsi, asi); | |
| if (priv) | |
| return std::make_shared<DataAccessException>(); | |
| else | |
| return std::make_shared<PrivilegedAction>(); | |
| } | |
| if ((asi == ASI_SWVR_UDB_INTR_W && !write) || | |
| (asi == ASI_SWVR_UDB_INTR_R && write)) { | |
| writeSfsr(vaddr, write, Primary, true, IllegalAsi, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| goto regAccessOk; | |
| handleScratchRegAccess: | |
| if (vaddr > 0x38 || (vaddr >= 0x20 && vaddr < 0x30 && !hpriv)) { | |
| writeSfsr(vaddr, write, Primary, true, IllegalAsi, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| goto regAccessOk; | |
| handleQueueRegAccess: | |
| if (!priv && !hpriv) { | |
| writeSfsr(vaddr, write, Primary, true, IllegalAsi, asi); | |
| return std::make_shared<PrivilegedAction>(); | |
| } | |
| if ((!hpriv && vaddr & 0xF) || vaddr > 0x3f8 || vaddr < 0x3c0) { | |
| writeSfsr(vaddr, write, Primary, true, IllegalAsi, asi); | |
| return std::make_shared<DataAccessException>(); | |
| } | |
| goto regAccessOk; | |
| handleSparcErrorRegAccess: | |
| if (!hpriv) { | |
| writeSfsr(vaddr, write, Primary, true, IllegalAsi, asi); | |
| if (priv) | |
| return std::make_shared<DataAccessException>(); | |
| else | |
| return std::make_shared<PrivilegedAction>(); | |
| } | |
| goto regAccessOk; | |
| regAccessOk: | |
| handleMmuRegAccess: | |
| DPRINTF(TLB, "TLB: DTB Translating MM IPR access\n"); | |
| req->setFlags(Request::MMAPPED_IPR); | |
| req->setPaddr(req->getVaddr()); | |
| return NoFault; | |
| }; | |
| Fault | |
| TLB::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode) | |
| { | |
| if (mode == Execute) | |
| return translateInst(req, tc); | |
| else | |
| return translateData(req, tc, mode == Write); | |
| } | |
| void | |
| TLB::translateTiming(const RequestPtr &req, ThreadContext *tc, | |
| Translation *translation, Mode mode) | |
| { | |
| assert(translation); | |
| translation->finish(translateAtomic(req, tc, mode), req, tc, mode); | |
| } | |
| Fault | |
| TLB::finalizePhysical(const RequestPtr &req, | |
| ThreadContext *tc, Mode mode) const | |
| { | |
| return NoFault; | |
| } | |
| Cycles | |
| TLB::doMmuRegRead(ThreadContext *tc, Packet *pkt) | |
| { | |
| Addr va = pkt->getAddr(); | |
| ASI asi = (ASI)pkt->req->getArchFlags(); | |
| uint64_t temp; | |
| DPRINTF(IPR, "Memory Mapped IPR Read: asi=%#X a=%#x\n", | |
| (uint32_t)pkt->req->getArchFlags(), pkt->getAddr()); | |
| TLB *itb = dynamic_cast<TLB *>(tc->getITBPtr()); | |
| switch (asi) { | |
| case ASI_LSU_CONTROL_REG: | |
| assert(va == 0); | |
| pkt->setBE(tc->readMiscReg(MISCREG_MMU_LSU_CTRL)); | |
| break; | |
| case ASI_MMU: | |
| switch (va) { | |
| case 0x8: | |
| pkt->setBE(tc->readMiscReg(MISCREG_MMU_P_CONTEXT)); | |
| break; | |
| case 0x10: | |
| pkt->setBE(tc->readMiscReg(MISCREG_MMU_S_CONTEXT)); | |
| break; | |
| default: | |
| goto doMmuReadError; | |
| } | |
| break; | |
| case ASI_QUEUE: | |
| pkt->setBE(tc->readMiscReg(MISCREG_QUEUE_CPU_MONDO_HEAD + | |
| (va >> 4) - 0x3c)); | |
| break; | |
| case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| pkt->setBE(c0_tsb_ps0); | |
| break; | |
| case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| pkt->setBE(c0_tsb_ps1); | |
| break; | |
| case ASI_DMMU_CTXT_ZERO_CONFIG: | |
| assert(va == 0); | |
| pkt->setBE(c0_config); | |
| break; | |
| case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| pkt->setBE(itb->c0_tsb_ps0); | |
| break; | |
| case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| pkt->setBE(itb->c0_tsb_ps1); | |
| break; | |
| case ASI_IMMU_CTXT_ZERO_CONFIG: | |
| assert(va == 0); | |
| pkt->setBE(itb->c0_config); | |
| break; | |
| case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| pkt->setBE(cx_tsb_ps0); | |
| break; | |
| case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| pkt->setBE(cx_tsb_ps1); | |
| break; | |
| case ASI_DMMU_CTXT_NONZERO_CONFIG: | |
| assert(va == 0); | |
| pkt->setBE(cx_config); | |
| break; | |
| case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| pkt->setBE(itb->cx_tsb_ps0); | |
| break; | |
| case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| pkt->setBE(itb->cx_tsb_ps1); | |
| break; | |
| case ASI_IMMU_CTXT_NONZERO_CONFIG: | |
| assert(va == 0); | |
| pkt->setBE(itb->cx_config); | |
| break; | |
| case ASI_SPARC_ERROR_STATUS_REG: | |
| pkt->setBE((uint64_t)0); | |
| break; | |
| case ASI_HYP_SCRATCHPAD: | |
| case ASI_SCRATCHPAD: | |
| pkt->setBE(tc->readMiscReg(MISCREG_SCRATCHPAD_R0 + (va >> 3))); | |
| break; | |
| case ASI_IMMU: | |
| switch (va) { | |
| case 0x0: | |
| temp = itb->tag_access; | |
| pkt->setBE(bits(temp,63,22) | bits(temp,12,0) << 48); | |
| break; | |
| case 0x18: | |
| pkt->setBE(itb->sfsr); | |
| break; | |
| case 0x30: | |
| pkt->setBE(itb->tag_access); | |
| break; | |
| default: | |
| goto doMmuReadError; | |
| } | |
| break; | |
| case ASI_DMMU: | |
| switch (va) { | |
| case 0x0: | |
| temp = tag_access; | |
| pkt->setBE(bits(temp,63,22) | bits(temp,12,0) << 48); | |
| break; | |
| case 0x18: | |
| pkt->setBE(sfsr); | |
| break; | |
| case 0x20: | |
| pkt->setBE(sfar); | |
| break; | |
| case 0x30: | |
| pkt->setBE(tag_access); | |
| break; | |
| case 0x80: | |
| pkt->setBE(tc->readMiscReg(MISCREG_MMU_PART_ID)); | |
| break; | |
| default: | |
| goto doMmuReadError; | |
| } | |
| break; | |
| case ASI_DMMU_TSB_PS0_PTR_REG: | |
| pkt->setBE(MakeTsbPtr(Ps0, | |
| tag_access, | |
| c0_tsb_ps0, | |
| c0_config, | |
| cx_tsb_ps0, | |
| cx_config)); | |
| break; | |
| case ASI_DMMU_TSB_PS1_PTR_REG: | |
| pkt->setBE(MakeTsbPtr(Ps1, | |
| tag_access, | |
| c0_tsb_ps1, | |
| c0_config, | |
| cx_tsb_ps1, | |
| cx_config)); | |
| break; | |
| case ASI_IMMU_TSB_PS0_PTR_REG: | |
| pkt->setBE(MakeTsbPtr(Ps0, | |
| itb->tag_access, | |
| itb->c0_tsb_ps0, | |
| itb->c0_config, | |
| itb->cx_tsb_ps0, | |
| itb->cx_config)); | |
| break; | |
| case ASI_IMMU_TSB_PS1_PTR_REG: | |
| pkt->setBE(MakeTsbPtr(Ps1, | |
| itb->tag_access, | |
| itb->c0_tsb_ps1, | |
| itb->c0_config, | |
| itb->cx_tsb_ps1, | |
| itb->cx_config)); | |
| break; | |
| case ASI_SWVR_INTR_RECEIVE: | |
| { | |
| SparcISA::Interrupts * interrupts = | |
| dynamic_cast<SparcISA::Interrupts *>( | |
| tc->getCpuPtr()->getInterruptController(0)); | |
| pkt->setBE(interrupts->get_vec(IT_INT_VEC)); | |
| } | |
| break; | |
| case ASI_SWVR_UDB_INTR_R: | |
| { | |
| SparcISA::Interrupts * interrupts = | |
| dynamic_cast<SparcISA::Interrupts *>( | |
| tc->getCpuPtr()->getInterruptController(0)); | |
| temp = findMsbSet(interrupts->get_vec(IT_INT_VEC)); | |
| tc->getCpuPtr()->clearInterrupt(0, IT_INT_VEC, temp); | |
| pkt->setBE(temp); | |
| } | |
| break; | |
| default: | |
| doMmuReadError: | |
| panic("need to impl DTB::doMmuRegRead() got asi=%#x, va=%#x\n", | |
| (uint32_t)asi, va); | |
| } | |
| pkt->makeAtomicResponse(); | |
| return Cycles(1); | |
| } | |
| Cycles | |
| TLB::doMmuRegWrite(ThreadContext *tc, Packet *pkt) | |
| { | |
| uint64_t data = pkt->getBE<uint64_t>(); | |
| Addr va = pkt->getAddr(); | |
| ASI asi = (ASI)pkt->req->getArchFlags(); | |
| Addr ta_insert; | |
| Addr va_insert; | |
| Addr ct_insert; | |
| int part_insert; | |
| int entry_insert = -1; | |
| bool real_insert; | |
| bool ignore; | |
| int part_id; | |
| int ctx_id; | |
| PageTableEntry pte; | |
| DPRINTF(IPR, "Memory Mapped IPR Write: asi=%#X a=%#x d=%#X\n", | |
| (uint32_t)asi, va, data); | |
| TLB *itb = dynamic_cast<TLB *>(tc->getITBPtr()); | |
| switch (asi) { | |
| case ASI_LSU_CONTROL_REG: | |
| assert(va == 0); | |
| tc->setMiscReg(MISCREG_MMU_LSU_CTRL, data); | |
| break; | |
| case ASI_MMU: | |
| switch (va) { | |
| case 0x8: | |
| tc->setMiscReg(MISCREG_MMU_P_CONTEXT, data); | |
| break; | |
| case 0x10: | |
| tc->setMiscReg(MISCREG_MMU_S_CONTEXT, data); | |
| break; | |
| default: | |
| goto doMmuWriteError; | |
| } | |
| break; | |
| case ASI_QUEUE: | |
| assert(mbits(data,13,6) == data); | |
| tc->setMiscReg(MISCREG_QUEUE_CPU_MONDO_HEAD + | |
| (va >> 4) - 0x3c, data); | |
| break; | |
| case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| c0_tsb_ps0 = data; | |
| break; | |
| case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| c0_tsb_ps1 = data; | |
| break; | |
| case ASI_DMMU_CTXT_ZERO_CONFIG: | |
| assert(va == 0); | |
| c0_config = data; | |
| break; | |
| case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| itb->c0_tsb_ps0 = data; | |
| break; | |
| case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| itb->c0_tsb_ps1 = data; | |
| break; | |
| case ASI_IMMU_CTXT_ZERO_CONFIG: | |
| assert(va == 0); | |
| itb->c0_config = data; | |
| break; | |
| case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| cx_tsb_ps0 = data; | |
| break; | |
| case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| cx_tsb_ps1 = data; | |
| break; | |
| case ASI_DMMU_CTXT_NONZERO_CONFIG: | |
| assert(va == 0); | |
| cx_config = data; | |
| break; | |
| case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS0: | |
| assert(va == 0); | |
| itb->cx_tsb_ps0 = data; | |
| break; | |
| case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS1: | |
| assert(va == 0); | |
| itb->cx_tsb_ps1 = data; | |
| break; | |
| case ASI_IMMU_CTXT_NONZERO_CONFIG: | |
| assert(va == 0); | |
| itb->cx_config = data; | |
| break; | |
| case ASI_SPARC_ERROR_EN_REG: | |
| case ASI_SPARC_ERROR_STATUS_REG: | |
| inform("Ignoring write to SPARC ERROR regsiter\n"); | |
| break; | |
| case ASI_HYP_SCRATCHPAD: | |
| case ASI_SCRATCHPAD: | |
| tc->setMiscReg(MISCREG_SCRATCHPAD_R0 + (va >> 3), data); | |
| break; | |
| case ASI_IMMU: | |
| switch (va) { | |
| case 0x18: | |
| itb->sfsr = data; | |
| break; | |
| case 0x30: | |
| sext<59>(bits(data, 59,0)); | |
| itb->tag_access = data; | |
| break; | |
| default: | |
| goto doMmuWriteError; | |
| } | |
| break; | |
| case ASI_ITLB_DATA_ACCESS_REG: | |
| entry_insert = bits(va, 8,3); | |
| M5_FALLTHROUGH; | |
| case ASI_ITLB_DATA_IN_REG: | |
| assert(entry_insert != -1 || mbits(va,10,9) == va); | |
| ta_insert = itb->tag_access; | |
| va_insert = mbits(ta_insert, 63,13); | |
| ct_insert = mbits(ta_insert, 12,0); | |
| part_insert = tc->readMiscReg(MISCREG_MMU_PART_ID); | |
| real_insert = bits(va, 9,9); | |
| pte.populate(data, bits(va,10,10) ? PageTableEntry::sun4v : | |
| PageTableEntry::sun4u); | |
| itb->insert(va_insert, part_insert, ct_insert, real_insert, | |
| pte, entry_insert); | |
| break; | |
| case ASI_DTLB_DATA_ACCESS_REG: | |
| entry_insert = bits(va, 8,3); | |
| M5_FALLTHROUGH; | |
| case ASI_DTLB_DATA_IN_REG: | |
| assert(entry_insert != -1 || mbits(va,10,9) == va); | |
| ta_insert = tag_access; | |
| va_insert = mbits(ta_insert, 63,13); | |
| ct_insert = mbits(ta_insert, 12,0); | |
| part_insert = tc->readMiscReg(MISCREG_MMU_PART_ID); | |
| real_insert = bits(va, 9,9); | |
| pte.populate(data, bits(va,10,10) ? PageTableEntry::sun4v : | |
| PageTableEntry::sun4u); | |
| insert(va_insert, part_insert, ct_insert, real_insert, pte, | |
| entry_insert); | |
| break; | |
| case ASI_IMMU_DEMAP: | |
| ignore = false; | |
| ctx_id = -1; | |
| part_id = tc->readMiscReg(MISCREG_MMU_PART_ID); | |
| switch (bits(va,5,4)) { | |
| case 0: | |
| ctx_id = tc->readMiscReg(MISCREG_MMU_P_CONTEXT); | |
| break; | |
| case 1: | |
| ignore = true; | |
| break; | |
| case 3: | |
| ctx_id = 0; | |
| break; | |
| default: | |
| ignore = true; | |
| } | |
| switch (bits(va,7,6)) { | |
| case 0: // demap page | |
| if (!ignore) | |
| itb->demapPage(mbits(va,63,13), part_id, bits(va,9,9), ctx_id); | |
| break; | |
| case 1: // demap context | |
| if (!ignore) | |
| itb->demapContext(part_id, ctx_id); | |
| break; | |
| case 2: | |
| itb->demapAll(part_id); | |
| break; | |
| default: | |
| panic("Invalid type for IMMU demap\n"); | |
| } | |
| break; | |
| case ASI_DMMU: | |
| switch (va) { | |
| case 0x18: | |
| sfsr = data; | |
| break; | |
| case 0x30: | |
| sext<59>(bits(data, 59,0)); | |
| tag_access = data; | |
| break; | |
| case 0x80: | |
| tc->setMiscReg(MISCREG_MMU_PART_ID, data); | |
| break; | |
| default: | |
| goto doMmuWriteError; | |
| } | |
| break; | |
| case ASI_DMMU_DEMAP: | |
| ignore = false; | |
| ctx_id = -1; | |
| part_id = tc->readMiscReg(MISCREG_MMU_PART_ID); | |
| switch (bits(va,5,4)) { | |
| case 0: | |
| ctx_id = tc->readMiscReg(MISCREG_MMU_P_CONTEXT); | |
| break; | |
| case 1: | |
| ctx_id = tc->readMiscReg(MISCREG_MMU_S_CONTEXT); | |
| break; | |
| case 3: | |
| ctx_id = 0; | |
| break; | |
| default: | |
| ignore = true; | |
| } | |
| switch (bits(va,7,6)) { | |
| case 0: // demap page | |
| if (!ignore) | |
| demapPage(mbits(va,63,13), part_id, bits(va,9,9), ctx_id); | |
| break; | |
| case 1: // demap context | |
| if (!ignore) | |
| demapContext(part_id, ctx_id); | |
| break; | |
| case 2: | |
| demapAll(part_id); | |
| break; | |
| default: | |
| panic("Invalid type for IMMU demap\n"); | |
| } | |
| break; | |
| case ASI_SWVR_INTR_RECEIVE: | |
| { | |
| int msb; | |
| // clear all the interrupts that aren't set in the write | |
| SparcISA::Interrupts * interrupts = | |
| dynamic_cast<SparcISA::Interrupts *>( | |
| tc->getCpuPtr()->getInterruptController(0)); | |
| while (interrupts->get_vec(IT_INT_VEC) & data) { | |
| msb = findMsbSet(interrupts->get_vec(IT_INT_VEC) & data); | |
| tc->getCpuPtr()->clearInterrupt(0, IT_INT_VEC, msb); | |
| } | |
| } | |
| break; | |
| case ASI_SWVR_UDB_INTR_W: | |
| tc->getSystemPtr()->threadContexts[bits(data,12,8)]->getCpuPtr()-> | |
| postInterrupt(0, bits(data, 5, 0), 0); | |
| break; | |
| default: | |
| doMmuWriteError: | |
| panic("need to impl DTB::doMmuRegWrite() got asi=%#x, va=%#x d=%#x\n", | |
| (uint32_t)pkt->req->getArchFlags(), pkt->getAddr(), data); | |
| } | |
| pkt->makeAtomicResponse(); | |
| return Cycles(1); | |
| } | |
| void | |
| TLB::GetTsbPtr(ThreadContext *tc, Addr addr, int ctx, Addr *ptrs) | |
| { | |
| uint64_t tag_access = mbits(addr,63,13) | mbits(ctx,12,0); | |
| TLB *itb = dynamic_cast<TLB *>(tc->getITBPtr()); | |
| ptrs[0] = MakeTsbPtr(Ps0, tag_access, | |
| c0_tsb_ps0, | |
| c0_config, | |
| cx_tsb_ps0, | |
| cx_config); | |
| ptrs[1] = MakeTsbPtr(Ps1, tag_access, | |
| c0_tsb_ps1, | |
| c0_config, | |
| cx_tsb_ps1, | |
| cx_config); | |
| ptrs[2] = MakeTsbPtr(Ps0, tag_access, | |
| itb->c0_tsb_ps0, | |
| itb->c0_config, | |
| itb->cx_tsb_ps0, | |
| itb->cx_config); | |
| ptrs[3] = MakeTsbPtr(Ps1, tag_access, | |
| itb->c0_tsb_ps1, | |
| itb->c0_config, | |
| itb->cx_tsb_ps1, | |
| itb->cx_config); | |
| } | |
| uint64_t | |
| TLB::MakeTsbPtr(TsbPageSize ps, uint64_t tag_access, uint64_t c0_tsb, | |
| uint64_t c0_config, uint64_t cX_tsb, uint64_t cX_config) | |
| { | |
| uint64_t tsb; | |
| uint64_t config; | |
| if (bits(tag_access, 12,0) == 0) { | |
| tsb = c0_tsb; | |
| config = c0_config; | |
| } else { | |
| tsb = cX_tsb; | |
| config = cX_config; | |
| } | |
| uint64_t ptr = mbits(tsb,63,13); | |
| bool split = bits(tsb,12,12); | |
| int tsb_size = bits(tsb,3,0); | |
| int page_size = (ps == Ps0) ? bits(config, 2,0) : bits(config,10,8); | |
| if (ps == Ps1 && split) | |
| ptr |= ULL(1) << (13 + tsb_size); | |
| ptr |= (tag_access >> (9 + page_size * 3)) & mask(12+tsb_size, 4); | |
| return ptr; | |
| } | |
| void | |
| TLB::serialize(CheckpointOut &cp) const | |
| { | |
| SERIALIZE_SCALAR(size); | |
| SERIALIZE_SCALAR(usedEntries); | |
| SERIALIZE_SCALAR(lastReplaced); | |
| // convert the pointer based free list into an index based one | |
| std::vector<int> free_list; | |
| for (const TlbEntry *entry : freeList) | |
| free_list.push_back(entry - tlb); | |
| SERIALIZE_CONTAINER(free_list); | |
| SERIALIZE_SCALAR(c0_tsb_ps0); | |
| SERIALIZE_SCALAR(c0_tsb_ps1); | |
| SERIALIZE_SCALAR(c0_config); | |
| SERIALIZE_SCALAR(cx_tsb_ps0); | |
| SERIALIZE_SCALAR(cx_tsb_ps1); | |
| SERIALIZE_SCALAR(cx_config); | |
| SERIALIZE_SCALAR(sfsr); | |
| SERIALIZE_SCALAR(tag_access); | |
| SERIALIZE_SCALAR(sfar); | |
| for (int x = 0; x < size; x++) { | |
| ScopedCheckpointSection sec(cp, csprintf("PTE%d", x)); | |
| tlb[x].serialize(cp); | |
| } | |
| } | |
| void | |
| TLB::unserialize(CheckpointIn &cp) | |
| { | |
| int oldSize; | |
| paramIn(cp, "size", oldSize); | |
| if (oldSize != size) | |
| panic("Don't support unserializing different sized TLBs\n"); | |
| UNSERIALIZE_SCALAR(usedEntries); | |
| UNSERIALIZE_SCALAR(lastReplaced); | |
| std::vector<int> free_list; | |
| UNSERIALIZE_CONTAINER(free_list); | |
| freeList.clear(); | |
| for (int idx : free_list) | |
| freeList.push_back(&tlb[idx]); | |
| UNSERIALIZE_SCALAR(c0_tsb_ps0); | |
| UNSERIALIZE_SCALAR(c0_tsb_ps1); | |
| UNSERIALIZE_SCALAR(c0_config); | |
| UNSERIALIZE_SCALAR(cx_tsb_ps0); | |
| UNSERIALIZE_SCALAR(cx_tsb_ps1); | |
| UNSERIALIZE_SCALAR(cx_config); | |
| UNSERIALIZE_SCALAR(sfsr); | |
| UNSERIALIZE_SCALAR(tag_access); | |
| lookupTable.clear(); | |
| for (int x = 0; x < size; x++) { | |
| ScopedCheckpointSection sec(cp, csprintf("PTE%d", x)); | |
| tlb[x].unserialize(cp); | |
| if (tlb[x].valid) | |
| lookupTable.insert(tlb[x].range, &tlb[x]); | |
| } | |
| UNSERIALIZE_SCALAR(sfar); | |
| } | |
| } // namespace SparcISA | |
| SparcISA::TLB * | |
| SparcTLBParams::create() | |
| { | |
| return new SparcISA::TLB(this); | |
| } |