diff options
author | Alyssa Ross <hi@alyssa.is> | 2020-03-09 03:02:23 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2020-03-09 03:02:23 +0000 |
commit | 1fb99239c0e5976cbad2fa8fdc45f15d219f7ed2 (patch) | |
tree | 0f44cc95b797eb50f78bf4246bdeb908f5981d7e /src/main.rs | |
parent | da70e9a5c947c0fef40479cc99ae13f52b4e1065 (diff) | |
parent | 9515b05c086c55b9e3fbddbc56fb6eb3e9a510a8 (diff) | |
download | crosvm-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.rs | 295 |
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) + ); + } } |