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}