diff options
author | Daniel Verkamp <dverkamp@chromium.org> | 2020-02-14 16:46:36 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-04-23 07:17:55 +0000 |
commit | fbd6122f0b722f40bb14f0c3a26342fa46d5a89d (patch) | |
tree | 237d82fabe09b84b02ed2b05963ab698a8eaf31e /arch | |
parent | 2c1417b43a1846d21bc589563460de4b962afcc6 (diff) | |
download | crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.tar crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.tar.gz crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.tar.bz2 crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.tar.lz crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.tar.xz crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.tar.zst crosvm-fbd6122f0b722f40bb14f0c3a26342fa46d5a89d.zip |
arch, devices: move serial creation to arch
Split the serial code into two parts: - Configuration and setup: arch/src/serial.rs - Serial device emulation: devices/src/serial.rs No change in functionality - this is just preparation for generalizing the command line parsing/setup code so that it can be used with virtio console devices as well. BUG=chromium:1059924 TEST=emerge-nami crosvm TEST=emerge-kevin crosvm Change-Id: I0aaf9dd6f8096eac4a17077ab5bf569f57d64ff5 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2127319 Reviewed-by: Dylan Reid <dgreid@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/src/lib.rs | 72 | ||||
-rw-r--r-- | arch/src/serial.rs | 269 |
2 files changed, 275 insertions, 66 deletions
diff --git a/arch/src/lib.rs b/arch/src/lib.rs index ab08c21..9ba1f27 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -5,6 +5,7 @@ pub mod android; pub mod fdt; pub mod pstore; +pub mod serial; use std::collections::BTreeMap; use std::error::Error as StdError; @@ -19,7 +20,6 @@ use devices::split_irqchip_common::GsiRelay; use devices::virtio::VirtioDevice; use devices::{ Bus, BusDevice, BusError, PciDevice, PciDeviceError, PciInterruptPin, PciRoot, ProxyDevice, - SerialParameters, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR, }; use io_jail::Minijail; use kvm::{IoeventAddress, Kvm, Vcpu, Vm}; @@ -28,6 +28,10 @@ use sync::Mutex; use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError}; use vm_control::VmIrqRequestSocket; +pub use serial::{ + add_serial_devices, get_serial_tty_string, SerialParameters, SerialType, SERIAL_ADDR, +}; + pub enum VmImage { Kernel(File), Bios(File), @@ -119,7 +123,7 @@ pub enum DeviceRegistrationError { // Unable to create a pipe. CreatePipe(sys_util::Error), // Unable to create serial device from serial parameters - CreateSerialDevice(devices::SerialError), + CreateSerialDevice(serial::Error), /// Could not clone an event fd. EventFdClone(sys_util::Error), /// Could not create an event fd. @@ -258,70 +262,6 @@ pub fn generate_pci_root( Ok((root, pci_irqs, pid_labels)) } -/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial -/// port number and serial device to be used for stdout if defined. -/// -/// # Arguments -/// -/// * `io_bus` - Bus to add the devices to -/// * `com_evt_1_3` - eventfd for com1 and com3 -/// * `com_evt_1_4` - eventfd for com2 and com4 -/// * `io_bus` - Bus to add the devices to -/// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not -/// provided for a port, then it will use the default configuation. -pub fn add_serial_devices( - io_bus: &mut Bus, - com_evt_1_3: &EventFd, - com_evt_2_4: &EventFd, - serial_parameters: &BTreeMap<u8, SerialParameters>, - serial_jail: Option<Minijail>, -) -> Result<Option<u8>, DeviceRegistrationError> { - let mut stdio_serial_num = None; - - for x in 0..=3 { - let com_evt = match x { - 0 => com_evt_1_3, - 1 => com_evt_2_4, - 2 => com_evt_1_3, - 3 => com_evt_2_4, - _ => com_evt_1_3, - }; - - let param = serial_parameters - .get(&(x + 1)) - .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]); - - if param.console { - stdio_serial_num = Some(x + 1); - } - - let mut preserved_fds = Vec::new(); - let com = param - .create_serial_device(&com_evt, &mut preserved_fds) - .map_err(DeviceRegistrationError::CreateSerialDevice)?; - - match serial_jail.as_ref() { - Some(jail) => { - let com = Arc::new(Mutex::new( - ProxyDevice::new(com, &jail, preserved_fds) - .map_err(DeviceRegistrationError::ProxyDeviceCreation)?, - )); - io_bus - .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false) - .unwrap(); - } - None => { - let com = Arc::new(Mutex::new(com)); - io_bus - .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false) - .unwrap(); - } - } - } - - Ok(stdio_serial_num) -} - /// Errors for image loading. #[derive(Debug)] pub enum LoadImageError { diff --git a/arch/src/serial.rs b/arch/src/serial.rs new file mode 100644 index 0000000..58947b5 --- /dev/null +++ b/arch/src/serial.rs @@ -0,0 +1,269 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::collections::BTreeMap; +use std::fmt::{self, Display}; +use std::fs::File; +use std::io::{self, stdin, stdout}; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; + +use devices::{Bus, ProxyDevice, Serial}; +use io_jail::Minijail; +use sync::Mutex; +use sys_util::{read_raw_stdin, syslog, EventFd}; + +use crate::DeviceRegistrationError; + +#[derive(Debug)] +pub enum Error { + CloneEventFd(sys_util::Error), + FileError(std::io::Error), + InvalidSerialType(String), + PathRequired, + Unimplemented(SerialType), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + + match self { + CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e), + FileError(e) => write!(f, "unable to open/create file: {}", e), + InvalidSerialType(e) => write!(f, "invalid serial type: {}", e), + PathRequired => write!(f, "serial device type file requires a path"), + Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()), + } + } +} + +/// Enum for possible type of serial devices +#[derive(Debug)] +pub enum SerialType { + File, + Stdout, + Sink, + Syslog, + UnixSocket, // NOT IMPLEMENTED +} + +impl Display for SerialType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match &self { + SerialType::File => "File".to_string(), + SerialType::Stdout => "Stdout".to_string(), + SerialType::Sink => "Sink".to_string(), + SerialType::Syslog => "Syslog".to_string(), + SerialType::UnixSocket => "UnixSocket".to_string(), + }; + + write!(f, "{}", s) + } +} + +impl FromStr for SerialType { + type Err = Error; + fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { + match s { + "file" | "File" => Ok(SerialType::File), + "stdout" | "Stdout" => Ok(SerialType::Stdout), + "sink" | "Sink" => Ok(SerialType::Sink), + "syslog" | "Syslog" => Ok(SerialType::Syslog), + "unix" | "UnixSocket" => Ok(SerialType::UnixSocket), + _ => Err(Error::InvalidSerialType(s.to_string())), + } + } +} + +/// Holds the parameters for a serial device +#[derive(Debug)] +pub struct SerialParameters { + pub type_: SerialType, + pub path: Option<PathBuf>, + pub input: Option<PathBuf>, + pub num: u8, + pub console: bool, + pub stdin: bool, +} + +impl SerialParameters { + /// Helper function to create a serial device from the defined parameters. + /// + /// # Arguments + /// * `evt_fd` - eventfd used for interrupt events + /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child + /// process. `evt_fd` will always be added to this vector by this function. + pub fn create_serial_device( + &self, + evt_fd: &EventFd, + keep_fds: &mut Vec<RawFd>, + ) -> std::result::Result<Serial, Error> { + let evt_fd = evt_fd.try_clone().map_err(Error::CloneEventFd)?; + keep_fds.push(evt_fd.as_raw_fd()); + let input: Option<Box<dyn io::Read + Send>> = if let Some(input_path) = &self.input { + let input_file = File::open(input_path.as_path()).map_err(Error::FileError)?; + keep_fds.push(input_file.as_raw_fd()); + Some(Box::new(input_file)) + } else if self.stdin { + keep_fds.push(stdin().as_raw_fd()); + // This wrapper is used in place of the libstd native version because we don't want + // buffering for stdin. + struct StdinWrapper; + impl io::Read for StdinWrapper { + fn read(&mut self, out: &mut [u8]) -> io::Result<usize> { + read_raw_stdin(out).map_err(|e| e.into()) + } + } + Some(Box::new(StdinWrapper)) + } else { + None + }; + match self.type_ { + SerialType::Stdout => { + keep_fds.push(stdout().as_raw_fd()); + Ok(Serial::new(evt_fd, input, Some(Box::new(stdout())))) + } + SerialType::Sink => Ok(Serial::new(evt_fd, input, None)), + SerialType::Syslog => { + syslog::push_fds(keep_fds); + Ok(Serial::new( + evt_fd, + input, + Some(Box::new(syslog::Syslogger::new( + syslog::Priority::Info, + syslog::Facility::Daemon, + ))), + )) + } + SerialType::File => match &self.path { + Some(path) => { + let file = File::create(path.as_path()).map_err(Error::FileError)?; + keep_fds.push(file.as_raw_fd()); + Ok(Serial::new(evt_fd, input, Some(Box::new(file)))) + } + _ => Err(Error::PathRequired), + }, + SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)), + } + } +} + +// Structure for holding the default configuration of the serial devices. +const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [ + SerialParameters { + type_: SerialType::Stdout, + path: None, + input: None, + num: 1, + console: true, + stdin: true, + }, + SerialParameters { + type_: SerialType::Sink, + path: None, + input: None, + num: 2, + console: false, + stdin: false, + }, + SerialParameters { + type_: SerialType::Sink, + path: None, + input: None, + num: 3, + console: false, + stdin: false, + }, + SerialParameters { + type_: SerialType::Sink, + path: None, + input: None, + num: 4, + console: false, + stdin: false, + }, +]; + +/// Address for Serial ports in x86 +pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8]; + +/// String representations of serial devices +const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"]; + +/// Helper function to get the tty string of a serial device based on the port number. Will default +/// to ttyS0 if an invalid number is given. +pub fn get_serial_tty_string(stdio_serial_num: u8) -> String { + stdio_serial_num + .checked_sub(1) + .and_then(|i| SERIAL_TTY_STRINGS.get(i as usize)) + .unwrap_or(&SERIAL_TTY_STRINGS[0]) + .to_string() +} + +/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial +/// port number and serial device to be used for stdout if defined. +/// +/// # Arguments +/// +/// * `io_bus` - Bus to add the devices to +/// * `com_evt_1_3` - eventfd for com1 and com3 +/// * `com_evt_1_4` - eventfd for com2 and com4 +/// * `io_bus` - Bus to add the devices to +/// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not +/// provided for a port, then it will use the default configuation. +pub fn add_serial_devices( + io_bus: &mut Bus, + com_evt_1_3: &EventFd, + com_evt_2_4: &EventFd, + serial_parameters: &BTreeMap<u8, SerialParameters>, + serial_jail: Option<Minijail>, +) -> Result<Option<u8>, DeviceRegistrationError> { + let mut stdio_serial_num = None; + + for x in 0..=3 { + let com_evt = match x { + 0 => com_evt_1_3, + 1 => com_evt_2_4, + 2 => com_evt_1_3, + 3 => com_evt_2_4, + _ => com_evt_1_3, + }; + + let param = serial_parameters + .get(&(x + 1)) + .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]); + + if param.console { + stdio_serial_num = Some(x + 1); + } + + let mut preserved_fds = Vec::new(); + let com = param + .create_serial_device(&com_evt, &mut preserved_fds) + .map_err(DeviceRegistrationError::CreateSerialDevice)?; + + match serial_jail.as_ref() { + Some(jail) => { + let com = Arc::new(Mutex::new( + ProxyDevice::new(com, &jail, preserved_fds) + .map_err(DeviceRegistrationError::ProxyDeviceCreation)?, + )); + io_bus + .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false) + .unwrap(); + } + None => { + let com = Arc::new(Mutex::new(com)); + io_bus + .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false) + .unwrap(); + } + } + } + + Ok(stdio_serial_num) +} |