]>
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 | |
8df5e27c | 11 | * qemu_plugin_register |
5c5d69b0 AB |
12 | * |
13 | * Which can then be passed back into the API to do additional things. | |
14 | * As such all the public functions in here are exported in | |
15 | * qemu-plugin.h. | |
16 | * | |
17 | * The general life-cycle of a plugin is: | |
18 | * | |
19 | * - plugin is loaded, public qemu_plugin_install called | |
20 | * - the install func registers callbacks for events | |
21 | * - usually an atexit_cb is registered to dump info at the end | |
22 | * - when a registered event occurs the plugin is called | |
23 | * - some events pass additional info | |
24 | * - during translation the plugin can decide to instrument any | |
25 | * instruction | |
26 | * - when QEMU exits all the registered atexit callbacks are called | |
27 | * | |
28 | * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> | |
29 | * Copyright (C) 2019, Linaro | |
30 | * | |
31 | * License: GNU GPL, version 2 or later. | |
32 | * See the COPYING file in the top-level directory. | |
33 | * | |
34 | * SPDX-License-Identifier: GPL-2.0-or-later | |
35 | * | |
36 | */ | |
37 | ||
38 | #include "qemu/osdep.h" | |
8df5e27c | 39 | #include "qemu/main-loop.h" |
5c5d69b0 | 40 | #include "qemu/plugin.h" |
cd617484 | 41 | #include "qemu/log.h" |
5c5d69b0 | 42 | #include "tcg/tcg.h" |
cbafa236 | 43 | #include "exec/exec-all.h" |
8df5e27c | 44 | #include "exec/gdbstub.h" |
787148bf | 45 | #include "exec/ram_addr.h" |
cbafa236 | 46 | #include "disas/disas.h" |
5c5d69b0 AB |
47 | #include "plugin.h" |
48 | #ifndef CONFIG_USER_ONLY | |
235537fa | 49 | #include "qemu/plugin-memory.h" |
5c5d69b0 | 50 | #include "hw/boards.h" |
91d40327 IA |
51 | #else |
52 | #include "qemu.h" | |
53 | #ifdef CONFIG_LINUX | |
54 | #include "loader.h" | |
55 | #endif | |
5c5d69b0 AB |
56 | #endif |
57 | ||
58 | /* Uninstall and Reset handlers */ | |
59 | ||
60 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
61 | { | |
62 | plugin_reset_uninstall(id, cb, false); | |
63 | } | |
64 | ||
65 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
66 | { | |
67 | plugin_reset_uninstall(id, cb, true); | |
68 | } | |
69 | ||
70 | /* | |
71 | * Plugin Register Functions | |
72 | * | |
73 | * This allows the plugin to register callbacks for various events | |
74 | * during the translation. | |
75 | */ | |
76 | ||
77 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
78 | qemu_plugin_vcpu_simple_cb_t cb) | |
79 | { | |
80 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
81 | } | |
82 | ||
83 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
84 | qemu_plugin_vcpu_simple_cb_t cb) | |
85 | { | |
86 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
87 | } | |
88 | ||
89 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, | |
90 | qemu_plugin_vcpu_udata_cb_t cb, | |
91 | enum qemu_plugin_cb_flags flags, | |
92 | void *udata) | |
93 | { | |
cfd405ea | 94 | if (!tb->mem_only) { |
33a277fe AO |
95 | int index = flags == QEMU_PLUGIN_CB_R_REGS || |
96 | flags == QEMU_PLUGIN_CB_RW_REGS ? | |
97 | PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR; | |
98 | ||
99 | plugin_register_dyn_cb__udata(&tb->cbs[index], | |
cfd405ea AB |
100 | cb, flags, udata); |
101 | } | |
5c5d69b0 AB |
102 | } |
103 | ||
104 | void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, | |
105 | enum qemu_plugin_op op, | |
106 | void *ptr, uint64_t imm) | |
107 | { | |
cfd405ea AB |
108 | if (!tb->mem_only) { |
109 | plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); | |
110 | } | |
5c5d69b0 AB |
111 | } |
112 | ||
113 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, | |
114 | qemu_plugin_vcpu_udata_cb_t cb, | |
115 | enum qemu_plugin_cb_flags flags, | |
116 | void *udata) | |
117 | { | |
cfd405ea | 118 | if (!insn->mem_only) { |
33a277fe AO |
119 | int index = flags == QEMU_PLUGIN_CB_R_REGS || |
120 | flags == QEMU_PLUGIN_CB_RW_REGS ? | |
121 | PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR; | |
122 | ||
123 | plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][index], | |
cfd405ea AB |
124 | cb, flags, udata); |
125 | } | |
5c5d69b0 AB |
126 | } |
127 | ||
128 | void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, | |
129 | enum qemu_plugin_op op, | |
130 | void *ptr, uint64_t imm) | |
131 | { | |
cfd405ea AB |
132 | if (!insn->mem_only) { |
133 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], | |
134 | 0, op, ptr, imm); | |
135 | } | |
5c5d69b0 AB |
136 | } |
137 | ||
138 | ||
cfd405ea AB |
139 | /* |
140 | * We always plant memory instrumentation because they don't finalise until | |
141 | * after the operation has complete. | |
142 | */ | |
5c5d69b0 AB |
143 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, |
144 | qemu_plugin_vcpu_mem_cb_t cb, | |
145 | enum qemu_plugin_cb_flags flags, | |
146 | enum qemu_plugin_mem_rw rw, | |
147 | void *udata) | |
148 | { | |
149 | plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], | |
cfd405ea | 150 | cb, flags, rw, udata); |
5c5d69b0 AB |
151 | } |
152 | ||
153 | void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, | |
154 | enum qemu_plugin_mem_rw rw, | |
155 | enum qemu_plugin_op op, void *ptr, | |
156 | uint64_t imm) | |
157 | { | |
158 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], | |
cfd405ea | 159 | rw, op, ptr, imm); |
5c5d69b0 AB |
160 | } |
161 | ||
162 | void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, | |
163 | qemu_plugin_vcpu_tb_trans_cb_t cb) | |
164 | { | |
165 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); | |
166 | } | |
167 | ||
168 | void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, | |
169 | qemu_plugin_vcpu_syscall_cb_t cb) | |
170 | { | |
171 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); | |
172 | } | |
173 | ||
174 | void | |
175 | qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, | |
176 | qemu_plugin_vcpu_syscall_ret_cb_t cb) | |
177 | { | |
178 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); | |
179 | } | |
180 | ||
181 | /* | |
182 | * Plugin Queries | |
183 | * | |
184 | * These are queries that the plugin can make to gauge information | |
185 | * from our opaque data types. We do not want to leak internal details | |
186 | * here just information useful to the plugin. | |
187 | */ | |
188 | ||
189 | /* | |
190 | * Translation block information: | |
191 | * | |
192 | * A plugin can query the virtual address of the start of the block | |
193 | * and the number of instructions in it. It can also get access to | |
194 | * each translated instruction. | |
195 | */ | |
196 | ||
197 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
198 | { | |
199 | return tb->n; | |
200 | } | |
201 | ||
202 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
203 | { | |
204 | return tb->vaddr; | |
205 | } | |
206 | ||
207 | struct qemu_plugin_insn * | |
208 | qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) | |
209 | { | |
cfd405ea | 210 | struct qemu_plugin_insn *insn; |
5c5d69b0 AB |
211 | if (unlikely(idx >= tb->n)) { |
212 | return NULL; | |
213 | } | |
cfd405ea AB |
214 | insn = g_ptr_array_index(tb->insns, idx); |
215 | insn->mem_only = tb->mem_only; | |
216 | return insn; | |
5c5d69b0 AB |
217 | } |
218 | ||
219 | /* | |
220 | * Instruction information | |
221 | * | |
222 | * These queries allow the plugin to retrieve information about each | |
223 | * instruction being translated. | |
224 | */ | |
225 | ||
226 | const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) | |
227 | { | |
228 | return insn->data->data; | |
229 | } | |
230 | ||
231 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
232 | { | |
233 | return insn->data->len; | |
234 | } | |
235 | ||
236 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
237 | { | |
238 | return insn->vaddr; | |
239 | } | |
240 | ||
241 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
242 | { | |
243 | return insn->haddr; | |
244 | } | |
245 | ||
cbafa236 AB |
246 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
247 | { | |
248 | CPUState *cpu = current_cpu; | |
249 | return plugin_disas(cpu, insn->vaddr, insn->data->len); | |
250 | } | |
251 | ||
7c4ab60f AB |
252 | const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) |
253 | { | |
254 | const char *sym = lookup_symbol(insn->vaddr); | |
255 | return sym[0] != 0 ? sym : NULL; | |
256 | } | |
257 | ||
5c5d69b0 AB |
258 | /* |
259 | * The memory queries allow the plugin to query information about a | |
260 | * memory access. | |
261 | */ | |
262 | ||
263 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
264 | { | |
37aff087 RH |
265 | MemOp op = get_memop(info); |
266 | return op & MO_SIZE; | |
5c5d69b0 AB |
267 | } |
268 | ||
269 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
270 | { | |
37aff087 RH |
271 | MemOp op = get_memop(info); |
272 | return op & MO_SIGN; | |
5c5d69b0 AB |
273 | } |
274 | ||
275 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
276 | { | |
37aff087 RH |
277 | MemOp op = get_memop(info); |
278 | return (op & MO_BSWAP) == MO_BE; | |
5c5d69b0 AB |
279 | } |
280 | ||
281 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
282 | { | |
37aff087 | 283 | return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; |
5c5d69b0 AB |
284 | } |
285 | ||
286 | /* | |
287 | * Virtual Memory queries | |
288 | */ | |
289 | ||
235537fa AB |
290 | #ifdef CONFIG_SOFTMMU |
291 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
a2b88169 | 292 | #endif |
235537fa AB |
293 | |
294 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, | |
295 | uint64_t vaddr) | |
296 | { | |
a2b88169 | 297 | #ifdef CONFIG_SOFTMMU |
235537fa | 298 | CPUState *cpu = current_cpu; |
37aff087 RH |
299 | unsigned int mmu_idx = get_mmuidx(info); |
300 | enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); | |
301 | hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; | |
235537fa | 302 | |
5413c37f RH |
303 | assert(mmu_idx < NB_MMU_MODES); |
304 | ||
235537fa | 305 | if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, |
37aff087 | 306 | hwaddr_info.is_store, &hwaddr_info)) { |
235537fa AB |
307 | error_report("invalid use of qemu_plugin_get_hwaddr"); |
308 | return NULL; | |
309 | } | |
310 | ||
311 | return &hwaddr_info; | |
235537fa | 312 | #else |
5c5d69b0 | 313 | return NULL; |
235537fa | 314 | #endif |
a2b88169 | 315 | } |
235537fa | 316 | |
308e7549 | 317 | bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
318 | { |
319 | #ifdef CONFIG_SOFTMMU | |
308e7549 | 320 | return haddr->is_io; |
235537fa AB |
321 | #else |
322 | return false; | |
323 | #endif | |
324 | } | |
325 | ||
787148bf | 326 | uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
327 | { |
328 | #ifdef CONFIG_SOFTMMU | |
329 | if (haddr) { | |
405c02d8 | 330 | return haddr->phys_addr; |
235537fa AB |
331 | } |
332 | #endif | |
333 | return 0; | |
334 | } | |
5c5d69b0 | 335 | |
b853a79f AB |
336 | const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) |
337 | { | |
338 | #ifdef CONFIG_SOFTMMU | |
339 | if (h && h->is_io) { | |
405c02d8 RH |
340 | MemoryRegion *mr = h->mr; |
341 | if (!mr->name) { | |
342 | unsigned maddr = (uintptr_t)mr; | |
343 | g_autofree char *temp = g_strdup_printf("anon%08x", maddr); | |
b853a79f AB |
344 | return g_intern_string(temp); |
345 | } else { | |
405c02d8 | 346 | return g_intern_string(mr->name); |
b853a79f AB |
347 | } |
348 | } else { | |
349 | return g_intern_static_string("RAM"); | |
350 | } | |
351 | #else | |
352 | return g_intern_static_string("Invalid"); | |
353 | #endif | |
354 | } | |
355 | ||
4a448b14 PB |
356 | int qemu_plugin_num_vcpus(void) |
357 | { | |
358 | return plugin_num_vcpus(); | |
359 | } | |
360 | ||
ca76a669 AB |
361 | /* |
362 | * Plugin output | |
363 | */ | |
364 | void qemu_plugin_outs(const char *string) | |
365 | { | |
366 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
367 | } | |
6a9e8a08 MM |
368 | |
369 | bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) | |
370 | { | |
371 | return name && value && qapi_bool_parse(name, value, ret, NULL); | |
372 | } | |
91d40327 IA |
373 | |
374 | /* | |
375 | * Binary path, start and end locations | |
376 | */ | |
377 | const char *qemu_plugin_path_to_binary(void) | |
378 | { | |
379 | char *path = NULL; | |
380 | #ifdef CONFIG_USER_ONLY | |
381 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
382 | path = g_strdup(ts->bprm->filename); | |
383 | #endif | |
384 | return path; | |
385 | } | |
386 | ||
387 | uint64_t qemu_plugin_start_code(void) | |
388 | { | |
389 | uint64_t start = 0; | |
390 | #ifdef CONFIG_USER_ONLY | |
391 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
392 | start = ts->info->start_code; | |
393 | #endif | |
394 | return start; | |
395 | } | |
396 | ||
397 | uint64_t qemu_plugin_end_code(void) | |
398 | { | |
399 | uint64_t end = 0; | |
400 | #ifdef CONFIG_USER_ONLY | |
401 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
402 | end = ts->info->end_code; | |
403 | #endif | |
404 | return end; | |
405 | } | |
406 | ||
407 | uint64_t qemu_plugin_entry_code(void) | |
408 | { | |
409 | uint64_t entry = 0; | |
410 | #ifdef CONFIG_USER_ONLY | |
411 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
412 | entry = ts->info->entry; | |
413 | #endif | |
414 | return entry; | |
415 | } | |
8df5e27c AB |
416 | |
417 | /* | |
418 | * Create register handles. | |
419 | * | |
420 | * We need to create a handle for each register so the plugin | |
421 | * infrastructure can call gdbstub to read a register. They are | |
422 | * currently just a pointer encapsulation of the gdb_reg but in | |
423 | * future may hold internal plugin state so its important plugin | |
424 | * authors are not tempted to treat them as numbers. | |
425 | * | |
426 | * We also construct a result array with those handles and some | |
427 | * ancillary data the plugin might find useful. | |
428 | */ | |
429 | ||
430 | static GArray *create_register_handles(GArray *gdbstub_regs) | |
431 | { | |
432 | GArray *find_data = g_array_new(true, true, | |
433 | sizeof(qemu_plugin_reg_descriptor)); | |
434 | ||
435 | for (int i = 0; i < gdbstub_regs->len; i++) { | |
436 | GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i); | |
437 | qemu_plugin_reg_descriptor desc; | |
438 | ||
439 | /* skip "un-named" regs */ | |
440 | if (!grd->name) { | |
441 | continue; | |
442 | } | |
443 | ||
444 | /* Create a record for the plugin */ | |
445 | desc.handle = GINT_TO_POINTER(grd->gdb_reg); | |
446 | desc.name = g_intern_string(grd->name); | |
447 | desc.feature = g_intern_string(grd->feature_name); | |
448 | g_array_append_val(find_data, desc); | |
449 | } | |
450 | ||
451 | return find_data; | |
452 | } | |
453 | ||
454 | GArray *qemu_plugin_get_registers(void) | |
455 | { | |
456 | g_assert(current_cpu); | |
457 | ||
458 | g_autoptr(GArray) regs = gdb_get_register_list(current_cpu); | |
459 | return create_register_handles(regs); | |
460 | } | |
461 | ||
462 | int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) | |
463 | { | |
464 | g_assert(current_cpu); | |
465 | ||
466 | return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg)); | |
467 | } |