1use crate::parsers::ARG_MACHINE;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::args::machine_type::{MachineTypeAarch64, MachineTypeX86_64};
8use crate::common::*;
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_CONFIDENTIAL_GUEST_SUPPORT: &str = "confidential-guest-support=";
24const KEY_HMAT: &str = "hmat=";
25const KEY_SPCR: &str = "spcr=";
26const KEY_AUX_RAM_SHARE: &str = "aux-ram-share=";
27const KEY_MEMORY_BACKEND: &str = "memory-backend=";
28const KEY_IGVM_CFG: &str = "igvm-cfg=";
29
30#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Arbitrary)]
32pub enum Granularity {
33 #[default]
34 G256,
35 G512,
36 G1k,
37 G2k,
38 G4k,
39 G8k,
40 G16k,
41}
42
43impl ToArg for Granularity {
44 fn to_arg(&self) -> &str {
45 match self {
46 Granularity::G256 => "256",
47 Granularity::G512 => "512",
48 Granularity::G1k => "1k",
49 Granularity::G2k => "2k",
50 Granularity::G4k => "4k",
51 Granularity::G8k => "8k",
52 Granularity::G16k => "16k",
53 }
54 }
55}
56
57impl FromStr for Granularity {
58 type Err = String;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 match s {
62 "256" => Ok(Self::G256),
63 "512" => Ok(Self::G512),
64 "1k" => Ok(Self::G1k),
65 "2k" => Ok(Self::G2k),
66 "4k" => Ok(Self::G4k),
67 "8k" => Ok(Self::G8k),
68 "16k" => Ok(Self::G16k),
69 other => Err(format!("invalid cxl-fmw interleave-granularity: {other}")),
70 }
71 }
72}
73
74#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
76pub struct CxlFmw {
77 targets: Vec<String>,
78 size: String,
79 interleave_granularity: Option<Granularity>,
80}
81
82#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
84pub struct SmpCache {
85 cache: String,
86 topology: String,
87}
88
89#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
91pub struct SgxEpc {
92 memdev: ShellString,
93 node: usize,
94}
95
96#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
98pub enum MachineType {
99 X86_64(MachineTypeX86_64),
100 Aarch(MachineTypeAarch64),
101}
102
103impl ToArg for MachineType {
104 fn to_arg(&self) -> &str {
105 match self {
106 MachineType::X86_64(mt) => mt.to_arg(),
107 MachineType::Aarch(mt) => mt.to_arg(),
108 }
109 }
110}
111#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
125pub struct Machine<T> {
126 machine_type: T,
128
129 accel: Option<Vec<AccelType>>,
135
136 vmport: Option<OnOffAuto>,
140
141 dump_guest_core: Option<OnOffDefaultOn>,
143
144 mem_merge: Option<OnOffDefaultOn>,
148
149 aes_key_wrap: Option<OnOffDefaultOn>,
154
155 dea_key_wrap: Option<OnOffDefaultOn>,
160
161 nvdimm: Option<OnOffDefaultOff>,
163
164 memory_encryption: Option<ShellString>,
166
167 confidential_guest_support: Option<ShellString>,
169
170 hmat: Option<OnOffDefaultOff>,
173
174 spcr: Option<OnOffDefaultOn>,
177
178 aux_ram_share: Option<OnOffDefaultOff>,
185
186 memory_backend: Option<ShellString>,
189
190 cxl_fmw: Option<Vec<CxlFmw>>,
192
193 igvm_cfg: Option<ShellString>,
195
196 sgx_epc: Option<Vec<SgxEpc>>,
198}
199
200#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
201pub struct MachineX86_64 {
202 pub m: Machine<MachineTypeX86_64>,
204}
205
206impl ToCommand for MachineX86_64 {
207 fn command(&self) -> String {
208 ARG_MACHINE.to_string()
209 }
210
211 fn to_args(&self) -> Vec<String> {
212 let mut args = vec![self.m.machine_type.to_arg().to_string()];
213
214 if let Some(accels) = &self.m.accel {
215 let accel_strs: Vec<&str> = accels.iter().map(|a| a.to_arg()).collect();
216 args.push(format!("{}{}", KEY_ACCEL, accel_strs.join(":")));
217 }
218 qao!(&self.m.vmport, args, KEY_VMPORT);
219 qao!(&self.m.dump_guest_core, args, KEY_DUMP_GUEST_CORE);
220 qao!(&self.m.mem_merge, args, KEY_MEM_MERGE);
221 qao!(&self.m.aes_key_wrap, args, KEY_AES_KEY_WRAP);
222 qao!(&self.m.dea_key_wrap, args, KEY_DEA_KEY_WRAP);
223 qao!(&self.m.nvdimm, args, KEY_NVDIMM);
224 if let Some(memory_encryption) = &self.m.memory_encryption {
225 args.push(format!("{}{}", KEY_MEMORY_ENCRYPTION, memory_encryption.as_ref()));
226 }
227 if let Some(confidential_guest_support) = &self.m.confidential_guest_support {
228 args.push(format!("{}{}", KEY_CONFIDENTIAL_GUEST_SUPPORT, confidential_guest_support.as_ref()));
229 }
230 qao!(&self.m.hmat, args, KEY_HMAT);
231 qao!(&self.m.spcr, args, KEY_SPCR);
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 push_cxl_fmw_args(&mut args, &self.m.cxl_fmw);
237 if let Some(igvm_cfg) = &self.m.igvm_cfg {
238 args.push(format!("{}{}", KEY_IGVM_CFG, igvm_cfg.as_ref()));
239 }
240 if let Some(sgx_epcs) = &self.m.sgx_epc {
241 for (idx, sgx_epc) in sgx_epcs.iter().enumerate() {
242 args.push(format!("sgx-epc.{}.memdev={}", idx, sgx_epc.memdev.as_ref()));
243 args.push(format!("sgx-epc.{}.node={}", idx, sgx_epc.node));
244 }
245 }
246
247 vec![args.join(DELIM_COMMA)]
248 }
249}
250
251impl FromStr for MachineX86_64 {
252 type Err = ShellStringError;
253
254 fn from_str(s: &str) -> Result<Self, Self::Err> {
255 parse_machine_x86_64(s).map_err(ShellStringError::new)
256 }
257}
258
259fn parse_machine_x86_64(s: &str) -> Result<MachineX86_64, String> {
260 let mut parts = s.split(DELIM_COMMA);
261 let first = parts.next().ok_or_else(|| "empty machine argument".to_string())?;
262
263 let mut machine_type = None;
264 let mut accel = None;
265 let mut vmport = None;
266 let mut dump_guest_core = None;
267 let mut mem_merge = None;
268 let mut aes_key_wrap = None;
269 let mut dea_key_wrap = None;
270 let mut nvdimm = None;
271 let mut memory_encryption = None;
272 let mut confidential_guest_support = None;
273 let mut hmat = None;
274 let mut spcr = None;
275 let mut aux_ram_share = None;
276 let mut memory_backend = None;
277 let mut cxl_fmw = std::collections::BTreeMap::<usize, CxlFmwParts>::new();
278 let mut igvm_cfg = None;
279 let mut sgx_epc = std::collections::BTreeMap::<usize, (Option<ShellString>, Option<usize>)>::new();
280
281 if let Some(value) = first.strip_prefix(KEY_TYPE) {
282 machine_type = Some(parse_machine_type(value)?);
283 } else if first.contains('=') {
284 parse_machine_option(
285 first,
286 &mut machine_type,
287 &mut accel,
288 &mut vmport,
289 &mut dump_guest_core,
290 &mut mem_merge,
291 &mut aes_key_wrap,
292 &mut dea_key_wrap,
293 &mut nvdimm,
294 &mut memory_encryption,
295 &mut confidential_guest_support,
296 &mut hmat,
297 &mut spcr,
298 &mut aux_ram_share,
299 &mut memory_backend,
300 &mut cxl_fmw,
301 &mut igvm_cfg,
302 &mut sgx_epc,
303 )?;
304 } else {
305 machine_type = Some(parse_machine_type(first)?);
306 }
307
308 for part in parts {
309 parse_machine_option(
310 part,
311 &mut machine_type,
312 &mut accel,
313 &mut vmport,
314 &mut dump_guest_core,
315 &mut mem_merge,
316 &mut aes_key_wrap,
317 &mut dea_key_wrap,
318 &mut nvdimm,
319 &mut memory_encryption,
320 &mut confidential_guest_support,
321 &mut hmat,
322 &mut spcr,
323 &mut aux_ram_share,
324 &mut memory_backend,
325 &mut cxl_fmw,
326 &mut igvm_cfg,
327 &mut sgx_epc,
328 )?;
329 }
330
331 let machine_type = machine_type.ok_or_else(|| "machine type is required".to_string())?;
332 let cxl_fmw = build_cxl_fmw(cxl_fmw)?;
333 let sgx_epc = build_sgx_epc(sgx_epc)?;
334
335 Ok(MachineX86_64 {
336 m: Machine {
337 machine_type,
338 accel,
339 vmport,
340 dump_guest_core,
341 mem_merge,
342 aes_key_wrap,
343 dea_key_wrap,
344 nvdimm,
345 memory_encryption,
346 confidential_guest_support,
347 hmat,
348 spcr,
349 aux_ram_share,
350 memory_backend,
351 cxl_fmw,
352 igvm_cfg,
353 sgx_epc,
354 },
355 })
356}
357
358#[allow(clippy::too_many_arguments)]
359fn parse_machine_option(
360 part: &str,
361 machine_type: &mut Option<MachineTypeX86_64>,
362 accel: &mut Option<Vec<AccelType>>,
363 vmport: &mut Option<OnOffAuto>,
364 dump_guest_core: &mut Option<OnOffDefaultOn>,
365 mem_merge: &mut Option<OnOffDefaultOn>,
366 aes_key_wrap: &mut Option<OnOffDefaultOn>,
367 dea_key_wrap: &mut Option<OnOffDefaultOn>,
368 nvdimm: &mut Option<OnOffDefaultOff>,
369 memory_encryption: &mut Option<ShellString>,
370 confidential_guest_support: &mut Option<ShellString>,
371 hmat: &mut Option<OnOffDefaultOff>,
372 spcr: &mut Option<OnOffDefaultOn>,
373 aux_ram_share: &mut Option<OnOffDefaultOff>,
374 memory_backend: &mut Option<ShellString>,
375 cxl_fmw: &mut std::collections::BTreeMap<usize, CxlFmwParts>,
376 igvm_cfg: &mut Option<ShellString>,
377 sgx_epc: &mut std::collections::BTreeMap<usize, (Option<ShellString>, Option<usize>)>,
378) -> Result<(), String> {
379 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid machine option: {part}"))?;
380 match key {
381 "type" => *machine_type = Some(parse_machine_type(value)?),
382 "accel" => {
383 let accels = value
384 .split(DELIM_COLON)
385 .map(|v| v.parse::<AccelType>().map_err(|_| format!("invalid accel value: {v}")))
386 .collect::<Result<Vec<_>, _>>()?;
387 *accel = Some(accels);
388 }
389 "vmport" => *vmport = Some(value.parse::<OnOffAuto>().map_err(|_| format!("invalid vmport value: {value}"))?),
390 "dump-guest-core" => *dump_guest_core = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid dump-guest-core value: {value}"))?),
391 "mem-merge" => *mem_merge = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid mem-merge value: {value}"))?),
392 "aes-key-wrap" => *aes_key_wrap = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid aes-key-wrap value: {value}"))?),
393 "dea-key-wrap" => *dea_key_wrap = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid dea-key-wrap value: {value}"))?),
394 "nvdimm" => *nvdimm = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid nvdimm value: {value}"))?),
395 "memory-encryption" => *memory_encryption = Some(ShellString::new(value)),
396 "confidential-guest-support" => *confidential_guest_support = Some(ShellString::new(value)),
397 "hmat" => *hmat = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid hmat value: {value}"))?),
398 "spcr" => *spcr = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid spcr value: {value}"))?),
399 "aux-ram-share" => *aux_ram_share = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid aux-ram-share value: {value}"))?),
400 "memory-backend" => *memory_backend = Some(ShellString::new(value)),
401 _ if key.starts_with("cxl-fmw.") => parse_cxl_fmw_option(key, value, cxl_fmw)?,
402 "igvm-cfg" => *igvm_cfg = Some(ShellString::new(value)),
403 _ if key.starts_with("sgx-epc.") => parse_sgx_epc_option(key, value, sgx_epc)?,
404 other => return Err(format!("unsupported machine option: {other}")),
405 }
406
407 Ok(())
408}
409
410fn parse_machine_type(value: &str) -> Result<MachineTypeX86_64, String> {
411 value.parse::<MachineTypeX86_64>()
412}
413
414#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
415pub struct MachineAarch64 {
416 pub m: Machine<MachineTypeAarch64>,
417}
418impl ToCommand for MachineAarch64 {
419 fn command(&self) -> String {
420 ARG_MACHINE.to_string()
421 }
422
423 fn to_args(&self) -> Vec<String> {
424 let mut args = vec![self.m.machine_type.to_arg().to_string()];
425
426 if let Some(accels) = &self.m.accel {
427 let accel_strs: Vec<&str> = accels.iter().map(|a| a.to_arg()).collect();
428 args.push(format!("{}{}", KEY_ACCEL, accel_strs.join(":")));
429 }
430 qao!(&self.m.dump_guest_core, args, KEY_DUMP_GUEST_CORE);
431 qao!(&self.m.mem_merge, args, KEY_MEM_MERGE);
432 qao!(&self.m.nvdimm, args, KEY_NVDIMM);
433 qao!(&self.m.spcr, args, KEY_SPCR);
434 if let Some(confidential_guest_support) = &self.m.confidential_guest_support {
435 args.push(format!("{}{}", KEY_CONFIDENTIAL_GUEST_SUPPORT, confidential_guest_support.as_ref()));
436 }
437 if let Some(memory_backend) = &self.m.memory_backend {
438 args.push(format!("{}{}", KEY_MEMORY_BACKEND, memory_backend.as_ref()));
439 }
440 push_cxl_fmw_args(&mut args, &self.m.cxl_fmw);
441 if let Some(igvm_cfg) = &self.m.igvm_cfg {
442 args.push(format!("{}{}", KEY_IGVM_CFG, igvm_cfg.as_ref()));
443 }
444 if let Some(sgx_epcs) = &self.m.sgx_epc {
445 for (idx, sgx_epc) in sgx_epcs.iter().enumerate() {
446 args.push(format!("sgx-epc.{}.memdev={}", idx, sgx_epc.memdev.as_ref()));
447 args.push(format!("sgx-epc.{}.node={}", idx, sgx_epc.node));
448 }
449 }
450
451 vec![args.join(DELIM_COMMA)]
452 }
453}
454impl FromStr for MachineAarch64 {
455 type Err = ShellStringError;
456
457 fn from_str(s: &str) -> Result<Self, Self::Err> {
458 parse_machine_aarch64(s).map_err(ShellStringError::new)
459 }
460}
461
462fn parse_machine_aarch64(s: &str) -> Result<MachineAarch64, String> {
463 let mut parts = s.split(DELIM_COMMA);
464 let first = parts.next().ok_or_else(|| "empty machine argument".to_string())?;
465
466 let mut machine_type = None;
467 let mut accel = None;
468 let mut dump_guest_core = None;
469 let mut mem_merge = None;
470 let mut nvdimm = None;
471 let mut spcr = None;
472 let mut confidential_guest_support = None;
473 let mut memory_backend = None;
474 let mut cxl_fmw = std::collections::BTreeMap::<usize, CxlFmwParts>::new();
475 let mut igvm_cfg = None;
476 let mut sgx_epc = std::collections::BTreeMap::<usize, (Option<ShellString>, Option<usize>)>::new();
477
478 if let Some(value) = first.strip_prefix(KEY_TYPE) {
479 machine_type = Some(parse_machine_type_aarch64(value)?);
480 } else if first.contains('=') {
481 parse_machine_aarch64_option(
482 first,
483 &mut machine_type,
484 &mut accel,
485 &mut dump_guest_core,
486 &mut mem_merge,
487 &mut nvdimm,
488 &mut spcr,
489 &mut confidential_guest_support,
490 &mut memory_backend,
491 &mut cxl_fmw,
492 &mut igvm_cfg,
493 &mut sgx_epc,
494 )?;
495 } else {
496 machine_type = Some(parse_machine_type_aarch64(first)?);
497 }
498
499 for part in parts {
500 parse_machine_aarch64_option(
501 part,
502 &mut machine_type,
503 &mut accel,
504 &mut dump_guest_core,
505 &mut mem_merge,
506 &mut nvdimm,
507 &mut spcr,
508 &mut confidential_guest_support,
509 &mut memory_backend,
510 &mut cxl_fmw,
511 &mut igvm_cfg,
512 &mut sgx_epc,
513 )?;
514 }
515
516 let machine_type = machine_type.ok_or_else(|| "machine type is required".to_string())?;
517 let cxl_fmw = build_cxl_fmw(cxl_fmw)?;
518 let sgx_epc = build_sgx_epc(sgx_epc)?;
519
520 Ok(MachineAarch64 {
521 m: Machine {
522 machine_type,
523 accel,
524 vmport: None,
525 dump_guest_core,
526 mem_merge,
527 aes_key_wrap: None,
528 dea_key_wrap: None,
529 nvdimm,
530 memory_encryption: None,
531 confidential_guest_support,
532 hmat: None,
533 spcr,
534 aux_ram_share: None,
535 memory_backend,
536 cxl_fmw,
537 igvm_cfg,
538 sgx_epc,
539 },
540 })
541}
542
543#[allow(clippy::too_many_arguments)]
544fn parse_machine_aarch64_option(
545 part: &str,
546 machine_type: &mut Option<MachineTypeAarch64>,
547 accel: &mut Option<Vec<AccelType>>,
548 dump_guest_core: &mut Option<OnOffDefaultOn>,
549 mem_merge: &mut Option<OnOffDefaultOn>,
550 nvdimm: &mut Option<OnOffDefaultOff>,
551 spcr: &mut Option<OnOffDefaultOn>,
552 confidential_guest_support: &mut Option<ShellString>,
553 memory_backend: &mut Option<ShellString>,
554 cxl_fmw: &mut std::collections::BTreeMap<usize, CxlFmwParts>,
555 igvm_cfg: &mut Option<ShellString>,
556 sgx_epc: &mut std::collections::BTreeMap<usize, (Option<ShellString>, Option<usize>)>,
557) -> Result<(), String> {
558 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid machine option: {part}"))?;
559 match key {
560 "type" => *machine_type = Some(parse_machine_type_aarch64(value)?),
561 "accel" => {
562 let accels = value
563 .split(DELIM_COLON)
564 .map(|v| v.parse::<AccelType>().map_err(|_| format!("invalid accel value: {v}")))
565 .collect::<Result<Vec<_>, _>>()?;
566 *accel = Some(accels);
567 }
568 "dump-guest-core" => *dump_guest_core = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid dump-guest-core value: {value}"))?),
569 "mem-merge" => *mem_merge = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid mem-merge value: {value}"))?),
570 "nvdimm" => *nvdimm = Some(value.parse::<OnOffDefaultOff>().map_err(|_| format!("invalid nvdimm value: {value}"))?),
571 "spcr" => *spcr = Some(value.parse::<OnOffDefaultOn>().map_err(|_| format!("invalid spcr value: {value}"))?),
572 "confidential-guest-support" => *confidential_guest_support = Some(ShellString::new(value)),
573 "memory-backend" => *memory_backend = Some(ShellString::new(value)),
574 _ if key.starts_with("cxl-fmw.") => parse_cxl_fmw_option(key, value, cxl_fmw)?,
575 "igvm-cfg" => *igvm_cfg = Some(ShellString::new(value)),
576 _ if key.starts_with("sgx-epc.") => parse_sgx_epc_option(key, value, sgx_epc)?,
577 other => return Err(format!("unsupported aarch64 machine option: {other}")),
578 }
579
580 Ok(())
581}
582
583#[derive(Debug, Default)]
584struct CxlFmwParts {
585 targets: std::collections::BTreeMap<usize, String>,
586 size: Option<String>,
587 interleave_granularity: Option<Granularity>,
588}
589
590fn push_cxl_fmw_args(args: &mut Vec<String>, cxl_fmw: &Option<Vec<CxlFmw>>) {
591 if let Some(cxl_fmws) = cxl_fmw {
592 for (idx, cxl_fmw) in cxl_fmws.iter().enumerate() {
593 for (target_idx, target) in cxl_fmw.targets.iter().enumerate() {
594 args.push(format!("cxl-fmw.{}.targets.{}={}", idx, target_idx, target));
595 }
596 args.push(format!("cxl-fmw.{}.size={}", idx, cxl_fmw.size));
597 if let Some(granularity) = &cxl_fmw.interleave_granularity {
598 args.push(format!("cxl-fmw.{}.interleave-granularity={}", idx, granularity.to_arg()));
599 }
600 }
601 }
602}
603
604fn parse_cxl_fmw_option(key: &str, value: &str, cxl_fmw: &mut std::collections::BTreeMap<usize, CxlFmwParts>) -> Result<(), String> {
605 let mut parts = key.split('.');
606 let prefix = parts.next();
607 let index = parts.next().ok_or_else(|| format!("invalid CXL FMW option: {key}"))?;
608 if prefix != Some("cxl-fmw") {
609 return Err(format!("invalid CXL FMW option: {key}"));
610 }
611
612 let index = index.parse::<usize>().map_err(|e| format!("invalid CXL FMW index: {e}"))?;
613 let entry = cxl_fmw.entry(index).or_default();
614
615 match parts.next() {
616 Some("targets") => {
617 let target_index = parts.next().ok_or_else(|| format!("invalid CXL FMW target option: {key}"))?;
618 if parts.next().is_some() {
619 return Err(format!("invalid CXL FMW target option: {key}"));
620 }
621 let target_index = target_index.parse::<usize>().map_err(|e| format!("invalid CXL FMW target index: {e}"))?;
622 entry.targets.insert(target_index, value.to_string());
623 }
624 Some("size") => {
625 if parts.next().is_some() {
626 return Err(format!("invalid CXL FMW size option: {key}"));
627 }
628 entry.size = Some(value.to_string());
629 }
630 Some("interleave-granularity") => {
631 if parts.next().is_some() {
632 return Err(format!("invalid CXL FMW interleave-granularity option: {key}"));
633 }
634 entry.interleave_granularity = Some(value.parse::<Granularity>()?);
635 }
636 Some(other) => return Err(format!("unsupported CXL FMW option: {other}")),
637 None => return Err(format!("invalid CXL FMW option: {key}")),
638 }
639
640 Ok(())
641}
642
643fn build_cxl_fmw(cxl_fmw: std::collections::BTreeMap<usize, CxlFmwParts>) -> Result<Option<Vec<CxlFmw>>, String> {
644 if cxl_fmw.is_empty() {
645 return Ok(None);
646 }
647
648 let mut windows = Vec::new();
649 for (index, parts) in cxl_fmw {
650 if parts.targets.is_empty() {
651 return Err(format!("cxl-fmw.{index} requires at least one target"));
652 }
653 windows.push(CxlFmw {
654 targets: parts.targets.into_values().collect(),
655 size: parts.size.ok_or_else(|| format!("cxl-fmw.{index} requires size"))?,
656 interleave_granularity: parts.interleave_granularity,
657 });
658 }
659 Ok(Some(windows))
660}
661
662fn parse_sgx_epc_option(key: &str, value: &str, sgx_epc: &mut std::collections::BTreeMap<usize, (Option<ShellString>, Option<usize>)>) -> Result<(), String> {
663 let mut parts = key.split('.');
664 let prefix = parts.next();
665 let index = parts.next().ok_or_else(|| format!("invalid SGX EPC option: {key}"))?;
666 let field = parts.next().ok_or_else(|| format!("invalid SGX EPC option: {key}"))?;
667 if prefix != Some("sgx-epc") || parts.next().is_some() {
668 return Err(format!("invalid SGX EPC option: {key}"));
669 }
670
671 let index = index.parse::<usize>().map_err(|e| format!("invalid SGX EPC index: {e}"))?;
672 let entry = sgx_epc.entry(index).or_default();
673 match field {
674 "memdev" => entry.0 = Some(ShellString::new(value)),
675 "node" => entry.1 = Some(value.parse::<usize>().map_err(|e| format!("invalid SGX EPC node: {e}"))?),
676 other => return Err(format!("unsupported SGX EPC option: {other}")),
677 }
678 Ok(())
679}
680
681fn build_sgx_epc(sgx_epc: std::collections::BTreeMap<usize, (Option<ShellString>, Option<usize>)>) -> Result<Option<Vec<SgxEpc>>, String> {
682 if sgx_epc.is_empty() {
683 return Ok(None);
684 }
685
686 let mut entries = Vec::new();
687 for (index, (memdev, node)) in sgx_epc {
688 entries.push(SgxEpc {
689 memdev: memdev.ok_or_else(|| format!("sgx-epc.{index} requires memdev"))?,
690 node: node.ok_or_else(|| format!("sgx-epc.{index} requires node"))?,
691 });
692 }
693 Ok(Some(entries))
694}
695
696fn parse_machine_type_aarch64(value: &str) -> Result<MachineTypeAarch64, String> {
697 value.parse::<MachineTypeAarch64>().map_err(|_| format!("{value} is not a supported aarch64 machine type"))
698}