From 2b390a9dea7435c5e83c11433c05e136ad9b163a Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 2 Sep 2016 12:37:38 -0700 Subject: [PATCH] Support triggers on TLB misses. --- riscv/gdbserver.cc | 10 +++++++++- riscv/mmu.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ riscv/mmu.h | 3 +++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc index c30b6bc..481e5b9 100644 --- a/riscv/gdbserver.cc +++ b/riscv/gdbserver.cc @@ -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); diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 0b60713..878d849 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -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)) diff --git a/riscv/mmu.h b/riscv/mmu.h index 3da2c92..1f8d34b 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -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; -- 2.30.2