#include "arch/sparc/faults.hh"
#include "arch/sparc/isa_traits.hh"
+#include "arch/sparc/types.hh"
#include "base/bitfield.hh"
#include "base/trace.hh"
#include "config/full_system.hh"
template<> SparcFaultBase::FaultVals
SparcFault<InterruptLevelN>::vals =
- {"interrupt_level_n", 0x041, 0, {P, P, SH}};
+ {"interrupt_level_n", 0x040, 0, {P, P, SH}};
template<> SparcFaultBase::FaultVals
SparcFault<HstickMatch>::vals =
SparcFault<TrapLevelZero>::vals =
{"trap_level_zero", 0x05F, 202, {H, H, SH}};
+template<> SparcFaultBase::FaultVals
+ SparcFault<InterruptVector>::vals =
+ {"interrupt_vector", 0x060, 2630, {H, H, H}};
+
template<> SparcFaultBase::FaultVals
SparcFault<PAWatchpoint>::vals =
{"PA_watchpoint", 0x061, 1209, {H, H, H}};
{"dev_mondo", 0x07D, 1611, {P, P, SH}};
template<> SparcFaultBase::FaultVals
- SparcFault<ResumeableError>::vals =
+ SparcFault<ResumableError>::vals =
{"resume_error", 0x07E, 3330, {P, P, SH}};
template<> SparcFaultBase::FaultVals
SparcFault<TrapInstruction>::vals =
{"trap_instruction", 0x100, 1602, {P, P, H}};
-#if !FULL_SYSTEM
-template<> SparcFaultBase::FaultVals
- SparcFault<PageTableFault>::vals =
- {"page_table_fault", 0x0000, 0, {SH, SH, SH}};
-#endif
+/**
+ * This causes the thread context to enter RED state. This causes the side
+ * effects which go with entering RED state because of a trap.
+ */
+
+void enterREDState(ThreadContext *tc)
+{
+ //@todo Disable the mmu?
+ //@todo Disable watchpoints?
+ MiscReg HPSTATE = tc->readMiscRegNoEffect(MISCREG_HPSTATE);
+ //HPSTATE.red = 1
+ HPSTATE |= (1 << 5);
+ //HPSTATE.hpriv = 1
+ HPSTATE |= (1 << 2);
+ tc->setMiscReg(MISCREG_HPSTATE, HPSTATE);
+ //PSTATE.priv is set to 1 here. The manual says it should be 0, but
+ //Legion sets it to 1.
+ MiscReg PSTATE = tc->readMiscRegNoEffect(MISCREG_PSTATE);
+ PSTATE |= (1 << 2);
+ tc->setMiscReg(MISCREG_PSTATE, PSTATE);
+}
+
+/**
+ * This sets everything up for a RED state trap except for actually jumping to
+ * the handler.
+ */
+
+void doREDFault(ThreadContext *tc, TrapType tt)
+{
+ MiscReg TL = tc->readMiscRegNoEffect(MISCREG_TL);
+ MiscReg TSTATE = tc->readMiscRegNoEffect(MISCREG_TSTATE);
+ MiscReg PSTATE = tc->readMiscRegNoEffect(MISCREG_PSTATE);
+ MiscReg HPSTATE = tc->readMiscRegNoEffect(MISCREG_HPSTATE);
+ //MiscReg CCR = tc->readMiscRegNoEffect(MISCREG_CCR);
+ MiscReg CCR = tc->readIntReg(NumIntArchRegs + 2);
+ MiscReg ASI = tc->readMiscRegNoEffect(MISCREG_ASI);
+ MiscReg CWP = tc->readMiscRegNoEffect(MISCREG_CWP);
+ //MiscReg CANSAVE = tc->readMiscRegNoEffect(MISCREG_CANSAVE);
+ MiscReg CANSAVE = tc->readMiscRegNoEffect(NumIntArchRegs + 3);
+ MiscReg GL = tc->readMiscRegNoEffect(MISCREG_GL);
+ MiscReg PC = tc->readPC();
+ MiscReg NPC = tc->readNextPC();
+
+ TL++;
+
+ if (bits(PSTATE, 3,3)) {
+ PC &= mask(32);
+ NPC &= mask(32);
+ }
+
+ //set TSTATE.gl to gl
+ replaceBits(TSTATE, 42, 40, GL);
+ //set TSTATE.ccr to ccr
+ replaceBits(TSTATE, 39, 32, CCR);
+ //set TSTATE.asi to asi
+ replaceBits(TSTATE, 31, 24, ASI);
+ //set TSTATE.pstate to pstate
+ replaceBits(TSTATE, 20, 8, PSTATE);
+ //set TSTATE.cwp to cwp
+ replaceBits(TSTATE, 4, 0, CWP);
+
+ //Write back TSTATE
+ tc->setMiscRegNoEffect(MISCREG_TSTATE, TSTATE);
+
+ //set TPC to PC
+ tc->setMiscRegNoEffect(MISCREG_TPC, PC);
+ //set TNPC to NPC
+ tc->setMiscRegNoEffect(MISCREG_TNPC, NPC);
+
+ //set HTSTATE.hpstate to hpstate
+ tc->setMiscRegNoEffect(MISCREG_HTSTATE, HPSTATE);
+
+ //TT = trap type;
+ tc->setMiscRegNoEffect(MISCREG_TT, tt);
+
+ //Update GL
+ tc->setMiscReg(MISCREG_GL, min<int>(GL+1, MaxGL));
+
+ PSTATE = mbits(PSTATE, 2, 2); // just save the priv bit
+ PSTATE |= (1 << 4); //set PSTATE.pef to 1
+ tc->setMiscRegNoEffect(MISCREG_PSTATE, PSTATE);
+
+ //set HPSTATE.red to 1
+ HPSTATE |= (1 << 5);
+ //set HPSTATE.hpriv to 1
+ HPSTATE |= (1 << 2);
+ //set HPSTATE.ibe to 0
+ HPSTATE &= ~(1 << 10);
+ //set HPSTATE.tlz to 0
+ HPSTATE &= ~(1 << 0);
+ tc->setMiscRegNoEffect(MISCREG_HPSTATE, HPSTATE);
+
+ bool changedCWP = true;
+ if(tt == 0x24)
+ CWP++;
+ else if(0x80 <= tt && tt <= 0xbf)
+ CWP += (CANSAVE + 2);
+ else if(0xc0 <= tt && tt <= 0xff)
+ CWP--;
+ else
+ changedCWP = false;
+
+ if(changedCWP)
+ {
+ CWP = (CWP + NWindows) % NWindows;
+ tc->setMiscReg(MISCREG_CWP, CWP);
+ }
+}
/**
* This sets everything up for a normal trap except for actually jumping to
- * the handler. It will need to be expanded to include the state machine in
- * the manual. Right now it assumes that traps will always be to the
- * privileged level.
+ * the handler.
*/
-void doNormalFault(ThreadContext *tc, TrapType tt)
+void doNormalFault(ThreadContext *tc, TrapType tt, bool gotoHpriv)
{
- uint64_t TL = tc->readMiscReg(MISCREG_TL);
- uint64_t TSTATE = tc->readMiscReg(MISCREG_TSTATE);
- uint64_t PSTATE = tc->readMiscReg(MISCREG_PSTATE);
- uint64_t HPSTATE = tc->readMiscReg(MISCREG_HPSTATE);
- uint64_t CCR = tc->readMiscReg(MISCREG_CCR);
- uint64_t ASI = tc->readMiscReg(MISCREG_ASI);
- uint64_t CWP = tc->readMiscReg(MISCREG_CWP);
- uint64_t CANSAVE = tc->readMiscReg(MISCREG_CANSAVE);
- uint64_t GL = tc->readMiscReg(MISCREG_GL);
- uint64_t PC = tc->readPC();
- uint64_t NPC = tc->readNextPC();
+ MiscReg TL = tc->readMiscRegNoEffect(MISCREG_TL);
+ MiscReg TSTATE = tc->readMiscRegNoEffect(MISCREG_TSTATE);
+ MiscReg PSTATE = tc->readMiscRegNoEffect(MISCREG_PSTATE);
+ MiscReg HPSTATE = tc->readMiscRegNoEffect(MISCREG_HPSTATE);
+ //MiscReg CCR = tc->readMiscRegNoEffect(MISCREG_CCR);
+ MiscReg CCR = tc->readIntReg(NumIntArchRegs + 2);
+ MiscReg ASI = tc->readMiscRegNoEffect(MISCREG_ASI);
+ MiscReg CWP = tc->readMiscRegNoEffect(MISCREG_CWP);
+ //MiscReg CANSAVE = tc->readMiscRegNoEffect(MISCREG_CANSAVE);
+ MiscReg CANSAVE = tc->readIntReg(NumIntArchRegs + 3);
+ MiscReg GL = tc->readMiscRegNoEffect(MISCREG_GL);
+ MiscReg PC = tc->readPC();
+ MiscReg NPC = tc->readNextPC();
+
+ if (bits(PSTATE, 3,3)) {
+ PC &= mask(32);
+ NPC &= mask(32);
+ }
//Increment the trap level
TL++;
- tc->setMiscReg(MISCREG_TL, TL);
+ tc->setMiscRegNoEffect(MISCREG_TL, TL);
//Save off state
replaceBits(TSTATE, 4, 0, CWP);
//Write back TSTATE
- tc->setMiscReg(MISCREG_TSTATE, TSTATE);
+ tc->setMiscRegNoEffect(MISCREG_TSTATE, TSTATE);
//set TPC to PC
- tc->setMiscReg(MISCREG_TPC, PC);
+ tc->setMiscRegNoEffect(MISCREG_TPC, PC);
//set TNPC to NPC
- tc->setMiscReg(MISCREG_TNPC, NPC);
+ tc->setMiscRegNoEffect(MISCREG_TNPC, NPC);
//set HTSTATE.hpstate to hpstate
- tc->setMiscReg(MISCREG_HTSTATE, HPSTATE);
+ tc->setMiscRegNoEffect(MISCREG_HTSTATE, HPSTATE);
//TT = trap type;
- tc->setMiscReg(MISCREG_TT, tt);
+ tc->setMiscRegNoEffect(MISCREG_TT, tt);
//Update the global register level
- if(1/*We're delivering the trap in priveleged mode*/)
- tc->setMiscReg(MISCREG_GL, min<int>(GL+1, MaxGL));
- else
+ if (!gotoHpriv)
tc->setMiscReg(MISCREG_GL, min<int>(GL+1, MaxPGL));
+ else
+ tc->setMiscReg(MISCREG_GL, min<int>(GL+1, MaxGL));
//PSTATE.mm is unchanged
- //PSTATE.pef = whether or not an fpu is present
- //XXX We'll say there's one present, even though there aren't
- //implementations for a decent number of the instructions
- PSTATE |= (1 << 4);
- //PSTATE.am = 0
- PSTATE &= ~(1 << 3);
- if(1/*We're delivering the trap in priveleged mode*/)
- {
- //PSTATE.priv = 1
- PSTATE |= (1 << 2);
- //PSTATE.cle = PSTATE.tle
- replaceBits(PSTATE, 9, 9, PSTATE >> 8);
- }
- else
- {
- //PSTATE.priv = 0
- PSTATE &= ~(1 << 2);
- //PSTATE.cle = 0
- PSTATE &= ~(1 << 9);
- }
- //PSTATE.ie = 0
- PSTATE &= ~(1 << 1);
+ PSTATE |= (1 << 4); //PSTATE.pef = whether or not an fpu is present
+ PSTATE &= ~(1 << 3); //PSTATE.am = 0
+ PSTATE &= ~(1 << 1); //PSTATE.ie = 0
//PSTATE.tle is unchanged
//PSTATE.tct = 0
- //XXX Where exactly is this field?
- tc->setMiscReg(MISCREG_PSTATE, PSTATE);
- if(0/*We're delivering the trap in hyperprivileged mode*/)
+ if (gotoHpriv)
{
- //HPSTATE.red = 0
- HPSTATE &= ~(1 << 5);
- //HPSTATE.hpriv = 1
- HPSTATE |= (1 << 2);
- //HPSTATE.ibe = 0
- HPSTATE &= ~(1 << 10);
+ PSTATE &= ~(1 << 9); // PSTATE.cle = 0
+ //The manual says PSTATE.priv should be 0, but Legion leaves it alone
+ HPSTATE &= ~(1 << 5); //HPSTATE.red = 0
+ HPSTATE |= (1 << 2); //HPSTATE.hpriv = 1
+ HPSTATE &= ~(1 << 10); //HPSTATE.ibe = 0
//HPSTATE.tlz is unchanged
- tc->setMiscReg(MISCREG_HPSTATE, HPSTATE);
+ tc->setMiscRegNoEffect(MISCREG_HPSTATE, HPSTATE);
+ } else { // we are going to priv
+ PSTATE |= (1 << 2); //PSTATE.priv = 1
+ replaceBits(PSTATE, 9, 9, PSTATE >> 8); //PSTATE.cle = PSTATE.tle
}
+ tc->setMiscRegNoEffect(MISCREG_PSTATE, PSTATE);
+
bool changedCWP = true;
- if(tt == 0x24)
+ if (tt == 0x24)
CWP++;
- else if(0x80 <= tt && tt <= 0xbf)
+ else if (0x80 <= tt && tt <= 0xbf)
CWP += (CANSAVE + 2);
- else if(0xc0 <= tt && tt <= 0xff)
+ else if (0xc0 <= tt && tt <= 0xff)
CWP--;
else
changedCWP = false;
- if(changedCWP)
+ if (changedCWP)
{
CWP = (CWP + NWindows) % NWindows;
- tc->setMiscRegWithEffect(MISCREG_CWP, CWP);
+ tc->setMiscReg(MISCREG_CWP, CWP);
}
}
+void getREDVector(MiscReg TT, Addr & PC, Addr & NPC)
+{
+ //XXX The following constant might belong in a header file.
+ const Addr RSTVAddr = 0xFFF0000000ULL;
+ PC = RSTVAddr | ((TT << 5) & 0xFF);
+ NPC = PC + sizeof(MachInst);
+}
+
+void getHyperVector(ThreadContext * tc, Addr & PC, Addr & NPC, MiscReg TT)
+{
+ Addr HTBA = tc->readMiscRegNoEffect(MISCREG_HTBA);
+ PC = (HTBA & ~mask(14)) | ((TT << 5) & mask(14));
+ NPC = PC + sizeof(MachInst);
+}
+
+void getPrivVector(ThreadContext * tc, Addr & PC, Addr & NPC, MiscReg TT, MiscReg TL)
+{
+ Addr TBA = tc->readMiscRegNoEffect(MISCREG_TBA);
+ PC = (TBA & ~mask(15)) |
+ (TL > 1 ? (1 << 14) : 0) |
+ ((TT << 5) & mask(14));
+ NPC = PC + sizeof(MachInst);
+}
+
#if FULL_SYSTEM
void SparcFaultBase::invoke(ThreadContext * tc)
{
+ //panic("Invoking a second fault!\n");
FaultBase::invoke(tc);
countStat()++;
- //Use the SPARC trap state machine
+ //We can refer to this to see what the trap level -was-, but something
+ //in the middle could change it in the regfile out from under us.
+ MiscReg tl = tc->readMiscRegNoEffect(MISCREG_TL);
+ MiscReg tt = tc->readMiscRegNoEffect(MISCREG_TT);
+ MiscReg pstate = tc->readMiscRegNoEffect(MISCREG_PSTATE);
+ MiscReg hpstate = tc->readMiscRegNoEffect(MISCREG_HPSTATE);
+
+ Addr PC, NPC;
+
+ PrivilegeLevel current;
+ if (hpstate & HPSTATE::hpriv)
+ current = Hyperprivileged;
+ else if (pstate & PSTATE::priv)
+ current = Privileged;
+ else
+ current = User;
+
+ PrivilegeLevel level = getNextLevel(current);
+
+ if ((hpstate & HPSTATE::red) || (tl == MaxTL - 1)) {
+ getREDVector(5, PC, NPC);
+ doREDFault(tc, tt);
+ //This changes the hpstate and pstate, so we need to make sure we
+ //save the old version on the trap stack in doREDFault.
+ enterREDState(tc);
+ } else if (tl == MaxTL) {
+ panic("Should go to error state here.. crap\n");
+ //Do error_state somehow?
+ //Probably inject a WDR fault using the interrupt mechanism.
+ //What should the PC and NPC be set to?
+ } else if (tl > MaxPTL && level == Privileged) {
+ //guest_watchdog fault
+ doNormalFault(tc, trapType(), true);
+ getHyperVector(tc, PC, NPC, 2);
+ } else if (level == Hyperprivileged ||
+ (level == Privileged && trapType() >= 384)) {
+ doNormalFault(tc, trapType(), true);
+ getHyperVector(tc, PC, NPC, trapType());
+ } else {
+ doNormalFault(tc, trapType(), false);
+ getPrivVector(tc, PC, NPC, trapType(), tl+1);
+ }
+
+ tc->setPC(PC);
+ tc->setNextPC(NPC);
+ tc->setNextNPC(NPC + sizeof(MachInst));
}
void PowerOnReset::invoke(ThreadContext * tc)
//For SPARC, when a system is first started, there is a power
//on reset Trap which sets the processor into the following state.
//Bits that aren't set aren't defined on startup.
- /*
- tl = MaxTL;
- gl = MaxGL;
- tickFields.counter = 0; //The TICK register is unreadable bya
- tickFields.npt = 1; //The TICK register is unreadable by by !priv
-
- softint = 0; // Clear all the soft interrupt bits
- tick_cmprFields.int_dis = 1; // disable timer compare interrupts
+ tc->setMiscRegNoEffect(MISCREG_TL, MaxTL);
+ tc->setMiscRegNoEffect(MISCREG_TT, trapType());
+ tc->setMiscReg(MISCREG_GL, MaxGL);
+
+ //Turn on pef and priv, set everything else to 0
+ tc->setMiscRegNoEffect(MISCREG_PSTATE, (1 << 4) | (1 << 2));
+
+ //Turn on red and hpriv, set everything else to 0
+ MiscReg HPSTATE = tc->readMiscRegNoEffect(MISCREG_HPSTATE);
+ //HPSTATE.red = 1
+ HPSTATE |= (1 << 5);
+ //HPSTATE.hpriv = 1
+ HPSTATE |= (1 << 2);
+ //HPSTATE.ibe = 0
+ HPSTATE &= ~(1 << 10);
+ //HPSTATE.tlz = 0
+ HPSTATE &= ~(1 << 0);
+ tc->setMiscRegNoEffect(MISCREG_HPSTATE, HPSTATE);
+
+ //The tick register is unreadable by nonprivileged software
+ tc->setMiscRegNoEffect(MISCREG_TICK, 1ULL << 63);
+
+ //Enter RED state. We do this last so that the actual state preserved in
+ //the trap stack is the state from before this fault.
+ enterREDState(tc);
+
+ Addr PC, NPC;
+ getREDVector(trapType(), PC, NPC);
+ tc->setPC(PC);
+ tc->setNextPC(NPC);
+ tc->setNextNPC(NPC + sizeof(MachInst));
+
+ //These registers are specified as "undefined" after a POR, and they
+ //should have reasonable values after the miscregfile is reset
+ /*
+ // Clear all the soft interrupt bits
+ softint = 0;
+ // disable timer compare interrupts, reset tick_cmpr
+ tc->setMiscRegNoEffect(MISCREG_
+ tick_cmprFields.int_dis = 1;
tick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing
stickFields.npt = 1; //The TICK register is unreadable by by !priv
stick_cmprFields.int_dis = 1; // disable timer compare interrupts
stick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing
tt[tl] = _trapType;
- pstate = 0; // fields 0 but pef
- pstateFields.pef = 1;
- hpstate = 0;
- hpstateFields.red = 1;
- hpstateFields.hpriv = 1;
- hpstateFields.tlz = 0; // this is a guess
hintp = 0; // no interrupts pending
hstick_cmprFields.int_dis = 1; // disable timer compare interrupts
hstick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing
*/
}
-#endif
+#else // !FULL_SYSTEM
-#if !FULL_SYSTEM
+void FastInstructionAccessMMUMiss::invoke(ThreadContext *tc)
+{
+ Process *p = tc->getProcessPtr();
+ TlbEntry entry;
+ bool success = p->pTable->lookup(vaddr, entry);
+ if(!success) {
+ panic("Tried to execute unmapped address %#x.\n", vaddr);
+ } else {
+ Addr alignedVaddr = p->pTable->pageAlign(vaddr);
+ tc->getITBPtr()->insert(alignedVaddr, 0 /*partition id*/,
+ p->M5_pid /*context id*/, false, entry.pte);
+ }
+}
+
+void FastDataAccessMMUMiss::invoke(ThreadContext *tc)
+{
+ Process *p = tc->getProcessPtr();
+ TlbEntry entry;
+ bool success = p->pTable->lookup(vaddr, entry);
+ if(!success) {
+ p->checkAndAllocNextPage(vaddr);
+ success = p->pTable->lookup(vaddr, entry);
+ }
+ if(!success) {
+ panic("Tried to access unmapped address %#x.\n", vaddr);
+ } else {
+ Addr alignedVaddr = p->pTable->pageAlign(vaddr);
+ tc->getDTBPtr()->insert(alignedVaddr, 0 /*partition id*/,
+ p->M5_pid /*context id*/, false, entry.pte);
+ }
+}
void SpillNNormal::invoke(ThreadContext *tc)
{
- doNormalFault(tc, trapType());
+ doNormalFault(tc, trapType(), false);
Process *p = tc->getProcessPtr();
- //This will only work in faults from a SparcLiveProcess
+ //XXX This will only work in faults from a SparcLiveProcess
SparcLiveProcess *lp = dynamic_cast<SparcLiveProcess *>(p);
assert(lp);
void FillNNormal::invoke(ThreadContext *tc)
{
- doNormalFault(tc, trapType());
+ doNormalFault(tc, trapType(), false);
Process * p = tc->getProcessPtr();
- //This will only work in faults from a SparcLiveProcess
+ //XXX This will only work in faults from a SparcLiveProcess
SparcLiveProcess *lp = dynamic_cast<SparcLiveProcess *>(p);
assert(lp);
- //The adjust the PC and NPC
+ //Then adjust the PC and NPC
Addr fillStart = lp->readFillStart();
tc->setPC(fillStart);
tc->setNextPC(fillStart + sizeof(MachInst));
tc->setNextNPC(fillStart + 2*sizeof(MachInst));
}
-void PageTableFault::invoke(ThreadContext *tc)
+void TrapInstruction::invoke(ThreadContext *tc)
{
+ //In SE, this mechanism is how the process requests a service from the
+ //operating system. We'll get the process object from the thread context
+ //and let it service the request.
+
Process *p = tc->getProcessPtr();
- // address is higher than the stack region or in the current stack region
- if (vaddr > p->stack_base || vaddr > p->stack_min)
- FaultBase::invoke(tc);
-
- // We've accessed the next page
- if (vaddr > p->stack_min - PageBytes) {
- p->stack_min -= PageBytes;
- if (p->stack_base - p->stack_min > 8*1024*1024)
- fatal("Over max stack size for one thread\n");
- p->pTable->allocate(p->stack_min, PageBytes);
- warn("Increasing stack size by one page.");
- } else {
- FaultBase::invoke(tc);
- }
+ SparcLiveProcess *lp = dynamic_cast<SparcLiveProcess *>(p);
+ assert(lp);
+
+ lp->handleTrap(_n, tc);
+
+ //We need to explicitly advance the pc, since that's not done for us
+ //on a faulting instruction
+ tc->setPC(tc->readNextPC());
+ tc->setNextPC(tc->readNextNPC());
+ tc->setNextNPC(tc->readNextNPC() + sizeof(MachInst));
}
#endif