]>
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" | |
cd617484 | 39 | #include "qemu/log.h" |
5c5d69b0 | 40 | #include "tcg/tcg.h" |
cbafa236 | 41 | #include "exec/exec-all.h" |
787148bf | 42 | #include "exec/ram_addr.h" |
cbafa236 | 43 | #include "disas/disas.h" |
5c5d69b0 AB |
44 | #include "plugin.h" |
45 | #ifndef CONFIG_USER_ONLY | |
235537fa | 46 | #include "qemu/plugin-memory.h" |
5c5d69b0 | 47 | #include "hw/boards.h" |
91d40327 IA |
48 | #else |
49 | #include "qemu.h" | |
50 | #ifdef CONFIG_LINUX | |
51 | #include "loader.h" | |
52 | #endif | |
5c5d69b0 AB |
53 | #endif |
54 | ||
55 | /* Uninstall and Reset handlers */ | |
56 | ||
57 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
58 | { | |
59 | plugin_reset_uninstall(id, cb, false); | |
60 | } | |
61 | ||
62 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
63 | { | |
64 | plugin_reset_uninstall(id, cb, true); | |
65 | } | |
66 | ||
67 | /* | |
68 | * Plugin Register Functions | |
69 | * | |
70 | * This allows the plugin to register callbacks for various events | |
71 | * during the translation. | |
72 | */ | |
73 | ||
74 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
75 | qemu_plugin_vcpu_simple_cb_t cb) | |
76 | { | |
77 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
78 | } | |
79 | ||
80 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
81 | qemu_plugin_vcpu_simple_cb_t cb) | |
82 | { | |
83 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
84 | } | |
85 | ||
86 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, | |
87 | qemu_plugin_vcpu_udata_cb_t cb, | |
88 | enum qemu_plugin_cb_flags flags, | |
89 | void *udata) | |
90 | { | |
cfd405ea AB |
91 | if (!tb->mem_only) { |
92 | plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], | |
93 | cb, flags, udata); | |
94 | } | |
5c5d69b0 AB |
95 | } |
96 | ||
97 | void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, | |
98 | enum qemu_plugin_op op, | |
99 | void *ptr, uint64_t imm) | |
100 | { | |
cfd405ea AB |
101 | if (!tb->mem_only) { |
102 | plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); | |
103 | } | |
5c5d69b0 AB |
104 | } |
105 | ||
106 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, | |
107 | qemu_plugin_vcpu_udata_cb_t cb, | |
108 | enum qemu_plugin_cb_flags flags, | |
109 | void *udata) | |
110 | { | |
cfd405ea AB |
111 | if (!insn->mem_only) { |
112 | plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], | |
113 | cb, flags, udata); | |
114 | } | |
5c5d69b0 AB |
115 | } |
116 | ||
117 | void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, | |
118 | enum qemu_plugin_op op, | |
119 | void *ptr, uint64_t imm) | |
120 | { | |
cfd405ea AB |
121 | if (!insn->mem_only) { |
122 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], | |
123 | 0, op, ptr, imm); | |
124 | } | |
5c5d69b0 AB |
125 | } |
126 | ||
127 | ||
cfd405ea AB |
128 | /* |
129 | * We always plant memory instrumentation because they don't finalise until | |
130 | * after the operation has complete. | |
131 | */ | |
5c5d69b0 AB |
132 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, |
133 | qemu_plugin_vcpu_mem_cb_t cb, | |
134 | enum qemu_plugin_cb_flags flags, | |
135 | enum qemu_plugin_mem_rw rw, | |
136 | void *udata) | |
137 | { | |
138 | plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], | |
cfd405ea | 139 | cb, flags, rw, udata); |
5c5d69b0 AB |
140 | } |
141 | ||
142 | void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, | |
143 | enum qemu_plugin_mem_rw rw, | |
144 | enum qemu_plugin_op op, void *ptr, | |
145 | uint64_t imm) | |
146 | { | |
147 | plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], | |
cfd405ea | 148 | rw, op, ptr, imm); |
5c5d69b0 AB |
149 | } |
150 | ||
151 | void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, | |
152 | qemu_plugin_vcpu_tb_trans_cb_t cb) | |
153 | { | |
154 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); | |
155 | } | |
156 | ||
157 | void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, | |
158 | qemu_plugin_vcpu_syscall_cb_t cb) | |
159 | { | |
160 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); | |
161 | } | |
162 | ||
163 | void | |
164 | qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, | |
165 | qemu_plugin_vcpu_syscall_ret_cb_t cb) | |
166 | { | |
167 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); | |
168 | } | |
169 | ||
170 | /* | |
171 | * Plugin Queries | |
172 | * | |
173 | * These are queries that the plugin can make to gauge information | |
174 | * from our opaque data types. We do not want to leak internal details | |
175 | * here just information useful to the plugin. | |
176 | */ | |
177 | ||
178 | /* | |
179 | * Translation block information: | |
180 | * | |
181 | * A plugin can query the virtual address of the start of the block | |
182 | * and the number of instructions in it. It can also get access to | |
183 | * each translated instruction. | |
184 | */ | |
185 | ||
186 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
187 | { | |
188 | return tb->n; | |
189 | } | |
190 | ||
191 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
192 | { | |
193 | return tb->vaddr; | |
194 | } | |
195 | ||
196 | struct qemu_plugin_insn * | |
197 | qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) | |
198 | { | |
cfd405ea | 199 | struct qemu_plugin_insn *insn; |
5c5d69b0 AB |
200 | if (unlikely(idx >= tb->n)) { |
201 | return NULL; | |
202 | } | |
cfd405ea AB |
203 | insn = g_ptr_array_index(tb->insns, idx); |
204 | insn->mem_only = tb->mem_only; | |
205 | return insn; | |
5c5d69b0 AB |
206 | } |
207 | ||
208 | /* | |
209 | * Instruction information | |
210 | * | |
211 | * These queries allow the plugin to retrieve information about each | |
212 | * instruction being translated. | |
213 | */ | |
214 | ||
215 | const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) | |
216 | { | |
217 | return insn->data->data; | |
218 | } | |
219 | ||
220 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
221 | { | |
222 | return insn->data->len; | |
223 | } | |
224 | ||
225 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
226 | { | |
227 | return insn->vaddr; | |
228 | } | |
229 | ||
230 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
231 | { | |
232 | return insn->haddr; | |
233 | } | |
234 | ||
cbafa236 AB |
235 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
236 | { | |
237 | CPUState *cpu = current_cpu; | |
238 | return plugin_disas(cpu, insn->vaddr, insn->data->len); | |
239 | } | |
240 | ||
7c4ab60f AB |
241 | const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) |
242 | { | |
243 | const char *sym = lookup_symbol(insn->vaddr); | |
244 | return sym[0] != 0 ? sym : NULL; | |
245 | } | |
246 | ||
5c5d69b0 AB |
247 | /* |
248 | * The memory queries allow the plugin to query information about a | |
249 | * memory access. | |
250 | */ | |
251 | ||
252 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
253 | { | |
37aff087 RH |
254 | MemOp op = get_memop(info); |
255 | return op & MO_SIZE; | |
5c5d69b0 AB |
256 | } |
257 | ||
258 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
259 | { | |
37aff087 RH |
260 | MemOp op = get_memop(info); |
261 | return op & MO_SIGN; | |
5c5d69b0 AB |
262 | } |
263 | ||
264 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
265 | { | |
37aff087 RH |
266 | MemOp op = get_memop(info); |
267 | return (op & MO_BSWAP) == MO_BE; | |
5c5d69b0 AB |
268 | } |
269 | ||
270 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
271 | { | |
37aff087 | 272 | return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; |
5c5d69b0 AB |
273 | } |
274 | ||
275 | /* | |
276 | * Virtual Memory queries | |
277 | */ | |
278 | ||
235537fa AB |
279 | #ifdef CONFIG_SOFTMMU |
280 | static __thread struct qemu_plugin_hwaddr hwaddr_info; | |
a2b88169 | 281 | #endif |
235537fa AB |
282 | |
283 | struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, | |
284 | uint64_t vaddr) | |
285 | { | |
a2b88169 | 286 | #ifdef CONFIG_SOFTMMU |
235537fa | 287 | CPUState *cpu = current_cpu; |
37aff087 RH |
288 | unsigned int mmu_idx = get_mmuidx(info); |
289 | enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); | |
290 | hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; | |
235537fa | 291 | |
5413c37f RH |
292 | assert(mmu_idx < NB_MMU_MODES); |
293 | ||
235537fa | 294 | if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, |
37aff087 | 295 | hwaddr_info.is_store, &hwaddr_info)) { |
235537fa AB |
296 | error_report("invalid use of qemu_plugin_get_hwaddr"); |
297 | return NULL; | |
298 | } | |
299 | ||
300 | return &hwaddr_info; | |
235537fa | 301 | #else |
5c5d69b0 | 302 | return NULL; |
235537fa | 303 | #endif |
a2b88169 | 304 | } |
235537fa | 305 | |
308e7549 | 306 | bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
307 | { |
308 | #ifdef CONFIG_SOFTMMU | |
308e7549 | 309 | return haddr->is_io; |
235537fa AB |
310 | #else |
311 | return false; | |
312 | #endif | |
313 | } | |
314 | ||
787148bf | 315 | uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) |
235537fa AB |
316 | { |
317 | #ifdef CONFIG_SOFTMMU | |
318 | if (haddr) { | |
319 | if (!haddr->is_io) { | |
787148bf AL |
320 | RAMBlock *block; |
321 | ram_addr_t offset; | |
2d932039 | 322 | void *hostaddr = haddr->v.ram.hostaddr; |
787148bf AL |
323 | |
324 | block = qemu_ram_block_from_host(hostaddr, false, &offset); | |
325 | if (!block) { | |
2d932039 | 326 | error_report("Bad host ram pointer %p", haddr->v.ram.hostaddr); |
235537fa AB |
327 | abort(); |
328 | } | |
787148bf AL |
329 | |
330 | return block->offset + offset + block->mr->addr; | |
235537fa | 331 | } else { |
787148bf | 332 | MemoryRegionSection *mrs = haddr->v.io.section; |
2da42253 | 333 | return mrs->offset_within_address_space + haddr->v.io.offset; |
235537fa AB |
334 | } |
335 | } | |
336 | #endif | |
337 | return 0; | |
338 | } | |
5c5d69b0 | 339 | |
b853a79f AB |
340 | const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) |
341 | { | |
342 | #ifdef CONFIG_SOFTMMU | |
343 | if (h && h->is_io) { | |
344 | MemoryRegionSection *mrs = h->v.io.section; | |
345 | if (!mrs->mr->name) { | |
346 | unsigned long maddr = 0xffffffff & (uintptr_t) mrs->mr; | |
347 | g_autofree char *temp = g_strdup_printf("anon%08lx", maddr); | |
348 | return g_intern_string(temp); | |
349 | } else { | |
350 | return g_intern_string(mrs->mr->name); | |
351 | } | |
352 | } else { | |
353 | return g_intern_static_string("RAM"); | |
354 | } | |
355 | #else | |
356 | return g_intern_static_string("Invalid"); | |
357 | #endif | |
358 | } | |
359 | ||
5c5d69b0 AB |
360 | /* |
361 | * Queries to the number and potential maximum number of vCPUs there | |
362 | * will be. This helps the plugin dimension per-vcpu arrays. | |
363 | */ | |
364 | ||
365 | #ifndef CONFIG_USER_ONLY | |
366 | static MachineState * get_ms(void) | |
367 | { | |
368 | return MACHINE(qdev_get_machine()); | |
369 | } | |
370 | #endif | |
371 | ||
372 | int qemu_plugin_n_vcpus(void) | |
373 | { | |
374 | #ifdef CONFIG_USER_ONLY | |
375 | return -1; | |
376 | #else | |
377 | return get_ms()->smp.cpus; | |
378 | #endif | |
379 | } | |
380 | ||
381 | int qemu_plugin_n_max_vcpus(void) | |
382 | { | |
383 | #ifdef CONFIG_USER_ONLY | |
384 | return -1; | |
385 | #else | |
386 | return get_ms()->smp.max_cpus; | |
387 | #endif | |
388 | } | |
ca76a669 AB |
389 | |
390 | /* | |
391 | * Plugin output | |
392 | */ | |
393 | void qemu_plugin_outs(const char *string) | |
394 | { | |
395 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
396 | } | |
6a9e8a08 MM |
397 | |
398 | bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) | |
399 | { | |
400 | return name && value && qapi_bool_parse(name, value, ret, NULL); | |
401 | } | |
91d40327 IA |
402 | |
403 | /* | |
404 | * Binary path, start and end locations | |
405 | */ | |
406 | const char *qemu_plugin_path_to_binary(void) | |
407 | { | |
408 | char *path = NULL; | |
409 | #ifdef CONFIG_USER_ONLY | |
410 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
411 | path = g_strdup(ts->bprm->filename); | |
412 | #endif | |
413 | return path; | |
414 | } | |
415 | ||
416 | uint64_t qemu_plugin_start_code(void) | |
417 | { | |
418 | uint64_t start = 0; | |
419 | #ifdef CONFIG_USER_ONLY | |
420 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
421 | start = ts->info->start_code; | |
422 | #endif | |
423 | return start; | |
424 | } | |
425 | ||
426 | uint64_t qemu_plugin_end_code(void) | |
427 | { | |
428 | uint64_t end = 0; | |
429 | #ifdef CONFIG_USER_ONLY | |
430 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
431 | end = ts->info->end_code; | |
432 | #endif | |
433 | return end; | |
434 | } | |
435 | ||
436 | uint64_t qemu_plugin_entry_code(void) | |
437 | { | |
438 | uint64_t entry = 0; | |
439 | #ifdef CONFIG_USER_ONLY | |
440 | TaskState *ts = (TaskState *) current_cpu->opaque; | |
441 | entry = ts->info->entry; | |
442 | #endif | |
443 | return entry; | |
444 | } |