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