Support triggers on TLB misses.
[riscv-isa-sim.git] / riscv / gdbserver.cc
index db88d30dfddab67d1b60157243caafab6b9bcb64..481e5b954ce7e34106e95fab71c8ae75e702a0d2 100644 (file)
@@ -351,6 +351,7 @@ class halt_op_t : public operation_t
 
     bool perform_step(unsigned int step) {
       switch (state) {
+        gs.tselect_valid = false;
         case ST_ENTER:
           if (gs.xlen == 0) {
             gs.dr_write32(0, xori(S1, ZERO, -1));
@@ -458,6 +459,7 @@ class continue_op_t : public operation_t
       operation_t(gdbserver), single_step(single_step) {};
 
     bool perform_step(unsigned int step) {
+      D(fprintf(stderr, "continue step %d\n", step));
       switch (step) {
         case 0:
           gs.dr_write_load(0, S0, SLOT_DATA0);
@@ -1069,7 +1071,7 @@ class hardware_breakpoint_insert_op_t : public operation_t
             return true;
           }
 
-          gs.dr_write32(0, csrr(S0, CSR_TDATA0));
+          gs.dr_write32(0, csrr(S0, CSR_TDATA1));
           gs.dr_write_store(1, S0, SLOT_DATA0);
           gs.dr_write_jump(2);
           state = STATE_CHECK_MCONTROL;
@@ -1091,9 +1093,10 @@ class hardware_breakpoint_insert_op_t : public operation_t
                 !get_field(mcontrol, MCONTROL_STORE)) {
               // Found an unused trigger.
               gs.dr_write_load(0, S0, SLOT_DATA1);
-              gs.dr_write32(1, csrw(S0, CSR_TDATA0));
+              gs.dr_write32(1, csrw(S0, CSR_TDATA1));
               gs.dr_write_jump(2);
               mcontrol = set_field(0, MCONTROL_ACTION, MCONTROL_ACTION_DEBUG_MODE);
+              mcontrol = set_field(mcontrol, MCONTROL_DMODE(gs.xlen), 1);
               mcontrol = set_field(mcontrol, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
               mcontrol = set_field(mcontrol, MCONTROL_M, 1);
               mcontrol = set_field(mcontrol, MCONTROL_H, 1);
@@ -1102,6 +1105,16 @@ class hardware_breakpoint_insert_op_t : public operation_t
               mcontrol = set_field(mcontrol, MCONTROL_EXECUTE, bp.execute);
               mcontrol = set_field(mcontrol, MCONTROL_LOAD, bp.load);
               mcontrol = set_field(mcontrol, MCONTROL_STORE, bp.store);
+              // For store triggers it's nicer to fire just before the
+              // instruction than just after. However, gdb doesn't clear the
+              // breakpoints and step before resuming from a store trigger.
+              // That means that without extra code, you'll keep hitting the
+              // same watchpoint over and over again. That's not useful at all.
+              // Instead of fixing this the right way, just set timing=1 for
+              // those triggers.
+              if (bp.load || bp.store)
+                mcontrol = set_field(mcontrol, MCONTROL_TIMING, 1);
+
               gs.dr_write(SLOT_DATA1, mcontrol);
               state = STATE_WRITE_ADDRESS;
             } else {
@@ -1115,7 +1128,7 @@ class hardware_breakpoint_insert_op_t : public operation_t
         case STATE_WRITE_ADDRESS:
           {
             gs.dr_write_load(0, S0, SLOT_DATA1);
-            gs.dr_write32(1, csrw(S0, CSR_TDATA1));
+            gs.dr_write32(1, csrw(S0, CSR_TDATA2));
             gs.dr_write_jump(2);
             gs.dr_write(SLOT_DATA1, bp.vaddr);
             gs.set_interrupt(0);
@@ -1141,6 +1154,45 @@ class hardware_breakpoint_insert_op_t : public operation_t
     hardware_breakpoint_t bp;
 };
 
+class maybe_save_tselect_op_t : public operation_t
+{
+  public:
+    maybe_save_tselect_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {};
+    bool perform_step(unsigned int step) {
+      if (gs.tselect_valid)
+        return true;
+
+      switch (step) {
+        case 0:
+          gs.dr_write32(0, csrr(S0, CSR_TDATA1));
+          gs.dr_write_store(1, S0, SLOT_DATA0);
+          gs.dr_write_jump(2);
+          gs.set_interrupt(0);
+          return false;
+        case 1:
+          gs.tselect = gs.dr_read(SLOT_DATA0);
+          gs.tselect_valid = true;
+          break;
+      }
+      return true;
+    }
+};
+
+class maybe_restore_tselect_op_t : public operation_t
+{
+  public:
+    maybe_restore_tselect_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {};
+    bool perform_step(unsigned int step) {
+      if (gs.tselect_valid) {
+        gs.dr_write_load(0, S0, SLOT_DATA1);
+        gs.dr_write32(1, csrw(S0, CSR_TSELECT));
+        gs.dr_write_jump(2);
+        gs.dr_write(SLOT_DATA1, gs.tselect);
+      }
+      return true;
+    }
+};
+
 class hardware_breakpoint_remove_op_t : public operation_t
 {
   public:
@@ -1151,7 +1203,7 @@ class hardware_breakpoint_remove_op_t : public operation_t
     bool perform_step(unsigned int step) {
       gs.dr_write32(0, addi(S0, ZERO, bp.index));
       gs.dr_write32(1, csrw(S0, CSR_TSELECT));
-      gs.dr_write32(2, csrw(ZERO, CSR_TDATA0));
+      gs.dr_write32(2, csrw(ZERO, CSR_TDATA1));
       gs.dr_write_jump(3);
       gs.set_interrupt(0);
       return true;
@@ -1733,6 +1785,7 @@ void gdbserver_t::handle_continue(const std::vector<uint8_t> &packet)
       return send_packet("E30");
   }
 
+  add_operation(new maybe_restore_tselect_op_t(*this));
   add_operation(new continue_op_t(*this, false));
 }
 
@@ -1747,6 +1800,7 @@ void gdbserver_t::handle_step(const std::vector<uint8_t> &packet)
       return send_packet("E40");
   }
 
+  add_operation(new maybe_restore_tselect_op_t(*this));
   add_operation(new continue_op_t(*this, true));
 }
 
@@ -1806,11 +1860,13 @@ void gdbserver_t::software_breakpoint_remove(reg_t vaddr, unsigned int size)
 
 void gdbserver_t::hardware_breakpoint_insert(const hardware_breakpoint_t &bp)
 {
+  add_operation(new maybe_save_tselect_op_t(*this));
   add_operation(new hardware_breakpoint_insert_op_t(*this, bp));
 }
 
 void gdbserver_t::hardware_breakpoint_remove(const hardware_breakpoint_t &bp)
 {
+  add_operation(new maybe_save_tselect_op_t(*this));
   hardware_breakpoint_t found = *hardware_breakpoints.find(bp);
   add_operation(new hardware_breakpoint_remove_op_t(*this, found));
 }