Commit 09d7b5d49a for qemu.org
commit 09d7b5d49ad6dff06c67ae61993fa5657f08dd7c
Author: Richard Henderson <richard.henderson@linaro.org>
Date: Sat Aug 8 11:23:07 2026 -0700
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>
diff --git a/disas/capstone.c b/disas/capstone.c
index 8fb8d0724c..78062339d0 100644
--- a/disas/capstone.c
+++ b/disas/capstone.c
@@ -49,6 +49,20 @@ static const cs_opt_skipdata cap_skipdata_s390x = {
.callback = cap_skipdata_s390x_cb
};
+/* Similarly for RISCV */
+static size_t CAPSTONE_API
+cap_skipdata_riscv_cb(const uint8_t *code, size_t code_size,
+ size_t offset, void *user_data)
+{
+ /* See insn_len() from target/riscv/internals.h */
+ return (code[offset] & 3) == 3 ? 4 : 2;
+}
+
+static const cs_opt_skipdata cap_skipdata_riscv = {
+ .mnemonic = ".byte",
+ .callback = cap_skipdata_riscv_cb
+};
+
/*
* Initialize the Capstone library.
*
@@ -76,6 +90,11 @@ static cs_err cap_disas_start(disassemble_info *info, csh *handle)
cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON);
switch (info->cap_arch) {
+ case CS_ARCH_RISCV:
+ cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
+ (uintptr_t)&cap_skipdata_riscv);
+ break;
+
case CS_ARCH_SYSZ:
cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
(uintptr_t)&cap_skipdata_s390x);
diff --git a/include/disas/capstone.h b/include/disas/capstone.h
index c43033f7f6..5c78824da2 100644
--- a/include/disas/capstone.h
+++ b/include/disas/capstone.h
@@ -11,6 +11,8 @@
/* Just enough to allow backends to init without ifdefs. */
+#define CS_API_MAJOR 0
+
#define CS_ARCH_ARM -1
#define CS_ARCH_ARM64 -1
#define CS_ARCH_MIPS -1
@@ -37,4 +39,36 @@
#define CS_MODE_MIPS64 0
#endif /* CONFIG_CAPSTONE */
+
+#if CS_API_MAJOR < 5
+#define CS_ARCH_RISCV -1
+#define CS_MODE_RISCV32 0
+#define CS_MODE_RISCV64 0
+#define CS_MODE_RISCV_C 0
+#elif CS_API_MAJOR == 5
+/* The C symbol name changed between v5 and v6 */
+#define CS_MODE_RISCV_C CS_MODE_RISCVC
+#endif
+#if CS_API_MAJOR < 6
+#define CS_MODE_RISCV_FD 0
+#define CS_MODE_RISCV_V 0
+#define CS_MODE_RISCV_ZFINX 0
+#define CS_MODE_RISCV_ZCMP_ZCMT_ZCE 0
+#define CS_MODE_RISCV_ZICFISS 0
+#define CS_MODE_RISCV_E 0
+#define CS_MODE_RISCV_A 0
+#define CS_MODE_RISCV_COREV 0
+#define CS_MODE_RISCV_THEAD 0
+#define CS_MODE_RISCV_SIFIVE 0
+#define CS_MODE_RISCV_BITMANIP 0
+#define CS_MODE_RISCV_ZBA 0
+#define CS_MODE_RISCV_ZBB 0
+#define CS_MODE_RISCV_ZBC 0
+#define CS_MODE_RISCV_ZBKB 0
+#define CS_MODE_RISCV_ZBKC 0
+#define CS_MODE_RISCV_ZBKX 0
+#define CS_MODE_RISCV_ZBS 0
+#define CS_MODE_RISCV_VENTANA 0
+#endif
+
#endif /* QEMU_CAPSTONE_H */
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 23b5023dd3..e73ff53159 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -39,6 +39,7 @@
#include "system/tcg.h"
#include "kvm/kvm_riscv.h"
#include "tcg/tcg-cpu.h"
+#include "disas/capstone.h"
#if !defined(CONFIG_USER_ONLY)
#include "target/riscv/tcg/debug.h"
#endif
@@ -1099,6 +1100,7 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info)
{
const RISCVCPU *cpu = RISCV_CPU(s);
const CPURISCVState *env = &cpu->env;
+ int cap_mode;
info->target_info = &cpu->cfg;
@@ -1111,16 +1113,93 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info)
switch (env->xl) {
case MXL_RV32:
info->print_insn = print_insn_riscv32;
+ cap_mode = CS_MODE_RISCV32;
break;
case MXL_RV64:
info->print_insn = print_insn_riscv64;
+ cap_mode = CS_MODE_RISCV64;
break;
case MXL_RV128:
info->print_insn = print_insn_riscv128;
- break;
+ /* capstone v6 doesn't support RV128 */
+ return;
default:
g_assert_not_reached();
}
+
+ info->cap_arch = CS_ARCH_RISCV;
+ info->cap_insn_unit = 4;
+ info->cap_insn_split = 4;
+
+ /*
+ * Capstone compresses some features together. See RISCV_getFeatureBits,
+ * which maps LLVM feature bits to capstone bits.
+ */
+ if (riscv_has_ext(env, RVC) || cpu->cfg.ext_zca) {
+ cap_mode |= CS_MODE_RISCV_C;
+ }
+ if (riscv_has_ext(env, RVF)) {
+ cap_mode |= CS_MODE_RISCV_FD;
+ }
+ if (riscv_has_ext(env, RVV)) {
+ cap_mode |= CS_MODE_RISCV_V;
+ }
+ if (cpu->cfg.ext_zfinx || cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx) {
+ cap_mode |= CS_MODE_RISCV_ZFINX;
+ }
+ if (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt || cpu->cfg.ext_zce) {
+ cap_mode |= CS_MODE_RISCV_ZCMP_ZCMT_ZCE;
+ }
+ if (cpu->cfg.ext_zicfiss) {
+ cap_mode |= CS_MODE_RISCV_ZICFISS;
+ }
+ if (riscv_has_ext(env, RVE)) {
+ cap_mode |= CS_MODE_RISCV_E;
+ }
+ if (riscv_has_ext(env, RVA)) {
+ cap_mode |= CS_MODE_RISCV_A;
+ }
+ if (cpu->cfg.ext_xlrbr) {
+ cap_mode |= CS_MODE_RISCV_BITMANIP;
+ }
+ if (cpu->cfg.ext_zba) {
+ cap_mode |= CS_MODE_RISCV_ZBA;
+ }
+ if (cpu->cfg.ext_zbb) {
+ cap_mode |= CS_MODE_RISCV_ZBB;
+ }
+ if (cpu->cfg.ext_zbc) {
+ cap_mode |= CS_MODE_RISCV_ZBC;
+ }
+ if (cpu->cfg.ext_zbkb) {
+ cap_mode |= CS_MODE_RISCV_ZBKB;
+ }
+ if (cpu->cfg.ext_zbkc) {
+ cap_mode |= CS_MODE_RISCV_ZBKC;
+ }
+ if (cpu->cfg.ext_zbkx) {
+ cap_mode |= CS_MODE_RISCV_ZBKX;
+ }
+ if (cpu->cfg.ext_zbs) {
+ cap_mode |= CS_MODE_RISCV_ZBS;
+ }
+ if (cpu->cfg.ext_xtheadba ||
+ cpu->cfg.ext_xtheadbb ||
+ cpu->cfg.ext_xtheadbs ||
+ cpu->cfg.ext_xtheadcmo ||
+ cpu->cfg.ext_xtheadcondmov ||
+ cpu->cfg.ext_xtheadfmemidx ||
+ cpu->cfg.ext_xtheadfmv ||
+ cpu->cfg.ext_xtheadmac ||
+ cpu->cfg.ext_xtheadmemidx ||
+ cpu->cfg.ext_xtheadmempair ||
+ cpu->cfg.ext_xtheadsync) {
+ cap_mode |= CS_MODE_RISCV_THEAD;
+ }
+ if (cpu->cfg.ext_XVentanaCondOps) {
+ cap_mode |= CS_MODE_RISCV_VENTANA;
+ }
+ info->cap_mode = cap_mode;
}
#ifndef CONFIG_USER_ONLY