Allow mstatus.MPP to store bad values; instead, validate on MRET
[riscv-isa-sim.git] / riscv / processor.cc
index 996f9e30ef297cc765ac062ae8e0d87dfa031410..0a7912bd791554454813dd071a978e624c4f1f94 100644 (file)
@@ -6,8 +6,8 @@
 #include "config.h"
 #include "sim.h"
 #include "mmu.h"
-#include "htif.h"
 #include "disasm.h"
+#include "gdbserver.h"
 #include <cinttypes>
 #include <cmath>
 #include <cstdlib>
 #undef STATE
 #define STATE state
 
-processor_t::processor_t(const char* isa, sim_t* sim, uint32_t id)
-  : sim(sim), ext(NULL), disassembler(new disassembler_t),
-    id(id), run(false), debug(false), halted(false), single_step(false)
+processor_t::processor_t(const char* isa, sim_t* sim, uint32_t id,
+        bool halt_on_reset)
+  : debug(false), sim(sim), ext(NULL), id(id), halt_on_reset(halt_on_reset)
 {
   parse_isa_string(isa);
+  register_base_instructions();
 
   mmu = new mmu_t(sim, this);
+  disassembler = new disassembler_t(max_xlen);
 
-  reset(true);
-
-  register_base_instructions();
+  reset();
 }
 
 processor_t::~processor_t()
@@ -84,6 +84,7 @@ void processor_t::parse_isa_string(const char* str)
 
   isa_string = "rv" + std::to_string(max_xlen) + p;
   isa |= 1L << ('s' - 'a'); // advertise support for supervisor mode
+  isa |= 1L << ('u' - 'a'); // advertise support for user mode
 
   while (*p) {
     isa |= 1L << (*p - 'a');
@@ -126,17 +127,6 @@ void processor_t::set_debug(bool value)
     ext->set_debug(value);
 }
 
-void processor_t::set_halted(bool value, halt_reason_t reason)
-{
-  halted = value;
-  halt_reason = reason;
-}
-
-void processor_t::set_single_step(bool value)
-{
-  single_step = value;
-}
-
 void processor_t::set_histogram(bool value)
 {
   histogram_enabled = value;
@@ -148,13 +138,11 @@ void processor_t::set_histogram(bool value)
 #endif
 }
 
-void processor_t::reset(bool value)
+void processor_t::reset()
 {
-  if (run == !value)
-    return;
-  run = !value;
-
   state.reset();
+  state.dcsr.halt = halt_on_reset;
+  halt_on_reset = false;
   set_csr(CSR_MSTATUS, state.mstatus);
 
   if (ext)
@@ -191,27 +179,46 @@ void processor_t::take_interrupt()
     raise_interrupt(ctz(enabled_interrupts));
 }
 
-static bool validate_priv(reg_t priv)
-{
-  return priv == PRV_U || priv == PRV_S || priv == PRV_M;
-}
-
 void processor_t::set_privilege(reg_t prv)
 {
-  assert(validate_priv(prv));
+  assert(prv <= PRV_M);
+  if (prv == PRV_H)
+    prv = PRV_U;
   mmu->flush_tlb();
   state.prv = prv;
 }
 
+void processor_t::enter_debug_mode(uint8_t cause)
+{
+  state.dcsr.cause = cause;
+  state.dcsr.prv = state.prv;
+  set_privilege(PRV_M);
+  state.dpc = state.pc;
+  state.pc = DEBUG_ROM_START;
+  //debug = true; // TODO
+}
+
 void processor_t::take_trap(trap_t& t, reg_t epc)
 {
-  if (debug)
+  if (debug) {
     fprintf(stderr, "core %3d: exception %s, epc 0x%016" PRIx64 "\n",
             id, t.name(), epc);
+    if (t.has_badaddr())
+      fprintf(stderr, "core %3d:           badaddr 0x%016" PRIx64 "\n", id,
+          t.get_badaddr());
+  }
 
-  if (t.cause() == CAUSE_BREAKPOINT &&
-          sim->gdbserver && sim->gdbserver->connected()) {
-    set_halted(true, HR_SWBP);
+  if (t.cause() == CAUSE_BREAKPOINT && (
+              (state.prv == PRV_M && state.dcsr.ebreakm) ||
+              (state.prv == PRV_H && state.dcsr.ebreakh) ||
+              (state.prv == PRV_S && state.dcsr.ebreaks) ||
+              (state.prv == PRV_U && state.dcsr.ebreaku))) {
+    enter_debug_mode(DCSR_CAUSE_SWBP);
+    return;
+  }
+
+  if (state.dcsr.cause) {
+    state.pc = DEBUG_ROM_EXCEPTION;
     return;
   }
 
@@ -236,8 +243,8 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
     set_privilege(PRV_S);
   } else {
     state.pc = state.mtvec;
-    state.mcause = t.cause();
     state.mepc = epc;
+    state.mcause = t.cause();
     if (t.has_badaddr())
       state.mbadaddr = t.get_badaddr();
 
@@ -268,6 +275,12 @@ static bool validate_vm(int max_xlen, reg_t vm)
   return vm == VM_MBARE;
 }
 
+int processor_t::paddr_bits()
+{
+  assert(xlen == max_xlen);
+  return max_xlen == 64 ? 50 : 34;
+}
+
 void processor_t::set_csr(int which, reg_t val)
 {
   val = zext_xlen(val);
@@ -290,17 +303,15 @@ void processor_t::set_csr(int which, reg_t val)
       break;
     case CSR_MSTATUS: {
       if ((val ^ state.mstatus) &
-          (MSTATUS_VM | MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_PUM))
+          (MSTATUS_VM | MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_PUM | MSTATUS_MXR))
         mmu->flush_tlb();
 
       reg_t mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE
                  | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_PUM
-                 | (ext ? MSTATUS_XS : 0);
+                 | MSTATUS_MPP | MSTATUS_MXR | (ext ? MSTATUS_XS : 0);
 
       if (validate_vm(max_xlen, get_field(val, MSTATUS_VM)))
         mask |= MSTATUS_VM;
-      if (validate_priv(get_field(val, MSTATUS_MPP)))
-        mask |= MSTATUS_MPP;
 
       state.mstatus = (state.mstatus & ~mask) | (val & mask);
 
@@ -352,9 +363,13 @@ void processor_t::set_csr(int which, reg_t val)
     case CSR_SIE:
       return set_csr(CSR_MIE,
                      (state.mie & ~state.mideleg) | (val & state.mideleg));
+    case CSR_SPTBR: {
+      // upper bits of sptbr are the ASID; we only support ASID = 0
+      state.sptbr = val & (((reg_t)1 << (paddr_bits() - PGSHIFT)) - 1);
+      break;
+    }
     case CSR_SEPC: state.sepc = val; break;
     case CSR_STVEC: state.stvec = val >> 2 << 2; break;
-    case CSR_SPTBR: state.sptbr = val; break;
     case CSR_SSCRATCH: state.sscratch = val; break;
     case CSR_SCAUSE: state.scause = val; break;
     case CSR_SBADADDR: state.sbadaddr = val; break;
@@ -363,20 +378,20 @@ void processor_t::set_csr(int which, reg_t val)
     case CSR_MSCRATCH: state.mscratch = val; break;
     case CSR_MCAUSE: state.mcause = val; break;
     case CSR_MBADADDR: state.mbadaddr = val; break;
-    case DCSR_ADDRESS:
-      state.dcsr.prv = (val & DCSR_PRV_MASK) >> DCSR_PRV_OFFSET;
-      state.dcsr.step = (val & DCSR_STEP_MASK) >> DCSR_STEP_OFFSET;
+    case CSR_DCSR:
+      state.dcsr.prv = get_field(val, DCSR_PRV);
+      state.dcsr.step = get_field(val, DCSR_STEP);
       // TODO: ndreset and fullreset
-      state.dcsr.ebreakm = (val & DCSR_EBREAKM_MASK) >> DCSR_EBREAKM_OFFSET;
-      state.dcsr.ebreakh = (val & DCSR_EBREAKH_MASK) >> DCSR_EBREAKH_OFFSET;
-      state.dcsr.ebreaks = (val & DCSR_EBREAKS_MASK) >> DCSR_EBREAKS_OFFSET;
-      state.dcsr.ebreaku = (val & DCSR_EBREAKU_MASK) >> DCSR_EBREAKU_OFFSET;
-      state.dcsr.halt = (val & DCSR_HALT_MASK) >> DCSR_HALT_OFFSET;
+      state.dcsr.ebreakm = get_field(val, DCSR_EBREAKM);
+      state.dcsr.ebreakh = get_field(val, DCSR_EBREAKH);
+      state.dcsr.ebreaks = get_field(val, DCSR_EBREAKS);
+      state.dcsr.ebreaku = get_field(val, DCSR_EBREAKU);
+      state.dcsr.halt = get_field(val, DCSR_HALT);
       break;
-    case DPC_ADDRESS:
+    case CSR_DPC:
       state.dpc = val;
       break;
-    case DSCRATCH_ADDRESS:
+    case CSR_DSCRATCH:
       state.dscratch = val;
       break;
   }
@@ -450,7 +465,6 @@ reg_t processor_t::get_csr(int which)
         return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1));
       return state.scause;
     case CSR_SPTBR: return state.sptbr;
