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