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