summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2020-03-09 13:16:46 -0700
committerCommit Bot <commit-bot@chromium.org>2020-04-23 07:17:59 +0000
commita7b6a1c897205d8320482eb506167c5df66647b2 (patch)
tree10b3f4ba971bfbd0a03c258193bbd47b8c672aa4 /src
parentb865810340f1b264b407137c4e69ad232194cc5e (diff)
downloadcrosvm-a7b6a1c897205d8320482eb506167c5df66647b2.tar
crosvm-a7b6a1c897205d8320482eb506167c5df66647b2.tar.gz
crosvm-a7b6a1c897205d8320482eb506167c5df66647b2.tar.bz2
crosvm-a7b6a1c897205d8320482eb506167c5df66647b2.tar.lz
crosvm-a7b6a1c897205d8320482eb506167c5df66647b2.tar.xz
crosvm-a7b6a1c897205d8320482eb506167c5df66647b2.tar.zst
crosvm-a7b6a1c897205d8320482eb506167c5df66647b2.zip
arch, main: add virtio-console parsing and creation
This allows the creation of virtio-console devices using the new
hardware=virtio-console parameter to the --serial option.

Also add support for the serial earlycon option, which allows using
virtio-console as the main console device with a traditional serial
device as the early console.  This allows logging during early boot
before PCI device discovery (when virtio-console devices are set up).

BUG=chromium:1059924
TEST=crosvm run -r vm_rootfs.img \
        --serial hardware=serial,type=stdout,console=false,earlycon=true \
        --serial hardware=virtio-console,type=stdout,console=true,stdin=true \
        vm_kernel

Change-Id: Iff48800272b154d49b1da00f3914799089268afe
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2127322
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'src')
-rw-r--r--src/crosvm.rs4
-rw-r--r--src/linux.rs31
-rw-r--r--src/main.rs60
3 files changed, 81 insertions, 14 deletions
diff --git a/src/crosvm.rs b/src/crosvm.rs
index 75e384f..49a08c0 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -16,7 +16,7 @@ use std::os::unix::io::RawFd;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
-use arch::{Pstore, SerialParameters};
+use arch::{Pstore, SerialHardware, SerialParameters};
 use devices::virtio::fs::passthrough;
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::GpuParameters;
@@ -193,7 +193,7 @@ pub struct Config {
     pub display_window_keyboard: bool,
     pub display_window_mouse: bool,
     pub ac97_parameters: Vec<Ac97Parameters>,
-    pub serial_parameters: BTreeMap<u8, SerialParameters>,
+    pub serial_parameters: BTreeMap<(SerialHardware, u8), SerialParameters>,
     pub syslog_tag: Option<String>,
     pub virtio_single_touch: Option<TouchDeviceOption>,
     pub virtio_trackpad: Option<TouchDeviceOption>,
diff --git a/src/linux.rs b/src/linux.rs
index c469e0c..8690ff7 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -29,7 +29,7 @@ use libc::{self, c_int, gid_t, uid_t};
 
 #[cfg(feature = "gpu")]
 use devices::virtio::EventDevice;
-use devices::virtio::{self, VirtioDevice};
+use devices::virtio::{self, Console, VirtioDevice};
 use devices::{
     self, Ac97Backend, Ac97Dev, HostBackendDeviceProvider, PciDevice, VfioContainer, VfioDevice,
     VfioPciDevice, VirtioPciDevice, XhciController,
@@ -61,7 +61,10 @@ use vm_control::{
 };
 
 use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption};
-use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents, VmImage};
+use arch::{
+    self, LinuxArch, RunnableLinuxVm, SerialHardware, SerialParameters, VirtioDeviceStub,
+    VmComponents, VmImage,
+};
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 use aarch64::AArch64 as Arch;
@@ -82,6 +85,7 @@ pub enum Error {
     ChownTpmStorage(sys_util::Error),
     CloneEventFd(sys_util::Error),
     CreateAc97(devices::PciDeviceError),
+    CreateConsole(arch::serial::Error),
     CreateDiskError(disk::Error),
     CreateEventFd(sys_util::Error),
     CreatePollContext(sys_util::Error),
@@ -166,6 +170,7 @@ impl Display for Error {
             ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e),
             CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e),
             CreateAc97(e) => write!(f, "failed to create ac97 device: {}", e),
+            CreateConsole(e) => write!(f, "failed to create console device: {}", e),
             CreateDiskError(e) => write!(f, "failed to create virtual disk: {}", e),
             CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e),
             CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
@@ -963,6 +968,19 @@ fn create_pmem_device(
     })
 }
 
