plugins: add flag to specify whether PC is rw
In addition to the flags specifying whether general-purpose registers are read-write (rw) during a plugin callback, we add an additional flag explicitly stating whether the PC is writable. This is in preparation of a patch that allows to explicitly set the PC to divert control flow from within a plugin callback, which is currently not possible. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Florian Hofhammer <florian.hofhammer@epfl.ch> Link: https://lore.kernel.org/qemu-devel/20260305-setpc-v5-v7-2-4c3adba52403@epfl.ch Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Florian Hofhammer committed
Mar 5, 2026 at 11:06 UTC
d16ffa424482a2ac39d60b775f60062b491eb761
3 files changed
+22
-14
include/plugins/qemu-plugin.h
+3
@@ -325,11 +325,14 @@ typedef struct {
325
* @QEMU_PLUGIN_CB_NO_REGS: callback does not access the CPU's regs
326
* @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs
327
* @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs
328
+ * @QEMU_PLUGIN_CB_RW_REGS_PC: callback reads and writes the CPU's
329
+ * regs and updates the PC
330
*/
331
enum qemu_plugin_cb_flags {
332
QEMU_PLUGIN_CB_NO_REGS,
333
QEMU_PLUGIN_CB_R_REGS,
334
QEMU_PLUGIN_CB_RW_REGS,
335
+ QEMU_PLUGIN_CB_RW_REGS_PC,
336
};
337
338
enum qemu_plugin_mem_rw {
plugins/api.c
+3
-1
@@ -458,7 +458,9 @@ bool qemu_plugin_write_register(struct qemu_plugin_register *reg,
458
{
459
g_assert(current_cpu);
460
461
- if (buf->len == 0 || qemu_plugin_get_cb_flags() != QEMU_PLUGIN_CB_RW_REGS) {
461
+ if (buf->len == 0 ||
462
+ (qemu_plugin_get_cb_flags() != QEMU_PLUGIN_CB_RW_REGS &&
463
+ qemu_plugin_get_cb_flags() != QEMU_PLUGIN_CB_RW_REGS_PC)) {
464
return false;
465
}
466
plugins/core.c
+16
-13
@@ -119,7 +119,7 @@ static void plugin_vcpu_cb__discon(CPUState *cpu,
119
struct qemu_plugin_cb *cb, *next;
120
uint64_t to = cpu->cc->get_pc(cpu);
121
122
- qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
122
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS_PC);
123
if (cpu->cpu_index < plugin.num_vcpus) {
124
/* iterate safely; plugins might uninstall themselves at any time */
125
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
@@ -395,15 +395,16 @@ void plugin_register_dyn_cb__udata(GArray **arr,
395
enum qemu_plugin_cb_flags flags,
396
void *udata)
397
{
398
- static TCGHelperInfo info[3] = {
398
+ static TCGHelperInfo info[4] = {
399
[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
400
[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
401
[QEMU_PLUGIN_CB_RW_REGS].flags = 0,
402
+ [QEMU_PLUGIN_CB_RW_REGS_PC].flags = 0,
403
/*
404
* Match qemu_plugin_vcpu_udata_cb_t:
405
* void (*)(uint32_t, void *)
406
*/
406
- [0 ... 2].typemask = (dh_typemask(void, 0) |
407
+ [0 ... 3].typemask = (dh_typemask(void, 0) |
408
dh_typemask(i32, 1) |
409
dh_typemask(ptr, 2))
410
};
@@ -425,15 +426,16 @@ void plugin_register_dyn_cond_cb__udata(GArray **arr,
426
uint64_t imm,
427
void *udata)
428
{
428
- static TCGHelperInfo info[3] = {
429
+ static TCGHelperInfo info[4] = {
430
[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
431
[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
432
[QEMU_PLUGIN_CB_RW_REGS].flags = 0,
433
+ [QEMU_PLUGIN_CB_RW_REGS_PC].flags = 0,
434
/*
435
* Match qemu_plugin_vcpu_udata_cb_t:
436
* void (*)(uint32_t, void *)
437
*/
436
- [0 ... 2].typemask = (dh_typemask(void, 0) |
438
+ [0 ... 3].typemask = (dh_typemask(void, 0) |
439
dh_typemask(i32, 1) |
440
dh_typemask(ptr, 2))
441
};
@@ -464,15 +466,16 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
466
!__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) &&
467
!__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t));
468
467
- static TCGHelperInfo info[3] = {
469
+ static TCGHelperInfo info[4] = {
470
[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
471
[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
472
[QEMU_PLUGIN_CB_RW_REGS].flags = 0,
473
+ [QEMU_PLUGIN_CB_RW_REGS_PC].flags = 0,
474
/*
475
* Match qemu_plugin_vcpu_mem_cb_t:
476
* void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *)
477
*/
475
- [0 ... 2].typemask =
478
+ [0 ... 3].typemask =
479
(dh_typemask(void, 0) |
480
dh_typemask(i32, 1) |
481
(__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t)
@@ -553,7 +556,7 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
556
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
557
qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall;
558
556
- qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
559
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS_PC);
560
func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8);
561
qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
562
}
@@ -577,7 +580,7 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
580
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
581
qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret;
582
580
- qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
583
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS_PC);
584
func(cb->ctx->id, cpu->cpu_index, num, ret);
585
qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
586
}
@@ -605,7 +608,7 @@ qemu_plugin_vcpu_syscall_filter(CPUState *cpu, int64_t num, uint64_t a1,
608
609
clamp_syscall_arguments(&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8);
610
608
- qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
611
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS_PC);
612
613
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
614
qemu_plugin_vcpu_syscall_filter_cb_t func = cb->f.vcpu_syscall_filter;
@@ -626,7 +629,7 @@ void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
629
{
630
/* idle and resume cb may be called before init, ignore in this case */
631
if (cpu->cpu_index < plugin.num_vcpus) {
629
- qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
632
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS_PC);
633
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
634
qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
635
}
@@ -635,7 +638,7 @@ void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
638
void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
639
{
640
if (cpu->cpu_index < plugin.num_vcpus) {
638
- qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
641
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS_PC);
642
plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
643
qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
644
}
@@ -906,6 +909,6 @@ enum qemu_plugin_cb_flags tcg_call_to_qemu_plugin_cb_flags(int flags)
909
} else if (flags & TCG_CALL_NO_WG) {
910
return QEMU_PLUGIN_CB_R_REGS;
911
} else {
909
- return QEMU_PLUGIN_CB_RW_REGS;
912
+ return QEMU_PLUGIN_CB_RW_REGS_PC;
913
}
914
}