xref: /qemu/plugins/core.c (revision 6d1829fce4ea50d343f2df63eeff96685a359bf5)
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/lockable.h"
16 #include "qemu/option.h"
17 #include "qemu/plugin.h"
18 #include "qemu/queue.h"
19 #include "qemu/rcu_queue.h"
20 #include "qemu/rcu.h"
21 #include "exec/tb-flush.h"
22 #include "tcg/tcg-op-common.h"
23 #include "plugin.h"
24 
25 struct qemu_plugin_cb {
26     struct qemu_plugin_ctx *ctx;
27     union qemu_plugin_cb_sig f;
28     void *udata;
29     QLIST_ENTRY(qemu_plugin_cb) entry;
30 };
31 
32 struct qemu_plugin_state plugin;
33 
plugin_id_to_ctx_locked(qemu_plugin_id_t id)34 struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id)
35 {
36     struct qemu_plugin_ctx *ctx;
37     qemu_plugin_id_t *id_p;
38 
39     id_p = g_hash_table_lookup(plugin.id_ht, &id);
40     ctx = container_of(id_p, struct qemu_plugin_ctx, id);
41     if (ctx == NULL) {
42         error_report("plugin: invalid plugin id %" PRIu64, id);
43         abort();
44     }
45     return ctx;
46 }
47 
plugin_cpu_update__async(CPUState * cpu,run_on_cpu_data data)48 static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data)
49 {
50     bitmap_copy(cpu->plugin_state->event_mask,
51                 &data.host_ulong, QEMU_PLUGIN_EV_MAX);
52     tcg_flush_jmp_cache(cpu);
53 }
54 
plugin_cpu_update__locked(gpointer k,gpointer v,gpointer udata)55 static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata)
56 {
57     CPUState *cpu = container_of(k, CPUState, cpu_index);
58     run_on_cpu_data mask = RUN_ON_CPU_HOST_ULONG(*plugin.mask);
59 
60     async_run_on_cpu(cpu, plugin_cpu_update__async, mask);
61 }
62 
plugin_unregister_cb__locked(struct qemu_plugin_ctx * ctx,enum qemu_plugin_event ev)63 void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,
64                                   enum qemu_plugin_event ev)
65 {
66     struct qemu_plugin_cb *cb = ctx->callbacks[ev];
67 
68     if (cb == NULL) {
69         return;
70     }
71     QLIST_REMOVE_RCU(cb, entry);
72     g_free(cb);
73     ctx->callbacks[ev] = NULL;
74     if (QLIST_EMPTY_RCU(&plugin.cb_lists[ev])) {
75         clear_bit(ev, plugin.mask);
76         g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked, NULL);
77     }
78 }
79 
80 /*
81  * Disable CFI checks.
82  * The callback function has been loaded from an external library so we do not
83  * have type information
84  */
85 QEMU_DISABLE_CFI
plugin_vcpu_cb__simple(CPUState * cpu,enum qemu_plugin_event ev)86 static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev)
87 {
88     struct qemu_plugin_cb *cb, *next;
89 
90     switch (ev) {
91     case QEMU_PLUGIN_EV_VCPU_INIT:
92     case QEMU_PLUGIN_EV_VCPU_EXIT:
93     case QEMU_PLUGIN_EV_VCPU_IDLE:
94     case QEMU_PLUGIN_EV_VCPU_RESUME:
95         /* iterate safely; plugins might uninstall themselves at any time */
96         QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
97             qemu_plugin_vcpu_simple_cb_t func = cb->f.vcpu_simple;
98 
99             func(cb->ctx->id, cpu->cpu_index);
100         }
101         break;
102     default:
103         g_assert_not_reached();
104     }
105 }
106 
107 /*
108  * Disable CFI checks.
109  * The callback function has been loaded from an external library so we do not
110  * have type information
111  */
112 QEMU_DISABLE_CFI
plugin_cb__simple(enum qemu_plugin_event ev)113 static void plugin_cb__simple(enum qemu_plugin_event ev)
114 {
115     struct qemu_plugin_cb *cb, *next;
116 
117     switch (ev) {
118     case QEMU_PLUGIN_EV_FLUSH:
119         QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
120             qemu_plugin_simple_cb_t func = cb->f.simple;
121 
122             func(cb->ctx->id);
123         }
124         break;
125     default:
126         g_assert_not_reached();
127     }
128 }
129 
130 /*
131  * Disable CFI checks.
132  * The callback function has been loaded from an external library so we do not
133  * have type information
134  */
135 QEMU_DISABLE_CFI
plugin_cb__udata(enum qemu_plugin_event ev)136 static void plugin_cb__udata(enum qemu_plugin_event ev)
137 {
138     struct qemu_plugin_cb *cb, *next;
139 
140     switch (ev) {
141     case QEMU_PLUGIN_EV_ATEXIT:
142         QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
143             qemu_plugin_udata_cb_t func = cb->f.udata;
144 
145             func(cb->ctx->id, cb->udata);
146         }
147         break;
148     default:
149         g_assert_not_reached();
150     }
151 }
152 
153 static void
do_plugin_register_cb(qemu_plugin_id_t id,enum qemu_plugin_event ev,void * func,void * udata)154 do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
155                       void *func, void *udata)
156 {
157     struct qemu_plugin_ctx *ctx;
158 
159     QEMU_LOCK_GUARD(&plugin.lock);
160     ctx = plugin_id_to_ctx_locked(id);
161     /* if the plugin is on its way out, ignore this request */
162     if (unlikely(ctx->uninstalling)) {
163         return;
164     }
165     if (func) {
166         struct qemu_plugin_cb *cb = ctx->callbacks[ev];
167 
168         if (cb) {
169             cb->f.generic = func;
170             cb->udata = udata;
171         } else {
172             cb = g_new(struct qemu_plugin_cb, 1);
173             cb->ctx = ctx;
174             cb->f.generic = func;
175             cb->udata = udata;
176             ctx->callbacks[ev] = cb;
177             QLIST_INSERT_HEAD_RCU(&plugin.cb_lists[ev], cb, entry);
178             if (!test_bit(ev, plugin.mask)) {
179                 set_bit(ev, plugin.mask);
180                 g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked,
181                                      NULL);
182             }
183         }
184     } else {
185         plugin_unregister_cb__locked(ctx, ev);
186     }
187 }
188 
plugin_register_cb(qemu_plugin_id_t id,enum qemu_plugin_event ev,void * func)189 void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
190                         void *func)
191 {
192     do_plugin_register_cb(id, ev, func, NULL);
193 }
194 
195 void
plugin_register_cb_udata(qemu_plugin_id_t id,enum qemu_plugin_event ev,void * func,void * udata)196 plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev,
197                          void *func, void *udata)
198 {
199     do_plugin_register_cb(id, ev, func, udata);
200 }
201 
qemu_plugin_create_vcpu_state(void)202 CPUPluginState *qemu_plugin_create_vcpu_state(void)
203 {
204     return g_new0(CPUPluginState, 1);
205 }
206 
plugin_grow_scoreboards__locked(CPUState * cpu)207 static void plugin_grow_scoreboards__locked(CPUState *cpu)
208 {
209     size_t scoreboard_size = plugin.scoreboard_alloc_size;
210     bool need_realloc = false;
211 
212     if (cpu->cpu_index < scoreboard_size) {
213         return;
214     }
215 
216     while (cpu->cpu_index >= scoreboard_size) {
217         scoreboard_size *= 2;
218         need_realloc = true;
219     }
220 
221     if (!need_realloc) {
222         return;
223     }
224 
225     if (QLIST_EMPTY(&plugin.scoreboards)) {
226         /* just update size for future scoreboards */
227         plugin.scoreboard_alloc_size = scoreboard_size;
228         return;
229     }
230 
231     /*
232      * A scoreboard creation/deletion might be in progress. If a new vcpu is
233      * initialized at the same time, we are safe, as the new
234      * plugin.scoreboard_alloc_size was not yet written.
235      */
236     qemu_rec_mutex_unlock(&plugin.lock);
237 
238     /* cpus must be stopped, as tb might still use an existing scoreboard. */
239     start_exclusive();
240     /* re-acquire lock */
241     qemu_rec_mutex_lock(&plugin.lock);
242     /* in case another vcpu is created between unlock and exclusive section. */
243     if (scoreboard_size > plugin.scoreboard_alloc_size) {
244         struct qemu_plugin_scoreboard *score;
245         QLIST_FOREACH(score, &plugin.scoreboards, entry) {
246             g_array_set_size(score->data, scoreboard_size);
247         }
248         plugin.scoreboard_alloc_size = scoreboard_size;
249         /* force all tb to be flushed, as scoreboard pointers were changed. */
250         tb_flush(cpu);
251     }
252     end_exclusive();
253 }
254 
qemu_plugin_vcpu_init__async(CPUState * cpu,run_on_cpu_data unused)255 static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused)
256 {
257     bool success;
258 
259     assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
260     qemu_rec_mutex_lock(&plugin.lock);
261     plugin.num_vcpus = MAX(plugin.num_vcpus, cpu->cpu_index + 1);
262     plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL);
263     success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index,
264                                   &cpu->cpu_index);
265     g_assert(success);
266     plugin_grow_scoreboards__locked(cpu);
267     qemu_rec_mutex_unlock(&plugin.lock);
268 
269     plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
270 }
271 
qemu_plugin_vcpu_init_hook(CPUState * cpu)272 void qemu_plugin_vcpu_init_hook(CPUState *cpu)
273 {
274     /* Plugin initialization must wait until the cpu start executing code */
275     async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
276 }
277 
qemu_plugin_vcpu_exit_hook(CPUState * cpu)278 void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
279 {
280     bool success;
281 
282     plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT);
283 
284     assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
285     qemu_rec_mutex_lock(&plugin.lock);
286     success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index);
287     g_assert(success);
288     qemu_rec_mutex_unlock(&plugin.lock);
289 }
290 
291 struct plugin_for_each_args {
292     struct qemu_plugin_ctx *ctx;
293     qemu_plugin_vcpu_simple_cb_t cb;
294 };
295 
plugin_vcpu_for_each(gpointer k,gpointer v,gpointer udata)296 static void plugin_vcpu_for_each(gpointer k, gpointer v, gpointer udata)
297 {
298     struct plugin_for_each_args *args = udata;
299     int cpu_index = *(int *)k;
300 
301     args->cb(args->ctx->id, cpu_index);
302 }
303 
qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,qemu_plugin_vcpu_simple_cb_t cb)304 void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
305                                qemu_plugin_vcpu_simple_cb_t cb)
306 {
307     struct plugin_for_each_args args;
308 
309     if (cb == NULL) {
310         return;
311     }
312     qemu_rec_mutex_lock(&plugin.lock);
313     args.ctx = plugin_id_to_ctx_locked(id);
314     args.cb = cb;
315     g_hash_table_foreach(plugin.cpu_ht, plugin_vcpu_for_each, &args);
316     qemu_rec_mutex_unlock(&plugin.lock);
317 }
318 
319 /* Allocate and return a callback record */
plugin_get_dyn_cb(GArray ** arr)320 static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr)
321 {
322     GArray *cbs = *arr;
323 
324     if (!cbs) {
325         cbs = g_array_sized_new(false, true,
326                                 sizeof(struct qemu_plugin_dyn_cb), 1);
327         *arr = cbs;
328     }
329 
330     g_array_set_size(cbs, cbs->len + 1);
331     return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
332 }
333 
op_to_cb_type(enum qemu_plugin_op op)334 static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op)
335 {
336     switch (op) {
337     case QEMU_PLUGIN_INLINE_ADD_U64:
338         return PLUGIN_CB_INLINE_ADD_U64;
339     case QEMU_PLUGIN_INLINE_STORE_U64:
340         return PLUGIN_CB_INLINE_STORE_U64;
341     default:
342         g_assert_not_reached();
343     }
344 }
345 
plugin_register_inline_op_on_entry(GArray ** arr,enum qemu_plugin_mem_rw rw,enum qemu_plugin_op op,qemu_plugin_u64 entry,uint64_t imm)346 void plugin_register_inline_op_on_entry(GArray **arr,
347                                         enum qemu_plugin_mem_rw rw,
348                                         enum qemu_plugin_op op,
349                                         qemu_plugin_u64 entry,
350                                         uint64_t imm)
351 {
352     struct qemu_plugin_dyn_cb *dyn_cb;
353 
354     struct qemu_plugin_inline_cb inline_cb = { .rw = rw,
355                                                .entry = entry,
356                                                .imm = imm };
357     dyn_cb = plugin_get_dyn_cb(arr);
358     dyn_cb->type = op_to_cb_type(op);
359     dyn_cb->inline_insn = inline_cb;
360 }
361 
plugin_register_dyn_cb__udata(GArray ** arr,qemu_plugin_vcpu_udata_cb_t cb,enum qemu_plugin_cb_flags flags,void * udata)362 void plugin_register_dyn_cb__udata(GArray **arr,
363                                    qemu_plugin_vcpu_udata_cb_t cb,
364                                    enum qemu_plugin_cb_flags flags,
365                                    void *udata)
366 {
367     static TCGHelperInfo info[3] = {
368         [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
369         [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
370         /*
371          * Match qemu_plugin_vcpu_udata_cb_t:
372          *   void (*)(uint32_t, void *)
373          */
374         [0 ... 2].typemask = (dh_typemask(void, 0) |
375                               dh_typemask(i32, 1) |
376                               dh_typemask(ptr, 2))
377     };
378     assert((unsigned)flags < ARRAY_SIZE(info));
379 
380     struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
381     struct qemu_plugin_regular_cb regular_cb = { .f.vcpu_udata = cb,
382                                                  .userp = udata,
383                                                  .info = &info[flags] };
384     dyn_cb->type = PLUGIN_CB_REGULAR;
385     dyn_cb->regular = regular_cb;
386 }
387 
plugin_register_dyn_cond_cb__udata(GArray ** arr,qemu_plugin_vcpu_udata_cb_t cb,enum qemu_plugin_cb_flags flags,enum qemu_plugin_cond cond,qemu_plugin_u64 entry,uint64_t imm,void * udata)388 void plugin_register_dyn_cond_cb__udata(GArray **arr,
389                                         qemu_plugin_vcpu_udata_cb_t cb,
390                                         enum qemu_plugin_cb_flags flags,
391                                         enum qemu_plugin_cond cond,
392                                         qemu_plugin_u64 entry,
393                                         uint64_t imm,
394                                         void *udata)
395 {
396     static TCGHelperInfo info[3] = {
397         [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
398         [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
399         /*
400          * Match qemu_plugin_vcpu_udata_cb_t:
401          *   void (*)(uint32_t, void *)
402          */
403         [0 ... 2].typemask = (dh_typemask(void, 0) |
404                               dh_typemask(i32, 1) |
405                               dh_typemask(ptr, 2))
406     };
407     assert((unsigned)flags < ARRAY_SIZE(info));
408 
409     struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
410     struct qemu_plugin_conditional_cb cond_cb = { .userp = udata,
411                                                   .f.vcpu_udata = cb,
412                                                   .cond = cond,
413                                                   .entry = entry,
414                                                   .imm = imm,
415                                                   .info = &info[flags] };
416     dyn_cb->type = PLUGIN_CB_COND;
417     dyn_cb->cond = cond_cb;
418 }
419 
plugin_register_vcpu_mem_cb(GArray ** arr,void * cb,enum qemu_plugin_cb_flags flags,enum qemu_plugin_mem_rw rw,void * udata)420 void plugin_register_vcpu_mem_cb(GArray **arr,
421                                  void *cb,
422                                  enum qemu_plugin_cb_flags flags,
423                                  enum qemu_plugin_mem_rw rw,
424                                  void *udata)
425 {
426     /*
427      * Expect that the underlying type for enum qemu_plugin_meminfo_t
428      * is either int32_t or uint32_t, aka int or unsigned int.
429      */
430     QEMU_BUILD_BUG_ON(
431         !__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) &&
432         !__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t));
433 
434     static TCGHelperInfo info[3] = {
435         [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
436         [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
437         /*
438          * Match qemu_plugin_vcpu_mem_cb_t:
439          *   void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *)
440          */
441         [0 ... 2].typemask =
442             (dh_typemask(void, 0) |
443              dh_typemask(i32, 1) |
444              (__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t)
445               ? dh_typemask(i32, 2) : dh_typemask(s32, 2)) |
446              dh_typemask(i64, 3) |
447              dh_typemask(ptr, 4))
448     };
449     assert((unsigned)flags < ARRAY_SIZE(info));
450 
451     struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
452     struct qemu_plugin_regular_cb regular_cb = { .userp = udata,
453                                                  .rw = rw,
454                                                  .f.vcpu_mem = cb,
455                                                  .info = &info[flags] };
456     dyn_cb->type = PLUGIN_CB_MEM_REGULAR;
457     dyn_cb->regular = regular_cb;
458 }
459 
460 /*
461  * Disable CFI checks.
462  * The callback function has been loaded from an external library so we do not
463  * have type information
464  */
465 QEMU_DISABLE_CFI
qemu_plugin_tb_trans_cb(CPUState * cpu,struct qemu_plugin_tb * tb)466 void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb)
467 {
468     struct qemu_plugin_cb *cb, *next;
469     enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS;
470 
471     /* no plugin_state->event_mask check here; caller should have checked */
472 
473     QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
474         qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans;
475 
476         func(cb->ctx->id, tb);
477     }
478 }
479 
480 /*
481  * Disable CFI checks.
482  * The callback function has been loaded from an external library so we do not
483  * have type information
484  */
485 QEMU_DISABLE_CFI
486 void
qemu_plugin_vcpu_syscall(CPUState * cpu,int64_t num,uint64_t a1,uint64_t a2,uint64_t a3,uint64_t a4,uint64_t a5,uint64_t a6,uint64_t a7,uint64_t a8)487 qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
488                          uint64_t a3, uint64_t a4, uint64_t a5,
489                          uint64_t a6, uint64_t a7, uint64_t a8)
490 {
491     struct qemu_plugin_cb *cb, *next;
492     enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL;
493 
494     if (!test_bit(ev, cpu->plugin_state->event_mask)) {
495         return;
496     }
497 
498     QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
499         qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall;
500 
501         func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8);
502     }
503 }
504 
505 /*
506  * Disable CFI checks.
507  * The callback function has been loaded from an external library so we do not
508  * have type information
509  */
510 QEMU_DISABLE_CFI
qemu_plugin_vcpu_syscall_ret(CPUState * cpu,int64_t num,int64_t ret)511 void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
512 {
513     struct qemu_plugin_cb *cb, *next;
514     enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_RET;
515 
516     if (!test_bit(ev, cpu->plugin_state->event_mask)) {
517         return;
518     }
519 
520     QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
521         qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret;
522 
523         func(cb->ctx->id, cpu->cpu_index, num, ret);
524     }
525 }
526 
qemu_plugin_vcpu_idle_cb(CPUState * cpu)527 void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
528 {
529     /* idle and resume cb may be called before init, ignore in this case */
530     if (cpu->cpu_index < plugin.num_vcpus) {
531         plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
532     }
533 }
534 
qemu_plugin_vcpu_resume_cb(CPUState * cpu)535 void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
536 {
537     if (cpu->cpu_index < plugin.num_vcpus) {
538         plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
539     }
540 }
541 
qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,qemu_plugin_vcpu_simple_cb_t cb)542 void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
543                                        qemu_plugin_vcpu_simple_cb_t cb)
544 {
545     plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_IDLE, cb);
546 }
547 
qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,qemu_plugin_vcpu_simple_cb_t cb)548 void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
549                                          qemu_plugin_vcpu_simple_cb_t cb)
550 {
551     plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb);
552 }
553 
qemu_plugin_register_flush_cb(qemu_plugin_id_t id,qemu_plugin_simple_cb_t cb)554 void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
555                                    qemu_plugin_simple_cb_t cb)
556 {
557     plugin_register_cb(id, QEMU_PLUGIN_EV_FLUSH, cb);
558 }
559 
free_dyn_cb_arr(void * p,uint32_t h,void * userp)560 static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp)
561 {
562     g_array_free((GArray *) p, true);
563     return true;
564 }
565 
qemu_plugin_flush_cb(void)566 void qemu_plugin_flush_cb(void)
567 {
568     qht_iter_remove(&plugin.dyn_cb_arr_ht, free_dyn_cb_arr, NULL);
569     qht_reset(&plugin.dyn_cb_arr_ht);
570 
571     plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
572 }
573 
exec_inline_op(enum plugin_dyn_cb_type type,struct qemu_plugin_inline_cb * cb,int cpu_index)574 void exec_inline_op(enum plugin_dyn_cb_type type,
575                     struct qemu_plugin_inline_cb *cb,
576                     int cpu_index)
577 {
578     char *ptr = cb->entry.score->data->data;
579     size_t elem_size = g_array_get_element_size(
580         cb->entry.score->data);
581     size_t offset = cb->entry.offset;
582     uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size);
583 
584     switch (type) {
585     case PLUGIN_CB_INLINE_ADD_U64:
586         *val += cb->imm;
587         break;
588     case PLUGIN_CB_INLINE_STORE_U64:
589         *val = cb->imm;
590         break;
591     default:
592         g_assert_not_reached();
593     }
594 }
595 
qemu_plugin_vcpu_mem_cb(CPUState * cpu,uint64_t vaddr,uint64_t value_low,uint64_t value_high,MemOpIdx oi,enum qemu_plugin_mem_rw rw)596 void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
597                              uint64_t value_low,
598                              uint64_t value_high,
599                              MemOpIdx oi, enum qemu_plugin_mem_rw rw)
600 {
601     GArray *arr = cpu->neg.plugin_mem_cbs;
602     size_t i;
603 
604     if (arr == NULL) {
605         return;
606     }
607 
608     cpu->neg.plugin_mem_value_low = value_low;
609     cpu->neg.plugin_mem_value_high = value_high;
610 
611     for (i = 0; i < arr->len; i++) {
612         struct qemu_plugin_dyn_cb *cb =
613             &g_array_index(arr, struct qemu_plugin_dyn_cb, i);
614 
615         switch (cb->type) {
616         case PLUGIN_CB_MEM_REGULAR:
617             if (rw & cb->regular.rw) {
618                 cb->regular.f.vcpu_mem(cpu->cpu_index,
619                                        make_plugin_meminfo(oi, rw),
620                                        vaddr, cb->regular.userp);
621             }
622             break;
623         case PLUGIN_CB_INLINE_ADD_U64:
624         case PLUGIN_CB_INLINE_STORE_U64:
625             if (rw & cb->inline_insn.rw) {
626                 exec_inline_op(cb->type, &cb->inline_insn, cpu->cpu_index);
627             }
628             break;
629         default:
630             g_assert_not_reached();
631         }
632     }
633 }
634 
qemu_plugin_atexit_cb(void)635 void qemu_plugin_atexit_cb(void)
636 {
637     plugin_cb__udata(QEMU_PLUGIN_EV_ATEXIT);
638 }
639 
qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,qemu_plugin_udata_cb_t cb,void * udata)640 void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
641                                     qemu_plugin_udata_cb_t cb,
642                                     void *udata)
643 {
644     plugin_register_cb_udata(id, QEMU_PLUGIN_EV_ATEXIT, cb, udata);
645 }
646 
647 /*
648  * Handle exit from linux-user. Unlike the normal atexit() mechanism
649  * we need to handle the clean-up manually as it's possible threads
650  * are still running. We need to remove all callbacks from code
651  * generation, flush the current translations and then we can safely
652  * trigger the exit callbacks.
653  */
654 
qemu_plugin_user_exit(void)655 void qemu_plugin_user_exit(void)
656 {
657     enum qemu_plugin_event ev;
658     CPUState *cpu;
659 
660     /*
661      * Locking order: we must acquire locks in an order that is consistent
662      * with the one in fork_start(). That is:
663      * - start_exclusive(), which acquires qemu_cpu_list_lock,
664      *   must be called before acquiring plugin.lock.
665      * - tb_flush(), which acquires mmap_lock(), must be called
666      *   while plugin.lock is not held.
667      */
668     start_exclusive();
669 
670     qemu_rec_mutex_lock(&plugin.lock);
671     /* un-register all callbacks except the final AT_EXIT one */
672     for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
673         if (ev != QEMU_PLUGIN_EV_ATEXIT) {
674             struct qemu_plugin_cb *cb, *next;
675 
676             QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
677                 plugin_unregister_cb__locked(cb->ctx, ev);
678             }
679         }
680     }
681     CPU_FOREACH(cpu) {
682         qemu_plugin_disable_mem_helpers(cpu);
683     }
684     qemu_rec_mutex_unlock(&plugin.lock);
685 
686     tb_flush(current_cpu);
687     end_exclusive();
688 
689     /* now it's safe to handle the exit case */
690     qemu_plugin_atexit_cb();
691 }
692 
693 /*
694  * Helpers for *-user to ensure locks are sane across fork() events.
695  */
696 
qemu_plugin_user_prefork_lock(void)697 void qemu_plugin_user_prefork_lock(void)
698 {
699     qemu_rec_mutex_lock(&plugin.lock);
700 }
701 
qemu_plugin_user_postfork(bool is_child)702 void qemu_plugin_user_postfork(bool is_child)
703 {
704     if (is_child) {
705         /* should we just reset via plugin_init? */
706         qemu_rec_mutex_init(&plugin.lock);
707     } else {
708         qemu_rec_mutex_unlock(&plugin.lock);
709     }
710 }
711 
plugin_dyn_cb_arr_cmp(const void * ap,const void * bp)712 static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
713 {
714     return ap == bp;
715 }
716 
plugin_init(void)717 static void __attribute__((__constructor__)) plugin_init(void)
718 {
719     int i;
720 
721     for (i = 0; i < QEMU_PLUGIN_EV_MAX; i++) {
722         QLIST_INIT(&plugin.cb_lists[i]);
723     }
724     qemu_rec_mutex_init(&plugin.lock);
725     plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal);
726     plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal);
727     QLIST_INIT(&plugin.scoreboards);
728     plugin.scoreboard_alloc_size = 16; /* avoid frequent reallocation */
729     QTAILQ_INIT(&plugin.ctxs);
730     qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16,
731              QHT_MODE_AUTO_RESIZE);
732     atexit(qemu_plugin_atexit_cb);
733 }
734 
plugin_num_vcpus(void)735 int plugin_num_vcpus(void)
736 {
737     return plugin.num_vcpus;
738 }
739 
plugin_scoreboard_new(size_t element_size)740 struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size)
741 {
742     struct qemu_plugin_scoreboard *score =
743         g_malloc0(sizeof(struct qemu_plugin_scoreboard));
744     score->data = g_array_new(FALSE, TRUE, element_size);
745     g_array_set_size(score->data, plugin.scoreboard_alloc_size);
746 
747     qemu_rec_mutex_lock(&plugin.lock);
748     QLIST_INSERT_HEAD(&plugin.scoreboards, score, entry);
749     qemu_rec_mutex_unlock(&plugin.lock);
750 
751     return score;
752 }
753 
plugin_scoreboard_free(struct qemu_plugin_scoreboard * score)754 void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score)
755 {
756     qemu_rec_mutex_lock(&plugin.lock);
757     QLIST_REMOVE(score, entry);
758     qemu_rec_mutex_unlock(&plugin.lock);
759 
760     g_array_free(score->data, TRUE);
761     g_free(score);
762 }
763