| /* |
| * Copyright (c) 2003-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. |
| */ |
| |
| #include "arch/sparc/faults.hh" |
| |
| #include <algorithm> |
| |
| #include "arch/sparc/mmu.hh" |
| #include "arch/sparc/process.hh" |
| #include "arch/sparc/se_workload.hh" |
| #include "arch/sparc/sparc_traits.hh" |
| #include "arch/sparc/types.hh" |
| #include "base/bitfield.hh" |
| #include "base/compiler.hh" |
| #include "base/trace.hh" |
| #include "cpu/base.hh" |
| #include "cpu/thread_context.hh" |
| #include "mem/page_table.hh" |
| #include "sim/full_system.hh" |
| #include "sim/process.hh" |
| |
| namespace gem5 |
| { |
| |
| namespace SparcISA |
| { |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<PowerOnReset>::vals |
| ("power_on_reset", 0x001, 0, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<WatchDogReset>::vals |
| ("watch_dog_reset", 0x002, 120, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<ExternallyInitiatedReset>::vals |
| ("externally_initiated_reset", 0x003, 110, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<SoftwareInitiatedReset>::vals |
| ("software_initiated_reset", 0x004, 130, {{SH, SH, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<REDStateException>::vals |
| ("RED_state_exception", 0x005, 1, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<StoreError>::vals |
| ("store_error", 0x007, 201, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InstructionAccessException>::vals |
| ("instruction_access_exception", 0x008, 300, {{H, H, H}}); |
| |
| //XXX This trap is apparently dropped from ua2005 |
| /*template<> SparcFaultBase::FaultVals |
| SparcFault<InstructionAccessMMUMiss>::vals |
| ("inst_mmu", 0x009, 2, {{H, H, H}});*/ |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InstructionAccessError>::vals |
| ("instruction_access_error", 0x00A, 400, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<IllegalInstruction>::vals |
| ("illegal_instruction", 0x010, 620, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<PrivilegedOpcode>::vals |
| ("privileged_opcode", 0x011, 700, {{P, SH, SH}}); |
| |
| //XXX This trap is apparently dropped from ua2005 |
| /*template<> SparcFaultBase::FaultVals |
| SparcFault<UnimplementedLDD>::vals |
| ("unimp_ldd", 0x012, 6, {{H, H, H}});*/ |
| |
| //XXX This trap is apparently dropped from ua2005 |
| /*template<> SparcFaultBase::FaultVals |
| SparcFault<UnimplementedSTD>::vals |
| ("unimp_std", 0x013, 6, {{H, H, H}});*/ |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FpDisabled>::vals |
| ("fp_disabled", 0x020, 800, {{P, P, H}}); |
| |
| /* SPARCv8 and SPARCv9 define just fp_disabled trap. SIMD is not contemplated |
| * as a separate part. Therefore, we use the same code and TT */ |
| template<> SparcFaultBase::FaultVals |
| SparcFault<VecDisabled>::vals |
| ("fp_disabled", 0x020, 800, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FpExceptionIEEE754>::vals |
| ("fp_exception_ieee_754", 0x021, 1110, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FpExceptionOther>::vals |
| ("fp_exception_other", 0x022, 1110, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<TagOverflow>::vals |
| ("tag_overflow", 0x023, 1400, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<CleanWindow>::vals |
| ("clean_window", 0x024, 1010, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DivisionByZero>::vals |
| ("division_by_zero", 0x028, 1500, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InternalProcessorError>::vals |
| ("internal_processor_error", 0x029, 4, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InstructionInvalidTSBEntry>::vals |
| ("instruction_invalid_tsb_entry", 0x02A, 210, {{H, H, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DataInvalidTSBEntry>::vals |
| ("data_invalid_tsb_entry", 0x02B, 1203, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DataAccessException>::vals |
| ("data_access_exception", 0x030, 1201, {{H, H, H}}); |
| |
| //XXX This trap is apparently dropped from ua2005 |
| /*template<> SparcFaultBase::FaultVals |
| SparcFault<DataAccessMMUMiss>::vals |
| ("data_mmu", 0x031, 12, {{H, H, H}});*/ |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DataAccessError>::vals |
| ("data_access_error", 0x032, 1210, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DataAccessProtection>::vals |
| ("data_access_protection", 0x033, 1207, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<MemAddressNotAligned>::vals |
| ("mem_address_not_aligned", 0x034, 1020, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<LDDFMemAddressNotAligned>::vals |
| ("LDDF_mem_address_not_aligned", 0x035, 1010, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<STDFMemAddressNotAligned>::vals |
| ("STDF_mem_address_not_aligned", 0x036, 1010, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<PrivilegedAction>::vals |
| ("privileged_action", 0x037, 1110, {{H, H, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<LDQFMemAddressNotAligned>::vals |
| ("LDQF_mem_address_not_aligned", 0x038, 1010, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<STQFMemAddressNotAligned>::vals |
| ("STQF_mem_address_not_aligned", 0x039, 1010, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InstructionRealTranslationMiss>::vals |
| ("instruction_real_translation_miss", 0x03E, 208, {{H, H, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DataRealTranslationMiss>::vals |
| ("data_real_translation_miss", 0x03F, 1203, {{H, H, H}}); |
| |
| //XXX This trap is apparently dropped from ua2005 |
| /*template<> SparcFaultBase::FaultVals |
| SparcFault<AsyncDataError>::vals |
| ("async_data", 0x040, 2, {{H, H, H}});*/ |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InterruptLevelN>::vals |
| ("interrupt_level_n", 0x040, 0, {{P, P, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<HstickMatch>::vals |
| ("hstick_match", 0x05E, 1601, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| 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}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<VAWatchpoint>::vals |
| ("VA_watchpoint", 0x062, 1120, {{P, P, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FastInstructionAccessMMUMiss>::vals |
| ("fast_instruction_access_MMU_miss", 0x064, 208, {{H, H, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FastDataAccessMMUMiss>::vals |
| ("fast_data_access_MMU_miss", 0x068, 1203, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FastDataAccessProtection>::vals |
| ("fast_data_access_protection", 0x06C, 1207, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<InstructionBreakpoint>::vals |
| ("instruction_break", 0x076, 610, {{H, H, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<CpuMondo>::vals |
| ("cpu_mondo", 0x07C, 1608, {{P, P, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<DevMondo>::vals |
| ("dev_mondo", 0x07D, 1611, {{P, P, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<ResumableError>::vals |
| ("resume_error", 0x07E, 3330, {{P, P, SH}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<SpillNNormal>::vals |
| ("spill_n_normal", 0x080, 900, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<SpillNOther>::vals |
| ("spill_n_other", 0x0A0, 900, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FillNNormal>::vals |
| ("fill_n_normal", 0x0C0, 900, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<FillNOther>::vals |
| ("fill_n_other", 0x0E0, 900, {{P, P, H}}); |
| |
| template<> SparcFaultBase::FaultVals |
| SparcFault<TrapInstruction>::vals |
| ("trap_instruction", 0x100, 1602, {{P, P, H}}); |
| |
| /** |
| * 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? |
| HPSTATE hpstate= tc->readMiscRegNoEffect(MISCREG_HPSTATE); |
| hpstate.red = 1; |
| hpstate.hpriv = 1; |
| 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. |
| PSTATE pstate = tc->readMiscRegNoEffect(MISCREG_PSTATE); |
| pstate.priv = 1; |
| 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) |
| { |
| RegVal TL = tc->readMiscRegNoEffect(MISCREG_TL); |
| RegVal TSTATE = tc->readMiscRegNoEffect(MISCREG_TSTATE); |
| PSTATE pstate = tc->readMiscRegNoEffect(MISCREG_PSTATE); |
| HPSTATE hpstate = tc->readMiscRegNoEffect(MISCREG_HPSTATE); |
| CCR ccr = tc->getReg(int_reg::Ccr); |
| RegVal ASI = tc->readMiscRegNoEffect(MISCREG_ASI); |
| RegVal CWP = tc->readMiscRegNoEffect(MISCREG_CWP); |
| RegVal CANSAVE = tc->getReg(int_reg::Cansave); |
| RegVal GL = tc->readMiscRegNoEffect(MISCREG_GL); |
| auto &pc = tc->pcState().as<PCState>(); |
| |
| TL++; |
| |
| Addr pcMask = pstate.am ? mask(32) : mask(64); |
| |
| // 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.pc() & pcMask); |
| // set TNPC to NPC |
| tc->setMiscRegNoEffect(MISCREG_TNPC, pc.npc() & pcMask); |
| |
| // set HTSTATE.hpstate to hpstate |
| tc->setMiscRegNoEffect(MISCREG_HTSTATE, hpstate); |
| |
| // TT = trap type; |
| tc->setMiscRegNoEffect(MISCREG_TT, tt); |
| |
| // Update GL |
| tc->setMiscReg(MISCREG_GL, std::min<int>(GL+1, MaxGL)); |
| |
| bool priv = pstate.priv; // just save the priv bit |
| pstate = 0; |
| pstate.priv = priv; |
| pstate.pef = 1; |
| tc->setMiscRegNoEffect(MISCREG_PSTATE, pstate); |
| |
| hpstate.red = 1; |
| hpstate.hpriv = 1; |
| hpstate.ibe = 0; |
| hpstate.tlz = 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. |
| */ |
| |
| void |
| doNormalFault(ThreadContext *tc, TrapType tt, bool gotoHpriv) |
| { |
| RegVal TL = tc->readMiscRegNoEffect(MISCREG_TL); |
| RegVal TSTATE = tc->readMiscRegNoEffect(MISCREG_TSTATE); |
| PSTATE pstate = tc->readMiscRegNoEffect(MISCREG_PSTATE); |
| HPSTATE hpstate = tc->readMiscRegNoEffect(MISCREG_HPSTATE); |
| CCR ccr = tc->getReg(int_reg::Ccr); |
| RegVal ASI = tc->readMiscRegNoEffect(MISCREG_ASI); |
| RegVal CWP = tc->readMiscRegNoEffect(MISCREG_CWP); |
| RegVal CANSAVE = tc->getReg(int_reg::Cansave); |
| RegVal GL = tc->readMiscRegNoEffect(MISCREG_GL); |
| auto &pc = tc->pcState().as<PCState>(); |
| |
| // Increment the trap level |
| TL++; |
| tc->setMiscRegNoEffect(MISCREG_TL, TL); |
| |
| Addr pcMask = pstate.am ? mask(32) : mask(64); |
| |
| // Save off state |
| |
| // 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.pc() & pcMask); |
| // set TNPC to NPC |
| tc->setMiscRegNoEffect(MISCREG_TNPC, pc.npc() & pcMask); |
| |
| // set HTSTATE.hpstate to hpstate |
| tc->setMiscRegNoEffect(MISCREG_HTSTATE, hpstate); |
| |
| // TT = trap type; |
| tc->setMiscRegNoEffect(MISCREG_TT, tt); |
| |
| // Update the global register level |
| if (!gotoHpriv) |
| tc->setMiscReg(MISCREG_GL, std::min<int>(GL + 1, MaxPGL)); |
| else |
| tc->setMiscReg(MISCREG_GL, std::min<int>(GL + 1, MaxGL)); |
| |
| // pstate.mm is unchanged |
| pstate.pef = 1; // PSTATE.pef = whether or not an fpu is present |
| pstate.am = 0; |
| pstate.ie = 0; |
| // pstate.tle is unchanged |
| // pstate.tct = 0 |
| |
| if (gotoHpriv) { |
| pstate.cle = 0; |
| // The manual says PSTATE.priv should be 0, but Legion leaves it alone |
| hpstate.red = 0; |
| hpstate.hpriv = 1; |
| hpstate.ibe = 0; |
| // hpstate.tlz is unchanged |
| tc->setMiscRegNoEffect(MISCREG_HPSTATE, hpstate); |
| } else { // we are going to priv |
| pstate.priv = 1; |
| pstate.cle = pstate.tle; |
| } |
| tc->setMiscRegNoEffect(MISCREG_PSTATE, pstate); |
| |
| |
| 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); |
| } |
| } |
| |
| void |
| getREDVector(RegVal 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, RegVal 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, RegVal TT, RegVal TL) |
| { |
| Addr TBA = tc->readMiscRegNoEffect(MISCREG_TBA); |
| PC = (TBA & ~mask(15)) | |
| (TL > 1 ? (1 << 14) : 0) | |
| ((TT << 5) & mask(14)); |
| NPC = PC + sizeof(MachInst); |
| } |
| |
| void |
| SparcFaultBase::invoke(ThreadContext * tc, const StaticInstPtr &inst) |
| { |
| FaultBase::invoke(tc); |
| if (!FullSystem) |
| return; |
| |
| |
| // 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. |
| RegVal tl = tc->readMiscRegNoEffect(MISCREG_TL); |
| RegVal tt = tc->readMiscRegNoEffect(MISCREG_TT); |
| PSTATE pstate = tc->readMiscRegNoEffect(MISCREG_PSTATE); |
| HPSTATE hpstate = tc->readMiscRegNoEffect(MISCREG_HPSTATE); |
| |
| Addr PC, NPC; |
| |
| PrivilegeLevel current; |
| if (hpstate.hpriv) |
| current = Hyperprivileged; |
| else if (pstate.priv) |
| current = Privileged; |
| else |
| current = User; |
| |
| PrivilegeLevel level = getNextLevel(current); |
| |
| if (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); |
| } |
| |
| PCState pc; |
| pc.pc(PC); |
| pc.npc(NPC); |
| pc.nnpc(NPC + sizeof(MachInst)); |
| pc.upc(0); |
| pc.nupc(1); |
| tc->pcState(pc); |
| } |
| |
| void |
| PowerOnReset::invoke(ThreadContext *tc, const StaticInstPtr &inst) |
| { |
| // 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. |
| |
| tc->setMiscRegNoEffect(MISCREG_TL, MaxTL); |
| tc->setMiscRegNoEffect(MISCREG_TT, trapType()); |
| tc->setMiscReg(MISCREG_GL, MaxGL); |
| |
| PSTATE pstate = 0; |
| pstate.pef = 1; |
| pstate.priv = 1; |
| tc->setMiscRegNoEffect(MISCREG_PSTATE, pstate); |
| |
| // Turn on red and hpriv, set everything else to 0 |
| HPSTATE hpstate = tc->readMiscRegNoEffect(MISCREG_HPSTATE); |
| hpstate.red = 1; |
| hpstate.hpriv = 1; |
| hpstate.ibe = 0; |
| hpstate.tlz = 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); |
| |
| PCState pc; |
| pc.pc(PC); |
| pc.npc(NPC); |
| pc.nnpc(NPC + sizeof(MachInst)); |
| pc.upc(0); |
| pc.nupc(1); |
| tc->pcState(pc); |
| |
| // 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; |
| |
| 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 |
| */ |
| } |
| |
| void |
| FastInstructionAccessMMUMiss::invoke(ThreadContext *tc, |
| const StaticInstPtr &inst) |
| { |
| if (FullSystem) { |
| SparcFaultBase::invoke(tc, inst); |
| return; |
| } |
| |
| Process *p = tc->getProcessPtr(); |
| const EmulationPageTable::Entry *pte = p->pTable->lookup(vaddr); |
| panic_if(!pte, "Tried to execute unmapped address %#x.\n", vaddr); |
| |
| Addr alignedvaddr = p->pTable->pageAlign(vaddr); |
| |
| // Grab fields used during instruction translation to figure out |
| // which context to use. |
| uint64_t tlbdata = tc->readMiscRegNoEffect(MISCREG_TLB_DATA); |
| |
| // Inside a VM, a real address is the address that guest OS would |
| // interpret to be a physical address. To map to the physical address, |
| // it still needs to undergo a translation. The instruction |
| // translation code in the SPARC ITLB code assumes that the context is |
| // zero (kernel-level) if real addressing is being used. |
| bool is_real_address = !bits(tlbdata, 4); |
| |
| // The SPARC ITLB code assumes that traps are executed in context |
| // zero so we carry that assumption through here. |
| bool trapped = bits(tlbdata, 18, 16) > 0; |
| |
| // The primary context acts as a PASID. It allows the MMU to |
| // distinguish between virtual addresses that would alias to the |
| // same physical address (if two or more processes shared the same |
| // virtual address mapping). |
| int primary_context = bits(tlbdata, 47, 32); |
| |
| // The partition id distinguishes between virtualized environments. |
| int const partition_id = 0; |
| |
| // Given the assumptions in the translateInst code in the SPARC ITLB, |
| // the logic works out to the following for the context. |
| int context_id = (is_real_address || trapped) ? 0 : primary_context; |
| |
| TlbEntry entry(p->pTable->pid(), alignedvaddr, pte->paddr, |
| pte->flags & EmulationPageTable::Uncacheable, |
| pte->flags & EmulationPageTable::ReadOnly); |
| |
| // Insert the TLB entry. |
| // The entry specifying whether the address is "real" is set to |
| // false for syscall emulation mode regardless of whether the |
| // address is real in preceding code. Not sure sure that this is |
| // correct, but also not sure if it matters at all. |
| static_cast<MMU *>(tc->getMMUPtr())->insertItlbEntry( |
| alignedvaddr, partition_id, context_id, |
| false, entry.pte); |
| } |
| |
| void |
| FastDataAccessMMUMiss::invoke(ThreadContext *tc, const StaticInstPtr &inst) |
| { |
| if (FullSystem) { |
| SparcFaultBase::invoke(tc, inst); |
| return; |
| } |
| |
| Process *p = tc->getProcessPtr(); |
| const EmulationPageTable::Entry *pte = p->pTable->lookup(vaddr); |
| if (!pte && p->fixupFault(vaddr)) |
| pte = p->pTable->lookup(vaddr); |
| panic_if(!pte, "Tried to access unmapped address %#x.\n", vaddr); |
| |
| Addr alignedvaddr = p->pTable->pageAlign(vaddr); |
| |
| // Grab fields used during data translation to figure out |
| // which context to use. |
| uint64_t tlbdata = tc->readMiscRegNoEffect(MISCREG_TLB_DATA); |
| |
| // The primary context acts as a PASID. It allows the MMU to |
| // distinguish between virtual addresses that would alias to the |
| // same physical address (if two or more processes shared the same |
| // virtual address mapping). There's a secondary context used in the |
| // DTLB translation code, but it should __probably__ be zero for |
| // syscall emulation code. (The secondary context is used by Solaris |
| // to allow kernel privilege code to access user space code: |
| // [ISBN 0-13-022496-0]:PG199.) |
| int primary_context = bits(tlbdata, 47, 32); |
| |
| // "Hyper-Privileged Mode" is in use. There are three main modes of |
| // operation for Sparc: Hyper-Privileged Mode, Privileged Mode, and |
| // User Mode. |
| int hpriv = bits(tlbdata, 0); |
| |
| // Reset, Error and Debug state is in use. Something horrible has |
| // happened or the system is operating in Reset Mode. |
| int red = bits(tlbdata, 1); |
| |
| // Inside a VM, a real address is the address that guest OS would |
| // interpret to be a physical address. To map to the physical address, |
| // it still needs to undergo a translation. The instruction |
| // translation code in the SPARC ITLB code assumes that the context is |
| // zero (kernel-level) if real addressing is being used. |
| int is_real_address = !bits(tlbdata, 5); |
| |
| // Grab the address space identifier register from the thread context. |
| // XXX: Inspecting how setMiscReg and setMiscRegNoEffect behave for |
| // MISCREG_ASI causes me to think that the ASI register implementation |
| // might be bugged. The NoEffect variant changes the ASI register |
| // value in the architectural state while the normal variant changes |
| // the context field in the thread context's currently decoded request |
| // but does not directly affect the ASI register value in the |
| // architectural state. The ASI values and the context field in the |
| // request packet seem to have completely different uses. |
| RegVal reg_asi = tc->readMiscRegNoEffect(MISCREG_ASI); |
| ASI asi = static_cast<ASI>(reg_asi); |
| |
| // The SPARC DTLB code assumes that traps are executed in context |
| // zero if the asi value is ASI_IMPLICIT (which is 0x0). There's also |
| // an assumption that the nucleus address space is being used, but |
| // the context is the relevant issue since we need to pass it to TLB. |
| bool trapped = bits(tlbdata, 18, 16) > 0; |
| |
| // Given the assumptions in the translateData code in the SPARC DTLB, |
| // the logic works out to the following for the context. |
| int context_id = ((!hpriv && !red && is_real_address) || |
| asiIsReal(asi) || |
| (trapped && asi == ASI_IMPLICIT)) |
| ? 0 : primary_context; |
| |
| // The partition id distinguishes between virtualized environments. |
| int const partition_id = 0; |
| |
| TlbEntry entry(p->pTable->pid(), alignedvaddr, pte->paddr, |
| pte->flags & EmulationPageTable::Uncacheable, |
| pte->flags & EmulationPageTable::ReadOnly); |
| |
| // Insert the TLB entry. |
| // The entry specifying whether the address is "real" is set to |
| // false for syscall emulation mode regardless of whether the |
| // address is real in preceding code. Not sure sure that this is |
| // correct, but also not sure if it matters at all. |
| static_cast<MMU *>(tc->getMMUPtr())->insertDtlbEntry( |
| alignedvaddr, partition_id, context_id, |
| false, entry.pte); |
| } |
| |
| void |
| SpillNNormal::invoke(ThreadContext *tc, const StaticInstPtr &inst) |
| { |
| if (FullSystem) { |
| SparcFaultBase::invoke(tc, inst); |
| return; |
| } |
| |
| doNormalFault(tc, trapType(), false); |
| |
| Process *p = tc->getProcessPtr(); |
| |
| SparcProcess *sp = dynamic_cast<SparcProcess *>(p); |
| assert(sp); |
| |
| // Then adjust the PC and NPC |
| tc->pcState(sp->readSpillStart()); |
| } |
| |
| void |
| FillNNormal::invoke(ThreadContext *tc, const StaticInstPtr &inst) |
| { |
| if (FullSystem) { |
| SparcFaultBase::invoke(tc, inst); |
| return; |
| } |
| |
| doNormalFault(tc, trapType(), false); |
| |
| Process *p = tc->getProcessPtr(); |
| |
| SparcProcess *sp = dynamic_cast<SparcProcess *>(p); |
| assert(sp); |
| |
| // Then adjust the PC and NPC |
| tc->pcState(sp->readFillStart()); |
| } |
| |
| void |
| TrapInstruction::invoke(ThreadContext *tc, const StaticInstPtr &inst) |
| { |
| if (FullSystem) { |
| SparcFaultBase::invoke(tc, inst); |
| return; |
| } |
| |
| // 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(); |
| |
| [[maybe_unused]] SparcProcess *sp = dynamic_cast<SparcProcess *>(p); |
| assert(sp); |
| |
| auto *workload = dynamic_cast<SEWorkload *>(tc->getSystemPtr()->workload); |
| workload->handleTrap(tc, _n); |
| |
| // We need to explicitly advance the pc, since that's not done for us |
| // on a faulting instruction |
| PCState pc = tc->pcState().as<PCState>(); |
| pc.advance(); |
| tc->pcState(pc); |
| } |
| |
| } // namespace SparcISA |
| } // namespace gem5 |