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