]>
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 | 19 | #include "qemu/option.h" |
c0061471 | 20 | #include "qemu/plugin.h" |
a3c2cf0b | 21 | #include "qemu/queue.h" |
54cb65d8 EC |
22 | #include "qemu/rcu_queue.h" |
23 | #include "qemu/xxhash.h" | |
24 | #include "qemu/rcu.h" | |
25 | #include "hw/core/cpu.h" | |
54cb65d8 | 26 | |
54cb65d8 | 27 | #include "exec/exec-all.h" |
548c9609 | 28 | #include "exec/tb-flush.h" |
54cb65d8 EC |
29 | #include "tcg/tcg.h" |
30 | #include "tcg/tcg-op.h" | |
54cb65d8 EC |
31 | #include "plugin.h" |
32 | ||
33 | struct qemu_plugin_cb { | |
34 | struct qemu_plugin_ctx *ctx; | |
35 | union qemu_plugin_cb_sig f; | |
36 | void *udata; | |
37 | QLIST_ENTRY(qemu_plugin_cb) entry; | |
38 | }; | |
39 | ||
40 | struct qemu_plugin_state plugin; | |
41 | ||
42 | struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id) | |
43 | { | |
44 | struct qemu_plugin_ctx *ctx; | |
45 | qemu_plugin_id_t *id_p; | |
46 | ||
47 | id_p = g_hash_table_lookup(plugin.id_ht, &id); | |
48 | ctx = container_of(id_p, struct qemu_plugin_ctx, id); | |
49 | if (ctx == NULL) { | |
50 | error_report("plugin: invalid plugin id %" PRIu64, id); | |
51 | abort(); | |
52 | } | |
53 | return ctx; | |
54 | } | |
55 | ||
56 | static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data) | |
57 | { | |
c0061471 AB |
58 | bitmap_copy(cpu->plugin_state->event_mask, |
59 | &data.host_ulong, QEMU_PLUGIN_EV_MAX); | |
a976a99a | 60 | tcg_flush_jmp_cache(cpu); |
54cb65d8 EC |
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 | ||
fb13735a | 68 | if (DEVICE(cpu)->realized) { |
54cb65d8 EC |
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 | ||
c0061471 AB |
214 | CPUPluginState *qemu_plugin_create_vcpu_state(void) |
215 | { | |
216 | return g_new0(CPUPluginState, 1); | |
217 | } | |
218 | ||
a3c2cf0b PB |
219 | static void plugin_grow_scoreboards__locked(CPUState *cpu) |
220 | { | |
221 | if (cpu->cpu_index < plugin.scoreboard_alloc_size) { | |
222 | return; | |
223 | } | |
224 | ||
225 | bool need_realloc = FALSE; | |
226 | while (cpu->cpu_index >= plugin.scoreboard_alloc_size) { | |
227 | plugin.scoreboard_alloc_size *= 2; | |
228 | need_realloc = TRUE; | |
229 | } | |
230 | ||
231 | ||
232 | if (!need_realloc || QLIST_EMPTY(&plugin.scoreboards)) { | |
233 | /* nothing to do, we just updated sizes for future scoreboards */ | |
234 | return; | |
235 | } | |
236 | ||
237 | /* cpus must be stopped, as tb might still use an existing scoreboard. */ | |
238 | start_exclusive(); | |
239 | struct qemu_plugin_scoreboard *score; | |
240 | QLIST_FOREACH(score, &plugin.scoreboards, entry) { | |
241 | g_array_set_size(score->data, plugin.scoreboard_alloc_size); | |
242 | } | |
243 | /* force all tb to be flushed, as scoreboard pointers were changed. */ | |
244 | tb_flush(cpu); | |
245 | end_exclusive(); | |
246 | } | |
247 | ||
54cb65d8 EC |
248 | void qemu_plugin_vcpu_init_hook(CPUState *cpu) |
249 | { | |
250 | bool success; | |
251 | ||
252 | qemu_rec_mutex_lock(&plugin.lock); | |
4a448b14 | 253 | plugin.num_vcpus = MAX(plugin.num_vcpus, cpu->cpu_index + 1); |
54cb65d8 EC |
254 | plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL); |
255 | success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index, | |
256 | &cpu->cpu_index); | |
257 | g_assert(success); | |
a3c2cf0b | 258 | plugin_grow_scoreboards__locked(cpu); |
54cb65d8 EC |
259 | qemu_rec_mutex_unlock(&plugin.lock); |
260 | ||
261 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); | |
262 | } | |
263 | ||
264 | void qemu_plugin_vcpu_exit_hook(CPUState *cpu) | |
265 | { | |
266 | bool success; | |
267 | ||
268 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT); | |
269 | ||
270 | qemu_rec_mutex_lock(&plugin.lock); | |
271 | success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index); | |
272 | g_assert(success); | |
273 | qemu_rec_mutex_unlock(&plugin.lock); | |
274 | } | |
275 | ||
276 | struct plugin_for_each_args { | |
277 | struct qemu_plugin_ctx *ctx; | |
278 | qemu_plugin_vcpu_simple_cb_t cb; | |
279 | }; | |
280 | ||
281 | static void plugin_vcpu_for_each(gpointer k, gpointer v, gpointer udata) | |
282 | { | |
283 | struct plugin_for_each_args *args = udata; | |
284 | int cpu_index = *(int *)k; | |
285 | ||
286 | args->cb(args->ctx->id, cpu_index); | |
287 | } | |
288 | ||
289 | void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id, | |
290 | qemu_plugin_vcpu_simple_cb_t cb) | |
291 | { | |
292 | struct plugin_for_each_args args; | |
293 | ||
294 | if (cb == NULL) { | |
295 | return; | |
296 | } | |
297 | qemu_rec_mutex_lock(&plugin.lock); | |
298 | args.ctx = plugin_id_to_ctx_locked(id); | |
299 | args.cb = cb; | |
300 | g_hash_table_foreach(plugin.cpu_ht, plugin_vcpu_for_each, &args); | |
301 | qemu_rec_mutex_unlock(&plugin.lock); | |
302 | } | |
303 | ||
304 | /* Allocate and return a callback record */ | |
305 | static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) | |
306 | { | |
307 | GArray *cbs = *arr; | |
308 | ||
309 | if (!cbs) { | |
25875fe9 | 310 | cbs = g_array_sized_new(false, true, |
54cb65d8 EC |
311 | sizeof(struct qemu_plugin_dyn_cb), 1); |
312 | *arr = cbs; | |
313 | } | |
314 | ||
315 | g_array_set_size(cbs, cbs->len + 1); | |
316 | return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1); | |
317 | } | |
318 | ||
0bcebaba PB |
319 | void plugin_register_inline_op_on_entry(GArray **arr, |
320 | enum qemu_plugin_mem_rw rw, | |
321 | enum qemu_plugin_op op, | |
322 | qemu_plugin_u64 entry, | |
323 | uint64_t imm) | |
324 | { | |
325 | struct qemu_plugin_dyn_cb *dyn_cb; | |
326 | ||
327 | dyn_cb = plugin_get_dyn_cb(arr); | |
328 | dyn_cb->userp = NULL; | |
329 | dyn_cb->type = PLUGIN_CB_INLINE; | |
330 | dyn_cb->rw = rw; | |
331 | dyn_cb->inline_insn.entry = entry; | |
332 | dyn_cb->inline_insn.op = op; | |
333 | dyn_cb->inline_insn.imm = imm; | |
334 | } | |
335 | ||
c7bb41b4 RH |
336 | void plugin_register_dyn_cb__udata(GArray **arr, |
337 | qemu_plugin_vcpu_udata_cb_t cb, | |
338 | enum qemu_plugin_cb_flags flags, | |
339 | void *udata) | |
54cb65d8 EC |
340 | { |
341 | struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); | |
342 | ||
343 | dyn_cb->userp = udata; | |
c7bb41b4 | 344 | /* Note flags are discarded as unused. */ |
aff56de5 | 345 | dyn_cb->regular.f.vcpu_udata = cb; |
54cb65d8 EC |
346 | dyn_cb->type = PLUGIN_CB_REGULAR; |
347 | } | |
348 | ||
349 | void plugin_register_vcpu_mem_cb(GArray **arr, | |
350 | void *cb, | |
351 | enum qemu_plugin_cb_flags flags, | |
352 | enum qemu_plugin_mem_rw rw, | |
353 | void *udata) | |
354 | { | |
355 | struct qemu_plugin_dyn_cb *dyn_cb; | |
356 | ||
357 | dyn_cb = plugin_get_dyn_cb(arr); | |
358 | dyn_cb->userp = udata; | |
c7bb41b4 | 359 | /* Note flags are discarded as unused. */ |
54cb65d8 EC |
360 | dyn_cb->type = PLUGIN_CB_REGULAR; |
361 | dyn_cb->rw = rw; | |
aff56de5 | 362 | dyn_cb->regular.f.vcpu_mem = cb; |
54cb65d8 EC |
363 | } |
364 | ||
c905a368 DB |
365 | /* |
366 | * Disable CFI checks. | |
367 | * The callback function has been loaded from an external library so we do not | |
368 | * have type information | |
369 | */ | |
370 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
371 | void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb) |
372 | { | |
373 | struct qemu_plugin_cb *cb, *next; | |
374 | enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS; | |
375 | ||
376 | /* no plugin_mask check here; caller should have checked */ | |
377 | ||
378 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
379 | qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans; | |
380 | ||
381 | func(cb->ctx->id, tb); | |
382 | } | |
383 | } | |
384 | ||
c905a368 DB |
385 | /* |
386 | * Disable CFI checks. | |
387 | * The callback function has been loaded from an external library so we do not | |
388 | * have type information | |
389 | */ | |
390 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
391 | void |
392 | qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2, | |
393 | uint64_t a3, uint64_t a4, uint64_t a5, | |
394 | uint64_t a6, uint64_t a7, uint64_t a8) | |
395 | { | |
396 | struct qemu_plugin_cb *cb, *next; | |
397 | enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL; | |
398 | ||
c0061471 | 399 | if (!test_bit(ev, cpu->plugin_state->event_mask)) { |
54cb65d8 EC |
400 | return; |
401 | } | |
402 | ||
403 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
404 | qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall; | |
405 | ||
406 | func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8); | |
407 | } | |
408 | } | |
409 | ||
c905a368 DB |
410 | /* |
411 | * Disable CFI checks. | |
412 | * The callback function has been loaded from an external library so we do not | |
413 | * have type information | |
414 | */ | |
415 | QEMU_DISABLE_CFI | |
54cb65d8 EC |
416 | void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) |
417 | { | |
418 | struct qemu_plugin_cb *cb, *next; | |
419 | enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_RET; | |
420 | ||
c0061471 | 421 | if (!test_bit(ev, cpu->plugin_state->event_mask)) { |
54cb65d8 EC |
422 | return; |
423 | } | |
424 | ||
425 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
426 | qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret; | |
427 | ||
428 | func(cb->ctx->id, cpu->cpu_index, num, ret); | |
429 | } | |
430 | } | |
431 | ||
432 | void qemu_plugin_vcpu_idle_cb(CPUState *cpu) | |
433 | { | |
c490e681 PB |
434 | /* idle and resume cb may be called before init, ignore in this case */ |
435 | if (cpu->cpu_index < plugin.num_vcpus) { | |
436 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE); | |
437 | } | |
54cb65d8 EC |
438 | } |
439 | ||
440 | void qemu_plugin_vcpu_resume_cb(CPUState *cpu) | |
441 | { | |
c490e681 PB |
442 | if (cpu->cpu_index < plugin.num_vcpus) { |
443 | plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME); | |
444 | } | |
54cb65d8 EC |
445 | } |
446 | ||
447 | void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, | |
448 | qemu_plugin_vcpu_simple_cb_t cb) | |
449 | { | |
450 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_IDLE, cb); | |
451 | } | |
452 | ||
453 | void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, | |
454 | qemu_plugin_vcpu_simple_cb_t cb) | |
455 | { | |
456 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb); | |
457 | } | |
458 | ||
459 | void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, | |
460 | qemu_plugin_simple_cb_t cb) | |
461 | { | |
462 | plugin_register_cb(id, QEMU_PLUGIN_EV_FLUSH, cb); | |
463 | } | |
464 | ||
465 | static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp) | |
466 | { | |
467 | g_array_free((GArray *) p, true); | |
468 | return true; | |
469 | } | |
470 | ||
471 | void qemu_plugin_flush_cb(void) | |
472 | { | |
473 | qht_iter_remove(&plugin.dyn_cb_arr_ht, free_dyn_cb_arr, NULL); | |
474 | qht_reset(&plugin.dyn_cb_arr_ht); | |
475 | ||
476 | plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH); | |
477 | } | |
478 | ||
62f92b8d | 479 | void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) |
54cb65d8 | 480 | { |
3077be25 PB |
481 | char *ptr = cb->inline_insn.entry.score->data->data; |
482 | size_t elem_size = g_array_get_element_size( | |
483 | cb->inline_insn.entry.score->data); | |
484 | size_t offset = cb->inline_insn.entry.offset; | |
62f92b8d | 485 | uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); |
54cb65d8 EC |
486 | |
487 | switch (cb->inline_insn.op) { | |
488 | case QEMU_PLUGIN_INLINE_ADD_U64: | |
489 | *val += cb->inline_insn.imm; | |
490 | break; | |
491 | default: | |
492 | g_assert_not_reached(); | |
493 | } | |
494 | } | |
495 | ||
37aff087 RH |
496 | void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, |
497 | MemOpIdx oi, enum qemu_plugin_mem_rw rw) | |
54cb65d8 EC |
498 | { |
499 | GArray *arr = cpu->plugin_mem_cbs; | |
500 | size_t i; | |
501 | ||
502 | if (arr == NULL) { | |
503 | return; | |
504 | } | |
505 | for (i = 0; i < arr->len; i++) { | |
506 | struct qemu_plugin_dyn_cb *cb = | |
507 | &g_array_index(arr, struct qemu_plugin_dyn_cb, i); | |
54cb65d8 | 508 | |
37aff087 | 509 | if (!(rw & cb->rw)) { |
54cb65d8 EC |
510 | break; |
511 | } | |
512 | switch (cb->type) { | |
513 | case PLUGIN_CB_REGULAR: | |
aff56de5 RH |
514 | cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), |
515 | vaddr, cb->userp); | |
54cb65d8 EC |
516 | break; |
517 | case PLUGIN_CB_INLINE: | |
62f92b8d | 518 | exec_inline_op(cb, cpu->cpu_index); |
54cb65d8 EC |
519 | break; |
520 | default: | |
521 | g_assert_not_reached(); | |
522 | } | |
523 | } | |
524 | } | |
525 | ||
526 | void qemu_plugin_atexit_cb(void) | |
527 | { | |
528 | plugin_cb__udata(QEMU_PLUGIN_EV_ATEXIT); | |
529 | } | |
530 | ||
531 | void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id, | |
532 | qemu_plugin_udata_cb_t cb, | |
533 | void *udata) | |
534 | { | |
535 | plugin_register_cb_udata(id, QEMU_PLUGIN_EV_ATEXIT, cb, udata); | |
536 | } | |
537 | ||
f7e68c9c AB |
538 | /* |
539 | * Handle exit from linux-user. Unlike the normal atexit() mechanism | |
540 | * we need to handle the clean-up manually as it's possible threads | |
541 | * are still running. We need to remove all callbacks from code | |
542 | * generation, flush the current translations and then we can safely | |
543 | * trigger the exit callbacks. | |
544 | */ | |
545 | ||
546 | void qemu_plugin_user_exit(void) | |
547 | { | |
548 | enum qemu_plugin_event ev; | |
549 | CPUState *cpu; | |
550 | ||
2bbbc1be EC |
551 | /* |
552 | * Locking order: we must acquire locks in an order that is consistent | |
553 | * with the one in fork_start(). That is: | |
554 | * - start_exclusive(), which acquires qemu_cpu_list_lock, | |
555 | * must be called before acquiring plugin.lock. | |
556 | * - tb_flush(), which acquires mmap_lock(), must be called | |
557 | * while plugin.lock is not held. | |
558 | */ | |
f7e68c9c AB |
559 | start_exclusive(); |
560 | ||
2bbbc1be | 561 | qemu_rec_mutex_lock(&plugin.lock); |
f7e68c9c AB |
562 | /* un-register all callbacks except the final AT_EXIT one */ |
563 | for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) { | |
564 | if (ev != QEMU_PLUGIN_EV_ATEXIT) { | |
f4554923 RH |
565 | struct qemu_plugin_cb *cb, *next; |
566 | ||
567 | QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { | |
568 | plugin_unregister_cb__locked(cb->ctx, ev); | |
f7e68c9c AB |
569 | } |
570 | } | |
571 | } | |
f7e68c9c AB |
572 | CPU_FOREACH(cpu) { |
573 | qemu_plugin_disable_mem_helpers(cpu); | |
574 | } | |
2bbbc1be | 575 | qemu_rec_mutex_unlock(&plugin.lock); |
f7e68c9c | 576 | |
2bbbc1be | 577 | tb_flush(current_cpu); |
f7e68c9c AB |
578 | end_exclusive(); |
579 | ||
580 | /* now it's safe to handle the exit case */ | |
581 | qemu_plugin_atexit_cb(); | |
582 | } | |
583 | ||
f7e15aff AB |
584 | /* |
585 | * Helpers for *-user to ensure locks are sane across fork() events. | |
586 | */ | |
587 | ||
588 | void qemu_plugin_user_prefork_lock(void) | |
589 | { | |
590 | qemu_rec_mutex_lock(&plugin.lock); | |
591 | } | |
592 | ||
593 | void qemu_plugin_user_postfork(bool is_child) | |
594 | { | |
595 | if (is_child) { | |
596 | /* should we just reset via plugin_init? */ | |
597 | qemu_rec_mutex_init(&plugin.lock); | |
598 | } else { | |
599 | qemu_rec_mutex_unlock(&plugin.lock); | |
600 | } | |
601 | } | |
602 | ||
54cb65d8 EC |
603 | static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp) |
604 | { | |
605 | return ap == bp; | |
606 | } | |
607 | ||
608 | static void __attribute__((__constructor__)) plugin_init(void) | |
609 | { | |
610 | int i; | |
611 | ||
612 | for (i = 0; i < QEMU_PLUGIN_EV_MAX; i++) { | |
613 | QLIST_INIT(&plugin.cb_lists[i]); | |
614 | } | |
615 | qemu_rec_mutex_init(&plugin.lock); | |
616 | plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal); | |
617 | plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal); | |
a3c2cf0b PB |
618 | QLIST_INIT(&plugin.scoreboards); |
619 | plugin.scoreboard_alloc_size = 16; /* avoid frequent reallocation */ | |
54cb65d8 EC |
620 | QTAILQ_INIT(&plugin.ctxs); |
621 | qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16, | |
622 | QHT_MODE_AUTO_RESIZE); | |
623 | atexit(qemu_plugin_atexit_cb); | |
624 | } | |
4a448b14 PB |
625 | |
626 | int plugin_num_vcpus(void) | |
627 | { | |
628 | return plugin.num_vcpus; | |
629 | } | |
a3c2cf0b PB |
630 | |
631 | struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size) | |
632 | { | |
633 | struct qemu_plugin_scoreboard *score = | |
634 | g_malloc0(sizeof(struct qemu_plugin_scoreboard)); | |
635 | score->data = g_array_new(FALSE, TRUE, element_size); | |
636 | g_array_set_size(score->data, plugin.scoreboard_alloc_size); | |
637 | ||
638 | qemu_rec_mutex_lock(&plugin.lock); | |
639 | QLIST_INSERT_HEAD(&plugin.scoreboards, score, entry); | |
640 | qemu_rec_mutex_unlock(&plugin.lock); | |
641 | ||
642 | return score; | |
643 | } | |
644 | ||
645 | void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) | |
646 | { | |
647 | qemu_rec_mutex_lock(&plugin.lock); | |
648 | QLIST_REMOVE(score, entry); | |
649 | qemu_rec_mutex_unlock(&plugin.lock); | |
650 | ||
651 | g_array_free(score->data, TRUE); | |
652 | g_free(score); | |
653 | } |