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}