qemu_plugin/plugin/
mod.rs

1//! Traits and helpers enabling idiomatic QEMU plugin implementation
2
3use std::sync::{Mutex, OnceLock};
4
5use crate::{
6    install::{Args, Info},
7    PluginId, TranslationBlock, VCPUIndex,
8};
9use crate::{
10    qemu_plugin_register_flush_cb, qemu_plugin_register_vcpu_exit_cb,
11    qemu_plugin_register_vcpu_idle_cb, qemu_plugin_register_vcpu_init_cb,
12    qemu_plugin_register_vcpu_resume_cb, qemu_plugin_register_vcpu_syscall_cb,
13    qemu_plugin_register_vcpu_syscall_ret_cb, qemu_plugin_register_vcpu_tb_trans_cb,
14};
15
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
30extern "C" fn handle_qemu_plugin_register_vcpu_exit_cb(id: PluginId, vcpu_id: VCPUIndex) {
31    let Some(plugin) = PLUGIN.get() else {
32        panic!("Plugin not set");
33    };
34
35    let Ok(mut plugin) = plugin.lock() else {
36        panic!("Failed to lock plugin");
37    };
38
39    plugin
40        .on_vcpu_exit(id, vcpu_id)
41        .expect("Failed running callback on_vcpu_exit");
42}
43
44extern "C" fn handle_qemu_plugin_register_vcpu_idle_cb(id: PluginId, vcpu_id: VCPUIndex) {
45    let Some(plugin) = PLUGIN.get() else {
46        panic!("Plugin not set");
47    };
48
49    let Ok(mut plugin) = plugin.lock() else {
50        panic!("Failed to lock plugin");
51    };
52
53    plugin
54        .on_vcpu_idle(id, vcpu_id)
55        .expect("Failed running callback on_vcpu_idle");
56}
57
58extern "C" fn handle_qemu_plugin_register_vcpu_resume_cb(id: PluginId, vcpu_id: VCPUIndex) {
59    let Some(plugin) = PLUGIN.get() else {
60        panic!("Plugin not set");
61    };
62
63    let Ok(mut plugin) = plugin.lock() else {
64        panic!("Failed to lock plugin");
65    };
66
67    plugin
68        .on_vcpu_resume(id, vcpu_id)
69        .expect("Failed running callback on_vcpu_resume");
70}
71
72extern "C" fn handle_qemu_plugin_register_vcpu_tb_trans_cb(
73    id: PluginId,
74    tb: *mut crate::sys::qemu_plugin_tb,
75) {
76    let Some(plugin) = PLUGIN.get() else {
77        panic!("Plugin not set");
78    };
79
80    let Ok(mut plugin) = plugin.lock() else {
81        panic!("Failed to lock plugin");
82    };
83
84    let tb = TranslationBlock::from(tb);
85
86    plugin
87        .on_translation_block_translate(id, tb)
88        .expect("Failed running callback on_translation_block_translate");
89}
90
91extern "C" fn handle_qemu_plugin_register_flush_cb(id: PluginId) {
92    let Some(plugin) = PLUGIN.get() else {
93        panic!("Plugin not set");
94    };
95
96    let Ok(mut plugin) = plugin.lock() else {
97        panic!("Failed to lock plugin");
98    };
99
100    plugin
101        .on_flush(id)
102        .expect("Failed running callback on_flush");
103}
104
105extern "C" fn handle_qemu_plugin_register_syscall_cb(
106    id: PluginId,
107    vcpu_index: VCPUIndex,
108    num: i64,
109    a1: u64,
110    a2: u64,
111    a3: u64,
112    a4: u64,
113    a5: u64,
114    a6: u64,
115    a7: u64,
116    a8: u64,
117) {
118    let Some(plugin) = PLUGIN.get() else {
119        panic!("Plugin not set");
120    };
121
122    let Ok(mut plugin) = plugin.lock() else {
123        panic!("Failed to lock plugin");
124    };
125
126    plugin
127        .on_syscall(id, vcpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8)
128        .expect("Failed running callback on_syscall");
129}
130
131extern "C" fn handle_qemu_plugin_register_syscall_ret_cb(
132    id: PluginId,
133    vcpu_index: VCPUIndex,
134    num: i64,
135    ret: i64,
136) {
137    let Some(plugin) = PLUGIN.get() else {
138        panic!("Plugin not set");
139    };
140
141    let Ok(mut plugin) = plugin.lock() else {
142        panic!("Failed to lock plugin");
143    };
144
145    plugin
146        .on_syscall_return(id, vcpu_index, num, ret)
147        .expect("Failed running callback on_syscall_return");
148}
149
150/// Trait which implemenents registering the callbacks implemented on a struct which
151/// `HasCallbacks` with QEMU
152pub trait Register: HasCallbacks + Send + Sync + 'static {
153    #[allow(unused)]
154    /// Called by QEMu when registering the plugin. This method should only be overridden if no
155    /// default callbacks are desired, and will require re-implementing handlers which is not
156    /// recommended.
157    fn register_default(
158        &mut self,
159        id: PluginId,
160        args: &Args,
161        info: &Info,
162    ) -> Result<(), anyhow::Error> {
163        qemu_plugin_register_vcpu_init_cb(id, Some(handle_qemu_plugin_register_vcpu_init_cb))?;
164
165        qemu_plugin_register_vcpu_exit_cb(id, Some(handle_qemu_plugin_register_vcpu_exit_cb))?;
166
167        qemu_plugin_register_vcpu_idle_cb(id, Some(handle_qemu_plugin_register_vcpu_idle_cb))?;
168
169        qemu_plugin_register_vcpu_resume_cb(id, Some(handle_qemu_plugin_register_vcpu_resume_cb))?;
170
171        qemu_plugin_register_vcpu_tb_trans_cb(
172            id,
173            Some(handle_qemu_plugin_register_vcpu_tb_trans_cb),
174        )?;
175
176        qemu_plugin_register_flush_cb(id, Some(handle_qemu_plugin_register_flush_cb));
177
178        qemu_plugin_register_vcpu_syscall_cb(id, Some(handle_qemu_plugin_register_syscall_cb));
179
180        qemu_plugin_register_vcpu_syscall_ret_cb(
181            id,
182            Some(handle_qemu_plugin_register_syscall_ret_cb),
183        );
184
185        self.register(id, args, info)?;
186
187        Ok(())
188    }
189
190    #[allow(unused)]
191    /// Called when registering the plugin. User definition of on-registration behavior should
192    /// be implemented here.
193    fn register(&mut self, id: PluginId, args: &Args, info: &Info) -> Result<(), anyhow::Error> {
194        Ok(())
195    }
196}
197
198/// Trait implemented by structs which have callbacks which should be registered with QEMU
199pub trait HasCallbacks: Send + Sync + 'static {
200    #[allow(unused)]
201    /// Callback triggered on vCPU init
202    ///
203    /// # Arguments
204    ///
205    /// * `id` - The ID of the plugin
206    /// * `vcpu_id` - The ID of the vCPU
207    fn on_vcpu_init(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<(), anyhow::Error> {
208        Ok(())
209    }
210
211    #[allow(unused)]
212    /// Callback triggered on vCPU exit
213    ///
214    /// # Arguments
215    ///
216    /// * `id` - The ID of the plugin
217    /// * `vcpu_id` - The ID of the vCPU
218    fn on_vcpu_exit(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<(), anyhow::Error> {
219        Ok(())
220    }
221
222    #[allow(unused)]
223    /// Callback triggered on vCPU idle
224    ///
225    /// # Arguments
226    ///
227    /// * `id` - The ID of the plugin
228    /// * `vcpu_id` - The ID of the vCPU
229    fn on_vcpu_idle(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<(), anyhow::Error> {
230        Ok(())
231    }
232
233    #[allow(unused)]
234    /// Callback triggered on vCPU resume
235    ///
236    /// # Arguments
237    ///
238    /// * `id` - The ID of the plugin
239    /// * `vcpu_id` - The ID of the vCPU
240    fn on_vcpu_resume(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<(), anyhow::Error> {
241        Ok(())
242    }
243
244    #[allow(unused)]
245    /// Callback triggered on translation block translation
246    ///
247    /// # Arguments
248    ///
249    /// * `id` - The ID of the plugin
250    /// * `tb` - The translation block
251    fn on_translation_block_translate(
252        &mut self,
253        id: PluginId,
254        tb: TranslationBlock,
255    ) -> Result<(), anyhow::Error> {
256        Ok(())
257    }
258
259    #[allow(unused)]
260    /// Callback triggered on flush
261    ///
262    /// # Arguments
263    ///
264    /// * `id` - The ID of the plugin
265    fn on_flush(&mut self, id: PluginId) -> Result<(), anyhow::Error> {
266        Ok(())
267    }
268
269    #[allow(unused, clippy::too_many_arguments)]
270    /// Callback triggered on syscall
271    ///
272    /// # Arguments
273    ///
274    /// * `id` - The ID of the plugin
275    /// * `vcpu_index` - The ID of the vCPU
276    /// * `num` - The syscall number
277    /// * `a1` - The first syscall argument
278    /// * `a2` - The second syscall argument
279    /// * `a3` - The third syscall argument
280    /// * `a4` - The fourth syscall argument
281    /// * `a5` - The fifth syscall argument
282    /// * `a6` - The sixth syscall argument
283    /// * `a7` - The seventh syscall argument
284    /// * `a8` - The eighth syscall argument
285    fn on_syscall(
286        &mut self,
287        id: PluginId,
288        vcpu_index: VCPUIndex,
289        num: i64,
290        a1: u64,
291        a2: u64,
292        a3: u64,
293        a4: u64,
294        a5: u64,
295        a6: u64,
296        a7: u64,
297        a8: u64,
298    ) -> Result<(), anyhow::Error> {
299        Ok(())
300    }
301
302    #[allow(unused)]
303    /// Callback triggered on syscall return
304    ///
305    /// # Arguments
306    ///
307    /// * `id` - The ID of the plugin
308    /// * `vcpu_index` - The ID of the vCPU
309    /// * `num` - The syscall number
310    /// * `ret` - The return value of the syscall
311    fn on_syscall_return(
312        &mut self,
313        id: PluginId,
314        vcpu_index: VCPUIndex,
315        num: i64,
316        ret: i64,
317    ) -> Result<(), anyhow::Error> {
318        Ok(())
319    }
320}
321
322/// Trait implemented by structs which are QEMU plugin contexts
323pub trait Plugin: Register + HasCallbacks {}
324
325/// The global plugin item
326pub static PLUGIN: OnceLock<Mutex<Box<dyn Plugin>>> = OnceLock::new();