summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs145
1 files changed, 110 insertions, 35 deletions
diff --git a/src/main.rs b/src/main.rs
index e33be66..7fd3eca 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;
+use arch::{set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType};
 use audio_streams::StreamEffect;
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
@@ -25,7 +25,7 @@ use crosvm::{
 };
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::{GpuMode, GpuParameters};
-use devices::{Ac97Backend, Ac97Parameters, SerialParameters, SerialType};
+use devices::{Ac97Backend, Ac97Parameters};
 use disk::QcowFile;
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
 use sys_util::{
@@ -74,7 +74,7 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
     let mut cpuset = Vec::new();
     for part in s.split(',') {
         let range: Vec<&str> = part.split('-').collect();
-        if range.len() == 0 || range.len() > 2 {
+        if range.is_empty() || range.len() > 2 {
             return Err(argument::Error::InvalidValue {
                 value: part.to_owned(),
                 expected: String::from("invalid list syntax"),
@@ -117,8 +117,8 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
 
     if let Some(s) = s {
         let opts = s
-            .split(",")
-            .map(|frag| frag.split("="))
+            .split(',')
+            .map(|frag| frag.split('='))
             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
         for (k, v) in opts {
@@ -252,8 +252,8 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
     let mut ac97_params: Ac97Parameters = Default::default();
 
     let opts = s
-        .split(",")
-        .map(|frag| frag.split("="))
+        .split(',')
+        .map(|frag| frag.split('='))
         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
     for (k, v) in opts {
@@ -273,7 +273,7 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
             }
             "capture_effects" => {
                 ac97_params.capture_effects = v
-                    .split("|")
+                    .split('|')
                     .map(|val| {
                         val.parse::<StreamEffect>()
                             .map_err(|e| argument::Error::InvalidValue {
@@ -298,19 +298,27 @@ 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,
     };
 
     let opts = s
-        .split(",")
-        .map(|frag| frag.split("="))
+        .split(',')
+        .map(|frag| frag.split('='))
         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
     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>()
@@ -336,12 +344,33 @@ 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))
-                })?
+                })?;
+                if serial_setting.stdin && serial_setting.input.is_some() {
+                    return Err(argument::Error::TooManyArguments(
+                        "Cannot specify both stdin and input options".to_string(),
+                    ));
+                }
             }
             "path" => serial_setting.path = Some(PathBuf::from(v)),
+            "input" => {
+                if serial_setting.stdin {
+                    return Err(argument::Error::TooManyArguments(
+                        "Cannot specify both stdin and input options".to_string(),
+                    ));
+                }
+                serial_setting.input = Some(PathBuf::from(v));
+            }
             _ => {
                 return Err(argument::Error::UnknownArgument(format!(
                     "serial parameter {}",
@@ -355,7 +384,7 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
 }
 
 fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
-    let components: Vec<&str> = value.split(":").collect();
+    let components: Vec<&str> = value.split(':').collect();
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
@@ -402,7 +431,7 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
 }
 
 fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
-    let components: Vec<&str> = value.split(":").collect();
+    let components: Vec<&str> = value.split(':').collect();
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
@@ -501,7 +530,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 )
         }
         "cpu-affinity" => {
-            if cfg.vcpu_affinity.len() != 0 {
+            if !cfg.vcpu_affinity.is_empty() {
                 return Err(argument::Error::TooManyArguments(
                     "`cpu-affinity` already given".to_owned(),
                 ));
@@ -532,10 +561,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,
                 )));
             }
 
@@ -543,8 +573,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,
                         )));
                     }
                 }
@@ -553,13 +604,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() {
@@ -1030,7 +1081,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let reader = BufReader::new(file);
             for l in reader.lines() {
                 let line = l.unwrap();
-                let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim();
+                let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
                 if !trimmed_line.is_empty() {
                     let mount = parse_plugin_mount_option(trimmed_line)?;
                     cfg.plugin_mounts.push(mount);
@@ -1049,7 +1100,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let reader = BufReader::new(file);
             for l in reader.lines() {
                 let line = l.unwrap();
-                let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim();
+                let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
                 if !trimmed_line.is_empty() {
                     let map = parse_plugin_gid_map_option(trimmed_line)?;
                     cfg.plugin_gid_maps.push(map);
@@ -1084,7 +1135,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "`single-touch` already given".to_owned(),
                 ));
             }
-            let mut it = value.unwrap().split(":");
+            let mut it = value.unwrap().split(':');
 
             let mut single_touch_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
@@ -1102,7 +1153,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "`trackpad` already given".to_owned(),
                 ));
             }
