Skip to content

Commit

Permalink
feat: add --no-console option
Browse files Browse the repository at this point in the history
It is now possible to run a VM without a console with --no-console option

Co-authored-by: Esteban Baron <[email protected]>
Signed-off-by: Alexis Langlet <[email protected]>
  • Loading branch information
EstebanBAR0N authored and alexis-langlet committed Mar 27, 2023
1 parent 9e708c5 commit 04ec86d
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 49 deletions.
16 changes: 13 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ struct VMMOpts {
/// Stdout console file path
#[clap(long)]
console: Option<String>,

/// no-console
#[clap(long)]
no_console: bool,
}

#[derive(Debug)]
Expand All @@ -47,11 +51,17 @@ fn main() -> Result<(), Error> {
// * Memory size (in MB)
// * Path to a Linux kernel
// * Optional path to console file
vmm.configure(opts.cpus, opts.memory, &opts.kernel, opts.console)
.map_err(Error::VmmConfigure)?;
vmm.configure(
opts.cpus,
opts.memory,
&opts.kernel,
opts.console,
opts.no_console,
)
.map_err(Error::VmmConfigure)?;

// Run the VMM
vmm.run().map_err(Error::VmmRun)?;
vmm.run(opts.no_console).map_err(Error::VmmRun)?;

Ok(())
}
22 changes: 16 additions & 6 deletions src/vmm/src/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,20 +215,30 @@ impl Vcpu {
}

/// vCPU emulation loop.
pub fn run(&mut self) {
pub fn run(&mut self, no_console: bool, should_stop: Arc<Mutex<bool>>) {
// 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
Expand Down
15 changes: 11 additions & 4 deletions src/vmm/src/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const HIMEM_START: u64 = 0x0010_0000; // 1 MB
const CMDLINE_START: u64 = 0x0002_0000;
// Default command line
const CMDLINE: &str = "console=ttyS0 i8042.nokbd reboot=k panic=1 pci=off";
const CMDLINE_NO_CONSOLE: &str = "i8042.nokbd reboot=k panic=1 pci=off"; // remove console=ttyS0

fn add_e820_entry(
params: &mut boot_params,
Expand Down Expand Up @@ -109,6 +110,7 @@ pub fn build_bootparams(
pub fn kernel_setup(
guest_memory: &GuestMemoryMmap,
kernel_path: PathBuf,
no_console: bool,
) -> Result<KernelLoaderResult> {
let mut kernel_image = File::open(kernel_path).map_err(Error::IO)?;
let zero_page_addr = GuestAddress(ZEROPG_START);
Expand All @@ -125,14 +127,19 @@ pub fn kernel_setup(
// Generate boot parameters.
let mut bootparams = build_bootparams(guest_memory, GuestAddress(HIMEM_START))?;

let right_cmdline = if no_console {
CMDLINE_NO_CONSOLE
} else {
CMDLINE
};

// Add the kernel command line to the boot parameters.
bootparams.hdr.cmd_line_ptr = CMDLINE_START as u32;
bootparams.hdr.cmdline_size = CMDLINE.len() as u32 + 1;
bootparams.hdr.cmdline_size = right_cmdline.len() as u32 + 1;

// Load the kernel command line into guest memory.
let mut cmdline = Cmdline::new(CMDLINE.len() + 1).map_err(Error::Cmdline)?;

cmdline.insert_str(CMDLINE).map_err(Error::Cmdline)?;
let mut cmdline = Cmdline::new(right_cmdline.len() + 1).map_err(Error::Cmdline)?;
cmdline.insert_str(right_cmdline).map_err(Error::Cmdline)?;
load_cmdline(
guest_memory,
GuestAddress(CMDLINE_START),
Expand Down
109 changes: 73 additions & 36 deletions src/vmm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern crate linux_loader;
extern crate vm_memory;
extern crate vm_superio;

use std::any::Any;
use std::io::stdout;
use std::os::unix::io::AsRawFd;
use std::os::unix::prelude::RawFd;
Expand Down Expand Up @@ -66,6 +67,10 @@ pub enum Error {
TerminalConfigure(kvm_ioctls::Error),
/// Console configuration error
ConsoleError(io::Error),
/// Access thread handler error
AccessThreadHandlerError,
/// Join thread error
JoinThreadError(Box<dyn Any + Send>),
}

/// Dedicated [`Result`](https://doc.rust-lang.org/std/result/) type.
Expand Down Expand Up @@ -164,10 +169,7 @@ impl VMM {
Ok(())
}

pub fn configure_console(
&mut self,
console_path: Option<String>
) -> Result<()> {
pub fn configure_console(&mut self, console_path: Option<String>) -> Result<()> {
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)?;
Expand Down Expand Up @@ -226,50 +228,85 @@ 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<thread::JoinHandle<_>> = 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);
}
}
}

let stdin = io::stdin();
let stdin_lock = stdin.lock();
stdin_lock
.set_raw_mode()
.map_err(Error::TerminalConfigure)?;
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
let epoll_fd = self.epoll.as_raw_fd();

// Let's start the STDIN polling thread.
loop {
let num_events =
epoll::wait(epoll_fd, -1, &mut events[..]).map_err(Error::EpollError)?;

for event in events.iter().take(num_events) {
let event_data = event.data as RawFd;

if let libc::STDIN_FILENO = event_data {
let mut out = [0u8; 64];

let count = stdin_lock.read_raw(&mut out).map_err(Error::StdinRead)?;

self.serial
.lock()
.unwrap()
.serial
.enqueue_raw_bytes(&out[..count])
.map_err(Error::StdinWrite)?;
if no_console {
for handler in handlers {
handler.join().map_err(Error::JoinThreadError)?
}

Ok(())
} else {
let stdin = io::stdin();
let stdin_lock = stdin.lock();
stdin_lock
.set_raw_mode()
.map_err(Error::TerminalConfigure)?;
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
let epoll_fd = self.epoll.as_raw_fd();

// Let's start the STDIN polling thread.
loop {
let num_events =
epoll::wait(epoll_fd, -1, &mut events[..]).map_err(Error::EpollError)?;

for event in events.iter().take(num_events) {
let event_data = event.data as RawFd;

if let libc::STDIN_FILENO = event_data {
let mut out = [0u8; 64];

let count = stdin_lock.read_raw(&mut out).map_err(Error::StdinRead)?;

self.serial
.lock()
.unwrap()
.serial
.enqueue_raw_bytes(&out[..count])
.map_err(Error::StdinWrite)?;
}
}
}
}
}

pub fn configure(&mut self, num_vcpus: u8, mem_size_mb: u32, kernel_path: &str, console: Option<String>) -> Result<()> {
pub fn configure(
&mut self,
num_vcpus: u8,
mem_size_mb: u32,
kernel_path: &str,
console: Option<String>,
no_console: bool,
) -> Result<()> {
self.configure_console(console)?;
self.configure_memory(mem_size_mb)?;
let kernel_load = kernel::kernel_setup(&self.guest_memory, PathBuf::from(kernel_path))?;
let kernel_load =
kernel::kernel_setup(&self.guest_memory, PathBuf::from(kernel_path), no_console)?;
self.configure_io()?;
self.configure_vcpus(num_vcpus, kernel_load)?;

Expand Down

0 comments on commit 04ec86d

Please sign in to comment.