gem5/src/arch/sparc/tlb.cc
Ali Saidi 139519ef87 Fix bugs in tlbmap (and thus rangemap since the code is nearly identical)
Deal with block initializing stores (by doing nothing, at some point we might want to do the write hint 64 like thing)
Fix tcc instruction igoner in legion-lock stuff to be correct in all cases
Have console interrupts warn rather than panicing until we figure out what to do with interrupts

src/arch/sparc/miscregfile.cc:
src/arch/sparc/miscregfile.hh:
    add a magic miscreg which reads all the bits the tlb needs in one go
src/arch/sparc/tlb.cc:
    initialized the context type and id to reasonable values and handle block init stores
src/arch/sparc/tlb_map.hh:
    fix bug in tlb map code
src/base/range_map.hh:
    fix bug in rangemap code and add range_multimap
    (these are probably useful for bus range stuff)
src/cpu/exetrace.cc:
    fixup tcc ignore code to be correct
src/dev/sparc/t1000.cc:
    make console interrupt stuff warn instead of panicing until we get interrupt stuff figured out
src/unittest/rangemaptest.cc:
    fix up the rangemap unit test to catch the missing case

--HG--
extra : convert_revision : 70604a8b5d0553aa0b0bd7649f775a0cfa8267a5
2006-12-12 17:55:27 -05:00

962 lines
28 KiB
C++