-    case CSR_SASID: return 0;
     case CSR_SSCRATCH: return state.sscratch;
     case CSR_MSTATUS: return state.mstatus;
     case CSR_MIP: return state.mip;
@@ -467,26 +481,30 @@ reg_t processor_t::get_csr(int which)
     case CSR_MTVEC: return state.mtvec;
     case CSR_MEDELEG: return state.medeleg;
     case CSR_MIDELEG: return state.mideleg;
-    case DCSR_ADDRESS:
-      return
-          (1 << DCSR_XDEBUGVER_OFFSET) |
-          (0 << DCSR_HWBPCOUNT_OFFSET) |
-          (0 << DCSR_NDRESET_OFFSET) |
-          (0 << DCSR_FULLRESET_OFFSET) |
-          (state.dcsr.prv << DCSR_PRV_OFFSET) |
-          (state.dcsr.step << DCSR_STEP_OFFSET) |
-          (state.dcsr.debugint << DCSR_DEBUGINT_OFFSET) |
-          (0 << DCSR_STOPCYCLE_OFFSET) |
-          (0 << DCSR_STOPTIME_OFFSET) |
-          (state.dcsr.ebreakm << DCSR_EBREAKM_OFFSET) |
-          (state.dcsr.ebreakh << DCSR_EBREAKH_OFFSET) |
-          (state.dcsr.ebreaks << DCSR_EBREAKS_OFFSET) |
-          (state.dcsr.ebreaku << DCSR_EBREAKU_OFFSET) |
-          (state.dcsr.halt << DCSR_HALT_OFFSET) |
-          (state.dcsr.cause << DCSR_CAUSE_OFFSET);
-    case DPC_ADDRESS:
+    case CSR_TDRSELECT: return 0;
+    case CSR_DCSR:
+      {
+        uint32_t v = 0;
+        v = set_field(v, DCSR_XDEBUGVER, 1);
+        v = set_field(v, DCSR_HWBPCOUNT, 0);
+        v = set_field(v, DCSR_NDRESET, 0);
+        v = set_field(v, DCSR_FULLRESET, 0);
+        v = set_field(v, DCSR_PRV, state.dcsr.prv);
+        v = set_field(v, DCSR_STEP, state.dcsr.step);
+        v = set_field(v, DCSR_DEBUGINT, sim->debug_module.get_interrupt(id));
+        v = set_field(v, DCSR_STOPCYCLE, 0);
+        v = set_field(v, DCSR_STOPTIME, 0);
+        v = set_field(v, DCSR_EBREAKM, state.dcsr.ebreakm);
+        v = set_field(v, DCSR_EBREAKH, state.dcsr.ebreakh);
+        v = set_field(v, DCSR_EBREAKS, state.dcsr.ebreaks);
+        v = set_field(v, DCSR_EBREAKU, state.dcsr.ebreaku);
+        v = set_field(v, DCSR_HALT, state.dcsr.halt);
+        v = set_field(v, DCSR_CAUSE, state.dcsr.cause);
+        return v;
+      }
+    case CSR_DPC:
       return state.dpc;
-    case DSCRATCH_ADDRESS:
+    case CSR_DSCRATCH:
       return state.dscratch;
   }
   throw trap_illegal_instruction();
@@ -543,7 +561,7 @@ void processor_t::build_opcode_map()
   std::sort(instructions.begin(), instructions.end(), cmp());
 
   for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++)
-    opcode_cache[i] = {1, 0, &illegal_instruction, &illegal_instruction};
+    opcode_cache[i] = {0, 0, &illegal_instruction, &illegal_instruction};
 }
 
 void processor_t::register_extension(extension_t* x)