+fn create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult {
+    let mut keep_fds = Vec::new();
+    let evt = EventFd::new().map_err(Error::CreateEventFd)?;
+    let dev = param
+        .create_serial_device::<Console>(&evt, &mut keep_fds)
+        .map_err(Error::CreateConsole)?;
+
+    Ok(VirtioDeviceStub {
+        dev: Box::new(dev),
+        jail: simple_jail(&cfg, "serial")?, // TODO(dverkamp): use a separate policy for console?
+    })
+}
+
 // gpu_device_socket is not used when GPU support is disabled.
 #[cfg_attr(not(feature = "gpu"), allow(unused_variables))]
 fn create_virtio_devices(
@@ -979,6 +997,15 @@ fn create_virtio_devices(
 ) -> DeviceResult<Vec<VirtioDeviceStub>> {
     let mut devs = Vec::new();
 
+    for (_, param) in cfg
+        .serial_parameters
+        .iter()
+        .filter(|(_k, v)| v.hardware == SerialHardware::VirtioConsole)
+    {
+        let dev = create_console_device(cfg, param)?;
+        devs.push(dev);
+    }
+
     for disk in &cfg.disks {
         let disk_device_socket = disk_device_sockets.remove(0);
         devs.push(create_block_device(cfg, disk, disk_device_socket)?);
diff --git a/src/main.rs b/src/main.rs
index 9775d5f..557c630 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,7 +17,7 @@ use std::string::String;
 use std::thread::sleep;
 use std::time::Duration;
 
-use arch::{Pstore, SerialParameters, SerialType};
+use arch::{set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType};
 use audio_streams::StreamEffect;
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
@@ -301,10 +301,12 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
     let mut serial_setting = SerialParameters {
         type_: SerialType::Sink,
+        hardware: SerialHardware::Serial,
         path: None,
         input: None,
         num: 1,
         console: false,
+        earlycon: false,
         stdin: false,
     };
 
@@ -315,6 +317,11 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
 
     for (k, v) in opts {
         match k {
+            "hardware" => {
+                serial_setting.hardware = v
+                    .parse::<SerialHardware>()
+                    .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
+            }
             "type" => {
                 serial_setting.type_ = v
                     .parse::<SerialType>()
@@ -340,6 +347,14 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
                     ))
                 })?
             }
+            "earlycon" => {
+                serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
+                    argument::Error::Syntax(format!(
+                        "serial device earlycon is not parseable: {}",
+                        e,
+                    ))
+                })?
+            }
             "stdin" => {
                 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
                     argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
@@ -549,10 +564,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
         "serial" => {
             let serial_params = parse_serial_options(value.unwrap())?;
             let num = serial_params.num;
-            if cfg.serial_parameters.contains_key(&num) {
+            let key = (serial_params.hardware, num);
+            if cfg.serial_parameters.contains_key(&key) {
                 return Err(argument::Error::TooManyArguments(format!(
-                    "serial num {}",
-                    num
+                    "serial hardware {} num {}",
+                    serial_params.hardware, num,
                 )));
             }
 
@@ -560,8 +576,29 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 for params in cfg.serial_parameters.values() {
                     if params.console {
                         return Err(argument::Error::TooManyArguments(format!(
-                            "serial device {} already set as console",
-                            params.num
+                            "{} device {} already set as console",
+                            params.hardware, params.num,
+                        )));
+                    }
+                }
+            }
+
+            if serial_params.earlycon {
+                // Only SerialHardware::Serial supports earlycon= currently.
+                match serial_params.hardware {
+                    SerialHardware::Serial => {}
+                    _ => {
+                        return Err(argument::Error::InvalidValue {
+                            value: serial_params.hardware.to_string().to_owned(),
+                            expected: String::from("earlycon not supported for hardware"),
+                        });
+                    }
+                }
+                for params in cfg.serial_parameters.values() {
+                    if params.earlycon {
+                        return Err(argument::Error::TooManyArguments(format!(
+                            "{} device {} already set as earlycon",
+                            params.hardware, params.num,
                         )));
                     }
                 }
@@ -570,13 +607,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if serial_params.stdin {
                 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
                     return Err(argument::Error::TooManyArguments(format!(
-                        "serial device {} already connected to standard input",
-                        previous_stdin.num
+                        "{} device {} already connected to standard input",
+                        previous_stdin.hardware, previous_stdin.num,
                     )));
                 }
             }
 
-            cfg.serial_parameters.insert(num, serial_params);
+            cfg.serial_parameters.insert(key, serial_params);
         }
         "syslog-tag" => {
             if cfg.syslog_tag.is_some() {
@@ -1231,6 +1268,7 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err
             }
         }
     }
+    set_default_serial_parameters(&mut cfg.serial_parameters);
     Ok(())
 }
 
@@ -1281,14 +1319,16 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
                           capture - Enable audio capture
                           capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec."),
           Argument::value("serial",
-                          "type=TYPE,[num=NUM,path=PATH,input=PATH,console,stdin]",
+                          "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
                           "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
                           Possible key values:
                           type=(stdout,syslog,sink,file) - Where to route the serial device
+                          hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
                           num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
                           path=PATH - The path to the file to write to when type=file
                           input=PATH - The path to the file to read from when not stdin
                           console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
+                          earlycon - Use this serial device as the early console. Can only be given once.
                           stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
                           "),
           Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),