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}