Pavel Dovgalyuk <pavel.dovga...@ispras.ru> writes:
> This patch adds support for dynamically loaded plugins. > Every plugin is a dynamic library with a set of optional exported > functions that will be called from QEMU. > <snip> > + > +static QLIST_HEAD(, QemuPluginInfo) qemu_plugins > + = QLIST_HEAD_INITIALIZER(qemu_plugins); > + > +static QemuOptsList qemu_plugin_opts = { > + .name = "plugin", > + .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head), > + .desc = { > + { > + .name = "file", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "args", > + .type = QEMU_OPT_STRING, > + }, > + { /* end of list */ } > + }, > +}; > + > +void qemu_plugin_parse_cmd_args(const char *optarg) > +{ > + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_plugin_opts, optarg, > false); > + qemu_plugin_load(qemu_opt_get(opts, "file"), > + qemu_opt_get(opts, "args")); > +} Currently this is only available to system mode emulation. Can it be extended to include linux-user as well? > + > +void qemu_plugin_load(const char *filename, const char *args) > +{ > + GModule *g_module; > + QemuPluginInfo *info = NULL; > + if (!filename) { > + error_report("plugin name was not specified"); > + return; > + } > + g_module = g_module_open(filename, > + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); > + if (!g_module) { > + error_report("can't load plugin '%s'", filename); > + return; > + } > + info = g_new0(QemuPluginInfo, 1); > + info->filename = g_strdup(filename); > + info->g_module = g_module; > + if (args) { > + info->args = g_strdup(args); > + } > + > + g_module_symbol(g_module, "plugin_init", (gpointer*)&info->init); > + > + /* Get the instrumentation callbacks */ > + g_module_symbol(g_module, "plugin_needs_before_insn", > + (gpointer*)&info->needs_before_insn); > + g_module_symbol(g_module, "plugin_before_insn", > + (gpointer*)&info->before_insn); > + > + QLIST_INSERT_HEAD(&qemu_plugins, info, next); > + > + return; > +} > + > +void qemu_plugins_init(void) > +{ > + QemuPluginInfo *info; > + QLIST_FOREACH(info, &qemu_plugins, next) { > + if (info->init) { > + info->init(info->args); > + } > + } > +} > diff --git a/qemu-options.hx b/qemu-options.hx > index c0d3951..d171544 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -3950,6 +3950,16 @@ Dump json-encoded vmstate information for current > machine type to file > in @var{file} > ETEXI > > +#ifdef CONFIG_PLUGINS > +DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, \ > + "-plugin file=<file>[,args=<args>] load <dso> plugin with > <args>\n", QEMU_ARCH_ALL) > +STEXI > +@item -plugin file=@var{file}[,args=@var{args}] > +@findex -plugin > +Load @var{file} plugin passing @var{args} arguments. > +ETEXI > +#endif > + > STEXI > @end table > ETEXI > diff --git a/vl.c b/vl.c > index 0603171..05420bf 100644 > --- a/vl.c > +++ b/vl.c > @@ -129,6 +129,7 @@ int main(int argc, char **argv) > #include "qapi/qapi-commands-run-state.h" > #include "qapi/qmp/qerror.h" > #include "sysemu/iothread.h" > +#include "qemu/plugins.h" > > #define MAX_VIRTIO_CONSOLES 1 > > @@ -3925,6 +3926,11 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > break; > +#ifdef CONFIG_PLUGINS > + case QEMU_OPTION_plugin: > + qemu_plugin_parse_cmd_args(optarg); > + break; > +#endif > case QEMU_OPTION_nodefconfig: > case QEMU_OPTION_nouserconfig: > /* Nothing to be parsed here. Especially, do not error out > below. */ > @@ -4470,6 +4476,8 @@ int main(int argc, char **argv, char **envp) > } > parse_numa_opts(current_machine); > > + qemu_plugins_init(); > + > /* do monitor/qmp handling at preconfig state if requested */ > main_loop(); > -- Alex Bennée