]>
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" |
36bc99bc | 45 | #include "exec/translator.h" |
cbafa236 | 46 | #include "disas/disas.h" |
5c5d69b0 AB |
47 | #include "plugin.h" |
48 | #ifndef CONFIG_USER_ONLY | |
155fb465 | 49 | #include "exec/ram_addr.h" |
235537fa | 50 | #include "qemu/plugin-memory.h" |
5c5d69b0 | 51 | #include "hw/boards.h" |
91d40327 IA |
52 | #else |
53 | #include "qemu.h" | |
54 | #ifdef CONFIG_LINUX | |
55 | #include "loader.h" | |
56 | #endif | |
5c5d69b0 AB |
57 | #endif |
58 | ||
59 | /* Uninstall and Reset handlers */ | |
60 | ||
61 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
62 | { | |
63 | plugin_reset_uninstall(id, cb, false); | |
64 | } | |
65 | ||
66 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
67 | { | |
68 | plugin_reset_uninstall(id, cb, true); | |
69 | } | |
70 | ||
71 | /* | |
72 | * Plugin Register Functions | |
73 | * | |
74 | * This allows the plugin to register callbacks for various events | |
75 | * during the translation. | |
76 | */ | |
77 | ||
78 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
79 | qemu_plugin_vcpu_simple_cb_t cb) | |
80 | { | |
81 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
82 | } | |
83 | ||
84 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
85 | qemu_plugin_vcpu_simple_cb_t cb) | |
86 | { | |
87 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
88 | } | |
89 | ||
e5013259 RH |
90 | static bool tb_is_mem_only(void) |
91 | { | |
92 | return tb_cflags(tcg_ctx->gen_tb) & CF_MEMI_ONLY; | |
93 | } | |
94 | ||
5c5d69b0 AB |
95 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, |
96 | qemu_plugin_vcpu_udata_cb_t cb, | |
97 | enum qemu_plugin_cb_flags flags, | |
98 | void *udata) | |
99 | { | |
e5013259 | 100 | if (!tb_is_mem_only()) { |
db409c01 | 101 | plugin_register_dyn_cb__udata(&tb->cbs, cb, flags, udata); |
cfd405ea | 102 | } |
5c5d69b0 AB |
103 | } |
104 | ||
0bcebaba PB |
105 | void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( |
106 | struct qemu_plugin_tb *tb, | |
107 | enum qemu_plugin_op op, | |
108 | qemu_plugin_u64 entry, | |
109 | uint64_t imm) | |
110 | { | |
e5013259 | 111 | if (!tb_is_mem_only()) { |
db409c01 | 112 | plugin_register_inline_op_on_entry(&tb->cbs, 0, op, entry, imm); |
0bcebaba PB |
113 | } |
114 | } | |
115 | ||
5c5d69b0 AB |
116 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, |
117 | qemu_plugin_vcpu_udata_cb_t cb, | |
118 | enum qemu_plugin_cb_flags flags, | |
119 | void *udata) | |
120 | { | |
e5013259 | 121 | if (!tb_is_mem_only()) { |
db409c01 | 122 | plugin_register_dyn_cb__udata(&insn->insn_cbs, cb, flags, udata); |
cfd405ea | 123 | } |
5c5d69b0 AB |
124 | } |
125 | ||
0bcebaba PB |
126 | void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( |
127 | struct qemu_plugin_insn *insn, | |
128 | enum qemu_plugin_op op, | |
129 | qemu_plugin_u64 entry, | |
130 | uint64_t imm) | |
131 | { | |
e5013259 | 132 | if (!tb_is_mem_only()) { |
db409c01 | 133 | plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm); |
0bcebaba PB |
134 | } |
135 | } | |
136 | ||
5c5d69b0 | 137 | |
cfd405ea AB |
138 | /* |
139 | * We always plant memory instrumentation because they don't finalise until | |
140 | * after the operation has complete. | |
141 | */ | |
5c5d69b0 AB |
142 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, |
143 | qemu_plugin_vcpu_mem_cb_t cb, | |
144 | enum qemu_plugin_cb_flags flags, | |
145 | enum qemu_plugin_mem_rw rw, | |
146 | void *udata) | |
147 | { | |
db409c01 | 148 | plugin_register_vcpu_mem_cb(&insn->mem_cbs, cb, flags, rw, udata); |
5c5d69b0 AB |
149 | } |
150 | ||
0bcebaba PB |
151 | void qemu_plugin_register_vcpu_mem_inline_per_vcpu( |
152 | struct qemu_plugin_insn *insn, | |
153 | enum qemu_plugin_mem_rw rw, | |
154 | enum qemu_plugin_op op, | |
155 | qemu_plugin_u64 entry, | |
156 | uint64_t imm) | |
157 | { | |
db409c01 | 158 | plugin_register_inline_op_on_entry(&insn->mem_cbs, rw, op, entry, imm); |
0bcebaba PB |
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 | 213 | insn = g_ptr_array_index(tb->insns, idx); |
cfd405ea | 214 | return insn; |
5c5d69b0 AB |
215 | } |
216 | ||
217 | /* | |
218 | * Instruction information | |
219 | * | |
220 | * These queries allow the plugin to retrieve information about each | |
221 | * instruction being translated. | |
222 | */ | |
223 | ||
4abc8923 RH |
224 | size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, |
225 | void *dest, size_t len) | |
5c5d69b0 | 226 | { |
36bc99bc RH |
227 | const DisasContextBase *db = tcg_ctx->plugin_db; |
228 | ||
229 | len = MIN(len, insn->len); | |
230 | return translator_st(db, dest, insn->vaddr, len) ? len : 0; | |
5c5d69b0 AB |
231 | } |
232 | ||
233 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
234 | { | |
36bc99bc | 235 | return insn->len; |
5c5d69b0 AB |
236 | } |
237 | ||
238 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
239 | { | |
240 | return insn->vaddr; | |
241 | } | |
242 | ||
243 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
244 | { | |
245 | return insn->haddr; | |
246 | } | |
247 | ||
cbafa236 AB |
248 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
249 | { | |
250 | CPUState *cpu = current_cpu; | |
36bc99bc | 251 | return plugin_disas(cpu, insn->vaddr, insn->len); |
cbafa236 AB |
252 | } |
253 | ||
7c4ab60f AB |
254 | const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) |
255 | { | |
256 | const char *sym = lookup_symbol(insn->vaddr); | |
257 | return sym[0] != 0 ? sym : NULL; | |
258 | } | |
259 | ||
5c5d69b0 AB |
260 | /* |
261 | * The memory queries allow the plugin to query information about a | |
262 | * memory access. | |
263 | */ | |
264 | ||
265 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
266 | { | |
37aff087 RH |
267 | MemOp op = get_memop(info); |
268 | return op & MO_SIZE; | |
5c5d69b0 AB |
269 | } |
270 | ||
271 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
272 | { | |
37aff087 RH |
273 | MemOp op = get_memop(info); |
274 | return op & MO_SIGN; | |
5c5d69b0 AB |
275 | } |
276 | ||
277 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
278 | { | |
37aff087 RH |
279 | MemOp op = get_memop(info); |
280 | return (op & MO_BSWAP) == MO_BE; | |
5c5d69b0 AB |
281 | } |
282 | ||
283 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
284 | { | |
37aff087 | 285 | return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; |
5c5d69b0 AB |
286 | } |
287 | ||
288 | /* | |
289 | * Virtual Memory queries | |
290 | */ | |
291 | ||
235537fa AB |
292 | #ifdef CONFIG_SOFTMMU |
293 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
a2b88169 | 294 | #endif |
235537fa AB |
295 | |
296 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, | |
297 | uint64_t vaddr) | |
298 | { | |
a2b88169 | 299 | #ifdef CONFIG_SOFTMMU |
235537fa | 300 | CPUState *cpu = current_cpu; |
37aff087 RH |
301 | unsigned int mmu_idx = get_mmuidx(info); |
302 | enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); | |
303 | hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; | |
235537fa | 304 | |
5413c37f RH |
305 | assert(mmu_idx < NB_MMU_MODES); |
306 | ||
235537fa | 307 | if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, |
37aff087 | 308 | hwaddr_info.is_store, &hwaddr_info)) { |
235537fa AB |
309 | error_report("invalid use of qemu_plugin_get_hwaddr"); |
310 | return NULL; | |
311 | } | |
312 | ||
313 | return &hwaddr_info; | |
235537fa | 314 | #else |
5c5d69b0 | 315 | return NULL; |
235537fa | 316 | #endif |
a2b88169 | 317 | } |
235537fa | 318 | |
308e7549 | 319 | bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
320 | { |
321 | #ifdef CONFIG_SOFTMMU | |
308e7549 | 322 | return haddr->is_io; |
235537fa AB |
323 | #else |
324 | return false; | |
325 | #endif | |
326 | } | |
327 | ||
787148bf | 328 | uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
329 | { |
330 | #ifdef CONFIG_SOFTMMU | |
331 | if (haddr) { | |
405c02d8 | 332 | return haddr->phys_addr; |
235537fa AB |
333 | } |
334 | #endif | |
335 | return 0; | |
336 | } | |
5c5d69b0 | 337 | |
b853a79f AB |
338 | const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) |
339 | { | |
340 | #ifdef CONFIG_SOFTMMU | |
341 | if (h && h->is_io) { | |
405c02d8 RH |
342 | MemoryRegion *mr = h->mr; |
343 | if (!mr->name) { | |
344 | unsigned maddr = (uintptr_t)mr; | |
345 | g_autofree char *temp = g_strdup_printf("anon%08x", maddr); | |
b853a79f AB |
346 | return g_intern_string(temp); |
347 | } else { | |
405c02d8 | 348 | return g_intern_string(mr->name); |
b853a79f AB |
349 | } |
350 | } else { | |
351 | return g_intern_static_string("RAM"); | |
352 | } | |
353 | #else | |
354 | return g_intern_static_string("Invalid"); | |
355 | #endif | |
356 | } | |
357 | ||
4a448b14 PB |
358 | int qemu_plugin_num_vcpus(void) |
359 | { | |
360 | return plugin_num_vcpus(); | |
361 | } | |
362 | ||
ca76a669 AB |
363 | /* |
364 | * Plugin output | |
365 | */ | |
366 | void qemu_plugin_outs(const char *string) | |
367 | { | |
368 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
369 | } | |
6a9e8a08 MM |
370 | |
371 | bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) | |
372 | { | |
373 | return name && value && qapi_bool_parse(name, value, ret, NULL); | |
374 | } | |
91d40327 IA |
375 | |
376 | /* | |
377 | * Binary path, start and end locations | |
378 | */ | |
379 | const char *qemu_plugin_path_to_binary(void) | |
380 | { | |
381 | char *path = NULL; | |
382 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 383 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
384 | path = g_strdup(ts->bprm->filename); |
385 | #endif | |
386 | return path; | |
387 | } | |
388 | ||
389 | uint64_t qemu_plugin_start_code(void) | |
390 | { | |
391 | uint64_t start = 0; | |
392 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 393 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
394 | start = ts->info->start_code; |
395 | #endif | |
396 | return start; | |
397 | } | |
398 | ||
399 | uint64_t qemu_plugin_end_code(void) | |
400 | { | |
401 | uint64_t end = 0; | |
402 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 403 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
404 | end = ts->info->end_code; |
405 | #endif | |
406 | return end; | |
407 | } | |
408 | ||
409 | uint64_t qemu_plugin_entry_code(void) | |
410 | { | |
411 | uint64_t entry = 0; | |
412 | #ifdef CONFIG_USER_ONLY | |
e4e5cb4a | 413 | TaskState *ts = get_task_state(current_cpu); |
91d40327 IA |
414 | entry = ts->info->entry; |
415 | #endif | |
416 | return entry; | |
417 | } | |
8df5e27c AB |
418 | |
419 | /* | |
420 | * Create register handles. | |
421 | * | |
422 | * We need to create a handle for each register so the plugin | |
423 | * infrastructure can call gdbstub to read a register. They are | |
424 | * currently just a pointer encapsulation of the gdb_reg but in | |
425 | * future may hold internal plugin state so its important plugin | |
426 | * authors are not tempted to treat them as numbers. | |
427 | * | |
428 | * We also construct a result array with those handles and some | |
429 | * ancillary data the plugin might find useful. | |
430 | */ | |
431 | ||
432 | static GArray *create_register_handles(GArray *gdbstub_regs) | |
433 | { | |
434 | GArray *find_data = g_array_new(true, true, | |
435 | sizeof(qemu_plugin_reg_descriptor)); | |
436 | ||
437 | for (int i = 0; i < gdbstub_regs->len; i++) { | |
438 | GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i); | |
439 | qemu_plugin_reg_descriptor desc; | |
440 | ||
441 | /* skip "un-named" regs */ | |
442 | if (!grd->name) { | |
443 | continue; | |
444 | } | |
445 | ||
446 | /* Create a record for the plugin */ | |
447 | desc.handle = GINT_TO_POINTER(grd->gdb_reg); | |
448 | desc.name = g_intern_string(grd->name); | |
449 | desc.feature = g_intern_string(grd->feature_name); | |
450 | g_array_append_val(find_data, desc); | |
451 | } | |
452 | ||
453 | return find_data; | |
454 | } | |
455 | ||
456 | GArray *qemu_plugin_get_registers(void) | |
457 | { | |
458 | g_assert(current_cpu); | |
459 | ||
460 | g_autoptr(GArray) regs = gdb_get_register_list(current_cpu); | |
461 | return create_register_handles(regs); | |
462 | } | |
463 | ||
464 | int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) | |
465 | { | |
466 | g_assert(current_cpu); | |
467 | ||
468 | return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg)); | |
469 | } | |
a3c2cf0b PB |
470 | |
471 | struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) | |
472 | { | |
473 | return plugin_scoreboard_new(element_size); | |
474 | } | |
475 | ||
476 | void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) | |
477 | { | |
478 | plugin_scoreboard_free(score); | |
479 | } | |
480 | ||
481 | void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, | |
482 | unsigned int vcpu_index) | |
483 | { | |
484 | g_assert(vcpu_index < qemu_plugin_num_vcpus()); | |
485 | /* we can't use g_array_index since entry size is not statically known */ | |
486 | char *base_ptr = score->data->data; | |
487 | return base_ptr + vcpu_index * g_array_get_element_size(score->data); | |
488 | } | |
8042e2ea PB |
489 | |
490 | static uint64_t *plugin_u64_address(qemu_plugin_u64 entry, | |
491 | unsigned int vcpu_index) | |
492 | { | |
493 | char *ptr = qemu_plugin_scoreboard_find(entry.score, vcpu_index); | |
494 | return (uint64_t *)(ptr + entry.offset); | |
495 | } | |
496 | ||
497 | void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, | |
498 | uint64_t added) | |
499 | { | |
500 | *plugin_u64_address(entry, vcpu_index) += added; | |
501 | } | |
502 | ||
503 | uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, | |
504 | unsigned int vcpu_index) | |
505 | { | |
506 | return *plugin_u64_address(entry, vcpu_index); | |
507 | } | |
508 | ||
509 | void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, | |
510 | uint64_t val) | |
511 | { | |
512 | *plugin_u64_address(entry, vcpu_index) = val; | |
513 | } | |
514 | ||
515 | uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) | |
516 | { | |
517 | uint64_t total = 0; | |
518 | for (int i = 0, n = qemu_plugin_num_vcpus(); i < n; ++i) { | |
519 | total += qemu_plugin_u64_get(entry, i); | |
520 | } | |
521 | return total; | |
522 | } |