use std::sync::{Mutex, OnceLock};
use crate::{
Args, Error, Info, PluginId, Result, TranslationBlock, VCPUIndex,
qemu_plugin_register_flush_cb, qemu_plugin_register_vcpu_exit_cb,
qemu_plugin_register_vcpu_idle_cb, qemu_plugin_register_vcpu_init_cb,
qemu_plugin_register_vcpu_resume_cb, qemu_plugin_register_vcpu_syscall_cb,
qemu_plugin_register_vcpu_syscall_ret_cb, qemu_plugin_register_vcpu_tb_trans_cb,
};
extern "C" fn handle_qemu_plugin_register_vcpu_init_cb(id: PluginId, vcpu_id: VCPUIndex) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_vcpu_init(id, vcpu_id)
.expect("Failed running callback on_vcpu_init");
}
extern "C" fn handle_qemu_plugin_register_vcpu_exit_cb(id: PluginId, vcpu_id: VCPUIndex) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_vcpu_exit(id, vcpu_id)
.expect("Failed running callback on_vcpu_exit");
}
extern "C" fn handle_qemu_plugin_register_vcpu_idle_cb(id: PluginId, vcpu_id: VCPUIndex) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_vcpu_idle(id, vcpu_id)
.expect("Failed running callback on_vcpu_idle");
}
extern "C" fn handle_qemu_plugin_register_vcpu_resume_cb(id: PluginId, vcpu_id: VCPUIndex) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_vcpu_resume(id, vcpu_id)
.expect("Failed running callback on_vcpu_resume");
}
extern "C" fn handle_qemu_plugin_register_vcpu_tb_trans_cb(
id: PluginId,
tb: *mut crate::sys::qemu_plugin_tb,
) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
let tb = TranslationBlock::from(tb);
plugin
.on_translation_block_translate(id, tb)
.expect("Failed running callback on_translation_block_translate");
}
extern "C" fn handle_qemu_plugin_register_flush_cb(id: PluginId) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_flush(id)
.expect("Failed running callback on_flush");
}
extern "C" fn handle_qemu_plugin_register_syscall_cb(
id: PluginId,
vcpu_index: VCPUIndex,
num: i64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_syscall(id, vcpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8)
.expect("Failed running callback on_syscall");
}
extern "C" fn handle_qemu_plugin_register_syscall_ret_cb(
id: PluginId,
vcpu_index: VCPUIndex,
num: i64,
ret: i64,
) {
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.on_syscall_return(id, vcpu_index, num, ret)
.expect("Failed running callback on_syscall_return");
}
pub trait Register: HasCallbacks + Send + Sync + 'static {
#[allow(unused)]
fn register_default(&mut self, id: PluginId, args: &Args, info: &Info) -> Result<()> {
qemu_plugin_register_vcpu_init_cb(id, Some(handle_qemu_plugin_register_vcpu_init_cb))?;
qemu_plugin_register_vcpu_exit_cb(id, Some(handle_qemu_plugin_register_vcpu_exit_cb))?;
qemu_plugin_register_vcpu_idle_cb(id, Some(handle_qemu_plugin_register_vcpu_idle_cb))?;
qemu_plugin_register_vcpu_resume_cb(id, Some(handle_qemu_plugin_register_vcpu_resume_cb))?;
qemu_plugin_register_vcpu_tb_trans_cb(
id,
Some(handle_qemu_plugin_register_vcpu_tb_trans_cb),
)?;
qemu_plugin_register_flush_cb(id, Some(handle_qemu_plugin_register_flush_cb));
qemu_plugin_register_vcpu_syscall_cb(id, Some(handle_qemu_plugin_register_syscall_cb));
qemu_plugin_register_vcpu_syscall_ret_cb(
id,
Some(handle_qemu_plugin_register_syscall_ret_cb),
);
self.register(id, args, info)?;
Ok(())
}
#[allow(unused)]
fn register(&mut self, id: PluginId, args: &Args, info: &Info) -> Result<()> {
Ok(())
}
}
pub trait HasCallbacks: Send + Sync + 'static {
#[allow(unused)]
fn on_vcpu_init(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
Ok(())
}
#[allow(unused)]
fn on_vcpu_exit(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
Ok(())
}
#[allow(unused)]
fn on_vcpu_idle(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
Ok(())
}
#[allow(unused)]
fn on_vcpu_resume(&mut self, id: PluginId, vcpu_id: VCPUIndex) -> Result<()> {
Ok(())
}
#[allow(unused)]
fn on_translation_block_translate(&mut self, id: PluginId, tb: TranslationBlock) -> Result<()> {
Ok(())
}
#[allow(unused)]
fn on_flush(&mut self, id: PluginId) -> Result<()> {
Ok(())
}
#[allow(unused, clippy::too_many_arguments)]
fn on_syscall(
&mut self,
id: PluginId,
vcpu_index: VCPUIndex,
num: i64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
) -> Result<()> {
Ok(())
}
#[allow(unused)]
fn on_syscall_return(
&mut self,
id: PluginId,
vcpu_index: VCPUIndex,
num: i64,
ret: i64,
) -> Result<()> {
Ok(())
}
}
pub trait Plugin: Register + HasCallbacks {}
impl<T> Plugin for T where T: Register + HasCallbacks {}
#[doc(hidden)]
pub static PLUGIN: OnceLock<Mutex<Box<dyn Plugin>>> = OnceLock::new();
#[doc(hidden)]
#[inline(never)]
pub fn register_plugin(plugin: impl Plugin) {
PLUGIN
.set(Mutex::new(Box::new(plugin)))
.map_err(|_| Error::PluginInstanceSetError)
.expect("Failed to set plugin");
}
#[macro_export]
macro_rules! register {
($plugin:expr) => {
#[cfg_attr(target_os = "linux", unsafe(link_section = ".text.startup"))]
extern "C" fn __plugin_ctor() {
$crate::plugin::register_plugin($plugin);
}
#[used]
#[cfg_attr(target_os = "linux", unsafe(link_section = ".init_array"))]
#[cfg_attr(
target_os = "macos",
unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs")
)]
#[cfg_attr(windows, unsafe(link_section = ".CRT$XCU"))]
static __PLUGIN_CTOR: unsafe extern "C" fn() = __plugin_ctor;
};
}