/*
* 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/asi.hh"
#include "arch/sparc/miscregfile.hh"
#include "arch/sparc/tlb.hh"
#include "base/bitfield.hh"
#include "base/trace.hh"
#include "cpu/thread_context.hh"
#include "cpu/base.hh"
#include "mem/packet_access.hh"
#include "mem/request.hh"
#include "sim/builder.hh"
/* @todo remove some of the magic constants. -- ali
* */
namespace SparcISA
{
TLB::TLB(const std::string &name, int s)
: SimObject(name), size(s)
{
// 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];
memset(tlb, 0, sizeof(TlbEntry) * size);
}
void
TLB::clearUsedBits()
{
MapIter i;
for (i = lookupTable.begin(); i != lookupTable.end();) {
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;
int x;
DPRINTF(TLB, "TLB: Inserting TLB Entry; va=%#x pa=%#x pid=%d cid=%d r=%d\n",
va, PTE.paddr(), partition_id, context_id, (int)real);
if (entry != -1) {
assert(entry < size && entry >= 0);
new_entry = &tlb[entry];
} else {
for (x = 0; x < size; x++) {
if (!tlb[x].valid || !tlb[x].used) {
new_entry = &tlb[x];
break;
}
}
}
// Update the last ently if their all locked
if (!new_entry)
new_entry = &tlb[size-1];
assert(PTE.valid());
new_entry->range.va = va;
new_entry->range.size = PTE.size();
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++;
// Demap any entry that conflicts
i = lookupTable.find(new_entry->range);
if (i != lookupTable.end()) {
i->second->valid = false;
if (i->second->used) {
i->second->used = false;
usedEntries--;
}
DPRINTF(TLB, "TLB: Found conflicting entry, deleting it\n");
lookupTable.erase(i);
}
lookupTable.insert(new_entry->range, new_entry);;
// If all entries have there 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)
{
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 = va + MachineBytes;
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());
if (!t->used) {
t->used = true;
usedEntries++;
if (usedEntries == size) {
clearUsedBits();
t->used = true;
usedEntries++;
}
}
return t;
}
void
TLB::dumpAll()
{
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;
// Assemble full address structure
tr.va = va;
tr.size = va + MachineBytes;
tr.contextId = context_id;
tr.partitionId = partition_id;
tr.real = real;
// Demap any entry that conflicts
i = lookupTable.find(tr);
if (i != lookupTable.end()) {
i->second->valid = false;
if (i->second->used) {
i->second->used = false;
usedEntries--;
}
lookupTable.erase(i);
}
}
void
TLB::demapContext(int partition_id, int context_id)
{
int x;
for (x = 0; x < size; x++) {
if (tlb[x].range.contextId == context_id &&
tlb[x].range.partitionId == partition_id) {
tlb[x].valid = false;
if (tlb[x].used) {
tlb[x].used = false;
usedEntries--;
}
lookupTable.erase(tlb[x].range);
}
}
}
void
TLB::demapAll(int partition_id)
{
int x;
for (x = 0; x < size; x++) {
if (!tlb[x].pte.locked() && tlb[x].range.partitionId == partition_id) {
tlb[x].valid = false;
if (tlb[x].used) {
tlb[x].used = false;
usedEntries--;
}
lookupTable.erase(tlb[x].range);
}
}
}
void
TLB::invalidateAll()
{
int x;
for (x = 0; x < size; x++) {
tlb[x].valid = false;
}
usedEntries = 0;
}
uint64_t
TLB::TteRead(int entry) {
assert(entry < size);
return tlb[entry].pte();
}
uint64_t
TLB::TagRead(int entry) {
assert(entry < size);
uint64_t tag;
tag = tlb[entry].range.contextId | tlb[entry].range.va |
(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(ThreadContext *tc, int reg, bool write, ContextType ct,
bool se, FaultTypes ft, int asi)
{
uint64_t sfsr;
sfsr = tc->readMiscReg(reg);
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;
tc->setMiscRegWithEffect(reg, sfsr);
}
void
TLB::writeTagAccess(ThreadContext *tc, int reg, Addr va, int context)
{
tc->setMiscRegWithEffect(reg, mbits(va, 63,13) | mbits(context,12,0));
}
void
ITB::writeSfsr(ThreadContext *tc, bool write, ContextType ct,
bool se, FaultTypes ft, int asi)
{
DPRINTF(TLB, "TLB: ITB Fault: w=%d ct=%d ft=%d asi=%d\n",
(int)write, ct, ft, asi);
TLB::writeSfsr(tc, MISCREG_MMU_ITLB_SFSR, write, ct, se, ft, asi);
}
void
ITB::writeTagAccess(ThreadContext *tc, Addr va, int context)
{
TLB::writeTagAccess(tc, MISCREG_MMU_ITLB_TAG_ACCESS, va, context);
}
void
DTB::writeSfr(ThreadContext *tc, Addr a, bool write, ContextType ct,
bool se, FaultTypes ft, int asi)
{
DPRINTF(TLB, "TLB: DTB Fault: A=%#x w=%d ct=%d ft=%d asi=%d\n",
a, (int)write, ct, ft, asi);
TLB::writeSfsr(tc, MISCREG_MMU_DTLB_SFSR, write, ct, se, ft, asi);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_SFAR, a);
}
void
DTB::writeTagAccess(ThreadContext *tc, Addr va, int context)
{
TLB::writeTagAccess(tc, MISCREG_MMU_DTLB_TAG_ACCESS, va, context);
}
Fault
ITB::translate(RequestPtr &req, ThreadContext *tc)
{
uint64_t hpstate = tc->readMiscReg(MISCREG_HPSTATE);
uint64_t pstate = tc->readMiscReg(MISCREG_PSTATE);
bool lsuIm = tc->readMiscReg(MISCREG_MMU_LSU_CTRL) >> 2 & 0x1;
uint64_t tl = tc->readMiscReg(MISCREG_TL);
uint64_t part_id = tc->readMiscReg(MISCREG_MMU_PART_ID);
bool addr_mask = pstate >> 3 & 0x1;
bool priv = pstate >> 2 & 0x1;
Addr vaddr = req->getVaddr();
int context;
ContextType ct;
int asi;
bool real = false;
TlbEntry *e;
DPRINTF(TLB, "TLB: ITB Request to translate va=%#x size=%d\n",
vaddr, req->getSize());
DPRINTF(TLB, "TLB: pstate: %#X hpstate: %#X lsudm: %#X part_id: %#X\n",
pstate, hpstate, lsuIm, part_id);
assert(req->getAsi() == ASI_IMPLICIT);
if (tl > 0) {
asi = ASI_N;
ct = Nucleus;
context = 0;
} else {
asi = ASI_P;
ct = Primary;
context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
}
if ( hpstate >> 2 & 0x1 || hpstate >> 5 & 0x1 ) {
req->setPaddr(req->getVaddr() & PAddrImplMask);
return NoFault;
}
// If the asi is unaligned trap
if (vaddr & req->getSize()-1) {
writeSfsr(tc, false, ct, false, OtherFault, asi);
return new MemAddressNotAligned;
}
if (addr_mask)
vaddr = vaddr & VAddrAMask;
if (!validVirtualAddress(vaddr, addr_mask)) {
writeSfsr(tc, false, ct, false, VaOutOfRange, asi);
return new InstructionAccessException;
}
if (!lsuIm) {
e = lookup(req->getVaddr(), part_id, true);
real = true;
context = 0;
} else {
e = lookup(vaddr, part_id, false, context);
}
if (e == NULL || !e->valid) {
tc->setMiscReg(MISCREG_MMU_ITLB_TAG_ACCESS,
vaddr & ~BytesInPageMask | context);
if (real)
return new InstructionRealTranslationMiss;
else
return new FastInstructionAccessMMUMiss;
}
// were not priviledged accesing priv page
if (!priv && e->pte.priv()) {
writeSfsr(tc, false, ct, false, PrivViolation, asi);
return new InstructionAccessException;
}
req->setPaddr(e->pte.paddr() & ~(e->pte.size()-1) |
req->getVaddr() & e->pte.size()-1 );
DPRINTF(TLB, "TLB: %#X -> %#X\n", req->getVaddr(), req->getPaddr());
return NoFault;
}
Fault
DTB::translate(RequestPtr &req, ThreadContext *tc, bool write)
{
/* @todo this could really use some profiling and fixing to make it faster! */
uint64_t hpstate = tc->readMiscReg(MISCREG_HPSTATE);
uint64_t pstate = tc->readMiscReg(MISCREG_PSTATE);
bool lsuDm = tc->readMiscReg(MISCREG_MMU_LSU_CTRL) >> 3 & 0x1;
uint64_t tl = tc->readMiscReg(MISCREG_TL);
uint64_t part_id = tc->readMiscReg(MISCREG_MMU_PART_ID);
bool hpriv = hpstate >> 2 & 0x1;
bool red = hpstate >> 5 >> 0x1;
bool addr_mask = pstate >> 3 & 0x1;
bool priv = pstate >> 2 & 0x1;
bool implicit = false;
bool real = false;
Addr vaddr = req->getVaddr();
Addr size = req->getSize();
ContextType ct = Primary;
int context = 0;
ASI asi;
TlbEntry *e;
asi = (ASI)req->getAsi();
DPRINTF(TLB, "TLB: DTB Request to translate va=%#x size=%d asi=%#x\n",
vaddr, size, asi);
DPRINTF(TLB, "TLB: pstate: %#X hpstate: %#X lsudm: %#X part_id: %#X\n",
pstate, hpstate, lsuDm, part_id);
if (asi == ASI_IMPLICIT)
implicit = true;
if (implicit) {
if (tl > 0) {
asi = ASI_N;
ct = Nucleus;
context = 0;
} else {
asi = ASI_P;
ct = Primary;
context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
}
} else if (!hpriv && !red) {
if (tl > 0 || AsiIsNucleus(asi)) {
ct = Nucleus;
context = 0;
} else if (AsiIsSecondary(asi)) {
ct = Secondary;
context = tc->readMiscReg(MISCREG_MMU_S_CONTEXT);
} else {
context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
ct = Primary; //???
}
// We need to check for priv level/asi priv
if (!priv && !AsiIsUnPriv(asi)) {
// It appears that context should be Nucleus in these cases?
writeSfr(tc, vaddr, write, Nucleus, false, IllegalAsi, asi);
return new PrivilegedAction;
}
if (priv && AsiIsHPriv(asi)) {
writeSfr(tc, vaddr, write, Nucleus, false, IllegalAsi, asi);
return new DataAccessException;
}
} else if (hpriv) {
if (asi == ASI_P) {
ct = Primary;
context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
goto continueDtbFlow;
}
}
if (!implicit) {
if (AsiIsLittle(asi))
panic("Little Endian ASIs not supported\n");
if (AsiIsBlock(asi))
panic("Block ASIs not supported\n");
if (AsiIsNoFault(asi))
panic("No Fault ASIs not supported\n");
if (write && asi == ASI_LDTX_P)
// block init store (like write hint64)
goto continueDtbFlow;
if (AsiIsTwin(asi))
panic("Twin ASIs not supported\n");
if (AsiIsPartialStore(asi))
panic("Partial Store ASIs not supported\n");
if (AsiIsInterrupt(asi))
panic("Interrupt ASIs not supported\n");
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))
panic("Accessing ASI %#X. Should we?\n", asi);
}
continueDtbFlow:
// If the asi is unaligned trap
if (vaddr & size-1) {
writeSfr(tc, vaddr, false, ct, false, OtherFault, asi);
return new MemAddressNotAligned;
}
if (addr_mask)
vaddr = vaddr & VAddrAMask;
if (!validVirtualAddress(vaddr, addr_mask)) {
writeSfr(tc, vaddr, false, ct, true, VaOutOfRange, asi);
return new DataAccessException;
}
if ((!lsuDm && !hpriv) || AsiIsReal(asi)) {
real = true;
context = 0;
};
if (hpriv && (implicit || (!AsiIsAsIfUser(asi) && !AsiIsReal(asi)))) {
req->setPaddr(req->getVaddr() & PAddrImplMask);
return NoFault;
}
e = lookup(req->getVaddr(), part_id, real, context);
if (e == NULL || !e->valid) {
tc->setMiscReg(MISCREG_MMU_DTLB_TAG_ACCESS,
vaddr & ~BytesInPageMask | context);
DPRINTF(TLB, "TLB: DTB Failed to find matching TLB entry\n");
if (real)
return new DataRealTranslationMiss;
else
return new FastDataAccessMMUMiss;
}
if (write && !e->pte.writable()) {
writeSfr(tc, vaddr, write, ct, e->pte.sideffect(), OtherFault, asi);
return new FastDataAccessProtection;
}
if (e->pte.nofault() && !AsiIsNoFault(asi)) {
writeSfr(tc, vaddr, write, ct, e->pte.sideffect(), LoadFromNfo, asi);
return new DataAccessException;
}
if (e->pte.sideffect())
req->setFlags(req->getFlags() | UNCACHEABLE);
if (!priv && e->pte.priv()) {
writeSfr(tc, vaddr, write, ct, e->pte.sideffect(), PrivViolation, asi);
return new DataAccessException;
}
req->setPaddr(e->pte.paddr() & ~(e->pte.size()-1) |
req->getVaddr() & e->pte.size()-1);
DPRINTF(TLB, "TLB: %#X -> %#X\n", req->getVaddr(), req->getPaddr());
return NoFault;
/** Normal flow ends here. */
handleScratchRegAccess:
if (vaddr > 0x38 || (vaddr >= 0x20 && vaddr < 0x30 && !hpriv)) {
writeSfr(tc, vaddr, write, Primary, true, IllegalAsi, asi);
return new DataAccessException;
}
goto regAccessOk;
handleQueueRegAccess:
if (!priv && !hpriv) {
writeSfr(tc, vaddr, write, Primary, true, IllegalAsi, asi);
return new PrivilegedAction;
}
if (priv && vaddr & 0xF || vaddr > 0x3f8 || vaddr < 0x3c0) {
writeSfr(tc, vaddr, write, Primary, true, IllegalAsi, asi);
return new DataAccessException;
}
goto regAccessOk;
handleSparcErrorRegAccess:
if (!hpriv) {
if (priv) {
writeSfr(tc, vaddr, write, Primary, true, IllegalAsi, asi);
return new DataAccessException;
} else {
writeSfr(tc, vaddr, write, Primary, true, IllegalAsi, asi);
return new PrivilegedAction;
}
}
goto regAccessOk;
regAccessOk:
handleMmuRegAccess:
DPRINTF(TLB, "TLB: DTB Translating MM IPR access\n");
req->setMmapedIpr(true);
req->setPaddr(req->getVaddr());
return NoFault;
};
Tick
DTB::doMmuRegRead(ThreadContext *tc, Packet *pkt)
{
Addr va = pkt->getAddr();
ASI asi = (ASI)pkt->req->getAsi();
DPRINTF(IPR, "Memory Mapped IPR Read: asi=%#X a=%#x\n",
(uint32_t)pkt->req->getAsi(), pkt->getAddr());
switch (asi) {
case ASI_LSU_CONTROL_REG:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_LSU_CTRL));
break;
case ASI_MMU:
switch (va) {
case 0x8:
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_P_CONTEXT));
break;
case 0x10:
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_S_CONTEXT));
break;
default:
goto doMmuReadError;
}
break;
case ASI_QUEUE:
pkt->set(tc->readMiscRegWithEffect(MISCREG_QUEUE_CPU_MONDO_HEAD +
(va >> 4) - 0x3c));
break;
case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS0:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_C0_TSB_PS0));
break;
case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS1:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_C0_TSB_PS1));
break;
case ASI_DMMU_CTXT_ZERO_CONFIG:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_C0_CONFIG));
break;
case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS0:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_C0_TSB_PS0));
break;
case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS1:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_C0_TSB_PS1));
break;
case ASI_IMMU_CTXT_ZERO_CONFIG:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_C0_CONFIG));
break;
case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS0:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_CX_TSB_PS0));
break;
case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS1:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_CX_TSB_PS1));
break;
case ASI_DMMU_CTXT_NONZERO_CONFIG:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_CX_CONFIG));
break;
case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS0:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_CX_TSB_PS0));
break;
case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS1:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_CX_TSB_PS1));
break;
case ASI_IMMU_CTXT_NONZERO_CONFIG:
assert(va == 0);
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_CX_CONFIG));
break;
case ASI_SPARC_ERROR_STATUS_REG:
warn("returning 0 for SPARC ERROR regsiter read\n");
pkt->set(0);
break;
case ASI_HYP_SCRATCHPAD:
case ASI_SCRATCHPAD:
pkt->set(tc->readMiscRegWithEffect(MISCREG_SCRATCHPAD_R0 + (va >> 3)));
break;
case ASI_IMMU:
switch (va) {
case 0x30:
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_TAG_ACCESS));
break;
default:
goto doMmuReadError;
}
break;
case ASI_DMMU:
switch (va) {
case 0x30:
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_TAG_ACCESS));
break;
case 0x80:
pkt->set(tc->readMiscRegWithEffect(MISCREG_MMU_PART_ID));
break;
default:
goto doMmuReadError;
}
break;
default:
doMmuReadError:
panic("need to impl DTB::doMmuRegRead() got asi=%#x, va=%#x\n",
(uint32_t)asi, va);
}
pkt->result = Packet::Success;
return tc->getCpuPtr()->cycles(1);
}
Tick
DTB::doMmuRegWrite(ThreadContext *tc, Packet *pkt)
{
uint64_t data = gtoh(pkt->get<uint64_t>());
Addr va = pkt->getAddr();
ASI asi = (ASI)pkt->req->getAsi();
Addr ta_insert;
Addr va_insert;
Addr ct_insert;
int part_insert;
int entry_insert = -1;
bool real_insert;
PageTableEntry pte;
DPRINTF(IPR, "Memory Mapped IPR Write: asi=%#X a=%#x d=%#X\n",
(uint32_t)asi, va, data);
switch (asi) {
case ASI_LSU_CONTROL_REG:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_LSU_CTRL, data);
break;
case ASI_MMU:
switch (va) {
case 0x8:
tc->setMiscRegWithEffect(MISCREG_MMU_P_CONTEXT, data);
break;
case 0x10:
tc->setMiscRegWithEffect(MISCREG_MMU_S_CONTEXT, data);
break;
default:
goto doMmuWriteError;
}
break;
case ASI_QUEUE:
assert(mbits(data,13,6) == data);
tc->setMiscRegWithEffect(MISCREG_QUEUE_CPU_MONDO_HEAD +
(va >> 4) - 0x3c, data);
break;
case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS0:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_C0_TSB_PS0, data);
break;
case ASI_DMMU_CTXT_ZERO_TSB_BASE_PS1:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_C0_TSB_PS1, data);
break;
case ASI_DMMU_CTXT_ZERO_CONFIG:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_C0_CONFIG, data);
break;
case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS0:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_C0_TSB_PS0, data);
break;
case ASI_IMMU_CTXT_ZERO_TSB_BASE_PS1:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_C0_TSB_PS1, data);
break;
case ASI_IMMU_CTXT_ZERO_CONFIG:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_C0_CONFIG, data);
break;
case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS0:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_CX_TSB_PS0, data);
break;
case ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS1:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_CX_TSB_PS1, data);
break;
case ASI_DMMU_CTXT_NONZERO_CONFIG:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_CX_CONFIG, data);
break;
case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS0:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_CX_TSB_PS0, data);
break;
case ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS1:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_CX_TSB_PS1, data);
break;
case ASI_IMMU_CTXT_NONZERO_CONFIG:
assert(va == 0);
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_CX_CONFIG, data);
break;
case ASI_SPARC_ERROR_EN_REG:
case ASI_SPARC_ERROR_STATUS_REG:
warn("Ignoring write to SPARC ERROR regsiter\n");
break;
case ASI_HYP_SCRATCHPAD:
case ASI_SCRATCHPAD:
tc->setMiscRegWithEffect(MISCREG_SCRATCHPAD_R0 + (va >> 3), data);
break;
case ASI_IMMU:
switch (va) {
case 0x30:
tc->setMiscRegWithEffect(MISCREG_MMU_ITLB_TAG_ACCESS, data);
break;
default:
goto doMmuWriteError;
}
break;
case ASI_ITLB_DATA_ACCESS_REG:
entry_insert = bits(va, 8,3);
case ASI_ITLB_DATA_IN_REG:
assert(entry_insert != -1 || mbits(va,10,9) == va);
ta_insert = tc->readMiscRegWithEffect(MISCREG_MMU_ITLB_TAG_ACCESS);
va_insert = mbits(ta_insert, 63,13);
ct_insert = mbits(ta_insert, 12,0);
part_insert = tc->readMiscRegWithEffect(MISCREG_MMU_PART_ID);
real_insert = bits(va, 9,9);
pte.populate(data, bits(va,10,10) ? PageTableEntry::sun4v :
PageTableEntry::sun4u);
tc->getITBPtr()->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);
case ASI_DTLB_DATA_IN_REG:
assert(entry_insert != -1 || mbits(va,10,9) == va);
ta_insert = tc->readMiscRegWithEffect(MISCREG_MMU_DTLB_TAG_ACCESS);
va_insert = mbits(ta_insert, 63,13);
ct_insert = mbits(ta_insert, 12,0);
part_insert = tc->readMiscRegWithEffect(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_DMMU:
switch (va) {
case 0x30:
tc->setMiscRegWithEffect(MISCREG_MMU_DTLB_TAG_ACCESS, data);
break;
case 0x80:
tc->setMiscRegWithEffect(MISCREG_MMU_PART_ID, data);
break;
default:
goto doMmuWriteError;
}
break;
default:
doMmuWriteError:
panic("need to impl DTB::doMmuRegWrite() got asi=%#x, va=%#x d=%#x\n",
(uint32_t)pkt->req->getAsi(), pkt->getAddr(), data);
}
pkt->result = Packet::Success;
return tc->getCpuPtr()->cycles(1);
}
void
TLB::serialize(std::ostream &os)
{
panic("Need to implement serialize tlb for SPARC\n");
}
void
TLB::unserialize(Checkpoint *cp, const std::string &section)
{
panic("Need to implement unserialize tlb for SPARC\n");
}
DEFINE_SIM_OBJECT_CLASS_NAME("SparcTLB", TLB)
BEGIN_DECLARE_SIM_OBJECT_PARAMS(ITB)
Param<int> size;
END_DECLARE_SIM_OBJECT_PARAMS(ITB)
BEGIN_INIT_SIM_OBJECT_PARAMS(ITB)
INIT_PARAM_DFLT(size, "TLB size", 48)
END_INIT_SIM_OBJECT_PARAMS(ITB)
CREATE_SIM_OBJECT(ITB)
{
return new ITB(getInstanceName(), size);
}
REGISTER_SIM_OBJECT("SparcITB", ITB)
BEGIN_DECLARE_SIM_OBJECT_PARAMS(DTB)
Param<int> size;
END_DECLARE_SIM_OBJECT_PARAMS(DTB)
BEGIN_INIT_SIM_OBJECT_PARAMS(DTB)
INIT_PARAM_DFLT(size, "TLB size", 64)
END_INIT_SIM_OBJECT_PARAMS(DTB)
CREATE_SIM_OBJECT(DTB)
{
return new DTB(getInstanceName(), size);
}
REGISTER_SIM_OBJECT("SparcDTB", DTB)
}