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}