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.c354
-rw-r--r--plugins/core.c92
-rw-r--r--plugins/loader.c21
-rw-r--r--plugins/meson.build78
-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, 553 insertions, 287 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..eac04cc 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -39,25 +39,14 @@
#include "qemu/main-loop.h"
#include "qemu/plugin.h"
#include "qemu/log.h"
-#include "qemu/timer.h"
+#include "system/memory.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 +238,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 +273,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 +338,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;
- }
-
- 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;
+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();
}
-#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 +390,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
@@ -531,9 +438,159 @@ int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
{
g_assert(current_cpu);
+ if (qemu_plugin_get_cb_flags() == QEMU_PLUGIN_CB_NO_REGS) {
+ return -1;
+ }
+
return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1);
}
+int qemu_plugin_write_register(struct qemu_plugin_register *reg,
+ GByteArray *buf)
+{
+ g_assert(current_cpu);
+
+ if (buf->len == 0 || qemu_plugin_get_cb_flags() != QEMU_PLUGIN_CB_RW_REGS) {
+ return -1;
+ }
+
+ return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1);
+}
+
+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;
+}
+
+bool qemu_plugin_write_memory_vaddr(uint64_t addr, GByteArray *data)
+{
+ g_assert(current_cpu);
+
+ if (data->len == 0) {
+ return false;
+ }
+
+ int result = cpu_memory_rw_debug(current_cpu, addr, data->data,
+ data->len, true);
+
+ if (result < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+enum qemu_plugin_hwaddr_operation_result
+qemu_plugin_read_memory_hwaddr(hwaddr addr, GByteArray *data, size_t len)
+{
+#ifdef CONFIG_SOFTMMU
+ if (len == 0) {
+ return QEMU_PLUGIN_HWADDR_OPERATION_ERROR;
+ }
+
+ g_assert(current_cpu);
+
+
+ int as_idx = cpu_asidx_from_attrs(current_cpu, MEMTXATTRS_UNSPECIFIED);
+ AddressSpace *as = cpu_get_address_space(current_cpu, as_idx);
+
+ if (as == NULL) {
+ return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE;
+ }
+
+ g_byte_array_set_size(data, len);
+ MemTxResult res = address_space_rw(as, addr,
+ MEMTXATTRS_UNSPECIFIED, data->data,
+ data->len, false);
+
+ switch (res) {
+ case MEMTX_OK:
+ return QEMU_PLUGIN_HWADDR_OPERATION_OK;
+ case MEMTX_ERROR:
+ return QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR;
+ case MEMTX_DECODE_ERROR:
+ return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS;
+ case MEMTX_ACCESS_ERROR:
+ return QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED;
+ default:
+ return QEMU_PLUGIN_HWADDR_OPERATION_ERROR;
+ }
+#else
+ return QEMU_PLUGIN_HWADDR_OPERATION_ERROR;
+#endif
+}
+
+enum qemu_plugin_hwaddr_operation_result
+qemu_plugin_write_memory_hwaddr(hwaddr addr, GByteArray *data)
+{
+#ifdef CONFIG_SOFTMMU
+ if (data->len == 0) {
+ return QEMU_PLUGIN_HWADDR_OPERATION_ERROR;
+ }
+
+ g_assert(current_cpu);
+
+ int as_idx = cpu_asidx_from_attrs(current_cpu, MEMTXATTRS_UNSPECIFIED);
+ AddressSpace *as = cpu_get_address_space(current_cpu, as_idx);
+
+ if (as == NULL) {
+ return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE;
+ }
+
+ MemTxResult res = address_space_rw(as, addr,
+ MEMTXATTRS_UNSPECIFIED, data->data,
+ data->len, true);
+ switch (res) {
+ case MEMTX_OK:
+ return QEMU_PLUGIN_HWADDR_OPERATION_OK;
+ case MEMTX_ERROR:
+ return QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR;
+ case MEMTX_DECODE_ERROR:
+ return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS;
+ case MEMTX_ACCESS_ERROR:
+ return QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED;
+ default:
+ return QEMU_PLUGIN_HWADDR_OPERATION_ERROR;
+ }
+#else
+ return QEMU_PLUGIN_HWADDR_OPERATION_ERROR;
+#endif
+}
+
+bool qemu_plugin_translate_vaddr(uint64_t vaddr, uint64_t *hwaddr)
+{
+#ifdef CONFIG_SOFTMMU
+ g_assert(current_cpu);
+
+ uint64_t res = cpu_get_phys_page_debug(current_cpu, vaddr);
+
+ if (res == (uint64_t)-1) {
+ return false;
+ }
+
+ *hwaddr = res | (vaddr & ~TARGET_PAGE_MASK);
+
+ return true;
+#else
+ return false;
+#endif
+}
+
struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size)
{
return plugin_scoreboard_new(element_size);
@@ -587,44 +644,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 12c67b4..c6e9ef1 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -12,22 +12,15 @@
* 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/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,30 +207,49 @@ 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();
}
@@ -255,7 +267,9 @@ static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused)
plugin_grow_scoreboards__locked(cpu);
qemu_rec_mutex_unlock(&plugin.lock);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
void qemu_plugin_vcpu_init_hook(CPUState *cpu)
@@ -268,7 +282,9 @@ void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
{
bool success;
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
qemu_rec_mutex_lock(&plugin.lock);
@@ -356,6 +372,7 @@ void plugin_register_dyn_cb__udata(GArray **arr,
static TCGHelperInfo info[3] = {
[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
+ [QEMU_PLUGIN_CB_RW_REGS].flags = 0,
/*
* Match qemu_plugin_vcpu_udata_cb_t:
* void (*)(uint32_t, void *)
@@ -385,6 +402,7 @@ void plugin_register_dyn_cond_cb__udata(GArray **arr,
static TCGHelperInfo info[3] = {
[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
+ [QEMU_PLUGIN_CB_RW_REGS].flags = 0,
/*
* Match qemu_plugin_vcpu_udata_cb_t:
* void (*)(uint32_t, void *)
@@ -423,6 +441,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
static TCGHelperInfo info[3] = {
[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
+ [QEMU_PLUGIN_CB_RW_REGS].flags = 0,
/*
* Match qemu_plugin_vcpu_mem_cb_t:
* void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *)
@@ -462,7 +481,9 @@ void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb)
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans;
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
func(cb->ctx->id, tb);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
}
@@ -487,7 +508,9 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall;
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
}
@@ -509,7 +532,9 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret;
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
func(cb->ctx->id, cpu->cpu_index, num, ret);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
}
@@ -517,14 +542,18 @@ void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
{
/* idle and resume cb may be called before init, ignore in this case */
if (cpu->cpu_index < plugin.num_vcpus) {
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
}
void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
{
if (cpu->cpu_index < plugin.num_vcpus) {
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
}
@@ -583,6 +612,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;
@@ -591,6 +622,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);
@@ -598,9 +633,13 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
switch (cb->type) {
case PLUGIN_CB_MEM_REGULAR:
if (rw & cb->regular.rw) {
+ qemu_plugin_set_cb_flags(cpu,
+ tcg_call_to_qemu_plugin_cb_flags(cb->regular.info->flags));
+
cb->regular.f.vcpu_mem(cpu->cpu_index,
make_plugin_meminfo(oi, rw),
vaddr, cb->regular.userp);
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
}
break;
case PLUGIN_CB_INLINE_ADD_U64:
@@ -743,3 +782,14 @@ void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score)
g_array_free(score->data, TRUE);
g_free(score);
}
+
+enum qemu_plugin_cb_flags tcg_call_to_qemu_plugin_cb_flags(int flags)
+{
+ if (flags & TCG_CALL_NO_RWG) {
+ return QEMU_PLUGIN_CB_NO_REGS;
+ } else if (flags & TCG_CALL_NO_WG) {
+ return QEMU_PLUGIN_CB_R_REGS;
+ } else {
+ return QEMU_PLUGIN_CB_RW_REGS;
+ }
+}
diff --git a/plugins/loader.c b/plugins/loader.c
index ebc01da..8f0d75c 100644
--- a/plugins/loader.c
+++ b/plugins/loader.c
@@ -29,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"
@@ -128,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);
@@ -297,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;
@@ -379,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 18a0303..62c991d 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -1,40 +1,68 @@
+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@'])
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
- emulator_link_args += ['-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',
- ))
+ 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;
+}