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