]>
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" |
30424b8d | 42 | #include "system/memory.h" |
5c5d69b0 | 43 | #include "tcg/tcg.h" |
8df5e27c | 44 | #include "exec/gdbstub.h" |
9d06b0cc | 45 | #include "exec/target_page.h" |
68df8c8d | 46 | #include "exec/translation-block.h" |
36bc99bc | 47 | #include "exec/translator.h" |
cbafa236 | 48 | #include "disas/disas.h" |
5c5d69b0 | 49 | #include "plugin.h" |
5c5d69b0 AB |
50 | |
51 | /* Uninstall and Reset handlers */ | |
52 | ||
53 | void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
54 | { | |
55 | plugin_reset_uninstall(id, cb, false); | |
56 | } | |
57 | ||
58 | void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) | |
59 | { | |
60 | plugin_reset_uninstall(id, cb, true); | |
61 | } | |
62 | ||
63 | /* | |
64 | * Plugin Register Functions | |
65 | * | |
66 | * This allows the plugin to register callbacks for various events | |
67 | * during the translation. | |
68 | */ | |
69 | ||
70 | void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, | |
71 | qemu_plugin_vcpu_simple_cb_t cb) | |
72 | { | |
73 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb); | |
74 | } | |
75 | ||
76 | void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, | |
77 | qemu_plugin_vcpu_simple_cb_t cb) | |
78 | { | |
79 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); | |
80 | } | |
81 | ||
e5013259 RH |
82 | static bool tb_is_mem_only(void) |
83 | { | |
84 | return tb_cflags(tcg_ctx->gen_tb) & CF_MEMI_ONLY; | |
85 | } | |
86 | ||
5c5d69b0 AB |
87 | void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, |
88 | qemu_plugin_vcpu_udata_cb_t cb, | |
89 | enum qemu_plugin_cb_flags flags, | |
90 | void *udata) | |
91 | { | |
e5013259 | 92 | if (!tb_is_mem_only()) { |
db409c01 | 93 | plugin_register_dyn_cb__udata(&tb->cbs, cb, flags, udata); |
cfd405ea | 94 | } |
5c5d69b0 AB |
95 | } |
96 | ||
7de77d37 PB |
97 | void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb, |
98 | qemu_plugin_vcpu_udata_cb_t cb, | |
99 | enum qemu_plugin_cb_flags flags, | |
100 | enum qemu_plugin_cond cond, | |
101 | qemu_plugin_u64 entry, | |
102 | uint64_t imm, | |
103 | void *udata) | |
104 | { | |
105 | if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) { | |
106 | return; | |
107 | } | |
108 | if (cond == QEMU_PLUGIN_COND_ALWAYS) { | |
109 | qemu_plugin_register_vcpu_tb_exec_cb(tb, cb, flags, udata); | |
110 | return; | |
111 | } | |
112 | plugin_register_dyn_cond_cb__udata(&tb->cbs, cb, flags, | |
113 | cond, entry, imm, udata); | |
114 | } | |
115 | ||
0bcebaba PB |
116 | void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( |
117 | struct qemu_plugin_tb *tb, | |
118 | enum qemu_plugin_op op, | |
119 | qemu_plugin_u64 entry, | |
120 | uint64_t imm) | |
121 | { | |
e5013259 | 122 | if (!tb_is_mem_only()) { |
db409c01 | 123 | plugin_register_inline_op_on_entry(&tb->cbs, 0, op, entry, imm); |
0bcebaba PB |
124 | } |
125 | } | |
126 | ||
5c5d69b0 AB |
127 | void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, |
128 | qemu_plugin_vcpu_udata_cb_t cb, | |
129 | enum qemu_plugin_cb_flags flags, | |
130 | void *udata) | |
131 | { | |
e5013259 | 132 | if (!tb_is_mem_only()) { |
db409c01 | 133 | plugin_register_dyn_cb__udata(&insn->insn_cbs, cb, flags, udata); |
cfd405ea | 134 | } |
5c5d69b0 AB |
135 | } |
136 | ||
7de77d37 PB |
137 | void qemu_plugin_register_vcpu_insn_exec_cond_cb( |
138 | struct qemu_plugin_insn *insn, | |
139 | qemu_plugin_vcpu_udata_cb_t cb, | |
140 | enum qemu_plugin_cb_flags flags, | |
141 | enum qemu_plugin_cond cond, | |
142 | qemu_plugin_u64 entry, | |
143 | uint64_t imm, | |
144 | void *udata) | |
145 | { | |
146 | if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) { | |
147 | return; | |
148 | } | |
149 | if (cond == QEMU_PLUGIN_COND_ALWAYS) { | |
150 | qemu_plugin_register_vcpu_insn_exec_cb(insn, cb, flags, udata); | |
151 | return; | |
152 | } | |
153 | plugin_register_dyn_cond_cb__udata(&insn->insn_cbs, cb, flags, | |
154 | cond, entry, imm, udata); | |
155 | } | |
156 | ||
0bcebaba PB |
157 | void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( |
158 | struct qemu_plugin_insn *insn, | |
159 | enum qemu_plugin_op op, | |
160 | qemu_plugin_u64 entry, | |
161 | uint64_t imm) | |
162 | { | |
e5013259 | 163 | if (!tb_is_mem_only()) { |
db409c01 | 164 | plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm); |
0bcebaba PB |
165 | } |
166 | } | |
167 | ||
5c5d69b0 | 168 | |
cfd405ea AB |
169 | /* |
170 | * We always plant memory instrumentation because they don't finalise until | |
171 | * after the operation has complete. | |
172 | */ | |
5c5d69b0 AB |
173 | void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, |
174 | qemu_plugin_vcpu_mem_cb_t cb, | |
175 | enum qemu_plugin_cb_flags flags, | |
176 | enum qemu_plugin_mem_rw rw, | |
177 | void *udata) | |
178 | { | |
db409c01 | 179 | plugin_register_vcpu_mem_cb(&insn->mem_cbs, cb, flags, rw, udata); |
5c5d69b0 AB |
180 | } |
181 | ||
0bcebaba PB |
182 | void qemu_plugin_register_vcpu_mem_inline_per_vcpu( |
183 | struct qemu_plugin_insn *insn, | |
184 | enum qemu_plugin_mem_rw rw, | |
185 | enum qemu_plugin_op op, | |
186 | qemu_plugin_u64 entry, | |
187 | uint64_t imm) | |
188 | { | |
db409c01 | 189 | plugin_register_inline_op_on_entry(&insn->mem_cbs, rw, op, entry, imm); |
0bcebaba PB |
190 | } |
191 | ||
5c5d69b0 AB |
192 | void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, |
193 | qemu_plugin_vcpu_tb_trans_cb_t cb) | |
194 | { | |
195 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb); | |
196 | } | |
197 | ||
198 | void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, | |
199 | qemu_plugin_vcpu_syscall_cb_t cb) | |
200 | { | |
201 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb); | |
202 | } | |
203 | ||
204 | void | |
205 | qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, | |
206 | qemu_plugin_vcpu_syscall_ret_cb_t cb) | |
207 | { | |
208 | plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb); | |
209 | } | |
210 | ||
211 | /* | |
212 | * Plugin Queries | |
213 | * | |
214 | * These are queries that the plugin can make to gauge information | |
215 | * from our opaque data types. We do not want to leak internal details | |
216 | * here just information useful to the plugin. | |
217 | */ | |
218 | ||
219 | /* | |
220 | * Translation block information: | |
221 | * | |
222 | * A plugin can query the virtual address of the start of the block | |
223 | * and the number of instructions in it. It can also get access to | |
224 | * each translated instruction. | |
225 | */ | |
226 | ||
227 | size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) | |
228 | { | |
229 | return tb->n; | |
230 | } | |
231 | ||
232 | uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) | |
233 | { | |
e763953a RH |
234 | const DisasContextBase *db = tcg_ctx->plugin_db; |
235 | return db->pc_first; | |
5c5d69b0 AB |
236 | } |
237 | ||
238 | struct qemu_plugin_insn * | |
239 | qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) | |
240 | { | |
241 | if (unlikely(idx >= tb->n)) { | |
242 | return NULL; | |
243 | } | |
720a0e41 | 244 | return g_ptr_array_index(tb->insns, idx); |
5c5d69b0 AB |
245 | } |
246 | ||
247 | /* | |
248 | * Instruction information | |
249 | * | |
250 | * These queries allow the plugin to retrieve information about each | |
251 | * instruction being translated. | |
252 | */ | |
253 | ||
4abc8923 RH |
254 | size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, |
255 | void *dest, size_t len) | |
5c5d69b0 | 256 | { |
36bc99bc RH |
257 | const DisasContextBase *db = tcg_ctx->plugin_db; |
258 | ||
259 | len = MIN(len, insn->len); | |
260 | return translator_st(db, dest, insn->vaddr, len) ? len : 0; | |
5c5d69b0 AB |
261 | } |
262 | ||
263 | size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) | |
264 | { | |
36bc99bc | 265 | return insn->len; |
5c5d69b0 AB |
266 | } |
267 | ||
268 | uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) | |
269 | { | |
270 | return insn->vaddr; | |
271 | } | |
272 | ||
273 | void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) | |
274 | { | |
d3ace105 | 275 | const DisasContextBase *db = tcg_ctx->plugin_db; |
9d06b0cc | 276 | vaddr page0_last = db->pc_first | ~qemu_target_page_mask(); |
d3ace105 RH |
277 | |
278 | if (db->fake_insn) { | |
279 | return NULL; | |
280 | } | |
281 | ||
282 | /* | |
283 | * ??? The return value is not intended for use of host memory, | |
284 | * but as a proxy for address space and physical address. | |
285 | * Thus we are only interested in the first byte and do not | |
286 | * care about spanning pages. | |
287 | */ | |
288 | if (insn->vaddr <= page0_last) { | |
289 | if (db->host_addr[0] == NULL) { | |
290 | return NULL; | |
291 | } | |
292 | return db->host_addr[0] + insn->vaddr - db->pc_first; | |
293 | } else { | |
294 | if (db->host_addr[1] == NULL) { | |
295 | return NULL; | |
296 | } | |
297 | return db->host_addr[1] + insn->vaddr - (page0_last + 1); | |
298 | } | |
5c5d69b0 AB |
299 | } |
300 | ||
cbafa236 AB |
301 | char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) |
302 | { | |
4c833c60 RH |
303 | return plugin_disas(tcg_ctx->cpu, tcg_ctx->plugin_db, |
304 | insn->vaddr, insn->len); | |
cbafa236 AB |
305 | } |
306 | ||
7c4ab60f AB |
307 | const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) |
308 | { | |
309 | const char *sym = lookup_symbol(insn->vaddr); | |
310 | return sym[0] != 0 ? sym : NULL; | |
311 | } | |
312 | ||
5c5d69b0 AB |
313 | /* |
314 | * The memory queries allow the plugin to query information about a | |
315 | * memory access. | |
316 | */ | |
317 | ||
318 | unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info) | |
319 | { | |
37aff087 RH |
320 | MemOp op = get_memop(info); |
321 | return op & MO_SIZE; | |
5c5d69b0 AB |
322 | } |
323 | ||
324 | bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info) | |
325 | { | |
37aff087 RH |
326 | MemOp op = get_memop(info); |
327 | return op & MO_SIGN; | |
5c5d69b0 AB |
328 | } |
329 | ||
330 | bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info) | |
331 | { | |
37aff087 RH |
332 | MemOp op = get_memop(info); |
333 | return (op & MO_BSWAP) == MO_BE; | |
5c5d69b0 AB |
334 | } |
335 | ||
336 | bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) | |
337 | { | |
37aff087 | 338 | return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; |
5c5d69b0 AB |
339 | } |
340 | ||
9505f85e PB |
341 | qemu_plugin_mem_value qemu_plugin_mem_get_value(qemu_plugin_meminfo_t info) |
342 | { | |
343 | uint64_t low = current_cpu->neg.plugin_mem_value_low; | |
344 | qemu_plugin_mem_value value; | |
345 | ||
346 | switch (qemu_plugin_mem_size_shift(info)) { | |
347 | case 0: | |
348 | value.type = QEMU_PLUGIN_MEM_VALUE_U8; | |
349 | value.data.u8 = (uint8_t)low; | |
350 | break; | |
351 | case 1: | |
352 | value.type = QEMU_PLUGIN_MEM_VALUE_U16; | |
353 | value.data.u16 = (uint16_t)low; | |
354 | break; | |
355 | case 2: | |
356 | value.type = QEMU_PLUGIN_MEM_VALUE_U32; | |
357 | value.data.u32 = (uint32_t)low; | |
358 | break; | |
359 | case 3: | |
360 | value.type = QEMU_PLUGIN_MEM_VALUE_U64; | |
361 | value.data.u64 = low; | |
362 | break; | |
363 | case 4: | |
364 | value.type = QEMU_PLUGIN_MEM_VALUE_U128; | |
365 | value.data.u128.low = low; | |
366 | value.data.u128.high = current_cpu->neg.plugin_mem_value_high; | |
367 | break; | |
368 | default: | |
369 | g_assert_not_reached(); | |
370 | } | |
371 | return value; | |
372 | } | |
373 | ||
4a448b14 PB |
374 | int qemu_plugin_num_vcpus(void) |
375 | { | |
376 | return plugin_num_vcpus(); | |
377 | } | |
378 | ||
ca76a669 AB |
379 | /* |
380 | * Plugin output | |
381 | */ | |
382 | void qemu_plugin_outs(const char *string) | |
383 | { | |
384 | qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); | |
385 | } | |
6a9e8a08 MM |
386 | |
387 | bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) | |
388 | { | |
389 | return name && value && qapi_bool_parse(name, value, ret, NULL); | |
390 | } | |
91d40327 | 391 | |
8df5e27c AB |
392 | /* |
393 | * Create register handles. | |
394 | * | |
395 | * We need to create a handle for each register so the plugin | |
396 | * infrastructure can call gdbstub to read a register. They are | |
397 | * currently just a pointer encapsulation of the gdb_reg but in | |
398 | * future may hold internal plugin state so its important plugin | |
399 | * authors are not tempted to treat them as numbers. | |
400 | * | |
401 | * We also construct a result array with those handles and some | |
402 | * ancillary data the plugin might find useful. | |
403 | */ | |
404 | ||
405 | static GArray *create_register_handles(GArray *gdbstub_regs) | |
406 | { | |
407 | GArray *find_data = g_array_new(true, true, | |
408 | sizeof(qemu_plugin_reg_descriptor)); | |
409 | ||
410 | for (int i = 0; i < gdbstub_regs->len; i++) { | |
411 | GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i); | |
412 | qemu_plugin_reg_descriptor desc; | |
413 | ||
414 | /* skip "un-named" regs */ | |
415 | if (!grd->name) { | |
416 | continue; | |
417 | } | |
418 | ||
419 | /* Create a record for the plugin */ | |
ad59d5ca | 420 | desc.handle = GINT_TO_POINTER(grd->gdb_reg + 1); |
8df5e27c AB |
421 | desc.name = g_intern_string(grd->name); |
422 | desc.feature = g_intern_string(grd->feature_name); | |
423 | g_array_append_val(find_data, desc); | |
424 | } | |
425 | ||
426 | return find_data; | |
427 | } | |
428 | ||
429 | GArray *qemu_plugin_get_registers(void) | |
430 | { | |
431 | g_assert(current_cpu); | |
432 | ||
433 | g_autoptr(GArray) regs = gdb_get_register_list(current_cpu); | |
434 | return create_register_handles(regs); | |
435 | } | |
436 | ||
1a92b658 RH |
437 | int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) |
438 | { | |
439 | g_assert(current_cpu); | |
440 | ||
766e00bd RH |
441 | if (qemu_plugin_get_cb_flags() == QEMU_PLUGIN_CB_NO_REGS) { |
442 | return -1; | |
443 | } | |
444 | ||
1a92b658 RH |
445 | return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); |
446 | } | |
447 | ||
448 | int qemu_plugin_write_register(struct qemu_plugin_register *reg, | |
449 | GByteArray *buf) | |
450 | { | |
451 | g_assert(current_cpu); | |
452 | ||
453 | if (buf->len == 0 || qemu_plugin_get_cb_flags() != QEMU_PLUGIN_CB_RW_REGS) { | |
454 | return -1; | |
455 | } | |
456 | ||
457 | return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1); | |
458 | } | |
459 | ||
09ac6268 | 460 | bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len) |
595cd9ce RH |
461 | { |
462 | g_assert(current_cpu); | |
463 | ||
464 | if (len == 0) { | |
465 | return false; | |
466 | } | |
467 | ||
468 | g_byte_array_set_size(data, len); | |
469 | ||
470 | int result = cpu_memory_rw_debug(current_cpu, addr, data->data, | |
471 | data->len, false); | |
472 | ||
473 | if (result < 0) { | |
474 | return false; | |
475 | } | |
476 | ||
477 | return true; | |
478 | } | |
479 | ||
f00373b8 RH |
480 | bool qemu_plugin_write_memory_vaddr(uint64_t addr, GByteArray *data) |
481 | { | |
482 | g_assert(current_cpu); | |
483 | ||
484 | if (data->len == 0) { | |
485 | return false; | |
486 | } | |
487 | ||
488 | int result = cpu_memory_rw_debug(current_cpu, addr, data->data, | |
489 | data->len, true); | |
490 | ||
491 | if (result < 0) { | |
492 | return false; | |
493 | } | |
494 | ||
495 | return true; | |
496 | } | |
497 | ||
30424b8d RH |
498 | enum qemu_plugin_hwaddr_operation_result |
499 | qemu_plugin_read_memory_hwaddr(hwaddr addr, GByteArray *data, size_t len) | |
500 | { | |
501 | #ifdef CONFIG_SOFTMMU | |
502 | if (len == 0) { | |
503 | return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; | |
504 | } | |
505 | ||
506 | g_assert(current_cpu); | |
507 | ||
508 | ||
509 | int as_idx = cpu_asidx_from_attrs(current_cpu, MEMTXATTRS_UNSPECIFIED); | |
510 | AddressSpace *as = cpu_get_address_space(current_cpu, as_idx); | |
511 | ||
512 | if (as == NULL) { | |
513 | return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE; | |
514 | } | |
515 | ||
516 | g_byte_array_set_size(data, len); | |
517 | MemTxResult res = address_space_rw(as, addr, | |
518 | MEMTXATTRS_UNSPECIFIED, data->data, | |
519 | data->len, false); | |
520 | ||
521 | switch (res) { | |
522 | case MEMTX_OK: | |
523 | return QEMU_PLUGIN_HWADDR_OPERATION_OK; | |
524 | case MEMTX_ERROR: | |
525 | return QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR; | |
526 | case MEMTX_DECODE_ERROR: | |
527 | return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS; | |
528 | case MEMTX_ACCESS_ERROR: | |
529 | return QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED; | |
530 | default: | |
531 | return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; | |
532 | } | |
533 | #else | |
534 | return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; | |
535 | #endif | |
536 | } | |
537 | ||
538 | enum qemu_plugin_hwaddr_operation_result | |
539 | qemu_plugin_write_memory_hwaddr(hwaddr addr, GByteArray *data) | |
540 | { | |
541 | #ifdef CONFIG_SOFTMMU | |
542 | if (data->len == 0) { | |
543 | return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; | |
544 | } | |
545 | ||
546 | g_assert(current_cpu); | |
547 | ||
548 | int as_idx = cpu_asidx_from_attrs(current_cpu, MEMTXATTRS_UNSPECIFIED); | |
549 | AddressSpace *as = cpu_get_address_space(current_cpu, as_idx); | |
550 | ||
551 | if (as == NULL) { | |
552 | return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE; | |
553 | } | |
554 | ||
555 | MemTxResult res = address_space_rw(as, addr, | |
556 | MEMTXATTRS_UNSPECIFIED, data->data, | |
557 | data->len, true); | |
558 | switch (res) { | |
559 | case MEMTX_OK: | |
560 | return QEMU_PLUGIN_HWADDR_OPERATION_OK; | |
561 | case MEMTX_ERROR: | |
562 | return QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR; | |
563 | case MEMTX_DECODE_ERROR: | |
564 | return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS; | |
565 | case MEMTX_ACCESS_ERROR: | |
566 | return QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED; | |
567 | default: | |
568 | return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; | |
569 | } | |
570 | #else | |
571 | return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; | |
572 | #endif | |
573 | } | |
574 | ||
575 | bool qemu_plugin_translate_vaddr(uint64_t vaddr, uint64_t *hwaddr) | |
576 | { | |
577 | #ifdef CONFIG_SOFTMMU | |
578 | g_assert(current_cpu); | |
579 | ||
580 | uint64_t res = cpu_get_phys_page_debug(current_cpu, vaddr); | |
581 | ||
582 | if (res == (uint64_t)-1) { | |
583 | return false; | |
584 | } | |
585 | ||
586 | *hwaddr = res | (vaddr & ~TARGET_PAGE_MASK); | |
587 | ||
588 | return true; | |
589 | #else | |
590 | return false; | |
591 | #endif | |
592 | } | |
593 | ||
a3c2cf0b PB |
594 | struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) |
595 | { | |
596 | return plugin_scoreboard_new(element_size); | |
597 | } | |
598 | ||
599 | void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) | |
600 | { | |
601 | plugin_scoreboard_free(score); | |
602 | } | |
603 | ||
604 | void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, | |
605 | unsigned int vcpu_index) | |
606 | { | |
607 | g_assert(vcpu_index < qemu_plugin_num_vcpus()); | |
608 | /* we can't use g_array_index since entry size is not statically known */ | |
609 | char *base_ptr = score->data->data; | |
610 | return base_ptr + vcpu_index * g_array_get_element_size(score->data); | |
611 | } | |
8042e2ea PB |
612 | |
613 | static uint64_t *plugin_u64_address(qemu_plugin_u64 entry, | |
614 | unsigned int vcpu_index) | |
615 | { | |
616 | char *ptr = qemu_plugin_scoreboard_find(entry.score, vcpu_index); | |
617 | return (uint64_t *)(ptr + entry.offset); | |
618 | } | |
619 | ||
620 | void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, | |
621 | uint64_t added) | |
622 | { | |
623 | *plugin_u64_address(entry, vcpu_index) += added; | |
624 | } | |
625 | ||
626 | uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, | |
627 | unsigned int vcpu_index) | |
628 | { | |
629 | return *plugin_u64_address(entry, vcpu_index); | |
630 | } | |
631 | ||
632 | void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, | |
633 | uint64_t val) | |
634 | { | |
635 | *plugin_u64_address(entry, vcpu_index) = val; | |
636 | } | |
637 | ||
638 | uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) | |
639 | { | |
640 | uint64_t total = 0; | |
641 | for (int i = 0, n = qemu_plugin_num_vcpus(); i < n; ++i) { | |
642 | total += qemu_plugin_u64_get(entry, i); | |
643 | } | |
644 | return total; | |
645 | } | |
847a65dd | 646 |