1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::path::PathBuf;
6
7 use devices::IommuDevType;
8 use devices::PciAddress;
9 use devices::SerialParameters;
10 use serde::Deserialize;
11 use serde::Serialize;
12 use serde_keyvalue::FromKeyValues;
13
14 use crate::crosvm::config::Config;
15
16 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
17 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
18 pub enum HypervisorKind {
19 Kvm {
20 device: Option<PathBuf>,
21 },
22 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
23 #[cfg(feature = "geniezone")]
24 Geniezone {
25 device: Option<PathBuf>,
26 },
27 #[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), feature = "gunyah"))]
28 Gunyah {
29 device: Option<PathBuf>,
30 },
31 }
32
33 #[cfg(feature = "audio")]
parse_ac97_options( #[allow(unused_variables)] ac97_params: &mut devices::Ac97Parameters, key: &str, #[allow(unused_variables)] value: &str, ) -> Result<(), String>34 pub fn parse_ac97_options(
35 #[allow(unused_variables)] ac97_params: &mut devices::Ac97Parameters,
36 key: &str,
37 #[allow(unused_variables)] value: &str,
38 ) -> Result<(), String> {
39 match key {
40 #[cfg(feature = "audio_cras")]
41 "client_type" => ac97_params
42 .set_client_type(value)
43 .map_err(|e| crate::crosvm::config::invalid_value_err(value, e)),
44 #[cfg(feature = "audio_cras")]
45 "socket_type" => ac97_params
46 .set_socket_type(value)
47 .map_err(|e| crate::crosvm::config::invalid_value_err(value, e)),
48 _ => Err(format!("unknown ac97 parameter {}", key)),
49 }
50 }
51
52 // Doesn't do anything on unix.
check_serial_params(_serial_params: &SerialParameters) -> Result<(), String>53 pub fn check_serial_params(_serial_params: &SerialParameters) -> Result<(), String> {
54 Ok(())
55 }
56
validate_config(_cfg: &mut Config) -> std::result::Result<(), String>57 pub fn validate_config(_cfg: &mut Config) -> std::result::Result<(), String> {
58 Ok(())
59 }
60
61 /// VFIO device structure for creating a new instance based on command line options.
62 #[derive(Serialize, Deserialize, FromKeyValues)]
63 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
64 pub struct VfioOption {
65 /// Path to the VFIO device.
66 pub path: PathBuf,
67
68 /// IOMMU type to use for this VFIO device.
69 #[serde(default)]
70 pub iommu: IommuDevType,
71
72 /// PCI address to use for the VFIO device in the guest.
73 /// If not specified, defaults to mirroring the host PCI address.
74 pub guest_address: Option<PciAddress>,
75
76 /// Apply special handling for Intel LPSS devices.
77 #[cfg(feature = "direct")]
78 #[serde(default)]
79 pub intel_lpss: bool,
80 }
81
82 #[cfg(test)]
83 mod tests {
84 use std::path::PathBuf;
85
86 use argh::FromArgs;
87
88 use super::*;
89 use crate::crosvm::config::from_key_values;
90 #[cfg(feature = "audio_cras")]
91 use crate::crosvm::config::parse_ac97_options;
92 use crate::crosvm::config::BindMount;
93 use crate::crosvm::config::DEFAULT_TOUCH_DEVICE_HEIGHT;
94 use crate::crosvm::config::DEFAULT_TOUCH_DEVICE_WIDTH;
95
96 #[cfg(feature = "audio_cras")]
97 #[test]
parse_ac97_socket_type()98 fn parse_ac97_socket_type() {
99 parse_ac97_options("socket_type=unified").expect("parse should have succeded");
100 parse_ac97_options("socket_type=legacy").expect("parse should have succeded");
101 }
102
103 #[test]
parse_coiommu_options()104 fn parse_coiommu_options() {
105 use std::time::Duration;
106
107 use devices::CoIommuParameters;
108 use devices::CoIommuUnpinPolicy;
109
110 // unpin_policy
111 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_policy=off").unwrap();
112 assert_eq!(
113 coiommu_params,
114 CoIommuParameters {
115 unpin_policy: CoIommuUnpinPolicy::Off,
116 ..Default::default()
117 }
118 );
119 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_policy=lru").unwrap();
120 assert_eq!(
121 coiommu_params,
122 CoIommuParameters {
123 unpin_policy: CoIommuUnpinPolicy::Lru,
124 ..Default::default()
125 }
126 );
127 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_policy=foo");
128 assert!(coiommu_params.is_err());
129
130 // unpin_interval
131 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_interval=42").unwrap();
132 assert_eq!(
133 coiommu_params,
134 CoIommuParameters {
135 unpin_interval: Duration::from_secs(42),
136 ..Default::default()
137 }
138 );
139 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_interval=foo");
140 assert!(coiommu_params.is_err());
141
142 // unpin_limit
143 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_limit=256").unwrap();
144 assert_eq!(
145 coiommu_params,
146 CoIommuParameters {
147 unpin_limit: Some(256),
148 ..Default::default()
149 }
150 );
151 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_limit=0");
152 assert!(coiommu_params.is_err());
153 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_limit=foo");
154 assert!(coiommu_params.is_err());
155
156 // unpin_gen_threshold
157 let coiommu_params =
158 from_key_values::<CoIommuParameters>("unpin_gen_threshold=32").unwrap();
159 assert_eq!(
160 coiommu_params,
161 CoIommuParameters {
162 unpin_gen_threshold: 32,
163 ..Default::default()
164 }
165 );
166 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_gen_threshold=foo");
167 assert!(coiommu_params.is_err());
168
169 // All together
170 let coiommu_params = from_key_values::<CoIommuParameters>(
171 "unpin_policy=lru,unpin_interval=90,unpin_limit=8,unpin_gen_threshold=64",
172 )
173 .unwrap();
174 assert_eq!(
175 coiommu_params,
176 CoIommuParameters {
177 unpin_policy: CoIommuUnpinPolicy::Lru,
178 unpin_interval: Duration::from_secs(90),
179 unpin_limit: Some(8),
180 unpin_gen_threshold: 64,
181 }
182 );
183
184 // invalid parameter
185 let coiommu_params = from_key_values::<CoIommuParameters>("unpin_invalid_param=0");
186 assert!(coiommu_params.is_err());
187 }
188
189 #[test]
parse_plugin_mount_valid()190 fn parse_plugin_mount_valid() {
191 let opt: BindMount = "/dev/null:/dev/zero:true".parse().unwrap();
192
193 assert_eq!(opt.src, PathBuf::from("/dev/null"));
194 assert_eq!(opt.dst, PathBuf::from("/dev/zero"));
195 assert!(opt.writable);
196 }
197
198 #[test]
parse_plugin_mount_valid_shorthand()199 fn parse_plugin_mount_valid_shorthand() {
200 let opt: BindMount = "/dev/null".parse().unwrap();
201 assert_eq!(opt.dst, PathBuf::from("/dev/null"));
202 assert!(!opt.writable);
203
204 let opt: BindMount = "/dev/null:/dev/zero".parse().unwrap();
205 assert_eq!(opt.dst, PathBuf::from("/dev/zero"));
206 assert!(!opt.writable);
207
208 let opt: BindMount = "/dev/null::true".parse().unwrap();
209 assert_eq!(opt.dst, PathBuf::from("/dev/null"));
210 assert!(opt.writable);
211 }
212
213 #[test]
single_touch_spec_and_track_pad_spec_default_size()214 fn single_touch_spec_and_track_pad_spec_default_size() {
215 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
216 &[],
217 &[
218 "--single-touch",
219 "/dev/single-touch-test",
220 "--trackpad",
221 "/dev/single-touch-test",
222 "/dev/null",
223 ],
224 )
225 .unwrap()
226 .try_into()
227 .unwrap();
228
229 assert_eq!(
230 config.virtio_single_touch.first().unwrap().get_size(),
231 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
232 );
233 assert_eq!(
234 config.virtio_trackpad.first().unwrap().get_size(),
235 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
236 );
237 }
238
239 #[cfg(feature = "gpu")]
240 #[test]
single_touch_spec_default_size_from_gpu()241 fn single_touch_spec_default_size_from_gpu() {
242 let width = 12345u32;
243 let height = 54321u32;
244
245 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
246 &[],
247 &[
248 "--single-touch",
249 "/dev/single-touch-test",
250 "--gpu",
251 &format!("width={},height={}", width, height),
252 "/dev/null",
253 ],
254 )
255 .unwrap()
256 .try_into()
257 .unwrap();
258
259 assert_eq!(
260 config.virtio_single_touch.first().unwrap().get_size(),
261 (width, height)
262 );
263 }
264
265 #[test]
single_touch_spec_and_track_pad_spec_with_size()266 fn single_touch_spec_and_track_pad_spec_with_size() {
267 let width = 12345u32;
268 let height = 54321u32;
269 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
270 &[],
271 &[
272 "--single-touch",
273 &format!("/dev/single-touch-test:{}:{}", width, height),
274 "--trackpad",
275 &format!("/dev/single-touch-test:{}:{}", width, height),
276 "/dev/null",
277 ],
278 )
279 .unwrap()
280 .try_into()
281 .unwrap();
282
283 assert_eq!(
284 config.virtio_single_touch.first().unwrap().get_size(),
285 (width, height)
286 );
287 assert_eq!(
288 config.virtio_trackpad.first().unwrap().get_size(),
289 (width, height)
290 );
291 }
292
293 #[cfg(feature = "gpu")]
294 #[test]
single_touch_spec_with_size_independent_from_gpu()295 fn single_touch_spec_with_size_independent_from_gpu() {
296 let touch_width = 12345u32;
297 let touch_height = 54321u32;
298 let display_width = 1234u32;
299 let display_height = 5432u32;
300 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
301 &[],
302 &[
303 "--single-touch",
304 &format!("/dev/single-touch-test:{}:{}", touch_width, touch_height),
305 "--gpu",
306 &format!("width={},height={}", display_width, display_height),
307 "/dev/null",
308 ],
309 )
310 .unwrap()
311 .try_into()
312 .unwrap();
313
314 assert_eq!(
315 config.virtio_single_touch.first().unwrap().get_size(),
316 (touch_width, touch_height)
317 );
318 }
319
320 #[test]
virtio_switches()321 fn virtio_switches() {
322 let mut config: Config = crate::crosvm::cmdline::RunCommand::from_args(
323 &[],
324 &["--switches", "/dev/switches-test", "/dev/null"],
325 )
326 .unwrap()
327 .try_into()
328 .unwrap();
329
330 assert_eq!(
331 config.virtio_switches.pop().unwrap(),
332 PathBuf::from("/dev/switches-test")
333 );
334 }
335
336 #[test]
vfio_pci_path()337 fn vfio_pci_path() {
338 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
339 &[],
340 &["--vfio", "/path/to/dev", "/dev/null"],
341 )
342 .unwrap()
343 .try_into()
344 .unwrap();
345
346 let vfio = config.vfio.first().unwrap();
347
348 assert_eq!(vfio.path, PathBuf::from("/path/to/dev"));
349 assert_eq!(vfio.iommu, IommuDevType::NoIommu);
350 assert_eq!(vfio.guest_address, None);
351 }
352
353 #[test]
vfio_pci_path_coiommu()354 fn vfio_pci_path_coiommu() {
355 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
356 &[],
357 &["--vfio", "/path/to/dev,iommu=coiommu", "/dev/null"],
358 )
359 .unwrap()
360 .try_into()
361 .unwrap();
362
363 let vfio = config.vfio.first().unwrap();
364
365 assert_eq!(vfio.path, PathBuf::from("/path/to/dev"));
366 assert_eq!(vfio.iommu, IommuDevType::CoIommu);
367 assert_eq!(vfio.guest_address, None);
368 }
369
370 #[test]
vfio_pci_path_viommu_guest_address()371 fn vfio_pci_path_viommu_guest_address() {
372 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
373 &[],
374 &[
375 "--vfio",
376 "/path/to/dev,iommu=viommu,guest-address=42:15.4",
377 "/dev/null",
378 ],
379 )
380 .unwrap()
381 .try_into()
382 .unwrap();
383
384 let vfio = config.vfio.first().unwrap();
385
386 assert_eq!(vfio.path, PathBuf::from("/path/to/dev"));
387 assert_eq!(vfio.iommu, IommuDevType::VirtioIommu);
388 assert_eq!(
389 vfio.guest_address,
390 Some(PciAddress::new(0, 0x42, 0x15, 4).unwrap())
391 );
392 }
393
394 #[test]
395 #[cfg(feature = "direct")]
vfio_pci_intel_lpss()396 fn vfio_pci_intel_lpss() {
397 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
398 &[],
399 &["--vfio", "/path/to/dev,intel-lpss=true", "/dev/null"],
400 )
401 .unwrap()
402 .try_into()
403 .unwrap();
404
405 let vfio = config.vfio.first().unwrap();
406
407 assert_eq!(vfio.intel_lpss, true);
408 }
409
410 #[test]
vfio_platform()411 fn vfio_platform() {
412 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
413 &[],
414 &["--vfio-platform", "/path/to/dev", "/dev/null"],
415 )
416 .unwrap()
417 .try_into()
418 .unwrap();
419
420 let vfio = config.vfio.first().unwrap();
421
422 assert_eq!(vfio.path, PathBuf::from("/path/to/dev"));
423 }
424 }
425