+static int riscv013_hart_count(struct target *target)
+{
+ dm013_info_t *dm = get_dm(target);
+ assert(dm);
+ return dm->hart_count;
+}
+
+static unsigned riscv013_data_bits(struct target *target)
+{
+ RISCV013_INFO(info);
+ /* TODO: Once there is a spec for discovering abstract commands, we can
+ * take those into account as well. For now we assume abstract commands
+ * support XLEN-wide accesses. */
+ if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
+ return riscv_xlen(target);
+
+ if (get_field(info->sbcs, DM_SBCS_SBACCESS128))
+ return 128;
+ if (get_field(info->sbcs, DM_SBCS_SBACCESS64))
+ return 64;
+ if (get_field(info->sbcs, DM_SBCS_SBACCESS32))
+ return 32;
+ if (get_field(info->sbcs, DM_SBCS_SBACCESS16))
+ return 16;
+ if (get_field(info->sbcs, DM_SBCS_SBACCESS8))
+ return 8;
+
+ return riscv_xlen(target);
+}
+
+static int prep_for_vector_access(struct target *target, uint64_t *vtype,
+ uint64_t *vl, unsigned *debug_vl)
+{
+ RISCV_INFO(r);
+ /* TODO: this continuous save/restore is terrible for performance. */
+ /* Write vtype and vl. */
+ unsigned encoded_vsew;
+ switch (riscv_xlen(target)) {
+ case 32:
+ encoded_vsew = 2;
+ break;
+ case 64:
+ encoded_vsew = 3;
+ break;
+ default:
+ LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
+ return ERROR_FAIL;
+ }
+
+ /* Save vtype and vl. */
+ if (register_read(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_read(target, vl, GDB_REGNO_VL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
+ return ERROR_FAIL;
+ *debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8,
+ riscv_xlen(target));
+ if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
+static int cleanup_after_vector_access(struct target *target, uint64_t vtype,
+ uint64_t vl)
+{
+ /* Restore vtype and vl. */
+ if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+static int riscv013_get_register_buf(struct target *target,
+ uint8_t *value, int regno)
+{
+ assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
+
+ riscv_reg_t s0;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t mstatus;
+ if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t vtype, vl;
+ unsigned debug_vl;
+ if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
+ return ERROR_FAIL;
+
+ unsigned vnum = regno - GDB_REGNO_V0;
+ unsigned xlen = riscv_xlen(target);
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_insert(&program, vmv_x_s(S0, vnum));
+ riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
+
+ int result = ERROR_OK;
+ for (unsigned i = 0; i < debug_vl; i++) {
+ /* Executing the program might result in an exception if there is some
+ * issue with the vector implementation/instructions we're using. If that
+ * happens, attempt to restore as usual. We may have clobbered the
+ * vector register we tried to read already.
+ * For other failures, we just return error because things are probably
+ * so messed up that attempting to restore isn't going to help. */
+ result = riscv_program_exec(&program, target);
+ if (result == ERROR_OK) {
+ uint64_t v;
+ if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ buf_set_u64(value, xlen * i, xlen, v);
+ } else {
+ break;
+ }
+ }
+
+ if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return result;
+}
+
+static int riscv013_set_register_buf(struct target *target,
+ int regno, const uint8_t *value)
+{
+ assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
+
+ riscv_reg_t s0;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t mstatus;
+ if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t vtype, vl;
+ unsigned debug_vl;
+ if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
+ return ERROR_FAIL;
+
+ unsigned vnum = regno - GDB_REGNO_V0;
+ unsigned xlen = riscv_xlen(target);
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
+ int result = ERROR_OK;
+ for (unsigned i = 0; i < debug_vl; i++) {
+ if (register_write_direct(target, GDB_REGNO_S0,
+ buf_get_u64(value, xlen * i, xlen)) != ERROR_OK)
+ return ERROR_FAIL;
+ result = riscv_program_exec(&program, target);
+ if (result != ERROR_OK)
+ break;
+ }
+
+ if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return result;
+}
+