-            let mut it = value.unwrap().split(":");
+            let mut it = value.unwrap().split(':');
 
             let mut trackpad_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
@@ -1214,6 +1265,7 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err
             }
         }
     }
+    set_default_serial_parameters(&mut cfg.serial_parameters);
     Ok(())
 }
 
@@ -1326,10 +1378,9 @@ Possible key values:
     effect value now is EchoCancellation or aec.
 "#,
         ),
-
         Argument::value(
             "serial",
-            "type=TYPE,[num=NUM,path=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.
@@ -1339,16 +1390,26 @@ 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.
@@ -1738,7 +1799,7 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
         println!("Set the balloon size of the crosvm instance to `SIZE` bytes.");
         return Err(());
     }
-    let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
+    let num_bytes = match args.next().unwrap().parse::<u64>() {
         Ok(n) => n,
         Err(_) => {
             error!("Failed to parse number of bytes");
@@ -1750,6 +1811,19 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
     vms_request(&VmRequest::BalloonCommand(command), args)
 }
 
+fn balloon_stats(args: std::env::Args) -> std::result::Result<(), ()> {
+    if args.len() != 1 {
+        print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
+        println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
+        return Err(());
+    }
+    let command = BalloonControlCommand::Stats {};
+    let request = &VmRequest::BalloonCommand(command);
+    let response = handle_request(request, args)?;
+    println!("{}", response);
+    Ok(())
+}
+
 fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
     let arguments = [
         Argument::positional("PATH", "where to create the qcow2 image"),
@@ -1796,7 +1870,7 @@ fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
     .map_err(|e| {
         error!("Unable to parse command line arguments: {}", e);
     })?;
-    if file_path.len() == 0 || !(size.is_some() ^ backing_file.is_some()) {
+    if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
         print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
         println!(
             "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
@@ -1837,11 +1911,11 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
         println!("  resize DISK_INDEX NEW_SIZE VM_SOCKET");
         return Err(());
     }
-    let subcommand: &str = &args.nth(0).unwrap();
+    let subcommand: &str = &args.next().unwrap();
 
     let request = match subcommand {
         "resize" => {
-            let disk_index = match args.nth(0).unwrap().parse::<usize>() {
+            let disk_index = match args.next().unwrap().parse::<usize>() {
                 Ok(n) => n,
                 Err(_) => {
                     error!("Failed to parse disk index");
@@ -1849,7 +1923,7 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
                 }
             };
 
-            let new_size = match args.nth(0).unwrap().parse::<u64>() {
+            let new_size = match args.next().unwrap().parse::<u64>() {
                 Ok(n) => n,
                 Err(_) => {
                     error!("Failed to parse disk size");
@@ -1911,7 +1985,7 @@ type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
 
 fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
     debug!("parse_bus_id_addr: {}", v);
-    let mut ids = v.split(":");
+    let mut ids = v.split(':');
     match (ids.next(), ids.next(), ids.next(), ids.next()) {
         (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
             let bus_id = bus_id
@@ -2069,7 +2143,7 @@ fn pkg_version() -> std::result::Result<(), ()> {
     print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
     match PKG_VERSION {
         Some(v) => println!("-{}", v),
-        None => println!(""),
+        None => println!(),
     }
     Ok(())
 }
@@ -2099,6 +2173,7 @@ fn crosvm_main() -> std::result::Result<(), ()> {
         Some("resume") => resume_vms(args),
         Some("run") => run_vm(args),
         Some("balloon") => balloon_vms(args),
+        Some("balloon_stats") => balloon_stats(args),
         Some("create_qcow2") => create_qcow2(args),
         Some("disk") => disk_cmd(args),
         Some("usb") => modify_usb(args),