summary refs log tree commit diff
path: root/arch
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2020-02-14 16:46:36 -0800
committerCommit Bot <commit-bot@chromium.org>2020-04-23 07:17:55 +0000
commitfbd6122f0b722f40bb14f0c3a26342fa46d5a89d (patch)
tree237d82fabe09b84b02ed2b05963ab698a8eaf31e /arch
parent2c1417b43a1846d21bc589563460de4b962afcc6 (diff)
downloadcrosvm-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.rs72
-rw-r--r--arch/src/serial.rs269
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)
+}