xref: /openbmc/qemu/plugins/loader.c (revision 40a770ea8b9478aefa3a60049bc67cc04ace569c)
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