qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

disas: Support the Capstone disassembler library

If configured, prefer this over our rather dated copy of the
GPLv2-only binutils. This will be especially apparent with
the proposed vector extensions to TCG, as disas/i386.c does
not handle AVX.

Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

+274 -13
+26
configure
··· 375 375 cpuid_h="no" 376 376 avx2_opt="no" 377 377 zlib="yes" 378 + capstone="" 378 379 lzo="" 379 380 snappy="" 380 381 bzip2="" ··· 1294 1295 error_exit "vhost-user isn't available on win32" 1295 1296 fi 1296 1297 ;; 1298 + --disable-capstone) capstone="no" 1299 + ;; 1300 + --enable-capstone) capstone="yes" 1301 + ;; 1297 1302 *) 1298 1303 echo "ERROR: unknown option $opt" 1299 1304 echo "Try '$0 --help' for more information" ··· 1541 1546 vxhs Veritas HyperScale vDisk backend support 1542 1547 crypto-afalg Linux AF_ALG crypto backend driver 1543 1548 vhost-user vhost-user support 1549 + capstone capstone disassembler support 1544 1550 1545 1551 NOTE: The object files are built at the place where configure is launched 1546 1552 EOF ··· 4411 4417 fi 4412 4418 4413 4419 ########################################## 4420 + # capstone 4421 + 4422 + if test "$capstone" != no; then 4423 + if $pkg_config capstone; then 4424 + capstone=yes 4425 + QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags capstone)" 4426 + LIBS="$($pkg_config --libs capstone) $LIBS" 4427 + else 4428 + if test "$capstone" = yes; then 4429 + feature_not_found capstone 4430 + fi 4431 + capstone=no 4432 + fi 4433 + fi 4434 + 4435 + ########################################## 4414 4436 # check if we have fdatasync 4415 4437 4416 4438 fdatasync=no ··· 5468 5490 echo "avx2 optimization $avx2_opt" 5469 5491 echo "replication support $replication" 5470 5492 echo "VxHS block device $vxhs" 5493 + echo "capstone $capstone" 5471 5494 5472 5495 if test "$sdl_too_old" = "yes"; then 5473 5496 echo "-> Your SDL version is too old - please upgrade to have SDL support" ··· 6141 6164 6142 6165 if test "$ivshmem" = "yes" ; then 6143 6166 echo "CONFIG_IVSHMEM=y" >> $config_host_mak 6167 + fi 6168 + if test "$capstone" = "yes" ; then 6169 + echo "CONFIG_CAPSTONE=y" >> $config_host_mak 6144 6170 fi 6145 6171 6146 6172 # Hold two types of flag:
+206 -13
disas.c
··· 6 6 7 7 #include "cpu.h" 8 8 #include "disas/disas.h" 9 + #include "disas/capstone.h" 9 10 10 11 typedef struct CPUDebug { 11 12 struct disassemble_info info; ··· 171 172 return print_insn_objdump(pc, info, "OBJD-T"); 172 173 } 173 174 175 + #ifdef CONFIG_CAPSTONE 176 + /* Temporary storage for the capstone library. This will be alloced via 177 + malloc with a size private to the library; thus there's no reason not 178 + to share this across calls and across host vs target disassembly. */ 179 + static __thread cs_insn *cap_insn; 180 + 181 + /* Initialize the Capstone library. */ 182 + /* ??? It would be nice to cache this. We would need one handle for the 183 + host and one for the target. For most targets we can reset specific 184 + parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change 185 + CS_ARCH_* in this way. Thus we would need to be able to close and 186 + re-open the target handle with a different arch for the target in order 187 + to handle AArch64 vs AArch32 mode switching. */ 188 + static cs_err cap_disas_start(disassemble_info *info, csh *handle) 189 + { 190 + cs_mode cap_mode = info->cap_mode; 191 + cs_err err; 192 + 193 + cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN 194 + : CS_MODE_LITTLE_ENDIAN); 195 + 196 + err = cs_open(info->cap_arch, cap_mode, handle); 197 + if (err != CS_ERR_OK) { 198 + return err; 199 + } 200 + 201 + /* ??? There probably ought to be a better place to put this. */ 202 + if (info->cap_arch == CS_ARCH_X86) { 203 + /* We don't care about errors (if for some reason the library 204 + is compiled without AT&T syntax); the user will just have 205 + to deal with the Intel syntax. */ 206 + cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); 207 + } 208 + 209 + /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */ 210 + cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); 211 + 212 + /* Allocate temp space for cs_disasm_iter. */ 213 + if (cap_insn == NULL) { 214 + cap_insn = cs_malloc(*handle); 215 + if (cap_insn == NULL) { 216 + cs_close(handle); 217 + return CS_ERR_MEM; 218 + } 219 + } 220 + return CS_ERR_OK; 221 + } 222 + 223 + /* Disassemble SIZE bytes at PC for the target. */ 224 + static bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size) 225 + { 226 + uint8_t cap_buf[1024]; 227 + csh handle; 228 + cs_insn *insn; 229 + size_t csize = 0; 230 + 231 + if (cap_disas_start(info, &handle) != CS_ERR_OK) { 232 + return false; 233 + } 234 + insn = cap_insn; 235 + 236 + while (1) { 237 + size_t tsize = MIN(sizeof(cap_buf) - csize, size); 238 + const uint8_t *cbuf = cap_buf; 239 + 240 + target_read_memory(pc + csize, cap_buf + csize, tsize, info); 241 + csize += tsize; 242 + size -= tsize; 243 + 244 + while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { 245 + (*info->fprintf_func)(info->stream, 246 + "0x%08" PRIx64 ": %-12s %s\n", 247 + insn->address, insn->mnemonic, 248 + insn->op_str); 249 + } 250 + 251 + /* If the target memory is not consumed, go back for more... */ 252 + if (size != 0) { 253 + /* ... taking care to move any remaining fractional insn 254 + to the beginning of the buffer. */ 255 + if (csize != 0) { 256 + memmove(cap_buf, cbuf, csize); 257 + } 258 + continue; 259 + } 260 + 261 + /* Since the target memory is consumed, we should not have 262 + a remaining fractional insn. */ 263 + if (csize != 0) { 264 + (*info->fprintf_func)(info->stream, 265 + "Disassembler disagrees with translator " 266 + "over instruction decoding\n" 267 + "Please report this to qemu-devel@nongnu.org\n"); 268 + } 269 + break; 270 + } 271 + 272 + cs_close(&handle); 273 + return true; 274 + } 275 + 276 + /* Disassemble SIZE bytes at CODE for the host. */ 277 + static bool cap_disas_host(disassemble_info *info, void *code, size_t size) 278 + { 279 + csh handle; 280 + const uint8_t *cbuf; 281 + cs_insn *insn; 282 + uint64_t pc; 283 + 284 + if (cap_disas_start(info, &handle) != CS_ERR_OK) { 285 + return false; 286 + } 287 + insn = cap_insn; 288 + 289 + cbuf = code; 290 + pc = (uintptr_t)code; 291 + 292 + while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) { 293 + (*info->fprintf_func)(info->stream, 294 + "0x%08" PRIx64 ": %-12s %s\n", 295 + insn->address, insn->mnemonic, 296 + insn->op_str); 297 + } 298 + if (size != 0) { 299 + (*info->fprintf_func)(info->stream, 300 + "Disassembler disagrees with TCG over instruction encoding\n" 301 + "Please report this to qemu-devel@nongnu.org\n"); 302 + } 303 + 304 + cs_close(&handle); 305 + return true; 306 + } 307 + 308 + #if !defined(CONFIG_USER_ONLY) 309 + /* Disassemble COUNT insns at PC for the target. */ 310 + static bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count) 311 + { 312 + uint8_t cap_buf[32]; 313 + csh handle; 314 + cs_insn *insn; 315 + size_t csize = 0; 316 + 317 + if (cap_disas_start(info, &handle) != CS_ERR_OK) { 318 + return false; 319 + } 320 + insn = cap_insn; 321 + 322 + while (1) { 323 + /* We want to read memory for one insn, but generically we do not 324 + know how much memory that is. We have a small buffer which is 325 + known to be sufficient for all supported targets. Try to not 326 + read beyond the page, Just In Case. For even more simplicity, 327 + ignore the actual target page size and use a 1k boundary. If 328 + that turns out to be insufficient, we'll come back around the 329 + loop and read more. */ 330 + uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024); 331 + size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc); 332 + const uint8_t *cbuf = cap_buf; 333 + 334 + /* Make certain that we can make progress. */ 335 + assert(tsize != 0); 336 + info->read_memory_func(pc, cap_buf + csize, tsize, info); 337 + csize += tsize; 338 + 339 + if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { 340 + (*info->fprintf_func)(info->stream, 341 + "0x%08" PRIx64 ": %-12s %s\n", 342 + insn->address, insn->mnemonic, 343 + insn->op_str); 344 + if (--count <= 0) { 345 + break; 346 + } 347 + } 348 + memmove(cap_buf, cbuf, csize); 349 + } 350 + 351 + cs_close(&handle); 352 + return true; 353 + } 354 + #endif /* !CONFIG_USER_ONLY */ 355 + #else 356 + # define cap_disas_target(i, p, s) false 357 + # define cap_disas_host(i, p, s) false 358 + # define cap_disas_monitor(i, p, c) false 359 + #endif /* CONFIG_CAPSTONE */ 360 + 174 361 /* Disassemble this for me please... (debugging). */ 175 362 void target_disas(FILE *out, CPUState *cpu, target_ulong code, 176 363 target_ulong size) ··· 187 374 s.info.buffer_vma = code; 188 375 s.info.buffer_length = size; 189 376 s.info.print_address_func = generic_print_address; 377 + s.info.cap_arch = -1; 378 + s.info.cap_mode = 0; 190 379 191 380 #ifdef TARGET_WORDS_BIGENDIAN 192 381 s.info.endian = BFD_ENDIAN_BIG; ··· 198 387 cc->disas_set_info(cpu, &s.info); 199 388 } 200 389 390 + if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { 391 + return; 392 + } 393 + 201 394 if (s.info.print_insn == NULL) { 202 395 s.info.print_insn = print_insn_od_target; 203 396 } ··· 205 398 for (pc = code; size > 0; pc += count, size -= count) { 206 399 fprintf(out, "0x" TARGET_FMT_lx ": ", pc); 207 400 count = s.info.print_insn(pc, &s.info); 208 - #if 0 209 - { 210 - int i; 211 - uint8_t b; 212 - fprintf(out, " {"); 213 - for(i = 0; i < count; i++) { 214 - target_read_memory(pc + i, &b, 1, &s.info); 215 - fprintf(out, " %02x", b); 216 - } 217 - fprintf(out, " }"); 218 - } 219 - #endif 220 401 fprintf(out, "\n"); 221 402 if (count < 0) 222 403 break; ··· 244 425 s.info.buffer = code; 245 426 s.info.buffer_vma = (uintptr_t)code; 246 427 s.info.buffer_length = size; 428 + s.info.cap_arch = -1; 429 + s.info.cap_mode = 0; 247 430 248 431 #ifdef HOST_WORDS_BIGENDIAN 249 432 s.info.endian = BFD_ENDIAN_BIG; ··· 281 464 #elif defined(__hppa__) 282 465 print_insn = print_insn_hppa; 283 466 #endif 467 + 468 + if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { 469 + return; 470 + } 471 + 284 472 if (print_insn == NULL) { 285 473 print_insn = print_insn_od_host; 286 474 } ··· 343 531 monitor_disas_is_physical = is_physical; 344 532 s.info.read_memory_func = monitor_read_memory; 345 533 s.info.print_address_func = generic_print_address; 346 - 347 534 s.info.buffer_vma = pc; 535 + s.info.cap_arch = -1; 536 + s.info.cap_mode = 0; 348 537 349 538 #ifdef TARGET_WORDS_BIGENDIAN 350 539 s.info.endian = BFD_ENDIAN_BIG; ··· 354 543 355 544 if (cc->disas_set_info) { 356 545 cc->disas_set_info(cpu, &s.info); 546 + } 547 + 548 + if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) { 549 + return; 357 550 } 358 551 359 552 if (!s.info.print_insn) {
+4
include/disas/bfd.h
··· 371 371 /* Command line options specific to the target disassembler. */ 372 372 char * disassembler_options; 373 373 374 + /* Options for Capstone disassembly. */ 375 + int cap_arch; 376 + int cap_mode; 377 + 374 378 } disassemble_info; 375 379 376 380
+38
include/disas/capstone.h
··· 1 + #ifndef QEMU_CAPSTONE_H 2 + #define QEMU_CAPSTONE_H 1 3 + 4 + #ifdef CONFIG_CAPSTONE 5 + 6 + #include <capstone.h> 7 + 8 + #else 9 + 10 + /* Just enough to allow backends to init without ifdefs. */ 11 + 12 + #define CS_ARCH_ARM -1 13 + #define CS_ARCH_ARM64 -1 14 + #define CS_ARCH_MIPS -1 15 + #define CS_ARCH_X86 -1 16 + #define CS_ARCH_PPC -1 17 + #define CS_ARCH_SPARC -1 18 + #define CS_ARCH_SYSZ -1 19 + 20 + #define CS_MODE_LITTLE_ENDIAN 0 21 + #define CS_MODE_BIG_ENDIAN 0 22 + #define CS_MODE_ARM 0 23 + #define CS_MODE_16 0 24 + #define CS_MODE_32 0 25 + #define CS_MODE_64 0 26 + #define CS_MODE_THUMB 0 27 + #define CS_MODE_MCLASS 0 28 + #define CS_MODE_V8 0 29 + #define CS_MODE_MICRO 0 30 + #define CS_MODE_MIPS3 0 31 + #define CS_MODE_MIPS32R6 0 32 + #define CS_MODE_MIPSGP64 0 33 + #define CS_MODE_V9 0 34 + #define CS_MODE_MIPS32 0 35 + #define CS_MODE_MIPS64 0 36 + 37 + #endif /* CONFIG_CAPSTONE */ 38 + #endif /* QEMU_CAPSTONE_H */