]>
Commit | Line | Data |
---|---|---|
5c5d69b0 AB |
1 | /* |
2 | * QEMU Plugin API | |
3 | * | |
4 | * This provides the API that is available to the plugins to interact | |
5 | * with QEMU. We have to be careful not to expose internal details of | |
6 | * how QEMU works so we abstract out things like translation and | |
7 | * instructions to anonymous data types: | |
8 | * | |
9 | * qemu_plugin_tb | |
10 | * qemu_plugin_insn | |
11 | * | |
12 | * Which can then be passed back into the API to do additional things. | |
13 | * As such all the public functions in here are exported in | |
14 | * qemu-plugin.h. | |
15 | * | |
16 | * The general life-cycle of a plugin is: | |
17 | * | |
18 | * - plugin is loaded, public qemu_plugin_install called | |
19 | * - the install func registers callbacks for events | |
20 | * - usually an atexit_cb is registered to dump info at the end | |
21 | * - when a registered event occurs the plugin is called | |
22 | * - some events pass additional info | |
23 | * - during translation the plugin can decide to instrument any | |
24 | * instruction | |
25 | * - when QEMU exits all the registered atexit callbacks are called | |
26 | * | |
27 | * Copyright (C) 2017, Emilio G. Cota <[email protected]> | |
28 | * Copyright (C) 2019, Linaro | |
29 | * | |
30 | * License: GNU GPL, version 2 or later. | |
31 | * See the COPYING file in the top-level directory. | |
32 | * | |
33 | * SPDX-License-Identifier: GPL-2.0-or-later | |
34 | * | |
35 | */ | |
36 | ||
37 | #include "qemu/osdep.h" | |
38 | #include "qemu/plugin.h" | |
39 | #include "cpu.h" | |
40 | #include "sysemu/sysemu.h" | |
41 | #include "tcg/tcg.h" | |
cbafa236 AB |
42 | #include "exec/exec-all.h" |
43 | #include "disas/disas.h" | |
5c5d69b0 AB |
44 | #include "plugin.h" |
45 | #ifndef CONFIG_USER_ONLY | |
235537fa | 46 | #include "qemu/plugin-memory.h" |
5c5d69b0 AB |
47 | #include "hw/boards.h" |
48 | #endif | |
49 | ||
50 | /* Uninstall and Reset handlers */ | |
51 | ||
52 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
53 | { | |
54 | plugin_reset_uninstall(id, cb, false); | |
55 | } | |
56 | ||
57 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
58 | { | |
59 | plugin_reset_uninstall(id, cb, true); | |
60 | } | |
61 | ||
62 | /* | |
63 | * Plugin Register Functions | |
64 | * | |
65 | * This allows the plugin to register callbacks for various events | |
66 | * during the translation. | |
67 | */ | |
68 | ||
69 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
70 | qemu_plugin_vcpu_simple_cb_t cb) | |
71 | { | |
72 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
73 | } | |
74 | ||
75 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
76 | qemu_plugin_vcpu_simple_cb_t cb) | |
77 | { | |
78 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
79 | } | |
80 | ||
81 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, | |
82 | qemu_plugin_vcpu_udata_cb_t cb, | |
83 | enum qemu_plugin_cb_flags flags, | |
84 | void *udata) | |
85 | { | |
86 | plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], | |
87 | cb, flags, udata); | |
88 | } | |
89 | ||
90 | void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, | |
91 | enum qemu_plugin_op op, | |
92 | void *ptr, uint64_t imm) | |
93 | { | |
94 | plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); | |
95 | } | |
96 | ||
97 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, | |
98 | qemu_plugin_vcpu_udata_cb_t cb, | |
99 | enum qemu_plugin_cb_flags flags, | |
100 | void *udata) | |
101 | { | |
102 | plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], | |
103 | cb, flags, udata); | |
104 | } | |
105 | ||
106 | void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, | |
107 | enum qemu_plugin_op op, | |
108 | void *ptr, uint64_t imm) | |
109 | { | |
110 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], | |
111 | 0, op, ptr, imm); | |
112 | } | |
113 | ||
114 | ||
115 | ||
116 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, | |
117 | qemu_plugin_vcpu_mem_cb_t cb, | |
118 | enum qemu_plugin_cb_flags flags, | |
119 | enum qemu_plugin_mem_rw rw, | |
120 | void *udata) | |
121 | { | |
122 | plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], | |
123 | cb, flags, rw, udata); | |
124 | } | |
125 | ||
126 | void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, | |
127 | enum qemu_plugin_mem_rw rw, | |
128 | enum qemu_plugin_op op, void *ptr, | |
129 | uint64_t imm) | |
130 | { | |
131 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], | |
132 | rw, op, ptr, imm); | |
133 | } | |
134 | ||
135 | void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, | |
136 | qemu_plugin_vcpu_tb_trans_cb_t cb) | |
137 | { | |
138 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); | |
139 | } | |
140 | ||
141 | void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, | |
142 | qemu_plugin_vcpu_syscall_cb_t cb) | |
143 | { | |
144 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); | |
145 | } | |
146 | ||
147 | void | |
148 | qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, | |
149 | qemu_plugin_vcpu_syscall_ret_cb_t cb) | |
150 | { | |
151 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); | |
152 | } | |
153 | ||
154 | /* | |
155 | * Plugin Queries | |
156 | * | |
157 | * These are queries that the plugin can make to gauge information | |
158 | * from our opaque data types. We do not want to leak internal details | |
159 | * here just information useful to the plugin. | |
160 | */ | |
161 | ||
162 | /* | |
163 | * Translation block information: | |
164 | * | |
165 | * A plugin can query the virtual address of the start of the block | |
166 | * and the number of instructions in it. It can also get access to | |
167 | * each translated instruction. | |
168 | */ | |
169 | ||
170 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
171 | { | |
172 | return tb->n; | |
173 | } | |
174 | ||
175 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
176 | { | |
177 | return tb->vaddr; | |
178 | } | |
179 | ||
180 | struct qemu_plugin_insn * | |
181 | qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) | |
182 | { | |
183 | if (unlikely(idx >= tb->n)) { | |
184 | return NULL; | |
185 | } | |
186 | return g_ptr_array_index(tb->insns, idx); | |
187 | } | |
188 | ||
189 | /* | |
190 | * Instruction information | |
191 | * | |
192 | * These queries allow the plugin to retrieve information about each | |
193 | * instruction being translated. | |
194 | */ | |
195 | ||
196 | const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) | |
197 | { | |
198 | return insn->data->data; | |
199 | } | |
200 | ||
201 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
202 | { | |
203 | return insn->data->len; | |
204 | } | |
205 | ||
206 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
207 | { | |
208 | return insn->vaddr; | |
209 | } | |
210 | ||
211 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
212 | { | |
213 | return insn->haddr; | |
214 | } | |
215 | ||
cbafa236 AB |
216 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
217 | { | |
218 | CPUState *cpu = current_cpu; | |
219 | return plugin_disas(cpu, insn->vaddr, insn->data->len); | |
220 | } | |
221 | ||
5c5d69b0 AB |
222 | /* |
223 | * The memory queries allow the plugin to query information about a | |
224 | * memory access. | |
225 | */ | |
226 | ||
227 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
228 | { | |
229 | return info & TRACE_MEM_SZ_SHIFT_MASK; | |
230 | } | |
231 | ||
232 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
233 | { | |
234 | return !!(info & TRACE_MEM_SE); | |
235 | } | |
236 | ||
237 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
238 | { | |
239 | return !!(info & TRACE_MEM_BE); | |
240 | } | |
241 | ||
242 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
243 | { | |
244 | return !!(info & TRACE_MEM_ST); | |
245 | } | |
246 | ||
247 | /* | |
248 | * Virtual Memory queries | |
249 | */ | |
250 | ||
235537fa AB |
251 | #ifdef CONFIG_SOFTMMU |
252 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
253 | ||
254 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, | |
255 | uint64_t vaddr) | |
256 | { | |
257 | CPUState *cpu = current_cpu; | |
258 | unsigned int mmu_idx = info >> TRACE_MEM_MMU_SHIFT; | |
259 | hwaddr_info.is_store = info & TRACE_MEM_ST; | |
260 | ||
261 | if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, | |
262 | info & TRACE_MEM_ST, &hwaddr_info)) { | |
263 | error_report("invalid use of qemu_plugin_get_hwaddr"); | |
264 | return NULL; | |
265 | } | |
266 | ||
267 | return &hwaddr_info; | |
268 | } | |
269 | #else | |
5c5d69b0 AB |
270 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, |
271 | uint64_t vaddr) | |
272 | { | |
273 | return NULL; | |
274 | } | |
235537fa AB |
275 | #endif |
276 | ||
277 | bool qemu_plugin_hwaddr_is_io(struct qemu_plugin_hwaddr *hwaddr) | |
278 | { | |
279 | #ifdef CONFIG_SOFTMMU | |
280 | return hwaddr->is_io; | |
281 | #else | |
282 | return false; | |
283 | #endif | |
284 | } | |
285 | ||
286 | uint64_t qemu_plugin_hwaddr_device_offset(const struct qemu_plugin_hwaddr *haddr) | |
287 | { | |
288 | #ifdef CONFIG_SOFTMMU | |
289 | if (haddr) { | |
290 | if (!haddr->is_io) { | |
291 | ram_addr_t ram_addr = qemu_ram_addr_from_host((void *) haddr->v.ram.hostaddr); | |
292 | if (ram_addr == RAM_ADDR_INVALID) { | |
293 | error_report("Bad ram pointer %"PRIx64"", haddr->v.ram.hostaddr); | |
294 | abort(); | |
295 | } | |
296 | return ram_addr; | |
297 | } else { | |
298 | return haddr->v.io.offset; | |
299 | } | |
300 | } | |
301 | #endif | |
302 | return 0; | |
303 | } | |
5c5d69b0 AB |
304 | |
305 | /* | |
306 | * Queries to the number and potential maximum number of vCPUs there | |
307 | * will be. This helps the plugin dimension per-vcpu arrays. | |
308 | */ | |
309 | ||
310 | #ifndef CONFIG_USER_ONLY | |
311 | static MachineState * get_ms(void) | |
312 | { | |
313 | return MACHINE(qdev_get_machine()); | |
314 | } | |
315 | #endif | |
316 | ||
317 | int qemu_plugin_n_vcpus(void) | |
318 | { | |
319 | #ifdef CONFIG_USER_ONLY | |
320 | return -1; | |
321 | #else | |
322 | return get_ms()->smp.cpus; | |
323 | #endif | |
324 | } | |
325 | ||
326 | int qemu_plugin_n_max_vcpus(void) | |
327 | { | |
328 | #ifdef CONFIG_USER_ONLY | |
329 | return -1; | |
330 | #else | |
331 | return get_ms()->smp.max_cpus; | |
332 | #endif | |
333 | } | |
ca76a669 AB |
334 | |
335 | /* | |
336 | * Plugin output | |
337 | */ | |
338 | void qemu_plugin_outs(const char *string) | |
339 | { | |
340 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
341 | } |