master
c 105 lines 3.55 KB
Raw
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * Copyright (C) 2026, Florian Hofhammer <florian.hofhammer@epfl.ch>
5 */
6 #include <assert.h>
7 #include <glib.h>
8 #include <inttypes.h>
9 #include <unistd.h>
10
11 #include <qemu-plugin.h>
12
13 /* If we detect this magic syscall, ... */
14 #define MAGIC_SYSCALL 4096
15 /* ... the plugin either jumps directly to the target address ... */
16 #define SETPC 0
17 /* ... or just updates the target address for future use in callbacks. */
18 #define SETTARGET 1
19
20 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
21
22 static uint64_t source_pc;
23 static uint64_t target_pc;
24 static uint64_t target_vaddr;
25
26 static bool vcpu_syscall_filter(unsigned int vcpu_index,
27 int64_t num, uint64_t a1, uint64_t a2,
28 uint64_t a3, uint64_t a4, uint64_t a5,
29 uint64_t a6, uint64_t a7, uint64_t a8,
30 int64_t *sysret, void *userdata)
31 {
32 if (num == MAGIC_SYSCALL) {
33 if (a1 == SETPC) {
34 qemu_plugin_outs("Magic syscall detected, jump to clean exit\n");
35 qemu_plugin_set_pc(a2);
36 } else if (a1 == SETTARGET) {
37 qemu_plugin_outs("Magic syscall detected, set target_pc / "
38 "target_vaddr\n");
39 source_pc = a2;
40 target_pc = a3;
41 target_vaddr = a4;
42 *sysret = 0;
43 return true;
44 } else {
45 qemu_plugin_outs("Unknown magic syscall argument, ignoring\n");
46 }
47 }
48 return false;
49 }
50
51 static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata)
52 {
53 uint64_t vaddr = (uint64_t)userdata;
54 if (vaddr == source_pc) {
55 g_assert(target_pc != 0);
56 g_assert(target_vaddr == 0);
57
58 qemu_plugin_outs("Marker insn detected, jump to clean return\n");
59 qemu_plugin_set_pc(target_pc);
60 }
61 }
62
63 static void vcpu_mem_access(unsigned int vcpu_index,
64 qemu_plugin_meminfo_t info,
65 uint64_t vaddr, void *userdata)
66 {
67 if (vaddr != 0 && vaddr == target_vaddr) {
68 g_assert(source_pc == 0);
69 g_assert(target_pc != 0);
70
71 qemu_plugin_outs("Marker mem access detected, jump to clean return\n");
72 qemu_plugin_set_pc(target_pc);
73 }
74 }
75
76 static void vcpu_tb_trans(struct qemu_plugin_tb *tb, void *userdata)
77 {
78 size_t insns = qemu_plugin_tb_n_insns(tb);
79 for (size_t i = 0; i < insns; i++) {
80 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
81 uint64_t insn_vaddr = qemu_plugin_insn_vaddr(insn);
82 /*
83 * Note: we cannot only register the callbacks if the instruction is
84 * in one of the functions of interest, because symbol lookup for
85 * filtering does not work for all architectures (e.g., ppc64).
86 */
87 qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec,
88 QEMU_PLUGIN_CB_RW_REGS_PC,
89 (void *)insn_vaddr);
90 qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access,
91 QEMU_PLUGIN_CB_RW_REGS_PC,
92 QEMU_PLUGIN_MEM_R, NULL);
93 }
94 }
95
96
97 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
98 const qemu_info_t *info,
99 int argc, char **argv)
100 {
101
102 qemu_plugin_register_vcpu_syscall_filter_cb(id, vcpu_syscall_filter, NULL);
103 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans, NULL);
104 return 0;
105 }