diff options
author | Trent Begin <tbegin@google.com> | 2019-04-17 13:51:25 -0600 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-05-15 13:36:25 -0700 |
commit | 17ccaadc24b7eaeedac578b87ddca4491c48b25f (patch) | |
tree | 653850131a0dc267b16aa7f60ded7306f146608d /src | |
parent | 6868c0a72ffc556fcd9c48006b673f5774d0d35b (diff) | |
download | crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.tar crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.tar.gz crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.tar.bz2 crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.tar.lz crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.tar.xz crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.tar.zst crosvm-17ccaadc24b7eaeedac578b87ddca4491c48b25f.zip |
crosvm: add cmdline flags for configuring serial outputs in guest machine
This change allows an output to be set for each serial device for a guest machine (stdout, syslog, or sink). BUG=chromium:953983 TEST=FEATURES=test emerge-sarien crosvm; cd sys_util; cargo test; ./build_test; manual testing on x86_64 and aarch_64 Change-Id: I9e7fcb0b296c0f8a5aa8d54b1a74ae801f6badc8 Reviewed-on: https://chromium-review.googlesource.com/1572813 Commit-Ready: Trent Begin <tbegin@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Tested-by: Trent Begin <tbegin@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/linux.rs | 45 | ||||
-rw-r--r-- | src/main.rs | 129 |
2 files changed, 155 insertions, 19 deletions
diff --git a/src/linux.rs b/src/linux.rs index 803a82e..979666e 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -789,7 +789,7 @@ fn create_virtio_devices( } fn create_devices( - cfg: Config, + cfg: &Config, mem: &GuestMemory, exit_evt: &EventFd, wayland_device_socket: WlControlRequestSocket, @@ -1180,17 +1180,22 @@ pub fn run_config(cfg: Config) -> Result<()> { } let sandbox = cfg.sandbox; - let linux = Arch::build_vm(components, cfg.split_irqchip, |m, e| { - create_devices( - cfg, - m, - e, - wayland_device_socket, - balloon_device_socket, - &mut disk_device_sockets, - usb_provider, - ) - }) + let linux = Arch::build_vm( + components, + cfg.split_irqchip, + &cfg.serial_parameters, + |m, e| { + create_devices( + &cfg, + m, + e, + wayland_device_socket, + balloon_device_socket, + &mut disk_device_sockets, + usb_provider, + ) + }, + ) .map_err(Error::BuildVm)?; let _render_node_host = (); @@ -1401,13 +1406,15 @@ fn run_control( warn!("error while reading stdin: {}", e); let _ = poll_ctx.delete(&stdin_handle); } - Ok(count) => { - linux - .stdio_serial - .lock() - .queue_input_bytes(&out[..count]) - .expect("failed to queue bytes into serial port"); - } + Ok(count) => match linux.stdio_serial { + Some(ref stdio_serial) => { + stdio_serial + .lock() + .queue_input_bytes(&out[..count]) + .expect("failed to queue bytes into serial port"); + } + None => {} + }, } } Token::ChildSignal => { diff --git a/src/main.rs b/src/main.rs index 48c1edd..6b948e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ pub mod panic_hook; #[cfg(feature = "plugin")] pub mod plugin; +use std::collections::BTreeMap; use std::fmt; use std::fs::{File, OpenOptions}; use std::net; @@ -20,6 +21,7 @@ use std::string::String; use std::thread::sleep; use std::time::Duration; +use devices::{SerialParameters, SerialType}; use msg_socket::{MsgReceiver, MsgSender, MsgSocket}; use qcow::QcowFile; use sys_util::{ @@ -102,6 +104,8 @@ pub struct Config { software_tpm: bool, cras_audio: bool, null_audio: bool, + serial_parameters: BTreeMap<u8, SerialParameters>, + syslog_tag: Option<String>, virtio_single_touch: Option<TouchDeviceOption>, virtio_trackpad: Option<TouchDeviceOption>, virtio_mouse: Option<PathBuf>, @@ -141,6 +145,8 @@ impl Default for Config { seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR), cras_audio: false, null_audio: false, + serial_parameters: BTreeMap::new(), + syslog_tag: None, virtio_single_touch: None, virtio_trackpad: None, virtio_mouse: None, @@ -221,6 +227,58 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> { Ok(cpuset) } +fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> { + let mut serial_setting = SerialParameters { + type_: SerialType::Sink, + path: None, + num: 0, + console: false, + }; + + let opts = s + .split(",") + .map(|frag| frag.split("=")) + .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); + + for (k, v) in opts { + match k { + "type" => { + serial_setting.type_ = v + .parse::<SerialType>() + .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))? + } + "num" => { + let num = v.parse::<u8>().map_err(|e| { + argument::Error::Syntax(format!("serial device number is not parsable: {}", e)) + })?; + if num < 1 || num > 4 { + return Err(argument::Error::InvalidValue { + value: num.to_string(), + expected: "Serial port num must be between 1 - 4", + }); + } + serial_setting.num = num; + } + "console" => { + serial_setting.console = v.parse::<bool>().map_err(|e| { + argument::Error::Syntax(format!( + "serial device console is not parseable: {}", + e + )) + })? + } + _ => { + return Err(argument::Error::UnknownArgument(format!( + "serial parameter {}", + k + ))); + } + } + } + + Ok(serial_setting) +} + fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> { match name { "" => { @@ -312,6 +370,38 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: "null-audio" => { cfg.null_audio = true; } + "serial" => { + let serial_params = parse_serial_options(value.unwrap())?; + let num = serial_params.num; + if cfg.serial_parameters.contains_key(&num) { + return Err(argument::Error::TooManyArguments(format!( + "serial num {}", + num + ))); + } + + if serial_params.console { + for (_, params) in &cfg.serial_parameters { + if params.console { + return Err(argument::Error::TooManyArguments(format!( + "serial device {} already set as console", + params.num + ))); + } + } + } + + cfg.serial_parameters.insert(num, serial_params); + } + "syslog-tag" => { + if cfg.syslog_tag.is_some() { + return Err(argument::Error::TooManyArguments( + "`syslog-tag` already given".to_owned(), + )); + } + syslog::set_proc_name(value.unwrap()); + cfg.syslog_tag = Some(value.unwrap().to_owned()); + } "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => { let disk_path = PathBuf::from(value.unwrap()); if !disk_path.exists() { @@ -702,6 +792,15 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { Argument::value("mac", "MAC", "MAC address for VM."), Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"), Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"), + Argument::value("serial", + "type=TYPE,[path=PATH,num=NUM,console]", + "Comma seperated key=value pairs for setting up serial devices. Can be given more than once. + Possible key values: + type=(stdout,syslog,sink) - Where to route the serial device + num=(1,2,3,4) - Serial Device Number + console - Use this serial device as the guest console. 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."), Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."), #[cfg(feature = "wl-dmabuf")] Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."), @@ -1271,4 +1370,34 @@ mod tests { fn parse_cpu_set_extra_comma() { parse_cpu_set("0,1,2,").expect_err("parse should have failed"); } + + #[test] + fn parse_serial_vaild() { + parse_serial_options("type=syslog,num=1,console=true").expect("parse should have succeded"); + } + + #[test] + fn parse_serial_invalid_type() { + parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed"); + } + + #[test] + fn parse_serial_invalid_num_upper() { + parse_serial_options("type=syslog,num=5").expect_err("parse should have failed"); + } + + #[test] + fn parse_serial_invalid_num_lower() { + parse_serial_options("type=syslog,num=0").expect_err("parse should have failed"); + } + + #[test] + fn parse_serial_invalid_num_string() { + parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed"); + } + + #[test] + fn parse_serial_invalid_option() { + parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed"); + } } |