aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/api-system.c131
-rw-r--r--plugins/api-user.c57
-rw-r--r--plugins/api.c223
-rw-r--r--plugins/core.c69
-rw-r--r--plugins/loader.c29
-rw-r--r--plugins/meson.build81
-rw-r--r--plugins/plugin.h7
-rw-r--r--plugins/qemu-plugins.symbols57
-rw-r--r--plugins/system.c24
-rw-r--r--plugins/user.c19
10 files changed, 406 insertions, 291 deletions
diff --git a/plugins/api-system.c b/plugins/api-system.c
new file mode 100644
index 0000000..cc190b1
--- /dev/null
+++ b/plugins/api-system.c
@@ -0,0 +1,131 @@
+/*
+ * QEMU Plugin API - System specific implementations
+ *
+ * This provides the APIs that have a specific system implementation
+ * or are only relevant to system-mode.
+ *
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ * Copyright (C) 2019-2025, Linaro
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "migration/blocker.h"
+#include "hw/boards.h"
+#include "qemu/plugin-memory.h"
+#include "qemu/plugin.h"
+
+/*
+ * In system mode we cannot trace the binary being executed so the
+ * helpers all return NULL/0.
+ */
+const char *qemu_plugin_path_to_binary(void)
+{
+ return NULL;
+}
+
+uint64_t qemu_plugin_start_code(void)
+{
+ return 0;
+}
+
+uint64_t qemu_plugin_end_code(void)
+{
+ return 0;
+}
+
+uint64_t qemu_plugin_entry_code(void)
+{
+ return 0;
+}
+
+/*
+ * Virtual Memory queries
+ */
+
+static __thread struct qemu_plugin_hwaddr hwaddr_info;
+
+struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
+ uint64_t vaddr)
+{
+ CPUState *cpu = current_cpu;
+ unsigned int mmu_idx = get_mmuidx(info);
+ enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
+ hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0;
+
+ assert(mmu_idx < NB_MMU_MODES);
+
+ if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx,
+ hwaddr_info.is_store, &hwaddr_info)) {
+ error_report("invalid use of qemu_plugin_get_hwaddr");
+ return NULL;
+ }
+
+ return &hwaddr_info;
+}
+
+bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr)
+{
+ return haddr->is_io;
+}
+
+uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr)
+{
+ if (haddr) {
+ return haddr->phys_addr;
+ }
+ return 0;
+}
+
+const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h)
+{
+ if (h && h->is_io) {
+ MemoryRegion *mr = h->mr;
+ if (!mr->name) {
+ unsigned maddr = (uintptr_t)mr;
+ g_autofree char *temp = g_strdup_printf("anon%08x", maddr);
+ return g_intern_string(temp);
+ } else {
+ return g_intern_string(mr->name);
+ }
+ } else {
+ return g_intern_static_string("RAM");
+ }
+}
+
+/*
+ * Time control
+ */
+static bool has_control;
+static Error *migration_blocker;
+
+const void *qemu_plugin_request_time_control(void)
+{
+ if (!has_control) {
+ has_control = true;
+ error_setg(&migration_blocker,
+ "TCG plugin time control does not support migration");
+ migrate_add_blocker(&migration_blocker, NULL);
+ return &has_control;
+ }
+ return NULL;
+}
+
+static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data)
+{
+ int64_t new_time = data.host_ulong;
+ qemu_clock_advance_virtual_time(new_time);
+}
+
+void qemu_plugin_update_ns(const void *handle, int64_t new_time)
+{
+ if (handle == &has_control) {
+ /* Need to execute out of cpu_exec, so bql can be locked. */
+ async_run_on_cpu(current_cpu,
+ advance_virtual_time__async,
+ RUN_ON_CPU_HOST_ULONG(new_time));
+ }
+}
diff --git a/plugins/api-user.c b/plugins/api-user.c
new file mode 100644
index 0000000..28704a8
--- /dev/null
+++ b/plugins/api-user.c
@@ -0,0 +1,57 @@
+/*
+ * QEMU Plugin API - user-mode only implementations
+ *
+ * This provides the APIs that have a user-mode specific
+ * implementations or are only relevant to user-mode.
+ *
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ * Copyright (C) 2019-2025, Linaro
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/plugin.h"
+#include "exec/log.h"
+
+/*
+ * Virtual Memory queries - these are all NOPs for user-mode which
+ * only ever has visibility of virtual addresses.
+ */
+
+struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
+ uint64_t vaddr)
+{
+ return NULL;
+}
+
+bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr)
+{
+ return false;
+}
+
+uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr)
+{
+ return 0;
+}
+
+const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h)
+{
+ return g_intern_static_string("Invalid");
+}
+
+/*
+ * Time control - for user mode the only real time is wall clock time
+ * so realistically all you can do in user mode is slow down execution
+ * which doesn't require the ability to mess with the clock.
+ */
+
+const void *qemu_plugin_request_time_control(void)
+{
+ return NULL;
+}
+
+void qemu_plugin_update_ns(const void *handle, int64_t new_time)
+{
+ qemu_log_mask(LOG_UNIMP, "user-mode can't control time");
+}
diff --git a/plugins/api.c b/plugins/api.c
index 2ff13d0..3c9d483 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -39,25 +39,13 @@
#include "qemu/main-loop.h"
#include "qemu/plugin.h"
#include "qemu/log.h"
-#include "qemu/timer.h"
#include "tcg/tcg.h"
-#include "exec/exec-all.h"
#include "exec/gdbstub.h"
+#include "exec/target_page.h"
+#include "exec/translation-block.h"
#include "exec/translator.h"
#include "disas/disas.h"
#include "plugin.h"
-#ifndef CONFIG_USER_ONLY
-#include "qapi/error.h"
-#include "migration/blocker.h"
-#include "exec/ram_addr.h"
-#include "qemu/plugin-memory.h"
-#include "hw/boards.h"
-#else
-#include "qemu.h"
-#ifdef CONFIG_LINUX
-#include "loader.h"
-#endif
-#endif
/* Uninstall and Reset handlers */
@@ -249,12 +237,10 @@ uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb)
struct qemu_plugin_insn *
qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx)
{
- struct qemu_plugin_insn *insn;
if (unlikely(idx >= tb->n)) {
return NULL;
}
- insn = g_ptr_array_index(tb->insns, idx);
- return insn;
+ return g_ptr_array_index(tb->insns, idx);
}
/*
@@ -286,7 +272,7 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn)
void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)
{
const DisasContextBase *db = tcg_ctx->plugin_db;
- vaddr page0_last = db->pc_first | ~TARGET_PAGE_MASK;
+ vaddr page0_last = db->pc_first | ~qemu_target_page_mask();
if (db->fake_insn) {
return NULL;
@@ -351,74 +337,37 @@ bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info)
return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W;
}
-/*
- * Virtual Memory queries
- */
-
-#ifdef CONFIG_SOFTMMU
-static __thread struct qemu_plugin_hwaddr hwaddr_info;
-#endif
-
-struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
- uint64_t vaddr)
-{
-#ifdef CONFIG_SOFTMMU
- CPUState *cpu = current_cpu;
- unsigned int mmu_idx = get_mmuidx(info);
- enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
- hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0;
-
- assert(mmu_idx < NB_MMU_MODES);
-
- if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx,
- hwaddr_info.is_store, &hwaddr_info)) {
- error_report("invalid use of qemu_plugin_get_hwaddr");
- return NULL;
+qemu_plugin_mem_value qemu_plugin_mem_get_value(qemu_plugin_meminfo_t info)
+{
+ uint64_t low = current_cpu->neg.plugin_mem_value_low;
+ qemu_plugin_mem_value value;
+
+ switch (qemu_plugin_mem_size_shift(info)) {
+ case 0:
+ value.type = QEMU_PLUGIN_MEM_VALUE_U8;
+ value.data.u8 = (uint8_t)low;
+ break;
+ case 1:
+ value.type = QEMU_PLUGIN_MEM_VALUE_U16;
+ value.data.u16 = (uint16_t)low;
+ break;
+ case 2:
+ value.type = QEMU_PLUGIN_MEM_VALUE_U32;
+ value.data.u32 = (uint32_t)low;
+ break;
+ case 3:
+ value.type = QEMU_PLUGIN_MEM_VALUE_U64;
+ value.data.u64 = low;
+ break;
+ case 4:
+ value.type = QEMU_PLUGIN_MEM_VALUE_U128;
+ value.data.u128.low = low;
+ value.data.u128.high = current_cpu->neg.plugin_mem_value_high;
+ break;
+ default:
+ g_assert_not_reached();
}
-
- return &hwaddr_info;
-#else
- return NULL;
-#endif
-}
-
-bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr)
-{
-#ifdef CONFIG_SOFTMMU
- return haddr->is_io;
-#else
- return false;
-#endif
-}
-
-uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr)
-{
-#ifdef CONFIG_SOFTMMU
- if (haddr) {
- return haddr->phys_addr;
- }
-#endif
- return 0;
-}
-
-const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h)
-{
-#ifdef CONFIG_SOFTMMU
- if (h && h->is_io) {
- MemoryRegion *mr = h->mr;
- if (!mr->name) {
- unsigned maddr = (uintptr_t)mr;
- g_autofree char *temp = g_strdup_printf("anon%08x", maddr);
- return g_intern_string(temp);
- } else {
- return g_intern_string(mr->name);
- }
- } else {
- return g_intern_static_string("RAM");
- }
-#else
- return g_intern_static_string("Invalid");
-#endif
+ return value;
}
int qemu_plugin_num_vcpus(void)
@@ -440,49 +389,6 @@ bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret)
}
/*
- * Binary path, start and end locations
- */
-const char *qemu_plugin_path_to_binary(void)
-{
- char *path = NULL;
-#ifdef CONFIG_USER_ONLY
- TaskState *ts = get_task_state(current_cpu);
- path = g_strdup(ts->bprm->filename);
-#endif
- return path;
-}
-
-uint64_t qemu_plugin_start_code(void)
-{
- uint64_t start = 0;
-#ifdef CONFIG_USER_ONLY
- TaskState *ts = get_task_state(current_cpu);
- start = ts->info->start_code;
-#endif
- return start;
-}
-
-uint64_t qemu_plugin_end_code(void)
-{
- uint64_t end = 0;
-#ifdef CONFIG_USER_ONLY
- TaskState *ts = get_task_state(current_cpu);
- end = ts->info->end_code;
-#endif
- return end;
-}
-
-uint64_t qemu_plugin_entry_code(void)
-{
- uint64_t entry = 0;
-#ifdef CONFIG_USER_ONLY
- TaskState *ts = get_task_state(current_cpu);
- entry = ts->info->entry;
-#endif
- return entry;
-}
-
-/*
* Create register handles.
*
* We need to create a handle for each register so the plugin
@@ -527,6 +433,26 @@ GArray *qemu_plugin_get_registers(void)
return create_register_handles(regs);
}
+bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len)
+{
+ g_assert(current_cpu);
+
+ if (len == 0) {
+ return false;
+ }
+
+ g_byte_array_set_size(data, len);
+
+ int result = cpu_memory_rw_debug(current_cpu, addr, data->data,
+ data->len, false);
+
+ if (result < 0) {
+ return false;
+ }
+
+ return true;
+}
+
int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
{
g_assert(current_cpu);
@@ -587,44 +513,3 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry)
return total;
}
-/*
- * Time control
- */
-static bool has_control;
-#ifdef CONFIG_SOFTMMU
-static Error *migration_blocker;
-#endif
-
-const void *qemu_plugin_request_time_control(void)
-{
- if (!has_control) {
- has_control = true;
-#ifdef CONFIG_SOFTMMU
- error_setg(&migration_blocker,
- "TCG plugin time control does not support migration");
- migrate_add_blocker(&migration_blocker, NULL);
-#endif
- return &has_control;
- }
- return NULL;
-}
-
-#ifdef CONFIG_SOFTMMU
-static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data)
-{
- int64_t new_time = data.host_ulong;
- qemu_clock_advance_virtual_time(new_time);
-}
-#endif
-
-void qemu_plugin_update_ns(const void *handle, int64_t new_time)
-{
-#ifdef CONFIG_SOFTMMU
- if (handle == &has_control) {
- /* Need to execute out of cpu_exec, so bql can be locked. */
- async_run_on_cpu(current_cpu,
- advance_virtual_time__async,
- RUN_ON_CPU_HOST_ULONG(new_time));
- }
-#endif
-}
diff --git a/plugins/core.c b/plugins/core.c
index 9d737d8..eb9281f 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -12,22 +12,14 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
-#include "qemu/error-report.h"
-#include "qemu/config-file.h"
-#include "qapi/error.h"
#include "qemu/lockable.h"
#include "qemu/option.h"
#include "qemu/plugin.h"
#include "qemu/queue.h"
#include "qemu/rcu_queue.h"
-#include "qemu/xxhash.h"
#include "qemu/rcu.h"
-#include "hw/core/cpu.h"
-
-#include "exec/exec-all.h"
#include "exec/tb-flush.h"
-#include "tcg/tcg.h"
-#include "tcg/tcg-op.h"
+#include "tcg/tcg-op-common.h"
#include "plugin.h"
struct qemu_plugin_cb {
@@ -214,37 +206,57 @@ CPUPluginState *qemu_plugin_create_vcpu_state(void)
static void plugin_grow_scoreboards__locked(CPUState *cpu)
{
- if (cpu->cpu_index < plugin.scoreboard_alloc_size) {
+ size_t scoreboard_size = plugin.scoreboard_alloc_size;
+ bool need_realloc = false;
+
+ if (cpu->cpu_index < scoreboard_size) {
return;
}
- bool need_realloc = FALSE;
- while (cpu->cpu_index >= plugin.scoreboard_alloc_size) {
- plugin.scoreboard_alloc_size *= 2;
- need_realloc = TRUE;
+ while (cpu->cpu_index >= scoreboard_size) {
+ scoreboard_size *= 2;
+ need_realloc = true;
}
+ if (!need_realloc) {
+ return;
+ }
- if (!need_realloc || QLIST_EMPTY(&plugin.scoreboards)) {
- /* nothing to do, we just updated sizes for future scoreboards */
+ if (QLIST_EMPTY(&plugin.scoreboards)) {
+ /* just update size for future scoreboards */
+ plugin.scoreboard_alloc_size = scoreboard_size;
return;
}
+ /*
+ * A scoreboard creation/deletion might be in progress. If a new vcpu is
+ * initialized at the same time, we are safe, as the new
+ * plugin.scoreboard_alloc_size was not yet written.
+ */
+ qemu_rec_mutex_unlock(&plugin.lock);
+
/* cpus must be stopped, as tb might still use an existing scoreboard. */
start_exclusive();
- struct qemu_plugin_scoreboard *score;
- QLIST_FOREACH(score, &plugin.scoreboards, entry) {
- g_array_set_size(score->data, plugin.scoreboard_alloc_size);
+ /* re-acquire lock */
+ qemu_rec_mutex_lock(&plugin.lock);
+ /* in case another vcpu is created between unlock and exclusive section. */
+ if (scoreboard_size > plugin.scoreboard_alloc_size) {
+ struct qemu_plugin_scoreboard *score;
+ QLIST_FOREACH(score, &plugin.scoreboards, entry) {
+ g_array_set_size(score->data, scoreboard_size);
+ }
+ plugin.scoreboard_alloc_size = scoreboard_size;
+ /* force all tb to be flushed, as scoreboard pointers were changed. */
+ tb_flush(cpu);
}
- /* force all tb to be flushed, as scoreboard pointers were changed. */
- tb_flush(cpu);
end_exclusive();
}
-void qemu_plugin_vcpu_init_hook(CPUState *cpu)
+static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused)
{
bool success;
+ assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
qemu_rec_mutex_lock(&plugin.lock);
plugin.num_vcpus = MAX(plugin.num_vcpus, cpu->cpu_index + 1);
plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL);
@@ -257,12 +269,19 @@ void qemu_plugin_vcpu_init_hook(CPUState *cpu)
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
}
+void qemu_plugin_vcpu_init_hook(CPUState *cpu)
+{
+ /* Plugin initialization must wait until the cpu start executing code */
+ async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
+}
+
void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
{
bool success;
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT);
+ assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
qemu_rec_mutex_lock(&plugin.lock);
success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index);
g_assert(success);
@@ -575,6 +594,8 @@ void exec_inline_op(enum plugin_dyn_cb_type type,
}
void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
+ uint64_t value_low,
+ uint64_t value_high,
MemOpIdx oi, enum qemu_plugin_mem_rw rw)
{
GArray *arr = cpu->neg.plugin_mem_cbs;
@@ -583,6 +604,10 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
if (arr == NULL) {
return;
}
+
+ cpu->neg.plugin_mem_value_low = value_low;
+ cpu->neg.plugin_mem_value_high = value_high;
+
for (i = 0; i < arr->len; i++) {
struct qemu_plugin_dyn_cb *cb =
&g_array_index(arr, struct qemu_plugin_dyn_cb, i);
diff --git a/plugins/loader.c b/plugins/loader.c
index 513a429..8f0d75c 100644
--- a/plugins/loader.c
+++ b/plugins/loader.c
@@ -18,6 +18,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/config-file.h"
+#include "qemu/help_option.h"
#include "qapi/error.h"
#include "qemu/lockable.h"
#include "qemu/option.h"
@@ -28,11 +29,8 @@
#include "qemu/xxhash.h"
#include "qemu/plugin.h"
#include "qemu/memalign.h"
-#include "hw/core/cpu.h"
+#include "qemu/target-info.h"
#include "exec/tb-flush.h"
-#ifndef CONFIG_USER_ONLY
-#include "hw/boards.h"
-#endif
#include "plugin.h"
@@ -98,7 +96,12 @@ static int plugin_add(void *opaque, const char *name, const char *value,
bool is_on;
char *fullarg;
- if (strcmp(name, "file") == 0) {
+ if (is_help_option(value)) {
+ printf("Plugin options\n");
+ printf(" file=<path/to/plugin.so>\n");
+ printf(" plugin specific arguments\n");
+ exit(0);
+ } else if (strcmp(name, "file") == 0) {
if (strcmp(value, "") == 0) {
error_setg(errp, "requires a non-empty argument");
return 1;
@@ -122,7 +125,7 @@ static int plugin_add(void *opaque, const char *name, const char *value,
/* Will treat arg="argname" as "argname=on" */
fullarg = g_strdup_printf("%s=%s", value, "on");
} else {
- fullarg = g_strdup_printf("%s", value);
+ fullarg = g_strdup(value);
}
warn_report("using 'arg=%s' is deprecated", value);
error_printf("Please use '%s' directly\n", fullarg);
@@ -291,17 +294,11 @@ int qemu_plugin_load_list(QemuPluginList *head, Error **errp)
struct qemu_plugin_desc *desc, *next;
g_autofree qemu_info_t *info = g_new0(qemu_info_t, 1);
- info->target_name = TARGET_NAME;
+ info->target_name = target_name();
info->version.min = QEMU_PLUGIN_MIN_VERSION;
info->version.cur = QEMU_PLUGIN_VERSION;
-#ifndef CONFIG_USER_ONLY
- MachineState *ms = MACHINE(qdev_get_machine());
- info->system_emulation = true;
- info->system.smp_vcpus = ms->smp.cpus;
- info->system.max_vcpus = ms->smp.max_cpus;
-#else
- info->system_emulation = false;
-#endif
+
+ qemu_plugin_fillin_mode_info(info);
QTAILQ_FOREACH_SAFE(desc, head, entry, next) {
int err;
@@ -373,7 +370,7 @@ static void plugin_reset_destroy(struct qemu_plugin_reset_data *data)
{
qemu_rec_mutex_lock(&plugin.lock);
plugin_reset_destroy__locked(data);
- qemu_rec_mutex_lock(&plugin.lock);
+ qemu_rec_mutex_unlock(&plugin.lock);
}
static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg)
diff --git a/plugins/meson.build b/plugins/meson.build
index 51b4350..62c991d 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -1,41 +1,68 @@
-plugin_ldflags = []
+if not get_option('plugins')
+ subdir_done()
+endif
+
+qemu_plugin_symbols = configure_file(
+ input: files('../include/qemu/qemu-plugin.h'),
+ output: 'qemu-plugin.symbols',
+ capture: true,
+ command: [files('../scripts/qemu-plugin-symbols.py'), '@INPUT@'])
+
# Modules need more symbols than just those in plugins/qemu-plugins.symbols
if not enable_modules
if host_os == 'darwin'
configure_file(
- input: files('qemu-plugins.symbols'),
+ input: qemu_plugin_symbols,
output: 'qemu-plugins-ld64.symbols',
capture: true,
command: ['sed', '-ne', 's/^[[:space:]]*\\(qemu_.*\\);/_\\1/p', '@INPUT@'])
- plugin_ldflags = ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols']
+ emulator_link_args += ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols']
+ elif host_os == 'windows' and meson.get_compiler('c').get_id() == 'clang'
+ # LLVM/lld does not support exporting specific symbols. However, it works
+ # out of the box with dllexport/dllimport attribute we set in the code.
else
- plugin_ldflags = ['-Xlinker', '--dynamic-list=' + (meson.project_source_root() / 'plugins/qemu-plugins.symbols')]
+ emulator_link_args += ['-Xlinker', '--dynamic-list=' + qemu_plugin_symbols.full_path()]
endif
endif
-if get_option('plugins')
- if host_os == 'windows'
- dlltool = find_program('dlltool', required: true)
+if host_os == 'windows'
+ # Generate a .lib file for plugins to link against.
+ # First, create a .def file listing all the symbols a plugin should expect to have
+ # available in qemu
+ win32_plugin_def = configure_file(
+ input: qemu_plugin_symbols,
+ output: 'qemu_plugin_api.def',
+ capture: true,
+ command: [python, '-c', 'import fileinput, re; print("EXPORTS", end=""); [print(re.sub(r"[{};]", "", line), end="") for line in fileinput.input()]', '@INPUT@'])
- # Generate a .lib file for plugins to link against.
- # First, create a .def file listing all the symbols a plugin should expect to have
- # available in qemu
- win32_plugin_def = configure_file(
- input: files('qemu-plugins.symbols'),
- output: 'qemu_plugin_api.def',
- capture: true,
- command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@'])
- # then use dlltool to assemble a delaylib.
- win32_qemu_plugin_api_lib = configure_file(
- input: win32_plugin_def,
- output: 'libqemu_plugin_api.a',
- command: [dlltool, '--input-def', '@INPUT@',
- '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe']
- )
+ # then use dlltool to assemble a delaylib.
+ # The delaylib will have an "imaginary" name (qemu.exe), that is used by the
+ # linker file we add with plugins (win32_linker.c) to identify that we want
+ # to find missing symbols in current program.
+ win32_qemu_plugin_api_link_flags = ['-Lplugins', '-lqemu_plugin_api']
+ if meson.get_compiler('c').get_id() == 'clang'
+ # With LLVM/lld, delaylib is specified at link time (-delayload)
+ dlltool = find_program('llvm-dlltool', required: true)
+ dlltool_cmd = [dlltool, '-d', '@INPUT@', '-l', '@OUTPUT@', '-D', 'qemu.exe']
+ win32_qemu_plugin_api_link_flags += ['-Wl,-delayload=qemu.exe']
+ else
+ # With gcc/ld, delay lib is built with a specific delay parameter.
+ dlltool = find_program('dlltool', required: true)
+ dlltool_cmd = [dlltool, '--input-def', '@INPUT@',
+ '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe']
endif
- specific_ss.add(files(
- 'loader.c',
- 'core.c',
- 'api.c',
- ), declare_dependency(link_args: plugin_ldflags))
+ win32_qemu_plugin_api_lib = configure_file(
+ input: win32_plugin_def,
+ output: 'libqemu_plugin_api.a',
+ command: dlltool_cmd
+ )
endif
+
+user_ss.add(files('user.c', 'api-user.c'))
+system_ss.add(files('system.c', 'api-system.c'))
+
+user_ss.add(files('api.c', 'core.c'))
+system_ss.add(files('api.c', 'core.c'))
+
+common_ss.add(files('loader.c'))
+
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 30e2299..6fbc443 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -13,6 +13,7 @@
#define PLUGIN_H
#include <gmodule.h>
+#include "qemu/queue.h"
#include "qemu/qht.h"
#define QEMU_PLUGIN_MIN_VERSION 2
@@ -118,4 +119,10 @@ struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size);
void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score);
+/**
+ * qemu_plugin_fillin_mode_info() - populate mode specific info
+ * info: pointer to qemu_info_t structure
+ */
+void qemu_plugin_fillin_mode_info(qemu_info_t *info);
+
#endif /* PLUGIN_H */
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
deleted file mode 100644
index ca773d8..0000000
--- a/plugins/qemu-plugins.symbols
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- qemu_plugin_bool_parse;
- qemu_plugin_end_code;
- qemu_plugin_entry_code;
- qemu_plugin_get_hwaddr;
- qemu_plugin_get_registers;
- qemu_plugin_hwaddr_device_name;
- qemu_plugin_hwaddr_is_io;
- qemu_plugin_hwaddr_phys_addr;
- qemu_plugin_insn_data;
- qemu_plugin_insn_disas;
- qemu_plugin_insn_haddr;
- qemu_plugin_insn_size;
- qemu_plugin_insn_symbol;
- qemu_plugin_insn_vaddr;
- qemu_plugin_mem_is_big_endian;
- qemu_plugin_mem_is_sign_extended;
- qemu_plugin_mem_is_store;
- qemu_plugin_mem_size_shift;
- qemu_plugin_num_vcpus;
- qemu_plugin_outs;
- qemu_plugin_path_to_binary;
- qemu_plugin_read_register;
- qemu_plugin_register_atexit_cb;
- qemu_plugin_register_flush_cb;
- qemu_plugin_register_vcpu_exit_cb;
- qemu_plugin_register_vcpu_idle_cb;
- qemu_plugin_register_vcpu_init_cb;
- qemu_plugin_register_vcpu_insn_exec_cb;
- qemu_plugin_register_vcpu_insn_exec_cond_cb;
- qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu;
- qemu_plugin_register_vcpu_mem_cb;
- qemu_plugin_register_vcpu_mem_inline_per_vcpu;
- qemu_plugin_register_vcpu_resume_cb;
- qemu_plugin_register_vcpu_syscall_cb;
- qemu_plugin_register_vcpu_syscall_ret_cb;
- qemu_plugin_register_vcpu_tb_exec_cb;
- qemu_plugin_register_vcpu_tb_exec_cond_cb;
- qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu;
- qemu_plugin_register_vcpu_tb_trans_cb;
- qemu_plugin_request_time_control;
- qemu_plugin_reset;
- qemu_plugin_scoreboard_free;
- qemu_plugin_scoreboard_find;
- qemu_plugin_scoreboard_new;
- qemu_plugin_start_code;
- qemu_plugin_tb_get_insn;
- qemu_plugin_tb_n_insns;
- qemu_plugin_tb_vaddr;
- qemu_plugin_u64_add;
- qemu_plugin_u64_get;
- qemu_plugin_u64_set;
- qemu_plugin_u64_sum;
- qemu_plugin_uninstall;
- qemu_plugin_update_ns;
- qemu_plugin_vcpu_for_each;
-};
diff --git a/plugins/system.c b/plugins/system.c
new file mode 100644
index 0000000..b3ecc33
--- /dev/null
+++ b/plugins/system.c
@@ -0,0 +1,24 @@
+/*
+ * QEMU Plugin system-emulation helpers
+ *
+ * Helpers that are specific to system emulation.
+ *
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ * Copyright (C) 2019-2025, Linaro
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/plugin.h"
+#include "hw/boards.h"
+
+#include "plugin.h"
+
+void qemu_plugin_fillin_mode_info(qemu_info_t *info)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ info->system_emulation = true;
+ info->system.smp_vcpus = ms->smp.cpus;
+ info->system.max_vcpus = ms->smp.max_cpus;
+}
diff --git a/plugins/user.c b/plugins/user.c
new file mode 100644
index 0000000..250d542
--- /dev/null
+++ b/plugins/user.c
@@ -0,0 +1,19 @@
+/*
+ * QEMU Plugin user-mode helpers
+ *
+ * Helpers that are specific to user-mode.
+ *
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ * Copyright (C) 2019-2025, Linaro
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/plugin.h"
+#include "plugin.h"
+
+void qemu_plugin_fillin_mode_info(qemu_info_t *info)
+{
+ info->system_emulation = false;
+}