panda/library_mode/
mod.rs1#[cfg(feature = "libpanda")]
2mod qcows;
3
4use crate::PandaArgs;
5use std::fmt;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8#[cfg(feature = "libpanda")]
9use std::{ffi::CString, mem::transmute, os::raw::c_char, sync::Mutex};
10
11#[cfg(feature = "libpanda")]
12use crate::{
13 inventory,
14 sys::{self, panda_init, panda_run, panda_set_library_mode},
15 InternalCallback, PPPCallbackSetup,
16};
17
18#[allow(non_camel_case_types)]
20#[derive(Copy, Clone, Debug, PartialEq)]
21pub enum Arch {
22 i386,
23 x86_64,
24 Arm,
25 Mips,
26 AArch64,
27}
28
29impl fmt::Display for Arch {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 write!(
33 f,
34 "{}",
35 match self {
36 Self::i386 => "i386",
37 Self::x86_64 => "x86_64",
38 Self::Arm => "arm",
39 Self::Mips => "mips",
40 Self::AArch64 => "aarch64",
41 }
42 )
43 }
44}
45
46#[derive(Default)]
48#[allow(dead_code)]
49pub struct Panda {
50 expect_prompt: Option<String>,
51 generic_qcow: Option<String>,
52 os_version: Option<String>,
53 qcow: Option<String>,
54 raw_monitor: bool,
55 graphics: bool,
56 os: String,
57 mem: Option<String>,
58 arch: Option<Arch>,
59 extra_args: Vec<String>,
60 replay: Option<String>,
61 configurable: bool,
62}
63
64static LIBRARY_STARTED: AtomicBool = AtomicBool::new(false);
65
66#[cfg(feature = "libpanda")]
67lazy_static::lazy_static! {
68 static ref AFTER_INIT_FUNCS: Mutex<Vec<Box<dyn FnOnce() + Send + Sync + 'static>>>
69 = Mutex::new(Vec::new());
70}
71
72impl Panda {
73 fn is_started() -> bool {
75 LIBRARY_STARTED.load(Ordering::Relaxed)
76 }
77
78 pub fn new() -> Self {
88 Self {
89 os: "linux".into(),
90 ..Default::default()
91 }
92 }
93
94 pub fn arg<S: Into<String>>(&mut self, arg: S) -> &mut Self {
104 self.extra_args.push(arg.into());
105
106 self
107 }
108
109 pub fn args<I, S>(&mut self, args: I) -> &mut Self
119 where
120 I: IntoIterator<Item = S>,
121 S: AsRef<str>,
122 {
123 for arg in args {
124 self.arg(arg.as_ref());
125 }
126
127 self
128 }
129
130 pub fn arch(&mut self, arch: Arch) -> &mut Self {
140 self.arch = Some(arch);
141
142 self
143 }
144
145 pub fn configurable(&mut self) -> &mut Self {
155 self.configurable = true;
156
157 self
158 }
159
160 pub fn enable_graphics(&mut self) -> &mut Self {
170 self.graphics = true;
171
172 self
173 }
174
175 pub fn expect_prompt<S: Into<String>>(&mut self, prompt_regex: S) -> &mut Self {
178 self.expect_prompt = Some(prompt_regex.into());
179
180 self
181 }
182
183 pub fn mem<S: Into<String>>(&mut self, mem: S) -> &mut Self {
186 self.mem = Some(mem.into());
187
188 self
189 }
190
191 pub fn generic<S: Into<String>>(&mut self, generic: S) -> &mut Self {
201 self.generic_qcow = Some(generic.into());
202
203 self
204 }
205
206 pub fn replay<S: Into<String>>(&mut self, replay: S) -> &mut Self {
217 self.replay = Some(replay.into());
218
219 self
220 }
221
222 pub fn plugin_args<T: PandaArgs>(&mut self, args: &T) -> &mut Self {
246 self.arg("-panda").arg(args.to_panda_args_str())
247 }
248
249 #[cfg(feature = "libpanda")]
250 fn get_args(&self) -> Vec<String> {
251 let generic_info = self
252 .generic_qcow
253 .as_ref()
254 .map(|generic| qcows::get_supported_image(generic));
255
256 let qcow_path = self.qcow.clone().map(Some).unwrap_or_else(|| {
257 self.generic_qcow
258 .as_ref()
259 .map(|generic| qcows::get_generic_path(generic).display().to_string())
260 });
261
262 let _arch = self
263 .arch
264 .or_else(|| generic_info.as_ref().map(|x| x.arch))
265 .unwrap_or(Arch::x86_64);
266
267 let mem = self
268 .mem
269 .as_ref()
270 .map(|x| &x[..])
271 .or_else(|| generic_info.as_ref().map(|x| x.default_mem))
272 .unwrap_or("128M")
273 .to_owned();
274
275 let mut args = vec![
276 "".into(), "-L".into(),
278 std::env::var("PANDA_PATH")
279 .expect("PANDA_PATH not set. Set it to panda's build folder.")
280 + "/pc-bios",
281 "-m".into(),
282 mem,
283 ];
284
285 if let Some(qcow) = qcow_path {
286 args.push(qcow)
287 }
288
289 if let Some(generic) = generic_info {
290 args.push("-os".into());
291 args.push(generic.os.into());
292 }
293
294 if self.configurable {
295 args.push("-M".into());
296 args.push("configurable".into());
297 }
298
299 if !self.graphics {
300 args.push("-nographic".into());
301 }
302
303 if let Some(replay) = &self.replay {
304 args.push("-replay".into());
305 args.push(replay.clone());
306 }
307
308 args.extend(self.extra_args.clone().into_iter());
309
310 args
311 }
312
313 pub fn run(&mut self) {
323 #[cfg(not(feature = "libpanda"))]
324 {
325 panic!("Panda::run cannot be used without the libpanda feature");
326 }
327 #[cfg(feature = "libpanda")]
328 {
329 let args = self.get_args();
330
331 println!("Running with args: {:?}", args);
332
333 let args: Vec<_> = args.into_iter().map(|x| CString::new(x).unwrap()).collect();
334 let args_ptrs: Vec<_> = args.iter().map(|s| s.as_ptr()).collect();
335
336 std::env::set_var("PANDA_DIR", std::env::var("PANDA_PATH").unwrap());
337
338 let x = &mut 0i8;
339 let empty = &mut (x as *mut c_char);
340 unsafe {
341 for cb in inventory::iter::<InternalCallback> {
342 sys::panda_register_callback(
343 self as *mut _ as _,
344 cb.cb_type,
345 ::core::mem::transmute(cb.fn_pointer),
346 );
347 }
348
349 if LIBRARY_STARTED.swap(true, Ordering::Relaxed) {
350 panic!("libpanda cannot be run twice in the same process");
351 }
352 panda_set_library_mode(true);
353 panda_init(args_ptrs.len() as i32, transmute(args_ptrs.as_ptr()), empty);
354
355 for cb in inventory::iter::<PPPCallbackSetup> {
356 cb.0();
357 }
358
359 let mut init_funcs = Vec::new();
360 core::mem::swap(&mut *AFTER_INIT_FUNCS.lock().unwrap(), &mut init_funcs);
361 for init_func in init_funcs {
362 init_func()
363 }
364
365 panda_run();
366 LIBRARY_STARTED.store(false, Ordering::Relaxed);
367 }
368 }
369 }
370
371 pub fn run_after_init(func: impl FnOnce() + Send + Sync + 'static) {
378 if cfg!(feature = "libpanda") && !Panda::is_started() {
379 #[cfg(feature = "libpanda")]
380 {
381 AFTER_INIT_FUNCS.lock().unwrap().push(Box::new(func));
382 }
383 } else {
384 func()
385 }
386 }
387}