| /* | |
| * Copyright (c) 2016 RISC-V Foundation | |
| * Copyright (c) 2016 The University of Virginia | |
| * Copyright (c) 2018 TU Dresden | |
| * 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: Alec Roelke | |
| * Robert Scheffel | |
| */ | |
| #include "arch/riscv/faults.hh" | |
| #include "arch/riscv/isa.hh" | |
| #include "arch/riscv/registers.hh" | |
| #include "arch/riscv/system.hh" | |
| #include "arch/riscv/utility.hh" | |
| #include "cpu/base.hh" | |
| #include "cpu/thread_context.hh" | |
| #include "sim/debug.hh" | |
| #include "sim/full_system.hh" | |
| namespace RiscvISA | |
| { | |
| void | |
| RiscvFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| panic("Fault %s encountered at pc 0x%016llx.", name(), tc->pcState().pc()); | |
| } | |
| void | |
| RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| PCState pcState = tc->pcState(); | |
| if (FullSystem) { | |
| PrivilegeMode pp = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV); | |
| PrivilegeMode prv = PRV_M; | |
| STATUS status = tc->readMiscReg(MISCREG_STATUS); | |
| // Set fault handler privilege mode | |
| if (isInterrupt()) { | |
| if (pp != PRV_M && | |
| bits(tc->readMiscReg(MISCREG_MIDELEG), _code) != 0) { | |
| prv = PRV_S; | |
| } | |
| if (pp == PRV_U && | |
| bits(tc->readMiscReg(MISCREG_SIDELEG), _code) != 0) { | |
| prv = PRV_U; | |
| } | |
| } else { | |
| if (pp != PRV_M && | |
| bits(tc->readMiscReg(MISCREG_MEDELEG), _code) != 0) { | |
| prv = PRV_S; | |
| } | |
| if (pp == PRV_U && | |
| bits(tc->readMiscReg(MISCREG_SEDELEG), _code) != 0) { | |
| prv = PRV_U; | |
| } | |
| } | |
| // Set fault registers and status | |
| MiscRegIndex cause, epc, tvec, tval; | |
| switch (prv) { | |
| case PRV_U: | |
| cause = MISCREG_UCAUSE; | |
| epc = MISCREG_UEPC; | |
| tvec = MISCREG_UTVEC; | |
| tval = MISCREG_UTVAL; | |
| status.upie = status.uie; | |
| status.uie = 0; | |
| break; | |
| case PRV_S: | |
| cause = MISCREG_SCAUSE; | |
| epc = MISCREG_SEPC; | |
| tvec = MISCREG_STVEC; | |
| tval = MISCREG_STVAL; | |
| status.spp = pp; | |
| status.spie = status.sie; | |
| status.sie = 0; | |
| break; | |
| case PRV_M: | |
| cause = MISCREG_MCAUSE; | |
| epc = MISCREG_MEPC; | |
| tvec = MISCREG_MTVEC; | |
| tval = MISCREG_MTVAL; | |
| status.mpp = pp; | |
| status.mpie = status.sie; | |
| status.mie = 0; | |
| break; | |
| default: | |
| panic("Unknown privilege mode %d.", prv); | |
| break; | |
| } | |
| // Set fault cause, privilege, and return PC | |
| tc->setMiscReg(cause, | |
| (isInterrupt() << (sizeof(uint64_t) * 4 - 1)) | _code); | |
| tc->setMiscReg(epc, tc->instAddr()); | |
| tc->setMiscReg(tval, trap_value()); | |
| tc->setMiscReg(MISCREG_PRV, prv); | |
| tc->setMiscReg(MISCREG_STATUS, status); | |
| // Set PC to fault handler address | |
| Addr addr = tc->readMiscReg(tvec) >> 2; | |
| if (isInterrupt() && bits(tc->readMiscReg(tvec), 1, 0) == 1) | |
| addr += 4 * _code; | |
| pcState.set(addr); | |
| } else { | |
| invokeSE(tc, inst); | |
| advancePC(pcState, inst); | |
| } | |
| tc->pcState(pcState); | |
| } | |
| void Reset::invoke(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| tc->setMiscReg(MISCREG_PRV, PRV_M); | |
| STATUS status = tc->readMiscReg(MISCREG_STATUS); | |
| status.mie = 0; | |
| status.mprv = 0; | |
| tc->setMiscReg(MISCREG_STATUS, status); | |
| tc->setMiscReg(MISCREG_MCAUSE, 0); | |
| // Advance the PC to the implementation-defined reset vector | |
| PCState pc = static_cast<RiscvSystem *>(tc->getSystemPtr())->resetVect(); | |
| tc->pcState(pc); | |
| } | |
| void | |
| UnknownInstFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| panic("Unknown instruction 0x%08x at pc 0x%016llx", inst->machInst, | |
| tc->pcState().pc()); | |
| } | |
| void | |
| IllegalInstFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| panic("Illegal instruction 0x%08x at pc 0x%016llx: %s", inst->machInst, | |
| tc->pcState().pc(), reason.c_str()); | |
| } | |
| void | |
| UnimplementedFault::invokeSE(ThreadContext *tc, | |
| const StaticInstPtr &inst) | |
| { | |
| panic("Unimplemented instruction %s at pc 0x%016llx", instName, | |
| tc->pcState().pc()); | |
| } | |
| void | |
| IllegalFrmFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| panic("Illegal floating-point rounding mode 0x%x at pc 0x%016llx.", | |
| frm, tc->pcState().pc()); | |
| } | |
| void | |
| BreakpointFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| schedRelBreak(0); | |
| } | |
| void | |
| SyscallFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) | |
| { | |
| Fault *fault = NoFault; | |
| tc->syscall(tc->readIntReg(SyscallNumReg), fault); | |
| } | |
| } // namespace RiscvISA |