From 24438e0390d33af0f55d779081455111fb027ed7 Mon Sep 17 00:00:00 2001 From: fazlamehrab Date: Mon, 22 Jul 2019 12:29:02 -0700 Subject: [PATCH] vm-virtio: Enable the vmm support for virtio-console To use the implemented virtio console device, the users can select one of the three options ("off", "tty" or "file=/path/to/the/file") with the command line argument "--console". By default, the console is enabled as a device named "hvc0" (option: tty). When "off" option is used, the console device is not added to the VM configuration at all. Signed-off-by: A K M Fazla Mehrab --- src/main.rs | 8 ++++++ vmm/src/config.rs | 35 ++++++++++++++++--------- vmm/src/vm.rs | 66 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index 02bd1f5a5..6aebcd7de 100755 --- a/src/main.rs +++ b/src/main.rs @@ -92,6 +92,12 @@ fn main() { Arg::with_name("serial") .long("serial") .help("Control serial port: off|tty|file=/path/to/a/file") + .default_value("off"), + ) + .arg( + Arg::with_name("console") + .long("console") + .help("Control (virtio) console: off|tty|file=/path/to/a/file") .default_value("tty"), ) .get_matches(); @@ -109,6 +115,7 @@ fn main() { let disks: Option> = cmd_arguments.values_of("disk").map(|x| x.collect()); let net: Option> = cmd_arguments.values_of("net").map(|x| x.collect()); + let console = cmd_arguments.value_of("console").unwrap(); let fs: Option> = cmd_arguments.values_of("fs").map(|x| x.collect()); let pmem: Option> = cmd_arguments.values_of("pmem").map(|x| x.collect()); @@ -123,6 +130,7 @@ fn main() { fs, pmem, serial, + console, }) { Ok(config) => config, Err(e) => { diff --git a/vmm/src/config.rs b/vmm/src/config.rs index bee9f8016..77f06d0df 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -50,8 +50,10 @@ pub enum Error<'a> { ParsePmemFileParam, /// Failed parsing size parameter. ParseSizeParam(std::num::ParseIntError), - /// Failed parsing serial parameter. - ParseSerialParam, + /// Failed parsing console parameter. + ParseConsoleParam, + /// Both console and serial are tty. + ParseTTYParam, } pub type Result<'a, T> = result::Result>; @@ -66,6 +68,7 @@ pub struct VmParams<'a> { pub fs: Option>, pub pmem: Option>, pub serial: &'a str, + pub console: &'a str, } fn parse_size(size: &str) -> Result { @@ -342,36 +345,36 @@ impl<'a> PmemConfig<'a> { } #[derive(PartialEq)] -pub enum SerialOutputMode { +pub enum ConsoleOutputMode { Off, Tty, File, } -pub struct SerialConfig<'a> { +pub struct ConsoleConfig<'a> { pub file: Option<&'a Path>, - pub mode: SerialOutputMode, + pub mode: ConsoleOutputMode, } -impl<'a> SerialConfig<'a> { +impl<'a> ConsoleConfig<'a> { pub fn parse(param: &'a str) -> Result { if param == "off" { Ok(Self { - mode: SerialOutputMode::Off, + mode: ConsoleOutputMode::Off, file: None, }) } else if param == "tty" { Ok(Self { - mode: SerialOutputMode::Tty, + mode: ConsoleOutputMode::Tty, file: None, }) } else if param.starts_with("file=") { Ok(Self { - mode: SerialOutputMode::File, + mode: ConsoleOutputMode::File, file: Some(Path::new(¶m[5..])), }) } else { - Err(Error::ParseSerialParam) + Err(Error::ParseConsoleParam) } } } @@ -386,7 +389,8 @@ pub struct VmConfig<'a> { pub rng: RngConfig<'a>, pub fs: Option>>, pub pmem: Option>>, - pub serial: SerialConfig<'a>, + pub serial: ConsoleConfig<'a>, + pub console: ConsoleConfig<'a>, } impl<'a> VmConfig<'a> { @@ -427,6 +431,12 @@ impl<'a> VmConfig<'a> { pmem = Some(pmem_config_list); } + let console = ConsoleConfig::parse(vm_params.console)?; + let serial = ConsoleConfig::parse(vm_params.serial)?; + if console.mode == ConsoleOutputMode::Tty && serial.mode == ConsoleOutputMode::Tty { + return Err(Error::ParseTTYParam); + } + Ok(VmConfig { cpus: CpusConfig::parse(vm_params.cpus)?, memory: MemoryConfig::parse(vm_params.memory)?, @@ -437,7 +447,8 @@ impl<'a> VmConfig<'a> { rng: RngConfig::parse(vm_params.rng)?, fs, pmem, - serial: SerialConfig::parse(vm_params.serial)?, + serial, + console, }) } } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 87fcd63da..c163fbaa4 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -21,7 +21,7 @@ extern crate vm_memory; extern crate vm_virtio; extern crate vmm_sys_util; -use crate::config::{SerialOutputMode, VmConfig}; +use crate::config::{ConsoleOutputMode, VmConfig}; use arch::RegionType; use devices::ioapic; use kvm_bindings::{ @@ -142,6 +142,9 @@ pub enum Error { /// Write to the serial console failed. Serial(vmm_sys_util::Error), + /// Write to the virtio console failed. + Console(vmm_sys_util::Error), + /// Cannot setup terminal in raw mode. SetTerminalRaw(vmm_sys_util::Error), @@ -189,6 +192,9 @@ pub enum DeviceManagerError { /// Cannot create virtio-net device CreateVirtioNet(vm_virtio::net::Error), + /// Cannot create virtio-console device + CreateVirtioConsole(io::Error), + /// Cannot create virtio-rng device CreateVirtioRng(io::Error), @@ -236,6 +242,9 @@ pub enum DeviceManagerError { /// Error creating serial output file SerialOutputFileOpen(io::Error), + + /// Error creating console output file + ConsoleOutputFileOpen(io::Error), } pub type DeviceManagerResult = result::Result; @@ -483,6 +492,7 @@ struct DeviceManager { // Serial port on 0x3f8 serial: Option>>, + console: Option>, // i8042 device for exit i8042: Arc>, @@ -525,12 +535,12 @@ impl DeviceManager { }; let serial_writer: Option> = match vm_cfg.serial.mode { - SerialOutputMode::File => Some(Box::new( + ConsoleOutputMode::File => Some(Box::new( File::create(vm_cfg.serial.file.unwrap()) .map_err(DeviceManagerError::SerialOutputFileOpen)?, )), - SerialOutputMode::Tty => Some(Box::new(stdout())), - SerialOutputMode::Off => None, + ConsoleOutputMode::Tty => Some(Box::new(stdout())), + ConsoleOutputMode::Off => None, }; let serial = if serial_writer.is_some() { // Serial is tied to IRQ #4 @@ -632,6 +642,31 @@ impl DeviceManager { } } + let console_writer: Option> = match vm_cfg.console.mode { + ConsoleOutputMode::File => Some(Box::new( + File::create(vm_cfg.console.file.unwrap()) + .map_err(DeviceManagerError::ConsoleOutputFileOpen)?, + )), + ConsoleOutputMode::Tty => Some(Box::new(stdout())), + ConsoleOutputMode::Off => None, + }; + let console = if console_writer.is_some() { + let (virtio_console_device, console) = vm_virtio::Console::new(console_writer) + .map_err(DeviceManagerError::CreateVirtioConsole)?; + DeviceManager::add_virtio_pci_device( + Box::new(virtio_console_device), + memory.clone(), + allocator, + vm_fd, + &mut pci, + &mut buses, + &interrupt_info, + )?; + Some(console) + } else { + None + }; + // Add virtio-rng if required if let Some(rng_path) = vm_cfg.rng.src.to_str() { let virtio_rng_device = @@ -745,6 +780,7 @@ impl DeviceManager { io_bus, mmio_bus, serial, + console, i8042, exit_evt, ioapic, @@ -1253,7 +1289,7 @@ impl<'a> Vm<'a> { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; let epoll_fd = self.epoll.as_raw_fd(); - if self.devices.serial.is_some() && self.on_tty { + if (self.devices.serial.is_some() || self.devices.console.is_some()) && self.on_tty { io::stdin() .lock() .set_raw_mode() @@ -1276,13 +1312,13 @@ impl<'a> Vm<'a> { break 'outer; } EpollDispatch::Stdin => { - if self.devices.serial.is_some() { - let mut out = [0u8; 64]; - let count = io::stdin() - .lock() - .read_raw(&mut out) - .map_err(Error::Serial)?; + let mut out = [0u8; 64]; + let count = io::stdin() + .lock() + .read_raw(&mut out) + .map_err(Error::Serial)?; + if self.devices.serial.is_some() { self.devices .serial .as_ref() @@ -1292,6 +1328,14 @@ impl<'a> Vm<'a> { .queue_input_bytes(&out[..count]) .map_err(Error::Serial)?; } + + if self.devices.console.is_some() { + self.devices + .console + .as_ref() + .unwrap() + .queue_input_bytes(&out[..count]); + } } } }