mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-26 11:53:53 -06:00
plugins: Add enforcement of QEMU_PLUGIN_CB flags in register R/W callbacks
This patch adds functionality to enforce the requested QEMU_PLUGIN_CB_ flags level passed when registering a callback function using the plugins API. Each time a callback is about to be invoked, a thread-local variable will be updated with the level that callback requested. Then, called API functions (in particular, the register read and write API) will call qemu_plugin_get_cb_flags() to check the level is at least the level they require. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Rowan Hart <rowanbhart@gmail.com> Message-ID: <20250624175351.440780-4-rowanbhart@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-ID: <20250627112512.1880708-8-alex.bennee@linaro.org>
This commit is contained in:
parent
1a92b65859
commit
766e00bd57
6 changed files with 96 additions and 6 deletions
|
@ -117,10 +117,20 @@ static TCGv_i32 gen_cpu_index(void)
|
|||
static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
|
||||
{
|
||||
TCGv_i32 cpu_index = gen_cpu_index();
|
||||
enum qemu_plugin_cb_flags cb_flags =
|
||||
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
|
||||
TCGv_i32 flags = tcg_constant_i32(cb_flags);
|
||||
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
|
||||
tcg_gen_st_i32(flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
|
||||
tcgv_i32_temp(cpu_index),
|
||||
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
|
||||
tcg_gen_st_i32(clear_flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
tcg_temp_free_i32(flags);
|
||||
tcg_temp_free_i32(clear_flags);
|
||||
}
|
||||
|
||||
static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
|
||||
|
@ -173,10 +183,20 @@ static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
|
|||
tcg_gen_ld_i64(val, ptr, 0);
|
||||
tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
|
||||
TCGv_i32 cpu_index = gen_cpu_index();
|
||||
enum qemu_plugin_cb_flags cb_flags =
|
||||
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
|
||||
TCGv_i32 flags = tcg_constant_i32(cb_flags);
|
||||
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
|
||||
tcg_gen_st_i32(flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
|
||||
tcgv_i32_temp(cpu_index),
|
||||
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
|
||||
tcg_gen_st_i32(clear_flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
tcg_temp_free_i32(flags);
|
||||
tcg_temp_free_i32(clear_flags);
|
||||
gen_set_label(after_cb);
|
||||
|
||||
tcg_temp_free_i64(val);
|
||||
|
@ -210,12 +230,22 @@ static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
|
|||
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
|
||||
{
|
||||
TCGv_i32 cpu_index = gen_cpu_index();
|
||||
enum qemu_plugin_cb_flags cb_flags =
|
||||
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
|
||||
TCGv_i32 flags = tcg_constant_i32(cb_flags);
|
||||
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
|
||||
tcg_gen_st_i32(flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
|
||||
tcgv_i32_temp(cpu_index),
|
||||
tcgv_i32_temp(tcg_constant_i32(meminfo)),
|
||||
tcgv_i64_temp(addr),
|
||||
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
|
||||
tcg_gen_st_i32(clear_flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
tcg_temp_free_i32(flags);
|
||||
tcg_temp_free_i32(clear_flags);
|
||||
}
|
||||
|
||||
static void inject_cb(struct qemu_plugin_dyn_cb *cb)
|
||||
|
|
|
@ -368,6 +368,7 @@ typedef struct CPUNegativeOffsetState {
|
|||
GArray *plugin_mem_cbs;
|
||||
uint64_t plugin_mem_value_low;
|
||||
uint64_t plugin_mem_value_high;
|
||||
int32_t plugin_cb_flags;
|
||||
#endif
|
||||
IcountDecr icount_decr;
|
||||
bool can_do_io;
|
||||
|
|
|
@ -209,6 +209,21 @@ void qemu_plugin_user_prefork_lock(void);
|
|||
*/
|
||||
void qemu_plugin_user_postfork(bool is_child);
|
||||
|
||||
enum qemu_plugin_cb_flags tcg_call_to_qemu_plugin_cb_flags(int flags);
|
||||
|
||||
static inline void qemu_plugin_set_cb_flags(CPUState *cpu,
|
||||
enum qemu_plugin_cb_flags flags)
|
||||
{
|
||||
assert(cpu);
|
||||
cpu->neg.plugin_cb_flags = flags;
|
||||
}
|
||||
|
||||
static inline enum qemu_plugin_cb_flags qemu_plugin_get_cb_flags(void)
|
||||
{
|
||||
assert(current_cpu);
|
||||
return current_cpu->neg.plugin_cb_flags;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PLUGIN */
|
||||
|
||||
static inline void qemu_plugin_add_opts(void)
|
||||
|
|
|
@ -254,9 +254,6 @@ typedef struct {
|
|||
* @QEMU_PLUGIN_CB_NO_REGS: callback does not access the CPU's regs
|
||||
* @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs
|
||||
* @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs
|
||||
*
|
||||
* Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change
|
||||
* system register state.
|
||||
*/
|
||||
enum qemu_plugin_cb_flags {
|
||||
QEMU_PLUGIN_CB_NO_REGS,
|
||||
|
@ -901,7 +898,12 @@ GArray *qemu_plugin_get_registers(void);
|
|||
* @buf: A GByteArray for the data owned by the plugin
|
||||
*
|
||||
* This function is only available in a context that register read access is
|
||||
* explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag.
|
||||
* explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag, if called inside a
|
||||
* callback that can be registered with a qemu_plugin_cb_flags argument. This
|
||||
* function can also be used in any callback context that does not use a flags
|
||||
* argument, such as in a callback registered with
|
||||
* qemu_plugin_register_vcpu_init_cb(), except for callbacks registered with
|
||||
* qemu_plugin_register_atexit_cb() and qemu_plugin_register_flush_cb().
|
||||
*
|
||||
* Returns the size of the read register. The content of @buf is in target byte
|
||||
* order. On failure returns -1.
|
||||
|
@ -916,8 +918,13 @@ int qemu_plugin_read_register(struct qemu_plugin_register *handle,
|
|||
* @handle: a @qemu_plugin_reg_handle handle
|
||||
* @buf: A GByteArray for the data owned by the plugin
|
||||
*
|
||||
* This function is only available in a context that register write access is
|
||||
* explicitly requested via the QEMU_PLUGIN_CB_RW_REGS flag.
|
||||
* This function is only available in a context that register read access is
|
||||
* explicitly requested via the QEMU_PLUGIN_CB_RW_REGS flag, if called inside a
|
||||
* callback that can be registered with a qemu_plugin_cb_flags argument. This
|
||||
* function can also be used in any callback context that does not use a flags
|
||||
* argument, such as in a callback registered with
|
||||
* qemu_plugin_register_vcpu_init_cb(), except for callbacks registered with
|
||||
* qemu_plugin_register_atexit_cb() and qemu_plugin_register_flush_cb().
|
||||
*
|
||||
* The size of @buf must be at least the size of the requested register.
|
||||
* Attempting to write a register with @buf smaller than the register size
|
||||
|
|
|
@ -437,6 +437,10 @@ 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#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/rcu.h"
|
||||
|
@ -266,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)
|
||||
|
@ -279,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);
|
||||
|
@ -367,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 *)
|
||||
|
@ -396,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 *)
|
||||
|
@ -434,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 *)
|
||||
|
@ -473,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -528,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,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:
|
||||
|
@ -760,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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue