]>
Commit | Line | Data |
---|---|---|
5c5d69b0 AB |
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 <[email protected]> | |
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" | |
cbafa236 AB |
42 | #include "exec/exec-all.h" |
43 | #include "disas/disas.h" | |
5c5d69b0 AB |
44 | #include "plugin.h" |
45 | #ifndef CONFIG_USER_ONLY | |
235537fa | 46 | #include "qemu/plugin-memory.h" |
5c5d69b0 AB |
47 | #include "hw/boards.h" |
48 | #endif | |
208b2d24 | 49 | #include "trace/mem.h" |
5c5d69b0 AB |
50 | |
51 | /* Uninstall and Reset handlers */ | |
52 | ||
53 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
54 | { | |
55 | plugin_reset_uninstall(id, cb, false); | |
56 | } | |
57 | ||
58 | void 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 | ||
70 | void 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 | ||
76 | void 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 | ||
82 | void 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 | ||
91 | void 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 | ||
98 | void 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 | ||
107 | void 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 | ||
117 | void 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 | ||
127 | void 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 | ||
136 | void 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 | ||
142 | void 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 | ||
148 | void | |
149 | qemu_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 | ||
171 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
172 | { | |
173 | return tb->n; | |
174 | } | |
175 | ||
176 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
177 | { | |
178 | return tb->vaddr; | |
179 | } | |
180 | ||
181 | struct qemu_plugin_insn * | |
182 | qemu_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 | ||
197 | const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) | |
198 | { | |
199 | return insn->data->data; | |
200 | } | |
201 | ||
202 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
203 | { | |
204 | return insn->data->len; | |
205 | } | |
206 | ||
207 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
208 | { | |
209 | return insn->vaddr; | |
210 | } | |
211 | ||
212 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
213 | { | |
214 | return insn->haddr; | |
215 | } | |
216 | ||
cbafa236 AB |
217 | char *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 | ||
5c5d69b0 AB |
223 | /* |
224 | * The memory queries allow the plugin to query information about a | |
225 | * memory access. | |
226 | */ | |
227 | ||
228 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
229 | { | |
230 | return info & TRACE_MEM_SZ_SHIFT_MASK; | |
231 | } | |
232 | ||
233 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
234 | { | |
235 | return !!(info & TRACE_MEM_SE); | |
236 | } | |
237 | ||
238 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
239 | { | |
240 | return !!(info & TRACE_MEM_BE); | |
241 | } | |
242 | ||
243 | bool 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 | ||
235537fa AB |
252 | #ifdef CONFIG_SOFTMMU |
253 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
254 | ||
255 | struct 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 | |
5c5d69b0 AB |
271 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, |
272 | uint64_t vaddr) | |
273 | { | |
274 | return NULL; | |
275 | } | |
235537fa AB |
276 | #endif |
277 | ||
308e7549 | 278 | bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
279 | { |
280 | #ifdef CONFIG_SOFTMMU | |
308e7549 | 281 | return haddr->is_io; |
235537fa AB |
282 | #else |
283 | return false; | |
284 | #endif | |
285 | } | |
286 | ||
287 | uint64_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 | } | |
5c5d69b0 AB |
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 | |
312 | static MachineState * get_ms(void) | |
313 | { | |
314 | return MACHINE(qdev_get_machine()); | |
315 | } | |
316 | #endif | |
317 | ||
318 | int 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 | ||
327 | int 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 | } | |
ca76a669 AB |
335 | |
336 | /* | |
337 | * Plugin output | |
338 | */ | |
339 | void qemu_plugin_outs(const char *string) | |
340 | { | |
341 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
342 | } |