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 <fazla.mehrab.akm@intel.com>
This commit is contained in:
fazlamehrab 2019-07-22 12:29:02 -07:00 committed by Rob Bradford
parent 577d44c8eb
commit 24438e0390
3 changed files with 86 additions and 23 deletions

View File

@ -92,6 +92,12 @@ fn main() {
Arg::with_name("serial") Arg::with_name("serial")
.long("serial") .long("serial")
.help("Control serial port: off|tty|file=/path/to/a/file") .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"), .default_value("tty"),
) )
.get_matches(); .get_matches();
@ -109,6 +115,7 @@ fn main() {
let disks: Option<Vec<&str>> = cmd_arguments.values_of("disk").map(|x| x.collect()); let disks: Option<Vec<&str>> = cmd_arguments.values_of("disk").map(|x| x.collect());
let net: Option<Vec<&str>> = cmd_arguments.values_of("net").map(|x| x.collect()); let net: Option<Vec<&str>> = cmd_arguments.values_of("net").map(|x| x.collect());
let console = cmd_arguments.value_of("console").unwrap();
let fs: Option<Vec<&str>> = cmd_arguments.values_of("fs").map(|x| x.collect()); let fs: Option<Vec<&str>> = cmd_arguments.values_of("fs").map(|x| x.collect());
let pmem: Option<Vec<&str>> = cmd_arguments.values_of("pmem").map(|x| x.collect()); let pmem: Option<Vec<&str>> = cmd_arguments.values_of("pmem").map(|x| x.collect());
@ -123,6 +130,7 @@ fn main() {
fs, fs,
pmem, pmem,
serial, serial,
console,
}) { }) {
Ok(config) => config, Ok(config) => config,
Err(e) => { Err(e) => {

View File

@ -50,8 +50,10 @@ pub enum Error<'a> {
ParsePmemFileParam, ParsePmemFileParam,
/// Failed parsing size parameter. /// Failed parsing size parameter.
ParseSizeParam(std::num::ParseIntError), ParseSizeParam(std::num::ParseIntError),
/// Failed parsing serial parameter. /// Failed parsing console parameter.
ParseSerialParam, ParseConsoleParam,
/// Both console and serial are tty.
ParseTTYParam,
} }
pub type Result<'a, T> = result::Result<T, Error<'a>>; pub type Result<'a, T> = result::Result<T, Error<'a>>;
@ -66,6 +68,7 @@ pub struct VmParams<'a> {
pub fs: Option<Vec<&'a str>>, pub fs: Option<Vec<&'a str>>,
pub pmem: Option<Vec<&'a str>>, pub pmem: Option<Vec<&'a str>>,
pub serial: &'a str, pub serial: &'a str,
pub console: &'a str,
} }
fn parse_size(size: &str) -> Result<u64> { fn parse_size(size: &str) -> Result<u64> {
@ -342,36 +345,36 @@ impl<'a> PmemConfig<'a> {
} }
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum SerialOutputMode { pub enum ConsoleOutputMode {
Off, Off,
Tty, Tty,
File, File,
} }
pub struct SerialConfig<'a> { pub struct ConsoleConfig<'a> {
pub file: Option<&'a Path>, 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<Self> { pub fn parse(param: &'a str) -> Result<Self> {
if param == "off" { if param == "off" {
Ok(Self { Ok(Self {
mode: SerialOutputMode::Off, mode: ConsoleOutputMode::Off,
file: None, file: None,
}) })
} else if param == "tty" { } else if param == "tty" {
Ok(Self { Ok(Self {
mode: SerialOutputMode::Tty, mode: ConsoleOutputMode::Tty,
file: None, file: None,
}) })
} else if param.starts_with("file=") { } else if param.starts_with("file=") {
Ok(Self { Ok(Self {
mode: SerialOutputMode::File, mode: ConsoleOutputMode::File,
file: Some(Path::new(&param[5..])), file: Some(Path::new(&param[5..])),
}) })
} else { } else {
Err(Error::ParseSerialParam) Err(Error::ParseConsoleParam)
} }
} }
} }
@ -386,7 +389,8 @@ pub struct VmConfig<'a> {
pub rng: RngConfig<'a>, pub rng: RngConfig<'a>,
pub fs: Option<Vec<FsConfig<'a>>>, pub fs: Option<Vec<FsConfig<'a>>>,
pub pmem: Option<Vec<PmemConfig<'a>>>, pub pmem: Option<Vec<PmemConfig<'a>>>,
pub serial: SerialConfig<'a>, pub serial: ConsoleConfig<'a>,
pub console: ConsoleConfig<'a>,
} }
impl<'a> VmConfig<'a> { impl<'a> VmConfig<'a> {
@ -427,6 +431,12 @@ impl<'a> VmConfig<'a> {
pmem = Some(pmem_config_list); 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 { Ok(VmConfig {
cpus: CpusConfig::parse(vm_params.cpus)?, cpus: CpusConfig::parse(vm_params.cpus)?,
memory: MemoryConfig::parse(vm_params.memory)?, memory: MemoryConfig::parse(vm_params.memory)?,
@ -437,7 +447,8 @@ impl<'a> VmConfig<'a> {
rng: RngConfig::parse(vm_params.rng)?, rng: RngConfig::parse(vm_params.rng)?,
fs, fs,
pmem, pmem,
serial: SerialConfig::parse(vm_params.serial)?, serial,
console,
}) })
} }
} }

