Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 1 | // 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 Richman | f32d0b4 | 2020-06-20 21:45:32 -0700 | [diff] [blame] | 5 | //! Runs a virtual machine |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 6 | |
Zach Reizner | b3fa5c9 | 2019-01-28 14:05:23 -0800 | [diff] [blame] | 7 | pub mod panic_hook; |
Zach Reizner | 29ad3c7 | 2017-08-04 15:12:58 -0700 | [diff] [blame] | 8 | |
Daniel Verkamp | c677fb4 | 2020-09-08 13:47:49 -0700 | [diff] [blame] | 9 | use std::collections::BTreeMap; |
Mattias Nissler | de2c640 | 2021-10-21 12:05:29 +0000 | [diff] [blame] | 10 | use std::convert::TryFrom; |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 11 | use std::default::Default; |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 12 | use std::fs::{File, OpenOptions}; |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 13 | use std::io::{BufRead, BufReader}; |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 14 | use std::path::{Path, PathBuf}; |
Jorge E. Moreira | 359e7de | 2020-12-02 18:25:53 -0800 | [diff] [blame] | 15 | use std::str::FromStr; |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 16 | use std::string::String; |
Zach Reizner | 39aa26b | 2017-12-12 18:03:23 -0800 | [diff] [blame] | 17 | use std::thread::sleep; |
Stephen Barber | 56fbf09 | 2017-06-29 16:12:14 -0700 | [diff] [blame] | 18 | use std::time::Duration; |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 19 | |
Keiichi Watanabe | 553d219 | 2021-08-16 16:42:27 +0900 | [diff] [blame] | 20 | use arch::{set_default_serial_parameters, Pstore, VcpuAffinity}; |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 21 | use base::{debug, error, getpid, info, kill_process_group, reap_child, syslog, warn}; |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 22 | #[cfg(feature = "direct")] |
| 23 | use crosvm::DirectIoOption; |
Zach Reizner | 267f2c8 | 2019-07-31 17:07:27 -0700 | [diff] [blame] | 24 | use crosvm::{ |
| 25 | argument::{self, print_help, set_arguments, Argument}, |
Michael Hoyle | e47a500 | 2020-10-15 16:24:13 -0700 | [diff] [blame] | 26 | platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption, |
Christian Blichmann | 50f9591 | 2021-11-05 16:59:39 +0100 | [diff] [blame] | 27 | VfioCommand, VhostUserFsOption, VhostUserOption, VhostUserWlOption, VhostVsockDeviceParameter, |
| 28 | DISK_ID_LEN, |
Zach Reizner | 267f2c8 | 2019-07-31 17:07:27 -0700 | [diff] [blame] | 29 | }; |
Keiichi Watanabe | 553d219 | 2021-08-16 16:42:27 +0900 | [diff] [blame] | 30 | use devices::serial_device::{SerialHardware, SerialParameters, SerialType}; |
Chia-I Wu | cba95db | 2021-12-09 15:18:58 -0800 | [diff] [blame] | 31 | #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))] |
| 32 | use devices::virtio::gpu::GpuRenderServerParameters; |
Woody Chow | 0b2b606 | 2021-09-03 15:40:02 +0900 | [diff] [blame] | 33 | #[cfg(feature = "audio_cras")] |
| 34 | use devices::virtio::snd::cras_backend::Error as CrasSndError; |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 35 | use devices::virtio::vhost::user::device::{ |
Chirantan Ekbote | f08bddd | 2021-09-10 18:41:06 +0900 | [diff] [blame] | 36 | run_block_device, run_console_device, run_fs_device, run_net_device, run_vsock_device, |
| 37 | run_wl_device, |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 38 | }; |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 39 | #[cfg(any(feature = "video-decoder", feature = "video-encoder"))] |
| 40 | use devices::virtio::VideoBackendType; |
Chirantan Ekbote | 7822529 | 2021-06-25 18:30:34 +0900 | [diff] [blame] | 41 | #[cfg(feature = "gpu")] |
| 42 | use devices::virtio::{ |
| 43 | gpu::{ |
Chia-I Wu | cba95db | 2021-12-09 15:18:58 -0800 | [diff] [blame] | 44 | GpuDisplayParameters, GpuMode, GpuParameters, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH, |
Chirantan Ekbote | 7822529 | 2021-06-25 18:30:34 +0900 | [diff] [blame] | 45 | }, |
| 46 | vhost::user::device::run_gpu_device, |
| 47 | }; |
Junichi Uekawa | b180f9c | 2021-12-07 09:21:36 +0900 | [diff] [blame] | 48 | #[cfg(feature = "direct")] |
| 49 | use devices::BusRange; |
Andrew Scull | 1590e6f | 2020-03-18 18:00:47 +0000 | [diff] [blame] | 50 | #[cfg(feature = "audio")] |
Daniel Verkamp | fbd6122 | 2020-02-14 16:46:36 -0800 | [diff] [blame] | 51 | use devices::{Ac97Backend, Ac97Parameters}; |
Andrew Walbran | 00f1c9f | 2021-12-10 17:13:08 +0000 | [diff] [blame] | 52 | use devices::{PciAddress, PciClassCode, StubPciParameters}; |
Daniel Verkamp | eb1640e | 2021-09-07 14:09:31 -0700 | [diff] [blame] | 53 | use disk::{self, QcowFile}; |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 54 | #[cfg(feature = "composite-disk")] |
Jooyung Han | 2e14c73 | 2021-07-29 13:27:54 +0900 | [diff] [blame] | 55 | use disk::{ |
| 56 | create_composite_disk, create_disk_file, create_zero_filler, ImagePartitionType, PartitionInfo, |
| 57 | }; |
Andrew Walbran | 00f1c9f | 2021-12-10 17:13:08 +0000 | [diff] [blame] | 58 | use hypervisor::ProtectionType; |
Jakub Staron | e7c5905 | 2019-04-09 12:31:14 -0700 | [diff] [blame] | 59 | use vm_control::{ |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 60 | client::{ |
| 61 | do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request, |
| 62 | ModifyUsbError, ModifyUsbResult, |
| 63 | }, |
| 64 | BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest, |
Hikaru Nishida | 6b51c75 | 2021-05-21 12:37:43 +0900 | [diff] [blame] | 65 | VmResponse, |
Jakub Staron | e7c5905 | 2019-04-09 12:31:14 -0700 | [diff] [blame] | 66 | }; |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 67 | |
Chirantan Ekbote | 520ad43 | 2021-12-03 17:43:37 +0900 | [diff] [blame] | 68 | #[cfg(feature = "scudo")] |
| 69 | #[global_allocator] |
| 70 | static ALLOCATOR: scudo::GlobalScudoAllocator = scudo::GlobalScudoAllocator; |
| 71 | |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 72 | fn executable_is_plugin(executable: &Option<Executable>) -> bool { |
Daniel Verkamp | c26d20b | 2020-11-04 14:39:31 -0800 | [diff] [blame] | 73 | matches!(executable, Some(Executable::Plugin(_))) |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 74 | } |
| 75 | |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 76 | // Wait for all children to exit. Return true if they have all exited, false |
| 77 | // otherwise. |
| 78 | fn wait_all_children() -> bool { |
Stephen Barber | 49dd2e2 | 2018-10-29 18:29:58 -0700 | [diff] [blame] | 79 | const CHILD_WAIT_MAX_ITER: isize = 100; |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 80 | const CHILD_WAIT_MS: u64 = 10; |
| 81 | for _ in 0..CHILD_WAIT_MAX_ITER { |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 82 | loop { |
Zach Reizner | 56158c8 | 2017-08-24 13:50:14 -0700 | [diff] [blame] | 83 | match reap_child() { |
| 84 | Ok(0) => break, |
| 85 | // We expect ECHILD which indicates that there were no children left. |
| 86 | Err(e) if e.errno() == libc::ECHILD => return true, |
| 87 | Err(e) => { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 88 | warn!("error while waiting for children: {}", e); |
Zach Reizner | 56158c8 | 2017-08-24 13:50:14 -0700 | [diff] [blame] | 89 | return false; |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 90 | } |
Zach Reizner | 56158c8 | 2017-08-24 13:50:14 -0700 | [diff] [blame] | 91 | // We reaped one child, so continue reaping. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 92 | _ => {} |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 93 | } |
| 94 | } |
Zach Reizner | 56158c8 | 2017-08-24 13:50:14 -0700 | [diff] [blame] | 95 | // There's no timeout option for waitpid which reap_child calls internally, so our only |
| 96 | // recourse is to sleep while waiting for the children to exit. |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 97 | sleep(Duration::from_millis(CHILD_WAIT_MS)); |
| 98 | } |
| 99 | |
| 100 | // If we've made it to this point, not all of the children have exited. |
David Tolnay | 5bbbf61 | 2018-12-01 17:49:30 -0800 | [diff] [blame] | 101 | false |
Stephen Barber | a00753b | 2017-07-18 13:57:26 -0700 | [diff] [blame] | 102 | } |
| 103 | |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 104 | /// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers. |
| 105 | fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> { |
| 106 | let mut cpuset = Vec::new(); |
| 107 | for part in s.split(',') { |
| 108 | let range: Vec<&str> = part.split('-').collect(); |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 109 | if range.is_empty() || range.len() > 2 { |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 110 | return Err(argument::Error::InvalidValue { |
| 111 | value: part.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 112 | expected: String::from("invalid list syntax"), |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 113 | }); |
| 114 | } |
| 115 | let first_cpu: usize = range[0] |
| 116 | .parse() |
| 117 | .map_err(|_| argument::Error::InvalidValue { |
| 118 | value: part.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 119 | expected: String::from("CPU index must be a non-negative integer"), |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 120 | })?; |
| 121 | let last_cpu: usize = if range.len() == 2 { |
| 122 | range[1] |
| 123 | .parse() |
| 124 | .map_err(|_| argument::Error::InvalidValue { |
| 125 | value: part.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 126 | expected: String::from("CPU index must be a non-negative integer"), |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 127 | })? |
| 128 | } else { |
| 129 | first_cpu |
| 130 | }; |
| 131 | |
| 132 | if last_cpu < first_cpu { |
| 133 | return Err(argument::Error::InvalidValue { |
| 134 | value: part.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 135 | expected: String::from("CPU ranges must be from low to high"), |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 136 | }); |
| 137 | } |
| 138 | |
| 139 | for cpu in first_cpu..=last_cpu { |
| 140 | cpuset.push(cpu); |
| 141 | } |
| 142 | } |
| 143 | Ok(cpuset) |
| 144 | } |
| 145 | |
Daniel Verkamp | c677fb4 | 2020-09-08 13:47:49 -0700 | [diff] [blame] | 146 | /// Parse a list of guest to host CPU mappings. |
| 147 | /// |
| 148 | /// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form |
| 149 | /// accepted by `parse_cpu_set`: |
| 150 | /// |
| 151 | /// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]` |
| 152 | fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> { |
| 153 | if s.contains('=') { |
| 154 | let mut affinity_map = BTreeMap::new(); |
| 155 | for cpu_pair in s.split(':') { |
| 156 | let assignment: Vec<&str> = cpu_pair.split('=').collect(); |
| 157 | if assignment.len() != 2 { |
| 158 | return Err(argument::Error::InvalidValue { |
| 159 | value: cpu_pair.to_owned(), |
| 160 | expected: String::from("invalid VCPU assignment syntax"), |
| 161 | }); |
| 162 | } |
| 163 | let guest_cpu = assignment[0] |
| 164 | .parse() |
| 165 | .map_err(|_| argument::Error::InvalidValue { |
| 166 | value: assignment[0].to_owned(), |
| 167 | expected: String::from("CPU index must be a non-negative integer"), |
| 168 | })?; |
| 169 | let host_cpu_set = parse_cpu_set(assignment[1])?; |
| 170 | if affinity_map.insert(guest_cpu, host_cpu_set).is_some() { |
| 171 | return Err(argument::Error::InvalidValue { |
| 172 | value: cpu_pair.to_owned(), |
| 173 | expected: String::from("VCPU index must be unique"), |
| 174 | }); |
| 175 | } |
| 176 | } |
| 177 | Ok(VcpuAffinity::PerVcpu(affinity_map)) |
| 178 | } else { |
| 179 | Ok(VcpuAffinity::Global(parse_cpu_set(s)?)) |
| 180 | } |
| 181 | } |
| 182 | |
Daniel Verkamp | 8a72afc | 2021-03-15 17:55:52 -0700 | [diff] [blame] | 183 | fn parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()> { |
| 184 | for cpu_pair in s.split(',') { |
| 185 | let assignment: Vec<&str> = cpu_pair.split('=').collect(); |
| 186 | if assignment.len() != 2 { |
| 187 | return Err(argument::Error::InvalidValue { |
| 188 | value: cpu_pair.to_owned(), |
| 189 | expected: String::from("invalid CPU capacity syntax"), |
| 190 | }); |
| 191 | } |
| 192 | let cpu = assignment[0] |
| 193 | .parse() |
| 194 | .map_err(|_| argument::Error::InvalidValue { |
| 195 | value: assignment[0].to_owned(), |
| 196 | expected: String::from("CPU index must be a non-negative integer"), |
| 197 | })?; |
| 198 | let capacity = assignment[1] |
| 199 | .parse() |
| 200 | .map_err(|_| argument::Error::InvalidValue { |
| 201 | value: assignment[1].to_owned(), |
| 202 | expected: String::from("CPU capacity must be a non-negative integer"), |
| 203 | })?; |
| 204 | if cpu_capacity.insert(cpu, capacity).is_some() { |
| 205 | return Err(argument::Error::InvalidValue { |
| 206 | value: cpu_pair.to_owned(), |
| 207 | expected: String::from("CPU index must be unique"), |
| 208 | }); |
| 209 | } |
| 210 | } |
| 211 | Ok(()) |
| 212 | } |
| 213 | |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 214 | #[cfg(feature = "gpu")] |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 215 | fn parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()> { |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 216 | #[cfg(feature = "gfxstream")] |
| 217 | let mut vulkan_specified = false; |
| 218 | #[cfg(feature = "gfxstream")] |
| 219 | let mut syncfd_specified = false; |
Jason Macnak | 046ed14 | 2020-10-08 09:11:53 -0700 | [diff] [blame] | 220 | #[cfg(feature = "gfxstream")] |
| 221 | let mut angle_specified = false; |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 222 | |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 223 | let mut display_w: Option<u32> = None; |
| 224 | let mut display_h: Option<u32> = None; |
| 225 | |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 226 | if let Some(s) = s { |
| 227 | let opts = s |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 228 | .split(',') |
| 229 | .map(|frag| frag.split('=')) |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 230 | .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); |
| 231 | |
| 232 | for (k, v) in opts { |
| 233 | match k { |
Lingfeng Yang | ddbe8b7 | 2020-01-30 10:00:36 -0800 | [diff] [blame] | 234 | // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple |
Chia-I Wu | e25859b | 2021-04-14 13:57:55 -0700 | [diff] [blame] | 235 | // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream) |
Jason Macnak | 327fc24 | 2020-01-10 12:45:36 -0800 | [diff] [blame] | 236 | "2d" | "2D" => { |
| 237 | gpu_params.mode = GpuMode::Mode2D; |
| 238 | } |
Chia-I Wu | e25859b | 2021-04-14 13:57:55 -0700 | [diff] [blame] | 239 | "3d" | "3D" | "virglrenderer" => { |
| 240 | gpu_params.mode = GpuMode::ModeVirglRenderer; |
Jason Macnak | 327fc24 | 2020-01-10 12:45:36 -0800 | [diff] [blame] | 241 | } |
Lingfeng Yang | ddbe8b7 | 2020-01-30 10:00:36 -0800 | [diff] [blame] | 242 | #[cfg(feature = "gfxstream")] |
| 243 | "gfxstream" => { |
Gurchetan Singh | b1394f7 | 2020-10-19 18:31:13 -0700 | [diff] [blame] | 244 | gpu_params.mode = GpuMode::ModeGfxstream; |
Lingfeng Yang | ddbe8b7 | 2020-01-30 10:00:36 -0800 | [diff] [blame] | 245 | } |
| 246 | // Preferred: Specifying --gpu,backend=<mode> |
| 247 | "backend" => match v { |
| 248 | "2d" | "2D" => { |
| 249 | gpu_params.mode = GpuMode::Mode2D; |
| 250 | } |
Chia-I Wu | e25859b | 2021-04-14 13:57:55 -0700 | [diff] [blame] | 251 | "3d" | "3D" | "virglrenderer" => { |
| 252 | gpu_params.mode = GpuMode::ModeVirglRenderer; |
Lingfeng Yang | ddbe8b7 | 2020-01-30 10:00:36 -0800 | [diff] [blame] | 253 | } |
| 254 | #[cfg(feature = "gfxstream")] |
| 255 | "gfxstream" => { |
Gurchetan Singh | b1394f7 | 2020-10-19 18:31:13 -0700 | [diff] [blame] | 256 | gpu_params.mode = GpuMode::ModeGfxstream; |
Lingfeng Yang | ddbe8b7 | 2020-01-30 10:00:36 -0800 | [diff] [blame] | 257 | } |
| 258 | _ => { |
| 259 | return Err(argument::Error::InvalidValue { |
| 260 | value: v.to_string(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 261 | expected: String::from( |
Chia-I Wu | e25859b | 2021-04-14 13:57:55 -0700 | [diff] [blame] | 262 | "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)", |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 263 | ), |
Lingfeng Yang | ddbe8b7 | 2020-01-30 10:00:36 -0800 | [diff] [blame] | 264 | }); |
| 265 | } |
| 266 | }, |
Jason Macnak | bf19558 | 2019-11-20 16:25:49 -0800 | [diff] [blame] | 267 | "egl" => match v { |
| 268 | "true" | "" => { |
| 269 | gpu_params.renderer_use_egl = true; |
| 270 | } |
| 271 | "false" => { |
| 272 | gpu_params.renderer_use_egl = false; |
| 273 | } |
| 274 | _ => { |
| 275 | return Err(argument::Error::InvalidValue { |
| 276 | value: v.to_string(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 277 | expected: String::from("gpu parameter 'egl' should be a boolean"), |
Jason Macnak | bf19558 | 2019-11-20 16:25:49 -0800 | [diff] [blame] | 278 | }); |
| 279 | } |
| 280 | }, |
| 281 | "gles" => match v { |
| 282 | "true" | "" => { |
| 283 | gpu_params.renderer_use_gles = true; |
| 284 | } |
| 285 | "false" => { |
| 286 | gpu_params.renderer_use_gles = false; |
| 287 | } |
| 288 | _ => { |
| 289 | return Err(argument::Error::InvalidValue { |
| 290 | value: v.to_string(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 291 | expected: String::from("gpu parameter 'gles' should be a boolean"), |
Jason Macnak | bf19558 | 2019-11-20 16:25:49 -0800 | [diff] [blame] | 292 | }); |
| 293 | } |
| 294 | }, |
| 295 | "glx" => match v { |
| 296 | "true" | "" => { |
| 297 | gpu_params.renderer_use_glx = true; |
| 298 | } |
| 299 | "false" => { |
| 300 | gpu_params.renderer_use_glx = false; |
| 301 | } |
| 302 | _ => { |
| 303 | return Err(argument::Error::InvalidValue { |
| 304 | value: v.to_string(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 305 | expected: String::from("gpu parameter 'glx' should be a boolean"), |
Jason Macnak | bf19558 | 2019-11-20 16:25:49 -0800 | [diff] [blame] | 306 | }); |
| 307 | } |
| 308 | }, |
| 309 | "surfaceless" => match v { |
| 310 | "true" | "" => { |
| 311 | gpu_params.renderer_use_surfaceless = true; |
| 312 | } |
| 313 | "false" => { |
| 314 | gpu_params.renderer_use_surfaceless = false; |
| 315 | } |
| 316 | _ => { |
| 317 | return Err(argument::Error::InvalidValue { |
| 318 | value: v.to_string(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 319 | expected: String::from( |
| 320 | "gpu parameter 'surfaceless' should be a boolean", |
| 321 | ), |
Jason Macnak | bf19558 | 2019-11-20 16:25:49 -0800 | [diff] [blame] | 322 | }); |
| 323 | } |
| 324 | }, |
Kaiyi Li | 6404e45 | 2020-07-07 19:12:03 -0700 | [diff] [blame] | 325 | #[cfg(feature = "gfxstream")] |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 326 | "syncfd" => { |
| 327 | syncfd_specified = true; |
| 328 | match v { |
| 329 | "true" | "" => { |
| 330 | gpu_params.gfxstream_use_syncfd = true; |
| 331 | } |
| 332 | "false" => { |
| 333 | gpu_params.gfxstream_use_syncfd = false; |
| 334 | } |
| 335 | _ => { |
| 336 | return Err(argument::Error::InvalidValue { |
| 337 | value: v.to_string(), |
| 338 | expected: String::from( |
| 339 | "gpu parameter 'syncfd' should be a boolean", |
| 340 | ), |
| 341 | }); |
| 342 | } |
Kaiyi Li | 6404e45 | 2020-07-07 19:12:03 -0700 | [diff] [blame] | 343 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 344 | } |
Kaiyi Li | 6c52a2e | 2020-07-07 19:28:32 -0700 | [diff] [blame] | 345 | #[cfg(feature = "gfxstream")] |
Jason Macnak | 046ed14 | 2020-10-08 09:11:53 -0700 | [diff] [blame] | 346 | "angle" => { |
| 347 | angle_specified = true; |
| 348 | match v { |
| 349 | "true" | "" => { |
| 350 | gpu_params.gfxstream_use_guest_angle = true; |
| 351 | } |
| 352 | "false" => { |
| 353 | gpu_params.gfxstream_use_guest_angle = false; |
| 354 | } |
| 355 | _ => { |
| 356 | return Err(argument::Error::InvalidValue { |
| 357 | value: v.to_string(), |
| 358 | expected: String::from("gpu parameter 'angle' should be a boolean"), |
| 359 | }); |
| 360 | } |
| 361 | } |
| 362 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 363 | "vulkan" => { |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 364 | #[cfg(feature = "gfxstream")] |
| 365 | { |
| 366 | vulkan_specified = true; |
| 367 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 368 | match v { |
| 369 | "true" | "" => { |
Chia-I Wu | 0beb246 | 2021-04-12 09:36:08 -0700 | [diff] [blame] | 370 | gpu_params.use_vulkan = true; |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 371 | } |
| 372 | "false" => { |
Chia-I Wu | 0beb246 | 2021-04-12 09:36:08 -0700 | [diff] [blame] | 373 | gpu_params.use_vulkan = false; |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 374 | } |
| 375 | _ => { |
| 376 | return Err(argument::Error::InvalidValue { |
| 377 | value: v.to_string(), |
| 378 | expected: String::from( |
| 379 | "gpu parameter 'vulkan' should be a boolean", |
| 380 | ), |
| 381 | }); |
| 382 | } |
Kaiyi Li | 6c52a2e | 2020-07-07 19:28:32 -0700 | [diff] [blame] | 383 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 384 | } |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 385 | "width" => { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 386 | let width = v |
| 387 | .parse::<u32>() |
| 388 | .map_err(|_| argument::Error::InvalidValue { |
| 389 | value: v.to_string(), |
| 390 | expected: String::from("gpu parameter 'width' must be a valid integer"), |
| 391 | })?; |
| 392 | display_w = Some(width); |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 393 | } |
| 394 | "height" => { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 395 | let height = v |
| 396 | .parse::<u32>() |
| 397 | .map_err(|_| argument::Error::InvalidValue { |
| 398 | value: v.to_string(), |
| 399 | expected: String::from( |
| 400 | "gpu parameter 'height' must be a valid integer", |
| 401 | ), |
| 402 | })?; |
| 403 | display_h = Some(height); |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 404 | } |
John Bates | b220eac | 2020-09-14 17:03:02 -0700 | [diff] [blame] | 405 | "cache-path" => gpu_params.cache_path = Some(v.to_string()), |
| 406 | "cache-size" => gpu_params.cache_size = Some(v.to_string()), |
Gurchetan Singh | 401340e | 2021-03-23 09:34:00 -0700 | [diff] [blame] | 407 | "udmabuf" => match v { |
| 408 | "true" | "" => { |
| 409 | gpu_params.udmabuf = true; |
| 410 | } |
| 411 | "false" => { |
| 412 | gpu_params.udmabuf = false; |
| 413 | } |
| 414 | _ => { |
| 415 | return Err(argument::Error::InvalidValue { |
| 416 | value: v.to_string(), |
| 417 | expected: String::from("gpu parameter 'udmabuf' should be a boolean"), |
| 418 | }); |
| 419 | } |
| 420 | }, |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 421 | "" => {} |
| 422 | _ => { |
| 423 | return Err(argument::Error::UnknownArgument(format!( |
| 424 | "gpu parameter {}", |
| 425 | k |
| 426 | ))); |
| 427 | } |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 432 | if display_w.is_some() || display_h.is_some() { |
| 433 | if display_w.is_none() || display_h.is_none() { |
| 434 | return Err(argument::Error::InvalidValue { |
| 435 | value: s.unwrap_or("").to_string(), |
| 436 | expected: String::from( |
| 437 | "gpu must include both 'width' and 'height' if either is supplied", |
| 438 | ), |
| 439 | }); |
| 440 | } |
| 441 | |
| 442 | gpu_params.displays.push(GpuDisplayParameters { |
| 443 | width: display_w.unwrap(), |
| 444 | height: display_h.unwrap(), |
| 445 | }); |
| 446 | } |
| 447 | |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 448 | #[cfg(feature = "gfxstream")] |
| 449 | { |
Chia-I Wu | 91df656 | 2021-04-12 09:47:38 -0700 | [diff] [blame] | 450 | if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream { |
| 451 | gpu_params.use_vulkan = true; |
| 452 | } |
| 453 | |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 454 | if syncfd_specified || angle_specified { |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 455 | match gpu_params.mode { |
Gurchetan Singh | b1394f7 | 2020-10-19 18:31:13 -0700 | [diff] [blame] | 456 | GpuMode::ModeGfxstream => {} |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 457 | _ => { |
| 458 | return Err(argument::Error::UnknownArgument( |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 459 | "gpu parameter syncfd and angle are only supported for gfxstream backend" |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 460 | .to_string(), |
| 461 | )); |
| 462 | } |
| 463 | } |
| 464 | } |
| 465 | } |
| 466 | |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 467 | Ok(()) |
| 468 | } |
| 469 | |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 470 | #[cfg(any(feature = "video-decoder", feature = "video-encoder"))] |
| 471 | fn parse_video_options(s: Option<&str>) -> argument::Result<VideoBackendType> { |
Alexandre Courbot | c02960d | 2021-07-11 23:06:30 +0900 | [diff] [blame] | 472 | const VALID_VIDEO_BACKENDS: &[&str] = &[ |
| 473 | #[cfg(feature = "libvda")] |
| 474 | "libvda", |
| 475 | ]; |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 476 | |
| 477 | match s { |
Alexandre Courbot | c02960d | 2021-07-11 23:06:30 +0900 | [diff] [blame] | 478 | #[cfg(feature = "libvda")] |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 479 | None => Ok(VideoBackendType::Libvda), |
Alexandre Courbot | c02960d | 2021-07-11 23:06:30 +0900 | [diff] [blame] | 480 | #[cfg(feature = "libvda")] |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 481 | Some("libvda") => Ok(VideoBackendType::Libvda), |
David Staessens | a78ae7a | 2021-07-15 09:48:56 +0900 | [diff] [blame] | 482 | #[cfg(feature = "libvda")] |
Alexandre Courbot | 54cf834 | 2021-12-20 18:10:08 +0900 | [diff] [blame] | 483 | Some("libvda-vd") => Ok(VideoBackendType::LibvdaVd), |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 484 | Some(s) => Err(argument::Error::InvalidValue { |
| 485 | value: s.to_owned(), |
| 486 | expected: format!("should be one of ({})", VALID_VIDEO_BACKENDS.join("|")), |
| 487 | }), |
| 488 | } |
| 489 | } |
| 490 | |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 491 | #[cfg(feature = "gpu")] |
| 492 | fn parse_gpu_display_options( |
| 493 | s: Option<&str>, |
| 494 | gpu_params: &mut GpuParameters, |
| 495 | ) -> argument::Result<()> { |
| 496 | let mut display_w: Option<u32> = None; |
| 497 | let mut display_h: Option<u32> = None; |
| 498 | |
| 499 | if let Some(s) = s { |
| 500 | let opts = s |
| 501 | .split(',') |
| 502 | .map(|frag| frag.split('=')) |
| 503 | .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); |
| 504 | |
| 505 | for (k, v) in opts { |
| 506 | match k { |
| 507 | "width" => { |
| 508 | let width = v |
| 509 | .parse::<u32>() |
| 510 | .map_err(|_| argument::Error::InvalidValue { |
| 511 | value: v.to_string(), |
| 512 | expected: String::from("gpu parameter 'width' must be a valid integer"), |
| 513 | })?; |
| 514 | display_w = Some(width); |
| 515 | } |
| 516 | "height" => { |
| 517 | let height = v |
| 518 | .parse::<u32>() |
| 519 | .map_err(|_| argument::Error::InvalidValue { |
| 520 | value: v.to_string(), |
| 521 | expected: String::from( |
| 522 | "gpu parameter 'height' must be a valid integer", |
| 523 | ), |
| 524 | })?; |
| 525 | display_h = Some(height); |
| 526 | } |
| 527 | "" => {} |
| 528 | _ => { |
| 529 | return Err(argument::Error::UnknownArgument(format!( |
| 530 | "gpu-display parameter {}", |
| 531 | k |
| 532 | ))); |
| 533 | } |
| 534 | } |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | if display_w.is_none() || display_h.is_none() { |
| 539 | return Err(argument::Error::InvalidValue { |
| 540 | value: s.unwrap_or("").to_string(), |
| 541 | expected: String::from("gpu-display must include both 'width' and 'height'"), |
| 542 | }); |
| 543 | } |
| 544 | |
| 545 | gpu_params.displays.push(GpuDisplayParameters { |
| 546 | width: display_w.unwrap(), |
| 547 | height: display_h.unwrap(), |
| 548 | }); |
| 549 | |
| 550 | Ok(()) |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 551 | } |
| 552 | |
Chia-I Wu | 16fb659 | 2021-11-10 11:45:32 -0800 | [diff] [blame] | 553 | #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))] |
| 554 | fn parse_gpu_render_server_options( |
| 555 | s: Option<&str>, |
| 556 | gpu_params: &mut GpuParameters, |
| 557 | ) -> argument::Result<()> { |
| 558 | let mut path: Option<PathBuf> = None; |
| 559 | |
| 560 | if let Some(s) = s { |
| 561 | let opts = s |
| 562 | .split(',') |
| 563 | .map(|frag| frag.split('=')) |
| 564 | .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); |
| 565 | |
| 566 | for (k, v) in opts { |
| 567 | match k { |
| 568 | "path" => { |
| 569 | path = |
| 570 | Some( |
| 571 | PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue { |
| 572 | value: v.to_string(), |
| 573 | expected: e.to_string(), |
| 574 | })?, |
| 575 | ) |
| 576 | } |
| 577 | "" => {} |
| 578 | _ => { |
| 579 | return Err(argument::Error::UnknownArgument(format!( |
| 580 | "gpu-render-server parameter {}", |
| 581 | k |
| 582 | ))); |
| 583 | } |
| 584 | } |
| 585 | } |
| 586 | } |
| 587 | |
| 588 | if let Some(p) = path { |
| 589 | gpu_params.render_server = Some(GpuRenderServerParameters { path: p }); |
| 590 | Ok(()) |
| 591 | } else { |
| 592 | Err(argument::Error::InvalidValue { |
| 593 | value: s.unwrap_or("").to_string(), |
| 594 | expected: String::from("gpu-render-server must include 'path'"), |
| 595 | }) |
| 596 | } |
| 597 | } |
| 598 | |
Andrew Scull | 1590e6f | 2020-03-18 18:00:47 +0000 | [diff] [blame] | 599 | #[cfg(feature = "audio")] |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 600 | fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> { |
| 601 | let mut ac97_params: Ac97Parameters = Default::default(); |
| 602 | |
| 603 | let opts = s |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 604 | .split(',') |
| 605 | .map(|frag| frag.split('=')) |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 606 | .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); |
| 607 | |
| 608 | for (k, v) in opts { |
| 609 | match k { |
| 610 | "backend" => { |
| 611 | ac97_params.backend = |
| 612 | v.parse::<Ac97Backend>() |
| 613 | .map_err(|e| argument::Error::InvalidValue { |
| 614 | value: v.to_string(), |
| 615 | expected: e.to_string(), |
| 616 | })?; |
| 617 | } |
| 618 | "capture" => { |
| 619 | ac97_params.capture = v.parse::<bool>().map_err(|e| { |
| 620 | argument::Error::Syntax(format!("invalid capture option: {}", e)) |
| 621 | })?; |
| 622 | } |
Dennis Kempin | 50a58f9 | 2021-06-23 11:34:31 -0700 | [diff] [blame] | 623 | #[cfg(feature = "audio_cras")] |
paulhsia | 83d5160 | 2021-03-09 17:13:14 +0800 | [diff] [blame] | 624 | "client_type" => { |
| 625 | ac97_params |
| 626 | .set_client_type(v) |
| 627 | .map_err(|e| argument::Error::InvalidValue { |
| 628 | value: v.to_string(), |
| 629 | expected: e.to_string(), |
| 630 | })?; |
| 631 | } |
Woody Chow | b27dea4 | 2021-09-08 15:51:22 +0900 | [diff] [blame] | 632 | #[cfg(feature = "audio_cras")] |
| 633 | "socket_type" => { |
| 634 | ac97_params |
| 635 | .set_socket_type(v) |
| 636 | .map_err(|e| argument::Error::InvalidValue { |
| 637 | value: v.to_string(), |
| 638 | expected: e.to_string(), |
| 639 | })?; |
| 640 | } |
Jorge E. Moreira | 6a88a5d | 2021-03-12 15:34:46 -0800 | [diff] [blame] | 641 | #[cfg(any(target_os = "linux", target_os = "android"))] |
Jorge E. Moreira | 359e7de | 2020-12-02 18:25:53 -0800 | [diff] [blame] | 642 | "server" => { |
| 643 | ac97_params.vios_server_path = |
| 644 | Some( |
| 645 | PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue { |
| 646 | value: v.to_string(), |
| 647 | expected: e.to_string(), |
| 648 | })?, |
| 649 | ); |
| 650 | } |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 651 | _ => { |
| 652 | return Err(argument::Error::UnknownArgument(format!( |
| 653 | "unknown ac97 parameter {}", |
| 654 | k |
| 655 | ))); |
| 656 | } |
| 657 | } |
| 658 | } |
| 659 | |
Jorge E. Moreira | 359e7de | 2020-12-02 18:25:53 -0800 | [diff] [blame] | 660 | // server is required for and exclusive to vios backend |
Jorge E. Moreira | 6a88a5d | 2021-03-12 15:34:46 -0800 | [diff] [blame] | 661 | #[cfg(any(target_os = "linux", target_os = "android"))] |
Jorge E. Moreira | 359e7de | 2020-12-02 18:25:53 -0800 | [diff] [blame] | 662 | match ac97_params.backend { |
| 663 | Ac97Backend::VIOS => { |
| 664 | if ac97_params.vios_server_path.is_none() { |
| 665 | return Err(argument::Error::ExpectedArgument(String::from( |
| 666 | "server argument is required for VIOS backend", |
| 667 | ))); |
| 668 | } |
| 669 | } |
| 670 | _ => { |
| 671 | if ac97_params.vios_server_path.is_some() { |
| 672 | return Err(argument::Error::UnexpectedValue(String::from( |
| 673 | "server argument is exclusive to the VIOS backend", |
| 674 | ))); |
| 675 | } |
| 676 | } |
| 677 | } |
| 678 | |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 679 | Ok(ac97_params) |
| 680 | } |
| 681 | |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 682 | fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> { |
| 683 | let mut serial_setting = SerialParameters { |
| 684 | type_: SerialType::Sink, |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 685 | hardware: SerialHardware::Serial, |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 686 | path: None, |
Iliyan Malchev | 2c1417b | 2020-04-14 09:40:41 -0700 | [diff] [blame] | 687 | input: None, |
Trent Begin | 923bab0 | 2019-06-17 13:48:06 -0600 | [diff] [blame] | 688 | num: 1, |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 689 | console: false, |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 690 | earlycon: false, |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 691 | stdin: false, |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 692 | }; |
| 693 | |
| 694 | let opts = s |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 695 | .split(',') |
Nicholas Hollingum | c76c2da | 2020-09-18 15:53:16 +1000 | [diff] [blame] | 696 | .map(|frag| frag.splitn(2, '=')) |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 697 | .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); |
| 698 | |
| 699 | for (k, v) in opts { |
| 700 | match k { |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 701 | "hardware" => { |
| 702 | serial_setting.hardware = v |
| 703 | .parse::<SerialHardware>() |
| 704 | .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))? |
| 705 | } |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 706 | "type" => { |
| 707 | serial_setting.type_ = v |
| 708 | .parse::<SerialType>() |
| 709 | .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))? |
| 710 | } |
| 711 | "num" => { |
| 712 | let num = v.parse::<u8>().map_err(|e| { |
| 713 | argument::Error::Syntax(format!("serial device number is not parsable: {}", e)) |
| 714 | })?; |
A. Cody Schuffelen | 3faab5a | 2020-10-05 17:29:16 -0700 | [diff] [blame] | 715 | if num < 1 { |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 716 | return Err(argument::Error::InvalidValue { |
| 717 | value: num.to_string(), |
A. Cody Schuffelen | 3faab5a | 2020-10-05 17:29:16 -0700 | [diff] [blame] | 718 | expected: String::from("Serial port num must be at least 1"), |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 719 | }); |
| 720 | } |
| 721 | serial_setting.num = num; |
| 722 | } |
| 723 | "console" => { |
| 724 | serial_setting.console = v.parse::<bool>().map_err(|e| { |
| 725 | argument::Error::Syntax(format!( |
| 726 | "serial device console is not parseable: {}", |
| 727 | e |
| 728 | )) |
| 729 | })? |
| 730 | } |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 731 | "earlycon" => { |
| 732 | serial_setting.earlycon = v.parse::<bool>().map_err(|e| { |
| 733 | argument::Error::Syntax(format!( |
| 734 | "serial device earlycon is not parseable: {}", |
| 735 | e, |
| 736 | )) |
| 737 | })? |
| 738 | } |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 739 | "stdin" => { |
| 740 | serial_setting.stdin = v.parse::<bool>().map_err(|e| { |
| 741 | argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e)) |
Iliyan Malchev | 2c1417b | 2020-04-14 09:40:41 -0700 | [diff] [blame] | 742 | })?; |
| 743 | if serial_setting.stdin && serial_setting.input.is_some() { |
| 744 | return Err(argument::Error::TooManyArguments( |
| 745 | "Cannot specify both stdin and input options".to_string(), |
| 746 | )); |
| 747 | } |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 748 | } |
Jorge E. Moreira | 9c9e0e7 | 2019-05-17 13:57:04 -0700 | [diff] [blame] | 749 | "path" => serial_setting.path = Some(PathBuf::from(v)), |
Iliyan Malchev | 2c1417b | 2020-04-14 09:40:41 -0700 | [diff] [blame] | 750 | "input" => { |
| 751 | if serial_setting.stdin { |
| 752 | return Err(argument::Error::TooManyArguments( |
| 753 | "Cannot specify both stdin and input options".to_string(), |
| 754 | )); |
| 755 | } |
| 756 | serial_setting.input = Some(PathBuf::from(v)); |
| 757 | } |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 758 | _ => { |
| 759 | return Err(argument::Error::UnknownArgument(format!( |
| 760 | "serial parameter {}", |
| 761 | k |
| 762 | ))); |
| 763 | } |
| 764 | } |
| 765 | } |
| 766 | |
A. Cody Schuffelen | 3faab5a | 2020-10-05 17:29:16 -0700 | [diff] [blame] | 767 | if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 { |
| 768 | return Err(argument::Error::InvalidValue { |
| 769 | value: serial_setting.num.to_string(), |
| 770 | expected: String::from("Serial port num must be 4 or less"), |
| 771 | }); |
| 772 | } |
| 773 | |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 774 | Ok(serial_setting) |
| 775 | } |
| 776 | |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 777 | fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> { |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 778 | let components: Vec<&str> = value.split(':').collect(); |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 779 | if components.is_empty() || components.len() > 3 || components[0].is_empty() { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 780 | return Err(argument::Error::InvalidValue { |
| 781 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 782 | expected: String::from( |
| 783 | "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]", |
| 784 | ), |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 785 | }); |
| 786 | } |
| 787 | |
| 788 | let src = PathBuf::from(components[0]); |
| 789 | if src.is_relative() { |
| 790 | return Err(argument::Error::InvalidValue { |
| 791 | value: components[0].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 792 | expected: String::from("the source path for `plugin-mount` must be absolute"), |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 793 | }); |
| 794 | } |
| 795 | if !src.exists() { |
| 796 | return Err(argument::Error::InvalidValue { |
| 797 | value: components[0].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 798 | expected: String::from("the source path for `plugin-mount` does not exist"), |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 799 | }); |
| 800 | } |
| 801 | |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 802 | let dst = PathBuf::from(match components.get(1) { |
| 803 | None | Some(&"") => components[0], |
| 804 | Some(path) => path, |
| 805 | }); |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 806 | if dst.is_relative() { |
| 807 | return Err(argument::Error::InvalidValue { |
| 808 | value: components[1].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 809 | expected: String::from("the destination path for `plugin-mount` must be absolute"), |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 810 | }); |
| 811 | } |
| 812 | |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 813 | let writable: bool = match components.get(2) { |
| 814 | None => false, |
| 815 | Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 816 | value: components[2].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 817 | expected: String::from("the <writable> component for `plugin-mount` is not valid bool"), |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 818 | })?, |
| 819 | }; |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 820 | |
| 821 | Ok(BindMount { src, dst, writable }) |
| 822 | } |
| 823 | |
| 824 | fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> { |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 825 | let components: Vec<&str> = value.split(':').collect(); |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 826 | if components.is_empty() || components.len() > 3 || components[0].is_empty() { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 827 | return Err(argument::Error::InvalidValue { |
| 828 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 829 | expected: String::from( |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 830 | "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]", |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 831 | ), |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 832 | }); |
| 833 | } |
| 834 | |
| 835 | let inner: libc::gid_t = components[0] |
| 836 | .parse() |
| 837 | .map_err(|_| argument::Error::InvalidValue { |
| 838 | value: components[0].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 839 | expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"), |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 840 | })?; |
| 841 | |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 842 | let outer: libc::gid_t = match components.get(1) { |
| 843 | None | Some(&"") => inner, |
| 844 | Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 845 | value: components[1].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 846 | expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"), |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 847 | })?, |
| 848 | }; |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 849 | |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 850 | let count: u32 = match components.get(2) { |
| 851 | None => 1, |
| 852 | Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 853 | value: components[2].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 854 | expected: String::from( |
| 855 | "the <count> component for `plugin-gid-map` is not valid number", |
| 856 | ), |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 857 | })?, |
| 858 | }; |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 859 | |
| 860 | Ok(GidMap { |
| 861 | inner, |
| 862 | outer, |
| 863 | count, |
| 864 | }) |
| 865 | } |
| 866 | |
Chuanxiao Dong | fd5626c | 2020-04-27 16:35:33 +0800 | [diff] [blame] | 867 | fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> { |
| 868 | let mut battery_type: BatteryType = Default::default(); |
| 869 | |
| 870 | if let Some(s) = s { |
| 871 | let opts = s |
Andrew Walbran | 9cfdbd9 | 2021-01-11 17:40:34 +0000 | [diff] [blame] | 872 | .split(',') |
| 873 | .map(|frag| frag.split('=')) |
Chuanxiao Dong | fd5626c | 2020-04-27 16:35:33 +0800 | [diff] [blame] | 874 | .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); |
| 875 | |
| 876 | for (k, v) in opts { |
| 877 | match k { |
| 878 | "type" => match v.parse::<BatteryType>() { |
| 879 | Ok(type_) => battery_type = type_, |
| 880 | Err(e) => { |
| 881 | return Err(argument::Error::InvalidValue { |
| 882 | value: v.to_string(), |
| 883 | expected: e.to_string(), |
| 884 | }); |
| 885 | } |
| 886 | }, |
| 887 | "" => {} |
| 888 | _ => { |
| 889 | return Err(argument::Error::UnknownArgument(format!( |
| 890 | "battery parameter {}", |
| 891 | k |
| 892 | ))); |
| 893 | } |
| 894 | } |
| 895 | } |
| 896 | } |
| 897 | |
| 898 | Ok(battery_type) |
| 899 | } |
| 900 | |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 901 | #[cfg(feature = "direct")] |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 902 | fn parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, std::num::ParseIntError> { |
| 903 | // Parse string starting with 0x as hex and others as numbers. |
| 904 | let without_prefix = maybe_hex_string.strip_prefix("0x"); |
| 905 | match without_prefix { |
| 906 | Some(hex_string) => u64::from_str_radix(hex_string, 16), |
| 907 | None => u64::from_str(maybe_hex_string), |
| 908 | } |
| 909 | } |
| 910 | |
| 911 | #[cfg(feature = "direct")] |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 912 | fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> { |
| 913 | let s = s.ok_or(argument::Error::ExpectedValue(String::from( |
| 914 | "expected path@range[,range] value", |
| 915 | )))?; |
| 916 | let parts: Vec<&str> = s.splitn(2, '@').collect(); |
| 917 | if parts.len() != 2 { |
| 918 | return Err(argument::Error::InvalidValue { |
| 919 | value: s.to_string(), |
| 920 | expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"), |
| 921 | }); |
| 922 | } |
| 923 | let path = PathBuf::from(parts[0]); |
| 924 | if !path.exists() { |
| 925 | return Err(argument::Error::InvalidValue { |
| 926 | value: parts[0].to_owned(), |
| 927 | expected: String::from("the path does not exist"), |
| 928 | }); |
| 929 | }; |
Junichi Uekawa | b180f9c | 2021-12-07 09:21:36 +0900 | [diff] [blame] | 930 | let ranges: argument::Result<Vec<BusRange>> = parts[1] |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 931 | .split(',') |
| 932 | .map(|frag| frag.split('-')) |
| 933 | .map(|mut range| { |
| 934 | let base = range |
| 935 | .next() |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 936 | .map(|v| parse_hex_or_decimal(v)) |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 937 | .map_or(Ok(None), |r| r.map(Some)); |
| 938 | let last = range |
| 939 | .next() |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 940 | .map(|v| parse_hex_or_decimal(v)) |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 941 | .map_or(Ok(None), |r| r.map(Some)); |
| 942 | (base, last) |
| 943 | }) |
| 944 | .map(|range| match range { |
Junichi Uekawa | b180f9c | 2021-12-07 09:21:36 +0900 | [diff] [blame] | 945 | (Ok(Some(base)), Ok(None)) => Ok(BusRange { base, len: 1 }), |
| 946 | (Ok(Some(base)), Ok(Some(last))) => Ok(BusRange { |
| 947 | base, |
| 948 | len: last.saturating_sub(base).saturating_add(1), |
| 949 | }), |
Junichi Uekawa | 4d31205 | 2021-12-08 16:59:38 +0900 | [diff] [blame] | 950 | (Err(_), _) => Err(argument::Error::InvalidValue { |
| 951 | value: s.to_owned(), |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 952 | expected: String::from("invalid base range value"), |
| 953 | }), |
Junichi Uekawa | 4d31205 | 2021-12-08 16:59:38 +0900 | [diff] [blame] | 954 | (_, Err(_)) => Err(argument::Error::InvalidValue { |
| 955 | value: s.to_owned(), |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 956 | expected: String::from("invalid last range value"), |
| 957 | }), |
| 958 | _ => Err(argument::Error::InvalidValue { |
| 959 | value: s.to_owned(), |
| 960 | expected: String::from("invalid range format"), |
| 961 | }), |
| 962 | }) |
| 963 | .collect(); |
| 964 | Ok(DirectIoOption { |
| 965 | path, |
| 966 | ranges: ranges?, |
| 967 | }) |
| 968 | } |
| 969 | |
Mattias Nissler | de2c640 | 2021-10-21 12:05:29 +0000 | [diff] [blame] | 970 | fn parse_stub_pci_parameters(s: Option<&str>) -> argument::Result<StubPciParameters> { |
| 971 | let s = s.ok_or(argument::Error::ExpectedValue(String::from( |
| 972 | "stub-pci-device configuration expected", |
| 973 | )))?; |
| 974 | |
| 975 | let mut options = argument::parse_key_value_options("stub-pci-device", s, ','); |
| 976 | let addr = options |
| 977 | .next() |
| 978 | .ok_or(argument::Error::ExpectedValue(String::from( |
| 979 | "stub-pci-device: expected device address", |
| 980 | )))? |
| 981 | .key(); |
| 982 | let mut params = StubPciParameters { |
| 983 | address: PciAddress::from_string(addr), |
| 984 | vendor_id: 0, |
| 985 | device_id: 0, |
| 986 | class: PciClassCode::Other, |
| 987 | subclass: 0, |
| 988 | programming_interface: 0, |
| 989 | multifunction: false, |
| 990 | subsystem_device_id: 0, |
| 991 | subsystem_vendor_id: 0, |
| 992 | revision_id: 0, |
| 993 | }; |
| 994 | for opt in options { |
| 995 | match opt.key() { |
| 996 | "vendor" => params.vendor_id = opt.parse_numeric::<u16>()?, |
| 997 | "device" => params.device_id = opt.parse_numeric::<u16>()?, |
| 998 | "class" => { |
| 999 | let class = opt.parse_numeric::<u32>()?; |
| 1000 | params.class = PciClassCode::try_from((class >> 16) as u8) |
| 1001 | .map_err(|_| opt.invalid_value_err(String::from("Unknown class code")))?; |
| 1002 | params.subclass = (class >> 8) as u8; |
| 1003 | params.programming_interface = class as u8; |
| 1004 | } |
| 1005 | "multifunction" => params.multifunction = opt.parse_or::<bool>(true)?, |
| 1006 | "subsystem_vendor" => params.subsystem_vendor_id = opt.parse_numeric::<u16>()?, |
| 1007 | "subsystem_device" => params.subsystem_device_id = opt.parse_numeric::<u16>()?, |
| 1008 | "revision" => params.revision_id = opt.parse_numeric::<u8>()?, |
| 1009 | _ => return Err(opt.invalid_key_err()), |
| 1010 | } |
| 1011 | } |
| 1012 | |
| 1013 | Ok(params) |
| 1014 | } |
| 1015 | |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1016 | fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> { |
| 1017 | match name { |
| 1018 | "" => { |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 1019 | if cfg.executable_path.is_some() { |
| 1020 | return Err(argument::Error::TooManyArguments(format!( |
| 1021 | "A VM executable was already specified: {:?}", |
| 1022 | cfg.executable_path |
| 1023 | ))); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1024 | } |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 1025 | let kernel_path = PathBuf::from(value.unwrap()); |
| 1026 | if !kernel_path.exists() { |
| 1027 | return Err(argument::Error::InvalidValue { |
| 1028 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1029 | expected: String::from("this kernel path does not exist"), |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 1030 | }); |
| 1031 | } |
| 1032 | cfg.executable_path = Some(Executable::Kernel(kernel_path)); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1033 | } |
Christian Blichmann | 33d5677 | 2021-03-04 19:03:54 +0100 | [diff] [blame] | 1034 | "kvm-device" => { |
| 1035 | let kvm_device_path = PathBuf::from(value.unwrap()); |
| 1036 | if !kvm_device_path.exists() { |
| 1037 | return Err(argument::Error::InvalidValue { |
| 1038 | value: value.unwrap().to_owned(), |
| 1039 | expected: String::from("this kvm device path does not exist"), |
| 1040 | }); |
| 1041 | } |
| 1042 | |
| 1043 | cfg.kvm_device_path = kvm_device_path; |
| 1044 | } |
Christian Blichmann | 50f9591 | 2021-11-05 16:59:39 +0100 | [diff] [blame] | 1045 | "vhost-vsock-fd" => { |
| 1046 | if cfg.vhost_vsock_device.is_some() { |
| 1047 | return Err(argument::Error::InvalidValue { |
| 1048 | value: value.unwrap().to_owned(), |
| 1049 | expected: String::from("A vhost-vsock device was already specified"), |
| 1050 | }); |
| 1051 | } |
| 1052 | cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Fd( |
| 1053 | value |
| 1054 | .unwrap() |
| 1055 | .parse() |
| 1056 | .map_err(|_| argument::Error::InvalidValue { |
| 1057 | value: value.unwrap().to_owned(), |
| 1058 | expected: String::from( |
| 1059 | "this value for `vhost-vsock-fd` needs to be integer", |
| 1060 | ), |
| 1061 | })?, |
| 1062 | )); |
| 1063 | } |
Christian Blichmann | 2f5d4b6 | 2021-03-10 18:08:08 +0100 | [diff] [blame] | 1064 | "vhost-vsock-device" => { |
Christian Blichmann | 50f9591 | 2021-11-05 16:59:39 +0100 | [diff] [blame] | 1065 | if cfg.vhost_vsock_device.is_some() { |
| 1066 | return Err(argument::Error::InvalidValue { |
| 1067 | value: value.unwrap().to_owned(), |
| 1068 | expected: String::from("A vhost-vsock device was already specified"), |
| 1069 | }); |
| 1070 | } |
Christian Blichmann | 2f5d4b6 | 2021-03-10 18:08:08 +0100 | [diff] [blame] | 1071 | let vhost_vsock_device_path = PathBuf::from(value.unwrap()); |
| 1072 | if !vhost_vsock_device_path.exists() { |
| 1073 | return Err(argument::Error::InvalidValue { |
| 1074 | value: value.unwrap().to_owned(), |
| 1075 | expected: String::from("this vhost-vsock device path does not exist"), |
| 1076 | }); |
| 1077 | } |
| 1078 | |
Christian Blichmann | 50f9591 | 2021-11-05 16:59:39 +0100 | [diff] [blame] | 1079 | cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Path(vhost_vsock_device_path)); |
Christian Blichmann | 2f5d4b6 | 2021-03-10 18:08:08 +0100 | [diff] [blame] | 1080 | } |
| 1081 | "vhost-net-device" => { |
| 1082 | let vhost_net_device_path = PathBuf::from(value.unwrap()); |
| 1083 | if !vhost_net_device_path.exists() { |
| 1084 | return Err(argument::Error::InvalidValue { |
| 1085 | value: value.unwrap().to_owned(), |
| 1086 | expected: String::from("this vhost-vsock device path does not exist"), |
| 1087 | }); |
| 1088 | } |
| 1089 | |
| 1090 | cfg.vhost_net_device_path = vhost_net_device_path; |
| 1091 | } |
Tristan Muntsinger | 4133b01 | 2018-12-21 16:01:56 -0800 | [diff] [blame] | 1092 | "android-fstab" => { |
| 1093 | if cfg.android_fstab.is_some() |
| 1094 | && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty() |
| 1095 | { |
| 1096 | return Err(argument::Error::TooManyArguments( |
| 1097 | "expected exactly one android fstab path".to_owned(), |
| 1098 | )); |
| 1099 | } else { |
| 1100 | let android_fstab = PathBuf::from(value.unwrap()); |
| 1101 | if !android_fstab.exists() { |
| 1102 | return Err(argument::Error::InvalidValue { |
| 1103 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1104 | expected: String::from("this android fstab path does not exist"), |
Tristan Muntsinger | 4133b01 | 2018-12-21 16:01:56 -0800 | [diff] [blame] | 1105 | }); |
| 1106 | } |
| 1107 | cfg.android_fstab = Some(android_fstab); |
| 1108 | } |
| 1109 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1110 | "params" => { |
Zach Reizner | bb67871 | 2018-01-30 18:13:04 -0800 | [diff] [blame] | 1111 | cfg.params.push(value.unwrap().to_owned()); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1112 | } |
| 1113 | "cpus" => { |
| 1114 | if cfg.vcpu_count.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1115 | return Err(argument::Error::TooManyArguments( |
| 1116 | "`cpus` already given".to_owned(), |
| 1117 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1118 | } |
| 1119 | cfg.vcpu_count = |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1120 | Some( |
| 1121 | value |
| 1122 | .unwrap() |
| 1123 | .parse() |
| 1124 | .map_err(|_| argument::Error::InvalidValue { |
| 1125 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1126 | expected: String::from("this value for `cpus` needs to be integer"), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1127 | })?, |
| 1128 | ) |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1129 | } |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 1130 | "cpu-affinity" => { |
Daniel Verkamp | c677fb4 | 2020-09-08 13:47:49 -0700 | [diff] [blame] | 1131 | if cfg.vcpu_affinity.is_some() { |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 1132 | return Err(argument::Error::TooManyArguments( |
| 1133 | "`cpu-affinity` already given".to_owned(), |
| 1134 | )); |
| 1135 | } |
Daniel Verkamp | c677fb4 | 2020-09-08 13:47:49 -0700 | [diff] [blame] | 1136 | cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?); |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 1137 | } |
Daniel Verkamp | 8a72afc | 2021-03-15 17:55:52 -0700 | [diff] [blame] | 1138 | "cpu-cluster" => { |
| 1139 | cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?); |
| 1140 | } |
| 1141 | "cpu-capacity" => { |
| 1142 | parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?; |
| 1143 | } |
Yusuke Sato | 31e136a | 2021-08-18 11:51:38 -0700 | [diff] [blame] | 1144 | "per-vm-core-scheduling" => { |
| 1145 | cfg.per_vm_core_scheduling = true; |
| 1146 | } |
Woody Chow | 737ff12 | 2021-03-22 17:49:57 +0900 | [diff] [blame] | 1147 | #[cfg(feature = "audio_cras")] |
| 1148 | "cras-snd" => { |
Chih-Yang Hsia | 41dc04f | 2021-12-08 16:04:23 +0800 | [diff] [blame] | 1149 | cfg.cras_snds.push( |
Woody Chow | 0b2b606 | 2021-09-03 15:40:02 +0900 | [diff] [blame] | 1150 | value |
| 1151 | .unwrap() |
| 1152 | .parse() |
| 1153 | .map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?, |
| 1154 | ); |
Woody Chow | 737ff12 | 2021-03-22 17:49:57 +0900 | [diff] [blame] | 1155 | } |
Suleiman Souhlal | 015c3c1 | 2020-10-07 14:15:41 +0900 | [diff] [blame] | 1156 | "no-smt" => { |
| 1157 | cfg.no_smt = true; |
| 1158 | } |
Kansho Nishida | ab205af | 2020-08-13 18:17:50 +0900 | [diff] [blame] | 1159 | "rt-cpus" => { |
| 1160 | if !cfg.rt_cpus.is_empty() { |
| 1161 | return Err(argument::Error::TooManyArguments( |
| 1162 | "`rt-cpus` already given".to_owned(), |
| 1163 | )); |
| 1164 | } |
| 1165 | cfg.rt_cpus = parse_cpu_set(value.unwrap())?; |
| 1166 | } |
Suleiman Souhlal | 63630e8 | 2021-02-18 11:53:11 +0900 | [diff] [blame] | 1167 | "delay-rt" => { |
| 1168 | cfg.delay_rt = true; |
| 1169 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1170 | "mem" => { |
| 1171 | if cfg.memory.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1172 | return Err(argument::Error::TooManyArguments( |
| 1173 | "`mem` already given".to_owned(), |
| 1174 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1175 | } |
| 1176 | cfg.memory = |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1177 | Some( |
| 1178 | value |
| 1179 | .unwrap() |
| 1180 | .parse() |
| 1181 | .map_err(|_| argument::Error::InvalidValue { |
| 1182 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1183 | expected: String::from("this value for `mem` needs to be integer"), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1184 | })?, |
| 1185 | ) |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1186 | } |
Will Deacon | c48e783 | 2021-07-30 19:03:06 +0100 | [diff] [blame] | 1187 | #[cfg(target_arch = "aarch64")] |
| 1188 | "swiotlb" => { |
| 1189 | if cfg.swiotlb.is_some() { |
| 1190 | return Err(argument::Error::TooManyArguments( |
| 1191 | "`swiotlb` already given".to_owned(), |
| 1192 | )); |
| 1193 | } |
| 1194 | cfg.swiotlb = |
| 1195 | Some( |
| 1196 | value |
| 1197 | .unwrap() |
| 1198 | .parse() |
| 1199 | .map_err(|_| argument::Error::InvalidValue { |
| 1200 | value: value.unwrap().to_owned(), |
| 1201 | expected: String::from("this value for `swiotlb` needs to be integer"), |
| 1202 | })?, |
| 1203 | ) |
| 1204 | } |
Sergey Senozhatsky | 1e369c5 | 2021-04-13 20:23:51 +0900 | [diff] [blame] | 1205 | "hugepages" => { |
| 1206 | cfg.hugepages = true; |
| 1207 | } |
Andrew Scull | 1590e6f | 2020-03-18 18:00:47 +0000 | [diff] [blame] | 1208 | #[cfg(feature = "audio")] |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 1209 | "ac97" => { |
| 1210 | let ac97_params = parse_ac97_options(value.unwrap())?; |
paulhsia | 1647824 | 2020-11-27 14:04:58 +0800 | [diff] [blame] | 1211 | // Add kernel parameters related to the intel8x0 driver for ac97 devices once. |
| 1212 | if cfg.ac97_parameters.is_empty() { |
| 1213 | // Set `inside_vm=1` to save some register read ops in the driver. |
| 1214 | cfg.params.push("snd_intel8x0.inside_vm=1".to_string()); |
| 1215 | // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver. |
| 1216 | cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string()); |
| 1217 | } |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 1218 | cfg.ac97_parameters.push(ac97_params); |
Dylan Reid | 3082e8e | 2019-01-07 10:33:48 -0800 | [diff] [blame] | 1219 | } |
Jorge E. Moreira | d4562d0 | 2021-06-28 16:21:12 -0700 | [diff] [blame] | 1220 | #[cfg(feature = "audio")] |
| 1221 | "sound" => { |
| 1222 | let client_path = PathBuf::from(value.unwrap()); |
| 1223 | cfg.sound = Some(client_path); |
| 1224 | } |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 1225 | "serial" => { |
| 1226 | let serial_params = parse_serial_options(value.unwrap())?; |
| 1227 | let num = serial_params.num; |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 1228 | let key = (serial_params.hardware, num); |
| 1229 | if cfg.serial_parameters.contains_key(&key) { |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 1230 | return Err(argument::Error::TooManyArguments(format!( |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 1231 | "serial hardware {} num {}", |
| 1232 | serial_params.hardware, num, |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 1233 | ))); |
| 1234 | } |
| 1235 | |
| 1236 | if serial_params.console { |
Jakub Staron | b6515a9 | 2019-06-05 15:18:25 -0700 | [diff] [blame] | 1237 | for params in cfg.serial_parameters.values() { |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 1238 | if params.console { |
| 1239 | return Err(argument::Error::TooManyArguments(format!( |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 1240 | "{} device {} already set as console", |
| 1241 | params.hardware, params.num, |
| 1242 | ))); |
| 1243 | } |
| 1244 | } |
| 1245 | } |
| 1246 | |
| 1247 | if serial_params.earlycon { |
| 1248 | // Only SerialHardware::Serial supports earlycon= currently. |
| 1249 | match serial_params.hardware { |
| 1250 | SerialHardware::Serial => {} |
| 1251 | _ => { |
| 1252 | return Err(argument::Error::InvalidValue { |
Andrew Walbran | 9cfdbd9 | 2021-01-11 17:40:34 +0000 | [diff] [blame] | 1253 | value: serial_params.hardware.to_string(), |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 1254 | expected: String::from("earlycon not supported for hardware"), |
| 1255 | }); |
| 1256 | } |
| 1257 | } |
| 1258 | for params in cfg.serial_parameters.values() { |
| 1259 | if params.earlycon { |
| 1260 | return Err(argument::Error::TooManyArguments(format!( |
| 1261 | "{} device {} already set as earlycon", |
| 1262 | params.hardware, params.num, |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 1263 | ))); |
| 1264 | } |
| 1265 | } |
| 1266 | } |
| 1267 | |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 1268 | if serial_params.stdin { |
| 1269 | if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) { |
| 1270 | return Err(argument::Error::TooManyArguments(format!( |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 1271 | "{} device {} already connected to standard input", |
| 1272 | previous_stdin.hardware, previous_stdin.num, |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 1273 | ))); |
| 1274 | } |
| 1275 | } |
| 1276 | |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 1277 | cfg.serial_parameters.insert(key, serial_params); |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 1278 | } |
| 1279 | "syslog-tag" => { |
| 1280 | if cfg.syslog_tag.is_some() { |
| 1281 | return Err(argument::Error::TooManyArguments( |
| 1282 | "`syslog-tag` already given".to_owned(), |
| 1283 | )); |
| 1284 | } |
| 1285 | syslog::set_proc_name(value.unwrap()); |
| 1286 | cfg.syslog_tag = Some(value.unwrap().to_owned()); |
| 1287 | } |
Daniel Verkamp | 4b62cd9 | 2019-11-08 13:09:27 -0800 | [diff] [blame] | 1288 | "root" | "rwroot" | "disk" | "rwdisk" => { |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1289 | let param = value.unwrap(); |
| 1290 | let mut components = param.split(','); |
Daniel Verkamp | 6a8cd10 | 2019-06-26 15:17:46 -0700 | [diff] [blame] | 1291 | let read_only = !name.starts_with("rw"); |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1292 | let disk_path = |
| 1293 | PathBuf::from( |
| 1294 | components |
| 1295 | .next() |
| 1296 | .ok_or_else(|| argument::Error::InvalidValue { |
| 1297 | value: param.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1298 | expected: String::from("missing disk path"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1299 | })?, |
| 1300 | ); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1301 | if !disk_path.exists() { |
| 1302 | return Err(argument::Error::InvalidValue { |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1303 | value: param.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1304 | expected: String::from("this disk path does not exist"), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1305 | }); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1306 | } |
Daniel Verkamp | 6a8cd10 | 2019-06-26 15:17:46 -0700 | [diff] [blame] | 1307 | if name.ends_with("root") { |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1308 | if cfg.disks.len() >= 26 { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1309 | return Err(argument::Error::TooManyArguments( |
| 1310 | "ran out of letters for to assign to root disk".to_owned(), |
| 1311 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1312 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1313 | cfg.params.push(format!( |
Daniel Verkamp | 6a8cd10 | 2019-06-26 15:17:46 -0700 | [diff] [blame] | 1314 | "root=/dev/vd{} {}", |
| 1315 | char::from(b'a' + cfg.disks.len() as u8), |
| 1316 | if read_only { "ro" } else { "rw" } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1317 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1318 | } |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1319 | |
| 1320 | let mut disk = DiskOption { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1321 | path: disk_path, |
Daniel Verkamp | 6a8cd10 | 2019-06-26 15:17:46 -0700 | [diff] [blame] | 1322 | read_only, |
Junichi Uekawa | 7bea39f | 2021-07-16 14:05:06 +0900 | [diff] [blame] | 1323 | o_direct: false, |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1324 | sparse: true, |
Daniel Verkamp | 2767223 | 2019-12-06 17:26:55 +1100 | [diff] [blame] | 1325 | block_size: 512, |
Daniel Verkamp | 4e1f99a | 2020-06-01 12:47:21 -0700 | [diff] [blame] | 1326 | id: None, |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1327 | }; |
| 1328 | |
| 1329 | for opt in components { |
| 1330 | let mut o = opt.splitn(2, '='); |
| 1331 | let kind = o.next().ok_or_else(|| argument::Error::InvalidValue { |
| 1332 | value: opt.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1333 | expected: String::from("disk options must not be empty"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1334 | })?; |
| 1335 | let value = o.next().ok_or_else(|| argument::Error::InvalidValue { |
| 1336 | value: opt.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1337 | expected: String::from("disk options must be of the form `kind=value`"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1338 | })?; |
| 1339 | |
| 1340 | match kind { |
| 1341 | "sparse" => { |
| 1342 | let sparse = value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1343 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1344 | expected: String::from("`sparse` must be a boolean"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1345 | })?; |
| 1346 | disk.sparse = sparse; |
| 1347 | } |
Junichi Uekawa | 7bea39f | 2021-07-16 14:05:06 +0900 | [diff] [blame] | 1348 | "o_direct" => { |
| 1349 | let o_direct = |
| 1350 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1351 | value: value.to_owned(), |
| 1352 | expected: String::from("`o_direct` must be a boolean"), |
| 1353 | })?; |
| 1354 | disk.o_direct = o_direct; |
| 1355 | } |
Daniel Verkamp | 2767223 | 2019-12-06 17:26:55 +1100 | [diff] [blame] | 1356 | "block_size" => { |
| 1357 | let block_size = |
| 1358 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1359 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1360 | expected: String::from("`block_size` must be an integer"), |
Daniel Verkamp | 2767223 | 2019-12-06 17:26:55 +1100 | [diff] [blame] | 1361 | })?; |
| 1362 | disk.block_size = block_size; |
| 1363 | } |
Daniel Verkamp | 4e1f99a | 2020-06-01 12:47:21 -0700 | [diff] [blame] | 1364 | "id" => { |
| 1365 | if value.len() > DISK_ID_LEN { |
| 1366 | return Err(argument::Error::InvalidValue { |
| 1367 | value: value.to_owned(), |
| 1368 | expected: format!( |
| 1369 | "`id` must be {} or fewer characters", |
| 1370 | DISK_ID_LEN |
| 1371 | ), |
| 1372 | }); |
| 1373 | } |
| 1374 | let mut id = [0u8; DISK_ID_LEN]; |
| 1375 | // Slicing id to value's length will never panic |
| 1376 | // because we checked that value will fit into id above. |
| 1377 | id[..value.len()].copy_from_slice(value.as_bytes()); |
| 1378 | disk.id = Some(id); |
| 1379 | } |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1380 | _ => { |
| 1381 | return Err(argument::Error::InvalidValue { |
| 1382 | value: kind.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1383 | expected: String::from("unrecognized disk option"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1384 | }); |
| 1385 | } |
| 1386 | } |
| 1387 | } |
| 1388 | |
| 1389 | cfg.disks.push(disk); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1390 | } |
Jakub Staron | a3411ea | 2019-04-24 10:55:25 -0700 | [diff] [blame] | 1391 | "pmem-device" | "rw-pmem-device" => { |
| 1392 | let disk_path = PathBuf::from(value.unwrap()); |
| 1393 | if !disk_path.exists() { |
| 1394 | return Err(argument::Error::InvalidValue { |
| 1395 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1396 | expected: String::from("this disk path does not exist"), |
Jakub Staron | a3411ea | 2019-04-24 10:55:25 -0700 | [diff] [blame] | 1397 | }); |
| 1398 | } |
| 1399 | |
| 1400 | cfg.pmem_devices.push(DiskOption { |
| 1401 | path: disk_path, |
| 1402 | read_only: !name.starts_with("rw"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 1403 | sparse: false, |
Junichi Uekawa | 7bea39f | 2021-07-16 14:05:06 +0900 | [diff] [blame] | 1404 | o_direct: false, |
Michael Hoyle | 6b19695 | 2020-08-02 20:09:41 -0700 | [diff] [blame] | 1405 | block_size: base::pagesize() as u32, |
Daniel Verkamp | 4e1f99a | 2020-06-01 12:47:21 -0700 | [diff] [blame] | 1406 | id: None, |
Jakub Staron | a3411ea | 2019-04-24 10:55:25 -0700 | [diff] [blame] | 1407 | }); |
| 1408 | } |
Kansho Nishida | 282115b | 2019-12-18 13:13:14 +0900 | [diff] [blame] | 1409 | "pstore" => { |
| 1410 | if cfg.pstore.is_some() { |
| 1411 | return Err(argument::Error::TooManyArguments( |
| 1412 | "`pstore` already given".to_owned(), |
| 1413 | )); |
| 1414 | } |
| 1415 | |
| 1416 | let value = value.unwrap(); |
| 1417 | let components: Vec<&str> = value.split(',').collect(); |
| 1418 | if components.len() != 2 { |
| 1419 | return Err(argument::Error::InvalidValue { |
| 1420 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1421 | expected: String::from( |
| 1422 | "pstore must have exactly 2 components: path=<path>,size=<size>", |
| 1423 | ), |
Kansho Nishida | 282115b | 2019-12-18 13:13:14 +0900 | [diff] [blame] | 1424 | }); |
| 1425 | } |
| 1426 | cfg.pstore = Some(Pstore { |
| 1427 | path: { |
| 1428 | if components[0].len() <= 5 || !components[0].starts_with("path=") { |
| 1429 | return Err(argument::Error::InvalidValue { |
| 1430 | value: components[0].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1431 | expected: String::from("pstore path must follow with `path=`"), |
Kansho Nishida | 282115b | 2019-12-18 13:13:14 +0900 | [diff] [blame] | 1432 | }); |
| 1433 | }; |
| 1434 | PathBuf::from(&components[0][5..]) |
| 1435 | }, |
| 1436 | size: { |
| 1437 | if components[1].len() <= 5 || !components[1].starts_with("size=") { |
| 1438 | return Err(argument::Error::InvalidValue { |
| 1439 | value: components[1].to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1440 | expected: String::from("pstore size must follow with `size=`"), |
Kansho Nishida | 282115b | 2019-12-18 13:13:14 +0900 | [diff] [blame] | 1441 | }); |
| 1442 | }; |
| 1443 | components[1][5..] |
| 1444 | .parse() |
| 1445 | .map_err(|_| argument::Error::InvalidValue { |
| 1446 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1447 | expected: String::from("pstore size must be an integer"), |
Kansho Nishida | 282115b | 2019-12-18 13:13:14 +0900 | [diff] [blame] | 1448 | })? |
| 1449 | }, |
| 1450 | }); |
| 1451 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1452 | "host_ip" => { |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1453 | if cfg.host_ip.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1454 | return Err(argument::Error::TooManyArguments( |
| 1455 | "`host_ip` already given".to_owned(), |
| 1456 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1457 | } |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1458 | cfg.host_ip = |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1459 | Some( |
| 1460 | value |
| 1461 | .unwrap() |
| 1462 | .parse() |
| 1463 | .map_err(|_| argument::Error::InvalidValue { |
| 1464 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1465 | expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1466 | })?, |
| 1467 | ) |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1468 | } |
| 1469 | "netmask" => { |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1470 | if cfg.netmask.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1471 | return Err(argument::Error::TooManyArguments( |
| 1472 | "`netmask` already given".to_owned(), |
| 1473 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1474 | } |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1475 | cfg.netmask = |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1476 | Some( |
| 1477 | value |
| 1478 | .unwrap() |
| 1479 | .parse() |
| 1480 | .map_err(|_| argument::Error::InvalidValue { |
| 1481 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1482 | expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1483 | })?, |
| 1484 | ) |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1485 | } |
| 1486 | "mac" => { |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1487 | if cfg.mac_address.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1488 | return Err(argument::Error::TooManyArguments( |
| 1489 | "`mac` already given".to_owned(), |
| 1490 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1491 | } |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1492 | cfg.mac_address = |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1493 | Some( |
| 1494 | value |
| 1495 | .unwrap() |
| 1496 | .parse() |
| 1497 | .map_err(|_| argument::Error::InvalidValue { |
| 1498 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1499 | expected: String::from( |
| 1500 | "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"", |
| 1501 | ), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1502 | })?, |
| 1503 | ) |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1504 | } |
Xiong Zhang | 773c707 | 2020-03-20 10:39:55 +0800 | [diff] [blame] | 1505 | "net-vq-pairs" => { |
| 1506 | if cfg.net_vq_pairs.is_some() { |
| 1507 | return Err(argument::Error::TooManyArguments( |
| 1508 | "`net-vq-pairs` already given".to_owned(), |
| 1509 | )); |
| 1510 | } |
| 1511 | cfg.net_vq_pairs = |
| 1512 | Some( |
| 1513 | value |
| 1514 | .unwrap() |
| 1515 | .parse() |
| 1516 | .map_err(|_| argument::Error::InvalidValue { |
| 1517 | value: value.unwrap().to_owned(), |
| 1518 | expected: String::from( |
| 1519 | "this value for `net-vq-pairs` needs to be integer", |
| 1520 | ), |
| 1521 | })?, |
| 1522 | ) |
| 1523 | } |
| 1524 | |
Stephen Barber | 28a5a61 | 2017-10-20 17:15:30 -0700 | [diff] [blame] | 1525 | "wayland-sock" => { |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 1526 | let mut components = value.unwrap().split(','); |
| 1527 | let path = |
| 1528 | PathBuf::from( |
| 1529 | components |
| 1530 | .next() |
| 1531 | .ok_or_else(|| argument::Error::InvalidValue { |
| 1532 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1533 | expected: String::from("missing socket path"), |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 1534 | })?, |
| 1535 | ); |
| 1536 | let mut name = ""; |
| 1537 | for c in components { |
| 1538 | let mut kv = c.splitn(2, '='); |
| 1539 | let (kind, value) = match (kv.next(), kv.next()) { |
| 1540 | (Some(kind), Some(value)) => (kind, value), |
| 1541 | _ => { |
| 1542 | return Err(argument::Error::InvalidValue { |
| 1543 | value: c.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1544 | expected: String::from("option must be of the form `kind=value`"), |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 1545 | }) |
| 1546 | } |
| 1547 | }; |
| 1548 | match kind { |
| 1549 | "name" => name = value, |
| 1550 | _ => { |
| 1551 | return Err(argument::Error::InvalidValue { |
| 1552 | value: kind.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1553 | expected: String::from("unrecognized option"), |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 1554 | }) |
| 1555 | } |
| 1556 | } |
Stephen Barber | 28a5a61 | 2017-10-20 17:15:30 -0700 | [diff] [blame] | 1557 | } |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 1558 | if cfg.wayland_socket_paths.contains_key(name) { |
| 1559 | return Err(argument::Error::TooManyArguments(format!( |
| 1560 | "wayland socket name already used: '{}'", |
| 1561 | name |
| 1562 | ))); |
Stephen Barber | 28a5a61 | 2017-10-20 17:15:30 -0700 | [diff] [blame] | 1563 | } |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 1564 | cfg.wayland_socket_paths.insert(name.to_string(), path); |
Stephen Barber | 28a5a61 | 2017-10-20 17:15:30 -0700 | [diff] [blame] | 1565 | } |
David Reveman | 52ba4e5 | 2018-04-22 21:42:09 -0400 | [diff] [blame] | 1566 | #[cfg(feature = "wl-dmabuf")] |
Nicholas Verne | fde2997 | 2021-07-06 22:02:05 +1000 | [diff] [blame] | 1567 | "wayland-dmabuf" => {} |
Zach Reizner | 0f2cfb0 | 2019-06-19 17:46:03 -0700 | [diff] [blame] | 1568 | "x-display" => { |
| 1569 | if cfg.x_display.is_some() { |
| 1570 | return Err(argument::Error::TooManyArguments( |
| 1571 | "`x-display` already given".to_owned(), |
| 1572 | )); |
| 1573 | } |
| 1574 | cfg.x_display = Some(value.unwrap().to_owned()); |
| 1575 | } |
Zach Reizner | 65b98f1 | 2019-11-22 17:34:58 -0800 | [diff] [blame] | 1576 | "display-window-keyboard" => { |
| 1577 | cfg.display_window_keyboard = true; |
| 1578 | } |
| 1579 | "display-window-mouse" => { |
| 1580 | cfg.display_window_mouse = true; |
| 1581 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1582 | "socket" => { |
| 1583 | if cfg.socket_path.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1584 | return Err(argument::Error::TooManyArguments( |
| 1585 | "`socket` already given".to_owned(), |
| 1586 | )); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1587 | } |
| 1588 | let mut socket_path = PathBuf::from(value.unwrap()); |
| 1589 | if socket_path.is_dir() { |
| 1590 | socket_path.push(format!("crosvm-{}.sock", getpid())); |
| 1591 | } |
| 1592 | if socket_path.exists() { |
| 1593 | return Err(argument::Error::InvalidValue { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1594 | value: socket_path.to_string_lossy().into_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1595 | expected: String::from("this socket path already exists"), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1596 | }); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 1597 | } |
| 1598 | cfg.socket_path = Some(socket_path); |
| 1599 | } |
Dylan Reid | d0c9adc | 2017-10-02 19:04:50 -0700 | [diff] [blame] | 1600 | "disable-sandbox" => { |
Lepton Wu | 9105e9f | 2019-03-14 11:38:31 -0700 | [diff] [blame] | 1601 | cfg.sandbox = false; |
Dylan Reid | d0c9adc | 2017-10-02 19:04:50 -0700 | [diff] [blame] | 1602 | } |
Chirantan Ekbote | 88f9cba | 2017-08-28 09:51:18 -0700 | [diff] [blame] | 1603 | "cid" => { |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1604 | if cfg.cid.is_some() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1605 | return Err(argument::Error::TooManyArguments( |
| 1606 | "`cid` alread given".to_owned(), |
| 1607 | )); |
Chirantan Ekbote | 88f9cba | 2017-08-28 09:51:18 -0700 | [diff] [blame] | 1608 | } |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1609 | cfg.cid = Some( |
| 1610 | value |
| 1611 | .unwrap() |
| 1612 | .parse() |
| 1613 | .map_err(|_| argument::Error::InvalidValue { |
| 1614 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1615 | expected: String::from("this value for `cid` must be an unsigned integer"), |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1616 | })?, |
| 1617 | ); |
Chirantan Ekbote | 88f9cba | 2017-08-28 09:51:18 -0700 | [diff] [blame] | 1618 | } |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 1619 | "shared-dir" => { |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1620 | // This is formatted as multiple fields, each separated by ":". The first 2 fields are |
| 1621 | // fixed (src:tag). The rest may appear in any order: |
| 1622 | // |
| 1623 | // * type=TYPE - must be one of "p9" or "fs" (default: p9) |
| 1624 | // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]" |
| 1625 | // (default: "0 <current euid> 1") |
| 1626 | // * gidmap=GIDMAP - a gid map in the same format as uidmap |
| 1627 | // (default: "0 <current egid> 1") |
Ryo Hashimoto | 6d92438 | 2021-06-21 20:59:04 +0900 | [diff] [blame] | 1628 | // * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When |
| 1629 | // performing quota-related operations, these UIDs are treated as if they have |
| 1630 | // CAP_FOWNER. |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1631 | // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes |
| 1632 | // and directory contents should be considered valid (default: 5) |
| 1633 | // * cache=CACHE - one of "never", "always", or "auto" (default: auto) |
| 1634 | // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false) |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 1635 | let param = value.unwrap(); |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1636 | let mut components = param.split(':'); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1637 | let src = |
| 1638 | PathBuf::from( |
| 1639 | components |
| 1640 | .next() |
| 1641 | .ok_or_else(|| argument::Error::InvalidValue { |
| 1642 | value: param.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1643 | expected: String::from("missing source path for `shared-dir`"), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1644 | })?, |
| 1645 | ); |
| 1646 | let tag = components |
| 1647 | .next() |
| 1648 | .ok_or_else(|| argument::Error::InvalidValue { |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 1649 | value: param.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1650 | expected: String::from("missing tag for `shared-dir`"), |
David Tolnay | 2bac1e7 | 2018-12-12 14:33:42 -0800 | [diff] [blame] | 1651 | })? |
| 1652 | .to_owned(); |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 1653 | |
| 1654 | if !src.is_dir() { |
| 1655 | return Err(argument::Error::InvalidValue { |
| 1656 | value: param.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1657 | expected: String::from("source path for `shared-dir` must be a directory"), |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 1658 | }); |
| 1659 | } |
| 1660 | |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1661 | let mut shared_dir = SharedDir { |
| 1662 | src, |
| 1663 | tag, |
| 1664 | ..Default::default() |
| 1665 | }; |
| 1666 | for opt in components { |
| 1667 | let mut o = opt.splitn(2, '='); |
| 1668 | let kind = o.next().ok_or_else(|| argument::Error::InvalidValue { |
| 1669 | value: opt.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1670 | expected: String::from("`shared-dir` options must not be empty"), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1671 | })?; |
| 1672 | let value = o.next().ok_or_else(|| argument::Error::InvalidValue { |
| 1673 | value: opt.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1674 | expected: String::from("`shared-dir` options must be of the form `kind=value`"), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1675 | })?; |
| 1676 | |
| 1677 | match kind { |
| 1678 | "type" => { |
| 1679 | shared_dir.kind = |
| 1680 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1681 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1682 | expected: String::from("`type` must be one of `fs` or `9p`"), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1683 | })? |
| 1684 | } |
| 1685 | "uidmap" => shared_dir.uid_map = value.into(), |
| 1686 | "gidmap" => shared_dir.gid_map = value.into(), |
Ryo Hashimoto | 6d92438 | 2021-06-21 20:59:04 +0900 | [diff] [blame] | 1687 | #[cfg(feature = "chromeos")] |
| 1688 | "privileged_quota_uids" => { |
| 1689 | shared_dir.fs_cfg.privileged_quota_uids = |
| 1690 | value.split(' ').map(|s| s.parse().unwrap()).collect(); |
| 1691 | } |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1692 | "timeout" => { |
| 1693 | let seconds = value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1694 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1695 | expected: String::from("`timeout` must be an integer"), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1696 | })?; |
| 1697 | |
| 1698 | let dur = Duration::from_secs(seconds); |
Andrew Walbran | 9cfdbd9 | 2021-01-11 17:40:34 +0000 | [diff] [blame] | 1699 | shared_dir.fs_cfg.entry_timeout = dur; |
Chirantan Ekbote | 75ba875 | 2020-10-27 18:33:02 +0900 | [diff] [blame] | 1700 | shared_dir.fs_cfg.attr_timeout = dur; |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1701 | } |
| 1702 | "cache" => { |
| 1703 | let policy = value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1704 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1705 | expected: String::from( |
| 1706 | "`cache` must be one of `never`, `always`, or `auto`", |
| 1707 | ), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1708 | })?; |
Chirantan Ekbote | 75ba875 | 2020-10-27 18:33:02 +0900 | [diff] [blame] | 1709 | shared_dir.fs_cfg.cache_policy = policy; |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1710 | } |
| 1711 | "writeback" => { |
| 1712 | let writeback = |
| 1713 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1714 | value: value.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1715 | expected: String::from("`writeback` must be a boolean"), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1716 | })?; |
Chirantan Ekbote | 75ba875 | 2020-10-27 18:33:02 +0900 | [diff] [blame] | 1717 | shared_dir.fs_cfg.writeback = writeback; |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1718 | } |
Chirantan Ekbote | d994e51 | 2020-06-12 18:46:52 +0900 | [diff] [blame] | 1719 | "rewrite-security-xattrs" => { |
| 1720 | let rewrite_security_xattrs = |
| 1721 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1722 | value: value.to_owned(), |
| 1723 | expected: String::from( |
| 1724 | "`rewrite-security-xattrs` must be a boolean", |
| 1725 | ), |
| 1726 | })?; |
Chirantan Ekbote | 75ba875 | 2020-10-27 18:33:02 +0900 | [diff] [blame] | 1727 | shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs; |
Chirantan Ekbote | d994e51 | 2020-06-12 18:46:52 +0900 | [diff] [blame] | 1728 | } |
Chirantan Ekbote | 09357c8 | 2020-09-10 20:08:28 +0900 | [diff] [blame] | 1729 | "ascii_casefold" => { |
| 1730 | let ascii_casefold = |
| 1731 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1732 | value: value.to_owned(), |
| 1733 | expected: String::from("`ascii_casefold` must be a boolean"), |
| 1734 | })?; |
Chirantan Ekbote | 75ba875 | 2020-10-27 18:33:02 +0900 | [diff] [blame] | 1735 | shared_dir.fs_cfg.ascii_casefold = ascii_casefold; |
| 1736 | shared_dir.p9_cfg.ascii_casefold = ascii_casefold; |
Chirantan Ekbote | 09357c8 | 2020-09-10 20:08:28 +0900 | [diff] [blame] | 1737 | } |
Chirantan Ekbote | 1b2d8dc | 2021-09-21 17:25:29 +0900 | [diff] [blame] | 1738 | "dax" => { |
| 1739 | let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1740 | value: value.to_owned(), |
| 1741 | expected: String::from("`dax` must be a boolean"), |
| 1742 | })?; |
| 1743 | shared_dir.fs_cfg.use_dax = use_dax; |
| 1744 | } |
Chirantan Ekbote | f1cd8d7 | 2021-09-21 17:33:58 +0900 | [diff] [blame] | 1745 | "posix_acl" => { |
| 1746 | let posix_acl = |
| 1747 | value.parse().map_err(|_| argument::Error::InvalidValue { |
| 1748 | value: value.to_owned(), |
| 1749 | expected: String::from("`posix_acl` must be a boolean"), |
| 1750 | })?; |
| 1751 | shared_dir.fs_cfg.posix_acl = posix_acl; |
| 1752 | } |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1753 | _ => { |
| 1754 | return Err(argument::Error::InvalidValue { |
| 1755 | value: kind.to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1756 | expected: String::from("unrecognized option for `shared-dir`"), |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 1757 | }) |
| 1758 | } |
| 1759 | } |
| 1760 | } |
| 1761 | cfg.shared_dirs.push(shared_dir); |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 1762 | } |
Dylan Reid | e026ef0 | 2017-10-02 19:03:52 -0700 | [diff] [blame] | 1763 | "seccomp-policy-dir" => { |
Dylan Reid | d0c9adc | 2017-10-02 19:04:50 -0700 | [diff] [blame] | 1764 | // `value` is Some because we are in this match so it's safe to unwrap. |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1765 | cfg.seccomp_policy_dir = PathBuf::from(value.unwrap()); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1766 | } |
Zach Reizner | 4486379 | 2019-06-26 14:22:08 -0700 | [diff] [blame] | 1767 | "seccomp-log-failures" => { |
Matt Delco | 45caf91 | 2019-11-13 08:11:09 -0800 | [diff] [blame] | 1768 | // A side-effect of this flag is to force the use of .policy files |
| 1769 | // instead of .bpf files (.bpf files are expected and assumed to be |
| 1770 | // compiled to fail an unpermitted action with "trap"). |
| 1771 | // Normally crosvm will first attempt to use a .bpf file, and if |
| 1772 | // not present it will then try to use a .policy file. It's up |
| 1773 | // to the build to decide which of these files is present for |
| 1774 | // crosvm to use (for CrOS the build will use .bpf files for |
| 1775 | // x64 builds and .policy files for arm/arm64 builds). |
| 1776 | // |
| 1777 | // This flag will likely work as expected for builds that use |
| 1778 | // .policy files. For builds that only use .bpf files the initial |
| 1779 | // result when using this flag is likely to be a file-not-found |
| 1780 | // error (since the .policy files are not present). |
| 1781 | // For .bpf builds you can either 1) manually add the .policy files, |
| 1782 | // or 2) do not use this command-line parameter and instead |
| 1783 | // temporarily change the build by passing "log" rather than |
| 1784 | // "trap" as the "--default-action" to compile_seccomp_policy.py. |
Zach Reizner | 4486379 | 2019-06-26 14:22:08 -0700 | [diff] [blame] | 1785 | cfg.seccomp_log_failures = true; |
| 1786 | } |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 1787 | "plugin" => { |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 1788 | if cfg.executable_path.is_some() { |
| 1789 | return Err(argument::Error::TooManyArguments(format!( |
| 1790 | "A VM executable was already specified: {:?}", |
| 1791 | cfg.executable_path |
| 1792 | ))); |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 1793 | } |
Zach Reizner | cc30d58 | 2018-01-23 21:16:42 -0800 | [diff] [blame] | 1794 | let plugin = PathBuf::from(value.unwrap().to_owned()); |
| 1795 | if plugin.is_relative() { |
| 1796 | return Err(argument::Error::InvalidValue { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1797 | value: plugin.to_string_lossy().into_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1798 | expected: String::from("the plugin path must be an absolute path"), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1799 | }); |
Zach Reizner | cc30d58 | 2018-01-23 21:16:42 -0800 | [diff] [blame] | 1800 | } |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 1801 | cfg.executable_path = Some(Executable::Plugin(plugin)); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1802 | } |
Dmitry Torokhov | 0f1770d | 2018-05-11 10:57:16 -0700 | [diff] [blame] | 1803 | "plugin-root" => { |
| 1804 | cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned())); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 1805 | } |
Chirantan Ekbote | d41d726 | 2018-11-16 16:37:45 -0800 | [diff] [blame] | 1806 | "plugin-mount" => { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1807 | let mount = parse_plugin_mount_option(value.unwrap())?; |
| 1808 | cfg.plugin_mounts.push(mount); |
Chirantan Ekbote | d41d726 | 2018-11-16 16:37:45 -0800 | [diff] [blame] | 1809 | } |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1810 | "plugin-mount-file" => { |
| 1811 | let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue { |
| 1812 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1813 | expected: String::from("unable to open `plugin-mount-file` file"), |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1814 | })?; |
| 1815 | let reader = BufReader::new(file); |
| 1816 | for l in reader.lines() { |
| 1817 | let line = l.unwrap(); |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 1818 | let trimmed_line = line.splitn(2, '#').next().unwrap().trim(); |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1819 | if !trimmed_line.is_empty() { |
| 1820 | let mount = parse_plugin_mount_option(trimmed_line)?; |
| 1821 | cfg.plugin_mounts.push(mount); |
| 1822 | } |
| 1823 | } |
| 1824 | } |
Dmitry Torokhov | 1a6262b | 2019-03-01 00:34:03 -0800 | [diff] [blame] | 1825 | "plugin-gid-map" => { |
Dmitry Torokhov | c689fd9 | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1826 | let map = parse_plugin_gid_map_option(value.unwrap())?; |
| 1827 | cfg.plugin_gid_maps.push(map); |
Dmitry Torokhov | 1a6262b | 2019-03-01 00:34:03 -0800 | [diff] [blame] | 1828 | } |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1829 | "plugin-gid-map-file" => { |
| 1830 | let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue { |
| 1831 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1832 | expected: String::from("unable to open `plugin-gid-map-file` file"), |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1833 | })?; |
| 1834 | let reader = BufReader::new(file); |
| 1835 | for l in reader.lines() { |
| 1836 | let line = l.unwrap(); |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 1837 | let trimmed_line = line.splitn(2, '#').next().unwrap().trim(); |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 1838 | if !trimmed_line.is_empty() { |
| 1839 | let map = parse_plugin_gid_map_option(trimmed_line)?; |
| 1840 | cfg.plugin_gid_maps.push(map); |
| 1841 | } |
| 1842 | } |
| 1843 | } |
Daniel Verkamp | aac2813 | 2018-10-15 14:58:48 -0700 | [diff] [blame] | 1844 | "vhost-net" => cfg.vhost_net = true, |
Chirantan Ekbote | 5f78721 | 2018-05-31 15:31:31 -0700 | [diff] [blame] | 1845 | "tap-fd" => { |
Jorge E. Moreira | b795280 | 2019-02-12 16:43:05 -0800 | [diff] [blame] | 1846 | cfg.tap_fd.push( |
| 1847 | value |
| 1848 | .unwrap() |
| 1849 | .parse() |
| 1850 | .map_err(|_| argument::Error::InvalidValue { |
| 1851 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1852 | expected: String::from( |
| 1853 | "this value for `tap-fd` must be an unsigned integer", |
| 1854 | ), |
Jorge E. Moreira | b795280 | 2019-02-12 16:43:05 -0800 | [diff] [blame] | 1855 | })?, |
| 1856 | ); |
Chirantan Ekbote | 5f78721 | 2018-05-31 15:31:31 -0700 | [diff] [blame] | 1857 | } |
Alexandre Courbot | 993aa7f | 2021-12-09 14:51:29 +0900 | [diff] [blame] | 1858 | "tap-name" => { |
| 1859 | cfg.tap_name.push(value.unwrap().to_owned()); |
| 1860 | } |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 1861 | #[cfg(feature = "gpu")] |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 1862 | "gpu" => { |
Chia-I Wu | 16fb659 | 2021-11-10 11:45:32 -0800 | [diff] [blame] | 1863 | let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default); |
| 1864 | parse_gpu_options(value, gpu_parameters)?; |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 1865 | } |
| 1866 | #[cfg(feature = "gpu")] |
| 1867 | "gpu-display" => { |
Chia-I Wu | 16fb659 | 2021-11-10 11:45:32 -0800 | [diff] [blame] | 1868 | let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default); |
| 1869 | parse_gpu_display_options(value, gpu_parameters)?; |
| 1870 | } |
| 1871 | #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))] |
| 1872 | "gpu-render-server" => { |
| 1873 | let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default); |
| 1874 | parse_gpu_render_server_options(value, gpu_parameters)?; |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 1875 | } |
David Tolnay | 43f8e21 | 2019-02-13 17:28:16 -0800 | [diff] [blame] | 1876 | "software-tpm" => { |
| 1877 | cfg.software_tpm = true; |
| 1878 | } |
Jorge E. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 1879 | "single-touch" => { |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 1880 | let mut it = value.unwrap().split(':'); |
Jorge E. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 1881 | |
| 1882 | let mut single_touch_spec = |
| 1883 | TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned())); |
| 1884 | if let Some(width) = it.next() { |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 1885 | single_touch_spec.set_width(width.trim().parse().unwrap()); |
Jorge E. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 1886 | } |
| 1887 | if let Some(height) = it.next() { |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 1888 | single_touch_spec.set_height(height.trim().parse().unwrap()); |
Jorge E. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 1889 | } |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 1890 | cfg.virtio_single_touch.push(single_touch_spec); |
Jorge E. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 1891 | } |
Tristan Muntsinger | 486cffc | 2020-09-29 22:05:41 +0000 | [diff] [blame] | 1892 | "multi-touch" => { |
Tristan Muntsinger | 486cffc | 2020-09-29 22:05:41 +0000 | [diff] [blame] | 1893 | let mut it = value.unwrap().split(':'); |
| 1894 | |
| 1895 | let mut multi_touch_spec = |
| 1896 | TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned())); |
| 1897 | if let Some(width) = it.next() { |
| 1898 | multi_touch_spec.set_width(width.trim().parse().unwrap()); |
| 1899 | } |
| 1900 | if let Some(height) = it.next() { |
| 1901 | multi_touch_spec.set_height(height.trim().parse().unwrap()); |
| 1902 | } |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 1903 | cfg.virtio_multi_touch.push(multi_touch_spec); |
Tristan Muntsinger | 486cffc | 2020-09-29 22:05:41 +0000 | [diff] [blame] | 1904 | } |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1905 | "trackpad" => { |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 1906 | let mut it = value.unwrap().split(':'); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1907 | |
| 1908 | let mut trackpad_spec = |
Jorge E. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 1909 | TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned())); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1910 | if let Some(width) = it.next() { |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 1911 | trackpad_spec.set_width(width.trim().parse().unwrap()); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1912 | } |
| 1913 | if let Some(height) = it.next() { |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 1914 | trackpad_spec.set_height(height.trim().parse().unwrap()); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1915 | } |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 1916 | cfg.virtio_trackpad.push(trackpad_spec); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1917 | } |
| 1918 | "mouse" => { |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 1919 | cfg.virtio_mice |
| 1920 | .push(PathBuf::from(value.unwrap().to_owned())); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1921 | } |
| 1922 | "keyboard" => { |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 1923 | cfg.virtio_keyboard |
| 1924 | .push(PathBuf::from(value.unwrap().to_owned())); |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1925 | } |
Daniel Norman | 5e23df7 | 2021-03-11 10:11:02 -0800 | [diff] [blame] | 1926 | "switches" => { |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 1927 | cfg.virtio_switches |
| 1928 | .push(PathBuf::from(value.unwrap().to_owned())); |
Daniel Norman | 5e23df7 | 2021-03-11 10:11:02 -0800 | [diff] [blame] | 1929 | } |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1930 | "evdev" => { |
| 1931 | let dev_path = PathBuf::from(value.unwrap()); |
| 1932 | if !dev_path.exists() { |
| 1933 | return Err(argument::Error::InvalidValue { |
| 1934 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 1935 | expected: String::from("this input device path does not exist"), |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 1936 | }); |
| 1937 | } |
| 1938 | cfg.virtio_input_evdevs.push(dev_path); |
| 1939 | } |
Miriam Zimmerman | 26ac928 | 2019-01-29 21:21:48 -0800 | [diff] [blame] | 1940 | "split-irqchip" => { |
| 1941 | cfg.split_irqchip = true; |
| 1942 | } |
Daniel Verkamp | e403f5c | 2018-12-11 16:29:26 -0800 | [diff] [blame] | 1943 | "initrd" => { |
| 1944 | cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned())); |
| 1945 | } |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 1946 | "bios" => { |
| 1947 | if cfg.executable_path.is_some() { |
| 1948 | return Err(argument::Error::TooManyArguments(format!( |
| 1949 | "A VM executable was already specified: {:?}", |
| 1950 | cfg.executable_path |
| 1951 | ))); |
| 1952 | } |
| 1953 | cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned()))); |
| 1954 | } |
Tomasz Nowicki | 344eb14 | 2021-09-22 05:51:58 +0000 | [diff] [blame] | 1955 | "vfio" | "vfio-platform" => { |
Tomasz Nowicki | 71aca79 | 2021-06-09 18:53:49 +0000 | [diff] [blame] | 1956 | let vfio_type = name.parse().unwrap(); |
| 1957 | let vfio_dev = VfioCommand::new(vfio_type, value.unwrap())?; |
| 1958 | cfg.vfio.push(vfio_dev); |
Xiong Zhang | 17b0daf | 2019-04-23 17:14:50 +0800 | [diff] [blame] | 1959 | } |
Alexandre Courbot | 8230abf | 2021-06-26 22:49:26 +0900 | [diff] [blame] | 1960 | #[cfg(feature = "video-decoder")] |
Keiichi Watanabe | 57df6a0 | 2019-12-06 22:24:40 +0900 | [diff] [blame] | 1961 | "video-decoder" => { |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 1962 | cfg.video_dec = Some(parse_video_options(value)?); |
Keiichi Watanabe | 57df6a0 | 2019-12-06 22:24:40 +0900 | [diff] [blame] | 1963 | } |
Alexandre Courbot | 8230abf | 2021-06-26 22:49:26 +0900 | [diff] [blame] | 1964 | #[cfg(feature = "video-encoder")] |
Keiichi Watanabe | 57df6a0 | 2019-12-06 22:24:40 +0900 | [diff] [blame] | 1965 | "video-encoder" => { |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 1966 | cfg.video_enc = Some(parse_video_options(value)?); |
Keiichi Watanabe | 57df6a0 | 2019-12-06 22:24:40 +0900 | [diff] [blame] | 1967 | } |
Tomasz Jeznach | 4264464 | 2020-05-20 23:27:59 -0700 | [diff] [blame] | 1968 | "acpi-table" => { |
| 1969 | let acpi_table = PathBuf::from(value.unwrap()); |
| 1970 | if !acpi_table.exists() { |
| 1971 | return Err(argument::Error::InvalidValue { |
| 1972 | value: value.unwrap().to_owned(), |
| 1973 | expected: String::from("the acpi-table path does not exist"), |
| 1974 | }); |
| 1975 | } |
| 1976 | if !acpi_table.is_file() { |
| 1977 | return Err(argument::Error::InvalidValue { |
| 1978 | value: value.unwrap().to_owned(), |
| 1979 | expected: String::from("the acpi-table path should be a file"), |
| 1980 | }); |
| 1981 | } |
| 1982 | |
| 1983 | cfg.acpi_tables.push(acpi_table); |
| 1984 | } |
Will Deacon | 6560c18 | 2020-10-06 18:47:18 +0100 | [diff] [blame] | 1985 | "protected-vm" => { |
Andrew Walbran | 413f854 | 2021-01-08 13:29:03 +0000 | [diff] [blame] | 1986 | cfg.protected_vm = ProtectionType::Protected; |
Will Deacon | 6560c18 | 2020-10-06 18:47:18 +0100 | [diff] [blame] | 1987 | } |
Andrew Walbran | 0bbbb68 | 2021-12-13 13:42:07 +0000 | [diff] [blame^] | 1988 | "protected-vm-without-firmware" => { |
| 1989 | cfg.protected_vm = ProtectionType::ProtectedWithoutFirmware; |
| 1990 | } |
Chuanxiao Dong | fd5626c | 2020-04-27 16:35:33 +0800 | [diff] [blame] | 1991 | "battery" => { |
| 1992 | let params = parse_battery_options(value)?; |
| 1993 | cfg.battery_type = Some(params); |
| 1994 | } |
Keiichi Watanabe | c5262e9 | 2020-10-21 15:57:33 +0900 | [diff] [blame] | 1995 | #[cfg(all(target_arch = "x86_64", feature = "gdb"))] |
| 1996 | "gdb" => { |
| 1997 | let port = value |
| 1998 | .unwrap() |
| 1999 | .parse() |
| 2000 | .map_err(|_| argument::Error::InvalidValue { |
| 2001 | value: value.unwrap().to_owned(), |
| 2002 | expected: String::from("expected a valid port number"), |
| 2003 | })?; |
| 2004 | cfg.gdb = Some(port); |
| 2005 | } |
Charles William Dick | 0e3d4b6 | 2020-12-14 12:16:46 +0900 | [diff] [blame] | 2006 | "balloon_bias_mib" => { |
| 2007 | cfg.balloon_bias = |
| 2008 | value |
| 2009 | .unwrap() |
| 2010 | .parse::<i64>() |
| 2011 | .map_err(|_| argument::Error::InvalidValue { |
| 2012 | value: value.unwrap().to_owned(), |
| 2013 | expected: String::from("expected a valid ballon bias in MiB"), |
| 2014 | })? |
| 2015 | * 1024 |
| 2016 | * 1024; // cfg.balloon_bias is in bytes. |
| 2017 | } |
Keiichi Watanabe | f3a37f4 | 2021-01-21 15:41:11 +0900 | [diff] [blame] | 2018 | "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption { |
| 2019 | socket: PathBuf::from(value.unwrap()), |
| 2020 | }), |
Federico 'Morg' Pareschi | 70fc7de | 2021-04-08 15:43:13 +0900 | [diff] [blame] | 2021 | "vhost-user-console" => cfg.vhost_user_console.push(VhostUserOption { |
| 2022 | socket: PathBuf::from(value.unwrap()), |
| 2023 | }), |
Chirantan Ekbote | 44292f5 | 2021-06-25 18:31:41 +0900 | [diff] [blame] | 2024 | "vhost-user-gpu" => cfg.vhost_user_gpu.push(VhostUserOption { |
| 2025 | socket: PathBuf::from(value.unwrap()), |
| 2026 | }), |
JaeMan Park | eb9cc53 | 2021-07-02 15:02:59 +0900 | [diff] [blame] | 2027 | "vhost-user-mac80211-hwsim" => { |
| 2028 | cfg.vhost_user_mac80211_hwsim = Some(VhostUserOption { |
| 2029 | socket: PathBuf::from(value.unwrap()), |
| 2030 | }); |
| 2031 | } |
Keiichi Watanabe | 6068658 | 2021-03-12 04:53:51 +0900 | [diff] [blame] | 2032 | "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption { |
| 2033 | socket: PathBuf::from(value.unwrap()), |
| 2034 | }), |
Woody Chow | 1b16db1 | 2021-04-02 16:59:59 +0900 | [diff] [blame] | 2035 | #[cfg(feature = "audio")] |
| 2036 | "vhost-user-snd" => cfg.vhost_user_snd.push(VhostUserOption { |
| 2037 | socket: PathBuf::from(value.unwrap()), |
| 2038 | }), |
Chirantan Ekbote | 84091e5 | 2021-09-10 18:43:17 +0900 | [diff] [blame] | 2039 | "vhost-user-vsock" => cfg.vhost_user_vsock.push(VhostUserOption { |
| 2040 | socket: PathBuf::from(value.unwrap()), |
| 2041 | }), |
Chirantan Ekbote | 2ee9dcd | 2021-05-26 18:21:44 +0900 | [diff] [blame] | 2042 | "vhost-user-wl" => { |
| 2043 | let mut components = value.unwrap().splitn(2, ":"); |
| 2044 | let socket = components.next().map(PathBuf::from).ok_or_else(|| { |
| 2045 | argument::Error::InvalidValue { |
| 2046 | value: value.unwrap().to_owned(), |
| 2047 | expected: String::from("missing socket path"), |
| 2048 | } |
| 2049 | })?; |
| 2050 | let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| { |
| 2051 | argument::Error::InvalidValue { |
| 2052 | value: value.unwrap().to_owned(), |
| 2053 | expected: String::from("missing vm tube path"), |
| 2054 | } |
| 2055 | })?; |
| 2056 | cfg.vhost_user_wl |
| 2057 | .push(VhostUserWlOption { socket, vm_tube }); |
| 2058 | } |
Woody Chow | 5890b70 | 2021-02-12 14:57:02 +0900 | [diff] [blame] | 2059 | "vhost-user-fs" => { |
| 2060 | // (socket:tag) |
| 2061 | let param = value.unwrap(); |
| 2062 | let mut components = param.split(':'); |
| 2063 | let socket = |
| 2064 | PathBuf::from( |
| 2065 | components |
| 2066 | .next() |
| 2067 | .ok_or_else(|| argument::Error::InvalidValue { |
| 2068 | value: param.to_owned(), |
| 2069 | expected: String::from("missing socket path for `vhost-user-fs`"), |
| 2070 | })?, |
| 2071 | ); |
| 2072 | let tag = components |
| 2073 | .next() |
| 2074 | .ok_or_else(|| argument::Error::InvalidValue { |
| 2075 | value: param.to_owned(), |
| 2076 | expected: String::from("missing tag for `vhost-user-fs`"), |
| 2077 | })? |
| 2078 | .to_owned(); |
| 2079 | cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag }); |
| 2080 | } |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 2081 | #[cfg(feature = "direct")] |
| 2082 | "direct-pmio" => { |
| 2083 | if cfg.direct_pmio.is_some() { |
| 2084 | return Err(argument::Error::TooManyArguments( |
| 2085 | "`direct_pmio` already given".to_owned(), |
| 2086 | )); |
| 2087 | } |
| 2088 | cfg.direct_pmio = Some(parse_direct_io_options(value)?); |
| 2089 | } |
Tomasz Jeznach | 7271f75 | 2021-03-04 01:44:06 -0800 | [diff] [blame] | 2090 | #[cfg(feature = "direct")] |
Tomasz Jeznach | 9e6c633 | 2021-05-27 21:49:14 -0700 | [diff] [blame] | 2091 | "direct-mmio" => { |
| 2092 | if cfg.direct_mmio.is_some() { |
| 2093 | return Err(argument::Error::TooManyArguments( |
| 2094 | "`direct_mmio` already given".to_owned(), |
| 2095 | )); |
| 2096 | } |
| 2097 | cfg.direct_mmio = Some(parse_direct_io_options(value)?); |
| 2098 | } |
| 2099 | #[cfg(feature = "direct")] |
Tomasz Jeznach | 7271f75 | 2021-03-04 01:44:06 -0800 | [diff] [blame] | 2100 | "direct-level-irq" => { |
| 2101 | cfg.direct_level_irq |
| 2102 | .push( |
| 2103 | value |
| 2104 | .unwrap() |
| 2105 | .parse() |
| 2106 | .map_err(|_| argument::Error::InvalidValue { |
| 2107 | value: value.unwrap().to_owned(), |
| 2108 | expected: String::from( |
| 2109 | "this value for `direct-level-irq` must be an unsigned integer", |
| 2110 | ), |
| 2111 | })?, |
| 2112 | ); |
| 2113 | } |
| 2114 | #[cfg(feature = "direct")] |
| 2115 | "direct-edge-irq" => { |
| 2116 | cfg.direct_edge_irq |
| 2117 | .push( |
| 2118 | value |
| 2119 | .unwrap() |
| 2120 | .parse() |
| 2121 | .map_err(|_| argument::Error::InvalidValue { |
| 2122 | value: value.unwrap().to_owned(), |
| 2123 | expected: String::from( |
| 2124 | "this value for `direct-edge-irq` must be an unsigned integer", |
| 2125 | ), |
| 2126 | })?, |
| 2127 | ); |
| 2128 | } |
Tomasz Jeznach | ccb2694 | 2021-03-30 22:44:11 -0700 | [diff] [blame] | 2129 | "dmi" => { |
| 2130 | if cfg.dmi_path.is_some() { |
| 2131 | return Err(argument::Error::TooManyArguments( |
| 2132 | "`dmi` already given".to_owned(), |
| 2133 | )); |
| 2134 | } |
| 2135 | let dmi_path = PathBuf::from(value.unwrap()); |
| 2136 | if !dmi_path.exists() { |
| 2137 | return Err(argument::Error::InvalidValue { |
| 2138 | value: value.unwrap().to_owned(), |
| 2139 | expected: String::from("the dmi path does not exist"), |
| 2140 | }); |
| 2141 | } |
| 2142 | if !dmi_path.is_dir() { |
| 2143 | return Err(argument::Error::InvalidValue { |
| 2144 | value: value.unwrap().to_owned(), |
| 2145 | expected: String::from("the dmi path should be directory"), |
| 2146 | }); |
| 2147 | } |
| 2148 | cfg.dmi_path = Some(dmi_path); |
| 2149 | } |
Tomasz Jeznach | d93c29f | 2021-04-12 11:00:24 -0700 | [diff] [blame] | 2150 | "no-legacy" => { |
| 2151 | cfg.no_legacy = true; |
| 2152 | } |
ZhaoLiu | 2aaf7ad | 2021-10-10 18:22:29 +0800 | [diff] [blame] | 2153 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 2154 | "host-cpu-topology" => { |
| 2155 | cfg.host_cpu_topology = true; |
| 2156 | } |
Mattias Nissler | de2c640 | 2021-10-21 12:05:29 +0000 | [diff] [blame] | 2157 | "stub-pci-device" => { |
| 2158 | cfg.stub_pci_devices.push(parse_stub_pci_parameters(value)?); |
| 2159 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2160 | "help" => return Err(argument::Error::PrintHelp), |
| 2161 | _ => unreachable!(), |
| 2162 | } |
| 2163 | Ok(()) |
| 2164 | } |
| 2165 | |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 2166 | fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> { |
| 2167 | if cfg.executable_path.is_none() { |
| 2168 | return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned())); |
| 2169 | } |
| 2170 | if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() { |
| 2171 | if cfg.host_ip.is_none() { |
| 2172 | return Err(argument::Error::ExpectedArgument( |
| 2173 | "`host_ip` missing from network config".to_owned(), |
| 2174 | )); |
| 2175 | } |
| 2176 | if cfg.netmask.is_none() { |
| 2177 | return Err(argument::Error::ExpectedArgument( |
| 2178 | "`netmask` missing from network config".to_owned(), |
| 2179 | )); |
| 2180 | } |
| 2181 | if cfg.mac_address.is_none() { |
| 2182 | return Err(argument::Error::ExpectedArgument( |
| 2183 | "`mac` missing from network config".to_owned(), |
| 2184 | )); |
| 2185 | } |
| 2186 | } |
| 2187 | if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) { |
| 2188 | return Err(argument::Error::ExpectedArgument( |
| 2189 | "`plugin-root` requires `plugin`".to_owned(), |
| 2190 | )); |
| 2191 | } |
| 2192 | #[cfg(feature = "gpu")] |
| 2193 | { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 2194 | if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() { |
| 2195 | if gpu_parameters.displays.is_empty() { |
| 2196 | gpu_parameters.displays.push(GpuDisplayParameters { |
| 2197 | width: DEFAULT_DISPLAY_WIDTH, |
| 2198 | height: DEFAULT_DISPLAY_HEIGHT, |
| 2199 | }); |
| 2200 | } |
| 2201 | |
| 2202 | let width = gpu_parameters.displays[0].width; |
| 2203 | let height = gpu_parameters.displays[0].height; |
| 2204 | |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 2205 | if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() { |
Tristan Muntsinger | 486cffc | 2020-09-29 22:05:41 +0000 | [diff] [blame] | 2206 | virtio_multi_touch.set_default_size(width, height); |
| 2207 | } |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 2208 | if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() { |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 2209 | virtio_single_touch.set_default_size(width, height); |
| 2210 | } |
| 2211 | } |
| 2212 | } |
Keiichi Watanabe | c5262e9 | 2020-10-21 15:57:33 +0900 | [diff] [blame] | 2213 | #[cfg(all(target_arch = "x86_64", feature = "gdb"))] |
Dennis Kempin | c3dedf3 | 2021-11-12 14:42:28 -0800 | [diff] [blame] | 2214 | if cfg.gdb.is_some() && cfg.vcpu_count.unwrap_or(1) != 1 { |
| 2215 | return Err(argument::Error::ExpectedArgument( |
| 2216 | "`gdb` requires the number of vCPU to be 1".to_owned(), |
| 2217 | )); |
Keiichi Watanabe | c5262e9 | 2020-10-21 15:57:33 +0900 | [diff] [blame] | 2218 | } |
ZhaoLiu | 2aaf7ad | 2021-10-10 18:22:29 +0800 | [diff] [blame] | 2219 | if cfg.host_cpu_topology { |
ZhaoLiu | 1e6e7b2 | 2021-12-06 17:23:21 +0800 | [diff] [blame] | 2220 | if cfg.no_smt { |
| 2221 | return Err(argument::Error::ExpectedArgument( |
| 2222 | "`host-cpu-topology` cannot be set at the same time as `no_smt`, since \ |
| 2223 | the smt of the Guest is the same as that of the Host when \ |
| 2224 | `host-cpu-topology` is set." |
| 2225 | .to_owned(), |
| 2226 | )); |
| 2227 | } |
| 2228 | |
ZhaoLiu | 2aaf7ad | 2021-10-10 18:22:29 +0800 | [diff] [blame] | 2229 | // Safe because we pass a flag for this call and the host supports this system call |
| 2230 | let pcpu_count = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) } as usize; |
| 2231 | if cfg.vcpu_count.is_some() { |
| 2232 | if pcpu_count != cfg.vcpu_count.unwrap() { |
| 2233 | return Err(argument::Error::ExpectedArgument(format!( |
| 2234 | "`host-cpu-topology` requires the count of vCPUs({}) to equal the \ |
| 2235 | count of CPUs({}) on host.", |
| 2236 | cfg.vcpu_count.unwrap(), |
| 2237 | pcpu_count |
| 2238 | ))); |
| 2239 | } |
| 2240 | } else { |
| 2241 | cfg.vcpu_count = Some(pcpu_count); |
| 2242 | } |
| 2243 | |
| 2244 | match &cfg.vcpu_affinity { |
| 2245 | None => { |
| 2246 | let mut affinity_map = BTreeMap::new(); |
| 2247 | for cpu_id in 0..cfg.vcpu_count.unwrap() { |
| 2248 | affinity_map.insert(cpu_id, vec![cpu_id]); |
| 2249 | } |
| 2250 | cfg.vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map)); |
| 2251 | } |
| 2252 | _ => { |
| 2253 | return Err(argument::Error::ExpectedArgument( |
| 2254 | "`host-cpu-topology` requires not to set `cpu-affinity` at the same time" |
| 2255 | .to_owned(), |
| 2256 | )); |
| 2257 | } |
| 2258 | } |
| 2259 | } |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 2260 | set_default_serial_parameters(&mut cfg.serial_parameters); |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 2261 | Ok(()) |
| 2262 | } |
| 2263 | |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 2264 | enum CommandStatus { |
| 2265 | Success, |
| 2266 | VmReset, |
| 2267 | VmStop, |
| 2268 | } |
| 2269 | |
| 2270 | fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> { |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2271 | let arguments = |
| 2272 | &[Argument::positional("KERNEL", "bzImage of kernel to run"), |
Christian Blichmann | 33d5677 | 2021-03-04 19:03:54 +0100 | [diff] [blame] | 2273 | Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"), |
Christian Blichmann | 50f9591 | 2021-11-05 16:59:39 +0100 | [diff] [blame] | 2274 | Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."), |
Christian Blichmann | 2f5d4b6 | 2021-03-10 18:08:08 +0100 | [diff] [blame] | 2275 | Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"), |
| 2276 | Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"), |
Tristan Muntsinger | 4133b01 | 2018-12-21 16:01:56 -0800 | [diff] [blame] | 2277 | Argument::value("android-fstab", "PATH", "Path to Android fstab"), |
Daniel Verkamp | e403f5c | 2018-12-11 16:29:26 -0800 | [diff] [blame] | 2278 | Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2279 | Argument::short_value('p', |
| 2280 | "params", |
| 2281 | "PARAMS", |
Zach Reizner | bb67871 | 2018-01-30 18:13:04 -0800 | [diff] [blame] | 2282 | "Extra kernel or plugin command line arguments. Can be given more than once."), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2283 | Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"), |
Daniel Verkamp | c677fb4 | 2020-09-08 13:47:49 -0700 | [diff] [blame] | 2284 | Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5) |
| 2285 | or colon-separated list of assignments of guest to host CPU assignments (e.g. 0=0:1=1:2=2) (default: no mask)"), |
Daniel Verkamp | 8a72afc | 2021-03-15 17:55:52 -0700 | [diff] [blame] | 2286 | Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"), |
| 2287 | Argument::value("cpu-capacity", "CPU=CAP[,CPU=CAP[,...]]", "Set the relative capacity of the given CPU (default: no capacity)"), |
Yusuke Sato | 31e136a | 2021-08-18 11:51:38 -0700 | [diff] [blame] | 2288 | Argument::flag("per-vm-core-scheduling", "Enable per-VM core scheduling intead of the default one (per-vCPU core scheduing) by |
| 2289 | making all vCPU threads share same cookie for core scheduling. |
| 2290 | This option is no-op on devices that have neither MDS nor L1TF vulnerability."), |
Woody Chow | 737ff12 | 2021-03-22 17:49:57 +0900 | [diff] [blame] | 2291 | #[cfg(feature = "audio_cras")] |
Woody Chow | 0b2b606 | 2021-09-03 15:40:02 +0900 | [diff] [blame] | 2292 | Argument::value("cras-snd", |
Chih-Yang Hsia | 99829b3 | 2021-12-08 22:00:48 +0800 | [diff] [blame] | 2293 | "[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]", |
Woody Chow | 0b2b606 | 2021-09-03 15:40:02 +0900 | [diff] [blame] | 2294 | "Comma separated key=value pairs for setting up cras snd devices. |
| 2295 | Possible key values: |
Chih-Yang Hsia | a2b4fc0 | 2021-12-12 12:43:56 +0800 | [diff] [blame] | 2296 | capture - Enable audio capture. Default to false. |
Woody Chow | 0b2b606 | 2021-09-03 15:40:02 +0900 | [diff] [blame] | 2297 | client_type - Set specific client type for cras backend. |
Chih-Yang Hsia | 99829b3 | 2021-12-08 22:00:48 +0800 | [diff] [blame] | 2298 | num_output_streams - Set number of output PCM streams |
| 2299 | num_input_streams - Set number of input PCM streams"), |
Suleiman Souhlal | 015c3c1 | 2020-10-07 14:15:41 +0900 | [diff] [blame] | 2300 | Argument::flag("no-smt", "Don't use SMT in the guest"), |
Kansho Nishida | ab205af | 2020-08-13 18:17:50 +0900 | [diff] [blame] | 2301 | 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)"), |
Suleiman Souhlal | 63630e8 | 2021-02-18 11:53:11 +0900 | [diff] [blame] | 2302 | Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2303 | Argument::short_value('m', |
| 2304 | "mem", |
| 2305 | "N", |
| 2306 | "Amount of guest memory in MiB. (default: 256)"), |
Sergey Senozhatsky | 1e369c5 | 2021-04-13 20:23:51 +0900 | [diff] [blame] | 2307 | Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2308 | Argument::short_value('r', |
| 2309 | "root", |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 2310 | "PATH[,key=value[,key=value[,...]]", |
| 2311 | "Path to a root disk image followed by optional comma-separated options. |
| 2312 | Like `--disk` but adds appropriate kernel command line option. |
| 2313 | See --disk for valid options."), |
| 2314 | Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options. |
| 2315 | See --disk for valid options."), |
| 2316 | Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options. |
| 2317 | Valid keys: |
Daniel Verkamp | 2767223 | 2019-12-06 17:26:55 +1100 | [diff] [blame] | 2318 | sparse=BOOL - Indicates whether the disk should support the discard operation (default: true) |
Daniel Verkamp | 4e1f99a | 2020-06-01 12:47:21 -0700 | [diff] [blame] | 2319 | block_size=BYTES - Set the reported block size of the disk (default: 512) |
Junichi Uekawa | 7bea39f | 2021-07-16 14:05:06 +0900 | [diff] [blame] | 2320 | id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID) |
| 2321 | o_direct=BOOL - Use O_DIRECT mode to bypass page cache"), |
Daniel Verkamp | e73c80f | 2019-11-08 10:11:16 -0800 | [diff] [blame] | 2322 | Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options. |
| 2323 | See --disk for valid options."), |
Jakub Staron | a3411ea | 2019-04-24 10:55:25 -0700 | [diff] [blame] | 2324 | Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."), |
| 2325 | Argument::value("pmem-device", "PATH", "Path to a disk image."), |
Kansho Nishida | 282115b | 2019-12-18 13:13:14 +0900 | [diff] [blame] | 2326 | Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2327 | Argument::value("host_ip", |
| 2328 | "IP", |
| 2329 | "IP address to assign to host tap interface."), |
| 2330 | Argument::value("netmask", "NETMASK", "Netmask for VM subnet."), |
| 2331 | Argument::value("mac", "MAC", "MAC address for VM."), |
Xiong Zhang | 773c707 | 2020-03-20 10:39:55 +0800 | [diff] [blame] | 2332 | Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"), |
Andrew Scull | 1590e6f | 2020-03-18 18:00:47 +0000 | [diff] [blame] | 2333 | #[cfg(feature = "audio")] |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 2334 | Argument::value("ac97", |
paulhsia | 83d5160 | 2021-03-09 17:13:14 +0800 | [diff] [blame] | 2335 | "[backend=BACKEND,capture=true,capture_effect=EFFECT,client_type=TYPE,shm-fd=FD,client-fd=FD,server-fd=FD]", |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 2336 | "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once . |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2337 | Possible key values: |
| 2338 | backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null. |
| 2339 | `null` for /dev/null, cras for CRAS server and vios for VioS server. |
| 2340 | capture - Enable audio capture |
| 2341 | capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec. |
| 2342 | client_type - Set specific client type for cras backend. |
Woody Chow | b27dea4 | 2021-09-08 15:51:22 +0900 | [diff] [blame] | 2343 | socket_type - Set specific socket type for cras backend. |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2344 | server - The to the VIOS server (unix socket)."), |
Jorge E. Moreira | d4562d0 | 2021-06-28 16:21:12 -0700 | [diff] [blame] | 2345 | #[cfg(feature = "audio")] |
| 2346 | Argument::value("sound", "[PATH]", "Path to the VioS server socket for setting up virtio-snd devices."), |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 2347 | Argument::value("serial", |
Daniel Verkamp | a7b6a1c | 2020-03-09 13:16:46 -0700 | [diff] [blame] | 2348 | "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]", |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 2349 | "Comma separated key=value pairs for setting up serial devices. Can be given more than once. |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2350 | Possible key values: |
| 2351 | type=(stdout,syslog,sink,file) - Where to route the serial device |
| 2352 | hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial). |
| 2353 | num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1. |
| 2354 | path=PATH - The path to the file to write to when type=file |
| 2355 | input=PATH - The path to the file to read from when not stdin |
| 2356 | console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided. |
| 2357 | earlycon - Use this serial device as the early console. Can only be given once. |
| 2358 | stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided. |
| 2359 | "), |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 2360 | Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."), |
Zach Reizner | 0f2cfb0 | 2019-06-19 17:46:03 -0700 | [diff] [blame] | 2361 | Argument::value("x-display", "DISPLAY", "X11 display name to use."), |
Zach Reizner | 65b98f1 | 2019-11-22 17:34:58 -0800 | [diff] [blame] | 2362 | Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."), |
| 2363 | Argument::flag("display-window-mouse", "Capture keyboard input from the display window."), |
Ryo Hashimoto | 0b788de | 2019-12-10 17:14:13 +0900 | [diff] [blame] | 2364 | 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 Reveman | 52ba4e5 | 2018-04-22 21:42:09 -0400 | [diff] [blame] | 2365 | #[cfg(feature = "wl-dmabuf")] |
Nicholas Verne | fde2997 | 2021-07-06 22:02:05 +1000 | [diff] [blame] | 2366 | Argument::flag("wayland-dmabuf", "DEPRECATED: Enable support for DMABufs in Wayland device."), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2367 | Argument::short_value('s', |
| 2368 | "socket", |
| 2369 | "PATH", |
| 2370 | "Path to put the control socket. If PATH is a directory, a name will be generated."), |
Dylan Reid | d0c9adc | 2017-10-02 19:04:50 -0700 | [diff] [blame] | 2371 | Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."), |
Chirantan Ekbote | ebd5681 | 2018-04-16 19:32:04 -0700 | [diff] [blame] | 2372 | Argument::value("cid", "CID", "Context ID for virtual sockets."), |
Chirantan Ekbote | f1cd8d7 | 2021-09-21 17:33:58 +0900 | [diff] [blame] | 2373 | Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE:dax=BOOL,posix_acl=BOOL]", |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 2374 | "Colon-separated options for configuring a directory to be shared with the VM. |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2375 | The first field is the directory to be shared and the second field is the tag that the VM can use to identify the device. |
| 2376 | The remaining fields are key=value pairs that may appear in any order. Valid keys are: |
| 2377 | type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9). |
| 2378 | uidmap=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). |
| 2379 | gidmap=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). |
| 2380 | cache=(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. |
| 2381 | timeout=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. |
Junichi Uekawa | 670f998 | 2021-12-13 17:35:44 +0900 | [diff] [blame] | 2382 | writeback=BOOL - Enables 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. |
| 2383 | dax=BOOL - Enables DAX support. Enabling DAX can improve performance for frequently accessed files by mapping regions of the file directly into the VM's memory. There is a cost of slightly increased latency the first time the file is accessed. Since the mapping is shared directly from the host kernel's file cache, enabling DAX can improve performance even when the guest cache policy is \"Never\". The default value for this option is \"false\". |
Chirantan Ekbote | f1cd8d7 | 2021-09-21 17:33:58 +0900 | [diff] [blame] | 2384 | posix_acl=BOOL - Indicates whether the shared directory supports POSIX ACLs. This should only be enabled when the underlying file system supports POSIX ACLs. The default value for this option is \"true\". |
Chirantan Ekbote | bd4723b | 2019-07-17 10:50:30 +0900 | [diff] [blame] | 2385 | "), |
Dylan Reid | e026ef0 | 2017-10-02 19:03:52 -0700 | [diff] [blame] | 2386 | Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."), |
Zach Reizner | 4486379 | 2019-06-26 14:22:08 -0700 | [diff] [blame] | 2387 | Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."), |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 2388 | #[cfg(feature = "plugin")] |
Zach Reizner | cc30d58 | 2018-01-23 21:16:42 -0800 | [diff] [blame] | 2389 | Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."), |
Daniel Verkamp | bd1a084 | 2019-01-08 15:50:34 -0800 | [diff] [blame] | 2390 | #[cfg(feature = "plugin")] |
Dmitry Torokhov | 0f1770d | 2018-05-11 10:57:16 -0700 | [diff] [blame] | 2391 | Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."), |
Daniel Verkamp | bd1a084 | 2019-01-08 15:50:34 -0800 | [diff] [blame] | 2392 | #[cfg(feature = "plugin")] |
Chirantan Ekbote | d41d726 | 2018-11-16 16:37:45 -0800 | [diff] [blame] | 2393 | Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."), |
Dmitry Torokhov | 1a6262b | 2019-03-01 00:34:03 -0800 | [diff] [blame] | 2394 | #[cfg(feature = "plugin")] |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 2395 | 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."), |
| 2396 | #[cfg(feature = "plugin")] |
Dmitry Torokhov | 1a6262b | 2019-03-01 00:34:03 -0800 | [diff] [blame] | 2397 | Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."), |
Dmitry Torokhov | 0f4c5ff | 2019-12-11 13:36:08 -0800 | [diff] [blame] | 2398 | #[cfg(feature = "plugin")] |
| 2399 | 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 Bradford | 8f002f5 | 2018-02-19 16:31:11 +0000 | [diff] [blame] | 2400 | Argument::flag("vhost-net", "Use vhost for networking."), |
Alexandre Courbot | 993aa7f | 2021-12-09 14:51:29 +0900 | [diff] [blame] | 2401 | Argument::value("tap-name", |
| 2402 | "NAME", |
| 2403 | "Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."), |
Chirantan Ekbote | 5f78721 | 2018-05-31 15:31:31 -0700 | [diff] [blame] | 2404 | Argument::value("tap-fd", |
| 2405 | "fd", |
Jorge E. Moreira | b795280 | 2019-02-12 16:43:05 -0800 | [diff] [blame] | 2406 | "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."), |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 2407 | #[cfg(feature = "gpu")] |
Jason Macnak | cc7070b | 2019-11-06 14:48:12 -0800 | [diff] [blame] | 2408 | Argument::flag_or_value("gpu", |
| 2409 | "[width=INT,height=INT]", |
| 2410 | "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2411 | Possible key values: |
| 2412 | backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol) |
| 2413 | width=INT - The width of the virtual display connected to the virtio-gpu. |
| 2414 | height=INT - The height of the virtual display connected to the virtio-gpu. |
| 2415 | egl[=true|=false] - If the backend should use a EGL context for rendering. |
| 2416 | glx[=true|=false] - If the backend should use a GLX context for rendering. |
| 2417 | surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering. |
| 2418 | angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver. |
| 2419 | syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync |
| 2420 | vulkan[=true|=false] - If the backend should support vulkan"), |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 2421 | #[cfg(feature = "gpu")] |
| 2422 | Argument::flag_or_value("gpu-display", |
| 2423 | "[width=INT,height=INT]", |
| 2424 | "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2425 | Possible key values: |
| 2426 | width=INT - The width of the virtual display connected to the virtio-gpu. |
| 2427 | height=INT - The height of the virtual display connected to the virtio-gpu."), |
Chia-I Wu | 16fb659 | 2021-11-10 11:45:32 -0800 | [diff] [blame] | 2428 | #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))] |
| 2429 | Argument::flag_or_value("gpu-render-server", |
| 2430 | "[path=PATH]", |
| 2431 | "(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device |
| 2432 | Possible key values: |
| 2433 | path=PATH - The path to the render server executable."), |
David Tolnay | 43f8e21 | 2019-02-13 17:28:16 -0800 | [diff] [blame] | 2434 | #[cfg(feature = "tpm")] |
| 2435 | Argument::flag("software-tpm", "enable a software emulated trusted platform module device"), |
Jorge E. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 2436 | 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. Moreira | 99d3f08 | 2019-03-07 10:59:54 -0800 | [diff] [blame] | 2437 | 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)."), |
Tristan Muntsinger | 486cffc | 2020-09-29 22:05:41 +0000 | [diff] [blame] | 2438 | Argument::value("multi-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read multi 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. Moreira | dffec50 | 2019-01-14 18:44:49 -0800 | [diff] [blame] | 2439 | 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)."), |
| 2440 | Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."), |
| 2441 | Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."), |
Daniel Norman | 5e23df7 | 2021-03-11 10:11:02 -0800 | [diff] [blame] | 2442 | Argument::value("switches", "PATH", "Path to a socket from where to read switch input events and write status updates to."), |
Miriam Zimmerman | 26ac928 | 2019-01-29 21:21:48 -0800 | [diff] [blame] | 2443 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 2444 | Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"), |
Cody Schuffelen | 6d1ab50 | 2019-05-21 12:12:38 -0700 | [diff] [blame] | 2445 | Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"), |
Tomasz Nowicki | 344eb14 | 2021-09-22 05:51:58 +0000 | [diff] [blame] | 2446 | Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of PCI pass through or mdev device. |
Zide Chen | dfc4b88 | 2021-03-10 16:35:37 -0800 | [diff] [blame] | 2447 | iommu=on|off - indicates whether to enable virtio IOMMU for this device"), |
Tomasz Nowicki | 344eb14 | 2021-09-22 05:51:58 +0000 | [diff] [blame] | 2448 | Argument::value("vfio-platform", "PATH", "Path to sysfs of platform pass through"), |
Keiichi Watanabe | 57df6a0 | 2019-12-06 22:24:40 +0900 | [diff] [blame] | 2449 | #[cfg(feature = "video-decoder")] |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 2450 | Argument::flag_or_value("video-decoder", "[backend]", "(EXPERIMENTAL) enable virtio-video decoder device |
| 2451 | Possible backend values: libvda"), |
Keiichi Watanabe | 57df6a0 | 2019-12-06 22:24:40 +0900 | [diff] [blame] | 2452 | #[cfg(feature = "video-encoder")] |
Alexandre Courbot | b42b3e5 | 2021-07-09 23:38:57 +0900 | [diff] [blame] | 2453 | Argument::flag_or_value("video-encoder", "[backend]", "(EXPERIMENTAL) enable virtio-video encoder device |
| 2454 | Possible backend values: libvda"), |
Tomasz Jeznach | 4264464 | 2020-05-20 23:27:59 -0700 | [diff] [blame] | 2455 | Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"), |
Will Deacon | 6560c18 | 2020-10-06 18:47:18 +0100 | [diff] [blame] | 2456 | Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"), |
Andrew Walbran | 0bbbb68 | 2021-12-13 13:42:07 +0000 | [diff] [blame^] | 2457 | Argument::flag("protected-vm-without-firmware", "(EXPERIMENTAL) prevent host access to guest memory, but don't use protected VM firmware"), |
Will Deacon | c48e783 | 2021-07-30 19:03:06 +0100 | [diff] [blame] | 2458 | #[cfg(target_arch = "aarch64")] |
Andrew Walbran | 0bbbb68 | 2021-12-13 13:42:07 +0000 | [diff] [blame^] | 2459 | Argument::value("swiotlb", "N", "(EXPERIMENTAL) Size of virtio swiotlb buffer in MiB (default: 64 if `--protected-vm` or `--protected-vm-without-firmware` is present)."), |
Chuanxiao Dong | fd5626c | 2020-04-27 16:35:33 +0800 | [diff] [blame] | 2460 | Argument::flag_or_value("battery", |
| 2461 | "[type=TYPE]", |
| 2462 | "Comma separated key=value pairs for setting up battery device |
Junichi Uekawa | aab74e4 | 2021-07-19 16:12:46 +0900 | [diff] [blame] | 2463 | Possible key values: |
| 2464 | type=goldfish - type of battery emulation, defaults to goldfish"), |
Keiichi Watanabe | c5262e9 | 2020-10-21 15:57:33 +0900 | [diff] [blame] | 2465 | Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"), |
Charles William Dick | 0e3d4b6 | 2020-12-14 12:16:46 +0900 | [diff] [blame] | 2466 | Argument::value("balloon_bias_mib", "N", "Amount to bias balance of memory between host and guest as the balloon inflates, in MiB."), |
Keiichi Watanabe | f3a37f4 | 2021-01-21 15:41:11 +0900 | [diff] [blame] | 2467 | Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"), |
Federico 'Morg' Pareschi | 70fc7de | 2021-04-08 15:43:13 +0900 | [diff] [blame] | 2468 | Argument::value("vhost-user-console", "SOCKET_PATH", "Path to a socket for vhost-user console"), |
Chirantan Ekbote | 44292f5 | 2021-06-25 18:31:41 +0900 | [diff] [blame] | 2469 | Argument::value("vhost-user-gpu", "SOCKET_PATH", "Paths to a vhost-user socket for gpu"), |
JaeMan Park | eb9cc53 | 2021-07-02 15:02:59 +0900 | [diff] [blame] | 2470 | Argument::value("vhost-user-mac80211-hwsim", "SOCKET_PATH", "Path to a socket for vhost-user mac80211_hwsim"), |
Keiichi Watanabe | 6068658 | 2021-03-12 04:53:51 +0900 | [diff] [blame] | 2471 | Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"), |
Woody Chow | 1b16db1 | 2021-04-02 16:59:59 +0900 | [diff] [blame] | 2472 | #[cfg(feature = "audio")] |
| 2473 | Argument::value("vhost-user-snd", "SOCKET_PATH", "Path to a socket for vhost-user snd"), |
Chirantan Ekbote | 84091e5 | 2021-09-10 18:43:17 +0900 | [diff] [blame] | 2474 | Argument::value("vhost-user-vsock", "SOCKET_PATH", "Path to a socket for vhost-user vsock"), |
Chirantan Ekbote | 2ee9dcd | 2021-05-26 18:21:44 +0900 | [diff] [blame] | 2475 | Argument::value("vhost-user-wl", "SOCKET_PATH:TUBE_PATH", "Paths to a vhost-user socket for wayland and a Tube socket for additional wayland-specific messages"), |
Woody Chow | 5890b70 | 2021-02-12 14:57:02 +0900 | [diff] [blame] | 2476 | Argument::value("vhost-user-fs", "SOCKET_PATH:TAG", |
| 2477 | "Path to a socket path for vhost-user fs, and tag for the shared dir"), |
Tomasz Jeznach | 3ce7476 | 2021-02-26 01:01:53 -0800 | [diff] [blame] | 2478 | #[cfg(feature = "direct")] |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 2479 | Argument::value("direct-pmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct port mapped I/O access. RANGE may be decimal or hex (starting with 0x)."), |
Tomasz Jeznach | 9e6c633 | 2021-05-27 21:49:14 -0700 | [diff] [blame] | 2480 | #[cfg(feature = "direct")] |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 2481 | Argument::value("direct-mmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct memory mapped I/O access. RANGE may be decimal or hex (starting with 0x)."), |
Tomasz Jeznach | 7271f75 | 2021-03-04 01:44:06 -0800 | [diff] [blame] | 2482 | #[cfg(feature = "direct")] |
| 2483 | Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"), |
| 2484 | #[cfg(feature = "direct")] |
| 2485 | Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"), |
Tomasz Jeznach | ccb2694 | 2021-03-30 22:44:11 -0700 | [diff] [blame] | 2486 | Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"), |
Tomasz Jeznach | d93c29f | 2021-04-12 11:00:24 -0700 | [diff] [blame] | 2487 | Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"), |
ZhaoLiu | 2aaf7ad | 2021-10-10 18:22:29 +0800 | [diff] [blame] | 2488 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 2489 | Argument::flag("host-cpu-topology", "Use mirror cpu topology of Host for Guest VM"), |
Mattias Nissler | de2c640 | 2021-10-21 12:05:29 +0000 | [diff] [blame] | 2490 | Argument::value("stub-pci-device", "DOMAIN:BUS:DEVICE.FUNCTION[,vendor=NUM][,device=NUM][,class=NUM][,multifunction][,subsystem_vendor=NUM][,subsystem_device=NUM][,revision=NUM]", "Comma-separated key=value pairs for setting up a stub PCI device that just enumerates. The first option in the list must specify a PCI address to claim. |
| 2491 | Optional further parameters |
| 2492 | vendor=NUM - PCI vendor ID |
| 2493 | device=NUM - PCI device ID |
| 2494 | class=NUM - PCI class (including class code, subclass, and programming interface) |
| 2495 | multifunction - whether to set the multifunction flag |
| 2496 | subsystem_vendor=NUM - PCI subsystem vendor ID |
| 2497 | subsystem_device=NUM - PCI subsystem device ID |
| 2498 | revision=NUM - revision"), |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2499 | Argument::short_flag('h', "help", "Print help message.")]; |
| 2500 | |
| 2501 | let mut cfg = Config::default(); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 2502 | let match_res = set_arguments(args, &arguments[..], |name, value| { |
| 2503 | set_argument(&mut cfg, name, value) |
David Tolnay | 2bac1e7 | 2018-12-12 14:33:42 -0800 | [diff] [blame] | 2504 | }) |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 2505 | .and_then(|_| validate_arguments(&mut cfg)); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2506 | |
| 2507 | match match_res { |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 2508 | #[cfg(feature = "plugin")] |
Zach Reizner | 267f2c8 | 2019-07-31 17:07:27 -0700 | [diff] [blame] | 2509 | Ok(()) if executable_is_plugin(&cfg.executable_path) => { |
| 2510 | match crosvm::plugin::run_config(cfg) { |
| 2511 | Ok(_) => { |
| 2512 | info!("crosvm and plugin have exited normally"); |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 2513 | Ok(CommandStatus::VmStop) |
Zach Reizner | 267f2c8 | 2019-07-31 17:07:27 -0700 | [diff] [blame] | 2514 | } |
| 2515 | Err(e) => { |
| 2516 | error!("{}", e); |
| 2517 | Err(()) |
| 2518 | } |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 2519 | } |
Zach Reizner | 267f2c8 | 2019-07-31 17:07:27 -0700 | [diff] [blame] | 2520 | } |
Michael Hoyle | e47a500 | 2020-10-15 16:24:13 -0700 | [diff] [blame] | 2521 | Ok(()) => match platform::run_config(cfg) { |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 2522 | Ok(platform::ExitState::Stop) => { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 2523 | info!("crosvm has exited normally"); |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 2524 | Ok(CommandStatus::VmStop) |
| 2525 | } |
| 2526 | Ok(platform::ExitState::Reset) => { |
| 2527 | info!("crosvm has exited normally due to reset request"); |
| 2528 | Ok(CommandStatus::VmReset) |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2529 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 2530 | Err(e) => { |
Alexandre Courbot | a184e31 | 2021-12-08 13:18:27 +0900 | [diff] [blame] | 2531 | error!("crosvm has exited with error: {:#}", e); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 2532 | Err(()) |
| 2533 | } |
| 2534 | }, |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 2535 | Err(argument::Error::PrintHelp) => { |
| 2536 | print_help("crosvm run", "KERNEL", &arguments[..]); |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 2537 | Ok(CommandStatus::Success) |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 2538 | } |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 2539 | Err(e) => { |
Dmitry Torokhov | 470b1e7 | 2020-01-15 12:46:49 -0800 | [diff] [blame] | 2540 | error!("{}", e); |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 2541 | Err(()) |
Zach Reizner | 8864cb0 | 2018-01-16 17:59:03 -0800 | [diff] [blame] | 2542 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 2543 | } |
| 2544 | } |
| 2545 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2546 | fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> { |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2547 | if args.len() == 0 { |
| 2548 | print_help("crosvm stop", "VM_SOCKET...", &[]); |
| 2549 | println!("Stops the crosvm instance listening on each `VM_SOCKET` given."); |
Jianxun Zhang | 56497d2 | 2019-03-04 14:38:24 -0800 | [diff] [blame] | 2550 | return Err(()); |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2551 | } |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2552 | let socket_path = &args.next().unwrap(); |
| 2553 | let socket_path = Path::new(&socket_path); |
| 2554 | vms_request(&VmRequest::Exit, socket_path) |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2555 | } |
| 2556 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2557 | fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> { |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2558 | if args.len() == 0 { |
| 2559 | print_help("crosvm suspend", "VM_SOCKET...", &[]); |
| 2560 | println!("Suspends the crosvm instance listening on each `VM_SOCKET` given."); |
Jianxun Zhang | 56497d2 | 2019-03-04 14:38:24 -0800 | [diff] [blame] | 2561 | return Err(()); |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2562 | } |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2563 | let socket_path = &args.next().unwrap(); |
| 2564 | let socket_path = Path::new(&socket_path); |
| 2565 | vms_request(&VmRequest::Suspend, socket_path) |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2566 | } |
| 2567 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2568 | fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> { |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2569 | if args.len() == 0 { |
| 2570 | print_help("crosvm resume", "VM_SOCKET...", &[]); |
| 2571 | println!("Resumes the crosvm instance listening on each `VM_SOCKET` given."); |
Jianxun Zhang | 56497d2 | 2019-03-04 14:38:24 -0800 | [diff] [blame] | 2572 | return Err(()); |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2573 | } |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2574 | let socket_path = &args.next().unwrap(); |
| 2575 | let socket_path = Path::new(&socket_path); |
| 2576 | vms_request(&VmRequest::Resume, socket_path) |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2577 | } |
| 2578 | |
| 2579 | fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> { |
| 2580 | if args.len() < 2 { |
| 2581 | print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]); |
| 2582 | println!("Set the ballon size of the crosvm instance to `SIZE` bytes."); |
Jianxun Zhang | 56497d2 | 2019-03-04 14:38:24 -0800 | [diff] [blame] | 2583 | return Err(()); |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2584 | } |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 2585 | let num_bytes = match args.next().unwrap().parse::<u64>() { |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2586 | Ok(n) => n, |
| 2587 | Err(_) => { |
| 2588 | error!("Failed to parse number of bytes"); |
| 2589 | return Err(()); |
| 2590 | } |
| 2591 | }; |
| 2592 | |
Jakub Staron | 1f828d7 | 2019-04-11 12:49:29 -0700 | [diff] [blame] | 2593 | let command = BalloonControlCommand::Adjust { num_bytes }; |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2594 | let socket_path = &args.next().unwrap(); |
| 2595 | let socket_path = Path::new(&socket_path); |
| 2596 | vms_request(&VmRequest::BalloonCommand(command), socket_path) |
Zach Reizner | 7898632 | 2019-02-21 20:43:21 -0800 | [diff] [blame] | 2597 | } |
| 2598 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2599 | fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> { |
Charles William Dick | ed22f6b | 2020-04-08 11:05:24 +0900 | [diff] [blame] | 2600 | if args.len() != 1 { |
| 2601 | print_help("crosvm balloon_stats", "VM_SOCKET", &[]); |
| 2602 | println!("Prints virtio balloon statistics for a `VM_SOCKET`."); |
| 2603 | return Err(()); |
| 2604 | } |
| 2605 | let command = BalloonControlCommand::Stats {}; |
| 2606 | let request = &VmRequest::BalloonCommand(command); |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2607 | let socket_path = &args.next().unwrap(); |
| 2608 | let socket_path = Path::new(&socket_path); |
| 2609 | let response = handle_request(request, socket_path)?; |
Hikaru Nishida | a0e381b | 2021-05-24 17:13:45 +0900 | [diff] [blame] | 2610 | match serde_json::to_string_pretty(&response) { |
| 2611 | Ok(response_json) => println!("{}", response_json), |
| 2612 | Err(e) => { |
| 2613 | error!("Failed to serialize into JSON: {}", e); |
| 2614 | return Err(()); |
| 2615 | } |
| 2616 | } |
Hikaru Nishida | 6b51c75 | 2021-05-21 12:37:43 +0900 | [diff] [blame] | 2617 | match response { |
| 2618 | VmResponse::BalloonStats { .. } => Ok(()), |
| 2619 | _ => Err(()), |
| 2620 | } |
Charles William Dick | ed22f6b | 2020-04-08 11:05:24 +0900 | [diff] [blame] | 2621 | } |
| 2622 | |
Keiichi Watanabe | 2400bd5 | 2021-08-04 19:18:02 +0900 | [diff] [blame] | 2623 | fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> { |
| 2624 | if args.len() < 4 { |
| 2625 | print_help( |
| 2626 | "crosvm battery BATTERY_TYPE ", |
| 2627 | "[status STATUS | \ |
| 2628 | present PRESENT | \ |
| 2629 | health HEALTH | \ |
| 2630 | capacity CAPACITY | \ |
| 2631 | aconline ACONLINE ] \ |
| 2632 | VM_SOCKET...", |
| 2633 | &[], |
| 2634 | ); |
| 2635 | return Err(()); |
| 2636 | } |
| 2637 | |
| 2638 | // This unwrap will not panic because of the above length check. |
| 2639 | let battery_type = args.next().unwrap(); |
| 2640 | let property = args.next().unwrap(); |
| 2641 | let target = args.next().unwrap(); |
| 2642 | |
| 2643 | let socket_path = args.next().unwrap(); |
| 2644 | let socket_path = Path::new(&socket_path); |
| 2645 | |
Daniel Verkamp | 166d1dd | 2021-08-19 17:05:29 -0700 | [diff] [blame] | 2646 | do_modify_battery(socket_path, &*battery_type, &*property, &*target) |
Keiichi Watanabe | 2400bd5 | 2021-08-04 19:18:02 +0900 | [diff] [blame] | 2647 | } |
| 2648 | |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2649 | #[cfg(feature = "composite-disk")] |
| 2650 | fn create_composite(mut args: std::env::Args) -> std::result::Result<(), ()> { |
| 2651 | if args.len() < 1 { |
| 2652 | print_help("crosvm create_composite", "PATH [LABEL:PARTITION]..", &[]); |
| 2653 | println!("Creates a new composite disk image containing the given partition images"); |
| 2654 | return Err(()); |
| 2655 | } |
| 2656 | |
| 2657 | let composite_image_path = args.next().unwrap(); |
Jooyung Han | 2e14c73 | 2021-07-29 13:27:54 +0900 | [diff] [blame] | 2658 | let zero_filler_path = format!("{}.filler", composite_image_path); |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2659 | let header_path = format!("{}.header", composite_image_path); |
| 2660 | let footer_path = format!("{}.footer", composite_image_path); |
| 2661 | |
| 2662 | let mut composite_image_file = OpenOptions::new() |
| 2663 | .create(true) |
| 2664 | .read(true) |
| 2665 | .write(true) |
| 2666 | .truncate(true) |
| 2667 | .open(&composite_image_path) |
| 2668 | .map_err(|e| { |
| 2669 | error!( |
| 2670 | "Failed opening composite disk image file at '{}': {}", |
| 2671 | composite_image_path, e |
| 2672 | ); |
| 2673 | })?; |
Jooyung Han | 2e14c73 | 2021-07-29 13:27:54 +0900 | [diff] [blame] | 2674 | create_zero_filler(&zero_filler_path).map_err(|e| { |
| 2675 | error!( |
| 2676 | "Failed to create zero filler file at '{}': {}", |
| 2677 | &zero_filler_path, e |
| 2678 | ); |
| 2679 | })?; |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2680 | let mut header_file = OpenOptions::new() |
| 2681 | .create(true) |
| 2682 | .read(true) |
| 2683 | .write(true) |
| 2684 | .truncate(true) |
| 2685 | .open(&header_path) |
| 2686 | .map_err(|e| { |
| 2687 | error!( |
| 2688 | "Failed opening header image file at '{}': {}", |
| 2689 | header_path, e |
| 2690 | ); |
| 2691 | })?; |
| 2692 | let mut footer_file = OpenOptions::new() |
| 2693 | .create(true) |
| 2694 | .read(true) |
| 2695 | .write(true) |
| 2696 | .truncate(true) |
| 2697 | .open(&footer_path) |
| 2698 | .map_err(|e| { |
| 2699 | error!( |
| 2700 | "Failed opening footer image file at '{}': {}", |
| 2701 | footer_path, e |
| 2702 | ); |
| 2703 | })?; |
| 2704 | |
| 2705 | let partitions = args |
| 2706 | .into_iter() |
| 2707 | .map(|partition_arg| { |
| 2708 | if let [label, path] = partition_arg.split(":").collect::<Vec<_>>()[..] { |
| 2709 | let partition_file = File::open(path) |
| 2710 | .map_err(|e| error!("Failed to open partition image: {}", e))?; |
Daniel Verkamp | eb1640e | 2021-09-07 14:09:31 -0700 | [diff] [blame] | 2711 | let size = create_disk_file(partition_file, disk::MAX_NESTING_DEPTH) |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2712 | .map_err(|e| error!("Failed to create DiskFile instance: {}", e))? |
| 2713 | .get_len() |
| 2714 | .map_err(|e| error!("Failed to get length of partition image: {}", e))?; |
| 2715 | Ok(PartitionInfo { |
| 2716 | label: label.to_owned(), |
Jooyung Han | d7e56ba | 2021-07-29 13:26:48 +0900 | [diff] [blame] | 2717 | path: Path::new(path).to_owned(), |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2718 | partition_type: ImagePartitionType::LinuxFilesystem, |
| 2719 | writable: false, |
Jooyung Han | d7e56ba | 2021-07-29 13:26:48 +0900 | [diff] [blame] | 2720 | size, |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2721 | }) |
| 2722 | } else { |
| 2723 | error!( |
| 2724 | "Must specify label and path for partition '{}', like LABEL:PATH", |
| 2725 | partition_arg |
| 2726 | ); |
| 2727 | Err(()) |
| 2728 | } |
| 2729 | }) |
| 2730 | .collect::<Result<Vec<_>, _>>()?; |
| 2731 | |
| 2732 | create_composite_disk( |
| 2733 | &partitions, |
Jooyung Han | 2e14c73 | 2021-07-29 13:27:54 +0900 | [diff] [blame] | 2734 | &PathBuf::from(zero_filler_path), |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 2735 | &PathBuf::from(header_path), |
| 2736 | &mut header_file, |
| 2737 | &PathBuf::from(footer_path), |
| 2738 | &mut footer_file, |
| 2739 | &mut composite_image_file, |
| 2740 | ) |
| 2741 | .map_err(|e| { |
| 2742 | error!( |
| 2743 | "Failed to create composite disk image at '{}': {}", |
| 2744 | composite_image_path, e |
| 2745 | ); |
| 2746 | })?; |
| 2747 | |
| 2748 | Ok(()) |
| 2749 | } |
| 2750 | |
A. Cody Schuffelen | 9ca6039 | 2019-12-23 18:27:11 -0800 | [diff] [blame] | 2751 | fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> { |
| 2752 | let arguments = [ |
| 2753 | Argument::positional("PATH", "where to create the qcow2 image"), |
| 2754 | Argument::positional("[SIZE]", "the expanded size of the image"), |
| 2755 | Argument::value( |
| 2756 | "backing_file", |
| 2757 | "path/to/file", |
| 2758 | " the file to back the image", |
| 2759 | ), |
| 2760 | ]; |
| 2761 | let mut positional_index = 0; |
| 2762 | let mut file_path = String::from(""); |
| 2763 | let mut size: Option<u64> = None; |
| 2764 | let mut backing_file: Option<String> = None; |
| 2765 | set_arguments(args, &arguments[..], |name, value| { |
| 2766 | match (name, positional_index) { |
| 2767 | ("", 0) => { |
| 2768 | // NAME |
| 2769 | positional_index += 1; |
| 2770 | file_path = value.unwrap().to_owned(); |
| 2771 | } |
| 2772 | ("", 1) => { |
| 2773 | // [SIZE] |
| 2774 | positional_index += 1; |
| 2775 | size = Some(value.unwrap().parse::<u64>().map_err(|_| { |
| 2776 | argument::Error::InvalidValue { |
| 2777 | value: value.unwrap().to_owned(), |
Judy Hsiao | 5934305 | 2020-03-16 15:58:03 +0800 | [diff] [blame] | 2778 | expected: String::from("SIZE should be a nonnegative integer"), |
A. Cody Schuffelen | 9ca6039 | 2019-12-23 18:27:11 -0800 | [diff] [blame] | 2779 | } |
| 2780 | })?); |
| 2781 | } |
| 2782 | ("", _) => { |
| 2783 | return Err(argument::Error::TooManyArguments( |
| 2784 | "Expected at most 2 positional arguments".to_owned(), |
| 2785 | )); |
| 2786 | } |
| 2787 | ("backing_file", _) => { |
| 2788 | backing_file = value.map(|x| x.to_owned()); |
| 2789 | } |
| 2790 | _ => unreachable!(), |
| 2791 | }; |
| 2792 | Ok(()) |
| 2793 | }) |
| 2794 | .map_err(|e| { |
| 2795 | error!("Unable to parse command line arguments: {}", e); |
| 2796 | })?; |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 2797 | if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) { |
A. Cody Schuffelen | 9ca6039 | 2019-12-23 18:27:11 -0800 | [diff] [blame] | 2798 | print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments); |
| 2799 | println!( |
| 2800 | "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or |
| 2801 | with a '--backing_file'." |
| 2802 | ); |
Jianxun Zhang | 56497d2 | 2019-03-04 14:38:24 -0800 | [diff] [blame] | 2803 | return Err(()); |
Dylan Reid | 2dcb632 | 2018-07-13 10:42:48 -0700 | [diff] [blame] | 2804 | } |
Dylan Reid | 2dcb632 | 2018-07-13 10:42:48 -0700 | [diff] [blame] | 2805 | |
| 2806 | let file = OpenOptions::new() |
| 2807 | .create(true) |
| 2808 | .read(true) |
| 2809 | .write(true) |
A. Cody Schuffelen | 9ca6039 | 2019-12-23 18:27:11 -0800 | [diff] [blame] | 2810 | .truncate(true) |
Dylan Reid | 2dcb632 | 2018-07-13 10:42:48 -0700 | [diff] [blame] | 2811 | .open(&file_path) |
| 2812 | .map_err(|e| { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 2813 | error!("Failed opening qcow file at '{}': {}", file_path, e); |
Dylan Reid | 2dcb632 | 2018-07-13 10:42:48 -0700 | [diff] [blame] | 2814 | })?; |
| 2815 | |
A. Cody Schuffelen | 9ca6039 | 2019-12-23 18:27:11 -0800 | [diff] [blame] | 2816 | match (size, backing_file) { |
| 2817 | (Some(size), None) => QcowFile::new(file, size).map_err(|e| { |
| 2818 | error!("Failed to create qcow file at '{}': {}", file_path, e); |
| 2819 | })?, |
| 2820 | (None, Some(backing_file)) => { |
Daniel Verkamp | eb1640e | 2021-09-07 14:09:31 -0700 | [diff] [blame] | 2821 | QcowFile::new_from_backing(file, &backing_file, disk::MAX_NESTING_DEPTH).map_err( |
| 2822 | |e| { |
| 2823 | error!("Failed to create qcow file at '{}': {}", file_path, e); |
| 2824 | }, |
| 2825 | )? |
A. Cody Schuffelen | 9ca6039 | 2019-12-23 18:27:11 -0800 | [diff] [blame] | 2826 | } |
| 2827 | _ => unreachable!(), |
| 2828 | }; |
Dylan Reid | 2dcb632 | 2018-07-13 10:42:48 -0700 | [diff] [blame] | 2829 | Ok(()) |
| 2830 | } |
| 2831 | |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 2832 | fn start_device(mut args: std::env::Args) -> std::result::Result<(), ()> { |
| 2833 | let print_usage = || { |
| 2834 | print_help( |
| 2835 | "crosvm device", |
Federico 'Morg' Pareschi | a118482 | 2021-09-09 10:52:58 +0900 | [diff] [blame] | 2836 | " (block|console|fs|gpu|net|wl) <device-specific arguments>", |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 2837 | &[], |
| 2838 | ); |
| 2839 | }; |
| 2840 | |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 2841 | if args.len() == 0 { |
| 2842 | print_usage(); |
| 2843 | return Err(()); |
| 2844 | } |
| 2845 | |
| 2846 | let device = args.next().unwrap(); |
| 2847 | |
| 2848 | let program_name = format!("crosvm device {}", device); |
| 2849 | let result = match device.as_str() { |
| 2850 | "block" => run_block_device(&program_name, args), |
Federico 'Morg' Pareschi | a118482 | 2021-09-09 10:52:58 +0900 | [diff] [blame] | 2851 | "console" => run_console_device(&program_name, args), |
| 2852 | "fs" => run_fs_device(&program_name, args), |
Chirantan Ekbote | 7822529 | 2021-06-25 18:30:34 +0900 | [diff] [blame] | 2853 | #[cfg(feature = "gpu")] |
| 2854 | "gpu" => run_gpu_device(&program_name, args), |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 2855 | "net" => run_net_device(&program_name, args), |
Chirantan Ekbote | f08bddd | 2021-09-10 18:41:06 +0900 | [diff] [blame] | 2856 | "vsock" => run_vsock_device(&program_name, args), |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 2857 | "wl" => run_wl_device(&program_name, args), |
| 2858 | _ => { |
| 2859 | println!("Unknown device name: {}", device); |
| 2860 | print_usage(); |
| 2861 | return Err(()); |
| 2862 | } |
| 2863 | }; |
| 2864 | |
| 2865 | result.map_err(|e| { |
Alexandre Courbot | 3abfaa5 | 2021-12-10 16:29:34 +0900 | [diff] [blame] | 2866 | error!("Failed to run {} device: {:#}", device, e); |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 2867 | }) |
| 2868 | } |
| 2869 | |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2870 | fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> { |
| 2871 | if args.len() < 2 { |
| 2872 | print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]); |
| 2873 | println!("Manage attached virtual disk devices."); |
| 2874 | println!("Subcommands:"); |
| 2875 | println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET"); |
Jianxun Zhang | 56497d2 | 2019-03-04 14:38:24 -0800 | [diff] [blame] | 2876 | return Err(()); |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2877 | } |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 2878 | let subcommand: &str = &args.next().unwrap(); |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2879 | |
| 2880 | let request = match subcommand { |
| 2881 | "resize" => { |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 2882 | let disk_index = match args.next().unwrap().parse::<usize>() { |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2883 | Ok(n) => n, |
| 2884 | Err(_) => { |
| 2885 | error!("Failed to parse disk index"); |
| 2886 | return Err(()); |
| 2887 | } |
| 2888 | }; |
| 2889 | |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 2890 | let new_size = match args.next().unwrap().parse::<u64>() { |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2891 | Ok(n) => n, |
| 2892 | Err(_) => { |
| 2893 | error!("Failed to parse disk size"); |
| 2894 | return Err(()); |
| 2895 | } |
| 2896 | }; |
| 2897 | |
Jakub Staron | ecf81e0 | 2019-04-11 11:43:39 -0700 | [diff] [blame] | 2898 | VmRequest::DiskCommand { |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2899 | disk_index, |
Jakub Staron | ecf81e0 | 2019-04-11 11:43:39 -0700 | [diff] [blame] | 2900 | command: DiskControlCommand::Resize { new_size }, |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2901 | } |
| 2902 | } |
| 2903 | _ => { |
| 2904 | error!("Unknown disk subcommand '{}'", subcommand); |
| 2905 | return Err(()); |
| 2906 | } |
| 2907 | }; |
| 2908 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2909 | let socket_path = &args.next().unwrap(); |
| 2910 | let socket_path = Path::new(&socket_path); |
| 2911 | vms_request(&request, socket_path) |
Daniel Verkamp | 92f73d7 | 2018-12-04 13:17:46 -0800 | [diff] [blame] | 2912 | } |
| 2913 | |
Keiichi Watanabe | 2400bd5 | 2021-08-04 19:18:02 +0900 | [diff] [blame] | 2914 | fn make_rt(mut args: std::env::Args) -> std::result::Result<(), ()> { |
| 2915 | if args.len() == 0 { |
| 2916 | print_help("crosvm make_rt", "VM_SOCKET...", &[]); |
| 2917 | println!("Makes the crosvm instance listening on each `VM_SOCKET` given RT."); |
| 2918 | return Err(()); |
| 2919 | } |
| 2920 | let socket_path = &args.next().unwrap(); |
| 2921 | let socket_path = Path::new(&socket_path); |
| 2922 | vms_request(&VmRequest::MakeRT, socket_path) |
| 2923 | } |
| 2924 | |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2925 | fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> { |
| 2926 | debug!("parse_bus_id_addr: {}", v); |
Keiichi Watanabe | 275c1ef | 2020-04-10 21:49:10 +0900 | [diff] [blame] | 2927 | let mut ids = v.split(':'); |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2928 | match (ids.next(), ids.next(), ids.next(), ids.next()) { |
| 2929 | (Some(bus_id), Some(addr), Some(vid), Some(pid)) => { |
| 2930 | let bus_id = bus_id |
| 2931 | .parse::<u8>() |
| 2932 | .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?; |
| 2933 | let addr = addr |
| 2934 | .parse::<u8>() |
| 2935 | .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?; |
Daniel Verkamp | 166d1dd | 2021-08-19 17:05:29 -0700 | [diff] [blame] | 2936 | let vid = u16::from_str_radix(vid, 16) |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2937 | .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?; |
Daniel Verkamp | 166d1dd | 2021-08-19 17:05:29 -0700 | [diff] [blame] | 2938 | let pid = u16::from_str_radix(pid, 16) |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2939 | .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?; |
| 2940 | Ok((bus_id, addr, vid, pid)) |
| 2941 | } |
| 2942 | _ => Err(ModifyUsbError::ArgParse( |
| 2943 | "BUS_ID_ADDR_BUS_NUM_DEV_NUM", |
| 2944 | v.to_owned(), |
| 2945 | )), |
| 2946 | } |
| 2947 | } |
| 2948 | |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2949 | fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> { |
| 2950 | let val = args |
| 2951 | .next() |
| 2952 | .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?; |
| 2953 | let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?; |
| 2954 | let dev_path = PathBuf::from( |
| 2955 | args.next() |
| 2956 | .ok_or(ModifyUsbError::ArgMissing("usb device path"))?, |
| 2957 | ); |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2958 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2959 | let socket_path = args |
| 2960 | .next() |
| 2961 | .ok_or(ModifyUsbError::ArgMissing("control socket path"))?; |
| 2962 | let socket_path = Path::new(&socket_path); |
| 2963 | |
Daniel Verkamp | 166d1dd | 2021-08-19 17:05:29 -0700 | [diff] [blame] | 2964 | do_usb_attach(socket_path, bus, addr, vid, pid, &dev_path) |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2965 | } |
| 2966 | |
| 2967 | fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> { |
| 2968 | let port: u8 = args |
| 2969 | .next() |
| 2970 | .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| { |
| 2971 | p.parse::<u8>() |
| 2972 | .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e)) |
| 2973 | })?; |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2974 | let socket_path = args |
| 2975 | .next() |
| 2976 | .ok_or(ModifyUsbError::ArgMissing("control socket path"))?; |
| 2977 | let socket_path = Path::new(&socket_path); |
Daniel Verkamp | 166d1dd | 2021-08-19 17:05:29 -0700 | [diff] [blame] | 2978 | do_usb_detach(socket_path, port) |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2979 | } |
| 2980 | |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2981 | fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> { |
| 2982 | let socket_path = args |
| 2983 | .next() |
| 2984 | .ok_or(ModifyUsbError::ArgMissing("control socket path"))?; |
| 2985 | let socket_path = Path::new(&socket_path); |
Daniel Verkamp | 166d1dd | 2021-08-19 17:05:29 -0700 | [diff] [blame] | 2986 | do_usb_list(socket_path) |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2987 | } |
| 2988 | |
| 2989 | fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> { |
Zach Reizner | aff94ca | 2019-03-18 20:58:31 -0700 | [diff] [blame] | 2990 | if args.len() < 2 { |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2991 | print_help("crosvm usb", |
Zach Reizner | aff94ca | 2019-03-18 20:58:31 -0700 | [diff] [blame] | 2992 | "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]); |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2993 | return Err(()); |
| 2994 | } |
| 2995 | |
| 2996 | // This unwrap will not panic because of the above length check. |
Kevin Hamacher | 6fc5f20 | 2021-03-18 12:41:23 +0100 | [diff] [blame] | 2997 | let command = &args.next().unwrap(); |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 2998 | let result = match command.as_ref() { |
| 2999 | "attach" => usb_attach(args), |
| 3000 | "detach" => usb_detach(args), |
| 3001 | "list" => usb_list(args), |
| 3002 | other => Err(ModifyUsbError::UnknownCommand(other.to_owned())), |
| 3003 | }; |
| 3004 | match result { |
| 3005 | Ok(response) => { |
| 3006 | println!("{}", response); |
| 3007 | Ok(()) |
| 3008 | } |
| 3009 | Err(e) => { |
| 3010 | println!("error {}", e); |
| 3011 | Err(()) |
| 3012 | } |
| 3013 | } |
| 3014 | } |
| 3015 | |
Keiichi Watanabe | 2400bd5 | 2021-08-04 19:18:02 +0900 | [diff] [blame] | 3016 | #[allow(clippy::unnecessary_wraps)] |
| 3017 | fn pkg_version() -> std::result::Result<(), ()> { |
| 3018 | const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); |
| 3019 | const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION"); |
| 3020 | |
| 3021 | print!("crosvm {}", VERSION.unwrap_or("UNKNOWN")); |
| 3022 | match PKG_VERSION { |
| 3023 | Some(v) => println!("-{}", v), |
| 3024 | None => println!(), |
| 3025 | } |
| 3026 | Ok(()) |
| 3027 | } |
| 3028 | |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3029 | fn print_usage() { |
Dmitry Torokhov | dacf104 | 2021-12-09 15:36:01 -0800 | [diff] [blame] | 3030 | print_help("crosvm", "[--extended-status] [command]", &[]); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3031 | println!("Commands:"); |
Junichi Uekawa | f0a7723 | 2021-04-08 16:44:44 +0900 | [diff] [blame] | 3032 | println!(" balloon - Set balloon size of the crosvm instance."); |
| 3033 | println!(" balloon_stats - Prints virtio balloon statistics."); |
| 3034 | println!(" battery - Modify battery."); |
Andrew Walbran | fb7c108 | 2021-06-17 18:12:14 +0100 | [diff] [blame] | 3035 | #[cfg(feature = "composite-disk")] |
| 3036 | println!(" create_composite - Create a new composite disk image file."); |
Dylan Reid | 2dcb632 | 2018-07-13 10:42:48 -0700 | [diff] [blame] | 3037 | println!(" create_qcow2 - Create a new qcow2 disk image file."); |
Keiichi Watanabe | ee4b58e | 2021-08-17 19:34:01 +0900 | [diff] [blame] | 3038 | println!(" device - Start a device process."); |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 3039 | println!(" disk - Manage attached virtual disk devices."); |
Keiichi Watanabe | 9568bb4 | 2021-08-16 17:59:30 +0900 | [diff] [blame] | 3040 | println!( |
| 3041 | " make_rt - Enables real-time vcpu priority for crosvm instances started with \ |
| 3042 | `--delay-rt`." |
| 3043 | ); |
Junichi Uekawa | f0a7723 | 2021-04-08 16:44:44 +0900 | [diff] [blame] | 3044 | println!(" resume - Resumes the crosvm instance."); |
| 3045 | println!(" run - Start a new crosvm instance."); |
| 3046 | println!(" stop - Stops crosvm instances via their control sockets."); |
| 3047 | println!(" suspend - Suspends the crosvm instance."); |
Jingkui Wang | 100e6e4 | 2019-03-08 20:41:57 -0800 | [diff] [blame] | 3048 | println!(" usb - Manage attached virtual USB devices."); |
Yi Sun | 54305cd | 2020-01-04 00:19:37 +0800 | [diff] [blame] | 3049 | println!(" version - Show package version."); |
| 3050 | } |
| 3051 | |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3052 | fn crosvm_main() -> std::result::Result<CommandStatus, ()> { |
Stephen Barber | 56fbf09 | 2017-06-29 16:12:14 -0700 | [diff] [blame] | 3053 | if let Err(e) = syslog::init() { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 3054 | println!("failed to initialize syslog: {}", e); |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 3055 | return Err(()); |
Stephen Barber | 56fbf09 | 2017-06-29 16:12:14 -0700 | [diff] [blame] | 3056 | } |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 3057 | |
Zach Reizner | b3fa5c9 | 2019-01-28 14:05:23 -0800 | [diff] [blame] | 3058 | panic_hook::set_panic_hook(); |
| 3059 | |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3060 | let mut args = std::env::args(); |
| 3061 | if args.next().is_none() { |
| 3062 | error!("expected executable name"); |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 3063 | return Err(()); |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 3064 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3065 | |
Dmitry Torokhov | dacf104 | 2021-12-09 15:36:01 -0800 | [diff] [blame] | 3066 | let mut cmd_arg = args.next(); |
| 3067 | let extended_status = match cmd_arg.as_ref().map(|s| s.as_ref()) { |
| 3068 | Some("--extended-status") => { |
| 3069 | cmd_arg = args.next(); |
| 3070 | true |
| 3071 | } |
| 3072 | _ => false, |
| 3073 | } || cfg!(feature = "direct"); // TODO(dtor): remove default for crosvm-direct after transition |
| 3074 | |
| 3075 | let command = match cmd_arg { |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3076 | Some(c) => c, |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 3077 | None => { |
| 3078 | print_usage(); |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3079 | return Ok(CommandStatus::Success); |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 3080 | } |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3081 | }; |
| 3082 | |
| 3083 | // Past this point, usage of exit is in danger of leaking zombie processes. |
| 3084 | let ret = if command == "run" { |
Dmitry Torokhov | dacf104 | 2021-12-09 15:36:01 -0800 | [diff] [blame] | 3085 | // We handle run_vm separately because it does not simply signal success/error |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3086 | // but also indicates whether the guest requested reset or stop. |
| 3087 | run_vm(args) |
| 3088 | } else { |
| 3089 | match &command[..] { |
| 3090 | "balloon" => balloon_vms(args), |
| 3091 | "balloon_stats" => balloon_stats(args), |
| 3092 | "battery" => modify_battery(args), |
| 3093 | #[cfg(feature = "composite-disk")] |
| 3094 | "create_composite" => create_composite(args), |
| 3095 | "create_qcow2" => create_qcow2(args), |
| 3096 | "device" => start_device(args), |
| 3097 | "disk" => disk_cmd(args), |
| 3098 | "make_rt" => make_rt(args), |
| 3099 | "resume" => resume_vms(args), |
| 3100 | "stop" => stop_vms(args), |
| 3101 | "suspend" => suspend_vms(args), |
| 3102 | "usb" => modify_usb(args), |
| 3103 | "version" => pkg_version(), |
| 3104 | c => { |
| 3105 | println!("invalid subcommand: {:?}", c); |
| 3106 | print_usage(); |
| 3107 | Err(()) |
| 3108 | } |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3109 | } |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3110 | .map(|_| CommandStatus::Success) |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 3111 | }; |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3112 | |
| 3113 | // Reap exit status from any child device processes. At this point, all devices should have been |
| 3114 | // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may |
| 3115 | // take some time for the processes to shut down. |
| 3116 | if !wait_all_children() { |
| 3117 | // We gave them a chance, and it's too late. |
| 3118 | warn!("not all child processes have exited; sending SIGKILL"); |
| 3119 | if let Err(e) = kill_process_group() { |
| 3120 | // We're now at the mercy of the OS to clean up after us. |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 3121 | warn!("unable to kill all child processes: {}", e); |
Zach Reizner | efe9578 | 2017-08-26 18:05:48 -0700 | [diff] [blame] | 3122 | } |
| 3123 | } |
| 3124 | |
| 3125 | // WARNING: Any code added after this point is not guaranteed to run |
| 3126 | // since we may forcibly kill this process (and its children) above. |
Dmitry Torokhov | dacf104 | 2021-12-09 15:36:01 -0800 | [diff] [blame] | 3127 | ret.map(|s| { |
| 3128 | if extended_status { |
| 3129 | s |
| 3130 | } else { |
| 3131 | CommandStatus::Success |
| 3132 | } |
| 3133 | }) |
Dylan Reid | bfba993 | 2018-02-05 15:51:59 -0800 | [diff] [blame] | 3134 | } |
| 3135 | |
| 3136 | fn main() { |
Dmitry Torokhov | f75699f | 2021-12-03 11:19:13 -0800 | [diff] [blame] | 3137 | let exit_code = match crosvm_main() { |
| 3138 | Ok(CommandStatus::Success | CommandStatus::VmStop) => 0, |
| 3139 | Ok(CommandStatus::VmReset) => 32, |
| 3140 | Err(_) => 1, |
| 3141 | }; |
| 3142 | std::process::exit(exit_code); |
Zach Reizner | 639d967 | 2017-05-01 17:57:18 -0700 | [diff] [blame] | 3143 | } |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 3144 | |
| 3145 | #[cfg(test)] |
| 3146 | mod tests { |
| 3147 | use super::*; |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3148 | use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH}; |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 3149 | |
| 3150 | #[test] |
| 3151 | fn parse_cpu_set_single() { |
| 3152 | assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]); |
| 3153 | } |
| 3154 | |
| 3155 | #[test] |
| 3156 | fn parse_cpu_set_list() { |
| 3157 | assert_eq!( |
| 3158 | parse_cpu_set("0,1,2,3").expect("parse failed"), |
| 3159 | vec![0, 1, 2, 3] |
| 3160 | ); |
| 3161 | } |
| 3162 | |
| 3163 | #[test] |
| 3164 | fn parse_cpu_set_range() { |
| 3165 | assert_eq!( |
| 3166 | parse_cpu_set("0-3").expect("parse failed"), |
| 3167 | vec![0, 1, 2, 3] |
| 3168 | ); |
| 3169 | } |
| 3170 | |
| 3171 | #[test] |
| 3172 | fn parse_cpu_set_list_of_ranges() { |
| 3173 | assert_eq!( |
| 3174 | parse_cpu_set("3-4,7-9,18").expect("parse failed"), |
| 3175 | vec![3, 4, 7, 8, 9, 18] |
| 3176 | ); |
| 3177 | } |
| 3178 | |
| 3179 | #[test] |
| 3180 | fn parse_cpu_set_repeated() { |
| 3181 | // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion. |
| 3182 | assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]); |
| 3183 | } |
| 3184 | |
| 3185 | #[test] |
| 3186 | fn parse_cpu_set_negative() { |
| 3187 | // Negative CPU numbers are not allowed. |
| 3188 | parse_cpu_set("-3").expect_err("parse should have failed"); |
| 3189 | } |
| 3190 | |
| 3191 | #[test] |
| 3192 | fn parse_cpu_set_reverse_range() { |
| 3193 | // Ranges must be from low to high. |
| 3194 | parse_cpu_set("5-2").expect_err("parse should have failed"); |
| 3195 | } |
| 3196 | |
| 3197 | #[test] |
| 3198 | fn parse_cpu_set_open_range() { |
| 3199 | parse_cpu_set("3-").expect_err("parse should have failed"); |
| 3200 | } |
| 3201 | |
| 3202 | #[test] |
| 3203 | fn parse_cpu_set_extra_comma() { |
| 3204 | parse_cpu_set("0,1,2,").expect_err("parse should have failed"); |
| 3205 | } |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 3206 | |
Daniel Verkamp | c677fb4 | 2020-09-08 13:47:49 -0700 | [diff] [blame] | 3207 | #[test] |
| 3208 | fn parse_cpu_affinity_global() { |
| 3209 | assert_eq!( |
| 3210 | parse_cpu_affinity("0,5-7,9").expect("parse failed"), |
| 3211 | VcpuAffinity::Global(vec![0, 5, 6, 7, 9]), |
| 3212 | ); |
| 3213 | } |
| 3214 | |
| 3215 | #[test] |
| 3216 | fn parse_cpu_affinity_per_vcpu_one_to_one() { |
| 3217 | let mut expected_map = BTreeMap::new(); |
| 3218 | expected_map.insert(0, vec![0]); |
| 3219 | expected_map.insert(1, vec![1]); |
| 3220 | expected_map.insert(2, vec![2]); |
| 3221 | expected_map.insert(3, vec![3]); |
| 3222 | assert_eq!( |
| 3223 | parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"), |
| 3224 | VcpuAffinity::PerVcpu(expected_map), |
| 3225 | ); |
| 3226 | } |
| 3227 | |
| 3228 | #[test] |
| 3229 | fn parse_cpu_affinity_per_vcpu_sets() { |
| 3230 | let mut expected_map = BTreeMap::new(); |
| 3231 | expected_map.insert(0, vec![0, 1, 2]); |
| 3232 | expected_map.insert(1, vec![3, 4, 5]); |
| 3233 | expected_map.insert(2, vec![6, 7, 8]); |
| 3234 | assert_eq!( |
| 3235 | parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"), |
| 3236 | VcpuAffinity::PerVcpu(expected_map), |
| 3237 | ); |
| 3238 | } |
| 3239 | |
Dennis Kempin | 50a58f9 | 2021-06-23 11:34:31 -0700 | [diff] [blame] | 3240 | #[cfg(feature = "audio_cras")] |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 3241 | #[test] |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 3242 | fn parse_ac97_vaild() { |
| 3243 | parse_ac97_options("backend=cras").expect("parse should have succeded"); |
| 3244 | } |
| 3245 | |
Andrew Scull | 1590e6f | 2020-03-18 18:00:47 +0000 | [diff] [blame] | 3246 | #[cfg(feature = "audio")] |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 3247 | #[test] |
| 3248 | fn parse_ac97_null_vaild() { |
| 3249 | parse_ac97_options("backend=null").expect("parse should have succeded"); |
| 3250 | } |
| 3251 | |
Dennis Kempin | 50a58f9 | 2021-06-23 11:34:31 -0700 | [diff] [blame] | 3252 | #[cfg(feature = "audio_cras")] |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 3253 | #[test] |
Judy Hsiao | b4b94c7 | 2020-09-07 15:56:00 +0800 | [diff] [blame] | 3254 | fn parse_ac97_capture_vaild() { |
| 3255 | parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded"); |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 3256 | } |
| 3257 | |
Dennis Kempin | 50a58f9 | 2021-06-23 11:34:31 -0700 | [diff] [blame] | 3258 | #[cfg(feature = "audio_cras")] |
Jorge E. Moreira | 359e7de | 2020-12-02 18:25:53 -0800 | [diff] [blame] | 3259 | #[test] |
paulhsia | 83d5160 | 2021-03-09 17:13:14 +0800 | [diff] [blame] | 3260 | fn parse_ac97_client_type() { |
| 3261 | parse_ac97_options("backend=cras,capture=true,client_type=crosvm") |
| 3262 | .expect("parse should have succeded"); |
| 3263 | parse_ac97_options("backend=cras,capture=true,client_type=arcvm") |
| 3264 | .expect("parse should have succeded"); |
| 3265 | parse_ac97_options("backend=cras,capture=true,client_type=none") |
| 3266 | .expect_err("parse should have failed"); |
| 3267 | } |
| 3268 | |
Woody Chow | b27dea4 | 2021-09-08 15:51:22 +0900 | [diff] [blame] | 3269 | #[cfg(feature = "audio_cras")] |
| 3270 | #[test] |
| 3271 | fn parse_ac97_socket_type() { |
| 3272 | parse_ac97_options("socket_type=unified").expect("parse should have succeded"); |
| 3273 | parse_ac97_options("socket_type=legacy").expect("parse should have succeded"); |
| 3274 | } |
| 3275 | |
paulhsia | 83d5160 | 2021-03-09 17:13:14 +0800 | [diff] [blame] | 3276 | #[cfg(feature = "audio")] |
| 3277 | #[test] |
Jorge E. Moreira | 359e7de | 2020-12-02 18:25:53 -0800 | [diff] [blame] | 3278 | fn parse_ac97_vios_valid() { |
| 3279 | parse_ac97_options("backend=vios,server=/path/to/server") |
| 3280 | .expect("parse should have succeded"); |
| 3281 | } |
| 3282 | |
Judy Hsiao | d5c1e96 | 2020-02-04 12:30:01 +0800 | [diff] [blame] | 3283 | #[test] |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 3284 | fn parse_serial_vaild() { |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 3285 | parse_serial_options("type=syslog,num=1,console=true,stdin=true") |
| 3286 | .expect("parse should have succeded"); |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 3287 | } |
| 3288 | |
| 3289 | #[test] |
A. Cody Schuffelen | 3faab5a | 2020-10-05 17:29:16 -0700 | [diff] [blame] | 3290 | fn parse_serial_virtio_console_vaild() { |
| 3291 | parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console") |
| 3292 | .expect("parse should have succeded"); |
| 3293 | } |
| 3294 | |
| 3295 | #[test] |
Trent Begin | 923bab0 | 2019-06-17 13:48:06 -0600 | [diff] [blame] | 3296 | fn parse_serial_valid_no_num() { |
| 3297 | parse_serial_options("type=syslog").expect("parse should have succeded"); |
| 3298 | } |
| 3299 | |
| 3300 | #[test] |
Nicholas Hollingum | c76c2da | 2020-09-18 15:53:16 +1000 | [diff] [blame] | 3301 | fn parse_serial_equals_in_value() { |
| 3302 | let parsed = parse_serial_options("type=syslog,path=foo=bar==.log") |
| 3303 | .expect("parse should have succeded"); |
| 3304 | assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log"))); |
| 3305 | } |
| 3306 | |
| 3307 | #[test] |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 3308 | fn parse_serial_invalid_type() { |
| 3309 | parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed"); |
| 3310 | } |
| 3311 | |
| 3312 | #[test] |
| 3313 | fn parse_serial_invalid_num_upper() { |
| 3314 | parse_serial_options("type=syslog,num=5").expect_err("parse should have failed"); |
| 3315 | } |
| 3316 | |
| 3317 | #[test] |
| 3318 | fn parse_serial_invalid_num_lower() { |
| 3319 | parse_serial_options("type=syslog,num=0").expect_err("parse should have failed"); |
| 3320 | } |
| 3321 | |
| 3322 | #[test] |
A. Cody Schuffelen | 3faab5a | 2020-10-05 17:29:16 -0700 | [diff] [blame] | 3323 | fn parse_serial_virtio_console_invalid_num_lower() { |
| 3324 | parse_serial_options("type=syslog,hardware=virtio-console,num=0") |
| 3325 | .expect_err("parse should have failed"); |
| 3326 | } |
| 3327 | |
| 3328 | #[test] |
Trent Begin | 17ccaad | 2019-04-17 13:51:25 -0600 | [diff] [blame] | 3329 | fn parse_serial_invalid_num_string() { |
| 3330 | parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed"); |
| 3331 | } |
| 3332 | |
| 3333 | #[test] |
| 3334 | fn parse_serial_invalid_option() { |
| 3335 | parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed"); |
| 3336 | } |
Jorge E. Moreira | 1e26230 | 2019-08-01 14:40:03 -0700 | [diff] [blame] | 3337 | |
| 3338 | #[test] |
| 3339 | fn parse_serial_invalid_two_stdin() { |
| 3340 | let mut config = Config::default(); |
| 3341 | set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true")) |
| 3342 | .expect("should parse the first serial argument"); |
| 3343 | set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true")) |
| 3344 | .expect_err("should fail to parse a second serial port connected to stdin"); |
| 3345 | } |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 3346 | |
| 3347 | #[test] |
| 3348 | fn parse_plugin_mount_valid() { |
| 3349 | let mut config = Config::default(); |
| 3350 | set_argument( |
| 3351 | &mut config, |
| 3352 | "plugin-mount", |
| 3353 | Some("/dev/null:/dev/zero:true"), |
| 3354 | ) |
| 3355 | .expect("parse should succeed"); |
| 3356 | assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null")); |
| 3357 | assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero")); |
Daniel Verkamp | 5e9959e | 2021-08-19 17:09:59 -0700 | [diff] [blame] | 3358 | assert!(config.plugin_mounts[0].writable); |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 3359 | } |
| 3360 | |
| 3361 | #[test] |
| 3362 | fn parse_plugin_mount_valid_shorthand() { |
| 3363 | let mut config = Config::default(); |
| 3364 | set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed"); |
| 3365 | assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null")); |
Daniel Verkamp | 5e9959e | 2021-08-19 17:09:59 -0700 | [diff] [blame] | 3366 | assert!(!config.plugin_mounts[0].writable); |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 3367 | set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero")) |
| 3368 | .expect("parse should succeed"); |
| 3369 | assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero")); |
Daniel Verkamp | 5e9959e | 2021-08-19 17:09:59 -0700 | [diff] [blame] | 3370 | assert!(!config.plugin_mounts[1].writable); |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 3371 | set_argument(&mut config, "plugin-mount", Some("/dev/null::true")) |
| 3372 | .expect("parse should succeed"); |
| 3373 | assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null")); |
Daniel Verkamp | 5e9959e | 2021-08-19 17:09:59 -0700 | [diff] [blame] | 3374 | assert!(config.plugin_mounts[2].writable); |
Dmitry Torokhov | 458bb64 | 2019-12-13 11:47:52 -0800 | [diff] [blame] | 3375 | } |
| 3376 | |
| 3377 | #[test] |
| 3378 | fn parse_plugin_mount_invalid() { |
| 3379 | let mut config = Config::default(); |
| 3380 | set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail"); |
| 3381 | set_argument( |
| 3382 | &mut config, |
| 3383 | "plugin-mount", |
| 3384 | Some("/dev/null:/dev/null:true:false"), |
| 3385 | ) |
| 3386 | .expect_err("parse should fail because too many arguments"); |
| 3387 | set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true")) |
| 3388 | .expect_err("parse should fail because source is not absolute"); |
| 3389 | set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true")) |
| 3390 | .expect_err("parse should fail because source is not absolute"); |
| 3391 | set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah")) |
| 3392 | .expect_err("parse should fail because flag is not boolean"); |
| 3393 | } |
| 3394 | |
| 3395 | #[test] |
| 3396 | fn parse_plugin_gid_map_valid() { |
| 3397 | let mut config = Config::default(); |
| 3398 | set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed"); |
| 3399 | assert_eq!(config.plugin_gid_maps[0].inner, 1); |
| 3400 | assert_eq!(config.plugin_gid_maps[0].outer, 2); |
| 3401 | assert_eq!(config.plugin_gid_maps[0].count, 3); |
| 3402 | } |
| 3403 | |
| 3404 | #[test] |
| 3405 | fn parse_plugin_gid_map_valid_shorthand() { |
| 3406 | let mut config = Config::default(); |
| 3407 | set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed"); |
| 3408 | assert_eq!(config.plugin_gid_maps[0].inner, 1); |
| 3409 | assert_eq!(config.plugin_gid_maps[0].outer, 1); |
| 3410 | assert_eq!(config.plugin_gid_maps[0].count, 1); |
| 3411 | set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed"); |
| 3412 | assert_eq!(config.plugin_gid_maps[1].inner, 1); |
| 3413 | assert_eq!(config.plugin_gid_maps[1].outer, 2); |
| 3414 | assert_eq!(config.plugin_gid_maps[1].count, 1); |
| 3415 | set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed"); |
| 3416 | assert_eq!(config.plugin_gid_maps[2].inner, 1); |
| 3417 | assert_eq!(config.plugin_gid_maps[2].outer, 1); |
| 3418 | assert_eq!(config.plugin_gid_maps[2].count, 3); |
| 3419 | } |
| 3420 | |
| 3421 | #[test] |
| 3422 | fn parse_plugin_gid_map_invalid() { |
| 3423 | let mut config = Config::default(); |
| 3424 | set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail"); |
| 3425 | set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4")) |
| 3426 | .expect_err("parse should fail because too many arguments"); |
| 3427 | set_argument(&mut config, "plugin-gid-map", Some("blah:2:3")) |
| 3428 | .expect_err("parse should fail because inner is not a number"); |
| 3429 | set_argument(&mut config, "plugin-gid-map", Some("1:blah:3")) |
| 3430 | .expect_err("parse should fail because outer is not a number"); |
| 3431 | set_argument(&mut config, "plugin-gid-map", Some("1:2:blah")) |
| 3432 | .expect_err("parse should fail because count is not a number"); |
| 3433 | } |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3434 | |
| 3435 | #[test] |
| 3436 | fn single_touch_spec_and_track_pad_spec_default_size() { |
| 3437 | let mut config = Config::default(); |
| 3438 | config |
| 3439 | .executable_path |
| 3440 | .replace(Executable::Kernel(PathBuf::from("kernel"))); |
| 3441 | set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap(); |
| 3442 | set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap(); |
| 3443 | validate_arguments(&mut config).unwrap(); |
| 3444 | assert_eq!( |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3445 | config.virtio_single_touch.first().unwrap().get_size(), |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3446 | (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT) |
| 3447 | ); |
| 3448 | assert_eq!( |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3449 | config.virtio_trackpad.first().unwrap().get_size(), |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3450 | (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT) |
| 3451 | ); |
| 3452 | } |
| 3453 | |
| 3454 | #[cfg(feature = "gpu")] |
| 3455 | #[test] |
| 3456 | fn single_touch_spec_default_size_from_gpu() { |
| 3457 | let width = 12345u32; |
| 3458 | let height = 54321u32; |
| 3459 | let mut config = Config::default(); |
| 3460 | config |
| 3461 | .executable_path |
| 3462 | .replace(Executable::Kernel(PathBuf::from("kernel"))); |
| 3463 | set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap(); |
| 3464 | set_argument( |
| 3465 | &mut config, |
| 3466 | "gpu", |
| 3467 | Some(&format!("width={},height={}", width, height)), |
| 3468 | ) |
| 3469 | .unwrap(); |
| 3470 | validate_arguments(&mut config).unwrap(); |
| 3471 | assert_eq!( |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3472 | config.virtio_single_touch.first().unwrap().get_size(), |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3473 | (width, height) |
| 3474 | ); |
| 3475 | } |
| 3476 | |
| 3477 | #[test] |
| 3478 | fn single_touch_spec_and_track_pad_spec_with_size() { |
| 3479 | let width = 12345u32; |
| 3480 | let height = 54321u32; |
| 3481 | let mut config = Config::default(); |
| 3482 | config |
| 3483 | .executable_path |
| 3484 | .replace(Executable::Kernel(PathBuf::from("kernel"))); |
| 3485 | set_argument( |
| 3486 | &mut config, |
| 3487 | "single-touch", |
| 3488 | Some(&format!("/dev/single-touch-test:{}:{}", width, height)), |
| 3489 | ) |
| 3490 | .unwrap(); |
| 3491 | set_argument( |
| 3492 | &mut config, |
| 3493 | "trackpad", |
| 3494 | Some(&format!("/dev/single-touch-test:{}:{}", width, height)), |
| 3495 | ) |
| 3496 | .unwrap(); |
| 3497 | validate_arguments(&mut config).unwrap(); |
| 3498 | assert_eq!( |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3499 | config.virtio_single_touch.first().unwrap().get_size(), |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3500 | (width, height) |
| 3501 | ); |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3502 | assert_eq!( |
| 3503 | config.virtio_trackpad.first().unwrap().get_size(), |
| 3504 | (width, height) |
| 3505 | ); |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3506 | } |
| 3507 | |
| 3508 | #[cfg(feature = "gpu")] |
| 3509 | #[test] |
| 3510 | fn single_touch_spec_with_size_independent_from_gpu() { |
| 3511 | let touch_width = 12345u32; |
| 3512 | let touch_height = 54321u32; |
| 3513 | let display_width = 1234u32; |
| 3514 | let display_height = 5432u32; |
| 3515 | let mut config = Config::default(); |
| 3516 | config |
| 3517 | .executable_path |
| 3518 | .replace(Executable::Kernel(PathBuf::from("kernel"))); |
| 3519 | set_argument( |
| 3520 | &mut config, |
| 3521 | "single-touch", |
| 3522 | Some(&format!( |
| 3523 | "/dev/single-touch-test:{}:{}", |
| 3524 | touch_width, touch_height |
| 3525 | )), |
| 3526 | ) |
| 3527 | .unwrap(); |
| 3528 | set_argument( |
| 3529 | &mut config, |
| 3530 | "gpu", |
| 3531 | Some(&format!( |
| 3532 | "width={},height={}", |
| 3533 | display_width, display_height |
| 3534 | )), |
| 3535 | ) |
| 3536 | .unwrap(); |
| 3537 | validate_arguments(&mut config).unwrap(); |
| 3538 | assert_eq!( |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3539 | config.virtio_single_touch.first().unwrap().get_size(), |
Kaiyi Li | bccb4eb | 2020-02-06 17:53:11 -0800 | [diff] [blame] | 3540 | (touch_width, touch_height) |
| 3541 | ); |
| 3542 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 3543 | |
Daniel Norman | 5e23df7 | 2021-03-11 10:11:02 -0800 | [diff] [blame] | 3544 | #[test] |
| 3545 | fn virtio_switches() { |
| 3546 | let mut config = Config::default(); |
| 3547 | config |
| 3548 | .executable_path |
| 3549 | .replace(Executable::Kernel(PathBuf::from("kernel"))); |
| 3550 | set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap(); |
| 3551 | validate_arguments(&mut config).unwrap(); |
| 3552 | assert_eq!( |
Jorge E. Moreira | 6635ca4 | 2021-04-28 13:11:41 -0700 | [diff] [blame] | 3553 | config.virtio_switches.pop().unwrap(), |
Keiichi Watanabe | d56a2f4 | 2021-03-18 20:16:23 +0900 | [diff] [blame] | 3554 | PathBuf::from("/dev/switches-test") |
| 3555 | ); |
Daniel Norman | 5e23df7 | 2021-03-11 10:11:02 -0800 | [diff] [blame] | 3556 | } |
| 3557 | |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 3558 | #[cfg(feature = "gpu")] |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 3559 | #[test] |
Chia-I Wu | 91df656 | 2021-04-12 09:47:38 -0700 | [diff] [blame] | 3560 | fn parse_gpu_options_default_vulkan_support() { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 3561 | { |
| 3562 | let mut gpu_params: GpuParameters = Default::default(); |
| 3563 | assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok()); |
| 3564 | assert!(!gpu_params.use_vulkan); |
| 3565 | } |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 3566 | |
| 3567 | #[cfg(feature = "gfxstream")] |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 3568 | { |
| 3569 | let mut gpu_params: GpuParameters = Default::default(); |
| 3570 | assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok()); |
| 3571 | assert!(gpu_params.use_vulkan); |
| 3572 | } |
Chia-I Wu | 91df656 | 2021-04-12 09:47:38 -0700 | [diff] [blame] | 3573 | } |
| 3574 | |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 3575 | #[cfg(feature = "gpu")] |
Chia-I Wu | 91df656 | 2021-04-12 09:47:38 -0700 | [diff] [blame] | 3576 | #[test] |
Chia-I Wu | 6d473b3 | 2021-04-12 10:14:24 -0700 | [diff] [blame] | 3577 | fn parse_gpu_options_with_vulkan_specified() { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 3578 | { |
| 3579 | let mut gpu_params: GpuParameters = Default::default(); |
| 3580 | assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok()); |
| 3581 | assert!(gpu_params.use_vulkan); |
| 3582 | } |
| 3583 | { |
| 3584 | let mut gpu_params: GpuParameters = Default::default(); |
| 3585 | assert!( |
| 3586 | parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params) |
| 3587 | .is_ok() |
| 3588 | ); |
| 3589 | assert!(gpu_params.use_vulkan); |
| 3590 | } |
| 3591 | { |
| 3592 | let mut gpu_params: GpuParameters = Default::default(); |
| 3593 | assert!( |
| 3594 | parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params) |
| 3595 | .is_ok() |
| 3596 | ); |
| 3597 | assert!(gpu_params.use_vulkan); |
| 3598 | } |
| 3599 | { |
| 3600 | let mut gpu_params: GpuParameters = Default::default(); |
| 3601 | assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok()); |
| 3602 | assert!(!gpu_params.use_vulkan); |
| 3603 | } |
| 3604 | { |
| 3605 | let mut gpu_params: GpuParameters = Default::default(); |
| 3606 | assert!( |
| 3607 | parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params) |
| 3608 | .is_ok() |
| 3609 | ); |
| 3610 | assert!(!gpu_params.use_vulkan); |
| 3611 | } |
| 3612 | { |
| 3613 | let mut gpu_params: GpuParameters = Default::default(); |
| 3614 | assert!( |
| 3615 | parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params) |
| 3616 | .is_ok() |
| 3617 | ); |
| 3618 | assert!(!gpu_params.use_vulkan); |
| 3619 | } |
| 3620 | { |
| 3621 | let mut gpu_params: GpuParameters = Default::default(); |
| 3622 | assert!(parse_gpu_options( |
| 3623 | Some("backend=virglrenderer,vulkan=invalid_value"), |
| 3624 | &mut gpu_params |
| 3625 | ) |
| 3626 | .is_err()); |
| 3627 | } |
| 3628 | { |
| 3629 | let mut gpu_params: GpuParameters = Default::default(); |
| 3630 | assert!(parse_gpu_options( |
| 3631 | Some("vulkan=invalid_value,backend=virglrenderer"), |
| 3632 | &mut gpu_params |
| 3633 | ) |
| 3634 | .is_err()); |
| 3635 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 3636 | } |
| 3637 | |
| 3638 | #[cfg(all(feature = "gpu", feature = "gfxstream"))] |
| 3639 | #[test] |
| 3640 | fn parse_gpu_options_gfxstream_with_syncfd_specified() { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 3641 | { |
| 3642 | let mut gpu_params: GpuParameters = Default::default(); |
| 3643 | assert!( |
| 3644 | parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok() |
| 3645 | ); |
| 3646 | assert!(gpu_params.gfxstream_use_syncfd); |
| 3647 | } |
| 3648 | { |
| 3649 | let mut gpu_params: GpuParameters = Default::default(); |
| 3650 | assert!( |
| 3651 | parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok() |
| 3652 | ); |
| 3653 | assert!(gpu_params.gfxstream_use_syncfd); |
| 3654 | } |
| 3655 | { |
| 3656 | let mut gpu_params: GpuParameters = Default::default(); |
| 3657 | assert!( |
| 3658 | parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok() |
| 3659 | ); |
| 3660 | assert!(!gpu_params.gfxstream_use_syncfd); |
| 3661 | } |
| 3662 | { |
| 3663 | let mut gpu_params: GpuParameters = Default::default(); |
| 3664 | assert!( |
| 3665 | parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok() |
| 3666 | ); |
| 3667 | assert!(!gpu_params.gfxstream_use_syncfd); |
| 3668 | } |
| 3669 | { |
| 3670 | let mut gpu_params: GpuParameters = Default::default(); |
| 3671 | assert!(parse_gpu_options( |
| 3672 | Some("backend=gfxstream,syncfd=invalid_value"), |
| 3673 | &mut gpu_params |
| 3674 | ) |
| 3675 | .is_err()); |
| 3676 | } |
| 3677 | { |
| 3678 | let mut gpu_params: GpuParameters = Default::default(); |
| 3679 | assert!(parse_gpu_options( |
| 3680 | Some("syncfd=invalid_value,backend=gfxstream"), |
| 3681 | &mut gpu_params |
| 3682 | ) |
| 3683 | .is_err()); |
| 3684 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 3685 | } |
| 3686 | |
| 3687 | #[cfg(all(feature = "gpu", feature = "gfxstream"))] |
| 3688 | #[test] |
| 3689 | fn parse_gpu_options_not_gfxstream_with_syncfd_specified() { |
Jason Macnak | d659a0d | 2021-03-15 15:33:01 -0700 | [diff] [blame] | 3690 | { |
| 3691 | let mut gpu_params: GpuParameters = Default::default(); |
| 3692 | assert!( |
| 3693 | parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params) |
| 3694 | .is_err() |
| 3695 | ); |
| 3696 | } |
| 3697 | { |
| 3698 | let mut gpu_params: GpuParameters = Default::default(); |
| 3699 | assert!( |
| 3700 | parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params) |
| 3701 | .is_err() |
| 3702 | ); |
| 3703 | } |
| 3704 | } |
| 3705 | |
| 3706 | #[cfg(feature = "gpu")] |
| 3707 | #[test] |
| 3708 | fn parse_gpu_display_options_valid() { |
| 3709 | { |
| 3710 | let mut gpu_params: GpuParameters = Default::default(); |
| 3711 | assert!( |
| 3712 | parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok() |
| 3713 | ); |
| 3714 | assert_eq!(gpu_params.displays.len(), 1); |
| 3715 | assert_eq!(gpu_params.displays[0].width, 500); |
| 3716 | assert_eq!(gpu_params.displays[0].height, 600); |
| 3717 | } |
| 3718 | } |
| 3719 | |
| 3720 | #[cfg(feature = "gpu")] |
| 3721 | #[test] |
| 3722 | fn parse_gpu_display_options_invalid() { |
| 3723 | { |
| 3724 | let mut gpu_params: GpuParameters = Default::default(); |
| 3725 | assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err()); |
| 3726 | } |
| 3727 | { |
| 3728 | let mut gpu_params: GpuParameters = Default::default(); |
| 3729 | assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err()); |
| 3730 | } |
| 3731 | { |
| 3732 | let mut gpu_params: GpuParameters = Default::default(); |
| 3733 | assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err()); |
| 3734 | } |
| 3735 | { |
| 3736 | let mut gpu_params: GpuParameters = Default::default(); |
| 3737 | assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err()); |
| 3738 | } |
| 3739 | } |
| 3740 | |
| 3741 | #[cfg(feature = "gpu")] |
| 3742 | #[test] |
| 3743 | fn parse_gpu_options_and_gpu_display_options_valid() { |
| 3744 | { |
| 3745 | let mut gpu_params: GpuParameters = Default::default(); |
| 3746 | assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok()); |
| 3747 | assert!( |
| 3748 | parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok() |
| 3749 | ); |
| 3750 | assert_eq!(gpu_params.displays.len(), 2); |
| 3751 | assert_eq!(gpu_params.displays[0].width, 500); |
| 3752 | assert_eq!(gpu_params.displays[0].height, 600); |
| 3753 | assert_eq!(gpu_params.displays[1].width, 700); |
| 3754 | assert_eq!(gpu_params.displays[1].height, 800); |
| 3755 | } |
| 3756 | { |
| 3757 | let mut gpu_params: GpuParameters = Default::default(); |
| 3758 | assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok()); |
| 3759 | assert!( |
| 3760 | parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok() |
| 3761 | ); |
| 3762 | assert_eq!(gpu_params.displays.len(), 1); |
| 3763 | assert_eq!(gpu_params.displays[0].width, 700); |
| 3764 | assert_eq!(gpu_params.displays[0].height, 800); |
| 3765 | } |
Kaiyi Li | dd348a4 | 2020-07-13 11:49:46 -0700 | [diff] [blame] | 3766 | } |
Chuanxiao Dong | fd5626c | 2020-04-27 16:35:33 +0800 | [diff] [blame] | 3767 | |
| 3768 | #[test] |
| 3769 | fn parse_battery_vaild() { |
| 3770 | parse_battery_options(Some("type=goldfish")).expect("parse should have succeded"); |
| 3771 | } |
| 3772 | |
| 3773 | #[test] |
| 3774 | fn parse_battery_vaild_no_type() { |
| 3775 | parse_battery_options(None).expect("parse should have succeded"); |
| 3776 | } |
| 3777 | |
| 3778 | #[test] |
| 3779 | fn parse_battery_invaild_parameter() { |
| 3780 | parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed"); |
| 3781 | } |
| 3782 | |
| 3783 | #[test] |
| 3784 | fn parse_battery_invaild_type_value() { |
| 3785 | parse_battery_options(Some("type=xxx")).expect_err("parse should have failed"); |
| 3786 | } |
Mattias Nissler | de2c640 | 2021-10-21 12:05:29 +0000 | [diff] [blame] | 3787 | |
| 3788 | #[test] |
| 3789 | fn parse_stub_pci() { |
| 3790 | let params = parse_stub_pci_parameters(Some("0000:01:02.3,vendor=0xfffe,device=0xfffd,class=0xffc1c2,multifunction=true,subsystem_vendor=0xfffc,subsystem_device=0xfffb,revision=0xa")).unwrap(); |
| 3791 | assert_eq!(params.address.bus, 1); |
| 3792 | assert_eq!(params.address.dev, 2); |
| 3793 | assert_eq!(params.address.func, 3); |
| 3794 | assert_eq!(params.vendor_id, 0xfffe); |
| 3795 | assert_eq!(params.device_id, 0xfffd); |
| 3796 | assert_eq!(params.class as u8, PciClassCode::Other as u8); |
| 3797 | assert_eq!(params.subclass, 0xc1); |
| 3798 | assert_eq!(params.programming_interface, 0xc2); |
| 3799 | assert!(params.multifunction); |
| 3800 | assert_eq!(params.subsystem_vendor_id, 0xfffc); |
| 3801 | assert_eq!(params.subsystem_device_id, 0xfffb); |
| 3802 | assert_eq!(params.revision_id, 0xa); |
| 3803 | } |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 3804 | |
| 3805 | #[cfg(feature = "direct")] |
| 3806 | #[test] |
| 3807 | fn parse_direct_io_options_valid() { |
| 3808 | let params = parse_direct_io_options(Some("/dev/mem@1,100-110")).unwrap(); |
| 3809 | assert_eq!(params.path.to_str(), Some("/dev/mem")); |
Junichi Uekawa | b180f9c | 2021-12-07 09:21:36 +0900 | [diff] [blame] | 3810 | assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 }); |
| 3811 | assert_eq!(params.ranges[1], BusRange { base: 100, len: 11 }); |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 3812 | } |
| 3813 | |
| 3814 | #[cfg(feature = "direct")] |
| 3815 | #[test] |
| 3816 | fn parse_direct_io_options_hex() { |
| 3817 | let params = parse_direct_io_options(Some("/dev/mem@1,0x10,100-110,0x10-0x20")).unwrap(); |
| 3818 | assert_eq!(params.path.to_str(), Some("/dev/mem")); |
Junichi Uekawa | b180f9c | 2021-12-07 09:21:36 +0900 | [diff] [blame] | 3819 | assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 }); |
| 3820 | assert_eq!(params.ranges[1], BusRange { base: 0x10, len: 1 }); |
| 3821 | assert_eq!(params.ranges[2], BusRange { base: 100, len: 11 }); |
| 3822 | assert_eq!( |
| 3823 | params.ranges[3], |
| 3824 | BusRange { |
| 3825 | base: 0x10, |
| 3826 | len: 0x11 |
| 3827 | } |
| 3828 | ); |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 3829 | } |
| 3830 | |
| 3831 | #[cfg(feature = "direct")] |
| 3832 | #[test] |
| 3833 | fn parse_direct_io_options_invalid() { |
Junichi Uekawa | 4d31205 | 2021-12-08 16:59:38 +0900 | [diff] [blame] | 3834 | assert!(parse_direct_io_options(Some("/dev/mem@0y10")) |
| 3835 | .unwrap_err() |
| 3836 | .to_string() |
| 3837 | .contains("invalid base range value")); |
| 3838 | |
| 3839 | assert!(parse_direct_io_options(Some("/dev/mem@")) |
| 3840 | .unwrap_err() |
| 3841 | .to_string() |
| 3842 | .contains("invalid base range value")); |
Junichi Uekawa | b6a6e94 | 2021-12-07 05:49:30 +0900 | [diff] [blame] | 3843 | } |
Daniel Verkamp | 107edb3 | 2019-04-05 09:58:48 -0700 | [diff] [blame] | 3844 | } |