@samitouri / QOSamiQemu / commits / 09d7b5d49a

target/riscv: Enable disassembly via capstone

In capstone v5, riscv support is spare, but v6 is pretty good. Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

Richard Henderson committed Aug 8, 2026 at 11:23 UTC 09d7b5d49ad6dff06c67ae61993fa5657f08dd7c
3 files changed +133 -1
disas/capstone.c
+19
@@ -49,6 +49,20 @@ static const cs_opt_skipdata cap_skipdata_s390x = {
49 .callback = cap_skipdata_s390x_cb
50 };
51
52 +/* Similarly for RISCV */
53 +static size_t CAPSTONE_API
54 +cap_skipdata_riscv_cb(const uint8_t *code, size_t code_size,
55 + size_t offset, void *user_data)
56 +{
57 + /* See insn_len() from target/riscv/internals.h */
58 + return (code[offset] & 3) == 3 ? 4 : 2;
59 +}
60 +
61 +static const cs_opt_skipdata cap_skipdata_riscv = {
62 + .mnemonic = ".byte",
63 + .callback = cap_skipdata_riscv_cb
64 +};
65 +
66 /*
67 * Initialize the Capstone library.
68 *
@@ -76,6 +90,11 @@ static cs_err cap_disas_start(disassemble_info *info, csh *handle)
90 cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON);
91
92 switch (info->cap_arch) {
93 + case CS_ARCH_RISCV:
94 + cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
95 + (uintptr_t)&cap_skipdata_riscv);
96 + break;
97 +
98 case CS_ARCH_SYSZ:
99 cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
100 (uintptr_t)&cap_skipdata_s390x);
include/disas/capstone.h
+34
@@ -11,6 +11,8 @@
11
12 /* Just enough to allow backends to init without ifdefs. */
13
14 +#define CS_API_MAJOR 0
15 +
16 #define CS_ARCH_ARM -1
17 #define CS_ARCH_ARM64 -1
18 #define CS_ARCH_MIPS -1
@@ -37,4 +39,36 @@
39 #define CS_MODE_MIPS64 0
40
41 #endif /* CONFIG_CAPSTONE */
42 +
43 +#if CS_API_MAJOR < 5
44 +#define CS_ARCH_RISCV -1
45 +#define CS_MODE_RISCV32 0
46 +#define CS_MODE_RISCV64 0
47 +#define CS_MODE_RISCV_C 0
48 +#elif CS_API_MAJOR == 5
49 +/* The C symbol name changed between v5 and v6 */
50 +#define CS_MODE_RISCV_C CS_MODE_RISCVC
51 +#endif
52 +#if CS_API_MAJOR < 6
53 +#define CS_MODE_RISCV_FD 0
54 +#define CS_MODE_RISCV_V 0
55 +#define CS_MODE_RISCV_ZFINX 0
56 +#define CS_MODE_RISCV_ZCMP_ZCMT_ZCE 0
57 +#define CS_MODE_RISCV_ZICFISS 0
58 +#define CS_MODE_RISCV_E 0
59 +#define CS_MODE_RISCV_A 0
60 +#define CS_MODE_RISCV_COREV 0
61 +#define CS_MODE_RISCV_THEAD 0
62 +#define CS_MODE_RISCV_SIFIVE 0
63 +#define CS_MODE_RISCV_BITMANIP 0
64 +#define CS_MODE_RISCV_ZBA 0
65 +#define CS_MODE_RISCV_ZBB 0
66 +#define CS_MODE_RISCV_ZBC 0
67 +#define CS_MODE_RISCV_ZBKB 0
68 +#define CS_MODE_RISCV_ZBKC 0
69 +#define CS_MODE_RISCV_ZBKX 0
70 +#define CS_MODE_RISCV_ZBS 0
71 +#define CS_MODE_RISCV_VENTANA 0
72 +#endif
73 +
74 #endif /* QEMU_CAPSTONE_H */
target/riscv/cpu.c
+80 -1
@@ -39,6 +39,7 @@
39 #include "system/tcg.h"
40 #include "kvm/kvm_riscv.h"
41 #include "tcg/tcg-cpu.h"
42 +#include "disas/capstone.h"
43 #if !defined(CONFIG_USER_ONLY)
44 #include "target/riscv/tcg/debug.h"
45 #endif
@@ -1099,6 +1100,7 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info)
1100 {
1101 const RISCVCPU *cpu = RISCV_CPU(s);
1102 const CPURISCVState *env = &cpu->env;
1103 + int cap_mode;
1104
1105 info->target_info = &cpu->cfg;
1106
@@ -1111,16 +1113,93 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info)
1113 switch (env->xl) {
1114 case MXL_RV32:
1115 info->print_insn = print_insn_riscv32;
1116 + cap_mode = CS_MODE_RISCV32;
1117 break;
1118 case MXL_RV64:
1119 info->print_insn = print_insn_riscv64;
1120 + cap_mode = CS_MODE_RISCV64;
1121 break;
1122 case MXL_RV128:
1123 info->print_insn = print_insn_riscv128;
1120 - break;
1124 + /* capstone v6 doesn't support RV128 */
1125 + return;
1126 default:
1127 g_assert_not_reached();
1128 }
1129 +
1130 + info->cap_arch = CS_ARCH_RISCV;
1131 + info->cap_insn_unit = 4;
1132 + info->cap_insn_split = 4;
1133 +
1134 + /*
1135 + * Capstone compresses some features together. See RISCV_getFeatureBits,
1136 + * which maps LLVM feature bits to capstone bits.
1137 + */
1138 + if (riscv_has_ext(env, RVC) || cpu->cfg.ext_zca) {
1139 + cap_mode |= CS_MODE_RISCV_C;
1140 + }
1141 + if (riscv_has_ext(env, RVF)) {
1142 + cap_mode |= CS_MODE_RISCV_FD;
1143 + }
1144 + if (riscv_has_ext(env, RVV)) {
1145 + cap_mode |= CS_MODE_RISCV_V;
1146 + }
1147 + if (cpu->cfg.ext_zfinx || cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx) {
1148 + cap_mode |= CS_MODE_RISCV_ZFINX;
1149 + }
1150 + if (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt || cpu->cfg.ext_zce) {
1151 + cap_mode |= CS_MODE_RISCV_ZCMP_ZCMT_ZCE;
1152 + }
1153 + if (cpu->cfg.ext_zicfiss) {
1154 + cap_mode |= CS_MODE_RISCV_ZICFISS;
1155 + }
1156 + if (riscv_has_ext(env, RVE)) {
1157 + cap_mode |= CS_MODE_RISCV_E;
1158 + }
1159 + if (riscv_has_ext(env, RVA)) {
1160 + cap_mode |= CS_MODE_RISCV_A;
1161 + }
1162 + if (cpu->cfg.ext_xlrbr) {
1163 + cap_mode |= CS_MODE_RISCV_BITMANIP;
1164 + }
1165 + if (cpu->cfg.ext_zba) {
1166 + cap_mode |= CS_MODE_RISCV_ZBA;
1167 + }
1168 + if (cpu->cfg.ext_zbb) {
1169 + cap_mode |= CS_MODE_RISCV_ZBB;
1170 + }
1171 + if (cpu->cfg.ext_zbc) {
1172 + cap_mode |= CS_MODE_RISCV_ZBC;
1173 + }
1174 + if (cpu->cfg.ext_zbkb) {
1175 + cap_mode |= CS_MODE_RISCV_ZBKB;
1176 + }
1177 + if (cpu->cfg.ext_zbkc) {
1178 + cap_mode |= CS_MODE_RISCV_ZBKC;
1179 + }
1180 + if (cpu->cfg.ext_zbkx) {
1181 + cap_mode |= CS_MODE_RISCV_ZBKX;
1182 + }
1183 + if (cpu->cfg.ext_zbs) {
1184 + cap_mode |= CS_MODE_RISCV_ZBS;
1185 + }
1186 + if (cpu->cfg.ext_xtheadba ||
1187 + cpu->cfg.ext_xtheadbb ||
1188 + cpu->cfg.ext_xtheadbs ||
1189 + cpu->cfg.ext_xtheadcmo ||
1190 + cpu->cfg.ext_xtheadcondmov ||
1191 + cpu->cfg.ext_xtheadfmemidx ||
1192 + cpu->cfg.ext_xtheadfmv ||
1193 + cpu->cfg.ext_xtheadmac ||
1194 + cpu->cfg.ext_xtheadmemidx ||
1195 + cpu->cfg.ext_xtheadmempair ||
1196 + cpu->cfg.ext_xtheadsync) {
1197 + cap_mode |= CS_MODE_RISCV_THEAD;
1198 + }
1199 + if (cpu->cfg.ext_XVentanaCondOps) {
1200 + cap_mode |= CS_MODE_RISCV_VENTANA;
1201 + }
1202 + info->cap_mode = cap_mode;
1203 }
1204
1205 #ifndef CONFIG_USER_ONLY