]>
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 | |
8df5e27c | 11 | * qemu_plugin_register |
5c5d69b0 AB |
12 | * |
13 | * Which can then be passed back into the API to do additional things. | |
14 | * As such all the public functions in here are exported in | |
15 | * qemu-plugin.h. | |
16 | * | |
17 | * The general life-cycle of a plugin is: | |
18 | * | |
19 | * - plugin is loaded, public qemu_plugin_install called | |
20 | * - the install func registers callbacks for events | |
21 | * - usually an atexit_cb is registered to dump info at the end | |
22 | * - when a registered event occurs the plugin is called | |
23 | * - some events pass additional info | |
24 | * - during translation the plugin can decide to instrument any | |
25 | * instruction | |
26 | * - when QEMU exits all the registered atexit callbacks are called | |
27 | * | |
28 | * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> | |
29 | * Copyright (C) 2019, Linaro | |
30 | * | |
31 | * License: GNU GPL, version 2 or later. | |
32 | * See the COPYING file in the top-level directory. | |
33 | * | |
34 | * SPDX-License-Identifier: GPL-2.0-or-later | |
35 | * | |
36 | */ | |
37 | ||
38 | #include "qemu/osdep.h" | |
8df5e27c | 39 | #include "qemu/main-loop.h" |
5c5d69b0 | 40 | #include "qemu/plugin.h" |
cd617484 | 41 | #include "qemu/log.h" |
5c5d69b0 | 42 | #include "tcg/tcg.h" |
cbafa236 | 43 | #include "exec/exec-all.h" |
8df5e27c | 44 | #include "exec/gdbstub.h" |
787148bf | 45 | #include "exec/ram_addr.h" |
cbafa236 | 46 | #include "disas/disas.h" |
5c5d69b0 AB |
47 | #include "plugin.h" |
48 | #ifndef CONFIG_USER_ONLY | |
235537fa | 49 | #include "qemu/plugin-memory.h" |
5c5d69b0 | 50 | #include "hw/boards.h" |
91d40327 IA |
51 | #else |
52 | #include "qemu.h" | |
53 | #ifdef CONFIG_LINUX | |
54 | #include "loader.h" | |
55 | #endif | |
5c5d69b0 AB |
56 | #endif |
57 | ||
58 | /* Uninstall and Reset handlers */ | |
59 | ||
60 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
61 | { | |
62 | plugin_reset_uninstall(id, cb, false); | |
63 | } | |
64 | ||
65 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
66 | { | |
67 | plugin_reset_uninstall(id, cb, true); | |
68 | } | |
69 | ||
70 | /* | |
71 | * Plugin Register Functions | |
72 | * | |
73 | * This allows the plugin to register callbacks for various events | |
74 | * during the translation. | |
75 | */ | |
76 | ||
77 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
78 | qemu_plugin_vcpu_simple_cb_t cb) | |
79 | { | |
80 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
81 | } | |
82 | ||
83 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
84 | qemu_plugin_vcpu_simple_cb_t cb) | |
85 | { | |
86 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
87 | } | |
88 | ||
89 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, | |
90 | qemu_plugin_vcpu_udata_cb_t cb, | |
91 | enum qemu_plugin_cb_flags flags, | |
92 | void *udata) | |
93 | { | |
cfd405ea | 94 | if (!tb->mem_only) { |
21a3f62f | 95 | plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], |
cfd405ea AB |
96 | cb, flags, udata); |
97 | } | |
5c5d69b0 AB |
98 | } |
99 | ||
0bcebaba PB |
100 | void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( |
101 | struct qemu_plugin_tb *tb, | |
102 | enum qemu_plugin_op op, | |
103 | qemu_plugin_u64 entry, | |
104 | uint64_t imm) | |
105 | { | |
106 | if (!tb->mem_only) { | |
107 | plugin_register_inline_op_on_entry( | |
108 | &tb->cbs[PLUGIN_CB_INLINE], 0, op, entry, imm); | |
109 | } | |
110 | } | |
111 | ||
5c5d69b0 AB |
112 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, |
113 | qemu_plugin_vcpu_udata_cb_t cb, | |
114 | enum qemu_plugin_cb_flags flags, | |
115 | void *udata) | |
116 | { | |
cfd405ea | 117 | if (!insn->mem_only) { |
ac977170 RH |
118 | plugin_register_dyn_cb__udata( |
119 | &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], cb, flags, udata); | |
cfd405ea | 120 | } |
5c5d69b0 AB |
121 | } |
122 | ||
0bcebaba PB |
123 | void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( |
124 | struct qemu_plugin_insn *insn, | |
125 | enum qemu_plugin_op op, | |
126 | qemu_plugin_u64 entry, | |
127 | uint64_t imm) | |
128 | { | |
129 | if (!insn->mem_only) { | |
130 | plugin_register_inline_op_on_entry( | |
131 | &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], 0, op, entry, imm); | |
132 | } | |
133 | } | |
134 | ||
5c5d69b0 | 135 | |
cfd405ea AB |
136 | /* |
137 | * We always plant memory instrumentation because they don't finalise until | |
138 | * after the operation has complete. | |
139 | */ | |
5c5d69b0 AB |
140 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, |
141 | qemu_plugin_vcpu_mem_cb_t cb, | |
142 | enum qemu_plugin_cb_flags flags, | |
143 | enum qemu_plugin_mem_rw rw, | |
144 | void *udata) | |
145 | { | |
146 | plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], | |
0bcebaba | 147 | cb, flags, rw, udata); |
5c5d69b0 AB |
148 | } |
149 | ||
0bcebaba PB |
150 | void qemu_plugin_register_vcpu_mem_inline_per_vcpu( |
151 | struct qemu_plugin_insn *insn, | |
152 | enum qemu_plugin_mem_rw rw, | |
153 | enum qemu_plugin_op op, | |
154 | qemu_plugin_u64 entry, | |
155 | uint64_t imm) | |
156 | { | |
157 | plugin_register_inline_op_on_entry( | |
158 | &insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], rw, op, entry, imm); | |
159 | } | |
160 | ||
5c5d69b0 AB |
161 | void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, |
162 | qemu_plugin_vcpu_tb_trans_cb_t cb) | |
163 | { | |
164 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); | |
165 | } | |
166 | ||
167 | void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, | |
168 | qemu_plugin_vcpu_syscall_cb_t cb) | |
169 | { | |
170 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); | |
171 | } | |
172 | ||
173 | void | |
174 | qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, | |
175 | qemu_plugin_vcpu_syscall_ret_cb_t cb) | |
176 | { | |
177 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); | |
178 | } | |
179 | ||
180 | /* | |
181 | * Plugin Queries | |
182 | * | |
183 | * These are queries that the plugin can make to gauge information | |
184 | * from our opaque data types. We do not want to leak internal details | |
185 | * here just information useful to the plugin. | |
186 | */ | |
187 | ||
188 | /* | |
189 | * Translation block information: | |
190 | * | |
191 | * A plugin can query the virtual address of the start of the block | |
192 | * and the number of instructions in it. It can also get access to | |
193 | * each translated instruction. | |
194 | */ | |
195 | ||
196 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
197 | { | |
198 | return tb->n; | |
199 | } | |
200 | ||
201 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
202 | { | |
203 | return tb->vaddr; | |
204 | } | |
205 | ||
206 | struct qemu_plugin_insn * | |
207 | qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) | |
208 | { | |
cfd405ea | 209 | struct qemu_plugin_insn *insn; |
5c5d69b0 AB |
210 | if (unlikely(idx >= tb->n)) { |
211 | return NULL; | |
212 | } | |
cfd405ea AB |
213 | insn = g_ptr_array_index(tb->insns, idx); |
214 | insn->mem_only = tb->mem_only; | |
215 | return insn; | |
5c5d69b0 AB |
216 | } |
217 | ||
218 | /* | |
219 | * Instruction information | |
220 | * | |
221 | * These queries allow the plugin to retrieve information about each | |
222 | * instruction being translated. | |
223 | */ | |
224 | ||
225 | const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) | |
226 | { | |
227 | return insn->data->data; | |
228 | } | |
229 | ||
230 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
231 | { | |
232 | return insn->data->len; | |
233 | } | |
234 | ||
235 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
236 | { | |
237 | return insn->vaddr; | |
238 | } | |
239 | ||
240 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
241 | { | |
242 | return insn->haddr; | |
243 | } | |
244 | ||
cbafa236 AB |
245 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
246 | { | |
247 | CPUState *cpu = current_cpu; | |
248 | return plugin_disas(cpu, insn->vaddr, insn->data->len); | |
249 | } | |
250 | ||
7c4ab60f AB |
251 | const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) |
252 | { | |
253 | const char *sym = lookup_symbol(insn->vaddr); | |
254 | return sym[0] != 0 ? sym : NULL; | |
255 | } | |
256 | ||
5c5d69b0 AB |
257 | /* |
258 | * The memory queries allow the plugin to query information about a | |
259 | * memory access. | |
260 | */ | |
261 | ||
262 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
263 | { | |
37aff087 RH |
264 | MemOp op = get_memop(info); |
265 | return op & MO_SIZE; | |
5c5d69b0 AB |
266 | } |
267 | ||
268 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
269 | { | |
37aff087 RH |
270 | MemOp op = get_memop(info); |
271 | return op & MO_SIGN; | |
5c5d69b0 AB |
272 | } |
273 | ||
274 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
275 | { | |
37aff087 RH |
276 | MemOp op = get_memop(info); |
277 | return (op & MO_BSWAP) == MO_BE; | |
5c5d69b0 AB |
278 | } |
279 | ||
280 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
281 | { | |
37aff087 | 282 | return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; |
5c5d69b0 AB |
283 | } |
284 | ||
285 | /* | |
286 | * Virtual Memory queries | |
287 | */ | |
288 | ||
235537fa AB |
289 | #ifdef CONFIG_SOFTMMU |
290 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
a2b88169 | 291 | #endif |
235537fa AB |
292 | |
293 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, | |
294 | uint64_t vaddr) | |
295 | { | |
a2b88169 | 296 | #ifdef CONFIG_SOFTMMU |
235537fa | 297 | CPUState *cpu = current_cpu; |
37aff087 RH |
298 | unsigned int mmu_idx = get_mmuidx(info); |
299 | enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); | |
300 | hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; | |
235537fa | 301 | |
5413c37f RH |
302 | assert(mmu_idx < NB_MMU_MODES); |
303 | ||
235537fa | 304 | if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, |
37aff087 | 305 | hwaddr_info.is_store, &hwaddr_info)) { |
235537fa AB |
306 | error_report("invalid use of qemu_plugin_get_hwaddr"); |
307 | return NULL; | |
308 | } | |
309 | ||
310 | return &hwaddr_info; | |
235537fa | 311 | #else |
5c5d69b0 | 312 | return NULL; |
235537fa | 313 | #endif |
a2b88169 | 314 | } |
235537fa | 315 | |
308e7549 | 316 | bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
317 | { |
318 | #ifdef CONFIG_SOFTMMU | |
308e7549 | 319 | return haddr->is_io; |
235537fa AB |
320 | #else |
321 | return false; | |
322 | #endif | |
323 | } | |
324 | ||
787148bf | 325 | uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
326 | { |
327 | #ifdef CONFIG_SOFTMMU | |
328 | if (haddr) { | |
405c02d8 | 329 | return haddr->phys_addr; |
235537fa AB |
330 | } |
331 | #endif | |
332 | return 0; | |
333 | } | |
5c5d69b0 | 334 | |
b853a79f AB |
335 | const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) |
336 | { | |
337 | #ifdef CONFIG_SOFTMMU | |
338 | if (h && h->is_io) { | |
405c02d8 RH |
339 | MemoryRegion *mr = h->mr; |
340 | if (!mr->name) { | |
341 | unsigned maddr = (uintptr_t)mr; | |
342 | g_autofree char *temp = g_strdup_printf("anon%08x", maddr); | |
b853a79f AB |
343 | return g_intern_string(temp); |
344 | } else { | |
405c02d8 | 345 | return g_intern_string(mr->name); |
b853a79f AB |
346 | } |
347 | } else { | |
348 | return g_intern_static_string("RAM"); | |
349 | } | |
350 | #else | |
351 | return g_intern_static_string("Invalid"); | |
352 | #endif | |
353 | } | |
354 | ||
4a448b14 PB |
355 | int qemu_plugin_num_vcpus(void) |
356 | { | |
357 | return plugin_num_vcpus(); | |
358 | } | |
359 | ||
ca76a669 AB |
360 | /* |
361 | * Plugin output | |
362 | */ | |
363 | void qemu_plugin_outs(const char *string) | |
364 | { | |
365 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
366 | } | |
6a9e8a08 MM |
367 | |
368 | bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) | |
369 | { | |
370 | return name && value && qapi_bool_parse(name, value, ret, NULL); | |
371 | } | |
91d40327 IA |
372 | |
373 | /* | |
374 | * Binary path, start and end locations | |
375 | */ | |
376 | const char *qemu_plugin_path_to_binary(void) | |
377 | { | |
378 | char *path = NULL; | |
379 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 380 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
381 | path = g_strdup(ts->bprm->filename); |
382 | #endif | |
383 | return path; | |
384 | } | |
385 | ||
386 | uint64_t qemu_plugin_start_code(void) | |
387 | { | |
388 | uint64_t start = 0; | |
389 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 390 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
391 | start = ts->info->start_code; |
392 | #endif | |
393 | return start; | |
394 | } | |
395 | ||
396 | uint64_t qemu_plugin_end_code(void) | |
397 | { | |
398 | uint64_t end = 0; | |
399 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 400 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
401 | end = ts->info->end_code; |
402 | #endif | |
403 | return end; | |
404 | } | |
405 | ||
406 | uint64_t qemu_plugin_entry_code(void) | |
407 | { | |
408 | uint64_t entry = 0; | |
409 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 410 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
411 | entry = ts->info->entry; |
412 | #endif | |
413 | return entry; | |
414 | } | |
8df5e27c AB |
415 | |
416 | /* | |
417 | * Create register handles. | |
418 | * | |
419 | * We need to create a handle for each register so the plugin | |
420 | * infrastructure can call gdbstub to read a register. They are | |
421 | * currently just a pointer encapsulation of the gdb_reg but in | |
422 | * future may hold internal plugin state so its important plugin | |
423 | * authors are not tempted to treat them as numbers. | |
424 | * | |
425 | * We also construct a result array with those handles and some | |
426 | * ancillary data the plugin might find useful. | |
427 | */ | |
428 | ||
429 | static GArray *create_register_handles(GArray *gdbstub_regs) | |
430 | { | |
431 | GArray *find_data = g_array_new(true, true, | |
432 | sizeof(qemu_plugin_reg_descriptor)); | |
433 | ||
434 | for (int i = 0; i < gdbstub_regs->len; i++) { | |
435 | GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i); | |
436 | qemu_plugin_reg_descriptor desc; | |
437 | ||
438 | /* skip "un-named" regs */ | |
439 | if (!grd->name) { | |
440 | continue; | |
441 | } | |
442 | ||
443 | /* Create a record for the plugin */ | |
444 | desc.handle = GINT_TO_POINTER(grd->gdb_reg); | |
445 | desc.name = g_intern_string(grd->name); | |
446 | desc.feature = g_intern_string(grd->feature_name); | |
447 | g_array_append_val(find_data, desc); | |
448 | } | |
449 | ||
450 | return find_data; | |
451 | } | |
452 | ||
453 | GArray *qemu_plugin_get_registers(void) | |
454 | { | |
455 | g_assert(current_cpu); | |
456 | ||
457 | g_autoptr(GArray) regs = gdb_get_register_list(current_cpu); | |
458 | return create_register_handles(regs); | |
459 | } | |
460 | ||
461 | int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) | |
462 | { | |
463 | g_assert(current_cpu); | |
464 | ||
465 | return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg)); | |
466 | } | |
a3c2cf0b PB |
467 | |
468 | struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) | |
469 | { | |
470 | return plugin_scoreboard_new(element_size); | |
471 | } | |
472 | ||
473 | void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) | |
474 | { | |
475 | plugin_scoreboard_free(score); | |
476 | } | |
477 | ||
478 | void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, | |
479 | unsigned int vcpu_index) | |
480 | { | |
481 | g_assert(vcpu_index < qemu_plugin_num_vcpus()); | |
482 | /* we can't use g_array_index since entry size is not statically known */ | |
483 | char *base_ptr = score->data->data; | |
484 | return base_ptr + vcpu_index * g_array_get_element_size(score->data); | |
485 | } | |
8042e2ea PB |
486 | |
487 | static uint64_t *plugin_u64_address(qemu_plugin_u64 entry, | |
488 | unsigned int vcpu_index) | |
489 | { | |
490 | char *ptr = qemu_plugin_scoreboard_find(entry.score, vcpu_index); | |
491 | return (uint64_t *)(ptr + entry.offset); | |
492 | } | |
493 | ||
494 | void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, | |
495 | uint64_t added) | |
496 | { | |
497 | *plugin_u64_address(entry, vcpu_index) += added; | |
498 | } | |
499 | ||
500 | uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, | |
501 | unsigned int vcpu_index) | |
502 | { | |
503 | return *plugin_u64_address(entry, vcpu_index); | |
504 | } | |
505 | ||
506 | void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, | |
507 | uint64_t val) | |
508 | { | |
509 | *plugin_u64_address(entry, vcpu_index) = val; | |
510 | } | |
511 | ||
512 | uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) | |
513 | { | |
514 | uint64_t total = 0; | |
515 | for (int i = 0, n = qemu_plugin_num_vcpus(); i < n; ++i) { | |
516 | total += qemu_plugin_u64_get(entry, i); | |
517 | } | |
518 | return total; | |
519 | } |