diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 577bf4d5d..63aa7e3d7 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -67,7 +67,7 @@ pub struct Serial { } impl Serial { - fn new(interrupt: Box, out: Option>) -> Serial { + pub fn new(interrupt: Box, out: Option>) -> Serial { Serial { interrupt_enable: 0, interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION, diff --git a/src/main.rs b/src/main.rs index 302817202..39ccef781 100755 --- a/src/main.rs +++ b/src/main.rs @@ -137,13 +137,13 @@ fn main() { .arg( Arg::with_name("serial") .long("serial") - .help("Control serial port: off|tty|file=/path/to/a/file") - .default_value("off"), + .help("Control serial port: off|null|tty|file=/path/to/a/file") + .default_value("null"), ) .arg( Arg::with_name("console") .long("console") - .help("Control (virtio) console: off|tty|file=/path/to/a/file") + .help("Control (virtio) console: off|null|tty|file=/path/to/a/file") .default_value("tty"), ) .arg( @@ -1322,7 +1322,7 @@ mod tests { } #[test] - fn test_serial_disable() { + fn test_serial_off() { test_block!(tb, "", { let mut clear = ClearDiskConfig::new(); let guest = Guest::new(&mut clear); @@ -1380,6 +1380,124 @@ mod tests { }); } + #[test] + fn test_serial_null() { + test_block!(tb, "", { + let mut clear = ClearDiskConfig::new(); + let guest = Guest::new(&mut clear); + let mut child = Command::new("target/debug/cloud-hypervisor") + .args(&["--cpus", "1"]) + .args(&["--memory", "size=512M"]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&[ + "--disk", + guest + .disk_config + .disk(DiskType::OperatingSystem) + .unwrap() + .as_str(), + guest + .disk_config + .disk(DiskType::CloudInit) + .unwrap() + .as_str(), + ]) + .args(&["--net", guest.default_net_string().as_str()]) + .args(&["--serial", "null"]) + .args(&["--console", "off"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + thread::sleep(std::time::Duration::new(20, 0)); + + // Test that there is a ttyS0 + aver_eq!( + tb, + guest + .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") + .trim() + .parse::() + .unwrap(), + 1 + ); + + guest.ssh_command("sudo reboot"); + thread::sleep(std::time::Duration::new(10, 0)); + let _ = child.kill(); + match child.wait_with_output() { + Ok(out) => { + aver!( + tb, + !String::from_utf8_lossy(&out.stdout).contains("cloud login:") + ); + } + Err(_) => aver!(tb, false), + } + Ok(()) + }); + } + + #[test] + fn test_serial_tty() { + test_block!(tb, "", { + let mut clear = ClearDiskConfig::new(); + let guest = Guest::new(&mut clear); + let mut child = Command::new("target/debug/cloud-hypervisor") + .args(&["--cpus", "1"]) + .args(&["--memory", "size=512M"]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&[ + "--disk", + guest + .disk_config + .disk(DiskType::OperatingSystem) + .unwrap() + .as_str(), + guest + .disk_config + .disk(DiskType::CloudInit) + .unwrap() + .as_str(), + ]) + .args(&["--net", guest.default_net_string().as_str()]) + .args(&["--serial", "tty"]) + .args(&["--console", "off"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + thread::sleep(std::time::Duration::new(20, 0)); + + // Test that there is a ttyS0 + aver_eq!( + tb, + guest + .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") + .trim() + .parse::() + .unwrap(), + 1 + ); + + guest.ssh_command("sudo reboot"); + thread::sleep(std::time::Duration::new(10, 0)); + let _ = child.kill(); + match child.wait_with_output() { + Ok(out) => { + aver!( + tb, + String::from_utf8_lossy(&out.stdout).contains("cloud login:") + ); + } + Err(_) => aver!(tb, false), + } + Ok(()) + }); + } + #[test] fn test_serial_file() { test_block!(tb, "", { diff --git a/vmm/src/config.rs b/vmm/src/config.rs index f40458274..eb07647ab 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -350,6 +350,7 @@ pub enum ConsoleOutputMode { Off, Tty, File, + Null, } impl ConsoleOutputMode { @@ -383,6 +384,11 @@ impl<'a> ConsoleConfig<'a> { mode: ConsoleOutputMode::File, file: Some(Path::new(¶m[5..])), }) + } else if param.starts_with("null") { + Ok(Self { + mode: ConsoleOutputMode::Null, + file: None, + }) } else { Err(Error::ParseConsoleParam) } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index f1381eb99..6508227d3 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -40,7 +40,7 @@ use pci::{ use qcow::{self, ImageType, QcowFile}; use std::ffi::CString; use std::fs::{File, OpenOptions}; -use std::io::{self, stdout}; +use std::io::{self, sink, stdout}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, RawFd}; use std::ptr::null_mut; @@ -561,9 +561,9 @@ impl DeviceManager { .map_err(DeviceManagerError::SerialOutputFileOpen)?, )), ConsoleOutputMode::Tty => Some(Box::new(stdout())), - ConsoleOutputMode::Off => None, + ConsoleOutputMode::Off | ConsoleOutputMode::Null => None, }; - let serial = if serial_writer.is_some() { + let serial = if vm_info.vm_cfg.serial.mode != ConsoleOutputMode::Off { // Serial is tied to IRQ #4 let serial_irq = 4; let interrupt: Box = if let Some(ioapic) = &ioapic { @@ -578,9 +578,9 @@ impl DeviceManager { Box::new(KernelIoapicIrq::new(serial_evt)) }; - Some(Arc::new(Mutex::new(devices::legacy::Serial::new_out( + Some(Arc::new(Mutex::new(devices::legacy::Serial::new( interrupt, - serial_writer.unwrap(), + serial_writer, )))) } else { None @@ -601,6 +601,7 @@ impl DeviceManager { .map_err(DeviceManagerError::ConsoleOutputFileOpen)?, )), ConsoleOutputMode::Tty => Some(Box::new(stdout())), + ConsoleOutputMode::Null => Some(Box::new(sink())), ConsoleOutputMode::Off => None, }; let console = if console_writer.is_some() {