]>
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" | |
5c5d69b0 | 39 | #include "tcg/tcg.h" |
cbafa236 | 40 | #include "exec/exec-all.h" |
787148bf | 41 | #include "exec/ram_addr.h" |
cbafa236 | 42 | #include "disas/disas.h" |
5c5d69b0 AB |
43 | #include "plugin.h" |
44 | #ifndef CONFIG_USER_ONLY | |
235537fa | 45 | #include "qemu/plugin-memory.h" |
5c5d69b0 AB |
46 | #include "hw/boards.h" |
47 | #endif | |
208b2d24 | 48 | #include "trace/mem.h" |
5c5d69b0 AB |
49 | |
50 | /* Uninstall and Reset handlers */ | |
51 | ||
52 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
53 | { | |
54 | plugin_reset_uninstall(id, cb, false); | |
55 | } | |
56 | ||
57 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
58 | { | |
59 | plugin_reset_uninstall(id, cb, true); | |
60 | } | |
61 | ||
62 | /* | |
63 | * Plugin Register Functions | |
64 | * | |
65 | * This allows the plugin to register callbacks for various events | |
66 | * during the translation. | |
67 | */ | |
68 | ||
69 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
70 | qemu_plugin_vcpu_simple_cb_t cb) | |
71 | { | |
72 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
73 | } | |
74 | ||
75 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
76 | qemu_plugin_vcpu_simple_cb_t cb) | |
77 | { | |
78 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
79 | } | |
80 | ||
81 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, | |
82 | qemu_plugin_vcpu_udata_cb_t cb, | |
83 | enum qemu_plugin_cb_flags flags, | |
84 | void *udata) | |
85 | { | |
cfd405ea AB |
86 | if (!tb->mem_only) { |
87 | plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], | |
88 | cb, flags, udata); | |
89 | } | |
5c5d69b0 AB |
90 | } |
91 | ||
92 | void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, | |
93 | enum qemu_plugin_op op, | |
94 | void *ptr, uint64_t imm) | |
95 | { | |
cfd405ea AB |
96 | if (!tb->mem_only) { |
97 | plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); | |
98 | } | |
5c5d69b0 AB |
99 | } |
100 | ||
101 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, | |
102 | qemu_plugin_vcpu_udata_cb_t cb, | |
103 | enum qemu_plugin_cb_flags flags, | |
104 | void *udata) | |
105 | { | |
cfd405ea AB |
106 | if (!insn->mem_only) { |
107 | plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], | |
108 | cb, flags, udata); | |
109 | } | |
5c5d69b0 AB |
110 | } |
111 | ||
112 | void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, | |
113 | enum qemu_plugin_op op, | |
114 | void *ptr, uint64_t imm) | |
115 | { | |
cfd405ea AB |
116 | if (!insn->mem_only) { |
117 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], | |
118 | 0, op, ptr, imm); | |
119 | } | |
5c5d69b0 AB |
120 | } |
121 | ||
122 | ||
cfd405ea AB |
123 | /* |
124 | * We always plant memory instrumentation because they don't finalise until | |
125 | * after the operation has complete. | |
126 | */ | |
5c5d69b0 AB |
127 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, |
128 | qemu_plugin_vcpu_mem_cb_t cb, | |
129 | enum qemu_plugin_cb_flags flags, | |
130 | enum qemu_plugin_mem_rw rw, | |
131 | void *udata) | |
132 | { | |
133 | plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], | |
cfd405ea | 134 | cb, flags, rw, udata); |
5c5d69b0 AB |
135 | } |
136 | ||
137 | void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, | |
138 | enum qemu_plugin_mem_rw rw, | |
139 | enum qemu_plugin_op op, void *ptr, | |
140 | uint64_t imm) | |
141 | { | |
142 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], | |
cfd405ea | 143 | rw, op, ptr, imm); |
5c5d69b0 AB |
144 | } |
145 | ||
146 | void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, | |
147 | qemu_plugin_vcpu_tb_trans_cb_t cb) | |
148 | { | |
149 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); | |
150 | } | |
151 | ||
152 | void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, | |
153 | qemu_plugin_vcpu_syscall_cb_t cb) | |
154 | { | |
155 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); | |
156 | } | |
157 | ||
158 | void | |
159 | qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, | |
160 | qemu_plugin_vcpu_syscall_ret_cb_t cb) | |
161 | { | |
162 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); | |
163 | } | |
164 | ||
165 | /* | |
166 | * Plugin Queries | |
167 | * | |
168 | * These are queries that the plugin can make to gauge information | |
169 | * from our opaque data types. We do not want to leak internal details | |
170 | * here just information useful to the plugin. | |
171 | */ | |
172 | ||
173 | /* | |
174 | * Translation block information: | |
175 | * | |
176 | * A plugin can query the virtual address of the start of the block | |
177 | * and the number of instructions in it. It can also get access to | |
178 | * each translated instruction. | |
179 | */ | |
180 | ||
181 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
182 | { | |
183 | return tb->n; | |
184 | } | |
185 | ||
186 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
187 | { | |
188 | return tb->vaddr; | |
189 | } | |
190 | ||
191 | struct qemu_plugin_insn * | |
192 | qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) | |
193 | { | |
cfd405ea | 194 | struct qemu_plugin_insn *insn; |
5c5d69b0 AB |
195 | if (unlikely(idx >= tb->n)) { |
196 | return NULL; | |
197 | } | |
cfd405ea AB |
198 | insn = g_ptr_array_index(tb->insns, idx); |
199 | insn->mem_only = tb->mem_only; | |
200 | return insn; | |
5c5d69b0 AB |
201 | } |
202 | ||
203 | /* | |
204 | * Instruction information | |
205 | * | |
206 | * These queries allow the plugin to retrieve information about each | |
207 | * instruction being translated. | |
208 | */ | |
209 | ||
210 | const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) | |
211 | { | |
212 | return insn->data->data; | |
213 | } | |
214 | ||
215 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
216 | { | |
217 | return insn->data->len; | |
218 | } | |
219 | ||
220 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
221 | { | |
222 | return insn->vaddr; | |
223 | } | |
224 | ||
225 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
226 | { | |
227 | return insn->haddr; | |
228 | } | |
229 | ||
cbafa236 AB |
230 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
231 | { | |
232 | CPUState *cpu = current_cpu; | |
233 | return plugin_disas(cpu, insn->vaddr, insn->data->len); | |
234 | } | |
235 | ||
7c4ab60f AB |
236 | const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) |
237 | { | |
238 | const char *sym = lookup_symbol(insn->vaddr); | |
239 | return sym[0] != 0 ? sym : NULL; | |
240 | } | |
241 | ||
5c5d69b0 AB |
242 | /* |
243 | * The memory queries allow the plugin to query information about a | |
244 | * memory access. | |
245 | */ | |
246 | ||
247 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
248 | { | |
249 | return info & TRACE_MEM_SZ_SHIFT_MASK; | |
250 | } | |
251 | ||
252 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
253 | { | |
254 | return !!(info & TRACE_MEM_SE); | |
255 | } | |
256 | ||
257 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
258 | { | |
259 | return !!(info & TRACE_MEM_BE); | |
260 | } | |
261 | ||
262 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
263 | { | |
264 | return !!(info & TRACE_MEM_ST); | |
265 | } | |
266 | ||
267 | /* | |
268 | * Virtual Memory queries | |
269 | */ | |
270 | ||
235537fa AB |
271 | #ifdef CONFIG_SOFTMMU |
272 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
a2b88169 | 273 | #endif |
235537fa AB |
274 | |
275 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, | |
276 | uint64_t vaddr) | |
277 | { | |
a2b88169 | 278 | #ifdef CONFIG_SOFTMMU |
235537fa AB |
279 | CPUState *cpu = current_cpu; |
280 | unsigned int mmu_idx = info >> TRACE_MEM_MMU_SHIFT; | |
281 | hwaddr_info.is_store = info & TRACE_MEM_ST; | |
282 | ||
283 | if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, | |
284 | info & TRACE_MEM_ST, &hwaddr_info)) { | |
285 | error_report("invalid use of qemu_plugin_get_hwaddr"); | |
286 | return NULL; | |
287 | } | |
288 | ||
289 | return &hwaddr_info; | |
235537fa | 290 | #else |
5c5d69b0 | 291 | return NULL; |
235537fa | 292 | #endif |
a2b88169 | 293 | } |
235537fa | 294 | |
308e7549 | 295 | bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
296 | { |
297 | #ifdef CONFIG_SOFTMMU | |
308e7549 | 298 | return haddr->is_io; |
235537fa AB |
299 | #else |
300 | return false; | |
301 | #endif | |
302 | } | |
303 | ||
787148bf | 304 | uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
305 | { |
306 | #ifdef CONFIG_SOFTMMU | |
307 | if (haddr) { | |
308 | if (!haddr->is_io) { | |
787148bf AL |
309 | RAMBlock *block; |
310 | ram_addr_t offset; | |
2d932039 | 311 | void *hostaddr = haddr->v.ram.hostaddr; |
787148bf AL |
312 | |
313 | block = qemu_ram_block_from_host(hostaddr, false, &offset); | |
314 | if (!block) { | |
2d932039 | 315 | error_report("Bad host ram pointer %p", haddr->v.ram.hostaddr); |
235537fa AB |
316 | abort(); |
317 | } | |
787148bf AL |
318 | |
319 | return block->offset + offset + block->mr->addr; | |
235537fa | 320 | } else { |
787148bf AL |
321 | MemoryRegionSection *mrs = haddr->v.io.section; |
322 | return haddr->v.io.offset + mrs->mr->addr; | |
235537fa AB |
323 | } |
324 | } | |
325 | #endif | |
326 | return 0; | |
327 | } | |
5c5d69b0 | 328 | |
b853a79f AB |
329 | const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) |
330 | { | |
331 | #ifdef CONFIG_SOFTMMU | |
332 | if (h && h->is_io) { | |
333 | MemoryRegionSection *mrs = h->v.io.section; | |
334 | if (!mrs->mr->name) { | |
335 | unsigned long maddr = 0xffffffff & (uintptr_t) mrs->mr; | |
336 | g_autofree char *temp = g_strdup_printf("anon%08lx", maddr); | |
337 | return g_intern_string(temp); | |
338 | } else { | |
339 | return g_intern_string(mrs->mr->name); | |
340 | } | |
341 | } else { | |
342 | return g_intern_static_string("RAM"); | |
343 | } | |
344 | #else | |
345 | return g_intern_static_string("Invalid"); | |
346 | #endif | |
347 | } | |
348 | ||
5c5d69b0 AB |
349 | /* |
350 | * Queries to the number and potential maximum number of vCPUs there | |
351 | * will be. This helps the plugin dimension per-vcpu arrays. | |
352 | */ | |
353 | ||
354 | #ifndef CONFIG_USER_ONLY | |
355 | static MachineState * get_ms(void) | |
356 | { | |
357 | return MACHINE(qdev_get_machine()); | |
358 | } | |
359 | #endif | |
360 | ||
361 | int qemu_plugin_n_vcpus(void) | |
362 | { | |
363 | #ifdef CONFIG_USER_ONLY | |
364 | return -1; | |
365 | #else | |
366 | return get_ms()->smp.cpus; | |
367 | #endif | |
368 | } | |
369 | ||
370 | int qemu_plugin_n_max_vcpus(void) | |
371 | { | |
372 | #ifdef CONFIG_USER_ONLY | |
373 | return -1; | |
374 | #else | |
375 | return get_ms()->smp.max_cpus; | |
376 | #endif | |
377 | } | |
ca76a669 AB |
378 | |
379 | /* | |
380 | * Plugin output | |
381 | */ | |
382 | void qemu_plugin_outs(const char *string) | |
383 | { | |
384 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
385 | } |