Support triggers on TLB misses.
authorTim Newsome <tim@sifive.com>
Fri, 2 Sep 2016 19:37:38 +0000 (12:37 -0700)
committerTim Newsome <tim@sifive.com>
Fri, 2 Sep 2016 19:37:38 +0000 (12:37 -0700)
riscv/gdbserver.cc
riscv/mmu.cc
riscv/mmu.h

index c30b6bc11b448d0f3f96362d800348aad0b7eec0..481e5b954ce7e34106e95fab71c8ae75e702a0d2 100644 (file)
@@ -459,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);
@@ -1104,7 +1105,14 @@ 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);
-              if (bp.load)
+              // 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);
index 0b60713fd376fb2c999334b6386c05b80eea4bca..878d849d7ecaed30521bb8053a23389500743a2b 100644 (file)
@@ -73,9 +73,36 @@ const uint16_t* mmu_t::fetch_slow_path(reg_t vaddr)
   }
 }
 
+reg_t reg_from_bytes(size_t len, const uint8_t* bytes)
+{
+  switch (len) {
+    case 1:
+      return bytes[0];
+    case 2:
+      return bytes[0] |
+        (((reg_t) bytes[1]) << 8);
+    case 4:
+      return bytes[0] |
+        (((reg_t) bytes[1]) << 8) |
+        (((reg_t) bytes[2]) << 16) |
+        (((reg_t) bytes[3]) << 24);
+    case 8:
+      return bytes[0] |
+        (((reg_t) bytes[1]) << 8) |
+        (((reg_t) bytes[2]) << 16) |
+        (((reg_t) bytes[3]) << 24) |
+        (((reg_t) bytes[4]) << 32) |
+        (((reg_t) bytes[5]) << 40) |
+        (((reg_t) bytes[6]) << 48) |
+        (((reg_t) bytes[7]) << 56);
+  }
+  abort();
+}
+
 void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
 {
   reg_t paddr = translate(addr, LOAD);
+
   if (sim->addr_is_mem(paddr)) {
     memcpy(bytes, sim->addr_to_mem(paddr), len);
     if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
@@ -85,11 +112,26 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
   } else if (!sim->mmio_load(paddr, len, bytes)) {
     throw trap_load_access_fault(addr);
   }
+
+  if (!matched_trigger) {
+    reg_t data = reg_from_bytes(len, bytes);
+    matched_trigger = trigger_exception(OPERATION_LOAD, addr, data);
+    if (matched_trigger)
+      throw *matched_trigger;
+  }
 }
 
 void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes)
 {
   reg_t paddr = translate(addr, STORE);
+
+  if (!matched_trigger) {
+    reg_t data = reg_from_bytes(len, bytes);
+    matched_trigger = trigger_exception(OPERATION_STORE, addr, data);
+    if (matched_trigger)
+      throw *matched_trigger;
+  }
+
   if (sim->addr_is_mem(paddr)) {
     memcpy(sim->addr_to_mem(paddr), bytes, len);
     if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
index 3da2c92e9773742897b5898582c8fb73370a5d08..1f8d34b3a9ad2f0bfcc5793640c74524e04f532b 100644 (file)
@@ -219,6 +219,9 @@ private:
   inline trigger_matched_t *trigger_exception(trigger_operation_t operation,
       reg_t address, reg_t data)
   {
+    if (!proc) {
+      return NULL;
+    }
     int match = proc->trigger_match(operation, address, data);
     if (match == -1)
       return NULL;