| /* |
| * Copyright (c) 2016 RISC-V Foundation |
| * Copyright (c) 2016 The University of Virginia |
| * Copyright (c) 2018 TU Dresden |
| * Copyright (c) 2020 Barkhausen Institut |
| * 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/riscv/faults.hh" |
| |
| #include "arch/riscv/isa.hh" |
| #include "arch/riscv/registers.hh" |
| #include "arch/riscv/utility.hh" |
| #include "cpu/base.hh" |
| #include "cpu/thread_context.hh" |
| #include "debug/Fault.hh" |
| #include "sim/debug.hh" |
| #include "sim/full_system.hh" |
| #include "sim/workload.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(); |
| |
| DPRINTFS(Fault, tc->getCpuPtr(), "Fault (%s) at PC: %s\n", |
| name(), 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.mie; |
| status.mie = 0; |
| break; |
| default: |
| panic("Unknown privilege mode %d.", prv); |
| break; |
| } |
| |
| // Set fault cause, privilege, and return PC |
| // Interrupt is indicated on the MSB of cause (bit 63 in RV64) |
| uint64_t _cause = _code; |
| if (isInterrupt()) { |
| _cause |= (1L << 63); |
| } |
| tc->setMiscReg(cause, _cause); |
| 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 = mbits(tc->readMiscReg(tvec), 63, 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 |
| auto workload = dynamic_cast<Workload *>(tc->getSystemPtr()->workload); |
| PCState pc = workload->getEntry(); |
| 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) |
| { |
| tc->getSystemPtr()->workload->syscall(tc); |
| } |
| |
| } // namespace RiscvISA |