1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! Installation for the QEMU plugin

use qemu_plugin_sys::{
    qemu_info_t, qemu_info_t__bindgen_ty_1, qemu_info_t__bindgen_ty_2__bindgen_ty_1,
    qemu_plugin_bool_parse, qemu_plugin_id_t, QEMU_PLUGIN_VERSION,
};
use std::{
    collections::HashMap,
    ffi::{c_char, c_int, CStr, CString},
};

use crate::{error::Error, plugin::PLUGIN};

#[no_mangle]
/// The version of the plugin API that this plugin is compatible with
pub static qemu_plugin_version: c_int = QEMU_PLUGIN_VERSION as c_int;

/// Code returned from `qemu_plugin_install` to indicate successful installation
pub const PLUGIN_INSTALL_SUCCESS: c_int = 0;

/// A value passed to a QEMU plugin via the command line
pub enum Value {
    /// A boolean argument to a QEMU plugin, for example `val=true` or `val=on`
    /// see https://www.qemu.org/docs/master/devel/tcg-plugins.html#c.qemu_plugin_bool_parse
    Bool(bool),
    /// An integer argument to a QEMU plugin, for example `val=1`
    Integer(i64),
    /// A string argument to a QEMU plugin, for example `val=foo`
    String(String),
}

impl Value {
    fn new(key: &str, value: &str) -> Result<Self, Error> {
        let mut maybe_bool = false;
        if unsafe {
            qemu_plugin_bool_parse(
                CString::new(key)?.as_ptr(),
                CString::new(value)?.as_ptr(),
                &mut maybe_bool,
            )
        } {
            Ok(Self::Bool(maybe_bool))
        } else if let Ok(int) = value.parse::<i64>() {
            Ok(Self::Integer(int))
        } else {
            Ok(Self::String(value.to_string()))
        }
    }
}

/// Arguments to QEMU as passed to `qemu_plugin_install`
pub struct Args {
    /// Arguments to the QEMU plugin as passed in by QEMU
    pub raw: Vec<String>,
    /// Arguments to the QEMU plugin, parsed into valid argument types and value
    /// types
    pub parsed: HashMap<String, Value>,
}

impl Args {
    /// Create a new QEMU `Args` container from the raw arguments passed to the plugin on the
    /// command line
    fn new(argc: c_int, value: *const *const c_char) -> Result<Self, Error> {
        Ok(Self {
            raw: (0..argc)
                .map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
                .map(|cstr| cstr.to_string_lossy().into_owned())
                .collect::<Vec<_>>(),
            parsed: (0..argc)
                .map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
                .map(|cstr| cstr.to_string_lossy().into_owned())
                .map(|argument| {
                    let mut split = argument.splitn(2, '=');
                    let Some(key) = split.next() else {
                        return Err(Error::MissingArgKey { argument });
                    };
                    let Some(value) = split.next() else {
                        return Err(Error::MissingArgValue { argument });
                    };
                    Ok((key.to_string(), Value::new(key, value)?))
                })
                .collect::<Result<Vec<(_, _)>, Error>>()?
                .into_iter()
                .collect::<HashMap<_, _>>(),
        })
    }
}

/// The version specification of the QEMU plugin API
pub struct Version {
    /// Current plugin API version
    pub current: i64,
    /// Minimum plugin API version
    pub mininum: i64,
}

impl From<&qemu_info_t__bindgen_ty_1> for Version {
    fn from(value: &qemu_info_t__bindgen_ty_1) -> Self {
        Self {
            current: value.cur as i64,
            mininum: value.min as i64,
        }
    }
}

/// Information about the system, present if the emulator is running in full
/// system emulation mode
pub struct System {
    /// The maximum number of virtual CPUs supported by the system
    pub max_vcpus: i64,
    /// The number of virtual CPUs currently configured
    pub smp_vcpus: i64,
}

impl From<&qemu_info_t__bindgen_ty_2__bindgen_ty_1> for System {
    fn from(value: &qemu_info_t__bindgen_ty_2__bindgen_ty_1) -> Self {
        Self {
            max_vcpus: value.max_vcpus as i64,
            smp_vcpus: value.smp_vcpus as i64,
        }
    }
}

/// Information about the simulation
pub struct Info {
    /// The target name of the simulation (e.g. `x86_64-softmmu`)
    pub target_name: String,
    /// The minimum and current plugin API version
    pub version: Version,
    /// Information about the system, if the emulator is running in full system
    /// emulation mode. If `None`, the emulator is running in user mode
    pub system: Option<System>,
}

impl Info {
    /// # Safety
    ///
    /// This method should only called by QEMU inside the `qemu_plugin_install` function
    /// when the plugin is loaded. The `value` pointer is a valid pointer to a
    /// `qemu_info_t` struct which is live for the duration of the `qemu_plugin_install`
    /// function.
    unsafe fn try_from(value: *const qemu_info_t) -> Result<Self, Error> {
        let target_name = unsafe { CStr::from_ptr((*value).target_name) }
            .to_str()
            .map_err(Error::from)?
            .to_string();
        let version = Version::from(unsafe { &(*value).version });
        let system_emulation = unsafe { (*value).system_emulation };
        let system = if system_emulation {
            // NOTE: This is safe because `system_emulation` is true, which means the
            // `system` field is valid
            Some(System::from(unsafe { &(*value).__bindgen_anon_1.system }))
        } else {
            None
        };

        Ok(Self {
            target_name,
            version,
            system,
        })
    }
}

#[no_mangle]
/// Called by QEMU when the plugin is loaded
///
/// # Safety
///
/// This function is called by QEMU when the plugin is loaded, and should not be called
/// by dependent code. The `info` pointer is valid for the duration of the function
/// call, and must not be accessed after the function returns. `argv` remains valid for
/// the duration of the plugin's lifetime.
pub unsafe extern "C" fn qemu_plugin_install(
    id: qemu_plugin_id_t,
    info: *const qemu_info_t,
    argc: c_int,
    argv: *const *const c_char,
) -> c_int {
    let args = Args::new(argc, argv).expect("Failed to parse arguments");
    let info = unsafe { Info::try_from(info) }.expect("Failed to convert qemu_info_t");

    let Some(plugin) = PLUGIN.get() else {
        panic!("Plugin not set");
    };

    let Ok(mut plugin) = plugin.lock() else {
        panic!("Failed to lock plugin");
    };

    plugin
        .register_default(id, &args, &info)
        .expect("Failed to register plugin");

    PLUGIN_INSTALL_SUCCESS
}