@@ -218,6 +218,15 @@ void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
qemu_plugin_udata_cb_t cb, void *userdata);
+typedef int64_t (*qemu_plugin_clock_func_t)(void);
+
+/*
+ * Can only be called from plugin_init.
+ * Returns true on success
+ */
+bool qemu_plugin_register_virtual_clock(qemu_plugin_id_t id,
+ qemu_plugin_clock_func_t clock);
+
/* returns -1 in user-mode */
int qemu_plugin_n_vcpus(void);
@@ -127,6 +127,8 @@ struct qemu_plugin_tb {
struct qemu_plugin_dyn_cb_arr cbs;
};
+extern bool use_plugin_clock;
+
static inline void qemu_plugin_insn_append(struct qemu_plugin_insn *insn,
const void *from, size_t size)
{
@@ -191,6 +193,7 @@ void qemu_plugin_flush_cb(void);
void qemu_plugin_atexit_cb(void);
void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr);
+int64_t plugin_get_clock(void);
#else /* !CONFIG_PLUGINS */
@@ -234,6 +237,8 @@ static inline
void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr)
{ }
+int64_t plugin_get_clock(void);
+
#endif /* !CONFIG_PLUGINS */
#endif /* QEMU_PLUGIN_H */
@@ -71,6 +71,13 @@ struct qemu_plugin_state {
* the code cache is flushed.
*/
struct qht dyn_cb_arr_ht;
+ /*
+ * We support a single clock reference from plugins. We keep a pointer
+ * to the context of the plugin that provides the reference,
+ * so that we can remove the reference when the plugin is uninstalled.
+ */
+ qemu_plugin_clock_func_t clock_ref;
+ struct qemu_plugin_ctx *clock_ctx;
};
/*
@@ -104,6 +111,8 @@ QemuOptsList qemu_plugin_opts = {
typedef int (*qemu_plugin_install_func_t)(qemu_plugin_id_t, int, char **);
static struct qemu_plugin_state plugin;
+bool use_plugin_clock;
+static bool plugin_installing;
static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
{
@@ -251,7 +260,9 @@ static int plugin_load(struct qemu_plugin_desc *desc)
QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry);
qemu_rec_mutex_unlock(&plugin.lock);
+ plugin_installing = true;
rc = install(ctx->id, desc->argc, desc->argv);
+ plugin_installing = false;
if (rc) {
error_report("%s: qemu_plugin_install returned error code %d",
__func__, rc);
@@ -418,6 +429,10 @@ void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_uninstall_cb_t cb)
for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
plugin_unregister_cb__locked(ctx, ev);
}
+ if (ctx == plugin.clock_ctx) {
+ atomic_set(&plugin.clock_ref, NULL);
+ plugin.clock_ctx = NULL;
+ }
qemu_rec_mutex_unlock(&plugin.lock);
/* XXX how to flush when we're not in a vCPU thread? */
@@ -964,6 +979,70 @@ uint64_t qemu_plugin_ram_addr_from_host(void *haddr)
#endif
}
+#ifndef CONFIG_USER_ONLY
+static bool
+qemu_plugin_register_virtual_clock__locked(qemu_plugin_id_t id,
+ qemu_plugin_clock_func_t clock)
+{
+ struct qemu_plugin_ctx *ctx = id_to_ctx__locked(id);
+
+ if (!plugin_installing) {
+ error_report("plugin: can only call %s during plugin installation",
+ __func__);
+ return false;
+ }
+ if (use_plugin_clock) {
+ error_report("plugin: clock reference already registered");
+ return false;
+ }
+ if (clock == NULL) {
+ error_report("%s: cannot pass NULL clock", __func__);
+ return false;
+ }
+ plugin.clock_ctx = ctx;
+ use_plugin_clock = true;
+ atomic_set(&plugin.clock_ref, clock);
+ return true;
+}
+#endif /* !CONFIG_USER_ONLY */
+
+bool qemu_plugin_register_virtual_clock(qemu_plugin_id_t id,
+ qemu_plugin_clock_func_t clock)
+{
+#ifdef CONFIG_USER_ONLY
+ return false;
+#else
+ bool ret;
+
+ qemu_rec_mutex_lock(&plugin.lock);
+ ret = qemu_plugin_register_virtual_clock__locked(id, clock);
+ qemu_rec_mutex_unlock(&plugin.lock);
+ return ret;
+#endif
+}
+
+#ifdef CONFIG_USER_ONLY
+int64_t plugin_get_clock(void)
+{
+ abort();
+ return 0;
+}
+#else
+/*
+ * Note: use_plugin_clock might be set, but the plugin providing the clock
+ * might have been uninstalled.
+ */
+int64_t plugin_get_clock(void)
+{
+ qemu_plugin_clock_func_t clock_ref = atomic_read(&plugin.clock_ref);
+
+ if (clock_ref) {
+ return clock_ref();
+ }
+ return cpu_get_clock();
+}
+#endif
+
static void __attribute__((__constructor__)) plugin_init(void)
{
int i;
new file mode 100644
@@ -0,0 +1,9 @@
+#include "qemu/osdep.h"
+#include "qemu/plugin.h"
+
+bool use_plugin_clock;
+
+int64_t plugin_get_clock(void)
+{
+ abort();
+}
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
+#include "qemu/plugin.h"
#include "qemu/timer.h"
#include "sysemu/replay.h"
#include "sysemu/sysemu.h"
@@ -601,6 +602,8 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
case QEMU_CLOCK_VIRTUAL_EXT:
if (use_icount) {
return cpu_get_icount();
+ } else if (use_plugin_clock) {
+ return plugin_get_clock();
} else {
return cpu_get_clock();
}
@@ -16,6 +16,7 @@
qemu_plugin_register_vcpu_syscall_cb;
qemu_plugin_register_vcpu_syscall_ret_cb;
qemu_plugin_register_atexit_cb;
+ qemu_plugin_register_virtual_clock;
qemu_plugin_tb_n_insns;
qemu_plugin_tb_get_insn;
qemu_plugin_tb_vaddr;
@@ -23,6 +23,7 @@ stub-obj-y += migr-blocker.o
stub-obj-y += change-state-handler.o
stub-obj-y += monitor.o
stub-obj-y += notify-event.o
+stub-obj-y += plugin.o
stub-obj-y += qtest.o
stub-obj-y += replay.o
stub-obj-y += runstate-check.o
Signed-off-by: Emilio G. Cota <cota@braap.org> --- include/qemu/plugin-api.h | 9 +++++ include/qemu/plugin.h | 5 +++ plugin.c | 79 +++++++++++++++++++++++++++++++++++++++ stubs/plugin.c | 9 +++++ util/qemu-timer.c | 3 ++ qemu-plugins.symbols | 1 + stubs/Makefile.objs | 1 + 7 files changed, 107 insertions(+) create mode 100644 stubs/plugin.c