View File

@ -21,7 +21,7 @@ extern crate vm_memory;
extern crate vm_virtio; extern crate vm_virtio;
extern crate vmm_sys_util; extern crate vmm_sys_util;
use crate::config::{SerialOutputMode, VmConfig}; use crate::config::{ConsoleOutputMode, VmConfig};
use arch::RegionType; use arch::RegionType;
use devices::ioapic; use devices::ioapic;
use kvm_bindings::{ use kvm_bindings::{
@ -142,6 +142,9 @@ pub enum Error {
/// Write to the serial console failed. /// Write to the serial console failed.
Serial(vmm_sys_util::Error), Serial(vmm_sys_util::Error),
/// Write to the virtio console failed.
Console(vmm_sys_util::Error),
/// Cannot setup terminal in raw mode. /// Cannot setup terminal in raw mode.
SetTerminalRaw(vmm_sys_util::Error), SetTerminalRaw(vmm_sys_util::Error),
@ -189,6 +192,9 @@ pub enum DeviceManagerError {
/// Cannot create virtio-net device /// Cannot create virtio-net device
CreateVirtioNet(vm_virtio::net::Error), CreateVirtioNet(vm_virtio::net::Error),
/// Cannot create virtio-console device
CreateVirtioConsole(io::Error),
/// Cannot create virtio-rng device /// Cannot create virtio-rng device
CreateVirtioRng(io::Error), CreateVirtioRng(io::Error),
@ -236,6 +242,9 @@ pub enum DeviceManagerError {
/// Error creating serial output file /// Error creating serial output file
SerialOutputFileOpen(io::Error), SerialOutputFileOpen(io::Error),
/// Error creating console output file
ConsoleOutputFileOpen(io::Error),
} }
pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>; pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>;
@ -483,6 +492,7 @@ struct DeviceManager {
// Serial port on 0x3f8 // Serial port on 0x3f8
serial: Option<Arc<Mutex<devices::legacy::Serial>>>, serial: Option<Arc<Mutex<devices::legacy::Serial>>>,
console: Option<Arc<vm_virtio::ConsoleInput>>,
// i8042 device for exit // i8042 device for exit
i8042: Arc<Mutex<devices::legacy::I8042Device>>, i8042: Arc<Mutex<devices::legacy::I8042Device>>,
@ -525,12 +535,12 @@ impl DeviceManager {
}; };
let serial_writer: Option<Box<io::Write + Send>> = match vm_cfg.serial.mode { let serial_writer: Option<Box<io::Write + Send>> = match vm_cfg.serial.mode {
SerialOutputMode::File => Some(Box::new( ConsoleOutputMode::File => Some(Box::new(
File::create(vm_cfg.serial.file.unwrap()) File::create(vm_cfg.serial.file.unwrap())
.map_err(DeviceManagerError::SerialOutputFileOpen)?, .map_err(DeviceManagerError::SerialOutputFileOpen)?,
)), )),
SerialOutputMode::Tty => Some(Box::new(stdout())), ConsoleOutputMode::Tty => Some(Box::new(stdout())),
SerialOutputMode::Off => None, ConsoleOutputMode::Off => None,
}; };
let serial = if serial_writer.is_some() { let serial = if serial_writer.is_some() {
// Serial is tied to IRQ #4 // Serial is tied to IRQ #4
@ -632,6 +642,31 @@ impl DeviceManager {
} }
} }
let console_writer: Option<Box<io::Write + Send>> = 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 // Add virtio-rng if required
if let Some(rng_path) = vm_cfg.rng.src.to_str() { if let Some(rng_path) = vm_cfg.rng.src.to_str() {
let virtio_rng_device = let virtio_rng_device =
@ -745,6 +780,7 @@ impl DeviceManager {
io_bus, io_bus,
mmio_bus, mmio_bus,
serial, serial,
console,
i8042, i8042,
exit_evt, exit_evt,
ioapic, ioapic,
@ -1253,7 +1289,7 @@ impl<'a> Vm<'a> {
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
let epoll_fd = self.epoll.as_raw_fd(); 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() io::stdin()
.lock() .lock()
.set_raw_mode() .set_raw_mode()
@ -1276,13 +1312,13 @@ impl<'a> Vm<'a> {
break 'outer; break 'outer;
} }
EpollDispatch::Stdin => { EpollDispatch::Stdin => {
if self.devices.serial.is_some() {
let mut out = [0u8; 64]; let mut out = [0u8; 64];
let count = io::stdin() let count = io::stdin()
.lock() .lock()
.read_raw(&mut out) .read_raw(&mut out)
.map_err(Error::Serial)?; .map_err(Error::Serial)?;
if self.devices.serial.is_some() {
self.devices self.devices
.serial .serial
.as_ref() .as_ref()
@ -1292,6 +1328,14 @@ impl<'a> Vm<'a> {
.queue_input_bytes(&out[..count]) .queue_input_bytes(&out[..count])
.map_err(Error::Serial)?; .map_err(Error::Serial)?;
} }
if self.devices.console.is_some() {
self.devices
.console
.as_ref()
.unwrap()
.queue_input_bytes(&out[..count]);
}
} }
} }
} }