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")
.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<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 console = cmd_arguments.value_of("console").unwrap();
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());
@ -123,6 +130,7 @@ fn main() {
fs,
pmem,
serial,
console,
}) {
Ok(config) => config,
Err(e) => {

View File

@ -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<T, Error<'a>>;
@ -66,6 +68,7 @@ pub struct VmParams<'a> {
pub fs: Option<Vec<&'a str>>,
pub pmem: Option<Vec<&'a str>>,
pub serial: &'a str,
pub console: &'a str,
}
fn parse_size(size: &str) -> Result<u64> {
@ -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<Self> {
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(&param[5..])),
})
} else {
Err(Error::ParseSerialParam)
Err(Error::ParseConsoleParam)
}
}
}
@ -386,7 +389,8 @@ pub struct VmConfig<'a> {
pub rng: RngConfig<'a>,
pub fs: Option<Vec<FsConfig<'a>>>,
pub pmem: Option<Vec<PmemConfig<'a>>>,
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,
})
}
}

View File

@ -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<T> = result::Result<T, DeviceManagerError>;
@ -483,6 +492,7 @@ struct DeviceManager {
// Serial port on 0x3f8
serial: Option<Arc<Mutex<devices::legacy::Serial>>>,
console: Option<Arc<vm_virtio::ConsoleInput>>,
// i8042 device for exit
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 {
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<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
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]);
}
}
}
}