qemu_plugin/plugin/
mod.rs

1//! Traits and helpers enabling idiomatic QEMU plugin implementation
2
3use std::sync::{Mutex, OnceLock};
4
5use crate::{
6    Args, Error, Info, PluginId, Result, TranslationBlock, VCPUIndex,
7    qemu_plugin_register_flush_cb, qemu_plugin_register_vcpu_exit_cb,
8    qemu_plugin_register_vcpu_idle_cb, qemu_plugin_register_vcpu_init_cb,
9    qemu_plugin_register_vcpu_resume_cb, qemu_plugin_register_vcpu_syscall_cb,
10    qemu_plugin_register_vcpu_syscall_ret_cb, qemu_plugin_register_vcpu_tb_trans_cb,
11};
12
13/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_init_cb`
14/// function. These callbacks are called when a vCPU is initialized in QEMU (in softmmu
15/// mode only) and notify us which vCPU index is newly initialized.
16extern "C" fn handle_qemu_plugin_register_vcpu_init_cb(id: PluginId, vcpu_id: VCPUIndex) {
17    let Some(plugin) = PLUGIN.get() else {
18        panic!("Plugin not set");
19    };
20
21    let Ok(mut plugin) = plugin.lock() else {
22        panic!("Failed to lock plugin");
23    };
24
25    plugin
26        .on_vcpu_init(id, vcpu_id)
27        .expect("Failed running callback on_vcpu_init");
28}
29
30/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_exit_cb`
31/// function. These callbacks are called when a vCPU exits in QEMU (in softmmu mode
32/// only) and notify us which vCPU index is exiting.
33extern "C" fn handle_qemu_plugin_register_vcpu_exit_cb(id: PluginId, vcpu_id: VCPUIndex) {
34    let Some(plugin) = PLUGIN.get() else {
35        panic!("Plugin not set");
36    };
37
38    let Ok(mut plugin) = plugin.lock() else {
39        panic!("Failed to lock plugin");
40    };
41
42    plugin
43        .on_vcpu_exit(id, vcpu_id)
44        .expect("Failed running callback on_vcpu_exit");
45}
46
47/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_idle_cb`
48/// function. These callbacks are called when a vCPU goes idle in QEMU (in softmmu mode
49/// only) and notify us which vCPU index is going idle.
50extern "C" fn handle_qemu_plugin_register_vcpu_idle_cb(id: PluginId, vcpu_id: VCPUIndex) {
51    let Some(plugin) = PLUGIN.get() else {
52        panic!("Plugin not set");
53    };
54
55    let Ok(mut plugin) = plugin.lock() else {
56        panic!("Failed to lock plugin");
57    };
58
59    plugin
60        .on_vcpu_idle(id, vcpu_id)
61        .expect("Failed running callback on_vcpu_idle");
62}
63
64/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_resume_cb`
65/// function. These callbacks are called when a vCPU resumes in QEMU (in softmmu mode
66/// only) and notify us which vCPU index is resuming.
67extern "C" fn handle_qemu_plugin_register_vcpu_resume_cb(id: PluginId, vcpu_id: VCPUIndex) {
68    let Some(plugin) = PLUGIN.get() else {
69        panic!("Plugin not set");
70    };
71
72    let Ok(mut plugin) = plugin.lock() else {
73        panic!("Failed to lock plugin");
74    };
75
76    plugin
77        .on_vcpu_resume(id, vcpu_id)
78        .expect("Failed running callback on_vcpu_resume");
79}
80
81/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_tb_trans_cb`
82/// function. These callbacks are called when a translation block is translated in QEMU
83/// and pass an opaque pointer to the translation block.
84extern "C" fn handle_qemu_plugin_register_vcpu_tb_trans_cb(
85    id: PluginId,
86    tb: *mut crate::sys::qemu_plugin_tb,
87) {
88    let Some(plugin) = PLUGIN.get() else {
89        panic!("Plugin not set");
90    };
91
92    let Ok(mut plugin) = plugin.lock() else {
93        panic!("Failed to lock plugin");
94    };
95
96    let tb = TranslationBlock::from(tb);
97
98    plugin
99        .on_translation_block_translate(id, tb)
100        .expect("Failed running callback on_translation_block_translate");
101}
102
103/// Handler for callbacks registered via the `qemu_plugin_register_flush_cb`
104/// function. These callbacks are called when QEMU flushes all TBs, which is
105/// roughly equivalent to a TLB flush to invalidate all cached instructions.
106extern "C" fn handle_qemu_plugin_register_flush_cb(id: PluginId) {
107    let Some(plugin) = PLUGIN.get() else {
108        panic!("Plugin not set");
109    };
110
111    let Ok(mut plugin) = plugin.lock() else {
112        panic!("Failed to lock plugin");
113    };
114
115    plugin
116        .on_flush(id)
117        .expect("Failed running callback on_flush");
118}
119
120/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_syscall_cb`
121/// function. These callbacks are called when a syscall is made in QEMU and pass the
122/// syscall number and its arguments.
123extern "C" fn handle_qemu_plugin_register_syscall_cb(
124    id: PluginId,
125    vcpu_index: VCPUIndex,
126    num: i64,
127    a1: u64,
128    a2: u64,
129    a3: u64,
130    a4: u64,
131    a5: u64,
132    a6: u64,
133    a7: u64,
134    a8: u64,
135) {
136    let Some(plugin) = PLUGIN.get() else {
137        panic!("Plugin not set");
138    };
139
140    let Ok(mut plugin) = plugin.lock() else {
141        panic!("Failed to lock plugin");
142    };
143
144    plugin
145        .on_syscall(id, vcpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8)
146        .expect("Failed running callback on_syscall");
147}
148
149/// Handler for callbacks registered via the `qemu_plugin_register_vcpu_syscall_ret_cb`
150/// function. These callbacks are called when a syscall returns in QEMU and pass the
151/// syscall number and its return value.
152extern "C" fn handle_qemu_plugin_register_syscall_ret_cb(
153    id: PluginId,
154    vcpu_index: VCPUIndex,
155    num: i64,
156    ret: i64,
157) {
158    let Some(plugin) = PLUGIN.get() else {
159        panic!("Plugin not set");
160    };
161
162    let Ok(mut plugin) = plugin.lock() else {
163        panic!("Failed to lock plugin");
164    };
165
166    plugin
167        .on_syscall_return(id, vcpu_index, num, ret)
168        .expect("Failed running callback on_syscall_return");
169}
170
171/// Trait which implemenents registering the callbacks implemented on a struct which
172/// `HasCallbacks` with QEMU
173///
174/// # Example
175///
176/// Using default registration, you can simply declare an empty `impl` block for
177/// `Register` on your type. Then, on plugin load, any callbacks you implement in
178/// `HasCallbacks` will be automatically registered with QEMU. Callback events you don't
179/// implement will default to no-ops. The only drawback of this approach is a small
180/// performance penalty for events even when there is no callback registered for them.
181///
182/// ```
183/// struct MyPlugin;
184///
185/// impl qemu_plugin::plugin::HasCallbacks for MyPlugin {
186///     fn on_translation_block_translate(&mut self, _: qemu_plugin::PluginId, tb: qemu_plugin::TranslationBlock) -> qemu_plugin::Result<()> {
187///         println!("Translation block translated");
188///         Ok(())
189///     }
190/// }
191///
192/// impl qemu_plugin::plugin::Register for MyPlugin {}
193/// ```
194///
195/// For more granular control or to register your own callback handlers, you can
196/// implement the `register` method yourself.
197///
198/// ```
199/// struct MyPlugin;
200///
201/// impl qemu_plugin::plugin::HasCallbacks for MyPlugin {}
202/// impl qemu_plugin::plugin::Register for MyPlugin {
203///    fn register(&mut self, id: qemu_plugin::PluginId, args: &qemu_plugin::install::Args, info: &qemu_plugin::install::Info) -> Result<(), qemu_plugin::Error> {
204///       // Custom registration logic here
205///       Ok(())
206///    }
207/// }
208/// ```
209///
210/// Finally, if you want to override the default registration behavior, you can
211/// implement `register_default` yourself. This allows you to circumvent any minor
212/// performance penalties.
213///
214/// ```
215/// struct MyPlugin;
216///
217/// impl qemu_plugin::plugin::HasCallbacks for MyPlugin {}
218/// impl qemu_plugin::plugin::Register for MyPlugin {
219///     fn register_default(
220///        &mut self,   
221///        id: qemu_plugin::PluginId,
222///        args: &qemu_plugin::install::Args,
223///        info: &qemu_plugin::install::Info,
224///     ) -> Result<()> {
225///         // Custom registration logic here, maybe registering a different
226///         // function as a callback rather than using `HasCallbacks`
227///         Ok(())
228///    }
229/// }
230/// ```
231///
232pub trait Register: HasCallbacks + Send + Sync + 'static {
233    #[allow(unused)]
234    /// Called by QEMU when registering the plugin. This method should only be overridden if no
235    /// default callbacks are desired, and will require re-implementing handlers which is not
236    /// recommended.
237    fn register_default(&mut self, id: PluginId, args: &Args, info: &Info) -> Result<()> {
238        qemu_plugin_register_vcpu_init_cb(id, Some(handle_qemu_plugin_register_vcpu_init_cb))?;
239
240        qemu_plugin_register_vcpu_exit_cb(id, Some(handle_qemu_plugin_register_vcpu_exit_cb))?;
241
242        qemu_plugin_register_vcpu_idle_cb(id, Some(handle_qemu_plugin_register_vcpu_idle_cb))?;
243
244        qemu_plugin_register_vcpu_resume_cb(id, Some(handle_qemu_plugin_register_vcpu_resume_cb))?;
245
246        qemu_plugin_register_vcpu_tb_trans_cb(
247            id,
248            Some(handle_qemu_plugin_register_vcpu_tb_trans_cb),
249        )?;
250
251        qemu_plugin_register_flush_cb(id, Some(handle_qemu_plugin_register_flush_cb));
252
253        qemu_plugin_register_vcpu_syscall_cb(id, Some(handle_qemu_plugin_register_syscall_cb));
254
255        qemu_plugin_register_vcpu_syscall_ret_cb(
256            id,
257            Some(handle_qemu_plugin_register_syscall_ret_cb),
258        );
259
260        self.register(id, args, info)?;
261
262        Ok(())
263    }
264
265    #[allow(unused)]
266    /// Called when registering the plugin. User definition of on-registration behavior should
267    /// be implemented here.
268    fn register(&mut self, id: PluginId, args: &Args, info: &Info) -> Result<()> {
269        Ok(())
270    }
271}
272
273/// Trait implemented by structs which have callbacks which should be registered with QEMU.
274///
275/// # Example
276///
277/// ```
278/// struct MyPlugin;
279///
280/// impl qemu_plugin::plugin::HasCallbacks for MyPlugin {
281///     // This callback will be registered on plugin load
282///     fn on_translation_block_translate(&mut self, _: qemu_plugin::PluginId, tb: qemu_plugin::TranslationBlock) -> qemu_plugin::Result<()> {
283///         println!("Translation block translated");
284///         Ok(())
285///     }
286/// }
287///
288/// impl qemu_plugin::plugin::Register for MyPlugin {}
289/// ```
290pub trait HasCallbacks: Send + Sync + 'static {
291    #[allow(unused)]
292    /// Callback triggered on vCPU init
293    ///
294    /// # Arguments
295    ///
296    /// * `id` - The ID of the plugin
297    /// * `vcpu_id` - The ID of the vCPU
298    ///
299    /// # Example
300    ///
301    /// ```
302    /// struct MyPlugin;
303    ///
304    /// impl qemu_plugin::plugin::HasCallbacks for MyPlugin {
305    ///     fn on_vcpu_init(&mut self, id: qemu_plugin::PluginId, vcpu_id: qemu_plugin::VCPUIndex) -> Result<(), qemu_plugin::Error> {
306    ///         println!("vCPU {} initialized for plugin {}", vcpu_id, id);
307    ///         Ok(())
308    ///     }
309    /// }
310    /// ```
311    /// struct MyPlugin;
312    fn on_vcpu_init(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
313        Ok(())
314    }
315
316    #[allow(unused)]
317    /// Callback triggered on vCPU exit
318    ///
319    /// # Arguments
320    ///
321    /// * `id` - The ID of the plugin
322    /// * `vcpu_id` - The ID of the vCPU
323    ///
324    /// # Example
325    fn on_vcpu_exit(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
326        Ok(())
327    }
328
329    #[allow(unused)]
330    /// Callback triggered on vCPU idle
331    ///
332    /// # Arguments
333    ///
334    /// * `id` - The ID of the plugin
335    /// * `vcpu_id` - The ID of the vCPU
336    fn on_vcpu_idle(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
337        Ok(())
338    }
339
340    #[allow(unused)]
341    /// Callback triggered on vCPU resume
342    ///
343    /// # Arguments
344    ///
345    /// * `id` - The ID of the plugin
346    /// * `vcpu_id` - The ID of the vCPU
347    fn on_vcpu_resume(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
348        Ok(())
349    }
350
351    #[allow(unused)]
352    /// Callback triggered on translation block translation
353    ///
354    /// # Arguments
355    ///
356    /// * `id` - The ID of the plugin
357    /// * `tb` - The translation block
358    fn on_translation_block_translate(&mut self, id: PluginId, tb: TranslationBlock) -> Result<()> {
359        Ok(())
360    }
361
362    #[allow(unused)]
363    /// Callback triggered on flush
364    ///
365    /// # Arguments
366    ///
367    /// * `id` - The ID of the plugin
368    fn on_flush(&mut self, id: PluginId) -> Result<()> {
369        Ok(())
370    }
371
372    #[allow(unused, clippy::too_many_arguments)]
373    /// Callback triggered on syscall
374    ///
375    /// # Arguments
376    ///
377    /// * `id` - The ID of the plugin
378    /// * `vcpu_index` - The ID of the vCPU
379    /// * `num` - The syscall number
380    /// * `a1` - The first syscall argument
381    /// * `a2` - The second syscall argument
382    /// * `a3` - The third syscall argument
383    /// * `a4` - The fourth syscall argument
384    /// * `a5` - The fifth syscall argument
385    /// * `a6` - The sixth syscall argument
386    /// * `a7` - The seventh syscall argument
387    /// * `a8` - The eighth syscall argument
388    fn on_syscall(
389        &mut self,
390        id: PluginId,
391        vcpu_index: VCPUIndex,
392        num: i64,
393        a1: u64,
394        a2: u64,
395        a3: u64,
396        a4: u64,
397        a5: u64,
398        a6: u64,
399        a7: u64,
400        a8: u64,
401    ) -> Result<()> {
402        Ok(())
403    }
404
405    #[allow(unused)]
406    /// Callback triggered on syscall return
407    ///
408    /// # Arguments
409    ///
410    /// * `id` - The ID of the plugin
411    /// * `vcpu_index` - The ID of the vCPU
412    /// * `num` - The syscall number
413    /// * `ret` - The return value of the syscall
414    fn on_syscall_return(
415        &mut self,
416        id: PluginId,
417        vcpu_index: VCPUIndex,
418        num: i64,
419        ret: i64,
420    ) -> Result<()> {
421        Ok(())
422    }
423}
424
425/// Trait implemented by structs which are QEMU plugin contexts
426pub trait Plugin: Register + HasCallbacks {}
427
428impl<T> Plugin for T where T: Register + HasCallbacks {}
429
430#[doc(hidden)]
431/// The global plugin item
432pub static PLUGIN: OnceLock<Mutex<Box<dyn Plugin>>> = OnceLock::new();
433
434#[doc(hidden)]
435#[inline(never)]
436pub fn register_plugin(plugin: impl Plugin) {
437    PLUGIN
438        .set(Mutex::new(Box::new(plugin)))
439        .map_err(|_| Error::PluginInstanceSetError)
440        .expect("Failed to set plugin");
441}
442
443#[macro_export]
444/// Register a plugin
445macro_rules! register {
446    ($plugin:expr) => {
447        #[cfg_attr(target_os = "linux", unsafe(link_section = ".text.startup"))]
448        extern "C" fn __plugin_ctor() {
449            $crate::plugin::register_plugin($plugin);
450        }
451
452        #[used]
453        // .init_array.XXXXX sections are processed in lexicographical order
454        #[cfg_attr(target_os = "linux", unsafe(link_section = ".init_array"))]
455        // But there is no way to specify such an ordering on MacOS, even with
456        // __TEXT,__init_offsets
457        #[cfg_attr(
458            target_os = "macos",
459            unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs")
460        )]
461        // On Windows, it's from .CRT$XCA to .CRT$XCZ, where usually XCU =
462        // early, XCT = middle, XCL = late
463        #[cfg_attr(windows, unsafe(link_section = ".CRT$XCU"))]
464        static __PLUGIN_CTOR: unsafe extern "C" fn() = __plugin_ctor;
465    };
466}