summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-03-09 03:02:23 +0000
committerAlyssa Ross <hi@alyssa.is>2020-03-09 03:02:23 +0000
commit1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2 (patch)
tree0f44cc95b797eb50f78bf4246bdeb908f5981d7e /src/main.rs
parentda70e9a5c947c0fef40479cc99ae13f52b4e1065 (diff)
parent9515b05c086c55b9e3fbddbc56fb6eb3e9a510a8 (diff)
downloadcrosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.tar
crosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.tar.gz
crosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.tar.bz2
crosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.tar.lz
crosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.tar.xz
crosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.tar.zst
crosvm-1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2.zip
Merge remote-tracking branch 'origin/master' into master
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs295
1 files changed, 243 insertions, 52 deletions
diff --git a/src/main.rs b/src/main.rs
index d0b12c1..7688134 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,7 +22,7 @@ use crosvm::{
     linux, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
 };
 #[cfg(feature = "gpu")]
-use devices::virtio::gpu::{GpuMode, GpuParameters, DEFAULT_GPU_PARAMS};
+use devices::virtio::gpu::{GpuMode, GpuParameters};
 use devices::{SerialParameters, SerialType};
 use disk::QcowFile;
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
@@ -114,7 +114,7 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
 
 #[cfg(feature = "gpu")]
 fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
-    let mut gpu_params = DEFAULT_GPU_PARAMS;
+    let mut gpu_params: GpuParameters = Default::default();
 
     if let Some(s) = s {
         let opts = s
@@ -124,12 +124,37 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
 
         for (k, v) in opts {
             match k {
+                // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
+                // times if the user specifies several modes (--gpu=2d,3d,gfxstream)
                 "2d" | "2D" => {
                     gpu_params.mode = GpuMode::Mode2D;
                 }
                 "3d" | "3D" => {
                     gpu_params.mode = GpuMode::Mode3D;
                 }
+                #[cfg(feature = "gfxstream")]
+                "gfxstream" => {
+                    gpu_params.mode = GpuMode::ModeGfxStream;
+                }
+                // Preferred: Specifying --gpu,backend=<mode>
+                "backend" => match v {
+                    "2d" | "2D" => {
+                        gpu_params.mode = GpuMode::Mode2D;
+                    }
+                    "3d" | "3D" => {
+                        gpu_params.mode = GpuMode::Mode3D;
+                    }
+                    #[cfg(feature = "gfxstream")]
+                    "gfxstream" => {
+                        gpu_params.mode = GpuMode::ModeGfxStream;
+                    }
+                    _ => {
+                        return Err(argument::Error::InvalidValue {
+                            value: v.to_string(),
+                            expected: "gpu parameter 'backend' should be one of (2d|3d|gfxstream)",
+                        });
+                    }
+                },
                 "egl" => match v {
                     "true" | "" => {
                         gpu_params.renderer_use_egl = true;
@@ -982,12 +1007,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let mut single_touch_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
             if let Some(width) = it.next() {
-                single_touch_spec.width = width.trim().parse().unwrap();
+                single_touch_spec.set_width(width.trim().parse().unwrap());
             }
             if let Some(height) = it.next() {
-                single_touch_spec.height = height.trim().parse().unwrap();
+                single_touch_spec.set_height(height.trim().parse().unwrap());
             }
-
             cfg.virtio_single_touch = Some(single_touch_spec);
         }
         "trackpad" => {
@@ -1001,12 +1025,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let mut trackpad_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
             if let Some(width) = it.next() {
-                trackpad_spec.width = width.trim().parse().unwrap();
+                trackpad_spec.set_width(width.trim().parse().unwrap());
             }
             if let Some(height) = it.next() {
-                trackpad_spec.height = height.trim().parse().unwrap();
+                trackpad_spec.set_height(height.trim().parse().unwrap());
             }
-
             cfg.virtio_trackpad = Some(trackpad_spec);
         }
         "mouse" => {
@@ -1074,6 +1097,44 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
     Ok(())
 }
 
+fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
+    if cfg.executable_path.is_none() {
+        return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
+    }
+    if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
+        if cfg.host_ip.is_none() {
+            return Err(argument::Error::ExpectedArgument(
+                "`host_ip` missing from network config".to_owned(),
+            ));
+        }
+        if cfg.netmask.is_none() {
+            return Err(argument::Error::ExpectedArgument(
+                "`netmask` missing from network config".to_owned(),
+            ));
+        }
+        if cfg.mac_address.is_none() {
+            return Err(argument::Error::ExpectedArgument(
+                "`mac` missing from network config".to_owned(),
+            ));
+        }
+    }
+    if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
+        return Err(argument::Error::ExpectedArgument(
+            "`plugin-root` requires `plugin`".to_owned(),
+        ));
+    }
+    #[cfg(feature = "gpu")]
+    {
+        if let Some(gpu_parameters) = cfg.gpu_parameters.as_ref() {
+            let (width, height) = (gpu_parameters.display_width, gpu_parameters.display_height);
+            if let Some(virtio_single_touch) = cfg.virtio_single_touch.as_mut() {
+                virtio_single_touch.set_default_size(width, height);
+            }
+        }
+    }
+    Ok(())
+}
+
 fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
     let arguments =
         &[Argument::positional("KERNEL", "bzImage of kernel to run."),
@@ -1264,6 +1325,8 @@ will be added each time this argument is given.
 Comma separated key=value pairs for setting up a virtio-gpu device.
 
 Possible key values:
+  backend=(2d|3d|gfxstream)
+    Which backend to use for virtio-gpu (determining rendering protocol).
 
   width=INT
     The width of the virtual display connected to the virtio-gpu.
@@ -1321,34 +1384,7 @@ Enable split-irqchip support.
     let match_res = set_arguments(args, &arguments[..], |name, value| {
         set_argument(&mut cfg, name, value)
     })
-    .and_then(|_| {
-        if cfg.executable_path.is_none() {
-            return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
-        }
-        if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
-            if cfg.host_ip.is_none() {
-                return Err(argument::Error::ExpectedArgument(
-                    "`host_ip` missing from network config".to_owned(),
-                ));
-            }
-            if cfg.netmask.is_none() {
-                return Err(argument::Error::ExpectedArgument(
-                    "`netmask` missing from network config".to_owned(),
-                ));
-            }
-            if cfg.mac_address.is_none() {
-                return Err(argument::Error::ExpectedArgument(
-                    "`mac` missing from network config".to_owned(),
-                ));
-            }
-        }
-        if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
-            return Err(argument::Error::ExpectedArgument(
-                "`plugin-root` requires `plugin`".to_owned(),
-            ));
-        }
-        Ok(())
-    });
+    .and_then(|_| validate_arguments(&mut cfg));
 
     match match_res {
         #[cfg(feature = "plugin")]
@@ -1475,34 +1511,82 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
     vms_request(&VmRequest::BalloonCommand(command), args)
 }
 
-fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
-    if args.len() != 2 {
-        print_help("crosvm create_qcow2", "PATH SIZE", &[]);
-        println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
+fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
+    let arguments = [
+        Argument::positional("PATH", "where to create the qcow2 image"),
+        Argument::positional("[SIZE]", "the expanded size of the image"),
+        Argument::value(
+            "backing_file",
+            "path/to/file",
+            " the file to back the image",
+        ),
+    ];
+    let mut positional_index = 0;
+    let mut file_path = String::from("");
+    let mut size: Option<u64> = None;
+    let mut backing_file: Option<String> = None;
+    set_arguments(args, &arguments[..], |name, value| {
+        match (name, positional_index) {
+            ("", 0) => {
+                // NAME
+                positional_index += 1;
+                file_path = value.unwrap().to_owned();
+            }
+            ("", 1) => {
+                // [SIZE]
+                positional_index += 1;
+                size = Some(value.unwrap().parse::<u64>().map_err(|_| {
+                    argument::Error::InvalidValue {
+                        value: value.unwrap().to_owned(),
+                        expected: "SIZE should be a nonnegative integer",
+                    }
+                })?);
+            }
+            ("", _) => {
+                return Err(argument::Error::TooManyArguments(
+                    "Expected at most 2 positional arguments".to_owned(),
+                ));
+            }
+            ("backing_file", _) => {
+                backing_file = value.map(|x| x.to_owned());
+            }
+            _ => unreachable!(),
+        };
+        Ok(())
+    })
+    .map_err(|e| {
+        error!("Unable to parse command line arguments: {}", e);
+    })?;
+    if file_path.len() == 0 || !(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
+with a '--backing_file'."
+        );
         return Err(());
     }
