]>
Commit | Line | Data |
---|---|---|
54cb65d8 EC |
1 | /* |
2 | * QEMU Plugin Core code | |
3 | * | |
4 | * This is the core code that deals with injecting instrumentation into the code | |
5 | * | |
6 | * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> | |
7 | * Copyright (C) 2019, Linaro | |
8 | * | |
9 | * License: GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | * | |
12 | * SPDX-License-Identifier: GPL-2.0-or-later | |
13 | */ | |
14 | #include "qemu/osdep.h" | |
15 | #include "qemu/error-report.h" | |
16 | #include "qemu/config-file.h" | |
17 | #include "qapi/error.h" | |
ac90871c | 18 | #include "qemu/lockable.h" |
54cb65d8 EC |
19 | #include "qemu/option.h" |
20 | #include "qemu/rcu_queue.h" | |
21 | #include "qemu/xxhash.h" | |
22 | #include "qemu/rcu.h" | |
23 | #include "hw/core/cpu.h" | |
24 | #include "exec/cpu-common.h" | |
25 | ||
54cb65d8 EC |
26 | #include "exec/exec-all.h" |
27 | #include "exec/helper-proto.h" | |
54cb65d8 EC |
28 | #include "tcg/tcg.h" |
29 | #include "tcg/tcg-op.h" | |
785ea711 | 30 | #include "trace/mem.h" /* mem_info macros */ |
54cb65d8 | 31 | #include "plugin.h" |
c905a368 | 32 | #include "qemu/compiler.h" |
54cb65d8 EC |
33 | |
34 | struct qemu_plugin_cb { | |
35 | struct qemu_plugin_ctx *ctx; | |
36 | union qemu_plugin_cb_sig f; | |
37 | void *udata; | |
38 | QLIST_ENTRY(qemu_plugin_cb) entry; | |
39 | }; | |
40 | ||
41 | struct qemu_plugin_state plugin; | |
42 | ||
43 | struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id) | |
44 | { | |
45 | struct qemu_plugin_ctx *ctx; | |
46 | qemu_plugin_id_t *id_p; | |
47 | ||
48 | id_p = g_hash_table_lookup(plugin.id_ht, &id); | |
49 | ctx = container_of(id_p, struct qemu_plugin_ctx, id); | |
50 | if (ctx == NULL) { | |
51 | error_report("plugin: invalid plugin id %" PRIu64, id); | |
52 | abort(); | |
53 | } | |
54 | return ctx; | |
55 | } | |
56 | ||
57 | static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data) | |
58 | { | |
59 | bitmap_copy(cpu->plugin_mask, &data.host_ulong, QEMU_PLUGIN_EV_MAX); | |
60 | cpu_tb_jmp_cache_clear(cpu); | |
61 | } | |
62 | ||
63 | static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata) | |
64 | { | |
65 | CPUState *cpu = container_of(k, CPUState, cpu_index); | |
66 | run_on_cpu_data mask = RUN_ON_CPU_HOST_ULONG(*plugin.mask); | |
67 | ||
68 | if (cpu->created) { | |
69 | async_run_on_cpu(cpu, plugin_cpu_update__async, mask); | |
70 | } else { | |
71 | plugin_cpu_update__async(cpu, mask); | |
72 | } | |
73 | } | |
74 | ||
75 | void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx, | |
76 | enum qemu_plugin_event ev) | |
77 | { | |
78 | struct qemu_plugin_cb *cb = ctx->callbacks[ev]; | |
79 | ||
80 | if (cb == NULL) { | |
81 | return; | |
82 | } | |
83 | QLIST_REMOVE_RCU(cb, entry); | |
84 | g_free(cb); | |
85 | ctx->callbacks[ev] = NULL; | |
86 | if (QLIST_EMPTY_RCU(&plugin.cb_lists[ev])) { | |
87 | clear_bit(ev, plugin.mask); | |
88 | g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked, NULL); | |
89 | } | |
90 | } | |
91 | ||
c905a368 DB |
92 | /* |
93 | * Disable CFI checks. | |
94 | * The callback function has been loaded from an external library so we do not | |
95 | * have type information | |
96 | */ | |
97 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
98 | static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev) |
99 | { | |
100 | struct qemu_plugin_cb *cb, *next; | |
101 | ||
102 | switch (ev) { | |
103 | case QEMU_PLUGIN_EV_VCPU_INIT: | |
104 | case QEMU_PLUGIN_EV_VCPU_EXIT: | |
105 | case QEMU_PLUGIN_EV_VCPU_IDLE: | |
106 | case QEMU_PLUGIN_EV_VCPU_RESUME: | |
107 | /* iterate safely; plugins might uninstall themselves at any time */ | |
108 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
109 | qemu_plugin_vcpu_simple_cb_t func = cb->f.vcpu_simple; | |
110 | ||
111 | func(cb->ctx->id, cpu->cpu_index); | |
112 | } | |
113 | break; | |
114 | default: | |
115 | g_assert_not_reached(); | |
116 | } | |
117 | } | |
118 | ||
c905a368 DB |
119 | /* |
120 | * Disable CFI checks. | |
121 | * The callback function has been loaded from an external library so we do not | |
122 | * have type information | |
123 | */ | |
124 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
125 | static void plugin_cb__simple(enum qemu_plugin_event ev) |
126 | { | |
127 | struct qemu_plugin_cb *cb, *next; | |
128 | ||
129 | switch (ev) { | |
130 | case QEMU_PLUGIN_EV_FLUSH: | |
131 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
132 | qemu_plugin_simple_cb_t func = cb->f.simple; | |
133 | ||
134 | func(cb->ctx->id); | |
135 | } | |
136 | break; | |
137 | default: | |
138 | g_assert_not_reached(); | |
139 | } | |
140 | } | |
141 | ||
c905a368 DB |
142 | /* |
143 | * Disable CFI checks. | |
144 | * The callback function has been loaded from an external library so we do not | |
145 | * have type information | |
146 | */ | |
147 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
148 | static void plugin_cb__udata(enum qemu_plugin_event ev) |
149 | { | |
150 | struct qemu_plugin_cb *cb, *next; | |
151 | ||
152 | switch (ev) { | |
153 | case QEMU_PLUGIN_EV_ATEXIT: | |
154 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
155 | qemu_plugin_udata_cb_t func = cb->f.udata; | |
156 | ||
157 | func(cb->ctx->id, cb->udata); | |
158 | } | |
159 | break; | |
160 | default: | |
161 | g_assert_not_reached(); | |
162 | } | |
163 | } | |
164 | ||
165 | static void | |
166 | do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, | |
167 | void *func, void *udata) | |
168 | { | |
169 | struct qemu_plugin_ctx *ctx; | |
170 | ||
ac90871c | 171 | QEMU_LOCK_GUARD(&plugin.lock); |
54cb65d8 EC |
172 | ctx = plugin_id_to_ctx_locked(id); |
173 | /* if the plugin is on its way out, ignore this request */ | |
174 | if (unlikely(ctx->uninstalling)) { | |
ac90871c | 175 | return; |
54cb65d8 EC |
176 | } |
177 | if (func) { | |
178 | struct qemu_plugin_cb *cb = ctx->callbacks[ev]; | |
179 | ||
180 | if (cb) { | |
181 | cb->f.generic = func; | |
182 | cb->udata = udata; | |
183 | } else { | |
184 | cb = g_new(struct qemu_plugin_cb, 1); | |
185 | cb->ctx = ctx; | |
186 | cb->f.generic = func; | |
187 | cb->udata = udata; | |
188 | ctx->callbacks[ev] = cb; | |
189 | QLIST_INSERT_HEAD_RCU(&plugin.cb_lists[ev], cb, entry); | |
190 | if (!test_bit(ev, plugin.mask)) { | |
191 | set_bit(ev, plugin.mask); | |
192 | g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked, | |
193 | NULL); | |
194 | } | |
195 | } | |
196 | } else { | |
197 | plugin_unregister_cb__locked(ctx, ev); | |
198 | } | |
54cb65d8 EC |
199 | } |
200 | ||
201 | void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, | |
202 | void *func) | |
203 | { | |
204 | do_plugin_register_cb(id, ev, func, NULL); | |
205 | } | |
206 | ||
207 | void | |
208 | plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev, | |
209 | void *func, void *udata) | |
210 | { | |
211 | do_plugin_register_cb(id, ev, func, udata); | |
212 | } | |
213 | ||
214 | void qemu_plugin_vcpu_init_hook(CPUState *cpu) | |
215 | { | |
216 | bool success; | |
217 | ||
218 | qemu_rec_mutex_lock(&plugin.lock); | |
219 | plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL); | |
220 | success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index, | |
221 | &cpu->cpu_index); | |
222 | g_assert(success); | |
223 | qemu_rec_mutex_unlock(&plugin.lock); | |
224 | ||
225 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); | |
226 | } | |
227 | ||
228 | void qemu_plugin_vcpu_exit_hook(CPUState *cpu) | |
229 | { | |
230 | bool success; | |
231 | ||
232 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT); | |
233 | ||
234 | qemu_rec_mutex_lock(&plugin.lock); | |
235 | success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index); | |
236 | g_assert(success); | |
237 | qemu_rec_mutex_unlock(&plugin.lock); | |
238 | } | |
239 | ||
240 | struct plugin_for_each_args { | |
241 | struct qemu_plugin_ctx *ctx; | |
242 | qemu_plugin_vcpu_simple_cb_t cb; | |
243 | }; | |
244 | ||
245 | static void plugin_vcpu_for_each(gpointer k, gpointer v, gpointer udata) | |
246 | { | |
247 | struct plugin_for_each_args *args = udata; | |
248 | int cpu_index = *(int *)k; | |
249 | ||
250 | args->cb(args->ctx->id, cpu_index); | |
251 | } | |
252 | ||
253 | void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id, | |
254 | qemu_plugin_vcpu_simple_cb_t cb) | |
255 | { | |
256 | struct plugin_for_each_args args; | |
257 | ||
258 | if (cb == NULL) { | |
259 | return; | |
260 | } | |
261 | qemu_rec_mutex_lock(&plugin.lock); | |
262 | args.ctx = plugin_id_to_ctx_locked(id); | |
263 | args.cb = cb; | |
264 | g_hash_table_foreach(plugin.cpu_ht, plugin_vcpu_for_each, &args); | |
265 | qemu_rec_mutex_unlock(&plugin.lock); | |
266 | } | |
267 | ||
268 | /* Allocate and return a callback record */ | |
269 | static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) | |
270 | { | |
271 | GArray *cbs = *arr; | |
272 | ||
273 | if (!cbs) { | |
274 | cbs = g_array_sized_new(false, false, | |
275 | sizeof(struct qemu_plugin_dyn_cb), 1); | |
276 | *arr = cbs; | |
277 | } | |
278 | ||
279 | g_array_set_size(cbs, cbs->len + 1); | |
280 | return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1); | |
281 | } | |
282 | ||
283 | void plugin_register_inline_op(GArray **arr, | |
284 | enum qemu_plugin_mem_rw rw, | |
285 | enum qemu_plugin_op op, void *ptr, | |
286 | uint64_t imm) | |
287 | { | |
288 | struct qemu_plugin_dyn_cb *dyn_cb; | |
289 | ||
290 | dyn_cb = plugin_get_dyn_cb(arr); | |
291 | dyn_cb->userp = ptr; | |
292 | dyn_cb->type = PLUGIN_CB_INLINE; | |
293 | dyn_cb->rw = rw; | |
294 | dyn_cb->inline_insn.op = op; | |
295 | dyn_cb->inline_insn.imm = imm; | |
296 | } | |
297 | ||
c7bb41b4 RH |
298 | void plugin_register_dyn_cb__udata(GArray **arr, |
299 | qemu_plugin_vcpu_udata_cb_t cb, | |
300 | enum qemu_plugin_cb_flags flags, | |
301 | void *udata) | |
54cb65d8 EC |
302 | { |
303 | struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); | |
304 | ||
305 | dyn_cb->userp = udata; | |
c7bb41b4 | 306 | /* Note flags are discarded as unused. */ |
54cb65d8 EC |
307 | dyn_cb->f.vcpu_udata = cb; |
308 | dyn_cb->type = PLUGIN_CB_REGULAR; | |
309 | } | |
310 | ||
311 | void plugin_register_vcpu_mem_cb(GArray **arr, | |
312 | void *cb, | |
313 | enum qemu_plugin_cb_flags flags, | |
314 | enum qemu_plugin_mem_rw rw, | |
315 | void *udata) | |
316 | { | |
317 | struct qemu_plugin_dyn_cb *dyn_cb; | |
318 | ||
319 | dyn_cb = plugin_get_dyn_cb(arr); | |
320 | dyn_cb->userp = udata; | |
c7bb41b4 | 321 | /* Note flags are discarded as unused. */ |
54cb65d8 EC |
322 | dyn_cb->type = PLUGIN_CB_REGULAR; |
323 | dyn_cb->rw = rw; | |
324 | dyn_cb->f.generic = cb; | |
325 | } | |
326 | ||
c905a368 DB |
327 | /* |
328 | * Disable CFI checks. | |
329 | * The callback function has been loaded from an external library so we do not | |
330 | * have type information | |
331 | */ | |
332 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
333 | void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb) |
334 | { | |
335 | struct qemu_plugin_cb *cb, *next; | |
336 | enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS; | |
337 | ||
338 | /* no plugin_mask check here; caller should have checked */ | |
339 | ||
340 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
341 | qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans; | |
342 | ||
343 | func(cb->ctx->id, tb); | |
344 | } | |
345 | } | |
346 | ||
c905a368 DB |
347 | /* |
348 | * Disable CFI checks. | |
349 | * The callback function has been loaded from an external library so we do not | |
350 | * have type information | |
351 | */ | |
352 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
353 | void |
354 | qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2, | |
355 | uint64_t a3, uint64_t a4, uint64_t a5, | |
356 | uint64_t a6, uint64_t a7, uint64_t a8) | |
357 | { | |
358 | struct qemu_plugin_cb *cb, *next; | |
359 | enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL; | |
360 | ||
361 | if (!test_bit(ev, cpu->plugin_mask)) { | |
362 | return; | |
363 | } | |
364 | ||
365 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
366 | qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall; | |
367 | ||
368 | func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8); | |
369 | } | |
370 | } | |
371 | ||
c905a368 DB |
372 | /* |
373 | * Disable CFI checks. | |
374 | * The callback function has been loaded from an external library so we do not | |
375 | * have type information | |
376 | */ | |
377 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
378 | void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) |
379 | { | |
380 | struct qemu_plugin_cb *cb, *next; | |
381 | enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_RET; | |
382 | ||
383 | if (!test_bit(ev, cpu->plugin_mask)) { | |
384 | return; | |
385 | } | |
386 | ||
387 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
388 | qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret; | |
389 | ||
390 | func(cb->ctx->id, cpu->cpu_index, num, ret); | |
391 | } | |
392 | } | |
393 | ||
394 | void qemu_plugin_vcpu_idle_cb(CPUState *cpu) | |
395 | { | |
396 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE); | |
397 | } | |
398 | ||
399 | void qemu_plugin_vcpu_resume_cb(CPUState *cpu) | |
400 | { | |
401 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME); | |
402 | } | |
403 | ||
404 | void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, | |
405 | qemu_plugin_vcpu_simple_cb_t cb) | |
406 | { | |
407 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_IDLE, cb); | |
408 | } | |
409 | ||
410 | void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, | |
411 | qemu_plugin_vcpu_simple_cb_t cb) | |
412 | { | |
413 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb); | |
414 | } | |
415 | ||
416 | void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, | |
417 | qemu_plugin_simple_cb_t cb) | |
418 | { | |
419 | plugin_register_cb(id, QEMU_PLUGIN_EV_FLUSH, cb); | |
420 | } | |
421 | ||
422 | static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp) | |
423 | { | |
424 | g_array_free((GArray *) p, true); | |
425 | return true; | |
426 | } | |
427 | ||
428 | void qemu_plugin_flush_cb(void) | |
429 | { | |
430 | qht_iter_remove(&plugin.dyn_cb_arr_ht, free_dyn_cb_arr, NULL); | |
431 | qht_reset(&plugin.dyn_cb_arr_ht); | |
432 | ||
433 | plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH); | |
434 | } | |
435 | ||
436 | void exec_inline_op(struct qemu_plugin_dyn_cb *cb) | |
437 | { | |
438 | uint64_t *val = cb->userp; | |
439 | ||
440 | switch (cb->inline_insn.op) { | |
441 | case QEMU_PLUGIN_INLINE_ADD_U64: | |
442 | *val += cb->inline_insn.imm; | |
443 | break; | |
444 | default: | |
445 | g_assert_not_reached(); | |
446 | } | |
447 | } | |
448 | ||
449 | void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, uint32_t info) | |
450 | { | |
451 | GArray *arr = cpu->plugin_mem_cbs; | |
452 | size_t i; | |
453 | ||
454 | if (arr == NULL) { | |
455 | return; | |
456 | } | |
457 | for (i = 0; i < arr->len; i++) { | |
458 | struct qemu_plugin_dyn_cb *cb = | |
459 | &g_array_index(arr, struct qemu_plugin_dyn_cb, i); | |
460 | int w = !!(info & TRACE_MEM_ST) + 1; | |
461 | ||
462 | if (!(w & cb->rw)) { | |
463 | break; | |
464 | } | |
465 | switch (cb->type) { | |
466 | case PLUGIN_CB_REGULAR: | |
467 | cb->f.vcpu_mem(cpu->cpu_index, info, vaddr, cb->userp); | |
468 | break; | |
469 | case PLUGIN_CB_INLINE: | |
470 | exec_inline_op(cb); | |
471 | break; | |
472 | default: | |
473 | g_assert_not_reached(); | |
474 | } | |
475 | } | |
476 | } | |
477 | ||
478 | void qemu_plugin_atexit_cb(void) | |
479 | { | |
480 | plugin_cb__udata(QEMU_PLUGIN_EV_ATEXIT); | |
481 | } | |
482 | ||
483 | void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id, | |
484 | qemu_plugin_udata_cb_t cb, | |
485 | void *udata) | |
486 | { | |
487 | plugin_register_cb_udata(id, QEMU_PLUGIN_EV_ATEXIT, cb, udata); | |
488 | } | |
489 | ||
490 | /* | |
491 | * Call this function after longjmp'ing to the main loop. It's possible that the | |
492 | * last instruction of a TB might have used helpers, and therefore the | |
493 | * "disable" instruction will never execute because it ended up as dead code. | |
494 | */ | |
495 | void qemu_plugin_disable_mem_helpers(CPUState *cpu) | |
496 | { | |
497 | cpu->plugin_mem_cbs = NULL; | |
498 | } | |
499 | ||
500 | static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp) | |
501 | { | |
502 | return ap == bp; | |
503 | } | |
504 | ||
505 | static void __attribute__((__constructor__)) plugin_init(void) | |
506 | { | |
507 | int i; | |
508 | ||
509 | for (i = 0; i < QEMU_PLUGIN_EV_MAX; i++) { | |
510 | QLIST_INIT(&plugin.cb_lists[i]); | |
511 | } | |
512 | qemu_rec_mutex_init(&plugin.lock); | |
513 | plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal); | |
514 | plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal); | |
515 | QTAILQ_INIT(&plugin.ctxs); | |
516 | qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16, | |
517 | QHT_MODE_AUTO_RESIZE); | |
518 | atexit(qemu_plugin_atexit_cb); | |
519 | } |