From 6daafabbe2f921ecb62eb1072c3f3289d7919564 Mon Sep 17 00:00:00 2001 From: esteban Date: Wed, 22 Mar 2023 10:52:09 +0100 Subject: [PATCH] feat: add --no-console option It is now possible to run a VM without a console with --no-console option Co-authored-by: Esteban Baron Signed-off-by: Alexis Langlet --- src/main.rs | 7 +++++- src/vmm/src/cpu/mod.rs | 22 ++++++++++++----- src/vmm/src/kernel.rs | 2 +- src/vmm/src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 02605ba..b35e37f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,10 @@ struct VMMOpts { /// Interface name #[clap(long)] net: Option, + + /// no-console + #[clap(long)] + no_console: bool, } #[derive(Debug)] @@ -60,13 +64,14 @@ fn main() -> Result<(), Error> { opts.memory, &opts.kernel, opts.console, + opts.no_console, opts.initramfs, opts.net, ) .map_err(Error::VmmConfigure)?; // Run the VMM - vmm.run().map_err(Error::VmmRun)?; + vmm.run(opts.no_console).map_err(Error::VmmRun)?; Ok(()) } diff --git a/src/vmm/src/cpu/mod.rs b/src/vmm/src/cpu/mod.rs index 440a6c5..c7e0467 100644 --- a/src/vmm/src/cpu/mod.rs +++ b/src/vmm/src/cpu/mod.rs @@ -223,20 +223,30 @@ impl Vcpu { } /// vCPU emulation loop. - pub fn run(&mut self) { + pub fn run(&mut self, no_console: bool, should_stop: Arc>) { // Call into KVM to launch (VMLAUNCH) or resume (VMRESUME) the virtual CPU. // This is a blocking function, it only returns for either an error or a // VM-Exit. In the latter case, we can inspect the exit reason. - match self.vcpu_fd.run() { + println!("Before running vCPU {}...", self.index); + let run = self.vcpu_fd.run(); + println!("After running vCPU {}...", self.index); + + match run { Ok(exit_reason) => match exit_reason { // The VM stopped (Shutdown ot HLT). VcpuExit::Shutdown | VcpuExit::Hlt => { println!("Guest shutdown: {:?}. Bye!", exit_reason); - let stdin = io::stdin(); - let stdin_lock = stdin.lock(); - stdin_lock.set_canon_mode().unwrap(); - unsafe { libc::exit(0) }; + if no_console { + println!("Exiting... {:?}", self.index); + *should_stop.lock().unwrap() = true; + } else { + let stdin = io::stdin(); + let stdin_lock = stdin.lock(); + stdin_lock.set_canon_mode().unwrap(); + + unsafe { libc::exit(0) }; + } } // This is a PIO write, i.e. the guest is trying to write diff --git a/src/vmm/src/kernel.rs b/src/vmm/src/kernel.rs index 100a1a9..4a5f01f 100644 --- a/src/vmm/src/kernel.rs +++ b/src/vmm/src/kernel.rs @@ -44,7 +44,7 @@ const HIMEM_START: u64 = 0x0010_0000; // 1 MB /// Address where the kernel command line is written. const CMDLINE_START: u64 = 0x0002_0000; // Default command line -pub const DEFAULT_CMDLINE: &str = "console=ttyS0 i8042.nokbd reboot=k panic=1 pci=off"; +pub const DEFAULT_CMDLINE: &str = "i8042.nokbd reboot=k panic=1 pci=off"; fn add_e820_entry( params: &mut boot_params, diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index f816442..3a8cf8e 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -8,6 +8,7 @@ extern crate linux_loader; extern crate vm_memory; extern crate vm_superio; +use std::any::Any; use std::fs::File; use std::io::stdout; use std::os::unix::io::AsRawFd; @@ -83,6 +84,10 @@ pub enum Error { VirtioNet(devices::net::VirtioNetError), /// Error related to IOManager. IoManager(vm_device::device_manager::Error), + /// Access thread handler error + AccessThreadHandlerError, + /// Join thread error + JoinThreadError(Box), } /// Dedicated [`Result`](https://doc.rust-lang.org/std/result/) type. @@ -248,7 +253,19 @@ impl VMM { Ok(()) } - pub fn configure_console(&mut self, console_path: Option) -> Result<()> { + pub fn configure_console( + &mut self, + console_path: Option, + disable_console: bool, + ) -> Result<()> { + if disable_console { + return Ok(()); + } + + self.cmdline + .insert_str("console=ttyS0") + .map_err(Error::Cmdline)?; + if let Some(console_path) = console_path { // We create the file if it does not exist, else we open let file = File::create(&console_path).map_err(Error::ConsoleError)?; @@ -312,12 +329,39 @@ impl VMM { } // Run all virtual CPUs. - pub fn run(&mut self) -> Result<()> { + pub fn run(&mut self, no_console: bool) -> Result<()> { + let mut handlers: Vec> = Vec::new(); + let should_stop = Arc::new(Mutex::new(false)); + for mut vcpu in self.vcpus.drain(..) { println!("Starting vCPU {:?}", vcpu.index); - let _ = thread::Builder::new().spawn(move || loop { - vcpu.run(); + + let should_stop_cloned = Arc::clone(&should_stop); + + let handler = thread::Builder::new().spawn(move || loop { + if *should_stop_cloned.lock().unwrap() { + println!("Stopping vCPU {:?}", vcpu.index); + break; + } + + vcpu.run(no_console, Arc::clone(&should_stop_cloned)); }); + + match handler { + Ok(handler) => handlers.push(handler), + Err(_) => { + println!("Failed to start vCPU"); + return Err(Error::AccessThreadHandlerError); + } + } + } + + if no_console { + for handler in handlers { + handler.join().map_err(Error::JoinThreadError)? + } + + return Ok(()); // We don't want to start the console if we are in no_console mode. } let stdin = io::stdin(); @@ -380,10 +424,11 @@ impl VMM { mem_size_mb: u32, kernel_path: &str, console: Option, + no_console: bool, initramfs_path: Option, if_name: Option, ) -> Result<()> { - self.configure_console(console)?; + self.configure_console(console, no_console)?; self.configure_memory(mem_size_mb)?; self.load_default_cmdline()?;