-    let file_path = args.nth(0).unwrap();
-    let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
-        Ok(n) => n,
-        Err(_) => {
-            error!("Failed to parse size of the disk.");
-            return Err(());
-        }
-    };
 
     let file = OpenOptions::new()
         .create(true)
         .read(true)
         .write(true)
+        .truncate(true)
         .open(&file_path)
         .map_err(|e| {
             error!("Failed opening qcow file at '{}': {}", file_path, e);
         })?;
 
-    QcowFile::new(file, size).map_err(|e| {
-        error!("Failed to create qcow file at '{}': {}", file_path, e);
-    })?;
-
+    match (size, backing_file) {
+        (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
+            error!("Failed to create qcow file at '{}': {}", file_path, e);
+        })?,
+        (None, Some(backing_file)) => {
+            QcowFile::new_from_backing(file, &backing_file).map_err(|e| {
+                error!("Failed to create qcow file at '{}': {}", file_path, e);
+            })?
+        }
+        _ => unreachable!(),
+    };
     Ok(())
 }
 
@@ -1811,6 +1895,7 @@ fn main() {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
 
     #[test]
     fn parse_cpu_set_single() {
@@ -2001,4 +2086,110 @@ mod tests {
         set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
             .expect_err("parse should fail because count is not a number");
     }
+
+    #[test]
+    fn single_touch_spec_and_track_pad_spec_default_size() {
+        let mut config = Config::default();
+        config
+            .executable_path
+            .replace(Executable::Kernel(PathBuf::from("kernel")));
+        set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
+        set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
+        validate_arguments(&mut config).unwrap();
+        assert_eq!(
+            config.virtio_single_touch.unwrap().get_size(),
+            (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
+        );
+        assert_eq!(
+            config.virtio_trackpad.unwrap().get_size(),
+            (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
+        );
+    }
+
+    #[cfg(feature = "gpu")]
+    #[test]
+    fn single_touch_spec_default_size_from_gpu() {
+        let width = 12345u32;
+        let height = 54321u32;
+        let mut config = Config::default();
+        config
+            .executable_path
+            .replace(Executable::Kernel(PathBuf::from("kernel")));
+        set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
+        set_argument(
+            &mut config,
+            "gpu",
+            Some(&format!("width={},height={}", width, height)),
+        )
+        .unwrap();
+        validate_arguments(&mut config).unwrap();
+        assert_eq!(
+            config.virtio_single_touch.unwrap().get_size(),
+            (width, height)
+        );
+    }
+
+    #[test]
+    fn single_touch_spec_and_track_pad_spec_with_size() {
+        let width = 12345u32;
+        let height = 54321u32;
+        let mut config = Config::default();
+        config
+            .executable_path
+            .replace(Executable::Kernel(PathBuf::from("kernel")));
+        set_argument(
+            &mut config,
+            "single-touch",
+            Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
+        )
+        .unwrap();
+        set_argument(
+            &mut config,
+            "trackpad",
+            Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
+        )
+        .unwrap();
+        validate_arguments(&mut config).unwrap();
+        assert_eq!(
+            config.virtio_single_touch.unwrap().get_size(),
+            (width, height)
+        );
+        assert_eq!(config.virtio_trackpad.unwrap().get_size(), (width, height));
+    }
+
+    #[cfg(feature = "gpu")]
+    #[test]
+    fn single_touch_spec_with_size_independent_from_gpu() {
+        let touch_width = 12345u32;
+        let touch_height = 54321u32;
+        let display_width = 1234u32;
+        let display_height = 5432u32;
+        let mut config = Config::default();
+        config
+            .executable_path
+            .replace(Executable::Kernel(PathBuf::from("kernel")));
+        set_argument(
+            &mut config,
+            "single-touch",
+            Some(&format!(
+                "/dev/single-touch-test:{}:{}",
+                touch_width, touch_height
+            )),
+        )
+        .unwrap();
+        set_argument(
+            &mut config,
+            "gpu",
+            Some(&format!(
+                "width={},height={}",
+                display_width, display_height
+            )),
+        )
+        .unwrap();
+        validate_arguments(&mut config).unwrap();
+        assert_eq!(
+            config.virtio_single_touch.unwrap().get_size(),
+            (touch_width, touch_height)
+        );
+    }
 }