qemu/plugins/core.c
<<
>>
Prefs
   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
  25struct 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
  32struct qemu_plugin_state plugin;
  33
  34struct 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
  48static 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
  55static 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
  63void 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 */
  85QEMU_DISABLE_CFI
  86static 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 */
 112QEMU_DISABLE_CFI
 113static 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 */
 135QEMU_DISABLE_CFI
 136static 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
 153static void
 154do_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
 189void 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
 195void
 196plugin_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
 202CPUPluginState *qemu_plugin_create_vcpu_state(void)
 203{
 204    return g_new0(CPUPluginState, 1);
 205}
 206
 207static 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
 255static 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
 272void 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
 278void 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
 291struct plugin_for_each_args {
 292    struct qemu_plugin_ctx *ctx;
 293    qemu_plugin_vcpu_simple_cb_t cb;
 294};
 295
 296static 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
 304void 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 */
 320static 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
 334static 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
 346void 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
 362void 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
 388void 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
 420void 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 */
 465QEMU_DISABLE_CFI
 466void 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 */
 485QEMU_DISABLE_CFI
 486void
 487qemu_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 */
 510QEMU_DISABLE_CFI
 511void 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
 527void 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
 535void 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
 542void 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
 548void 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
 554void 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
 560static 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
 566void 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
 574void 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
 596void 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
 635void qemu_plugin_atexit_cb(void)
 636{
 637    plugin_cb__udata(QEMU_PLUGIN_EV_ATEXIT);
 638}
 639
 640void 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
 655void 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
 697void qemu_plugin_user_prefork_lock(void)
 698{
 699    qemu_rec_mutex_lock(&plugin.lock);
 700}
 701
 702void 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
 712static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
 713{
 714    return ap == bp;
 715}
 716
 717static 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
 735int plugin_num_vcpus(void)
 736{
 737    return plugin.num_vcpus;
 738}
 739
 740struct 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
 754void 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