+class hardware_breakpoint_insert_op_t : public operation_t
+{
+ public:
+ hardware_breakpoint_insert_op_t(gdbserver_t& gdbserver,
+ hardware_breakpoint_t bp) :
+ operation_t(gdbserver), state(STATE_START), bp(bp) {};
+
+ void write_new_index_program()
+ {
+ gs.dr_write_load(0, S0, SLOT_DATA1);
+ gs.dr_write32(1, csrw(S0, CSR_TSELECT));
+ gs.dr_write32(2, csrr(S0, CSR_TSELECT));
+ gs.dr_write_store(3, S0, SLOT_DATA1);
+ gs.dr_write_jump(4);
+ gs.dr_write(SLOT_DATA1, bp.index);
+ }
+
+ bool perform_step(unsigned int step)
+ {
+ switch (state) {
+ case STATE_START:
+ bp.index = 0;
+ write_new_index_program();
+ state = STATE_CHECK_INDEX;
+ break;
+
+ case STATE_CHECK_INDEX:
+ if (gs.dr_read(SLOT_DATA1) != bp.index) {
+ // We've exhausted breakpoints without finding an appropriate one.
+ gs.send_packet("E58");
+ return true;
+ }
+
+ gs.dr_write32(0, csrr(S0, CSR_TDATA0));
+ gs.dr_write_store(1, S0, SLOT_DATA0);
+ gs.dr_write_jump(2);
+ state = STATE_CHECK_MCONTROL;
+ break;
+
+ case STATE_CHECK_MCONTROL:
+ {
+ reg_t mcontrol = gs.dr_read(SLOT_DATA0);
+ unsigned int type = mcontrol >> (gs.xlen - 4);
+ if (type == 0) {
+ // We've exhausted breakpoints without finding an appropriate one.
+ gs.send_packet("E58");
+ return true;
+ }
+
+ if (type == 2 &&
+ get_field(mcontrol, MCONTROL_ACTION) == MCONTROL_ACTION_NONE) {
+ // Found an unused trigger.
+ gs.dr_write_load(0, S0, SLOT_DATA1);
+ gs.dr_write32(1, csrw(S0, CSR_TDATA0));
+ gs.dr_write_jump(2);
+ mcontrol = set_field(0, MCONTROL_ACTION, MCONTROL_ACTION_DEBUG_MODE);
+ 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_S, 1);
+ mcontrol = set_field(mcontrol, MCONTROL_U, 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);
+ gs.dr_write(SLOT_DATA1, mcontrol);
+ state = STATE_WRITE_ADDRESS;
+ } else {
+ bp.index++;
+ write_new_index_program();
+ state = STATE_CHECK_INDEX;
+ }
+ }
+ break;
+
+ case STATE_WRITE_ADDRESS:
+ {
+ gs.dr_write_load(0, S0, SLOT_DATA1);
+ gs.dr_write32(1, csrw(S0, CSR_TDATA1));
+ gs.dr_write_jump(2);
+ gs.dr_write(SLOT_DATA1, bp.vaddr);
+ gs.set_interrupt(0);
+ gs.send_packet("OK");
+
+ gs.hardware_breakpoints.insert(bp);
+
+ return true;
+ }
+ }
+
+ gs.set_interrupt(0);
+ return false;
+ }
+
+ private:
+ enum {
+ STATE_START,
+ STATE_CHECK_INDEX,
+ STATE_CHECK_MCONTROL,
+ STATE_WRITE_ADDRESS
+ } state;
+ hardware_breakpoint_t bp;
+};
+
+class hardware_breakpoint_remove_op_t : public operation_t
+{
+ public:
+ hardware_breakpoint_remove_op_t(gdbserver_t& gdbserver,
+ hardware_breakpoint_t bp) :
+ operation_t(gdbserver), bp(bp) {};
+
+ 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_write_jump(3);
+ gs.set_interrupt(0);
+ return true;
+ }
+
+ private:
+ hardware_breakpoint_t bp;
+};
+