|  | /* | 
|  | * 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); | 
|  | } |