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));
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);
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;
!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);
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 {
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);
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:
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;
return send_packet("E30");
}
+ add_operation(new maybe_restore_tselect_op_t(*this));
add_operation(new continue_op_t(*this, false));
}
return send_packet("E40");
}
+ add_operation(new maybe_restore_tselect_op_t(*this));
add_operation(new continue_op_t(*this, true));
}
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));
}