154cb65d8SEmilio G. Cota /*
254cb65d8SEmilio G. Cota * QEMU Plugin Core Loader Code
354cb65d8SEmilio G. Cota *
454cb65d8SEmilio G. Cota * This is the code responsible for loading and unloading the plugins.
554cb65d8SEmilio G. Cota * Aside from the basic housekeeping tasks we also need to ensure any
654cb65d8SEmilio G. Cota * generated code is flushed when we remove a plugin so we cannot end
754cb65d8SEmilio G. Cota * up calling and unloaded helper function.
854cb65d8SEmilio G. Cota *
954cb65d8SEmilio G. Cota * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
1054cb65d8SEmilio G. Cota * Copyright (C) 2019, Linaro
1154cb65d8SEmilio G. Cota *
1254cb65d8SEmilio G. Cota * License: GNU GPL, version 2 or later.
1354cb65d8SEmilio G. Cota * See the COPYING file in the top-level directory.
1454cb65d8SEmilio G. Cota *
1554cb65d8SEmilio G. Cota * SPDX-License-Identifier: GPL-2.0-or-later
1654cb65d8SEmilio G. Cota */
1754cb65d8SEmilio G. Cota
1854cb65d8SEmilio G. Cota #include "qemu/osdep.h"
1954cb65d8SEmilio G. Cota #include "qemu/error-report.h"
2054cb65d8SEmilio G. Cota #include "qemu/config-file.h"
21*7b690fd3SAlex Bennée #include "qemu/help_option.h"
2254cb65d8SEmilio G. Cota #include "qapi/error.h"
23ac90871cSStefan Hajnoczi #include "qemu/lockable.h"
2454cb65d8SEmilio G. Cota #include "qemu/option.h"
2554cb65d8SEmilio G. Cota #include "qemu/rcu_queue.h"
2654cb65d8SEmilio G. Cota #include "qemu/qht.h"
2754cb65d8SEmilio G. Cota #include "qemu/bitmap.h"
28ad768e6fSPeter Maydell #include "qemu/cacheinfo.h"
2954cb65d8SEmilio G. Cota #include "qemu/xxhash.h"
3054cb65d8SEmilio G. Cota #include "qemu/plugin.h"
315df022cfSPeter Maydell #include "qemu/memalign.h"
3254cb65d8SEmilio G. Cota #include "hw/core/cpu.h"
33548c9609SAlex Bennée #include "exec/tb-flush.h"
345901b2e1SAlex Bennée #ifndef CONFIG_USER_ONLY
355901b2e1SAlex Bennée #include "hw/boards.h"
365901b2e1SAlex Bennée #endif
375901b2e1SAlex Bennée
3854cb65d8SEmilio G. Cota #include "plugin.h"
3954cb65d8SEmilio G. Cota
4054cb65d8SEmilio G. Cota /*
4154cb65d8SEmilio G. Cota * For convenience we use a bitmap for plugin.mask, but really all we need is a
4254cb65d8SEmilio G. Cota * u32, which is what we store in TranslationBlock.
4354cb65d8SEmilio G. Cota */
4454cb65d8SEmilio G. Cota QEMU_BUILD_BUG_ON(QEMU_PLUGIN_EV_MAX > 32);
4554cb65d8SEmilio G. Cota
4654cb65d8SEmilio G. Cota struct qemu_plugin_desc {
4754cb65d8SEmilio G. Cota char *path;
4854cb65d8SEmilio G. Cota char **argv;
4954cb65d8SEmilio G. Cota QTAILQ_ENTRY(qemu_plugin_desc) entry;
5054cb65d8SEmilio G. Cota int argc;
5154cb65d8SEmilio G. Cota };
5254cb65d8SEmilio G. Cota
5354cb65d8SEmilio G. Cota struct qemu_plugin_parse_arg {
5454cb65d8SEmilio G. Cota QemuPluginList *head;
5554cb65d8SEmilio G. Cota struct qemu_plugin_desc *curr;
5654cb65d8SEmilio G. Cota };
5754cb65d8SEmilio G. Cota
5854cb65d8SEmilio G. Cota QemuOptsList qemu_plugin_opts = {
5954cb65d8SEmilio G. Cota .name = "plugin",
6054cb65d8SEmilio G. Cota .implied_opt_name = "file",
6154cb65d8SEmilio G. Cota .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head),
6254cb65d8SEmilio G. Cota .desc = {
6354cb65d8SEmilio G. Cota /* do our own parsing to support multiple plugins */
6454cb65d8SEmilio G. Cota { /* end of list */ }
6554cb65d8SEmilio G. Cota },
6654cb65d8SEmilio G. Cota };
6754cb65d8SEmilio G. Cota
685901b2e1SAlex Bennée typedef int (*qemu_plugin_install_func_t)(qemu_plugin_id_t, const qemu_info_t *, int, char **);
6954cb65d8SEmilio G. Cota
7054cb65d8SEmilio G. Cota extern struct qemu_plugin_state plugin;
7154cb65d8SEmilio G. Cota
qemu_plugin_add_dyn_cb_arr(GArray * arr)7254cb65d8SEmilio G. Cota void qemu_plugin_add_dyn_cb_arr(GArray *arr)
7354cb65d8SEmilio G. Cota {
7454cb65d8SEmilio G. Cota uint32_t hash = qemu_xxhash2((uint64_t)(uintptr_t)arr);
7554cb65d8SEmilio G. Cota bool inserted;
7654cb65d8SEmilio G. Cota
7754cb65d8SEmilio G. Cota inserted = qht_insert(&plugin.dyn_cb_arr_ht, arr, hash, NULL);
7854cb65d8SEmilio G. Cota g_assert(inserted);
7954cb65d8SEmilio G. Cota }
8054cb65d8SEmilio G. Cota
plugin_find_desc(QemuPluginList * head,const char * path)8154cb65d8SEmilio G. Cota static struct qemu_plugin_desc *plugin_find_desc(QemuPluginList *head,
8254cb65d8SEmilio G. Cota const char *path)
8354cb65d8SEmilio G. Cota {
8454cb65d8SEmilio G. Cota struct qemu_plugin_desc *desc;
8554cb65d8SEmilio G. Cota
8654cb65d8SEmilio G. Cota QTAILQ_FOREACH(desc, head, entry) {
8754cb65d8SEmilio G. Cota if (strcmp(desc->path, path) == 0) {
8854cb65d8SEmilio G. Cota return desc;
8954cb65d8SEmilio G. Cota }
9054cb65d8SEmilio G. Cota }
9154cb65d8SEmilio G. Cota return NULL;
9254cb65d8SEmilio G. Cota }
9354cb65d8SEmilio G. Cota
plugin_add(void * opaque,const char * name,const char * value,Error ** errp)9454cb65d8SEmilio G. Cota static int plugin_add(void *opaque, const char *name, const char *value,
9554cb65d8SEmilio G. Cota Error **errp)
9654cb65d8SEmilio G. Cota {
9754cb65d8SEmilio G. Cota struct qemu_plugin_parse_arg *arg = opaque;
9854cb65d8SEmilio G. Cota struct qemu_plugin_desc *p;
993a445acbSMahmoud Mandour bool is_on;
1003a445acbSMahmoud Mandour char *fullarg;
10154cb65d8SEmilio G. Cota
102*7b690fd3SAlex Bennée if (is_help_option(value)) {
103*7b690fd3SAlex Bennée printf("Plugin options\n");
104*7b690fd3SAlex Bennée printf(" file=<path/to/plugin.so>\n");
105*7b690fd3SAlex Bennée printf(" plugin specific arguments\n");
106*7b690fd3SAlex Bennée exit(0);
107*7b690fd3SAlex Bennée } else if (strcmp(name, "file") == 0) {
10854cb65d8SEmilio G. Cota if (strcmp(value, "") == 0) {
10954cb65d8SEmilio G. Cota error_setg(errp, "requires a non-empty argument");
11054cb65d8SEmilio G. Cota return 1;
11154cb65d8SEmilio G. Cota }
11254cb65d8SEmilio G. Cota p = plugin_find_desc(arg->head, value);
11354cb65d8SEmilio G. Cota if (p == NULL) {
11454cb65d8SEmilio G. Cota p = g_new0(struct qemu_plugin_desc, 1);
11554cb65d8SEmilio G. Cota p->path = g_strdup(value);
11654cb65d8SEmilio G. Cota QTAILQ_INSERT_TAIL(arg->head, p, entry);
11754cb65d8SEmilio G. Cota }
11854cb65d8SEmilio G. Cota arg->curr = p;
1193a445acbSMahmoud Mandour } else {
12054cb65d8SEmilio G. Cota if (arg->curr == NULL) {
12154cb65d8SEmilio G. Cota error_setg(errp, "missing earlier '-plugin file=' option");
12254cb65d8SEmilio G. Cota return 1;
12354cb65d8SEmilio G. Cota }
1243a445acbSMahmoud Mandour
1253a445acbSMahmoud Mandour if (g_strcmp0(name, "arg") == 0 &&
1263a445acbSMahmoud Mandour !qapi_bool_parse(name, value, &is_on, NULL)) {
1273a445acbSMahmoud Mandour if (strchr(value, '=') == NULL) {
1283a445acbSMahmoud Mandour /* Will treat arg="argname" as "argname=on" */
1293a445acbSMahmoud Mandour fullarg = g_strdup_printf("%s=%s", value, "on");
1303a445acbSMahmoud Mandour } else {
1313a445acbSMahmoud Mandour fullarg = g_strdup_printf("%s", value);
1323a445acbSMahmoud Mandour }
1333a445acbSMahmoud Mandour warn_report("using 'arg=%s' is deprecated", value);
1343a445acbSMahmoud Mandour error_printf("Please use '%s' directly\n", fullarg);
1353a445acbSMahmoud Mandour } else {
1363a445acbSMahmoud Mandour fullarg = g_strdup_printf("%s=%s", name, value);
1373a445acbSMahmoud Mandour }
1383a445acbSMahmoud Mandour
13954cb65d8SEmilio G. Cota p = arg->curr;
14054cb65d8SEmilio G. Cota p->argc++;
14154cb65d8SEmilio G. Cota p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *));
1423a445acbSMahmoud Mandour p->argv[p->argc - 1] = fullarg;
14354cb65d8SEmilio G. Cota }
1443a445acbSMahmoud Mandour
14554cb65d8SEmilio G. Cota return 0;
14654cb65d8SEmilio G. Cota }
14754cb65d8SEmilio G. Cota
qemu_plugin_opt_parse(const char * optstr,QemuPluginList * head)14882f3346fSPhilippe Mathieu-Daudé void qemu_plugin_opt_parse(const char *optstr, QemuPluginList *head)
14954cb65d8SEmilio G. Cota {
15054cb65d8SEmilio G. Cota struct qemu_plugin_parse_arg arg;
15154cb65d8SEmilio G. Cota QemuOpts *opts;
15254cb65d8SEmilio G. Cota
15382f3346fSPhilippe Mathieu-Daudé opts = qemu_opts_parse_noisily(qemu_find_opts("plugin"), optstr, true);
15454cb65d8SEmilio G. Cota if (opts == NULL) {
15554cb65d8SEmilio G. Cota exit(1);
15654cb65d8SEmilio G. Cota }
15754cb65d8SEmilio G. Cota arg.head = head;
15854cb65d8SEmilio G. Cota arg.curr = NULL;
15954cb65d8SEmilio G. Cota qemu_opt_foreach(opts, plugin_add, &arg, &error_fatal);
16054cb65d8SEmilio G. Cota qemu_opts_del(opts);
16154cb65d8SEmilio G. Cota }
16254cb65d8SEmilio G. Cota
16354cb65d8SEmilio G. Cota /*
16454cb65d8SEmilio G. Cota * From: https://en.wikipedia.org/wiki/Xorshift
16554cb65d8SEmilio G. Cota * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
16654cb65d8SEmilio G. Cota * guaranteed to be >= INT_MAX).
16754cb65d8SEmilio G. Cota */
xorshift64star(uint64_t x)16854cb65d8SEmilio G. Cota static uint64_t xorshift64star(uint64_t x)
16954cb65d8SEmilio G. Cota {
17054cb65d8SEmilio G. Cota x ^= x >> 12; /* a */
17154cb65d8SEmilio G. Cota x ^= x << 25; /* b */
17254cb65d8SEmilio G. Cota x ^= x >> 27; /* c */
17354cb65d8SEmilio G. Cota return x * UINT64_C(2685821657736338717);
17454cb65d8SEmilio G. Cota }
17554cb65d8SEmilio G. Cota
176c905a368SDaniele Buono /*
177c905a368SDaniele Buono * Disable CFI checks.
178c905a368SDaniele Buono * The install and version functions have been loaded from an external library
179c905a368SDaniele Buono * so we do not have type information
180c905a368SDaniele Buono */
181c905a368SDaniele Buono QEMU_DISABLE_CFI
plugin_load(struct qemu_plugin_desc * desc,const qemu_info_t * info,Error ** errp)1820572f558SPaolo Bonzini static int plugin_load(struct qemu_plugin_desc *desc, const qemu_info_t *info, Error **errp)
18354cb65d8SEmilio G. Cota {
18454cb65d8SEmilio G. Cota qemu_plugin_install_func_t install;
18554cb65d8SEmilio G. Cota struct qemu_plugin_ctx *ctx;
18654cb65d8SEmilio G. Cota gpointer sym;
18754cb65d8SEmilio G. Cota int rc;
18854cb65d8SEmilio G. Cota
18954cb65d8SEmilio G. Cota ctx = qemu_memalign(qemu_dcache_linesize, sizeof(*ctx));
19054cb65d8SEmilio G. Cota memset(ctx, 0, sizeof(*ctx));
19154cb65d8SEmilio G. Cota ctx->desc = desc;
19254cb65d8SEmilio G. Cota
19354cb65d8SEmilio G. Cota ctx->handle = g_module_open(desc->path, G_MODULE_BIND_LOCAL);
19454cb65d8SEmilio G. Cota if (ctx->handle == NULL) {
1950572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: %s", desc->path, g_module_error());
19654cb65d8SEmilio G. Cota goto err_dlopen;
19754cb65d8SEmilio G. Cota }
19854cb65d8SEmilio G. Cota
19954cb65d8SEmilio G. Cota if (!g_module_symbol(ctx->handle, "qemu_plugin_install", &sym)) {
2000572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: %s", desc->path, g_module_error());
20154cb65d8SEmilio G. Cota goto err_symbol;
20254cb65d8SEmilio G. Cota }
20354cb65d8SEmilio G. Cota install = (qemu_plugin_install_func_t) sym;
20454cb65d8SEmilio G. Cota /* symbol was found; it could be NULL though */
20554cb65d8SEmilio G. Cota if (install == NULL) {
2060572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: qemu_plugin_install is NULL",
2070572f558SPaolo Bonzini desc->path);
20854cb65d8SEmilio G. Cota goto err_symbol;
20954cb65d8SEmilio G. Cota }
21054cb65d8SEmilio G. Cota
2113fb356ccSAlex Bennée if (!g_module_symbol(ctx->handle, "qemu_plugin_version", &sym)) {
2120572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: plugin does not declare API version %s",
2133fb356ccSAlex Bennée desc->path, g_module_error());
2143fb356ccSAlex Bennée goto err_symbol;
2153fb356ccSAlex Bennée } else {
2163fb356ccSAlex Bennée int version = *(int *)sym;
2173fb356ccSAlex Bennée if (version < QEMU_PLUGIN_MIN_VERSION) {
2180572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: plugin requires API version %d, but "
2193fb356ccSAlex Bennée "this QEMU supports only a minimum version of %d",
2203fb356ccSAlex Bennée desc->path, version, QEMU_PLUGIN_MIN_VERSION);
2213fb356ccSAlex Bennée goto err_symbol;
2223fb356ccSAlex Bennée } else if (version > QEMU_PLUGIN_VERSION) {
2230572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: plugin requires API version %d, but "
2243fb356ccSAlex Bennée "this QEMU supports only up to version %d",
2253fb356ccSAlex Bennée desc->path, version, QEMU_PLUGIN_VERSION);
2263fb356ccSAlex Bennée goto err_symbol;
2273fb356ccSAlex Bennée }
2283fb356ccSAlex Bennée }
2293fb356ccSAlex Bennée
23054cb65d8SEmilio G. Cota qemu_rec_mutex_lock(&plugin.lock);
23154cb65d8SEmilio G. Cota
23254cb65d8SEmilio G. Cota /* find an unused random id with &ctx as the seed */
23354cb65d8SEmilio G. Cota ctx->id = (uint64_t)(uintptr_t)ctx;
23454cb65d8SEmilio G. Cota for (;;) {
23554cb65d8SEmilio G. Cota void *existing;
23654cb65d8SEmilio G. Cota
23754cb65d8SEmilio G. Cota ctx->id = xorshift64star(ctx->id);
23854cb65d8SEmilio G. Cota existing = g_hash_table_lookup(plugin.id_ht, &ctx->id);
23954cb65d8SEmilio G. Cota if (likely(existing == NULL)) {
24054cb65d8SEmilio G. Cota bool success;
24154cb65d8SEmilio G. Cota
24254cb65d8SEmilio G. Cota success = g_hash_table_insert(plugin.id_ht, &ctx->id, &ctx->id);
24354cb65d8SEmilio G. Cota g_assert(success);
24454cb65d8SEmilio G. Cota break;
24554cb65d8SEmilio G. Cota }
24654cb65d8SEmilio G. Cota }
24754cb65d8SEmilio G. Cota QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry);
24854cb65d8SEmilio G. Cota ctx->installing = true;
2495901b2e1SAlex Bennée rc = install(ctx->id, info, desc->argc, desc->argv);
25054cb65d8SEmilio G. Cota ctx->installing = false;
25154cb65d8SEmilio G. Cota if (rc) {
2520572f558SPaolo Bonzini error_setg(errp, "Could not load plugin %s: qemu_plugin_install returned error code %d",
2530572f558SPaolo Bonzini desc->path, rc);
25454cb65d8SEmilio G. Cota /*
25554cb65d8SEmilio G. Cota * we cannot rely on the plugin doing its own cleanup, so
25654cb65d8SEmilio G. Cota * call a full uninstall if the plugin did not yet call it.
25754cb65d8SEmilio G. Cota */
25854cb65d8SEmilio G. Cota if (!ctx->uninstalling) {
25954cb65d8SEmilio G. Cota plugin_reset_uninstall(ctx->id, NULL, false);
26054cb65d8SEmilio G. Cota }
26154cb65d8SEmilio G. Cota }
26254cb65d8SEmilio G. Cota
26354cb65d8SEmilio G. Cota qemu_rec_mutex_unlock(&plugin.lock);
26454cb65d8SEmilio G. Cota return rc;
26554cb65d8SEmilio G. Cota
26654cb65d8SEmilio G. Cota err_symbol:
267b3137100SYonggang Luo g_module_close(ctx->handle);
26854cb65d8SEmilio G. Cota err_dlopen:
26954cb65d8SEmilio G. Cota qemu_vfree(ctx);
27054cb65d8SEmilio G. Cota return 1;
27154cb65d8SEmilio G. Cota }
27254cb65d8SEmilio G. Cota
27354cb65d8SEmilio G. Cota /* call after having removed @desc from the list */
plugin_desc_free(struct qemu_plugin_desc * desc)27454cb65d8SEmilio G. Cota static void plugin_desc_free(struct qemu_plugin_desc *desc)
27554cb65d8SEmilio G. Cota {
27654cb65d8SEmilio G. Cota int i;
27754cb65d8SEmilio G. Cota
27854cb65d8SEmilio G. Cota for (i = 0; i < desc->argc; i++) {
27954cb65d8SEmilio G. Cota g_free(desc->argv[i]);
28054cb65d8SEmilio G. Cota }
28154cb65d8SEmilio G. Cota g_free(desc->argv);
28254cb65d8SEmilio G. Cota g_free(desc->path);
28354cb65d8SEmilio G. Cota g_free(desc);
28454cb65d8SEmilio G. Cota }
28554cb65d8SEmilio G. Cota
28654cb65d8SEmilio G. Cota /**
28754cb65d8SEmilio G. Cota * qemu_plugin_load_list - load a list of plugins
28854cb65d8SEmilio G. Cota * @head: head of the list of descriptors of the plugins to be loaded
28954cb65d8SEmilio G. Cota *
29054cb65d8SEmilio G. Cota * Returns 0 if all plugins in the list are installed, !0 otherwise.
29154cb65d8SEmilio G. Cota *
29254cb65d8SEmilio G. Cota * Note: the descriptor of each successfully installed plugin is removed
29354cb65d8SEmilio G. Cota * from the list given by @head.
29454cb65d8SEmilio G. Cota */
qemu_plugin_load_list(QemuPluginList * head,Error ** errp)2950572f558SPaolo Bonzini int qemu_plugin_load_list(QemuPluginList *head, Error **errp)
29654cb65d8SEmilio G. Cota {
29754cb65d8SEmilio G. Cota struct qemu_plugin_desc *desc, *next;
2985901b2e1SAlex Bennée g_autofree qemu_info_t *info = g_new0(qemu_info_t, 1);
2995901b2e1SAlex Bennée
3005901b2e1SAlex Bennée info->target_name = TARGET_NAME;
3013fb356ccSAlex Bennée info->version.min = QEMU_PLUGIN_MIN_VERSION;
3023fb356ccSAlex Bennée info->version.cur = QEMU_PLUGIN_VERSION;
3035901b2e1SAlex Bennée #ifndef CONFIG_USER_ONLY
3045901b2e1SAlex Bennée MachineState *ms = MACHINE(qdev_get_machine());
3055901b2e1SAlex Bennée info->system_emulation = true;
3065901b2e1SAlex Bennée info->system.smp_vcpus = ms->smp.cpus;
3075901b2e1SAlex Bennée info->system.max_vcpus = ms->smp.max_cpus;
3085901b2e1SAlex Bennée #else
3095901b2e1SAlex Bennée info->system_emulation = false;
3105901b2e1SAlex Bennée #endif
31154cb65d8SEmilio G. Cota
31254cb65d8SEmilio G. Cota QTAILQ_FOREACH_SAFE(desc, head, entry, next) {
31354cb65d8SEmilio G. Cota int err;
31454cb65d8SEmilio G. Cota
3150572f558SPaolo Bonzini err = plugin_load(desc, info, errp);
31654cb65d8SEmilio G. Cota if (err) {
31754cb65d8SEmilio G. Cota return err;
31854cb65d8SEmilio G. Cota }
31954cb65d8SEmilio G. Cota QTAILQ_REMOVE(head, desc, entry);
32054cb65d8SEmilio G. Cota }
32154cb65d8SEmilio G. Cota return 0;
32254cb65d8SEmilio G. Cota }
32354cb65d8SEmilio G. Cota
32454cb65d8SEmilio G. Cota struct qemu_plugin_reset_data {
32554cb65d8SEmilio G. Cota struct qemu_plugin_ctx *ctx;
32654cb65d8SEmilio G. Cota qemu_plugin_simple_cb_t cb;
32754cb65d8SEmilio G. Cota bool reset;
32854cb65d8SEmilio G. Cota };
32954cb65d8SEmilio G. Cota
plugin_reset_destroy__locked(struct qemu_plugin_reset_data * data)33054cb65d8SEmilio G. Cota static void plugin_reset_destroy__locked(struct qemu_plugin_reset_data *data)
33154cb65d8SEmilio G. Cota {
33254cb65d8SEmilio G. Cota struct qemu_plugin_ctx *ctx = data->ctx;
33354cb65d8SEmilio G. Cota enum qemu_plugin_event ev;
33454cb65d8SEmilio G. Cota bool success;
33554cb65d8SEmilio G. Cota
33654cb65d8SEmilio G. Cota /*
33754cb65d8SEmilio G. Cota * After updating the subscription lists there is no need to wait for an RCU
33854cb65d8SEmilio G. Cota * grace period to elapse, because right now we either are in a "safe async"
33954cb65d8SEmilio G. Cota * work environment (i.e. all vCPUs are asleep), or no vCPUs have yet been
34054cb65d8SEmilio G. Cota * created.
34154cb65d8SEmilio G. Cota */
34254cb65d8SEmilio G. Cota for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
34354cb65d8SEmilio G. Cota plugin_unregister_cb__locked(ctx, ev);
34454cb65d8SEmilio G. Cota }
34554cb65d8SEmilio G. Cota
34654cb65d8SEmilio G. Cota if (data->reset) {
34754cb65d8SEmilio G. Cota g_assert(ctx->resetting);
34854cb65d8SEmilio G. Cota if (data->cb) {
34954cb65d8SEmilio G. Cota data->cb(ctx->id);
35054cb65d8SEmilio G. Cota }
35154cb65d8SEmilio G. Cota ctx->resetting = false;
35254cb65d8SEmilio G. Cota g_free(data);
35354cb65d8SEmilio G. Cota return;
35454cb65d8SEmilio G. Cota }
35554cb65d8SEmilio G. Cota
35654cb65d8SEmilio G. Cota g_assert(ctx->uninstalling);
35754cb65d8SEmilio G. Cota /* we cannot dlclose if we are going to return to plugin code */
35854cb65d8SEmilio G. Cota if (ctx->installing) {
35954cb65d8SEmilio G. Cota error_report("Calling qemu_plugin_uninstall from the install function "
36054cb65d8SEmilio G. Cota "is a bug. Instead, return !0 from the install function.");
36154cb65d8SEmilio G. Cota abort();
36254cb65d8SEmilio G. Cota }
36354cb65d8SEmilio G. Cota
36454cb65d8SEmilio G. Cota success = g_hash_table_remove(plugin.id_ht, &ctx->id);
36554cb65d8SEmilio G. Cota g_assert(success);
36654cb65d8SEmilio G. Cota QTAILQ_REMOVE(&plugin.ctxs, ctx, entry);
36754cb65d8SEmilio G. Cota if (data->cb) {
36854cb65d8SEmilio G. Cota data->cb(ctx->id);
36954cb65d8SEmilio G. Cota }
37054cb65d8SEmilio G. Cota if (!g_module_close(ctx->handle)) {
37154cb65d8SEmilio G. Cota warn_report("%s: %s", __func__, g_module_error());
37254cb65d8SEmilio G. Cota }
37354cb65d8SEmilio G. Cota plugin_desc_free(ctx->desc);
37454cb65d8SEmilio G. Cota qemu_vfree(ctx);
37554cb65d8SEmilio G. Cota g_free(data);
37654cb65d8SEmilio G. Cota }
37754cb65d8SEmilio G. Cota
plugin_reset_destroy(struct qemu_plugin_reset_data * data)37854cb65d8SEmilio G. Cota static void plugin_reset_destroy(struct qemu_plugin_reset_data *data)
37954cb65d8SEmilio G. Cota {
38054cb65d8SEmilio G. Cota qemu_rec_mutex_lock(&plugin.lock);
38154cb65d8SEmilio G. Cota plugin_reset_destroy__locked(data);
38254cb65d8SEmilio G. Cota qemu_rec_mutex_lock(&plugin.lock);
38354cb65d8SEmilio G. Cota }
38454cb65d8SEmilio G. Cota
plugin_flush_destroy(CPUState * cpu,run_on_cpu_data arg)38554cb65d8SEmilio G. Cota static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg)
38654cb65d8SEmilio G. Cota {
38754cb65d8SEmilio G. Cota struct qemu_plugin_reset_data *data = arg.host_ptr;
38854cb65d8SEmilio G. Cota
38954cb65d8SEmilio G. Cota g_assert(cpu_in_exclusive_context(cpu));
39054cb65d8SEmilio G. Cota tb_flush(cpu);
39154cb65d8SEmilio G. Cota plugin_reset_destroy(data);
39254cb65d8SEmilio G. Cota }
39354cb65d8SEmilio G. Cota
plugin_reset_uninstall(qemu_plugin_id_t id,qemu_plugin_simple_cb_t cb,bool reset)39454cb65d8SEmilio G. Cota void plugin_reset_uninstall(qemu_plugin_id_t id,
39554cb65d8SEmilio G. Cota qemu_plugin_simple_cb_t cb,
39654cb65d8SEmilio G. Cota bool reset)
39754cb65d8SEmilio G. Cota {
39854cb65d8SEmilio G. Cota struct qemu_plugin_reset_data *data;
399c65288deSMarc-André Lureau struct qemu_plugin_ctx *ctx = NULL;
40054cb65d8SEmilio G. Cota
401ac90871cSStefan Hajnoczi WITH_QEMU_LOCK_GUARD(&plugin.lock) {
40254cb65d8SEmilio G. Cota ctx = plugin_id_to_ctx_locked(id);
40354cb65d8SEmilio G. Cota if (ctx->uninstalling || (reset && ctx->resetting)) {
40454cb65d8SEmilio G. Cota return;
40554cb65d8SEmilio G. Cota }
40654cb65d8SEmilio G. Cota ctx->resetting = reset;
40754cb65d8SEmilio G. Cota ctx->uninstalling = !reset;
408ac90871cSStefan Hajnoczi }
40954cb65d8SEmilio G. Cota
41054cb65d8SEmilio G. Cota data = g_new(struct qemu_plugin_reset_data, 1);
41154cb65d8SEmilio G. Cota data->ctx = ctx;
41254cb65d8SEmilio G. Cota data->cb = cb;
41354cb65d8SEmilio G. Cota data->reset = reset;
41454cb65d8SEmilio G. Cota /*
41554cb65d8SEmilio G. Cota * Only flush the code cache if the vCPUs have been created. If so,
41654cb65d8SEmilio G. Cota * current_cpu must be non-NULL.
41754cb65d8SEmilio G. Cota */
41854cb65d8SEmilio G. Cota if (current_cpu) {
41954cb65d8SEmilio G. Cota async_safe_run_on_cpu(current_cpu, plugin_flush_destroy,
42054cb65d8SEmilio G. Cota RUN_ON_CPU_HOST_PTR(data));
42154cb65d8SEmilio G. Cota } else {
42254cb65d8SEmilio G. Cota /*
42354cb65d8SEmilio G. Cota * If current_cpu isn't set, then we don't have yet any vCPU threads
42454cb65d8SEmilio G. Cota * and we therefore can remove the callbacks synchronously.
42554cb65d8SEmilio G. Cota */
42654cb65d8SEmilio G. Cota plugin_reset_destroy(data);
42754cb65d8SEmilio G. Cota }
42854cb65d8SEmilio G. Cota }
429