diff options
-rw-r--r-- | devices/src/virtio/gpu/mod.rs | 24 | ||||
-rw-r--r-- | src/crosvm.rs | 51 | ||||
-rw-r--r-- | src/linux.rs | 41 | ||||
-rw-r--r-- | src/main.rs | 188 |
4 files changed, 237 insertions, 67 deletions
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs index aa674a8..49fdff4 100644 --- a/devices/src/virtio/gpu/mod.rs +++ b/devices/src/virtio/gpu/mod.rs @@ -64,16 +64,6 @@ pub struct GpuParameters { pub mode: GpuMode, } -pub const DEFAULT_GPU_PARAMS: GpuParameters = GpuParameters { - display_width: DEFAULT_DISPLAY_WIDTH, - display_height: DEFAULT_DISPLAY_HEIGHT, - renderer_use_egl: true, - renderer_use_gles: true, - renderer_use_glx: false, - renderer_use_surfaceless: true, - mode: GpuMode::Mode3D, -}; - // First queue is for virtio gpu commands. Second queue is for cursor commands, which we expect // there to be fewer of. const QUEUE_SIZES: &[u16] = &[256, 16]; @@ -83,6 +73,20 @@ const GPU_BAR_NUM: u8 = 4; const GPU_BAR_OFFSET: u64 = 0; const GPU_BAR_SIZE: u64 = 1 << 33; +impl Default for GpuParameters { + fn default() -> Self { + GpuParameters { + display_width: DEFAULT_DISPLAY_WIDTH, + display_height: DEFAULT_DISPLAY_HEIGHT, + renderer_use_egl: true, + renderer_use_gles: true, + renderer_use_glx: false, + renderer_use_surfaceless: true, + mode: GpuMode::Mode3D, + } + } +} + /// A virtio-gpu backend state tracker which supports display and potentially accelerated rendering. /// /// Commands from the virtio-gpu protocol can be submitted here using the methods, and they will be diff --git a/src/crosvm.rs b/src/crosvm.rs index 082e43c..e0ddf06 100644 --- a/src/crosvm.rs +++ b/src/crosvm.rs @@ -13,7 +13,7 @@ pub mod plugin; use std::collections::BTreeMap; use std::net; use std::os::unix::io::RawFd; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use arch::Pstore; @@ -57,23 +57,58 @@ pub struct GidMap { pub count: u32, } -const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 800; -const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1280; +pub const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 800; +pub const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1280; pub struct TouchDeviceOption { - pub path: PathBuf, - pub width: u32, - pub height: u32, + path: PathBuf, + width: Option<u32>, + height: Option<u32>, + default_width: u32, + default_height: u32, } impl TouchDeviceOption { pub fn new(path: PathBuf) -> TouchDeviceOption { TouchDeviceOption { path, - width: DEFAULT_TOUCH_DEVICE_WIDTH, - height: DEFAULT_TOUCH_DEVICE_HEIGHT, + width: None, + height: None, + default_width: DEFAULT_TOUCH_DEVICE_WIDTH, + default_height: DEFAULT_TOUCH_DEVICE_HEIGHT, } } + + /// Getter for the path to the input event streams. + pub fn get_path(&self) -> &Path { + self.path.as_path() + } + + /// When a user specifies the parameters for a touch device, width and height are optional. + /// If the width and height are missing, default values are used. Default values can be set + /// dynamically, for example from the display sizes specified by the gpu argument. + pub fn set_default_size(&mut self, width: u32, height: u32) { + self.default_width = width; + self.default_height = height; + } + + /// Setter for the width specified by the user. + pub fn set_width(&mut self, width: u32) { + self.width.replace(width); + } + + /// Setter for the height specified by the user. + pub fn set_height(&mut self, height: u32) { + self.height.replace(height); + } + + /// If the user specifies the size, use it. Otherwise, use the default values. + pub fn get_size(&self) -> (u32, u32) { + ( + self.width.unwrap_or(self.default_width), + self.height.unwrap_or(self.default_height), + ) + } } pub enum SharedDirKind { diff --git a/src/linux.rs b/src/linux.rs index 84edf5c..ab6c64c 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -62,7 +62,10 @@ use vm_control::{ VmRunMode, }; -use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption}; +use crate::{ + Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption, + DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH, +}; use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents, VmImage}; @@ -473,13 +476,16 @@ fn create_tpm_device(cfg: &Config) -> DeviceResult { } fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOption) -> DeviceResult { - let socket = single_touch_spec.path.into_unix_stream().map_err(|e| { - error!("failed configuring virtio single touch: {:?}", e); - e - })?; - - let dev = virtio::new_single_touch(socket, single_touch_spec.width, single_touch_spec.height) - .map_err(Error::InputDeviceNew)?; + let socket = single_touch_spec + .get_path() + .into_unix_stream() + .map_err(|e| { + error!("failed configuring virtio single touch: {:?}", e); + e + })?; + + let (width, height) = single_touch_spec.get_size(); + let dev = virtio::new_single_touch(socket, width, height).map_err(Error::InputDeviceNew)?; Ok(VirtioDeviceStub { dev: Box::new(dev), jail: simple_jail(&cfg, "input_device")?, @@ -487,13 +493,13 @@ fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOptio } fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> DeviceResult { - let socket = trackpad_spec.path.into_unix_stream().map_err(|e| { + let socket = trackpad_spec.get_path().into_unix_stream().map_err(|e| { error!("failed configuring virtio trackpad: {}", e); e })?; - let dev = virtio::new_trackpad(socket, trackpad_spec.width, trackpad_spec.height) - .map_err(Error::InputDeviceNew)?; + let (width, height) = trackpad_spec.get_size(); + let dev = virtio::new_trackpad(socket, width, height).map_err(Error::InputDeviceNew)?; Ok(VirtioDeviceStub { dev: Box::new(dev), @@ -1030,8 +1036,17 @@ fn create_virtio_devices( // TODO(nkgold): the width/height here should match the display's height/width. When // those settings are available as CLI options, we should use the CLI options here // as well. - let dev = virtio::new_single_touch(virtio_dev_socket, 1280, 1024) - .map_err(Error::InputDeviceNew)?; + let (single_touch_width, single_touch_height) = cfg + .virtio_single_touch + .as_ref() + .map(|single_touch_spec| single_touch_spec.get_size()) + .unwrap_or((DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)); + let dev = virtio::new_single_touch( + virtio_dev_socket, + single_touch_width, + single_touch_height, + ) + .map_err(Error::InputDeviceNew)?; devs.push(VirtioDeviceStub { dev: Box::new(dev), jail: simple_jail(&cfg, "input_device")?, diff --git a/src/main.rs b/src/main.rs index fb1be25..1e86e47 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 @@ -982,12 +982,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 +1000,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 +1072,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"), @@ -1196,34 +1232,7 @@ writeback=BOOL - Indicates whether the VM can use writeback caching (default: fa 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")] @@ -1683,6 +1692,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() { @@ -1873,4 +1883,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) + ); + } } |