qemu_plugin/lib.rs
1//! High level idiomatic Rust bindings to the QEMU plugin API
2//!
3//! `qemu-plugin` makes it easy to write QEMU plugins in Rust.
4//!
5//! # Example
6//!
7//! Below is a minimal plugin example for a plugin which prints the execution trace of the
8//! program running in QEMU. Notice that all we do is register a struct which implements
9//! `Plugin`, and the library takes care of the rest.
10//!
11//! ```rust,ignore
12//! use qemu_plugin::{
13//! HasCallbacks, Plugin, Register, register, Error, Result, PluginId,
14//! TranslationBlock,
15//! };
16//!
17//! struct TinyTrace;
18//!
19//! impl Register for TinyTrace {}
20//!
21//! impl HasCallbacks for TinyTrace {
22//! fn on_translation_block_translate(
23//! &mut self,
24//! _id: PluginId,
25//! tb: TranslationBlock,
26//! ) -> Result<()> {
27//! tb.instructions().enumerate().try_for_each(|(idx, insn)| {
28//! if idx == 0 {
29//! println!("====TB: {:08x}", insn.vaddr());
30//! }
31//!
32//! println!("{:08x}: {}", insn.vaddr(), insn.disas()?);
33//! Ok::<(), Error>(())
34//! })?;
35//!
36//! Ok(())
37//! }
38//! }
39//!
40//! register!(TinyTrace);
41//! ```
42//!
43//! The above `src/lib.rs` in a Cargo project with the following `Cargo.toml` will compile to
44//! `libtiny.so`, which can be loaded in QEMU by running `qemu-system-ARCH -plugin ./libtiny.so`.
45//!
46//! ```toml
47//! [package]
48//! name = "tiny"
49//! version = "0.1.0"
50//! edition = "2024"
51//!
52//! [lib]
53//! crate-type = ["cdylib"]
54//!
55//! [dependencies]
56//! qemu-plugin = "10.1.0-v2"
57//! ```
58
59#![deny(missing_docs)]
60#![cfg_attr(feature = "num-traits", feature(generic_const_exprs))]
61
62#[cfg(windows)]
63mod win_link_hook;
64
65use crate::sys::{
66 qemu_plugin_cb_flags, qemu_plugin_id_t, qemu_plugin_insn, qemu_plugin_mem_rw,
67 qemu_plugin_meminfo_t, qemu_plugin_op, qemu_plugin_simple_cb_t, qemu_plugin_tb,
68 qemu_plugin_vcpu_simple_cb_t, qemu_plugin_vcpu_syscall_cb_t, qemu_plugin_vcpu_syscall_ret_cb_t,
69 qemu_plugin_vcpu_tb_trans_cb_t,
70};
71#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
72use crate::sys::{qemu_plugin_reg_descriptor, qemu_plugin_u64};
73#[cfg(not(any(
74 feature = "plugin-api-v0",
75 feature = "plugin-api-v1",
76 feature = "plugin-api-v1",
77 feature = "plugin-api-v2"
78)))]
79use qemu_plugin_sys::qemu_plugin_cond;
80#[cfg(not(feature = "plugin-api-v0"))]
81use std::{ffi::CStr, path::PathBuf};
82use std::{
83 ffi::{CString, c_uint, c_void},
84 sync::{Mutex, OnceLock},
85};
86
87pub mod error;
88#[allow(unused_imports)]
89pub use error::*;
90pub mod install;
91pub use install::*;
92pub mod plugin;
93pub use plugin::*;
94pub mod instruction;
95pub mod sys;
96pub use instruction::*;
97pub mod translation_block;
98pub use translation_block::*;
99#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
100pub mod register;
101#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
102pub use register::*;
103pub mod memory;
104pub use memory::*;
105#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
106pub mod scoreboard;
107#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
108pub use scoreboard::*;
109pub mod glib;
110pub(crate) use glib::*;
111
112/// The index of a vCPU
113pub type VCPUIndex = c_uint;
114#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
115/// u64 member of an entry in a scoreboard, allows access to a specific u64 member in
116/// one given entry, located at a specified offset. Inline operations expect this as an
117/// entry.
118pub type PluginU64 = qemu_plugin_u64;
119/// Flags for callbacks
120pub type CallbackFlags = qemu_plugin_cb_flags;
121/// Memory read/write flags
122pub type MemRW = qemu_plugin_mem_rw;
123#[cfg(not(any(
124 feature = "plugin-api-v0",
125 feature = "plugin-api-v1",
126 feature = "plugin-api-v2"
127)))]
128/// A condition for a callback to be run
129pub type PluginCondition = qemu_plugin_cond;
130/// Plugin operations for inline operations
131pub type PluginOp = qemu_plugin_op;
132/// A plugin ID
133pub type PluginId = qemu_plugin_id_t;
134
135/// A callback that can be called many times, each time a vCPU is initialized
136///
137/// # Arguments
138///
139/// - `id`: The plugin ID
140/// - `vcpu_index`: The index of the vCPU that was initialized
141pub type VCPUInitCallback = qemu_plugin_vcpu_simple_cb_t;
142
143/// A callback that can be called many times, each time a vCPU exits
144///
145/// # Arguments
146///
147/// - `id`: The plugin ID
148/// - `vcpu_index`: The index of the vCPU that exited
149pub type VCPUExitCallback = qemu_plugin_vcpu_simple_cb_t;
150
151/// A callback that can be called many times, each time a vCPU idles
152///
153/// # Arguments
154///
155/// - `id`: The plugin ID
156/// - `vcpu_index`: The index of the vCPU that idled
157pub type VCPUIdleCallback = qemu_plugin_vcpu_simple_cb_t;
158
159/// A callback that can be called many times, each time a vCPU is resumed
160///
161/// # Arguments
162///
163/// - `id`: The plugin ID
164/// - `vcpu_index`: The index of the vCPU that resumed
165pub type VCPUResumeCallback = qemu_plugin_vcpu_simple_cb_t;
166
167/// A callback that can be called many times, each time a translation occurs. The
168/// callback is passed an opaque `qemu_plugin_tb` pointer, which can be queried for
169/// additional information including the list of translated instructions. The callback
170/// can register further callbacks to be triggered when the block or individual
171/// instructions execute.
172///
173/// # Arguments
174///
175/// - `id`: The plugin ID
176/// - `tb`: The translated block
177pub type VCPUTranslationBlockTranslationCallback = qemu_plugin_vcpu_tb_trans_cb_t;
178
179/// A callback called on flush.
180///
181/// # Arguments
182///
183/// - `id`: The plugin ID
184pub type FlushCallback = qemu_plugin_simple_cb_t;
185
186/// A callback called on Syscall entry
187///
188/// # Arguments
189///
190/// - `id`: The plugin ID
191/// - `vcpu_index`: The index of the vCPU that executed the instruction
192/// - `num`: The syscall number
193/// - `a1`: The first syscall argument
194/// - `a2`: The second syscall argument
195/// - `a3`: The third syscall argument
196/// - `a4`: The fourth syscall argument
197/// - `a5`: The fifth syscall argument
198/// - `a6`: The sixth syscall argument
199/// - `a7`: The seventh syscall argument
200/// - `a8`: The eighth syscall argument
201pub type SyscallCallback = qemu_plugin_vcpu_syscall_cb_t;
202
203/// A callback called on Syscall return
204///
205/// # Arguments
206///
207/// - `id`: The plugin ID
208/// - `vcpu_index`: The index of the vCPU that executed the instruction
209/// - `num`: The syscall number
210/// - `ret`: The syscall return value
211pub type SyscallReturnCallback = qemu_plugin_vcpu_syscall_ret_cb_t;
212
213// NOTE: Box<Box< is not strictly necessary here because the pointer is never sent via
214// FFI which means we never downcast to an 8-byte pointer from fat, but it is best not
215// to rely on that.
216
217#[allow(clippy::type_complexity)]
218/// A callback which will run once removal and uninstallation of the plugin is finalized. This callback
219/// can only be set once, by calling the `qemu_plugin_uninstall` function.
220static UNINSTALL_CALLBACK: OnceLock<
221 Mutex<Option<Box<Box<dyn FnOnce(qemu_plugin_id_t) + Send + Sync + 'static>>>>,
222> = OnceLock::new();
223
224#[allow(clippy::type_complexity)]
225/// A callback which will run once the plugin is reset. This callback is set by calling the
226/// `qemu_plugin_reset` function.
227static RESET_CALLBACK: OnceLock<
228 Mutex<Option<Box<Box<dyn FnOnce(qemu_plugin_id_t) + Send + Sync + 'static>>>>,
229> = OnceLock::new();
230
231/// Handle the invocation of the uninstall callback by calling the stored
232/// callback closure, if one exists.
233extern "C" fn handle_qemu_plugin_uninstall_callback(id: qemu_plugin_id_t) {
234 if let Some(callback) = UNINSTALL_CALLBACK.get()
235 && let Ok(mut callback) = callback.lock()
236 && let Some(callback) = callback.take()
237 {
238 callback(id);
239 }
240 // NOTE: An error here is ignored, and exceedingly fatal
241}
242
243/// Handle the invocation of the reset callback by calling the stored
244/// callback closure, if one exists.
245extern "C" fn handle_qemu_plugin_reset_callback(id: qemu_plugin_id_t) {
246 if let Some(callback) = UNINSTALL_CALLBACK.get()
247 && let Ok(mut callback) = callback.lock()
248 && let Some(callback) = callback.take()
249 {
250 callback(id);
251 }
252 // NOTE: An error here is ignored, and exceedingly fatal
253}
254
255/// Uninstall a plugin.
256///
257/// # Arguments
258///
259/// - `id`: The plugin ID
260/// - `cb`: A callback function that will be called when the plugin has been
261/// uninstalled.
262///
263/// # Safety
264///
265/// Do NOT assume that the plugin has been uninstalled once this function returns.
266/// Plugins are uninstalled asynchronously, and therefore the given plugin receives
267/// callbacks until cb is called. This function must not be called from
268/// `qemu_plugin_install`.
269pub fn qemu_plugin_uninstall<F>(id: qemu_plugin_id_t, cb: F) -> Result<()>
270where
271 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
272{
273 UNINSTALL_CALLBACK
274 .set(Mutex::new(Some(Box::new(Box::new(cb)))))
275 .map_err(|_| Error::ConcurrentPluginUninstallCallbackSet)?;
276
277 unsafe { crate::sys::qemu_plugin_uninstall(id, Some(handle_qemu_plugin_uninstall_callback)) };
278
279 Ok(())
280}
281
282/// Reset a plugin
283///
284/// # Arguments
285///
286/// - `id`: The plugin ID
287/// - `cb`: A callback function that will be called when the plugin has been reset.
288///
289/// # Safety
290///
291/// Do NOT assume that the plugin has been reset once this function returns. Plugins
292/// are reset asynchronously, and therefore the given plugin receives callbacks until
293/// cb is called.
294pub fn qemu_plugin_reset<F>(id: qemu_plugin_id_t, cb: F) -> Result<()>
295where
296 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
297{
298 if let Some(callback) = RESET_CALLBACK.get() {
299 let Ok(mut callback) = callback.lock() else {
300 return Err(Error::PluginResetCallbackState);
301 };
302 let _ = callback.replace(Box::new(Box::new(cb)));
303 } else {
304 RESET_CALLBACK
305 .set(Mutex::new(Some(Box::new(Box::new(cb)))))
306 .map_err(|_| Error::ConcurrentPluginResetCallbackSet)?;
307 }
308
309 unsafe { crate::sys::qemu_plugin_reset(id, Some(handle_qemu_plugin_reset_callback)) };
310
311 Ok(())
312}
313
314/// Register a callback to be called when a vCPU is initialized. The callback does not receive
315/// user data, so it is not possible to register it via closure.
316///
317/// # Arguments
318///
319/// - `id`: The plugin ID
320/// - `cb`: The callback to be called
321pub fn qemu_plugin_register_vcpu_init_cb(id: qemu_plugin_id_t, cb: VCPUInitCallback) -> Result<()> {
322 unsafe { crate::sys::qemu_plugin_register_vcpu_init_cb(id, cb) };
323 Ok(())
324}
325
326/// Register a callback to be called when a vCPU exits. The callback does not receive
327/// user data, so it is not possible to register it via closure.
328///
329/// # Arguments
330///
331/// - `id`: The plugin ID
332/// - `cb`: The callback to be called
333pub fn qemu_plugin_register_vcpu_exit_cb(id: qemu_plugin_id_t, cb: VCPUExitCallback) -> Result<()> {
334 unsafe { crate::sys::qemu_plugin_register_vcpu_exit_cb(id, cb) };
335 Ok(())
336}
337
338/// Register a callback to be called when a vCPU idles. The callback does not receive
339/// user data, so it is not possible to register it via closure.
340///
341/// # Arguments
342///
343/// - `id`: The plugin ID
344/// - `cb`: The callback to be called
345pub fn qemu_plugin_register_vcpu_idle_cb(id: qemu_plugin_id_t, cb: VCPUIdleCallback) -> Result<()> {
346 unsafe { crate::sys::qemu_plugin_register_vcpu_idle_cb(id, cb) };
347 Ok(())
348}
349
350/// Register a callback to be called when a vCPU resumes. The callback does not receive
351/// user data, so it is not possible to register it via closure.
352///
353/// # Arguments
354///
355/// - `id`: The plugin ID
356/// - `cb`: The callback to be called
357pub fn qemu_plugin_register_vcpu_resume_cb(
358 id: qemu_plugin_id_t,
359 cb: VCPUResumeCallback,
360) -> Result<()> {
361 unsafe { crate::sys::qemu_plugin_register_vcpu_resume_cb(id, cb) };
362 Ok(())
363}
364
365/// Register a callback to be called when a translation block is translated. The callback
366/// receives a pointer to a `qemu_plugin_tb` structure, which can be queried for additional
367/// information including the list of translated instructions. The callback can register
368/// further callbacks to be triggered when the block or individual instructions execute.
369///
370/// # Arguments
371///
372/// - `id`: The plugin ID
373/// - `cb`: The callback to be called
374pub fn qemu_plugin_register_vcpu_tb_trans_cb(
375 id: qemu_plugin_id_t,
376 cb: VCPUTranslationBlockTranslationCallback,
377) -> Result<()> {
378 unsafe { crate::sys::qemu_plugin_register_vcpu_tb_trans_cb(id, cb) };
379 Ok(())
380}
381
382extern "C" fn handle_qemu_plugin_register_vcpu_tb_exec_cb<F>(
383 vcpu_index: VCPUIndex,
384 userdata: *mut c_void,
385) where
386 F: FnMut(VCPUIndex) + Send + Sync + 'static,
387{
388 let mut cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
389 cb(vcpu_index);
390 Box::leak(cb);
391}
392
393#[allow(clippy::not_unsafe_ptr_arg_deref)]
394/// Register a callback to be called when a translation block is executed.
395///
396/// # Arguments
397///
398/// - `tb`: The translated block to register the execution callback for
399/// - `cb`: The callback to be called when the block `tb` is executed
400///
401/// # Safety
402///
403/// This function is safe when the pointer `tb` is a valid pointer to a `qemu_plugin_tb`
404/// structure, which is always opaque.
405pub fn qemu_plugin_register_vcpu_tb_exec_cb<F>(tb: TranslationBlock, cb: F, flags: CallbackFlags)
406where
407 F: FnMut(VCPUIndex) + Send + Sync + 'static,
408{
409 tb.register_execute_callback_flags(cb, flags);
410}
411
412#[cfg(not(any(
413 feature = "plugin-api-v0",
414 feature = "plugin-api-v1",
415 feature = "plugin-api-v2"
416)))]
417/// Register a callback to be conditionally called when a translation block is executed.
418///
419/// # Arguments
420///
421/// - `tb`: The translated block to register the execution callback for
422/// - `cb`: The callback to be called when the block `tb` is executed
423/// - `cond`: The condition to be met for the callback to be called
424/// - `entry`: The entry to be passed to the callback
425/// - `immediate`: The immediate value to be passed to the callback
426///
427/// # Safety
428///
429/// This function is safe when the pointer `tb` is a valid pointer to a `qemu_plugin_tb`
430/// structure, which is always opaque.
431pub fn qemu_plugin_register_vcpu_tb_exec_cond_cb<F>(
432 tb: TranslationBlock,
433 cb: F,
434 flags: CallbackFlags,
435 cond: PluginCondition,
436 entry: PluginU64,
437 immediate: u64,
438) where
439 F: FnMut(VCPUIndex) + Send + Sync + 'static,
440{
441 tb.register_conditional_execute_callback_flags(cb, flags, cond, entry, immediate);
442}
443
444#[cfg(any(feature = "plugin-api-v0", feature = "plugin-api-v1"))]
445#[allow(clippy::not_unsafe_ptr_arg_deref)]
446/// Register an inline callback to be called when a translation block is executed.
447///
448/// # Arguments
449///
450/// - `tb`: The translated block to register the execution callback for
451/// - `op`: The operation to be performed
452/// - `ptr`: The pointer to the data to be passed to the operation
453/// - `imm`: The immediate value to be passed to the operation
454pub fn qemu_plugin_register_vcpu_tb_exec_inline(
455 tb: TranslationBlock,
456 op: PluginOp,
457 ptr: *mut c_void,
458 imm: u64,
459) {
460 unsafe {
461 crate::sys::qemu_plugin_register_vcpu_tb_exec_inline(
462 tb.translation_block as *mut qemu_plugin_tb,
463 op,
464 ptr,
465 imm,
466 );
467 }
468}
469
470#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
471#[allow(clippy::not_unsafe_ptr_arg_deref)]
472/// Register an inline callback to be called when a translation block is executed.
473///
474/// # Arguments
475///
476/// - `tb`: The translated block to register the execution callback for
477/// - `op`: The operation to be performed
478/// - `entry`: The entry to be passed to the operation
479/// - `imm`: The immediate value to be passed to the operation
480pub fn qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
481 tb: TranslationBlock,
482 op: PluginOp,
483 entry: PluginU64,
484 imm: u64,
485) {
486 unsafe {
487 crate::sys::qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
488 tb.translation_block as *mut qemu_plugin_tb,
489 op,
490 entry,
491 imm,
492 );
493 }
494}
495
496extern "C" fn handle_qemu_plugin_register_vcpu_insn_exec_cb<F>(
497 vcpu_index: VCPUIndex,
498 userdata: *mut c_void,
499) where
500 F: FnMut(VCPUIndex) + Send + Sync + 'static,
501{
502 let mut cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
503 cb(vcpu_index);
504 // NOTE: This memory will be freed on plugin exit
505 Box::leak(cb);
506}
507
508#[allow(clippy::not_unsafe_ptr_arg_deref)]
509/// Register a callback to be called when an instruction is executed.
510///
511/// # Arguments
512///
513/// - `insn`: The instruction handle to register the callback for
514/// - `cb`: The callback to be called
515pub fn qemu_plugin_register_vcpu_insn_exec_cb<F>(insn: Instruction, cb: F, flags: CallbackFlags)
516where
517 F: FnMut(VCPUIndex) + Send + Sync + 'static,
518{
519 insn.register_execute_callback_flags(cb, flags);
520}
521
522#[cfg(not(any(
523 feature = "plugin-api-v0",
524 feature = "plugin-api-v1",
525 feature = "plugin-api-v2"
526)))]
527#[allow(clippy::not_unsafe_ptr_arg_deref)]
528/// Register a callback to be conditionally called when an instruction is executed.
529///
530/// # Arguments
531///
532/// - `insn`: The instruction handle to register the callback for
533/// - `cb`: The callback to be called
534/// - `cond`: The condition to be met for the callback to be called
535/// - `entry`: The entry to be passed to the callback
536/// - `immediate`: The immediate value to be passed to the callback
537pub fn qemu_plugin_register_vcpu_insn_exec_cond_cb<F>(
538 insn: Instruction,
539 cb: F,
540 flags: CallbackFlags,
541 cond: PluginCondition,
542 entry: PluginU64,
543 immediate: u64,
544) where
545 F: FnMut(VCPUIndex) + Send + Sync + 'static,
546{
547 insn.register_conditional_execute_callback_flags(cb, flags, cond, entry, immediate);
548}
549
550#[cfg(any(feature = "plugin-api-v0", feature = "plugin-api-v1"))]
551#[allow(clippy::not_unsafe_ptr_arg_deref)]
552/// Register an inline callback to be called when an instruction is executed.
553///
554/// # Arguments
555///
556/// - `insn`: The instruction handle to register the callback for
557/// - `op`: The operation to be performed
558/// - `ptr`: The pointer to the data to be passed to the operation
559/// - `imm`: The immediate value to be passed to the operation
560pub fn qemu_plugin_register_vcpu_insn_exec_inline(
561 insn: Instruction,
562 op: PluginOp,
563 ptr: *mut c_void,
564 imm: u64,
565) {
566 unsafe {
567 crate::sys::qemu_plugin_register_vcpu_insn_exec_inline(
568 insn.instruction as *mut qemu_plugin_insn,
569 op,
570 ptr,
571 imm,
572 );
573 }
574}
575
576#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
577#[allow(clippy::not_unsafe_ptr_arg_deref)]
578/// Register an inline callback to be called when an instruction is executed.
579///
580/// # Arguments
581///
582/// - `insn`: The instruction handle to register the callback for
583/// - `op`: The operation to be performed
584/// - `entry`: The entry to be passed to the operation
585/// - `imm`: The immediate value to be passed to the operation
586pub fn qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
587 insn: Instruction,
588 op: PluginOp,
589 entry: PluginU64,
590 imm: u64,
591) {
592 unsafe {
593 crate::sys::qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
594 insn.instruction as *mut qemu_plugin_insn,
595 op,
596 entry,
597 imm,
598 );
599 }
600}
601
602extern "C" fn handle_qemu_plugin_register_vcpu_mem_cb<F>(
603 vcpu_index: VCPUIndex,
604 meminfo: qemu_plugin_meminfo_t,
605 vaddr: u64,
606 userdata: *mut c_void,
607) where
608 F: for<'a> FnMut(VCPUIndex, MemoryInfo<'a>, u64) + Send + Sync + 'static,
609{
610 let mut cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
611 let meminfo = MemoryInfo::from(meminfo);
612 cb(vcpu_index, meminfo, vaddr);
613 // NOTE: This memory will be freed on plugin exit
614 Box::leak(cb);
615}
616
617/// Register a callback for every memory transaction of a particular instruction. If the
618/// instruction is executed multiple times, the callback will be called multiple times.
619///
620/// # Arguments
621///
622/// - `insn`: The instruction handle to register the callback for
623/// - `cb`: The callback to be called
624/// - `rw`: Whether the callback should be called for reads, writes, or both
625pub fn qemu_plugin_register_vcpu_mem_cb<F>(
626 insn: Instruction,
627 cb: F,
628 flags: CallbackFlags,
629 rw: MemRW,
630) where
631 F: for<'a> FnMut(VCPUIndex, MemoryInfo<'a>, u64) + Send + Sync + 'static,
632{
633 insn.register_memory_access_callback_flags(cb, rw, flags);
634}
635
636#[cfg(any(feature = "plugin-api-v0", feature = "plugin-api-v1"))]
637#[allow(clippy::not_unsafe_ptr_arg_deref)]
638/// Register an inline callback for every memory transaction of a particular instruction.
639///
640/// # Arguments
641///
642/// - `insn`: The instruction handle to register the callback for
643/// - `rw`: Whether the callback should be called for reads, writes, or both
644/// - `op`: The operation to be performed
645/// - `ptr`: The pointer to the data to be passed to the operation
646/// - `imm`: The immediate value to be passed to the operation
647pub fn qemu_plugin_register_vcpu_mem_inline(
648 insn: Instruction,
649 rw: MemRW,
650 op: PluginOp,
651 ptr: *mut c_void,
652 imm: u64,
653) {
654 unsafe {
655 crate::sys::qemu_plugin_register_vcpu_mem_inline(
656 insn.instruction as *mut qemu_plugin_insn,
657 rw,
658 op,
659 ptr,
660 imm,
661 );
662 }
663}
664
665#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
666#[allow(clippy::not_unsafe_ptr_arg_deref)]
667/// Register an inline callback for every memory transaction of a particular instruction.
668///
669/// # Arguments
670///
671/// - `insn`: The instruction handle to register the callback for
672/// - `rw`: Whether the callback should be called for reads, writes, or both
673/// - `op`: The operation to be performed
674/// - `entry`: The entry to be passed to the operation
675/// - `imm`: The immediate value to be passed to the operation
676pub fn qemu_plugin_register_vcpu_mem_inline_per_vcpu(
677 insn: Instruction,
678 rw: MemRW,
679 op: PluginOp,
680 entry: PluginU64,
681 imm: u64,
682) {
683 unsafe {
684 crate::sys::qemu_plugin_register_vcpu_mem_inline_per_vcpu(
685 insn.instruction as *mut qemu_plugin_insn,
686 rw,
687 op,
688 entry,
689 imm,
690 );
691 }
692}
693
694extern "C" fn handle_qemu_plugin_register_atexit_cb<F>(id: qemu_plugin_id_t, userdata: *mut c_void)
695where
696 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
697{
698 let cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
699 cb(id);
700 // NOTE: This memory is not leaked because this is the last callback to be called
701 // and it can only be called once, so we allow it to drop
702}
703
704/// Register a callback to run once execution is finished. Plugins should be able to free all
705/// their resources at this point.
706///
707/// # Arguments
708///
709/// - `id`: The plugin ID
710/// - `cb`: The callback to be called
711pub fn qemu_plugin_register_atexit_cb<F>(id: qemu_plugin_id_t, cb: F) -> Result<()>
712where
713 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
714{
715 let callback = Box::new(cb);
716 let callback_box = Box::new(callback);
717 unsafe {
718 crate::sys::qemu_plugin_register_atexit_cb(
719 id,
720 Some(handle_qemu_plugin_register_atexit_cb::<F>),
721 Box::into_raw(callback_box) as *mut c_void,
722 )
723 };
724 Ok(())
725}
726
727/// Register a callback to run after QEMU flushes all translation blocks. This is
728/// roughly equivalent to a TLB flush, where all instruction caches are invalidated.
729///
730/// # Arguments
731///
732/// - `id`: The plugin ID
733/// - `cb`: The callback to be called
734pub fn qemu_plugin_register_flush_cb(id: qemu_plugin_id_t, cb: FlushCallback) {
735 unsafe { crate::sys::qemu_plugin_register_flush_cb(id, cb) };
736}
737
738/// Register a callback to run on Syscall
739///
740/// # Arguments
741///
742/// - `id`: The plugin ID
743/// - `cb`: The callback to be called
744pub fn qemu_plugin_register_vcpu_syscall_cb(id: qemu_plugin_id_t, cb: SyscallCallback) {
745 unsafe { crate::sys::qemu_plugin_register_vcpu_syscall_cb(id, cb) };
746}
747
748/// Register a callback to run on Syscall return
749///
750/// # Arguments
751///
752/// - `id`: The plugin ID
753/// - `cb`: The callback to be called
754pub fn qemu_plugin_register_vcpu_syscall_ret_cb(id: qemu_plugin_id_t, cb: SyscallReturnCallback) {
755 unsafe { crate::sys::qemu_plugin_register_vcpu_syscall_ret_cb(id, cb) };
756}
757
758/// Output a string via the QEMU logging mechanism
759pub fn qemu_plugin_outs<S>(string: S) -> Result<()>
760where
761 S: AsRef<str>,
762{
763 unsafe {
764 crate::sys::qemu_plugin_outs(CString::new(string.as_ref())?.as_ptr());
765 }
766
767 Ok(())
768}
769
770#[cfg(feature = "plugin-api-v0")]
771/// Parse a boolean argument in the form of `=[on|yes|true|off|no|false]`.
772/// returns true if the combination @name=@val parses correctly to a boolean
773/// argument, and false otherwise. Note that in plugin API v0, this function is
774/// not available, so we implement it ourselves. This may ostensibly lead to
775/// different behavior.
776///
777/// # Arguments
778///
779/// - `name`: argument name, the part before the equals sign @val: argument
780/// value, what’s after the equals sign @ret: output return value
781/// - `val`: Argument value, what’s after the equals sign
782///
783pub fn qemu_plugin_bool_parse<S>(name: S, val: S) -> Result<bool>
784where
785 S: AsRef<str>,
786{
787 // We can't call sys::qemu_plugin_bool_parse directly because it doesn't exist in plugin-api-v0
788 match val.as_ref() {
789 "on" | "yes" | "true" => Ok(true),
790 "off" | "no" | "false" => Ok(false),
791 _ => Err(Error::InvalidBool {
792 name: name.as_ref().to_string(),
793 val: val.as_ref().to_string(),
794 }),
795 }
796}
797
798#[cfg(not(feature = "plugin-api-v0"))]
799/// Parse a boolean argument in the form of `=[on|yes|true|off|no|false]`. returns true
800/// if the combination @name=@val parses correctly to a boolean argument, and false
801/// otherwise.
802///
803/// # Arguments
804///
805/// - `name`: argument name, the part before the equals sign @val: argument value, what’s
806/// after the equals sign @ret: output return value
807/// - `val`: Argument value, what’s after the equals sign
808///
809pub fn qemu_plugin_bool_parse<S>(name: S, val: S) -> Result<bool>
810where
811 S: AsRef<str>,
812{
813 let mut value = false;
814 if unsafe {
815 crate::sys::qemu_plugin_bool_parse(
816 CString::new(name.as_ref())?.as_ptr(),
817 CString::new(val.as_ref())?.as_ptr(),
818 &mut value,
819 )
820 } {
821 Ok(value)
822 } else {
823 Err(Error::InvalidBool {
824 name: name.as_ref().to_string(),
825 val: val.as_ref().to_string(),
826 })
827 }
828}
829
830#[cfg(not(feature = "plugin-api-v0"))]
831/// Return the path to the binary file being executed if running in user mode,
832/// or None if running in System mode. Return an error if the path cannot be
833/// converted to a string.
834pub fn qemu_plugin_path_to_binary() -> Result<Option<PathBuf>> {
835 let path_str = unsafe { crate::sys::qemu_plugin_path_to_binary() };
836 if path_str.is_null() {
837 Ok(None)
838 } else {
839 let path = unsafe { PathBuf::from(CStr::from_ptr(path_str).to_str()?) };
840 unsafe { g_free(path_str as *mut _) };
841 Ok(Some(path))
842 }
843}
844
845#[cfg(not(feature = "plugin-api-v0"))]
846/// Return the start of the text segment of the binary file being executed if
847/// running in user mode, or None if running in System mode. If not running in
848/// system mode, `None` may be interpreted as zero by callers, but the caller
849/// must take care to ensure the plugin is not running in a system mode context.
850pub fn qemu_plugin_start_code() -> Option<u64> {
851 let start = unsafe { crate::sys::qemu_plugin_start_code() };
852
853 if start == 0 { None } else { Some(start) }
854}
855
856#[cfg(not(feature = "plugin-api-v0"))]
857/// Return the end of the text segment of the binary file being executed if
858/// running in user mode, or None if running in System mode. If not running in
859/// system mode, `None` may be interpreted as zero by callers, but the caller
860/// must take care to ensure the plugin is not running in a system mode context.
861pub fn qemu_plugin_end_code() -> Option<u64> {
862 let end = unsafe { crate::sys::qemu_plugin_end_code() };
863
864 if end == 0 { None } else { Some(end) }
865}
866
867#[cfg(not(feature = "plugin-api-v0"))]
868/// Return the start address for the module of the binary file being executed if
869/// running in user mode, or None if running in System mode. If not running in
870/// system mode, `None` may be interpreted as zero by callers, but the caller
871/// must take care to ensure the plugin is not running in a system mode context.
872pub fn qemu_plugin_entry_code() -> Option<u64> {
873 let entry = unsafe { crate::sys::qemu_plugin_entry_code() };
874
875 if entry == 0 { None } else { Some(entry) }
876}
877
878#[cfg(any(feature = "plugin-api-v0", feature = "plugin-api-v1"))]
879/// Return the number of vCPUs, if running in system mode
880pub fn qemu_plugin_n_vcpus() -> Option<i32> {
881 let vcpus = unsafe { crate::sys::qemu_plugin_n_vcpus() };
882
883 if vcpus == -1 { None } else { Some(vcpus) }
884}
885
886#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
887/// Return the number of vCPUs, if running in system mode
888pub fn qemu_plugin_num_vcpus() -> Option<i32> {
889 let vcpus = unsafe { crate::sys::qemu_plugin_num_vcpus() };
890
891 if vcpus == -1 { None } else { Some(vcpus) }
892}
893
894#[cfg(any(feature = "plugin-api-v0", feature = "plugin-api-v1"))]
895/// Return the maximum number of vCPUs, if running in system mode
896pub fn qemu_plugin_n_max_vcpus() -> Option<i32> {
897 let max_cpus = unsafe { crate::sys::qemu_plugin_n_max_vcpus() };
898
899 if max_cpus == -1 { None } else { Some(max_cpus) }
900}
901
902#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
903/// Returns a potentially empty list of registers. This should be used from a
904/// qemu_plugin_register_vcpu_init_cb callback after the vcpu has been initialized.
905pub fn qemu_plugin_get_registers<'a>() -> Result<Vec<RegisterDescriptor<'a>>> {
906 use std::slice::from_raw_parts;
907
908 let array = unsafe { crate::sys::qemu_plugin_get_registers() };
909
910 let registers = unsafe {
911 from_raw_parts(
912 (*array).data as *mut qemu_plugin_reg_descriptor,
913 (*array).len as usize,
914 )
915 }
916 .iter()
917 .map(|desc| RegisterDescriptor::from(*desc))
918 .collect::<Vec<_>>();
919
920 // Function notes say caller frees the array but not the strings in each entry
921 assert_eq!(
922 unsafe { g_array_free(array, true) },
923 std::ptr::null_mut(),
924 "g_array_free return value must be NULL"
925 );
926
927 Ok(registers)
928}
929
930#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
931/// Add a value to a `PluginU64` for a given VCPU
932pub fn qemu_plugin_u64_add(entry: PluginU64, vcpu_index: VCPUIndex, added: u64) -> Result<()> {
933 unsafe { crate::sys::qemu_plugin_u64_add(entry, vcpu_index, added) };
934 Ok(())
935}
936
937#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
938/// Get the value of a `PluginU64` for a given VCPU
939pub fn qemu_plugin_u64_get(entry: PluginU64, vcpu_index: VCPUIndex) -> u64 {
940 unsafe { crate::sys::qemu_plugin_u64_get(entry, vcpu_index) }
941}
942
943#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
944/// Set the value of a `PluginU64` for a given VCPU
945pub fn qemu_plugin_u64_set(entry: PluginU64, vcpu_index: VCPUIndex, value: u64) {
946 unsafe { crate::sys::qemu_plugin_u64_set(entry, vcpu_index, value) }
947}
948
949#[cfg(not(any(feature = "plugin-api-v0", feature = "plugin-api-v1")))]
950/// Get the sum of all VCPU entries in a scoreboard
951pub fn qemu_plugin_scoreboard_sum(entry: PluginU64) -> u64 {
952 unsafe { crate::sys::qemu_plugin_u64_sum(entry) }
953}