qemu_plugin/install/
mod.rs

1//! Installation for the QEMU plugin
2
3use crate::qemu_plugin_bool_parse;
4use qemu_plugin_sys::{
5    QEMU_PLUGIN_VERSION, qemu_info_t, qemu_info_t__bindgen_ty_1,
6    qemu_info_t__bindgen_ty_2__bindgen_ty_1, qemu_plugin_id_t,
7};
8use std::{
9    collections::HashMap,
10    ffi::{CStr, c_char, c_int},
11};
12
13use crate::{error::Error, plugin::PLUGIN};
14
15#[allow(non_upper_case_globals)]
16#[unsafe(no_mangle)]
17/// The version of the plugin API that this plugin is compatible with
18pub static qemu_plugin_version: c_int = QEMU_PLUGIN_VERSION as c_int;
19
20/// Code returned from `qemu_plugin_install` to indicate successful installation
21pub const PLUGIN_INSTALL_SUCCESS: c_int = 0;
22
23/// A value passed to a QEMU plugin via the command line, either as a boolean,
24/// integer, or string. Booleans are parsed using the `qemu_plugin_bool_parse`
25/// function, integers are parsed from strings, and strings are taken as-is.
26pub enum Value {
27    /// A boolean argument to a QEMU plugin, for example `val=true` or `val=on`
28    /// see https://www.qemu.org/docs/master/devel/tcg-plugins.html#c.qemu_plugin_bool_parse
29    Bool(bool),
30    /// An integer argument to a QEMU plugin, for example `val=1`
31    Integer(i64),
32    /// A string argument to a QEMU plugin, for example `val=foo`
33    String(String),
34}
35
36impl Value {
37    fn new(key: &str, value: &str) -> Result<Self, Error> {
38        if let Ok(maybe_bool) = qemu_plugin_bool_parse(key, value) {
39            Ok(Self::Bool(maybe_bool))
40        } else if let Ok(int) = value.parse::<i64>() {
41            Ok(Self::Integer(int))
42        } else {
43            Ok(Self::String(value.to_string()))
44        }
45    }
46}
47
48/// Arguments to QEMU as passed to `qemu_plugin_install`. `qemu_plugin_install`
49/// takes a comma-separated list of key=value pairs, such as
50/// `val1=foo,val2=bar`.
51pub struct Args {
52    /// Arguments to the QEMU plugin as passed in by QEMU. Each entry is a
53    /// key=value pair where the key is the name of the argument and the value
54    /// is the value of the argument.
55    pub raw: Vec<String>,
56    /// Arguments to the QEMU plugin, parsed into valid argument types and value
57    /// types. Each key is the name of the argument and the value is a `Value`
58    /// enum which can be a boolean, integer, or string.
59    pub parsed: HashMap<String, Value>,
60}
61
62impl Args {
63    /// Create a new QEMU `Args` container from the raw arguments passed to the plugin on the
64    /// command line
65    fn new(argc: c_int, value: *const *const c_char) -> Result<Self, Error> {
66        Ok(Self {
67            raw: (0..argc)
68                .map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
69                .map(|cstr| cstr.to_string_lossy().into_owned())
70                .collect::<Vec<_>>(),
71            parsed: (0..argc)
72                .map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
73                .map(|cstr| cstr.to_string_lossy().into_owned())
74                .map(|argument| {
75                    let mut split = argument.splitn(2, '=');
76                    let Some(key) = split.next() else {
77                        return Err(Error::MissingArgKey { argument });
78                    };
79                    let Some(value) = split.next() else {
80                        return Err(Error::MissingArgValue { argument });
81                    };
82                    Ok((key.to_string(), Value::new(key, value)?))
83                })
84                .collect::<Result<Vec<(_, _)>, Error>>()?
85                .into_iter()
86                .collect::<HashMap<_, _>>(),
87        })
88    }
89}
90
91/// The version specification of the QEMU plugin API
92pub struct Version {
93    /// Current plugin API version
94    pub current: i64,
95    /// Minimum plugin API version
96    pub mininum: i64,
97}
98
99impl From<&qemu_info_t__bindgen_ty_1> for Version {
100    fn from(value: &qemu_info_t__bindgen_ty_1) -> Self {
101        Self {
102            current: value.cur as i64,
103            mininum: value.min as i64,
104        }
105    }
106}
107
108/// Information about the virtualized system, present if the emulator is running
109/// in full system emulation mode
110pub struct System {
111    /// The maximum number of virtual CPUs supported by the system
112    pub max_vcpus: i64,
113    /// The number of virtual CPUs currently configured
114    pub smp_vcpus: i64,
115}
116
117impl From<&qemu_info_t__bindgen_ty_2__bindgen_ty_1> for System {
118    fn from(value: &qemu_info_t__bindgen_ty_2__bindgen_ty_1) -> Self {
119        Self {
120            max_vcpus: value.max_vcpus as i64,
121            smp_vcpus: value.smp_vcpus as i64,
122        }
123    }
124}
125
126/// Information about the simulation, including the target name, version, and virtual
127/// system information
128pub struct Info {
129    /// The target name of the simulation (e.g. `x86_64-softmmu`)
130    pub target_name: String,
131    /// The minimum and current plugin API version
132    pub version: Version,
133    /// Information about the system, if the emulator is running in full system
134    /// emulation mode. If `None`, the emulator is running in user mode
135    pub system: Option<System>,
136}
137
138impl Info {
139    /// # Safety
140    ///
141    /// This method should only called by QEMU inside the `qemu_plugin_install` function
142    /// when the plugin is loaded. The `value` pointer is a valid pointer to a
143    /// `qemu_info_t` struct which is live for the duration of the `qemu_plugin_install`
144    /// function.
145    unsafe fn try_from(value: *const qemu_info_t) -> Result<Self, Error> {
146        let target_name = unsafe { CStr::from_ptr((*value).target_name) }
147            .to_str()
148            .map_err(Error::from)?
149            .to_string();
150        let version = Version::from(unsafe { &(*value).version });
151        let system_emulation = unsafe { (*value).system_emulation };
152        let system = if system_emulation {
153            // NOTE: This is safe because `system_emulation` is true, which means the
154            // `system` field is valid
155            Some(System::from(unsafe { &(*value).__bindgen_anon_1.system }))
156        } else {
157            None
158        };
159
160        Ok(Self {
161            target_name,
162            version,
163            system,
164        })
165    }
166}
167
168#[unsafe(no_mangle)]
169/// Called by QEMU when the plugin is loaded
170///
171/// # Safety
172///
173/// This function is called by QEMU when the plugin is loaded, and should not be called
174/// by dependent code. The `info` pointer is valid for the duration of the function
175/// call, and must not be accessed after the function returns. `argv` remains valid for
176/// the duration of the plugin's lifetime.
177pub unsafe extern "C" fn qemu_plugin_install(
178    id: qemu_plugin_id_t,
179    info: *const qemu_info_t,
180    argc: c_int,
181    argv: *const *const c_char,
182) -> c_int {
183    let args = Args::new(argc, argv).expect("Failed to parse arguments");
184    let info = unsafe { Info::try_from(info) }.expect("Failed to convert qemu_info_t");
185
186    let Some(plugin) = PLUGIN.get() else {
187        panic!("Plugin not set");
188    };
189
190    let Ok(mut plugin) = plugin.lock() else {
191        panic!("Failed to lock plugin");
192    };
193
194    plugin
195        .register_default(id, &args, &info)
196        .expect("Failed to register plugin");
197
198    PLUGIN_INSTALL_SUCCESS
199}