1use crate::parsers::ARG_MACHINE;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::common::*;
8use crate::machine_type::{MachineTypeAarch64, MachineTypeX86_64};
9use crate::parsers::{DELIM_COLON, DELIM_COMMA};
10use crate::qao;
11use crate::shell_string::{ShellString, ShellStringError};
12use crate::to_command::{ToArg, ToCommand};
13
14const KEY_ACCEL: &str = "accel=";
15const KEY_TYPE: &str = "type=";
16const KEY_VMPORT: &str = "vmport=";
17const KEY_DUMP_GUEST_CORE: &str = "dump-guest-core=";
18const KEY_MEM_MERGE: &str = "mem-merge=";
19const KEY_AES_KEY_WRAP: &str = "aes-key-wrap=";
20const KEY_DEA_KEY_WRAP: &str = "dea-key-wrap=";
21const KEY_NVDIMM: &str = "nvdimm=";
22const KEY_MEMORY_ENCRYPTION: &str = "memory-encryption=";
23const KEY_HMAT: &str = "hmat=";
24const KEY_AUX_RAM_SHARE: &str = "aux-ram-share=";
25const KEY_MEMORY_BACKEND: &str = "memory-backend=";
26
27#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Arbitrary)]
29pub enum Granularity {
30 #[default]
31 G256,
32 G512,
33 G1k,
34 G2k,
35 G4k,
36 G8k,
37 G16k,
38}
39
40impl ToArg for Granularity {
41 fn to_arg(&self) -> &str {
42 match self {
43 Granularity::G256 => "256",
44 Granularity::G512 => "512",
45 Granularity::G1k => "1k",
46 Granularity::G2k => "2k",
47 Granularity::G4k => "4k",
48 Granularity::G8k => "8k",
49 Granularity::G16k => "16k",
50 }
51 }
52}
53#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
55pub struct CxlFmw {
56 targets: Vec<String>,
57 size: String,
58 interleave_granularity: Option<Granularity>,
59}
60
61#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
63pub struct SmpCache {
64 cache: String,
65 topology: String,
66}
67
68#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
70pub enum MachineType {
71 X86_64(MachineTypeX86_64),
72 Aarch(MachineTypeAarch64),
73}
74
75impl ToArg for MachineType {
76 fn to_arg(&self) -> &str {
77 match self {
78 MachineType::X86_64(mt) => mt.to_arg(),
79 MachineType::Aarch(mt) => mt.to_arg(),
80 }
81 }
82}
83#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
97pub struct Machine<T> {
98 machine_type: T,
100
101 accel: Option<Vec<AccelType>>,
107
108 vmport: Option<OnOffAuto>,
112
113 dump_guest_core: Option<OnOffDefaultOn>,
115
116 mem_merge: Option<OnOffDefaultOn>,
120
121 aes_key_wrap: Option<OnOffDefaultOn>,
126
127 dea_key_wrap: Option<OnOffDefaultOn>,
132
133 nvdimm: Option<OnOffDefaultOff>,
135
136 memory_encryption: Option<ShellString>, hmat: Option<OnOffDefaultOff>,
142
143 aux_ram_share: Option<OnOffDefaultOff>,
150
151 memory_backend: Option<ShellString>, }
203
204#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
205pub struct MachineX86_64 {
206 pub m: Machine<MachineTypeX86_64>,
208}
209
210impl ToCommand for MachineX86_64 {
211 fn command(&self) -> String {
212 ARG_MACHINE.to_string()
213 }
214
215 fn to_args(&self) -> Vec<String> {
216 let mut args = vec![self.m.machine_type.to_arg().to_string()];
217
218 if let Some(accels) = &self.m.accel {
219 let accel_strs: Vec<&str> = accels.iter().map(|a| a.to_arg()).collect();
220 args.push(format!("{}{}", KEY_ACCEL, accel_strs.join(":")));
221 }
222 qao!(&self.m.vmport, args, KEY_VMPORT);
223 qao!(&self.m.dump_guest_core, args, KEY_DUMP_GUEST_CORE);
224 qao!(&self.m.mem_merge, args, KEY_MEM_MERGE);
225 qao!(&self.m.aes_key_wrap, args, KEY_AES_KEY_WRAP);
226 qao!(&self.m.dea_key_wrap, args, KEY_DEA_KEY_WRAP);
227 qao!(&self.m.nvdimm, args, KEY_NVDIMM);
228 if let Some(memory_encryption) = &self.m.memory_encryption {
229 args.push(format!("{}{}", KEY_MEMORY_ENCRYPTION, memory_encryption.as_ref()));
230 }
231 qao!(&self.m.hmat, args, KEY_HMAT);
232 qao!(&self.m.aux_ram_share, args, KEY_AUX_RAM_SHARE);
233 if let Some(memory_backend) = &self.m.memory_backend {
234 args.push(format!("{}{}", KEY_MEMORY_BACKEND, memory_backend.as_ref()));
235 }
236
237 vec![args.join(DELIM_COMMA)]
255 }
256}
257
258impl FromStr for MachineX86_64 {
259 type Err = ShellStringError;
260
261 fn from_str(s: &str) -> Result<Self, Self::Err> {
262 parse_machine_x86_64(s).map_err(ShellStringError::new)
263 }
264}
265
266fn parse_machine_x86_64(s: &str) -> Result<MachineX86_64, String> {
267 let mut parts = s.split(DELIM_COMMA);
268 let first = parts.next().ok_or_else(|| "empty machine argument".to_string())?;
269
270 let mut machine_type = None;
271 let mut accel = None;
272 let mut vmport = None;
273 let mut dump_guest_core = None;
274 let mut mem_merge = None;
275 let mut aes_key_wrap = None;
276 let mut dea_key_wrap = None;
277 let mut nvdimm = None;
278 let mut memory_encryption = None;
279 let mut hmat = None;
280 let mut aux_ram_share = None;
281 let mut memory_backend = None;
282
283 if let Some(value) = first.strip_prefix(KEY_TYPE) {
284 machine_type = Some(parse_machine_type(value)?);
285 } else if first.contains('=') {
286 parse_machine_option(
287 first,
288 &mut machine_type,
289 &mut accel,
290 &mut vmport,
291 &mut dump_guest_core,
292 &mut mem_merge,
293 &mut aes_key_wrap,
294 &mut dea_key_wrap,
295 &mut nvdimm,
296 &mut memory_encryption,
297 &mut hmat,
298 &mut aux_ram_share,
299 &mut memory_backend,
300 )?;
301 } else {
302 machine_type = Some(parse_machine_type(first)?);
303 }
304
305 for part in parts {
306 parse_machine_option(
307 part,
308 &mut machine_type,
309 &mut accel,
310 &mut vmport,
311 &mut dump_guest_core,
312 &mut mem_merge,
313 &mut aes_key_wrap,
314 &mut dea_key_wrap,
315 &mut nvdimm,
316 &mut memory_encryption,
317 &mut hmat,
318 &mut aux_ram_share,
319 &mut memory_backend,
320 )?;
321 }
322
323 let machine_type = machine_type.ok_or_else(|| "machine type is required".to_string())?;
324
325 Ok(MachineX86_64 {
326 m: Machine {
327 machine_type,
328 accel,
329 vmport,
330 dump_guest_core,
331 mem_merge,
332 aes_key_wrap,
333 dea_key_wrap,
334 nvdimm,
335 memory_encryption,
336 hmat,
337 aux_ram_share,
338 memory_backend,
339 },
340 })
341}
342
343#[allow(clippy::too_many_arguments)]
344fn parse_machine_option(
345 part: &str,
346 machine_type: &mut Option<MachineTypeX86_64>,
347 accel: &mut Option<Vec<AccelType>>,
348 vmport: &mut Option<OnOffAuto>,
349 dump_guest_core: &mut Option<OnOffDefaultOn>,
350 mem_merge: &mut Option<OnOffDefaultOn>,
351 aes_key_wrap: &mut Option<OnOffDefaultOn>,
352 dea_key_wrap: &mut Option<OnOffDefaultOn>,
353 nvdimm: &mut Option<OnOffDefaultOff>,
354 memory_encryption: &mut Option<ShellString>,
355 hmat: &mut Option<OnOffDefaultOff>,
356 aux_ram_share: &mut Option<OnOffDefaultOff>,
357 memory_backend: &mut Option<ShellString>,
358) -> Result<(), String> {
359 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid machine option: {part}"))?;
360 match key {
361 "type" => *machine_type = Some(parse_machine_type(value)?),
362 "accel" => {
363 let accels = value
364 .split(DELIM_COLON)
365 .map(|v| v.parse::<AccelType>().map_err(|_| format!("invalid accel value: {v}")))
366 .collect::<Result<Vec<_>, _>>()?;
367 *accel = Some(accels);
368 }
369 "vmport" => *vmport = Some(value.parse::<OnOffAuto>().map_err(|_| format!("invalid vmport value: {value}"))?),
370 "dump-guest-core" => *dump_guest_core = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid dump-guest-core value: {value}"))?),
371 "mem-merge" => *mem_merge = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid mem-merge value: {value}"))?),
372 "aes-key-wrap" => *aes_key_wrap = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid aes-key-wrap value: {value}"))?),
373 "dea-key-wrap" => *dea_key_wrap = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid dea-key-wrap value: {value}"))?),
374 "nvdimm" => *nvdimm = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid nvdimm value: {value}"))?),
375 "memory-encryption" => *memory_encryption = Some(ShellString::new(value)),
376 "hmat" => *hmat = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid hmat value: {value}"))?),
377 "aux-ram-share" => *aux_ram_share = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid aux-ram-share value: {value}"))?),
378 "memory-backend" => *memory_backend = Some(ShellString::new(value)),
379 other => return Err(format!("unsupported machine option: {other}")),
380 }
381
382 Ok(())
383}
384
385fn parse_machine_type(value: &str) -> Result<MachineTypeX86_64, String> {
386 value.parse::<MachineTypeX86_64>()
387}
388
389#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
390pub struct MachineAarch64 {
391 pub m: Machine<MachineTypeAarch64>,
392}
393impl ToCommand for MachineAarch64 {
394 fn command(&self) -> String {
395 ARG_MACHINE.to_string()
396 }
397
398 fn to_args(&self) -> Vec<String> {
399 let mut args = vec![self.m.machine_type.to_arg().to_string()];
400
401 if let Some(accels) = &self.m.accel {
402 let accel_strs: Vec<&str> = accels.iter().map(|a| a.to_arg()).collect();
403 args.push(format!("{}{}", KEY_ACCEL, accel_strs.join(":")));
404 }
405 qao!(&self.m.dump_guest_core, args, KEY_DUMP_GUEST_CORE);
406 qao!(&self.m.mem_merge, args, KEY_MEM_MERGE);
407 qao!(&self.m.nvdimm, args, KEY_NVDIMM);
408 if let Some(memory_backend) = &self.m.memory_backend {
409 args.push(format!("{}{}", KEY_MEMORY_BACKEND, memory_backend.as_ref()));
410 }
411
412 vec![args.join(DELIM_COMMA)]
413 }
414}
415impl FromStr for MachineAarch64 {
416 type Err = ShellStringError;
417
418 fn from_str(s: &str) -> Result<Self, Self::Err> {
419 parse_machine_aarch64(s).map_err(ShellStringError::new)
420 }
421}
422
423fn parse_machine_aarch64(s: &str) -> Result<MachineAarch64, String> {
424 let mut parts = s.split(DELIM_COMMA);
425 let first = parts.next().ok_or_else(|| "empty machine argument".to_string())?;
426
427 let mut machine_type = None;
428 let mut accel = None;
429 let mut dump_guest_core = None;
430 let mut mem_merge = None;
431 let mut nvdimm = None;
432 let mut memory_backend = None;
433
434 if let Some(value) = first.strip_prefix(KEY_TYPE) {
435 machine_type = Some(parse_machine_type_aarch64(value)?);
436 } else if first.contains('=') {
437 parse_machine_aarch64_option(first, &mut machine_type, &mut accel, &mut dump_guest_core, &mut mem_merge, &mut nvdimm, &mut memory_backend)?;
438 } else {
439 machine_type = Some(parse_machine_type_aarch64(first)?);
440 }
441
442 for part in parts {
443 parse_machine_aarch64_option(part, &mut machine_type, &mut accel, &mut dump_guest_core, &mut mem_merge, &mut nvdimm, &mut memory_backend)?;
444 }
445
446 let machine_type = machine_type.ok_or_else(|| "machine type is required".to_string())?;
447
448 Ok(MachineAarch64 {
449 m: Machine {
450 machine_type,
451 accel,
452 vmport: None,
453 dump_guest_core,
454 mem_merge,
455 aes_key_wrap: None,
456 dea_key_wrap: None,
457 nvdimm,
458 memory_encryption: None,
459 hmat: None,
460 aux_ram_share: None,
461 memory_backend,
462 },
463 })
464}
465
466fn parse_machine_aarch64_option(
467 part: &str,
468 machine_type: &mut Option<MachineTypeAarch64>,
469 accel: &mut Option<Vec<AccelType>>,
470 dump_guest_core: &mut Option<OnOffDefaultOn>,
471 mem_merge: &mut Option<OnOffDefaultOn>,
472 nvdimm: &mut Option<OnOffDefaultOff>,
473 memory_backend: &mut Option<ShellString>,
474) -> Result<(), String> {
475 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid machine option: {part}"))?;
476 match key {
477 "type" => *machine_type = Some(parse_machine_type_aarch64(value)?),
478 "accel" => {
479 let accels = value
480 .split(DELIM_COLON)
481 .map(|v| v.parse::<AccelType>().map_err(|_| format!("invalid accel value: {v}")))
482 .collect::<Result<Vec<_>, _>>()?;
483 *accel = Some(accels);
484 }
485 "dump-guest-core" => *dump_guest_core = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid dump-guest-core value: {value}"))?),
486 "mem-merge" => *mem_merge = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid mem-merge value: {value}"))?),
487 "nvdimm" => *nvdimm = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid nvdimm value: {value}"))?),
488 "memory-backend" => *memory_backend = Some(ShellString::new(value)),
489 other => return Err(format!("unsupported aarch64 machine option: {other}")),
490 }
491
492 Ok(())
493}
494
495fn parse_machine_type_aarch64(value: &str) -> Result<MachineTypeAarch64, String> {
496 value.parse::<MachineTypeAarch64>().map_err(|_| format!("{value} is not a supported aarch64 machine type"))
497}