qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at master 342 lines 9.3 kB view raw
1/* 2 * QEMU Plugin API 3 * 4 * This provides the API that is available to the plugins to interact 5 * with QEMU. We have to be careful not to expose internal details of 6 * how QEMU works so we abstract out things like translation and 7 * instructions to anonymous data types: 8 * 9 * qemu_plugin_tb 10 * qemu_plugin_insn 11 * 12 * Which can then be passed back into the API to do additional things. 13 * As such all the public functions in here are exported in 14 * qemu-plugin.h. 15 * 16 * The general life-cycle of a plugin is: 17 * 18 * - plugin is loaded, public qemu_plugin_install called 19 * - the install func registers callbacks for events 20 * - usually an atexit_cb is registered to dump info at the end 21 * - when a registered event occurs the plugin is called 22 * - some events pass additional info 23 * - during translation the plugin can decide to instrument any 24 * instruction 25 * - when QEMU exits all the registered atexit callbacks are called 26 * 27 * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> 28 * Copyright (C) 2019, Linaro 29 * 30 * License: GNU GPL, version 2 or later. 31 * See the COPYING file in the top-level directory. 32 * 33 * SPDX-License-Identifier: GPL-2.0-or-later 34 * 35 */ 36 37#include "qemu/osdep.h" 38#include "qemu/plugin.h" 39#include "cpu.h" 40#include "sysemu/sysemu.h" 41#include "tcg/tcg.h" 42#include "exec/exec-all.h" 43#include "disas/disas.h" 44#include "plugin.h" 45#ifndef CONFIG_USER_ONLY 46#include "qemu/plugin-memory.h" 47#include "hw/boards.h" 48#endif 49#include "trace/mem.h" 50 51/* Uninstall and Reset handlers */ 52 53void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) 54{ 55 plugin_reset_uninstall(id, cb, false); 56} 57 58void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) 59{ 60 plugin_reset_uninstall(id, cb, true); 61} 62 63/* 64 * Plugin Register Functions 65 * 66 * This allows the plugin to register callbacks for various events 67 * during the translation. 68 */ 69 70void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, 71 qemu_plugin_vcpu_simple_cb_t cb) 72{ 73 plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); 74} 75 76void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, 77 qemu_plugin_vcpu_simple_cb_t cb) 78{ 79 plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); 80} 81 82void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, 83 qemu_plugin_vcpu_udata_cb_t cb, 84 enum qemu_plugin_cb_flags flags, 85 void *udata) 86{ 87 plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], 88 cb, flags, udata); 89} 90 91void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, 92 enum qemu_plugin_op op, 93 void *ptr, uint64_t imm) 94{ 95 plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); 96} 97 98void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, 99 qemu_plugin_vcpu_udata_cb_t cb, 100 enum qemu_plugin_cb_flags flags, 101 void *udata) 102{ 103 plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], 104 cb, flags, udata); 105} 106 107void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, 108 enum qemu_plugin_op op, 109 void *ptr, uint64_t imm) 110{ 111 plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], 112 0, op, ptr, imm); 113} 114 115 116 117void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, 118 qemu_plugin_vcpu_mem_cb_t cb, 119 enum qemu_plugin_cb_flags flags, 120 enum qemu_plugin_mem_rw rw, 121 void *udata) 122{ 123 plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], 124 cb, flags, rw, udata); 125} 126 127void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, 128 enum qemu_plugin_mem_rw rw, 129 enum qemu_plugin_op op, void *ptr, 130 uint64_t imm) 131{ 132 plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], 133 rw, op, ptr, imm); 134} 135 136void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, 137 qemu_plugin_vcpu_tb_trans_cb_t cb) 138{ 139 plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); 140} 141 142void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, 143 qemu_plugin_vcpu_syscall_cb_t cb) 144{ 145 plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); 146} 147 148void 149qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, 150 qemu_plugin_vcpu_syscall_ret_cb_t cb) 151{ 152 plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); 153} 154 155/* 156 * Plugin Queries 157 * 158 * These are queries that the plugin can make to gauge information 159 * from our opaque data types. We do not want to leak internal details 160 * here just information useful to the plugin. 161 */ 162 163/* 164 * Translation block information: 165 * 166 * A plugin can query the virtual address of the start of the block 167 * and the number of instructions in it. It can also get access to 168 * each translated instruction. 169 */ 170 171size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) 172{ 173 return tb->n; 174} 175 176uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) 177{ 178 return tb->vaddr; 179} 180 181struct qemu_plugin_insn * 182qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) 183{ 184 if (unlikely(idx >= tb->n)) { 185 return NULL; 186 } 187 return g_ptr_array_index(tb->insns, idx); 188} 189 190/* 191 * Instruction information 192 * 193 * These queries allow the plugin to retrieve information about each 194 * instruction being translated. 195 */ 196 197const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) 198{ 199 return insn->data->data; 200} 201 202size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) 203{ 204 return insn->data->len; 205} 206 207uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) 208{ 209 return insn->vaddr; 210} 211 212void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) 213{ 214 return insn->haddr; 215} 216 217char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) 218{ 219 CPUState *cpu = current_cpu; 220 return plugin_disas(cpu, insn->vaddr, insn->data->len); 221} 222 223/* 224 * The memory queries allow the plugin to query information about a 225 * memory access. 226 */ 227 228unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) 229{ 230 return info & TRACE_MEM_SZ_SHIFT_MASK; 231} 232 233bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) 234{ 235 return !!(info & TRACE_MEM_SE); 236} 237 238bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) 239{ 240 return !!(info & TRACE_MEM_BE); 241} 242 243bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) 244{ 245 return !!(info & TRACE_MEM_ST); 246} 247 248/* 249 * Virtual Memory queries 250 */ 251 252#ifdef CONFIG_SOFTMMU 253static __thread struct qemu_plugin_hwaddr hwaddr_info; 254 255struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, 256 uint64_t vaddr) 257{ 258 CPUState *cpu = current_cpu; 259 unsigned int mmu_idx = info >> TRACE_MEM_MMU_SHIFT; 260 hwaddr_info.is_store = info & TRACE_MEM_ST; 261 262 if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, 263 info & TRACE_MEM_ST, &hwaddr_info)) { 264 error_report("invalid use of qemu_plugin_get_hwaddr"); 265 return NULL; 266 } 267 268 return &hwaddr_info; 269} 270#else 271struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, 272 uint64_t vaddr) 273{ 274 return NULL; 275} 276#endif 277 278bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) 279{ 280#ifdef CONFIG_SOFTMMU 281 return haddr->is_io; 282#else 283 return false; 284#endif 285} 286 287uint64_t qemu_plugin_hwaddr_device_offset(const struct qemu_plugin_hwaddr *haddr) 288{ 289#ifdef CONFIG_SOFTMMU 290 if (haddr) { 291 if (!haddr->is_io) { 292 ram_addr_t ram_addr = qemu_ram_addr_from_host((void *) haddr->v.ram.hostaddr); 293 if (ram_addr == RAM_ADDR_INVALID) { 294 error_report("Bad ram pointer %"PRIx64"", haddr->v.ram.hostaddr); 295 abort(); 296 } 297 return ram_addr; 298 } else { 299 return haddr->v.io.offset; 300 } 301 } 302#endif 303 return 0; 304} 305 306/* 307 * Queries to the number and potential maximum number of vCPUs there 308 * will be. This helps the plugin dimension per-vcpu arrays. 309 */ 310 311#ifndef CONFIG_USER_ONLY 312static MachineState * get_ms(void) 313{ 314 return MACHINE(qdev_get_machine()); 315} 316#endif 317 318int qemu_plugin_n_vcpus(void) 319{ 320#ifdef CONFIG_USER_ONLY 321 return -1; 322#else 323 return get_ms()->smp.cpus; 324#endif 325} 326 327int qemu_plugin_n_max_vcpus(void) 328{ 329#ifdef CONFIG_USER_ONLY 330 return -1; 331#else 332 return get_ms()->smp.max_cpus; 333#endif 334} 335 336/* 337 * Plugin output 338 */ 339void qemu_plugin_outs(const char *string) 340{ 341 qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); 342}