]>
Commit | Line | Data |
---|---|---|
54cb65d8 EC |
1 | /* |
2 | * QEMU Plugin Core Loader Code | |
3 | * | |
4 | * This is the code responsible for loading and unloading the plugins. | |
5 | * Aside from the basic housekeeping tasks we also need to ensure any | |
6 | * generated code is flushed when we remove a plugin so we cannot end | |
7 | * up calling and unloaded helper function. | |
8 | * | |
9 | * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> | |
10 | * Copyright (C) 2019, Linaro | |
11 | * | |
12 | * License: GNU GPL, version 2 or later. | |
13 | * See the COPYING file in the top-level directory. | |
14 | * | |
15 | * SPDX-License-Identifier: GPL-2.0-or-later | |
16 | */ | |
17 | ||
18 | #include "qemu/osdep.h" | |
19 | #include "qemu/error-report.h" | |
20 | #include "qemu/config-file.h" | |
21 | #include "qapi/error.h" | |
ac90871c | 22 | #include "qemu/lockable.h" |
54cb65d8 EC |
23 | #include "qemu/option.h" |
24 | #include "qemu/rcu_queue.h" | |
25 | #include "qemu/qht.h" | |
26 | #include "qemu/bitmap.h" | |
ad768e6f | 27 | #include "qemu/cacheinfo.h" |
54cb65d8 EC |
28 | #include "qemu/xxhash.h" |
29 | #include "qemu/plugin.h" | |
5df022cf | 30 | #include "qemu/memalign.h" |
54cb65d8 | 31 | #include "hw/core/cpu.h" |
548c9609 | 32 | #include "exec/tb-flush.h" |
5901b2e1 AB |
33 | #ifndef CONFIG_USER_ONLY |
34 | #include "hw/boards.h" | |
35 | #endif | |
c905a368 | 36 | #include "qemu/compiler.h" |
5901b2e1 | 37 | |
54cb65d8 EC |
38 | #include "plugin.h" |
39 | ||
40 | /* | |
41 | * For convenience we use a bitmap for plugin.mask, but really all we need is a | |
42 | * u32, which is what we store in TranslationBlock. | |
43 | */ | |
44 | QEMU_BUILD_BUG_ON(QEMU_PLUGIN_EV_MAX > 32); | |
45 | ||
46 | struct qemu_plugin_desc { | |
47 | char *path; | |
48 | char **argv; | |
49 | QTAILQ_ENTRY(qemu_plugin_desc) entry; | |
50 | int argc; | |
51 | }; | |
52 | ||
53 | struct qemu_plugin_parse_arg { | |
54 | QemuPluginList *head; | |
55 | struct qemu_plugin_desc *curr; | |
56 | }; | |
57 | ||
58 | QemuOptsList qemu_plugin_opts = { | |
59 | .name = "plugin", | |
60 | .implied_opt_name = "file", | |
61 | .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), | |
62 | .desc = { | |
63 | /* do our own parsing to support multiple plugins */ | |
64 | { /* end of list */ } | |
65 | }, | |
66 | }; | |
67 | ||
5901b2e1 | 68 | typedef int (*qemu_plugin_install_func_t)(qemu_plugin_id_t, const qemu_info_t *, int, char **); |
54cb65d8 EC |
69 | |
70 | extern struct qemu_plugin_state plugin; | |
71 | ||
72 | void qemu_plugin_add_dyn_cb_arr(GArray *arr) | |
73 | { | |
74 | uint32_t hash = qemu_xxhash2((uint64_t)(uintptr_t)arr); | |
75 | bool inserted; | |
76 | ||
77 | inserted = qht_insert(&plugin.dyn_cb_arr_ht, arr, hash, NULL); | |
78 | g_assert(inserted); | |
79 | } | |
80 | ||
81 | static struct qemu_plugin_desc *plugin_find_desc(QemuPluginList *head, | |
82 | const char *path) | |
83 | { | |
84 | struct qemu_plugin_desc *desc; | |
85 | ||
86 | QTAILQ_FOREACH(desc, head, entry) { | |
87 | if (strcmp(desc->path, path) == 0) { | |
88 | return desc; | |
89 | } | |
90 | } | |
91 | return NULL; | |
92 | } | |
93 | ||
94 | static int plugin_add(void *opaque, const char *name, const char *value, | |
95 | Error **errp) | |
96 | { | |
97 | struct qemu_plugin_parse_arg *arg = opaque; | |
98 | struct qemu_plugin_desc *p; | |
3a445acb MM |
99 | bool is_on; |
100 | char *fullarg; | |
54cb65d8 EC |
101 | |
102 | if (strcmp(name, "file") == 0) { | |
103 | if (strcmp(value, "") == 0) { | |
104 | error_setg(errp, "requires a non-empty argument"); | |
105 | return 1; | |
106 | } | |
107 | p = plugin_find_desc(arg->head, value); | |
108 | if (p == NULL) { | |
109 | p = g_new0(struct qemu_plugin_desc, 1); | |
110 | p->path = g_strdup(value); | |
111 | QTAILQ_INSERT_TAIL(arg->head, p, entry); | |
112 | } | |
113 | arg->curr = p; | |
3a445acb | 114 | } else { |
54cb65d8 EC |
115 | if (arg->curr == NULL) { |
116 | error_setg(errp, "missing earlier '-plugin file=' option"); | |
117 | return 1; | |
118 | } | |
3a445acb MM |
119 | |
120 | if (g_strcmp0(name, "arg") == 0 && | |
121 | !qapi_bool_parse(name, value, &is_on, NULL)) { | |
122 | if (strchr(value, '=') == NULL) { | |
123 | /* Will treat arg="argname" as "argname=on" */ | |
124 | fullarg = g_strdup_printf("%s=%s", value, "on"); | |
125 | } else { | |
126 | fullarg = g_strdup_printf("%s", value); | |
127 | } | |
128 | warn_report("using 'arg=%s' is deprecated", value); | |
129 | error_printf("Please use '%s' directly\n", fullarg); | |
130 | } else { | |
131 | fullarg = g_strdup_printf("%s=%s", name, value); | |
132 | } | |
133 | ||
54cb65d8 EC |
134 | p = arg->curr; |
135 | p->argc++; | |
136 | p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *)); | |
3a445acb | 137 | p->argv[p->argc - 1] = fullarg; |
54cb65d8 | 138 | } |
3a445acb | 139 | |
54cb65d8 EC |
140 | return 0; |
141 | } | |
142 | ||
82f3346f | 143 | void qemu_plugin_opt_parse(const char *optstr, QemuPluginList *head) |
54cb65d8 EC |
144 | { |
145 | struct qemu_plugin_parse_arg arg; | |
146 | QemuOpts *opts; | |
147 | ||
82f3346f | 148 | opts = qemu_opts_parse_noisily(qemu_find_opts("plugin"), optstr, true); |
54cb65d8 EC |
149 | if (opts == NULL) { |
150 | exit(1); | |
151 | } | |
152 | arg.head = head; | |
153 | arg.curr = NULL; | |
154 | qemu_opt_foreach(opts, plugin_add, &arg, &error_fatal); | |
155 | qemu_opts_del(opts); | |
156 | } | |
157 | ||
158 | /* | |
159 | * From: https://en.wikipedia.org/wiki/Xorshift | |
160 | * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only | |
161 | * guaranteed to be >= INT_MAX). | |
162 | */ | |
163 | static uint64_t xorshift64star(uint64_t x) | |
164 | { | |
165 | x ^= x >> 12; /* a */ | |
166 | x ^= x << 25; /* b */ | |
167 | x ^= x >> 27; /* c */ | |
168 | return x * UINT64_C(2685821657736338717); | |
169 | } | |
170 | ||
c905a368 DB |
171 | /* |
172 | * Disable CFI checks. | |
173 | * The install and version functions have been loaded from an external library | |
174 | * so we do not have type information | |
175 | */ | |
176 | QEMU_DISABLE_CFI | |
0572f558 | 177 | static int plugin_load(struct qemu_plugin_desc *desc, const qemu_info_t *info, Error **errp) |
54cb65d8 EC |
178 | { |
179 | qemu_plugin_install_func_t install; | |
180 | struct qemu_plugin_ctx *ctx; | |
181 | gpointer sym; | |
182 | int rc; | |
183 | ||
184 | ctx = qemu_memalign(qemu_dcache_linesize, sizeof(*ctx)); | |
185 | memset(ctx, 0, sizeof(*ctx)); | |
186 | ctx->desc = desc; | |
187 | ||
188 | ctx->handle = g_module_open(desc->path, G_MODULE_BIND_LOCAL); | |
189 | if (ctx->handle == NULL) { | |
0572f558 | 190 | error_setg(errp, "Could not load plugin %s: %s", desc->path, g_module_error()); |
54cb65d8 EC |
191 | goto err_dlopen; |
192 | } | |
193 | ||
194 | if (!g_module_symbol(ctx->handle, "qemu_plugin_install", &sym)) { | |
0572f558 | 195 | error_setg(errp, "Could not load plugin %s: %s", desc->path, g_module_error()); |
54cb65d8 EC |
196 | goto err_symbol; |
197 | } | |
198 | install = (qemu_plugin_install_func_t) sym; | |
199 | /* symbol was found; it could be NULL though */ | |
200 | if (install == NULL) { | |
0572f558 PB |
201 | error_setg(errp, "Could not load plugin %s: qemu_plugin_install is NULL", |
202 | desc->path); | |
54cb65d8 EC |
203 | goto err_symbol; |
204 | } | |
205 | ||
3fb356cc | 206 | if (!g_module_symbol(ctx->handle, "qemu_plugin_version", &sym)) { |
0572f558 PB |
207 | error_setg(errp, "Could not load plugin %s: plugin does not declare API version %s", |
208 | desc->path, g_module_error()); | |
3fb356cc AB |
209 | goto err_symbol; |
210 | } else { | |
211 | int version = *(int *)sym; | |
212 | if (version < QEMU_PLUGIN_MIN_VERSION) { | |
0572f558 PB |
213 | error_setg(errp, "Could not load plugin %s: plugin requires API version %d, but " |
214 | "this QEMU supports only a minimum version of %d", | |
215 | desc->path, version, QEMU_PLUGIN_MIN_VERSION); | |
3fb356cc AB |
216 | goto err_symbol; |
217 | } else if (version > QEMU_PLUGIN_VERSION) { | |
0572f558 PB |
218 | error_setg(errp, "Could not load plugin %s: plugin requires API version %d, but " |
219 | "this QEMU supports only up to version %d", | |
220 | desc->path, version, QEMU_PLUGIN_VERSION); | |
3fb356cc AB |
221 | goto err_symbol; |
222 | } | |
223 | } | |
224 | ||
54cb65d8 EC |
225 | qemu_rec_mutex_lock(&plugin.lock); |
226 | ||
227 | /* find an unused random id with &ctx as the seed */ | |
228 | ctx->id = (uint64_t)(uintptr_t)ctx; | |
229 | for (;;) { | |
230 | void *existing; | |
231 | ||
232 | ctx->id = xorshift64star(ctx->id); | |
233 | existing = g_hash_table_lookup(plugin.id_ht, &ctx->id); | |
234 | if (likely(existing == NULL)) { | |
235 | bool success; | |
236 | ||
237 | success = g_hash_table_insert(plugin.id_ht, &ctx->id, &ctx->id); | |
238 | g_assert(success); | |
239 | break; | |
240 | } | |
241 | } | |
242 | QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry); | |
243 | ctx->installing = true; | |
5901b2e1 | 244 | rc = install(ctx->id, info, desc->argc, desc->argv); |
54cb65d8 EC |
245 | ctx->installing = false; |
246 | if (rc) { | |
0572f558 PB |
247 | error_setg(errp, "Could not load plugin %s: qemu_plugin_install returned error code %d", |
248 | desc->path, rc); | |
54cb65d8 EC |
249 | /* |
250 | * we cannot rely on the plugin doing its own cleanup, so | |
251 | * call a full uninstall if the plugin did not yet call it. | |
252 | */ | |
253 | if (!ctx->uninstalling) { | |
254 | plugin_reset_uninstall(ctx->id, NULL, false); | |
255 | } | |
256 | } | |
257 | ||
258 | qemu_rec_mutex_unlock(&plugin.lock); | |
259 | return rc; | |
260 | ||
261 | err_symbol: | |
b3137100 | 262 | g_module_close(ctx->handle); |
54cb65d8 EC |
263 | err_dlopen: |
264 | qemu_vfree(ctx); | |
265 | return 1; | |
266 | } | |
267 | ||
268 | /* call after having removed @desc from the list */ | |
269 | static void plugin_desc_free(struct qemu_plugin_desc *desc) | |
270 | { | |
271 | int i; | |
272 | ||
273 | for (i = 0; i < desc->argc; i++) { | |
274 | g_free(desc->argv[i]); | |
275 | } | |
276 | g_free(desc->argv); | |
277 | g_free(desc->path); | |
278 | g_free(desc); | |
279 | } | |
280 | ||
281 | /** | |
282 | * qemu_plugin_load_list - load a list of plugins | |
283 | * @head: head of the list of descriptors of the plugins to be loaded | |
284 | * | |
285 | * Returns 0 if all plugins in the list are installed, !0 otherwise. | |
286 | * | |
287 | * Note: the descriptor of each successfully installed plugin is removed | |
288 | * from the list given by @head. | |
289 | */ | |
0572f558 | 290 | int qemu_plugin_load_list(QemuPluginList *head, Error **errp) |
54cb65d8 EC |
291 | { |
292 | struct qemu_plugin_desc *desc, *next; | |
5901b2e1 AB |
293 | g_autofree qemu_info_t *info = g_new0(qemu_info_t, 1); |
294 | ||
295 | info->target_name = TARGET_NAME; | |
3fb356cc AB |
296 | info->version.min = QEMU_PLUGIN_MIN_VERSION; |
297 | info->version.cur = QEMU_PLUGIN_VERSION; | |
5901b2e1 AB |
298 | #ifndef CONFIG_USER_ONLY |
299 | MachineState *ms = MACHINE(qdev_get_machine()); | |
300 | info->system_emulation = true; | |
301 | info->system.smp_vcpus = ms->smp.cpus; | |
302 | info->system.max_vcpus = ms->smp.max_cpus; | |
303 | #else | |
304 | info->system_emulation = false; | |
305 | #endif | |
54cb65d8 EC |
306 | |
307 | QTAILQ_FOREACH_SAFE(desc, head, entry, next) { | |
308 | int err; | |
309 | ||
0572f558 | 310 | err = plugin_load(desc, info, errp); |
54cb65d8 EC |
311 | if (err) { |
312 | return err; | |
313 | } | |
314 | QTAILQ_REMOVE(head, desc, entry); | |
315 | } | |
316 | return 0; | |
317 | } | |
318 | ||
319 | struct qemu_plugin_reset_data { | |
320 | struct qemu_plugin_ctx *ctx; | |
321 | qemu_plugin_simple_cb_t cb; | |
322 | bool reset; | |
323 | }; | |
324 | ||
325 | static void plugin_reset_destroy__locked(struct qemu_plugin_reset_data *data) | |
326 | { | |
327 | struct qemu_plugin_ctx *ctx = data->ctx; | |
328 | enum qemu_plugin_event ev; | |
329 | bool success; | |
330 | ||
331 | /* | |
332 | * After updating the subscription lists there is no need to wait for an RCU | |
333 | * grace period to elapse, because right now we either are in a "safe async" | |
334 | * work environment (i.e. all vCPUs are asleep), or no vCPUs have yet been | |
335 | * created. | |
336 | */ | |
337 | for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) { | |
338 | plugin_unregister_cb__locked(ctx, ev); | |
339 | } | |
340 | ||
341 | if (data->reset) { | |
342 | g_assert(ctx->resetting); | |
343 | if (data->cb) { | |
344 | data->cb(ctx->id); | |
345 | } | |
346 | ctx->resetting = false; | |
347 | g_free(data); | |
348 | return; | |
349 | } | |
350 | ||
351 | g_assert(ctx->uninstalling); | |
352 | /* we cannot dlclose if we are going to return to plugin code */ | |
353 | if (ctx->installing) { | |
354 | error_report("Calling qemu_plugin_uninstall from the install function " | |
355 | "is a bug. Instead, return !0 from the install function."); | |
356 | abort(); | |
357 | } | |
358 | ||
359 | success = g_hash_table_remove(plugin.id_ht, &ctx->id); | |
360 | g_assert(success); | |
361 | QTAILQ_REMOVE(&plugin.ctxs, ctx, entry); | |
362 | if (data->cb) { | |
363 | data->cb(ctx->id); | |
364 | } | |
365 | if (!g_module_close(ctx->handle)) { | |
366 | warn_report("%s: %s", __func__, g_module_error()); | |
367 | } | |
368 | plugin_desc_free(ctx->desc); | |
369 | qemu_vfree(ctx); | |
370 | g_free(data); | |
371 | } | |
372 | ||
373 | static void plugin_reset_destroy(struct qemu_plugin_reset_data *data) | |
374 | { | |
375 | qemu_rec_mutex_lock(&plugin.lock); | |
376 | plugin_reset_destroy__locked(data); | |
377 | qemu_rec_mutex_lock(&plugin.lock); | |
378 | } | |
379 | ||
380 | static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg) | |
381 | { | |
382 | struct qemu_plugin_reset_data *data = arg.host_ptr; | |
383 | ||
384 | g_assert(cpu_in_exclusive_context(cpu)); | |
385 | tb_flush(cpu); | |
386 | plugin_reset_destroy(data); | |
387 | } | |
388 | ||
389 | void plugin_reset_uninstall(qemu_plugin_id_t id, | |
390 | qemu_plugin_simple_cb_t cb, | |
391 | bool reset) | |
392 | { | |
393 | struct qemu_plugin_reset_data *data; | |
394 | struct qemu_plugin_ctx *ctx; | |
395 | ||
ac90871c SH |
396 | WITH_QEMU_LOCK_GUARD(&plugin.lock) { |
397 | ctx = plugin_id_to_ctx_locked(id); | |
398 | if (ctx->uninstalling || (reset && ctx->resetting)) { | |
399 | return; | |
400 | } | |
401 | ctx->resetting = reset; | |
402 | ctx->uninstalling = !reset; | |
54cb65d8 | 403 | } |
54cb65d8 EC |
404 | |
405 | data = g_new(struct qemu_plugin_reset_data, 1); | |
406 | data->ctx = ctx; | |
407 | data->cb = cb; | |
408 | data->reset = reset; | |
409 | /* | |
410 | * Only flush the code cache if the vCPUs have been created. If so, | |
411 | * current_cpu must be non-NULL. | |
412 | */ | |
413 | if (current_cpu) { | |
414 | async_safe_run_on_cpu(current_cpu, plugin_flush_destroy, | |
415 | RUN_ON_CPU_HOST_PTR(data)); | |
416 | } else { | |
417 | /* | |
418 | * If current_cpu isn't set, then we don't have yet any vCPU threads | |
419 | * and we therefore can remove the callbacks synchronously. | |
420 | */ | |
421 | plugin_reset_destroy(data); | |
422 | } | |
423 | } |