blob: 1ba2e321aaae0da89845edf40c9936f306462ad2 [file] [log] [blame]
Zach Reizner639d9672017-05-01 17:57:18 -07001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Steven Richmanf32d0b42020-06-20 21:45:32 -07005//! Runs a virtual machine
Zach Reizner639d9672017-05-01 17:57:18 -07006
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08007pub mod panic_hook;
Zach Reizner29ad3c72017-08-04 15:12:58 -07008
Daniel Verkampc677fb42020-09-08 13:47:49 -07009use std::collections::BTreeMap;
Judy Hsiaod5c1e962020-02-04 12:30:01 +080010use std::default::Default;
Jingkui Wang100e6e42019-03-08 20:41:57 -080011use std::fmt;
12use std::fs::{File, OpenOptions};
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -080013use std::io::{BufRead, BufReader};
Jingkui Wang100e6e42019-03-08 20:41:57 -080014use std::num::ParseIntError;
Jingkui Wang100e6e42019-03-08 20:41:57 -080015use std::path::{Path, PathBuf};
Zach Reizner639d9672017-05-01 17:57:18 -070016use std::string::String;
Zach Reizner39aa26b2017-12-12 18:03:23 -080017use std::thread::sleep;
Stephen Barber56fbf092017-06-29 16:12:14 -070018use std::time::Duration;
Zach Reizner639d9672017-05-01 17:57:18 -070019
Daniel Verkampc677fb42020-09-08 13:47:49 -070020use arch::{
21 set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType,
22 VcpuAffinity,
23};
Michael Hoyle6b196952020-08-02 20:09:41 -070024use base::{
25 debug, error, getpid, info, kill_process_group, net::UnixSeqpacket, reap_child, syslog,
Michael Hoylea596a072020-11-10 19:32:45 -080026 validate_raw_descriptor, warn, FromRawDescriptor, IntoRawDescriptor, RawDescriptor,
27 SafeDescriptor,
Michael Hoyle6b196952020-08-02 20:09:41 -070028};
Zach Reizner267f2c82019-07-31 17:07:27 -070029use crosvm::{
30 argument::{self, print_help, set_arguments, Argument},
Michael Hoylee47a5002020-10-15 16:24:13 -070031 platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -070032 DISK_ID_LEN,
Zach Reizner267f2c82019-07-31 17:07:27 -070033};
Jason Macnakcc7070b2019-11-06 14:48:12 -080034#[cfg(feature = "gpu")]
Kaiyi Libccb4eb2020-02-06 17:53:11 -080035use devices::virtio::gpu::{GpuMode, GpuParameters};
Andrew Scull1590e6f2020-03-18 18:00:47 +000036#[cfg(feature = "audio")]
Daniel Verkampfbd61222020-02-14 16:46:36 -080037use devices::{Ac97Backend, Ac97Parameters};
Daniel Verkampf2eecc42019-12-17 17:04:58 -080038use disk::QcowFile;
David Tolnayfe3ef7d2019-03-08 15:57:49 -080039use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
Jakub Starone7c59052019-04-09 12:31:14 -070040use vm_control::{
Chuanxiao Dong256be3a2020-04-27 16:39:33 +080041 BalloonControlCommand, BatControlCommand, BatControlResult, BatteryType, DiskControlCommand,
42 MaybeOwnedDescriptor, UsbControlCommand, UsbControlResult, VmControlRequestSocket, VmRequest,
43 VmResponse, USB_CONTROL_MAX_PORTS,
Jakub Starone7c59052019-04-09 12:31:14 -070044};
Zach Reizner639d9672017-05-01 17:57:18 -070045
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070046fn executable_is_plugin(executable: &Option<Executable>) -> bool {
Daniel Verkampc26d20b2020-11-04 14:39:31 -080047 matches!(executable, Some(Executable::Plugin(_)))
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070048}
49
Stephen Barbera00753b2017-07-18 13:57:26 -070050// Wait for all children to exit. Return true if they have all exited, false
51// otherwise.
52fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -070053 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -070054 const CHILD_WAIT_MS: u64 = 10;
55 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -070056 loop {
Zach Reizner56158c82017-08-24 13:50:14 -070057 match reap_child() {
58 Ok(0) => break,
59 // We expect ECHILD which indicates that there were no children left.
60 Err(e) if e.errno() == libc::ECHILD => return true,
61 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -080062 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -070063 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -070064 }
Zach Reizner56158c82017-08-24 13:50:14 -070065 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -070066 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -070067 }
68 }
Zach Reizner56158c82017-08-24 13:50:14 -070069 // There's no timeout option for waitpid which reap_child calls internally, so our only
70 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -070071 sleep(Duration::from_millis(CHILD_WAIT_MS));
72 }
73
74 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -080075 false
Stephen Barbera00753b2017-07-18 13:57:26 -070076}
77
Daniel Verkamp107edb32019-04-05 09:58:48 -070078/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
79fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
80 let mut cpuset = Vec::new();
81 for part in s.split(',') {
82 let range: Vec<&str> = part.split('-').collect();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +090083 if range.is_empty() || range.len() > 2 {
Daniel Verkamp107edb32019-04-05 09:58:48 -070084 return Err(argument::Error::InvalidValue {
85 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +080086 expected: String::from("invalid list syntax"),
Daniel Verkamp107edb32019-04-05 09:58:48 -070087 });
88 }
89 let first_cpu: usize = range[0]
90 .parse()
91 .map_err(|_| argument::Error::InvalidValue {
92 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +080093 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -070094 })?;
95 let last_cpu: usize = if range.len() == 2 {
96 range[1]
97 .parse()
98 .map_err(|_| argument::Error::InvalidValue {
99 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800100 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700101 })?
102 } else {
103 first_cpu
104 };
105
106 if last_cpu < first_cpu {
107 return Err(argument::Error::InvalidValue {
108 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800109 expected: String::from("CPU ranges must be from low to high"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700110 });
111 }
112
113 for cpu in first_cpu..=last_cpu {
114 cpuset.push(cpu);
115 }
116 }
117 Ok(cpuset)
118}
119
Daniel Verkampc677fb42020-09-08 13:47:49 -0700120/// Parse a list of guest to host CPU mappings.
121///
122/// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
123/// accepted by `parse_cpu_set`:
124///
125/// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
126fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
127 if s.contains('=') {
128 let mut affinity_map = BTreeMap::new();
129 for cpu_pair in s.split(':') {
130 let assignment: Vec<&str> = cpu_pair.split('=').collect();
131 if assignment.len() != 2 {
132 return Err(argument::Error::InvalidValue {
133 value: cpu_pair.to_owned(),
134 expected: String::from("invalid VCPU assignment syntax"),
135 });
136 }
137 let guest_cpu = assignment[0]
138 .parse()
139 .map_err(|_| argument::Error::InvalidValue {
140 value: assignment[0].to_owned(),
141 expected: String::from("CPU index must be a non-negative integer"),
142 })?;
143 let host_cpu_set = parse_cpu_set(assignment[1])?;
144 if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
145 return Err(argument::Error::InvalidValue {
146 value: cpu_pair.to_owned(),
147 expected: String::from("VCPU index must be unique"),
148 });
149 }
150 }
151 Ok(VcpuAffinity::PerVcpu(affinity_map))
152 } else {
153 Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
154 }
155}
156
Jason Macnakcc7070b2019-11-06 14:48:12 -0800157#[cfg(feature = "gpu")]
158fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
Kaiyi Libccb4eb2020-02-06 17:53:11 -0800159 let mut gpu_params: GpuParameters = Default::default();
Kaiyi Lidd348a42020-07-13 11:49:46 -0700160 #[cfg(feature = "gfxstream")]
161 let mut vulkan_specified = false;
162 #[cfg(feature = "gfxstream")]
163 let mut syncfd_specified = false;
Jason Macnak046ed142020-10-08 09:11:53 -0700164 #[cfg(feature = "gfxstream")]
165 let mut angle_specified = false;
Jason Macnakcc7070b2019-11-06 14:48:12 -0800166
167 if let Some(s) = s {
168 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900169 .split(',')
170 .map(|frag| frag.split('='))
Jason Macnakcc7070b2019-11-06 14:48:12 -0800171 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
172
173 for (k, v) in opts {
174 match k {
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800175 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
176 // times if the user specifies several modes (--gpu=2d,3d,gfxstream)
Jason Macnak327fc242020-01-10 12:45:36 -0800177 "2d" | "2D" => {
178 gpu_params.mode = GpuMode::Mode2D;
179 }
180 "3d" | "3D" => {
181 gpu_params.mode = GpuMode::Mode3D;
182 }
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800183 #[cfg(feature = "gfxstream")]
184 "gfxstream" => {
185 gpu_params.mode = GpuMode::ModeGfxStream;
186 }
187 // Preferred: Specifying --gpu,backend=<mode>
188 "backend" => match v {
189 "2d" | "2D" => {
190 gpu_params.mode = GpuMode::Mode2D;
191 }
192 "3d" | "3D" => {
193 gpu_params.mode = GpuMode::Mode3D;
194 }
195 #[cfg(feature = "gfxstream")]
196 "gfxstream" => {
197 gpu_params.mode = GpuMode::ModeGfxStream;
198 }
199 _ => {
200 return Err(argument::Error::InvalidValue {
201 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800202 expected: String::from(
203 "gpu parameter 'backend' should be one of (2d|3d|gfxstream)",
204 ),
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800205 });
206 }
207 },
Jason Macnakbf195582019-11-20 16:25:49 -0800208 "egl" => match v {
209 "true" | "" => {
210 gpu_params.renderer_use_egl = true;
211 }
212 "false" => {
213 gpu_params.renderer_use_egl = false;
214 }
215 _ => {
216 return Err(argument::Error::InvalidValue {
217 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800218 expected: String::from("gpu parameter 'egl' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800219 });
220 }
221 },
222 "gles" => match v {
223 "true" | "" => {
224 gpu_params.renderer_use_gles = true;
225 }
226 "false" => {
227 gpu_params.renderer_use_gles = false;
228 }
229 _ => {
230 return Err(argument::Error::InvalidValue {
231 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800232 expected: String::from("gpu parameter 'gles' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800233 });
234 }
235 },
236 "glx" => match v {
237 "true" | "" => {
238 gpu_params.renderer_use_glx = true;
239 }
240 "false" => {
241 gpu_params.renderer_use_glx = false;
242 }
243 _ => {
244 return Err(argument::Error::InvalidValue {
245 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800246 expected: String::from("gpu parameter 'glx' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800247 });
248 }
249 },
250 "surfaceless" => match v {
251 "true" | "" => {
252 gpu_params.renderer_use_surfaceless = true;
253 }
254 "false" => {
255 gpu_params.renderer_use_surfaceless = false;
256 }
257 _ => {
258 return Err(argument::Error::InvalidValue {
259 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800260 expected: String::from(
261 "gpu parameter 'surfaceless' should be a boolean",
262 ),
Jason Macnakbf195582019-11-20 16:25:49 -0800263 });
264 }
265 },
Kaiyi Li6404e452020-07-07 19:12:03 -0700266 #[cfg(feature = "gfxstream")]
Kaiyi Lidd348a42020-07-13 11:49:46 -0700267 "syncfd" => {
268 syncfd_specified = true;
269 match v {
270 "true" | "" => {
271 gpu_params.gfxstream_use_syncfd = true;
272 }
273 "false" => {
274 gpu_params.gfxstream_use_syncfd = false;
275 }
276 _ => {
277 return Err(argument::Error::InvalidValue {
278 value: v.to_string(),
279 expected: String::from(
280 "gpu parameter 'syncfd' should be a boolean",
281 ),
282 });
283 }
Kaiyi Li6404e452020-07-07 19:12:03 -0700284 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700285 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700286 #[cfg(feature = "gfxstream")]
Jason Macnak046ed142020-10-08 09:11:53 -0700287 "angle" => {
288 angle_specified = true;
289 match v {
290 "true" | "" => {
291 gpu_params.gfxstream_use_guest_angle = true;
292 }
293 "false" => {
294 gpu_params.gfxstream_use_guest_angle = false;
295 }
296 _ => {
297 return Err(argument::Error::InvalidValue {
298 value: v.to_string(),
299 expected: String::from("gpu parameter 'angle' should be a boolean"),
300 });
301 }
302 }
303 }
304 #[cfg(feature = "gfxstream")]
Kaiyi Lidd348a42020-07-13 11:49:46 -0700305 "vulkan" => {
306 vulkan_specified = true;
307 match v {
308 "true" | "" => {
309 gpu_params.gfxstream_support_vulkan = true;
310 }
311 "false" => {
312 gpu_params.gfxstream_support_vulkan = false;
313 }
314 _ => {
315 return Err(argument::Error::InvalidValue {
316 value: v.to_string(),
317 expected: String::from(
318 "gpu parameter 'vulkan' should be a boolean",
319 ),
320 });
321 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700322 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700323 }
Jason Macnakcc7070b2019-11-06 14:48:12 -0800324 "width" => {
325 gpu_params.display_width =
326 v.parse::<u32>()
327 .map_err(|_| argument::Error::InvalidValue {
328 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800329 expected: String::from(
330 "gpu parameter 'width' must be a valid integer",
331 ),
Jason Macnakcc7070b2019-11-06 14:48:12 -0800332 })?;
333 }
334 "height" => {
335 gpu_params.display_height =
336 v.parse::<u32>()
337 .map_err(|_| argument::Error::InvalidValue {
338 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800339 expected: String::from(
340 "gpu parameter 'height' must be a valid integer",
341 ),
Jason Macnakcc7070b2019-11-06 14:48:12 -0800342 })?;
343 }
John Batesb220eac2020-09-14 17:03:02 -0700344 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
345 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
Jason Macnakcc7070b2019-11-06 14:48:12 -0800346 "" => {}
347 _ => {
348 return Err(argument::Error::UnknownArgument(format!(
349 "gpu parameter {}",
350 k
351 )));
352 }
353 }
354 }
355 }
356
Kaiyi Lidd348a42020-07-13 11:49:46 -0700357 #[cfg(feature = "gfxstream")]
358 {
Jason Macnak046ed142020-10-08 09:11:53 -0700359 if vulkan_specified || syncfd_specified || angle_specified {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700360 match gpu_params.mode {
361 GpuMode::ModeGfxStream => {}
362 _ => {
363 return Err(argument::Error::UnknownArgument(
364 "gpu parameter vulkan and syncfd are only supported for gfxstream backend"
365 .to_string(),
366 ));
367 }
368 }
369 }
370 }
371
Jason Macnakcc7070b2019-11-06 14:48:12 -0800372 Ok(gpu_params)
373}
374
Andrew Scull1590e6f2020-03-18 18:00:47 +0000375#[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800376fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
377 let mut ac97_params: Ac97Parameters = Default::default();
378
379 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900380 .split(',')
381 .map(|frag| frag.split('='))
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800382 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
383
384 for (k, v) in opts {
385 match k {
386 "backend" => {
387 ac97_params.backend =
388 v.parse::<Ac97Backend>()
389 .map_err(|e| argument::Error::InvalidValue {
390 value: v.to_string(),
391 expected: e.to_string(),
392 })?;
393 }
394 "capture" => {
395 ac97_params.capture = v.parse::<bool>().map_err(|e| {
396 argument::Error::Syntax(format!("invalid capture option: {}", e))
397 })?;
398 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800399 _ => {
400 return Err(argument::Error::UnknownArgument(format!(
401 "unknown ac97 parameter {}",
402 k
403 )));
404 }
405 }
406 }
407
408 Ok(ac97_params)
409}
410
Trent Begin17ccaad2019-04-17 13:51:25 -0600411fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
412 let mut serial_setting = SerialParameters {
413 type_: SerialType::Sink,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700414 hardware: SerialHardware::Serial,
Trent Begin17ccaad2019-04-17 13:51:25 -0600415 path: None,
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700416 input: None,
Trent Begin923bab02019-06-17 13:48:06 -0600417 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600418 console: false,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700419 earlycon: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700420 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600421 };
422
423 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900424 .split(',')
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +1000425 .map(|frag| frag.splitn(2, '='))
Trent Begin17ccaad2019-04-17 13:51:25 -0600426 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
427
428 for (k, v) in opts {
429 match k {
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700430 "hardware" => {
431 serial_setting.hardware = v
432 .parse::<SerialHardware>()
433 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
434 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600435 "type" => {
436 serial_setting.type_ = v
437 .parse::<SerialType>()
438 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
439 }
440 "num" => {
441 let num = v.parse::<u8>().map_err(|e| {
442 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
443 })?;
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700444 if num < 1 {
Trent Begin17ccaad2019-04-17 13:51:25 -0600445 return Err(argument::Error::InvalidValue {
446 value: num.to_string(),
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700447 expected: String::from("Serial port num must be at least 1"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600448 });
449 }
450 serial_setting.num = num;
451 }
452 "console" => {
453 serial_setting.console = v.parse::<bool>().map_err(|e| {
454 argument::Error::Syntax(format!(
455 "serial device console is not parseable: {}",
456 e
457 ))
458 })?
459 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700460 "earlycon" => {
461 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
462 argument::Error::Syntax(format!(
463 "serial device earlycon is not parseable: {}",
464 e,
465 ))
466 })?
467 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700468 "stdin" => {
469 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
470 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700471 })?;
472 if serial_setting.stdin && serial_setting.input.is_some() {
473 return Err(argument::Error::TooManyArguments(
474 "Cannot specify both stdin and input options".to_string(),
475 ));
476 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700477 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700478 "path" => serial_setting.path = Some(PathBuf::from(v)),
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700479 "input" => {
480 if serial_setting.stdin {
481 return Err(argument::Error::TooManyArguments(
482 "Cannot specify both stdin and input options".to_string(),
483 ));
484 }
485 serial_setting.input = Some(PathBuf::from(v));
486 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600487 _ => {
488 return Err(argument::Error::UnknownArgument(format!(
489 "serial parameter {}",
490 k
491 )));
492 }
493 }
494 }
495
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700496 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
497 return Err(argument::Error::InvalidValue {
498 value: serial_setting.num.to_string(),
499 expected: String::from("Serial port num must be 4 or less"),
500 });
501 }
502
Trent Begin17ccaad2019-04-17 13:51:25 -0600503 Ok(serial_setting)
504}
505
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800506fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900507 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800508 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800509 return Err(argument::Error::InvalidValue {
510 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800511 expected: String::from(
512 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
513 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800514 });
515 }
516
517 let src = PathBuf::from(components[0]);
518 if src.is_relative() {
519 return Err(argument::Error::InvalidValue {
520 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800521 expected: String::from("the source path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800522 });
523 }
524 if !src.exists() {
525 return Err(argument::Error::InvalidValue {
526 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800527 expected: String::from("the source path for `plugin-mount` does not exist"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800528 });
529 }
530
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800531 let dst = PathBuf::from(match components.get(1) {
532 None | Some(&"") => components[0],
533 Some(path) => path,
534 });
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800535 if dst.is_relative() {
536 return Err(argument::Error::InvalidValue {
537 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800538 expected: String::from("the destination path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800539 });
540 }
541
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800542 let writable: bool = match components.get(2) {
543 None => false,
544 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800545 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800546 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800547 })?,
548 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800549
550 Ok(BindMount { src, dst, writable })
551}
552
553fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900554 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800555 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800556 return Err(argument::Error::InvalidValue {
557 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800558 expected: String::from(
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800559 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
Judy Hsiao59343052020-03-16 15:58:03 +0800560 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800561 });
562 }
563
564 let inner: libc::gid_t = components[0]
565 .parse()
566 .map_err(|_| argument::Error::InvalidValue {
567 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800568 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800569 })?;
570
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800571 let outer: libc::gid_t = match components.get(1) {
572 None | Some(&"") => inner,
573 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800574 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800575 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800576 })?,
577 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800578
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800579 let count: u32 = match components.get(2) {
580 None => 1,
581 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800582 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800583 expected: String::from(
584 "the <count> component for `plugin-gid-map` is not valid number",
585 ),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800586 })?,
587 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800588
589 Ok(GidMap {
590 inner,
591 outer,
592 count,
593 })
594}
595
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800596fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
597 let mut battery_type: BatteryType = Default::default();
598
599 if let Some(s) = s {
600 let opts = s
601 .split(",")
602 .map(|frag| frag.split("="))
603 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
604
605 for (k, v) in opts {
606 match k {
607 "type" => match v.parse::<BatteryType>() {
608 Ok(type_) => battery_type = type_,
609 Err(e) => {
610 return Err(argument::Error::InvalidValue {
611 value: v.to_string(),
612 expected: e.to_string(),
613 });
614 }
615 },
616 "" => {}
617 _ => {
618 return Err(argument::Error::UnknownArgument(format!(
619 "battery parameter {}",
620 k
621 )));
622 }
623 }
624 }
625 }
626
627 Ok(battery_type)
628}
629
Zach Reiznerefe95782017-08-26 18:05:48 -0700630fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
631 match name {
632 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700633 if cfg.executable_path.is_some() {
634 return Err(argument::Error::TooManyArguments(format!(
635 "A VM executable was already specified: {:?}",
636 cfg.executable_path
637 )));
Zach Reiznerefe95782017-08-26 18:05:48 -0700638 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700639 let kernel_path = PathBuf::from(value.unwrap());
640 if !kernel_path.exists() {
641 return Err(argument::Error::InvalidValue {
642 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800643 expected: String::from("this kernel path does not exist"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700644 });
645 }
646 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -0700647 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800648 "android-fstab" => {
649 if cfg.android_fstab.is_some()
650 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
651 {
652 return Err(argument::Error::TooManyArguments(
653 "expected exactly one android fstab path".to_owned(),
654 ));
655 } else {
656 let android_fstab = PathBuf::from(value.unwrap());
657 if !android_fstab.exists() {
658 return Err(argument::Error::InvalidValue {
659 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800660 expected: String::from("this android fstab path does not exist"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800661 });
662 }
663 cfg.android_fstab = Some(android_fstab);
664 }
665 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700666 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800667 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700668 }
669 "cpus" => {
670 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700671 return Err(argument::Error::TooManyArguments(
672 "`cpus` already given".to_owned(),
673 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700674 }
675 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700676 Some(
677 value
678 .unwrap()
679 .parse()
680 .map_err(|_| argument::Error::InvalidValue {
681 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800682 expected: String::from("this value for `cpus` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700683 })?,
684 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700685 }
Daniel Verkamp107edb32019-04-05 09:58:48 -0700686 "cpu-affinity" => {
Daniel Verkampc677fb42020-09-08 13:47:49 -0700687 if cfg.vcpu_affinity.is_some() {
Daniel Verkamp107edb32019-04-05 09:58:48 -0700688 return Err(argument::Error::TooManyArguments(
689 "`cpu-affinity` already given".to_owned(),
690 ));
691 }
Daniel Verkampc677fb42020-09-08 13:47:49 -0700692 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
Daniel Verkamp107edb32019-04-05 09:58:48 -0700693 }
Suleiman Souhlal015c3c12020-10-07 14:15:41 +0900694 "no-smt" => {
695 cfg.no_smt = true;
696 }
Kansho Nishidaab205af2020-08-13 18:17:50 +0900697 "rt-cpus" => {
698 if !cfg.rt_cpus.is_empty() {
699 return Err(argument::Error::TooManyArguments(
700 "`rt-cpus` already given".to_owned(),
701 ));
702 }
703 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
704 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700705 "mem" => {
706 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700707 return Err(argument::Error::TooManyArguments(
708 "`mem` already given".to_owned(),
709 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700710 }
711 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700712 Some(
713 value
714 .unwrap()
715 .parse()
716 .map_err(|_| argument::Error::InvalidValue {
717 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800718 expected: String::from("this value for `mem` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700719 })?,
720 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700721 }
Andrew Scull1590e6f2020-03-18 18:00:47 +0000722 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800723 "ac97" => {
724 let ac97_params = parse_ac97_options(value.unwrap())?;
paulhsia16478242020-11-27 14:04:58 +0800725 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
726 if cfg.ac97_parameters.is_empty() {
727 // Set `inside_vm=1` to save some register read ops in the driver.
728 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
729 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
730 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
731 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800732 cfg.ac97_parameters.push(ac97_params);
Dylan Reid3082e8e2019-01-07 10:33:48 -0800733 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600734 "serial" => {
735 let serial_params = parse_serial_options(value.unwrap())?;
736 let num = serial_params.num;
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700737 let key = (serial_params.hardware, num);
738 if cfg.serial_parameters.contains_key(&key) {
Trent Begin17ccaad2019-04-17 13:51:25 -0600739 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700740 "serial hardware {} num {}",
741 serial_params.hardware, num,
Trent Begin17ccaad2019-04-17 13:51:25 -0600742 )));
743 }
744
745 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -0700746 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -0600747 if params.console {
748 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700749 "{} device {} already set as console",
750 params.hardware, params.num,
751 )));
752 }
753 }
754 }
755
756 if serial_params.earlycon {
757 // Only SerialHardware::Serial supports earlycon= currently.
758 match serial_params.hardware {
759 SerialHardware::Serial => {}
760 _ => {
761 return Err(argument::Error::InvalidValue {
762 value: serial_params.hardware.to_string().to_owned(),
763 expected: String::from("earlycon not supported for hardware"),
764 });
765 }
766 }
767 for params in cfg.serial_parameters.values() {
768 if params.earlycon {
769 return Err(argument::Error::TooManyArguments(format!(
770 "{} device {} already set as earlycon",
771 params.hardware, params.num,
Trent Begin17ccaad2019-04-17 13:51:25 -0600772 )));
773 }
774 }
775 }
776
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700777 if serial_params.stdin {
778 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
779 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700780 "{} device {} already connected to standard input",
781 previous_stdin.hardware, previous_stdin.num,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700782 )));
783 }
784 }
785
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700786 cfg.serial_parameters.insert(key, serial_params);
Trent Begin17ccaad2019-04-17 13:51:25 -0600787 }
788 "syslog-tag" => {
789 if cfg.syslog_tag.is_some() {
790 return Err(argument::Error::TooManyArguments(
791 "`syslog-tag` already given".to_owned(),
792 ));
793 }
794 syslog::set_proc_name(value.unwrap());
795 cfg.syslog_tag = Some(value.unwrap().to_owned());
796 }
Daniel Verkamp4b62cd92019-11-08 13:09:27 -0800797 "root" | "rwroot" | "disk" | "rwdisk" => {
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800798 let param = value.unwrap();
799 let mut components = param.split(',');
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700800 let read_only = !name.starts_with("rw");
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800801 let disk_path =
802 PathBuf::from(
803 components
804 .next()
805 .ok_or_else(|| argument::Error::InvalidValue {
806 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800807 expected: String::from("missing disk path"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800808 })?,
809 );
Zach Reiznerefe95782017-08-26 18:05:48 -0700810 if !disk_path.exists() {
811 return Err(argument::Error::InvalidValue {
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800812 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800813 expected: String::from("this disk path does not exist"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700814 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700815 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700816 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -0700817 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -0700818 return Err(argument::Error::TooManyArguments(
819 "ran out of letters for to assign to root disk".to_owned(),
820 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700821 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700822 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700823 "root=/dev/vd{} {}",
824 char::from(b'a' + cfg.disks.len() as u8),
825 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -0700826 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700827 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800828
829 let mut disk = DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -0700830 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700831 read_only,
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800832 sparse: true,
Daniel Verkamp27672232019-12-06 17:26:55 +1100833 block_size: 512,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -0700834 id: None,
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800835 };
836
837 for opt in components {
838 let mut o = opt.splitn(2, '=');
839 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
840 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800841 expected: String::from("disk options must not be empty"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800842 })?;
843 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
844 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800845 expected: String::from("disk options must be of the form `kind=value`"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800846 })?;
847
848 match kind {
849 "sparse" => {
850 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
851 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800852 expected: String::from("`sparse` must be a boolean"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800853 })?;
854 disk.sparse = sparse;
855 }
Daniel Verkamp27672232019-12-06 17:26:55 +1100856 "block_size" => {
857 let block_size =
858 value.parse().map_err(|_| argument::Error::InvalidValue {
859 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800860 expected: String::from("`block_size` must be an integer"),
Daniel Verkamp27672232019-12-06 17:26:55 +1100861 })?;
862 disk.block_size = block_size;
863 }
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -0700864 "id" => {
865 if value.len() > DISK_ID_LEN {
866 return Err(argument::Error::InvalidValue {
867 value: value.to_owned(),
868 expected: format!(
869 "`id` must be {} or fewer characters",
870 DISK_ID_LEN
871 ),
872 });
873 }
874 let mut id = [0u8; DISK_ID_LEN];
875 // Slicing id to value's length will never panic
876 // because we checked that value will fit into id above.
877 id[..value.len()].copy_from_slice(value.as_bytes());
878 disk.id = Some(id);
879 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800880 _ => {
881 return Err(argument::Error::InvalidValue {
882 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800883 expected: String::from("unrecognized disk option"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800884 });
885 }
886 }
887 }
888
889 cfg.disks.push(disk);
Zach Reiznerefe95782017-08-26 18:05:48 -0700890 }
Jakub Starona3411ea2019-04-24 10:55:25 -0700891 "pmem-device" | "rw-pmem-device" => {
892 let disk_path = PathBuf::from(value.unwrap());
893 if !disk_path.exists() {
894 return Err(argument::Error::InvalidValue {
895 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800896 expected: String::from("this disk path does not exist"),
Jakub Starona3411ea2019-04-24 10:55:25 -0700897 });
898 }
899
900 cfg.pmem_devices.push(DiskOption {
901 path: disk_path,
902 read_only: !name.starts_with("rw"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -0800903 sparse: false,
Michael Hoyle6b196952020-08-02 20:09:41 -0700904 block_size: base::pagesize() as u32,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -0700905 id: None,
Jakub Starona3411ea2019-04-24 10:55:25 -0700906 });
907 }
Kansho Nishida282115b2019-12-18 13:13:14 +0900908 "pstore" => {
909 if cfg.pstore.is_some() {
910 return Err(argument::Error::TooManyArguments(
911 "`pstore` already given".to_owned(),
912 ));
913 }
914
915 let value = value.unwrap();
916 let components: Vec<&str> = value.split(',').collect();
917 if components.len() != 2 {
918 return Err(argument::Error::InvalidValue {
919 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800920 expected: String::from(
921 "pstore must have exactly 2 components: path=<path>,size=<size>",
922 ),
Kansho Nishida282115b2019-12-18 13:13:14 +0900923 });
924 }
925 cfg.pstore = Some(Pstore {
926 path: {
927 if components[0].len() <= 5 || !components[0].starts_with("path=") {
928 return Err(argument::Error::InvalidValue {
929 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800930 expected: String::from("pstore path must follow with `path=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +0900931 });
932 };
933 PathBuf::from(&components[0][5..])
934 },
935 size: {
936 if components[1].len() <= 5 || !components[1].starts_with("size=") {
937 return Err(argument::Error::InvalidValue {
938 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800939 expected: String::from("pstore size must follow with `size=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +0900940 });
941 };
942 components[1][5..]
943 .parse()
944 .map_err(|_| argument::Error::InvalidValue {
945 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800946 expected: String::from("pstore size must be an integer"),
Kansho Nishida282115b2019-12-18 13:13:14 +0900947 })?
948 },
949 });
950 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700951 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700952 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700953 return Err(argument::Error::TooManyArguments(
954 "`host_ip` already given".to_owned(),
955 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700956 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700957 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -0700958 Some(
959 value
960 .unwrap()
961 .parse()
962 .map_err(|_| argument::Error::InvalidValue {
963 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800964 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -0700965 })?,
966 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700967 }
968 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700969 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700970 return Err(argument::Error::TooManyArguments(
971 "`netmask` already given".to_owned(),
972 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700973 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700974 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -0700975 Some(
976 value
977 .unwrap()
978 .parse()
979 .map_err(|_| argument::Error::InvalidValue {
980 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800981 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -0700982 })?,
983 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700984 }
985 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700986 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700987 return Err(argument::Error::TooManyArguments(
988 "`mac` already given".to_owned(),
989 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700990 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700991 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -0700992 Some(
993 value
994 .unwrap()
995 .parse()
996 .map_err(|_| argument::Error::InvalidValue {
997 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800998 expected: String::from(
999 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1000 ),
Zach Reizner55a9e502018-10-03 10:22:32 -07001001 })?,
1002 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001003 }
Xiong Zhang773c7072020-03-20 10:39:55 +08001004 "net-vq-pairs" => {
1005 if cfg.net_vq_pairs.is_some() {
1006 return Err(argument::Error::TooManyArguments(
1007 "`net-vq-pairs` already given".to_owned(),
1008 ));
1009 }
1010 cfg.net_vq_pairs =
1011 Some(
1012 value
1013 .unwrap()
1014 .parse()
1015 .map_err(|_| argument::Error::InvalidValue {
1016 value: value.unwrap().to_owned(),
1017 expected: String::from(
1018 "this value for `net-vq-pairs` needs to be integer",
1019 ),
1020 })?,
1021 )
1022 }
1023
Stephen Barber28a5a612017-10-20 17:15:30 -07001024 "wayland-sock" => {
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001025 let mut components = value.unwrap().split(',');
1026 let path =
1027 PathBuf::from(
1028 components
1029 .next()
1030 .ok_or_else(|| argument::Error::InvalidValue {
1031 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001032 expected: String::from("missing socket path"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001033 })?,
1034 );
1035 let mut name = "";
1036 for c in components {
1037 let mut kv = c.splitn(2, '=');
1038 let (kind, value) = match (kv.next(), kv.next()) {
1039 (Some(kind), Some(value)) => (kind, value),
1040 _ => {
1041 return Err(argument::Error::InvalidValue {
1042 value: c.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001043 expected: String::from("option must be of the form `kind=value`"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001044 })
1045 }
1046 };
1047 match kind {
1048 "name" => name = value,
1049 _ => {
1050 return Err(argument::Error::InvalidValue {
1051 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001052 expected: String::from("unrecognized option"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001053 })
1054 }
1055 }
Stephen Barber28a5a612017-10-20 17:15:30 -07001056 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001057 if cfg.wayland_socket_paths.contains_key(name) {
1058 return Err(argument::Error::TooManyArguments(format!(
1059 "wayland socket name already used: '{}'",
1060 name
1061 )));
Stephen Barber28a5a612017-10-20 17:15:30 -07001062 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001063 cfg.wayland_socket_paths.insert(name.to_string(), path);
Stephen Barber28a5a612017-10-20 17:15:30 -07001064 }
David Reveman52ba4e52018-04-22 21:42:09 -04001065 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -07001066 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001067 "x-display" => {
1068 if cfg.x_display.is_some() {
1069 return Err(argument::Error::TooManyArguments(
1070 "`x-display` already given".to_owned(),
1071 ));
1072 }
1073 cfg.x_display = Some(value.unwrap().to_owned());
1074 }
Zach Reizner65b98f12019-11-22 17:34:58 -08001075 "display-window-keyboard" => {
1076 cfg.display_window_keyboard = true;
1077 }
1078 "display-window-mouse" => {
1079 cfg.display_window_mouse = true;
1080 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001081 "socket" => {
1082 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001083 return Err(argument::Error::TooManyArguments(
1084 "`socket` already given".to_owned(),
1085 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001086 }
1087 let mut socket_path = PathBuf::from(value.unwrap());
1088 if socket_path.is_dir() {
1089 socket_path.push(format!("crosvm-{}.sock", getpid()));
1090 }
1091 if socket_path.exists() {
1092 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001093 value: socket_path.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001094 expected: String::from("this socket path already exists"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001095 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001096 }
1097 cfg.socket_path = Some(socket_path);
1098 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001099 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -07001100 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001101 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001102 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001103 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001104 return Err(argument::Error::TooManyArguments(
1105 "`cid` alread given".to_owned(),
1106 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001107 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001108 cfg.cid = Some(
1109 value
1110 .unwrap()
1111 .parse()
1112 .map_err(|_| argument::Error::InvalidValue {
1113 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001114 expected: String::from("this value for `cid` must be an unsigned integer"),
Daniel Verkampaac28132018-10-15 14:58:48 -07001115 })?,
1116 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001117 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001118 "shared-dir" => {
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001119 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1120 // fixed (src:tag). The rest may appear in any order:
1121 //
1122 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1123 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1124 // (default: "0 <current euid> 1")
1125 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1126 // (default: "0 <current egid> 1")
1127 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1128 // and directory contents should be considered valid (default: 5)
1129 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1130 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001131 let param = value.unwrap();
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001132 let mut components = param.split(':');
Zach Reizner55a9e502018-10-03 10:22:32 -07001133 let src =
1134 PathBuf::from(
1135 components
1136 .next()
1137 .ok_or_else(|| argument::Error::InvalidValue {
1138 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001139 expected: String::from("missing source path for `shared-dir`"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001140 })?,
1141 );
1142 let tag = components
1143 .next()
1144 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001145 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001146 expected: String::from("missing tag for `shared-dir`"),
David Tolnay2bac1e72018-12-12 14:33:42 -08001147 })?
1148 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001149
1150 if !src.is_dir() {
1151 return Err(argument::Error::InvalidValue {
1152 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001153 expected: String::from("source path for `shared-dir` must be a directory"),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001154 });
1155 }
1156
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001157 let mut shared_dir = SharedDir {
1158 src,
1159 tag,
1160 ..Default::default()
1161 };
1162 for opt in components {
1163 let mut o = opt.splitn(2, '=');
1164 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1165 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001166 expected: String::from("`shared-dir` options must not be empty"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001167 })?;
1168 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1169 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001170 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001171 })?;
1172
1173 match kind {
1174 "type" => {
1175 shared_dir.kind =
1176 value.parse().map_err(|_| argument::Error::InvalidValue {
1177 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001178 expected: String::from("`type` must be one of `fs` or `9p`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001179 })?
1180 }
1181 "uidmap" => shared_dir.uid_map = value.into(),
1182 "gidmap" => shared_dir.gid_map = value.into(),
1183 "timeout" => {
1184 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1185 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001186 expected: String::from("`timeout` must be an integer"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001187 })?;
1188
1189 let dur = Duration::from_secs(seconds);
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001190 shared_dir.fs_cfg.entry_timeout = dur.clone();
1191 shared_dir.fs_cfg.attr_timeout = dur;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001192 }
1193 "cache" => {
1194 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1195 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001196 expected: String::from(
1197 "`cache` must be one of `never`, `always`, or `auto`",
1198 ),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001199 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001200 shared_dir.fs_cfg.cache_policy = policy;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001201 }
1202 "writeback" => {
1203 let writeback =
1204 value.parse().map_err(|_| argument::Error::InvalidValue {
1205 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001206 expected: String::from("`writeback` must be a boolean"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001207 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001208 shared_dir.fs_cfg.writeback = writeback;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001209 }
Chirantan Ekboted994e512020-06-12 18:46:52 +09001210 "rewrite-security-xattrs" => {
1211 let rewrite_security_xattrs =
1212 value.parse().map_err(|_| argument::Error::InvalidValue {
1213 value: value.to_owned(),
1214 expected: String::from(
1215 "`rewrite-security-xattrs` must be a boolean",
1216 ),
1217 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001218 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
Chirantan Ekboted994e512020-06-12 18:46:52 +09001219 }
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001220 "ascii_casefold" => {
1221 let ascii_casefold =
1222 value.parse().map_err(|_| argument::Error::InvalidValue {
1223 value: value.to_owned(),
1224 expected: String::from("`ascii_casefold` must be a boolean"),
1225 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001226 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1227 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001228 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001229 _ => {
1230 return Err(argument::Error::InvalidValue {
1231 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001232 expected: String::from("unrecognized option for `shared-dir`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001233 })
1234 }
1235 }
1236 }
1237 cfg.shared_dirs.push(shared_dir);
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001238 }
Dylan Reide026ef02017-10-02 19:03:52 -07001239 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001240 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -07001241 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -07001242 }
Zach Reizner44863792019-06-26 14:22:08 -07001243 "seccomp-log-failures" => {
Matt Delco45caf912019-11-13 08:11:09 -08001244 // A side-effect of this flag is to force the use of .policy files
1245 // instead of .bpf files (.bpf files are expected and assumed to be
1246 // compiled to fail an unpermitted action with "trap").
1247 // Normally crosvm will first attempt to use a .bpf file, and if
1248 // not present it will then try to use a .policy file. It's up
1249 // to the build to decide which of these files is present for
1250 // crosvm to use (for CrOS the build will use .bpf files for
1251 // x64 builds and .policy files for arm/arm64 builds).
1252 //
1253 // This flag will likely work as expected for builds that use
1254 // .policy files. For builds that only use .bpf files the initial
1255 // result when using this flag is likely to be a file-not-found
1256 // error (since the .policy files are not present).
1257 // For .bpf builds you can either 1) manually add the .policy files,
1258 // or 2) do not use this command-line parameter and instead
1259 // temporarily change the build by passing "log" rather than
1260 // "trap" as the "--default-action" to compile_seccomp_policy.py.
Zach Reizner44863792019-06-26 14:22:08 -07001261 cfg.seccomp_log_failures = true;
1262 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001263 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001264 if cfg.executable_path.is_some() {
1265 return Err(argument::Error::TooManyArguments(format!(
1266 "A VM executable was already specified: {:?}",
1267 cfg.executable_path
1268 )));
Zach Reizner8864cb02018-01-16 17:59:03 -08001269 }
Zach Reiznercc30d582018-01-23 21:16:42 -08001270 let plugin = PathBuf::from(value.unwrap().to_owned());
1271 if plugin.is_relative() {
1272 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001273 value: plugin.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001274 expected: String::from("the plugin path must be an absolute path"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001275 });
Zach Reiznercc30d582018-01-23 21:16:42 -08001276 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001277 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -07001278 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001279 "plugin-root" => {
1280 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -07001281 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001282 "plugin-mount" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001283 let mount = parse_plugin_mount_option(value.unwrap())?;
1284 cfg.plugin_mounts.push(mount);
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001285 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001286 "plugin-mount-file" => {
1287 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1288 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001289 expected: String::from("unable to open `plugin-mount-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001290 })?;
1291 let reader = BufReader::new(file);
1292 for l in reader.lines() {
1293 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001294 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001295 if !trimmed_line.is_empty() {
1296 let mount = parse_plugin_mount_option(trimmed_line)?;
1297 cfg.plugin_mounts.push(mount);
1298 }
1299 }
1300 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001301 "plugin-gid-map" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001302 let map = parse_plugin_gid_map_option(value.unwrap())?;
1303 cfg.plugin_gid_maps.push(map);
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001304 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001305 "plugin-gid-map-file" => {
1306 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1307 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001308 expected: String::from("unable to open `plugin-gid-map-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001309 })?;
1310 let reader = BufReader::new(file);
1311 for l in reader.lines() {
1312 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001313 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001314 if !trimmed_line.is_empty() {
1315 let map = parse_plugin_gid_map_option(trimmed_line)?;
1316 cfg.plugin_gid_maps.push(map);
1317 }
1318 }
1319 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001320 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001321 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001322 cfg.tap_fd.push(
1323 value
1324 .unwrap()
1325 .parse()
1326 .map_err(|_| argument::Error::InvalidValue {
1327 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001328 expected: String::from(
1329 "this value for `tap-fd` must be an unsigned integer",
1330 ),
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001331 })?,
1332 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001333 }
Jason Macnakcc7070b2019-11-06 14:48:12 -08001334 #[cfg(feature = "gpu")]
Zach Reizner3a8100a2017-09-13 19:15:43 -07001335 "gpu" => {
Jason Macnakcc7070b2019-11-06 14:48:12 -08001336 let params = parse_gpu_options(value)?;
1337 cfg.gpu_parameters = Some(params);
Zach Reizner3a8100a2017-09-13 19:15:43 -07001338 }
David Tolnay43f8e212019-02-13 17:28:16 -08001339 "software-tpm" => {
1340 cfg.software_tpm = true;
1341 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001342 "single-touch" => {
1343 if cfg.virtio_single_touch.is_some() {
1344 return Err(argument::Error::TooManyArguments(
1345 "`single-touch` already given".to_owned(),
1346 ));
1347 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001348 let mut it = value.unwrap().split(':');
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001349
1350 let mut single_touch_spec =
1351 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1352 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001353 single_touch_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001354 }
1355 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001356 single_touch_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001357 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001358 cfg.virtio_single_touch = Some(single_touch_spec);
1359 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001360 "trackpad" => {
1361 if cfg.virtio_trackpad.is_some() {
1362 return Err(argument::Error::TooManyArguments(
1363 "`trackpad` already given".to_owned(),
1364 ));
1365 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001366 let mut it = value.unwrap().split(':');
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001367
1368 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001369 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001370 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001371 trackpad_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001372 }
1373 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001374 trackpad_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001375 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001376 cfg.virtio_trackpad = Some(trackpad_spec);
1377 }
1378 "mouse" => {
1379 if cfg.virtio_mouse.is_some() {
1380 return Err(argument::Error::TooManyArguments(
1381 "`mouse` already given".to_owned(),
1382 ));
1383 }
1384 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
1385 }
1386 "keyboard" => {
1387 if cfg.virtio_keyboard.is_some() {
1388 return Err(argument::Error::TooManyArguments(
1389 "`keyboard` already given".to_owned(),
1390 ));
1391 }
1392 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
1393 }
1394 "evdev" => {
1395 let dev_path = PathBuf::from(value.unwrap());
1396 if !dev_path.exists() {
1397 return Err(argument::Error::InvalidValue {
1398 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001399 expected: String::from("this input device path does not exist"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001400 });
1401 }
1402 cfg.virtio_input_evdevs.push(dev_path);
1403 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08001404 "split-irqchip" => {
1405 cfg.split_irqchip = true;
1406 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001407 "initrd" => {
1408 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1409 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001410 "bios" => {
1411 if cfg.executable_path.is_some() {
1412 return Err(argument::Error::TooManyArguments(format!(
1413 "A VM executable was already specified: {:?}",
1414 cfg.executable_path
1415 )));
1416 }
1417 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1418 }
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001419 "vfio" => {
1420 let vfio_path = PathBuf::from(value.unwrap());
1421 if !vfio_path.exists() {
1422 return Err(argument::Error::InvalidValue {
1423 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001424 expected: String::from("the vfio path does not exist"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001425 });
1426 }
1427 if !vfio_path.is_dir() {
1428 return Err(argument::Error::InvalidValue {
1429 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001430 expected: String::from("the vfio path should be directory"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001431 });
1432 }
1433
Xiong Zhang8bb4faa2019-11-12 10:06:13 +08001434 cfg.vfio.push(vfio_path);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001435 }
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001436 "video-decoder" => {
1437 cfg.video_dec = true;
1438 }
1439 "video-encoder" => {
1440 cfg.video_enc = true;
1441 }
Tomasz Jeznach42644642020-05-20 23:27:59 -07001442 "acpi-table" => {
1443 let acpi_table = PathBuf::from(value.unwrap());
1444 if !acpi_table.exists() {
1445 return Err(argument::Error::InvalidValue {
1446 value: value.unwrap().to_owned(),
1447 expected: String::from("the acpi-table path does not exist"),
1448 });
1449 }
1450 if !acpi_table.is_file() {
1451 return Err(argument::Error::InvalidValue {
1452 value: value.unwrap().to_owned(),
1453 expected: String::from("the acpi-table path should be a file"),
1454 });
1455 }
1456
1457 cfg.acpi_tables.push(acpi_table);
1458 }
Will Deacon6560c182020-10-06 18:47:18 +01001459 "protected-vm" => {
Will Deacon7d2b8ac2020-10-06 18:51:12 +01001460 cfg.protected_vm = true;
Will Deacon6560c182020-10-06 18:47:18 +01001461 cfg.params.push("swiotlb=force".to_string());
1462 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08001463 "battery" => {
1464 let params = parse_battery_options(value)?;
1465 cfg.battery_type = Some(params);
1466 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001467 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1468 "gdb" => {
1469 let port = value
1470 .unwrap()
1471 .parse()
1472 .map_err(|_| argument::Error::InvalidValue {
1473 value: value.unwrap().to_owned(),
1474 expected: String::from("expected a valid port number"),
1475 })?;
1476 cfg.gdb = Some(port);
1477 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001478 "help" => return Err(argument::Error::PrintHelp),
1479 _ => unreachable!(),
1480 }
1481 Ok(())
1482}
1483
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001484fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
1485 if cfg.executable_path.is_none() {
1486 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
1487 }
1488 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
1489 if cfg.host_ip.is_none() {
1490 return Err(argument::Error::ExpectedArgument(
1491 "`host_ip` missing from network config".to_owned(),
1492 ));
1493 }
1494 if cfg.netmask.is_none() {
1495 return Err(argument::Error::ExpectedArgument(
1496 "`netmask` missing from network config".to_owned(),
1497 ));
1498 }
1499 if cfg.mac_address.is_none() {
1500 return Err(argument::Error::ExpectedArgument(
1501 "`mac` missing from network config".to_owned(),
1502 ));
1503 }
1504 }
1505 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
1506 return Err(argument::Error::ExpectedArgument(
1507 "`plugin-root` requires `plugin`".to_owned(),
1508 ));
1509 }
1510 #[cfg(feature = "gpu")]
1511 {
1512 if let Some(gpu_parameters) = cfg.gpu_parameters.as_ref() {
1513 let (width, height) = (gpu_parameters.display_width, gpu_parameters.display_height);
1514 if let Some(virtio_single_touch) = cfg.virtio_single_touch.as_mut() {
1515 virtio_single_touch.set_default_size(width, height);
1516 }
1517 }
1518 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001519 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1520 if cfg.gdb.is_some() {
1521 if cfg.vcpu_count.unwrap_or(1) != 1 {
1522 return Err(argument::Error::ExpectedArgument(
1523 "`gdb` requires the number of vCPU to be 1".to_owned(),
1524 ));
1525 }
1526 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001527 set_default_serial_parameters(&mut cfg.serial_parameters);
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001528 Ok(())
1529}
1530
Dylan Reidbfba9932018-02-05 15:51:59 -08001531fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -07001532 let arguments =
1533 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001534 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001535 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001536 Argument::short_value('p',
1537 "params",
1538 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -08001539 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001540 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkampc677fb42020-09-08 13:47:49 -07001541 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
1542 or colon-separated list of assignments of guest to host CPU assignments (e.g. 0=0:1=1:2=2) (default: no mask)"),
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09001543 Argument::flag("no-smt", "Don't use SMT in the guest"),
Kansho Nishidaab205af2020-08-13 18:17:50 +09001544 Argument::value("rt-cpus", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: none)"),
Zach Reiznerefe95782017-08-26 18:05:48 -07001545 Argument::short_value('m',
1546 "mem",
1547 "N",
1548 "Amount of guest memory in MiB. (default: 256)"),
1549 Argument::short_value('r',
1550 "root",
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001551 "PATH[,key=value[,key=value[,...]]",
1552 "Path to a root disk image followed by optional comma-separated options.
1553 Like `--disk` but adds appropriate kernel command line option.
1554 See --disk for valid options."),
1555 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
1556 See --disk for valid options."),
1557 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
1558 Valid keys:
Daniel Verkamp27672232019-12-06 17:26:55 +11001559 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001560 block_size=BYTES - Set the reported block size of the disk (default: 512)
1561 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001562 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
1563 See --disk for valid options."),
Jakub Starona3411ea2019-04-24 10:55:25 -07001564 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
1565 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Kansho Nishida282115b2019-12-18 13:13:14 +09001566 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001567 Argument::value("host_ip",
1568 "IP",
1569 "IP address to assign to host tap interface."),
1570 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
1571 Argument::value("mac", "MAC", "MAC address for VM."),
Xiong Zhang773c7072020-03-20 10:39:55 +08001572 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
Andrew Scull1590e6f2020-03-18 18:00:47 +00001573 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001574 Argument::value("ac97",
1575 "[backend=BACKEND,capture=true,capture_effect=EFFECT]",
1576 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
1577 Possible key values:
1578 backend=(null, cras) - Where to route the audio device. If not provided, backend will default to null.
1579 `null` for /dev/null, and cras for CRAS server.
1580 capture - Enable audio capture
1581 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec."),
Trent Begin17ccaad2019-04-17 13:51:25 -06001582 Argument::value("serial",
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001583 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
Jason Macnakcc7070b2019-11-06 14:48:12 -08001584 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
Trent Begin17ccaad2019-04-17 13:51:25 -06001585 Possible key values:
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -07001586 type=(stdout,syslog,sink,file) - Where to route the serial device
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001587 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
Trent Begin923bab02019-06-17 13:48:06 -06001588 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -07001589 path=PATH - The path to the file to write to when type=file
Iliyan Malchev2c1417b2020-04-14 09:40:41 -07001590 input=PATH - The path to the file to read from when not stdin
Trent Begin17ccaad2019-04-17 13:51:25 -06001591 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001592 earlycon - Use this serial device as the early console. Can only be given once.
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001593 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
Trent Begin17ccaad2019-04-17 13:51:25 -06001594 "),
1595 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001596 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Zach Reizner65b98f12019-11-22 17:34:58 -08001597 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
1598 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001599 Argument::value("wayland-sock", "PATH[,name=NAME]", "Path to the Wayland socket to use. The unnamed one is used for displaying virtual screens. Named ones are only for IPC."),
David Reveman52ba4e52018-04-22 21:42:09 -04001600 #[cfg(feature = "wl-dmabuf")]
1601 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001602 Argument::short_value('s',
1603 "socket",
1604 "PATH",
1605 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001606 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001607 Argument::value("cid", "CID", "Context ID for virtual sockets."),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001608 Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE]",
1609 "Colon-separated options for configuring a directory to be shared with the VM.
1610The first field is the directory to be shared and the second field is the tag that the VM can use to identify the device.
1611The remaining fields are key=value pairs that may appear in any order. Valid keys are:
1612type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
1613uidmap=UIDMAP - The uid map to use for the device's jail in the format \"inner outer count[,inner outer count]\" (default: 0 <current euid> 1).
1614gidmap=GIDMAP - The gid map to use for the device's jail in the format \"inner outer count[,inner outer count]\" (default: 0 <current egid> 1).
1615cache=(never, auto, always) - Indicates whether the VM can cache the contents of the shared directory (default: auto). When set to \"auto\" and the type is \"fs\", the VM will use close-to-open consistency for file contents.
1616timeout=SECONDS - How long the VM should consider file attributes and directory entries to be valid (default: 5). If the VM has exclusive access to the directory, then this should be a large value. If the directory can be modified by other processes, then this should be 0.
1617writeback=BOOL - Indicates whether the VM can use writeback caching (default: false). This is only safe to do when the VM has exclusive access to the files in a directory. Additionally, the server should have read permission for all files as the VM may issue read requests even for files that are opened write-only.
1618"),
Dylan Reide026ef02017-10-02 19:03:52 -07001619 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -07001620 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -08001621 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -08001622 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08001623 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001624 Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08001625 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001626 Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001627 #[cfg(feature = "plugin")]
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001628 Argument::value("plugin-mount-file", "PATH", "Path to the file listing paths be mounted into the plugin's root filesystem. Can be given more than once."),
1629 #[cfg(feature = "plugin")]
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001630 Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001631 #[cfg(feature = "plugin")]
1632 Argument::value("plugin-gid-map-file", "PATH", "Path to the file listing supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
Rob Bradford8f002f52018-02-19 16:31:11 +00001633 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001634 Argument::value("tap-fd",
1635 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001636 "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
Zach Reizner3a8100a2017-09-13 19:15:43 -07001637 #[cfg(feature = "gpu")]
Jason Macnakcc7070b2019-11-06 14:48:12 -08001638 Argument::flag_or_value("gpu",
1639 "[width=INT,height=INT]",
1640 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
1641 Possible key values:
Lingfeng Yangddbe8b72020-01-30 10:00:36 -08001642 backend=(2d|3d|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
Jason Macnakcc7070b2019-11-06 14:48:12 -08001643 width=INT - The width of the virtual display connected to the virtio-gpu.
1644 height=INT - The height of the virtual display connected to the virtio-gpu.
Jason Macnakbf195582019-11-20 16:25:49 -08001645 egl[=true|=false] - If the virtio-gpu backend should use a EGL context for rendering.
1646 glx[=true|=false] - If the virtio-gpu backend should use a GLX context for rendering.
1647 surfaceless[=true|=false] - If the virtio-gpu backend should use a surfaceless context for rendering.
Jason Macnak046ed142020-10-08 09:11:53 -07001648 angle[=true|=false] - If the guest is using ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
Kaiyi Li6404e452020-07-07 19:12:03 -07001649 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
Kaiyi Li6c52a2e2020-07-07 19:28:32 -07001650 vulkan[=true|=false] - If the gfxstream backend should support vulkan
Jason Macnakcc7070b2019-11-06 14:48:12 -08001651 "),
David Tolnay43f8e212019-02-13 17:28:16 -08001652 #[cfg(feature = "tpm")]
1653 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001654 Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"),
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001655 Argument::value("single-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read single touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001656 Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."),
1657 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
1658 Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08001659 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1660 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001661 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001662 Argument::value("vfio", "PATH", "Path to sysfs of pass through or mdev device"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001663 #[cfg(feature = "video-decoder")]
1664 Argument::flag("video-decoder", "(EXPERIMENTAL) enable virtio-video decoder device"),
1665 #[cfg(feature = "video-encoder")]
1666 Argument::flag("video-encoder", "(EXPERIMENTAL) enable virtio-video encoder device"),
Tomasz Jeznach42644642020-05-20 23:27:59 -07001667 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
Will Deacon6560c182020-10-06 18:47:18 +01001668 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08001669 Argument::flag_or_value("battery",
1670 "[type=TYPE]",
1671 "Comma separated key=value pairs for setting up battery device
1672 Possible key values:
1673 type=goldfish - type of battery emulation, defaults to goldfish
1674 "),
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001675 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
Zach Reiznerefe95782017-08-26 18:05:48 -07001676 Argument::short_flag('h', "help", "Print help message.")];
1677
1678 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -07001679 let match_res = set_arguments(args, &arguments[..], |name, value| {
1680 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -08001681 })
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001682 .and_then(|_| validate_arguments(&mut cfg));
Zach Reiznerefe95782017-08-26 18:05:48 -07001683
1684 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -08001685 #[cfg(feature = "plugin")]
Zach Reizner267f2c82019-07-31 17:07:27 -07001686 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
1687 match crosvm::plugin::run_config(cfg) {
1688 Ok(_) => {
1689 info!("crosvm and plugin have exited normally");
1690 Ok(())
1691 }
1692 Err(e) => {
1693 error!("{}", e);
1694 Err(())
1695 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001696 }
Zach Reizner267f2c82019-07-31 17:07:27 -07001697 }
Michael Hoylee47a5002020-10-15 16:24:13 -07001698 Ok(()) => match platform::run_config(cfg) {
Zach Reizner55a9e502018-10-03 10:22:32 -07001699 Ok(_) => {
1700 info!("crosvm has exited normally");
1701 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -07001702 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001703 Err(e) => {
1704 error!("{}", e);
1705 Err(())
1706 }
1707 },
Dylan Reidbfba9932018-02-05 15:51:59 -08001708 Err(argument::Error::PrintHelp) => {
1709 print_help("crosvm run", "KERNEL", &arguments[..]);
1710 Ok(())
1711 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001712 Err(e) => {
Dmitry Torokhov470b1e72020-01-15 12:46:49 -08001713 error!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08001714 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -08001715 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001716 }
1717}
1718
Jingkui Wang100e6e42019-03-08 20:41:57 -08001719fn handle_request(
1720 request: &VmRequest,
1721 args: std::env::Args,
1722) -> std::result::Result<VmResponse, ()> {
1723 let mut return_result = Err(());
Zach Reiznerefe95782017-08-26 18:05:48 -07001724 for socket_path in args {
Zach Reiznera60744b2019-02-13 17:33:32 -08001725 match UnixSeqpacket::connect(&socket_path) {
Zach Reiznerefe95782017-08-26 18:05:48 -07001726 Ok(s) => {
Jakub Starone7c59052019-04-09 12:31:14 -07001727 let socket: VmControlRequestSocket = MsgSocket::new(s);
Zach Reizner78986322019-02-21 20:43:21 -08001728 if let Err(e) = socket.send(request) {
Zach Reizner55a9e502018-10-03 10:22:32 -07001729 error!(
David Tolnayb4bd00f2019-02-12 17:51:26 -08001730 "failed to send request to socket at '{}': {}",
Zach Reizner55a9e502018-10-03 10:22:32 -07001731 socket_path, e
1732 );
Zach Reizner78986322019-02-21 20:43:21 -08001733 return_result = Err(());
1734 continue;
Zach Reiznerefe95782017-08-26 18:05:48 -07001735 }
Zach Reizner78986322019-02-21 20:43:21 -08001736 match socket.recv() {
Jingkui Wang100e6e42019-03-08 20:41:57 -08001737 Ok(response) => return_result = Ok(response),
Zach Reizner78986322019-02-21 20:43:21 -08001738 Err(e) => {
1739 error!(
1740 "failed to send request to socket at2 '{}': {}",
1741 socket_path, e
1742 );
1743 return_result = Err(());
1744 continue;
1745 }
Dylan Reidd4432042017-12-06 18:20:09 -08001746 }
1747 }
Dylan Reidbfba9932018-02-05 15:51:59 -08001748 Err(e) => {
1749 error!("failed to connect to socket at '{}': {}", socket_path, e);
1750 return_result = Err(());
1751 }
Dylan Reidd4432042017-12-06 18:20:09 -08001752 }
1753 }
Dylan Reidbfba9932018-02-05 15:51:59 -08001754
1755 return_result
Dylan Reidd4432042017-12-06 18:20:09 -08001756}
Zach Reiznerefe95782017-08-26 18:05:48 -07001757
Jingkui Wang100e6e42019-03-08 20:41:57 -08001758fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
1759 let response = handle_request(request, args)?;
1760 info!("request response was {}", response);
1761 Ok(())
1762}
1763
Zach Reizner78986322019-02-21 20:43:21 -08001764fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1765 if args.len() == 0 {
1766 print_help("crosvm stop", "VM_SOCKET...", &[]);
1767 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001768 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001769 }
1770 vms_request(&VmRequest::Exit, args)
1771}
1772
1773fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1774 if args.len() == 0 {
1775 print_help("crosvm suspend", "VM_SOCKET...", &[]);
1776 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001777 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001778 }
1779 vms_request(&VmRequest::Suspend, args)
1780}
1781
1782fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1783 if args.len() == 0 {
1784 print_help("crosvm resume", "VM_SOCKET...", &[]);
1785 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001786 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001787 }
1788 vms_request(&VmRequest::Resume, args)
1789}
1790
1791fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
1792 if args.len() < 2 {
1793 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
1794 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001795 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001796 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001797 let num_bytes = match args.next().unwrap().parse::<u64>() {
Zach Reizner78986322019-02-21 20:43:21 -08001798 Ok(n) => n,
1799 Err(_) => {
1800 error!("Failed to parse number of bytes");
1801 return Err(());
1802 }
1803 };
1804
Jakub Staron1f828d72019-04-11 12:49:29 -07001805 let command = BalloonControlCommand::Adjust { num_bytes };
1806 vms_request(&VmRequest::BalloonCommand(command), args)
Zach Reizner78986322019-02-21 20:43:21 -08001807}
1808
Charles William Dicked22f6b2020-04-08 11:05:24 +09001809fn balloon_stats(args: std::env::Args) -> std::result::Result<(), ()> {
1810 if args.len() != 1 {
1811 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
1812 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
1813 return Err(());
1814 }
1815 let command = BalloonControlCommand::Stats {};
1816 let request = &VmRequest::BalloonCommand(command);
1817 let response = handle_request(request, args)?;
1818 println!("{}", response);
1819 Ok(())
1820}
1821
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08001822fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
1823 let arguments = [
1824 Argument::positional("PATH", "where to create the qcow2 image"),
1825 Argument::positional("[SIZE]", "the expanded size of the image"),
1826 Argument::value(
1827 "backing_file",
1828 "path/to/file",
1829 " the file to back the image",
1830 ),
1831 ];
1832 let mut positional_index = 0;
1833 let mut file_path = String::from("");
1834 let mut size: Option<u64> = None;
1835 let mut backing_file: Option<String> = None;
1836 set_arguments(args, &arguments[..], |name, value| {
1837 match (name, positional_index) {
1838 ("", 0) => {
1839 // NAME
1840 positional_index += 1;
1841 file_path = value.unwrap().to_owned();
1842 }
1843 ("", 1) => {
1844 // [SIZE]
1845 positional_index += 1;
1846 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
1847 argument::Error::InvalidValue {
1848 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001849 expected: String::from("SIZE should be a nonnegative integer"),
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08001850 }
1851 })?);
1852 }
1853 ("", _) => {
1854 return Err(argument::Error::TooManyArguments(
1855 "Expected at most 2 positional arguments".to_owned(),
1856 ));
1857 }
1858 ("backing_file", _) => {
1859 backing_file = value.map(|x| x.to_owned());
1860 }
1861 _ => unreachable!(),
1862 };
1863 Ok(())
1864 })
1865 .map_err(|e| {
1866 error!("Unable to parse command line arguments: {}", e);
1867 })?;
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001868 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08001869 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
1870 println!(
1871 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
1872with a '--backing_file'."
1873 );
Jianxun Zhang56497d22019-03-04 14:38:24 -08001874 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07001875 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07001876
1877 let file = OpenOptions::new()
1878 .create(true)
1879 .read(true)
1880 .write(true)
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08001881 .truncate(true)
Dylan Reid2dcb6322018-07-13 10:42:48 -07001882 .open(&file_path)
1883 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001884 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07001885 })?;
1886
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08001887 match (size, backing_file) {
1888 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
1889 error!("Failed to create qcow file at '{}': {}", file_path, e);
1890 })?,
1891 (None, Some(backing_file)) => {
1892 QcowFile::new_from_backing(file, &backing_file).map_err(|e| {
1893 error!("Failed to create qcow file at '{}': {}", file_path, e);
1894 })?
1895 }
1896 _ => unreachable!(),
1897 };
Dylan Reid2dcb6322018-07-13 10:42:48 -07001898 Ok(())
1899}
1900
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001901fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
1902 if args.len() < 2 {
1903 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
1904 println!("Manage attached virtual disk devices.");
1905 println!("Subcommands:");
1906 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001907 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001908 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001909 let subcommand: &str = &args.next().unwrap();
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001910
1911 let request = match subcommand {
1912 "resize" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001913 let disk_index = match args.next().unwrap().parse::<usize>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001914 Ok(n) => n,
1915 Err(_) => {
1916 error!("Failed to parse disk index");
1917 return Err(());
1918 }
1919 };
1920
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001921 let new_size = match args.next().unwrap().parse::<u64>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001922 Ok(n) => n,
1923 Err(_) => {
1924 error!("Failed to parse disk size");
1925 return Err(());
1926 }
1927 };
1928
Jakub Staronecf81e02019-04-11 11:43:39 -07001929 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001930 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07001931 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001932 }
1933 }
1934 _ => {
1935 error!("Unknown disk subcommand '{}'", subcommand);
1936 return Err(());
1937 }
1938 };
1939
Zach Reizner78986322019-02-21 20:43:21 -08001940 vms_request(&request, args)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001941}
1942
Jingkui Wang100e6e42019-03-08 20:41:57 -08001943enum ModifyUsbError {
1944 ArgMissing(&'static str),
1945 ArgParse(&'static str, String),
1946 ArgParseInt(&'static str, String, ParseIntError),
Michael Hoylea596a072020-11-10 19:32:45 -08001947 FailedDescriptorValidate(base::Error),
Jingkui Wang100e6e42019-03-08 20:41:57 -08001948 PathDoesNotExist(PathBuf),
1949 SocketFailed,
1950 UnexpectedResponse(VmResponse),
1951 UnknownCommand(String),
1952 UsbControl(UsbControlResult),
1953}
1954
1955impl fmt::Display for ModifyUsbError {
1956 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1957 use self::ModifyUsbError::*;
1958
1959 match self {
1960 ArgMissing(a) => write!(f, "argument missing: {}", a),
1961 ArgParse(name, value) => {
1962 write!(f, "failed to parse argument {} value `{}`", name, value)
1963 }
1964 ArgParseInt(name, value, e) => write!(
1965 f,
1966 "failed to parse integer argument {} value `{}`: {}",
1967 name, value, e
1968 ),
Michael Hoylea596a072020-11-10 19:32:45 -08001969 FailedDescriptorValidate(e) => write!(f, "failed to validate file descriptor: {}", e),
Jingkui Wang100e6e42019-03-08 20:41:57 -08001970 PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()),
1971 SocketFailed => write!(f, "socket failed"),
1972 UnexpectedResponse(r) => write!(f, "unexpected response: {}", r),
1973 UnknownCommand(c) => write!(f, "unknown command: `{}`", c),
1974 UsbControl(e) => write!(f, "{}", e),
1975 }
1976 }
1977}
1978
1979type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
1980
1981fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
1982 debug!("parse_bus_id_addr: {}", v);
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001983 let mut ids = v.split(':');
Jingkui Wang100e6e42019-03-08 20:41:57 -08001984 match (ids.next(), ids.next(), ids.next(), ids.next()) {
1985 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
1986 let bus_id = bus_id
1987 .parse::<u8>()
1988 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
1989 let addr = addr
1990 .parse::<u8>()
1991 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
1992 let vid = u16::from_str_radix(&vid, 16)
1993 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
1994 let pid = u16::from_str_radix(&pid, 16)
1995 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
1996 Ok((bus_id, addr, vid, pid))
1997 }
1998 _ => Err(ModifyUsbError::ArgParse(
1999 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
2000 v.to_owned(),
2001 )),
2002 }
2003}
2004
Michael Hoylea596a072020-11-10 19:32:45 -08002005fn raw_descriptor_from_path(path: &Path) -> ModifyUsbResult<RawDescriptor> {
Jingkui Wang100e6e42019-03-08 20:41:57 -08002006 if !path.exists() {
2007 return Err(ModifyUsbError::PathDoesNotExist(path.to_owned()));
2008 }
Michael Hoylea596a072020-11-10 19:32:45 -08002009 let raw_descriptor = path
Jingkui Wang100e6e42019-03-08 20:41:57 -08002010 .file_name()
2011 .and_then(|fd_osstr| fd_osstr.to_str())
2012 .map_or(
2013 Err(ModifyUsbError::ArgParse(
2014 "USB_DEVICE_PATH",
2015 path.to_string_lossy().into_owned(),
2016 )),
2017 |fd_str| {
2018 fd_str.parse::<libc::c_int>().map_err(|e| {
2019 ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e)
2020 })
2021 },
2022 )?;
Michael Hoylea596a072020-11-10 19:32:45 -08002023 validate_raw_descriptor(raw_descriptor).map_err(ModifyUsbError::FailedDescriptorValidate)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002024}
2025
2026fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2027 let val = args
2028 .next()
2029 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
2030 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
2031 let dev_path = PathBuf::from(
2032 args.next()
2033 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
2034 );
2035 let usb_file: Option<File> = if dev_path == Path::new("-") {
2036 None
2037 } else if dev_path.parent() == Some(Path::new("/proc/self/fd")) {
2038 // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
2039 // Safe because we will validate |raw_fd|.
Michael Hoylea596a072020-11-10 19:32:45 -08002040 Some(unsafe { File::from_raw_descriptor(raw_descriptor_from_path(&dev_path)?) })
Jingkui Wang100e6e42019-03-08 20:41:57 -08002041 } else {
2042 Some(
2043 OpenOptions::new()
2044 .read(true)
2045 .write(true)
2046 .open(&dev_path)
2047 .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))?,
2048 )
2049 };
2050
2051 let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice {
2052 bus,
2053 addr,
2054 vid,
2055 pid,
Michael Hoyle6a0960a2020-10-09 01:20:52 -07002056 // Safe because we are transferring ownership to the rawdescriptor
2057 descriptor: usb_file.map(|file| {
2058 MaybeOwnedDescriptor::Owned(unsafe {
2059 SafeDescriptor::from_raw_descriptor(file.into_raw_descriptor())
2060 })
2061 }),
Jingkui Wang100e6e42019-03-08 20:41:57 -08002062 });
2063 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
2064 match response {
2065 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
2066 r => Err(ModifyUsbError::UnexpectedResponse(r)),
2067 }
2068}
2069
2070fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2071 let port: u8 = args
2072 .next()
2073 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
2074 p.parse::<u8>()
2075 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
2076 })?;
2077 let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port });
2078 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
2079 match response {
2080 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
2081 r => Err(ModifyUsbError::UnexpectedResponse(r)),
2082 }
2083}
2084
Zach Reizneraff94ca2019-03-18 20:58:31 -07002085fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2086 let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
2087 for (index, port) in ports.iter_mut().enumerate() {
2088 *port = index as u8
2089 }
2090 let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
Jingkui Wang100e6e42019-03-08 20:41:57 -08002091 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
2092 match response {
2093 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
2094 r => Err(ModifyUsbError::UnexpectedResponse(r)),
2095 }
2096}
2097
2098fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07002099 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08002100 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07002101 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08002102 return Err(());
2103 }
2104
2105 // This unwrap will not panic because of the above length check.
2106 let command = args.next().unwrap();
2107 let result = match command.as_ref() {
2108 "attach" => usb_attach(args),
2109 "detach" => usb_detach(args),
2110 "list" => usb_list(args),
2111 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
2112 };
2113 match result {
2114 Ok(response) => {
2115 println!("{}", response);
2116 Ok(())
2117 }
2118 Err(e) => {
2119 println!("error {}", e);
2120 Err(())
2121 }
2122 }
2123}
2124
Zach Reiznerefe95782017-08-26 18:05:48 -07002125fn print_usage() {
2126 print_help("crosvm", "[stop|run]", &[]);
2127 println!("Commands:");
2128 println!(" stop - Stops crosvm instances via their control sockets.");
2129 println!(" run - Start a new crosvm instance.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07002130 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08002131 println!(" disk - Manage attached virtual disk devices.");
2132 println!(" usb - Manage attached virtual USB devices.");
Yi Sun54305cd2020-01-04 00:19:37 +08002133 println!(" version - Show package version.");
2134}
2135
2136fn pkg_version() -> std::result::Result<(), ()> {
2137 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
2138 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
2139
2140 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
2141 match PKG_VERSION {
2142 Some(v) => println!("-{}", v),
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002143 None => println!(),
Yi Sun54305cd2020-01-04 00:19:37 +08002144 }
2145 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002146}
2147
Chuanxiao Dong256be3a2020-04-27 16:39:33 +08002148enum ModifyBatError {
2149 BatControlErr(BatControlResult),
2150}
2151
2152impl fmt::Display for ModifyBatError {
2153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2154 use self::ModifyBatError::*;
2155
2156 match self {
2157 BatControlErr(e) => write!(f, "{}", e),
2158 }
2159 }
2160}
2161
2162fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2163 if args.len() < 4 {
2164 print_help("crosvm battery BATTERY_TYPE ",
2165 "[status STATUS | present PRESENT | health HEALTH | capacity CAPACITY | aconline ACONLINE ] VM_SOCKET...", &[]);
2166 return Err(());
2167 }
2168
2169 // This unwrap will not panic because of the above length check.
2170 let battery_type = args.next().unwrap();
2171 let property = args.next().unwrap();
2172 let target = args.next().unwrap();
2173
2174 let response = match battery_type.parse::<BatteryType>() {
2175 Ok(type_) => match BatControlCommand::new(property, target) {
2176 Ok(cmd) => {
2177 let request = VmRequest::BatCommand(type_, cmd);
2178 Ok(handle_request(&request, args)?)
2179 }
2180 Err(e) => Err(ModifyBatError::BatControlErr(e)),
2181 },
2182 Err(e) => Err(ModifyBatError::BatControlErr(e)),
2183 };
2184
2185 match response {
2186 Ok(response) => {
2187 println!("{}", response);
2188 Ok(())
2189 }
2190 Err(e) => {
2191 println!("error {}", e);
2192 Err(())
2193 }
2194 }
2195}
2196
Dylan Reidbfba9932018-02-05 15:51:59 -08002197fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07002198 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002199 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002200 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07002201 }
Zach Reizner639d9672017-05-01 17:57:18 -07002202
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08002203 panic_hook::set_panic_hook();
2204
Zach Reiznerefe95782017-08-26 18:05:48 -07002205 let mut args = std::env::args();
2206 if args.next().is_none() {
2207 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08002208 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07002209 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002210
Zach Reizner8864cb02018-01-16 17:59:03 -08002211 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -08002212 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
2213 None => {
2214 print_usage();
2215 Ok(())
2216 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002217 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -08002218 Some("suspend") => suspend_vms(args),
2219 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07002220 Some("run") => run_vm(args),
2221 Some("balloon") => balloon_vms(args),
Charles William Dicked22f6b2020-04-08 11:05:24 +09002222 Some("balloon_stats") => balloon_stats(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07002223 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002224 Some("disk") => disk_cmd(args),
Jingkui Wang100e6e42019-03-08 20:41:57 -08002225 Some("usb") => modify_usb(args),
Yi Sun54305cd2020-01-04 00:19:37 +08002226 Some("version") => pkg_version(),
Chuanxiao Dong256be3a2020-04-27 16:39:33 +08002227 Some("battery") => modify_battery(args),
Zach Reiznerefe95782017-08-26 18:05:48 -07002228 Some(c) => {
2229 println!("invalid subcommand: {:?}", c);
2230 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -08002231 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002232 }
Dylan Reidbfba9932018-02-05 15:51:59 -08002233 };
Zach Reiznerefe95782017-08-26 18:05:48 -07002234
2235 // Reap exit status from any child device processes. At this point, all devices should have been
2236 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
2237 // take some time for the processes to shut down.
2238 if !wait_all_children() {
2239 // We gave them a chance, and it's too late.
2240 warn!("not all child processes have exited; sending SIGKILL");
2241 if let Err(e) = kill_process_group() {
2242 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08002243 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07002244 }
2245 }
2246
2247 // WARNING: Any code added after this point is not guaranteed to run
2248 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -08002249 ret
2250}
2251
2252fn main() {
2253 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -07002254}
Daniel Verkamp107edb32019-04-05 09:58:48 -07002255
2256#[cfg(test)]
2257mod tests {
2258 use super::*;
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002259 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
Daniel Verkamp107edb32019-04-05 09:58:48 -07002260
2261 #[test]
2262 fn parse_cpu_set_single() {
2263 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
2264 }
2265
2266 #[test]
2267 fn parse_cpu_set_list() {
2268 assert_eq!(
2269 parse_cpu_set("0,1,2,3").expect("parse failed"),
2270 vec![0, 1, 2, 3]
2271 );
2272 }
2273
2274 #[test]
2275 fn parse_cpu_set_range() {
2276 assert_eq!(
2277 parse_cpu_set("0-3").expect("parse failed"),
2278 vec![0, 1, 2, 3]
2279 );
2280 }
2281
2282 #[test]
2283 fn parse_cpu_set_list_of_ranges() {
2284 assert_eq!(
2285 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
2286 vec![3, 4, 7, 8, 9, 18]
2287 );
2288 }
2289
2290 #[test]
2291 fn parse_cpu_set_repeated() {
2292 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
2293 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
2294 }
2295
2296 #[test]
2297 fn parse_cpu_set_negative() {
2298 // Negative CPU numbers are not allowed.
2299 parse_cpu_set("-3").expect_err("parse should have failed");
2300 }
2301
2302 #[test]
2303 fn parse_cpu_set_reverse_range() {
2304 // Ranges must be from low to high.
2305 parse_cpu_set("5-2").expect_err("parse should have failed");
2306 }
2307
2308 #[test]
2309 fn parse_cpu_set_open_range() {
2310 parse_cpu_set("3-").expect_err("parse should have failed");
2311 }
2312
2313 #[test]
2314 fn parse_cpu_set_extra_comma() {
2315 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
2316 }
Trent Begin17ccaad2019-04-17 13:51:25 -06002317
Daniel Verkampc677fb42020-09-08 13:47:49 -07002318 #[test]
2319 fn parse_cpu_affinity_global() {
2320 assert_eq!(
2321 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
2322 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
2323 );
2324 }
2325
2326 #[test]
2327 fn parse_cpu_affinity_per_vcpu_one_to_one() {
2328 let mut expected_map = BTreeMap::new();
2329 expected_map.insert(0, vec![0]);
2330 expected_map.insert(1, vec![1]);
2331 expected_map.insert(2, vec![2]);
2332 expected_map.insert(3, vec![3]);
2333 assert_eq!(
2334 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
2335 VcpuAffinity::PerVcpu(expected_map),
2336 );
2337 }
2338
2339 #[test]
2340 fn parse_cpu_affinity_per_vcpu_sets() {
2341 let mut expected_map = BTreeMap::new();
2342 expected_map.insert(0, vec![0, 1, 2]);
2343 expected_map.insert(1, vec![3, 4, 5]);
2344 expected_map.insert(2, vec![6, 7, 8]);
2345 assert_eq!(
2346 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
2347 VcpuAffinity::PerVcpu(expected_map),
2348 );
2349 }
2350
Andrew Scull1590e6f2020-03-18 18:00:47 +00002351 #[cfg(feature = "audio")]
Trent Begin17ccaad2019-04-17 13:51:25 -06002352 #[test]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002353 fn parse_ac97_vaild() {
2354 parse_ac97_options("backend=cras").expect("parse should have succeded");
2355 }
2356
Andrew Scull1590e6f2020-03-18 18:00:47 +00002357 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002358 #[test]
2359 fn parse_ac97_null_vaild() {
2360 parse_ac97_options("backend=null").expect("parse should have succeded");
2361 }
2362
Andrew Scull1590e6f2020-03-18 18:00:47 +00002363 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002364 #[test]
Judy Hsiaob4b94c72020-09-07 15:56:00 +08002365 fn parse_ac97_capture_vaild() {
2366 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002367 }
2368
2369 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002370 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002371 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
2372 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06002373 }
2374
2375 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07002376 fn parse_serial_virtio_console_vaild() {
2377 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
2378 .expect("parse should have succeded");
2379 }
2380
2381 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06002382 fn parse_serial_valid_no_num() {
2383 parse_serial_options("type=syslog").expect("parse should have succeded");
2384 }
2385
2386 #[test]
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +10002387 fn parse_serial_equals_in_value() {
2388 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
2389 .expect("parse should have succeded");
2390 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
2391 }
2392
2393 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002394 fn parse_serial_invalid_type() {
2395 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
2396 }
2397
2398 #[test]
2399 fn parse_serial_invalid_num_upper() {
2400 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
2401 }
2402
2403 #[test]
2404 fn parse_serial_invalid_num_lower() {
2405 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
2406 }
2407
2408 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07002409 fn parse_serial_virtio_console_invalid_num_lower() {
2410 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
2411 .expect_err("parse should have failed");
2412 }
2413
2414 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002415 fn parse_serial_invalid_num_string() {
2416 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
2417 }
2418
2419 #[test]
2420 fn parse_serial_invalid_option() {
2421 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
2422 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002423
2424 #[test]
2425 fn parse_serial_invalid_two_stdin() {
2426 let mut config = Config::default();
2427 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
2428 .expect("should parse the first serial argument");
2429 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
2430 .expect_err("should fail to parse a second serial port connected to stdin");
2431 }
Dmitry Torokhov458bb642019-12-13 11:47:52 -08002432
2433 #[test]
2434 fn parse_plugin_mount_valid() {
2435 let mut config = Config::default();
2436 set_argument(
2437 &mut config,
2438 "plugin-mount",
2439 Some("/dev/null:/dev/zero:true"),
2440 )
2441 .expect("parse should succeed");
2442 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
2443 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
2444 assert_eq!(config.plugin_mounts[0].writable, true);
2445 }
2446
2447 #[test]
2448 fn parse_plugin_mount_valid_shorthand() {
2449 let mut config = Config::default();
2450 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
2451 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
2452 assert_eq!(config.plugin_mounts[0].writable, false);
2453 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
2454 .expect("parse should succeed");
2455 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
2456 assert_eq!(config.plugin_mounts[1].writable, false);
2457 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
2458 .expect("parse should succeed");
2459 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
2460 assert_eq!(config.plugin_mounts[2].writable, true);
2461 }
2462
2463 #[test]
2464 fn parse_plugin_mount_invalid() {
2465 let mut config = Config::default();
2466 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
2467 set_argument(
2468 &mut config,
2469 "plugin-mount",
2470 Some("/dev/null:/dev/null:true:false"),
2471 )
2472 .expect_err("parse should fail because too many arguments");
2473 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
2474 .expect_err("parse should fail because source is not absolute");
2475 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
2476 .expect_err("parse should fail because source is not absolute");
2477 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
2478 .expect_err("parse should fail because flag is not boolean");
2479 }
2480
2481 #[test]
2482 fn parse_plugin_gid_map_valid() {
2483 let mut config = Config::default();
2484 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
2485 assert_eq!(config.plugin_gid_maps[0].inner, 1);
2486 assert_eq!(config.plugin_gid_maps[0].outer, 2);
2487 assert_eq!(config.plugin_gid_maps[0].count, 3);
2488 }
2489
2490 #[test]
2491 fn parse_plugin_gid_map_valid_shorthand() {
2492 let mut config = Config::default();
2493 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
2494 assert_eq!(config.plugin_gid_maps[0].inner, 1);
2495 assert_eq!(config.plugin_gid_maps[0].outer, 1);
2496 assert_eq!(config.plugin_gid_maps[0].count, 1);
2497 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
2498 assert_eq!(config.plugin_gid_maps[1].inner, 1);
2499 assert_eq!(config.plugin_gid_maps[1].outer, 2);
2500 assert_eq!(config.plugin_gid_maps[1].count, 1);
2501 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
2502 assert_eq!(config.plugin_gid_maps[2].inner, 1);
2503 assert_eq!(config.plugin_gid_maps[2].outer, 1);
2504 assert_eq!(config.plugin_gid_maps[2].count, 3);
2505 }
2506
2507 #[test]
2508 fn parse_plugin_gid_map_invalid() {
2509 let mut config = Config::default();
2510 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
2511 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
2512 .expect_err("parse should fail because too many arguments");
2513 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
2514 .expect_err("parse should fail because inner is not a number");
2515 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
2516 .expect_err("parse should fail because outer is not a number");
2517 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
2518 .expect_err("parse should fail because count is not a number");
2519 }
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002520
2521 #[test]
2522 fn single_touch_spec_and_track_pad_spec_default_size() {
2523 let mut config = Config::default();
2524 config
2525 .executable_path
2526 .replace(Executable::Kernel(PathBuf::from("kernel")));
2527 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
2528 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
2529 validate_arguments(&mut config).unwrap();
2530 assert_eq!(
2531 config.virtio_single_touch.unwrap().get_size(),
2532 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
2533 );
2534 assert_eq!(
2535 config.virtio_trackpad.unwrap().get_size(),
2536 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
2537 );
2538 }
2539
2540 #[cfg(feature = "gpu")]
2541 #[test]
2542 fn single_touch_spec_default_size_from_gpu() {
2543 let width = 12345u32;
2544 let height = 54321u32;
2545 let mut config = Config::default();
2546 config
2547 .executable_path
2548 .replace(Executable::Kernel(PathBuf::from("kernel")));
2549 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
2550 set_argument(
2551 &mut config,
2552 "gpu",
2553 Some(&format!("width={},height={}", width, height)),
2554 )
2555 .unwrap();
2556 validate_arguments(&mut config).unwrap();
2557 assert_eq!(
2558 config.virtio_single_touch.unwrap().get_size(),
2559 (width, height)
2560 );
2561 }
2562
2563 #[test]
2564 fn single_touch_spec_and_track_pad_spec_with_size() {
2565 let width = 12345u32;
2566 let height = 54321u32;
2567 let mut config = Config::default();
2568 config
2569 .executable_path
2570 .replace(Executable::Kernel(PathBuf::from("kernel")));
2571 set_argument(
2572 &mut config,
2573 "single-touch",
2574 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
2575 )
2576 .unwrap();
2577 set_argument(
2578 &mut config,
2579 "trackpad",
2580 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
2581 )
2582 .unwrap();
2583 validate_arguments(&mut config).unwrap();
2584 assert_eq!(
2585 config.virtio_single_touch.unwrap().get_size(),
2586 (width, height)
2587 );
2588 assert_eq!(config.virtio_trackpad.unwrap().get_size(), (width, height));
2589 }
2590
2591 #[cfg(feature = "gpu")]
2592 #[test]
2593 fn single_touch_spec_with_size_independent_from_gpu() {
2594 let touch_width = 12345u32;
2595 let touch_height = 54321u32;
2596 let display_width = 1234u32;
2597 let display_height = 5432u32;
2598 let mut config = Config::default();
2599 config
2600 .executable_path
2601 .replace(Executable::Kernel(PathBuf::from("kernel")));
2602 set_argument(
2603 &mut config,
2604 "single-touch",
2605 Some(&format!(
2606 "/dev/single-touch-test:{}:{}",
2607 touch_width, touch_height
2608 )),
2609 )
2610 .unwrap();
2611 set_argument(
2612 &mut config,
2613 "gpu",
2614 Some(&format!(
2615 "width={},height={}",
2616 display_width, display_height
2617 )),
2618 )
2619 .unwrap();
2620 validate_arguments(&mut config).unwrap();
2621 assert_eq!(
2622 config.virtio_single_touch.unwrap().get_size(),
2623 (touch_width, touch_height)
2624 );
2625 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07002626
2627 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
2628 #[test]
2629 fn parse_gpu_options_gfxstream_with_vulkan_specified() {
2630 assert!(
2631 parse_gpu_options(Some("backend=gfxstream,vulkan=true"))
2632 .unwrap()
2633 .gfxstream_support_vulkan
2634 );
2635 assert!(
2636 parse_gpu_options(Some("vulkan=true,backend=gfxstream"))
2637 .unwrap()
2638 .gfxstream_support_vulkan
2639 );
2640 assert!(
2641 !parse_gpu_options(Some("backend=gfxstream,vulkan=false"))
2642 .unwrap()
2643 .gfxstream_support_vulkan
2644 );
2645 assert!(
2646 !parse_gpu_options(Some("vulkan=false,backend=gfxstream"))
2647 .unwrap()
2648 .gfxstream_support_vulkan
2649 );
2650 assert!(parse_gpu_options(Some("backend=gfxstream,vulkan=invalid_value")).is_err());
2651 assert!(parse_gpu_options(Some("vulkan=invalid_value,backend=gfxstream")).is_err());
2652 }
2653
2654 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
2655 #[test]
2656 fn parse_gpu_options_not_gfxstream_with_vulkan_specified() {
2657 assert!(parse_gpu_options(Some("backend=3d,vulkan=true")).is_err());
2658 assert!(parse_gpu_options(Some("vulkan=true,backend=3d")).is_err());
2659 }
2660
2661 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
2662 #[test]
2663 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
2664 assert!(
2665 parse_gpu_options(Some("backend=gfxstream,syncfd=true"))
2666 .unwrap()
2667 .gfxstream_use_syncfd
2668 );
2669 assert!(
2670 parse_gpu_options(Some("syncfd=true,backend=gfxstream"))
2671 .unwrap()
2672 .gfxstream_use_syncfd
2673 );
2674 assert!(
2675 !parse_gpu_options(Some("backend=gfxstream,syncfd=false"))
2676 .unwrap()
2677 .gfxstream_use_syncfd
2678 );
2679 assert!(
2680 !parse_gpu_options(Some("syncfd=false,backend=gfxstream"))
2681 .unwrap()
2682 .gfxstream_use_syncfd
2683 );
2684 assert!(parse_gpu_options(Some("backend=gfxstream,syncfd=invalid_value")).is_err());
2685 assert!(parse_gpu_options(Some("syncfd=invalid_value,backend=gfxstream")).is_err());
2686 }
2687
2688 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
2689 #[test]
2690 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
2691 assert!(parse_gpu_options(Some("backend=3d,syncfd=true")).is_err());
2692 assert!(parse_gpu_options(Some("syncfd=true,backend=3d")).is_err());
2693 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08002694
2695 #[test]
2696 fn parse_battery_vaild() {
2697 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
2698 }
2699
2700 #[test]
2701 fn parse_battery_vaild_no_type() {
2702 parse_battery_options(None).expect("parse should have succeded");
2703 }
2704
2705 #[test]
2706 fn parse_battery_invaild_parameter() {
2707 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
2708 }
2709
2710 #[test]
2711 fn parse_battery_invaild_type_value() {
2712 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
2713 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07002714}