1#![no_std]
12#![forbid(unsafe_code)]
13
14use arrayvec::ArrayString;
15use arrayvec::ArrayVec;
16use core::fmt::Display;
17use core::fmt::Write;
18use core::mem::size_of;
19use hvdef::HV_PAGE_SIZE;
20use igvm_defs::MemoryMapEntryType;
21#[cfg(feature = "inspect")]
22use inspect::Inspect;
23use memory_range::MemoryRange;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27#[cfg_attr(feature = "inspect", derive(Inspect))]
28pub struct VmbusInfo {
29 #[cfg_attr(feature = "inspect", inspect(with = "inspect_helpers::mmio_internal"))]
31 pub mmio: ArrayVec<MemoryRange, 2>,
32 #[cfg_attr(feature = "inspect", inspect(hex))]
34 pub connection_id: u32,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq)]
39#[cfg_attr(feature = "inspect", derive(Inspect))]
40pub struct GicInfo {
41 #[cfg_attr(feature = "inspect", inspect(hex))]
43 pub gic_distributor_base: u64,
44 #[cfg_attr(feature = "inspect", inspect(hex))]
46 pub gic_distributor_size: u64,
47 #[cfg_attr(feature = "inspect", inspect(hex))]
49 pub gic_redistributors_base: u64,
50 #[cfg_attr(feature = "inspect", inspect(hex))]
52 pub gic_redistributors_size: u64,
53 #[cfg_attr(feature = "inspect", inspect(hex))]
55 pub gic_redistributor_stride: u64,
56}
57
58#[derive(Debug)]
60pub struct Error<'a>(ErrorKind<'a>);
61
62impl Display for Error<'_> {
63 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64 f.write_fmt(format_args!("Parsing failed due to: {}", self.0))
65 }
66}
67
68impl core::error::Error for Error<'_> {}
69
70#[derive(Debug)]
71enum ErrorKind<'a> {
72 Dt(fdt::parser::Error<'a>),
73 Node {
74 parent_name: &'a str,
75 error: fdt::parser::Error<'a>,
76 },
77 PropMissing {
78 node_name: &'a str,
79 prop_name: &'static str,
80 },
81 Prop(fdt::parser::Error<'a>),
82 TooManyCpus,
83 MemoryRegUnaligned {
84 node_name: &'a str,
85 base: u64,
86 len: u64,
87 },
88 MemoryRegOverlap {
89 lower: MemoryEntry,
90 upper: MemoryEntry,
91 },
92 TooManyMemoryEntries,
93 PropInvalidU32 {
94 node_name: &'a str,
95 prop_name: &'a str,
96 expected: u32,
97 actual: u32,
98 },
99 PropInvalidStr {
100 node_name: &'a str,
101 prop_name: &'a str,
102 expected: &'a str,
103 actual: &'a str,
104 },
105 UnexpectedVmbusVtl {
106 node_name: &'a str,
107 vtl: u32,
108 },
109 MultipleVmbusNode {
110 node_name: &'a str,
111 },
112 VmbusRangesChildParent {
113 node_name: &'a str,
114 child_base: u64,
115 parent_base: u64,
116 },
117 VmbusRangesNotAligned {
118 node_name: &'a str,
119 base: u64,
120 len: u64,
121 },
122 TooManyVmbusMmioRanges {
123 node_name: &'a str,
124 ranges: usize,
125 },
126 VmbusMmioOverlapsRam {
127 mmio: MemoryRange,
128 ram: MemoryEntry,
129 },
130 VmbusMmioOverlapsVmbusMmio {
131 mmio_a: MemoryRange,
132 mmio_b: MemoryRange,
133 },
134 CmdlineSize,
135 UnexpectedMemoryAllocationMode {
136 mode: &'a str,
137 },
138}
139
140impl Display for ErrorKind<'_> {
141 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142 match self {
143 ErrorKind::Dt(e) => f.write_fmt(format_args!("invalid device tree: {}", e)),
144 ErrorKind::Node { parent_name, error } => {
145 f.write_fmt(format_args!("invalid device tree node with parent {parent_name}: {error}"))
146 }
147 ErrorKind::PropMissing {
148 node_name,
149 prop_name,
150 } => f.write_fmt(format_args!(
151 "{node_name} did not have the following required property {prop_name}",
152 )),
153 ErrorKind::Prop(e) => f.write_fmt(format_args!("reading node property failed: {e}")),
154 ErrorKind::TooManyCpus => {
155 f.write_str("device tree contained more enabled CPUs than can be parsed")
156 }
157 ErrorKind::MemoryRegUnaligned {
158 node_name,
159 base,
160 len,
161 } => f.write_fmt(format_args!(
162 "memory node {node_name} contains 4K unaligned base {base} or len {len}"
163 )),
164 ErrorKind::MemoryRegOverlap { lower, upper, } => {
165 f.write_fmt(format_args!("ram at {}..{} of type {:?} overlaps ram at {}..{} of type {:?}", lower.range.start(), lower.range.end(), lower.mem_type, upper.range.start(), upper.range.end(), upper.mem_type))
166 }
167 ErrorKind::TooManyMemoryEntries => {
168 f.write_str("device tree contained more memory ranges than can be parsed")
169 }
170 ErrorKind::PropInvalidU32 { node_name, prop_name, expected, actual } => f.write_fmt(format_args!("{node_name} had an invalid u32 value for {prop_name}: expected {expected}, actual {actual}")),
171 ErrorKind::PropInvalidStr { node_name, prop_name, expected, actual } => f.write_fmt(format_args!("{node_name} had an invalid str value for {prop_name}: expected {expected}, actual {actual}")),
172 ErrorKind::UnexpectedVmbusVtl { node_name, vtl } => f.write_fmt(format_args!("{node_name} has an unexpected vtl {vtl}")),
173 ErrorKind::MultipleVmbusNode { node_name } => f.write_fmt(format_args!("{node_name} specifies a duplicate vmbus node")),
174 ErrorKind::VmbusRangesChildParent { node_name, child_base, parent_base } => f.write_fmt(format_args!("vmbus {node_name} ranges child base {child_base} does not match parent {parent_base}")),
175 ErrorKind::VmbusRangesNotAligned { node_name, base, len } => f.write_fmt(format_args!("vmbus {node_name} base {base} or len {len} not aligned to 4K")),
176 ErrorKind::TooManyVmbusMmioRanges { node_name, ranges } => f.write_fmt(format_args!("vmbus {node_name} has more than 2 mmio ranges {ranges}")),
177 ErrorKind::VmbusMmioOverlapsRam { mmio, ram } => {
178 f.write_fmt(format_args!("vmbus mmio at {}..{} overlaps ram at {}..{}", mmio.start(), mmio.end(), ram.range.start(), ram.range.end()))
179 }
180 ErrorKind::VmbusMmioOverlapsVmbusMmio { mmio_a, mmio_b } => {
181 f.write_fmt(format_args!("vmbus mmio at {}..{} overlaps vmbus mmio at {}..{}", mmio_a.start(), mmio_a.end(), mmio_b.start(), mmio_b.end()))
182 }
183 ErrorKind::CmdlineSize => f.write_str("commandline too small to parse /chosen bootargs"),
184 ErrorKind::UnexpectedMemoryAllocationMode { mode } => f.write_fmt(format_args!("unexpected memory allocation mode: {}", mode)),
185 }
186 }
187}
188
189const COM3_REG_BASE: u64 = 0x3E8;
190
191#[derive(Debug, PartialEq, Eq)]
193#[cfg_attr(feature = "inspect", derive(Inspect))]
194pub struct ParsedDeviceTree<
195 const MAX_MEMORY_ENTRIES: usize,
196 const MAX_CPU_ENTRIES: usize,
197 const MAX_COMMAND_LINE_SIZE: usize,
198 const MAX_ENTROPY_SIZE: usize,
199> {
200 pub device_tree_size: usize,
202 #[cfg_attr(
204 feature = "inspect",
205 inspect(with = "inspect_helpers::memory_internal")
206 )]
207 pub memory: ArrayVec<MemoryEntry, MAX_MEMORY_ENTRIES>,
208 #[cfg_attr(feature = "inspect", inspect(hex))]
210 pub boot_cpuid_phys: u32,
211 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
213 pub cpus: ArrayVec<CpuEntry, MAX_CPU_ENTRIES>,
214 pub vmbus_vtl0: Option<VmbusInfo>,
216 pub vmbus_vtl2: Option<VmbusInfo>,
218 #[cfg_attr(feature = "inspect", inspect(display))]
221 pub command_line: ArrayString<MAX_COMMAND_LINE_SIZE>,
222 pub com3_serial: bool,
224 pub gic: Option<GicInfo>,
226 pub memory_allocation_mode: MemoryAllocationMode,
228 #[cfg_attr(feature = "inspect", inspect(with = "Option::is_some"))]
230 pub entropy: Option<ArrayVec<u8, MAX_ENTROPY_SIZE>>,
231 pub device_dma_page_count: Option<u64>,
236 pub nvme_keepalive: bool,
238 pub vtl0_alias_map: Option<u64>,
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
245#[cfg_attr(feature = "inspect", derive(Inspect))]
246#[cfg_attr(feature = "inspect", inspect(external_tag))]
247pub enum MemoryAllocationMode {
248 Host,
252 Vtl2 {
255 memory_size: Option<u64>,
258 mmio_size: Option<u64>,
261 },
262}
263
264#[derive(Copy, Clone, Debug, PartialEq, Eq)]
266#[cfg_attr(feature = "inspect", derive(Inspect))]
267pub struct MemoryEntry {
268 pub range: MemoryRange,
270 #[cfg_attr(
272 feature = "inspect",
273 inspect(with = "inspect_helpers::inspect_memory_map_entry_type")
274 )]
275 pub mem_type: MemoryMapEntryType,
276 pub vnode: u32,
278}
279
280#[derive(Copy, Clone, Debug, PartialEq, Eq)]
282#[cfg_attr(feature = "inspect", derive(Inspect))]
283pub struct CpuEntry {
284 #[cfg_attr(feature = "inspect", inspect(hex))]
288 pub reg: u64,
289 pub vnode: u32,
291}
292
293impl<
294 'a,
295 'b,
296 const MAX_MEMORY_ENTRIES: usize,
297 const MAX_CPU_ENTRIES: usize,
298 const MAX_COMMAND_LINE_SIZE: usize,
299 const MAX_ENTROPY_SIZE: usize,
300> ParsedDeviceTree<MAX_MEMORY_ENTRIES, MAX_CPU_ENTRIES, MAX_COMMAND_LINE_SIZE, MAX_ENTROPY_SIZE>
301{
302 pub const fn new() -> Self {
305 Self {
306 device_tree_size: 0,
307 memory: ArrayVec::new_const(),
308 boot_cpuid_phys: 0,
309 cpus: ArrayVec::new_const(),
310 vmbus_vtl0: None,
311 vmbus_vtl2: None,
312 command_line: ArrayString::new_const(),
313 com3_serial: false,
314 gic: None,
315 memory_allocation_mode: MemoryAllocationMode::Host,
316 entropy: None,
317 device_dma_page_count: None,
318 nvme_keepalive: false,
319 vtl0_alias_map: None,
320 }
321 }
322
323 pub fn cpu_count(&self) -> usize {
325 self.cpus.len()
326 }
327
328 pub fn parse(dt: &'a [u8], storage: &'b mut Self) -> Result<&'b Self, Error<'a>> {
330 Self::parse_inner(dt, storage).map_err(Error)
331 }
332
333 fn parse_inner(dt: &'a [u8], storage: &'b mut Self) -> Result<&'b Self, ErrorKind<'a>> {
334 let parser = fdt::parser::Parser::new(dt).map_err(ErrorKind::Dt)?;
335 let root = match parser.root() {
336 Ok(v) => v,
337 Err(e) => {
338 return Err(ErrorKind::Node {
339 parent_name: "",
340 error: e,
341 });
342 }
343 };
344
345 let insert_memory_entry = |memory: &mut ArrayVec<MemoryEntry, MAX_MEMORY_ENTRIES>,
350 entry: MemoryEntry|
351 -> Result<(), ErrorKind<'a>> {
352 let insert_index = match memory.binary_search_by_key(&entry.range, |k| k.range) {
353 Ok(index) => {
354 return Err(ErrorKind::MemoryRegOverlap {
355 lower: memory[index],
356 upper: entry,
357 });
358 }
359 Err(index) => index,
360 };
361
362 memory
363 .try_insert(insert_index, entry)
364 .map_err(|_| ErrorKind::TooManyMemoryEntries)
365 };
366
367 for child in root.children() {
368 let child = child.map_err(|error| ErrorKind::Node {
369 parent_name: root.name,
370 error,
371 })?;
372
373 match child.name {
374 "cpus" => {
375 let address_cells = child
376 .find_property("#address-cells")
377 .map_err(ErrorKind::Prop)?
378 .ok_or(ErrorKind::PropMissing {
379 node_name: child.name,
380 prop_name: "#address-cells",
381 })?
382 .read_u32(0)
383 .map_err(ErrorKind::Prop)?;
384
385 if address_cells > 2 {
388 return Err(ErrorKind::PropInvalidU32 {
389 node_name: child.name,
390 prop_name: "#address-cells",
391 expected: 2,
392 actual: address_cells,
393 });
394 }
395
396 for cpu in child.children() {
397 let cpu = cpu.map_err(|error| ErrorKind::Node {
398 parent_name: child.name,
399 error,
400 })?;
401
402 if cpu
403 .find_property("status")
404 .map_err(ErrorKind::Prop)?
405 .ok_or(ErrorKind::PropMissing {
406 node_name: cpu.name,
407 prop_name: "status",
408 })?
409 .read_str()
410 .map_err(ErrorKind::Prop)?
411 != "okay"
412 {
413 continue;
414 }
415
416 let reg_property = cpu
420 .find_property("reg")
421 .map_err(ErrorKind::Prop)?
422 .ok_or(ErrorKind::PropMissing {
423 node_name: cpu.name,
424 prop_name: "reg",
425 })?;
426
427 let reg = if address_cells == 1 {
428 reg_property.read_u32(0).map_err(ErrorKind::Prop)? as u64
429 } else {
430 reg_property.read_u64(0).map_err(ErrorKind::Prop)?
431 };
432
433 let vnode = cpu
434 .find_property("numa-node-id")
435 .map_err(ErrorKind::Prop)?
436 .ok_or(ErrorKind::PropMissing {
437 node_name: cpu.name,
438 prop_name: "numa-node-id",
439 })?
440 .read_u32(0)
441 .map_err(ErrorKind::Prop)?;
442
443 storage
444 .cpus
445 .try_push(CpuEntry { reg, vnode })
446 .map_err(|_| ErrorKind::TooManyCpus)?;
447 }
448 }
449 "openhcl" => {
450 let memory_allocation_mode = child
451 .find_property("memory-allocation-mode")
452 .map_err(ErrorKind::Prop)?
453 .ok_or(ErrorKind::PropMissing {
454 node_name: child.name,
455 prop_name: "memory-allocation-mode",
456 })?;
457
458 match memory_allocation_mode.read_str().map_err(ErrorKind::Prop)? {
459 "host" => {
460 storage.memory_allocation_mode = MemoryAllocationMode::Host;
461 }
462 "vtl2" => {
463 let memory_size = child
464 .find_property("memory-size")
465 .map_err(ErrorKind::Prop)?
466 .map(|p| p.read_u64(0))
467 .transpose()
468 .map_err(ErrorKind::Prop)?;
469
470 let mmio_size = child
471 .find_property("mmio-size")
472 .map_err(ErrorKind::Prop)?
473 .map(|p| p.read_u64(0))
474 .transpose()
475 .map_err(ErrorKind::Prop)?;
476
477 storage.memory_allocation_mode = MemoryAllocationMode::Vtl2 {
478 memory_size,
479 mmio_size,
480 };
481 }
482 mode => {
483 return Err(ErrorKind::UnexpectedMemoryAllocationMode { mode });
484 }
485 }
486
487 storage.vtl0_alias_map = child
488 .find_property("vtl0-alias-map")
489 .map_err(ErrorKind::Prop)?
490 .map(|p| p.read_u64(0))
491 .transpose()
492 .map_err(ErrorKind::Prop)?;
493
494 for openhcl_child in child.children() {
495 let openhcl_child = openhcl_child.map_err(|error| ErrorKind::Node {
496 parent_name: root.name,
497 error,
498 })?;
499
500 match openhcl_child.name {
501 "entropy" => {
502 let host_entropy = openhcl_child
503 .find_property("reg")
504 .map_err(ErrorKind::Prop)?
505 .ok_or(ErrorKind::PropMissing {
506 node_name: openhcl_child.name,
507 prop_name: "reg",
508 })?
509 .data;
510
511 if host_entropy.len() > MAX_ENTROPY_SIZE {
512 #[cfg(feature = "tracing")]
513 tracing::warn!(
514 entropy_len = host_entropy.len(),
515 "Truncating host-provided entropy",
516 );
517 }
518 let use_entropy_bytes =
519 core::cmp::min(host_entropy.len(), MAX_ENTROPY_SIZE);
520 let entropy =
521 ArrayVec::try_from(&host_entropy[..use_entropy_bytes]).unwrap();
522
523 storage.entropy = Some(entropy);
524 }
525 "keep-alive" => {
527 storage.nvme_keepalive = openhcl_child
528 .find_property("device-types")
529 .ok()
530 .flatten()
531 .and_then(|p| p.read_str().ok())
532 == Some("nvme");
533 }
534 "device-dma" => {
535 storage.device_dma_page_count = openhcl_child
537 .find_property("total-pages")
538 .ok()
539 .flatten()
540 .and_then(|p| p.read_u64(0).ok());
541 }
542 _ => {
543 #[cfg(feature = "tracing")]
544 tracing::warn!(?openhcl_child.name, "Unrecognized OpenHCL child node");
545 }
546 }
547 }
548 }
549
550 _ if child.name.starts_with("memory@") => {
551 let igvm_type = if let Some(igvm_type) = child
552 .find_property(igvm_defs::dt::IGVM_DT_IGVM_TYPE_PROPERTY)
553 .map_err(ErrorKind::Prop)?
554 {
555 let typ = igvm_type.read_u32(0).map_err(ErrorKind::Prop)?;
556 MemoryMapEntryType(typ as u16)
557 } else {
558 MemoryMapEntryType::MEMORY
559 };
560
561 let reg = child.find_property("reg").map_err(ErrorKind::Prop)?.ok_or(
562 ErrorKind::PropMissing {
563 node_name: child.name,
564 prop_name: "reg",
565 },
566 )?;
567
568 let vnode = child
569 .find_property("numa-node-id")
570 .map_err(ErrorKind::Prop)?
571 .ok_or(ErrorKind::PropMissing {
572 node_name: child.name,
573 prop_name: "numa-node-id",
574 })?
575 .read_u32(0)
576 .map_err(ErrorKind::Prop)?;
577
578 let len = reg.data.len();
579 let reg_tuple_size = size_of::<u64>() * 2;
580 let number_of_ranges = len / reg_tuple_size;
581
582 for i in 0..number_of_ranges {
583 let base = reg.read_u64(i * 2).map_err(ErrorKind::Prop)?;
584 let len = reg.read_u64(i * 2 + 1).map_err(ErrorKind::Prop)?;
585
586 if base % HV_PAGE_SIZE != 0 || len % HV_PAGE_SIZE != 0 {
587 return Err(ErrorKind::MemoryRegUnaligned {
588 node_name: child.name,
589 base,
590 len,
591 });
592 }
593
594 insert_memory_entry(
595 &mut storage.memory,
596 MemoryEntry {
597 range: MemoryRange::try_new(base..(base + len))
598 .expect("valid range"),
599 mem_type: igvm_type,
600 vnode,
601 },
602 )?;
603 }
604 }
605 "chosen" => {
606 let cmdline = child
607 .find_property("bootargs")
608 .map_err(ErrorKind::Prop)?
609 .map(|prop| prop.read_str().map_err(ErrorKind::Prop))
610 .transpose()?
611 .unwrap_or("");
612
613 write!(storage.command_line, "{}", cmdline)
614 .map_err(|_| ErrorKind::CmdlineSize)?;
615 }
616 _ if child.name.starts_with("intc@") => {
617 validate_property_str(&child, "compatible", "arm,gic-v3")?;
618 validate_property_u32(&child, "#redistributor-regions", 1, 0)?;
619 validate_property_u32(&child, "#address-cells", 2, 0)?;
620 validate_property_u32(&child, "#size-cells", 2, 0)?;
621 validate_property_u32(&child, "#interrupt-cells", 3, 0)?;
622
623 let gic_redistributor_stride = child
624 .find_property("redistributor-stride")
625 .map_err(ErrorKind::Prop)?
626 .ok_or(ErrorKind::PropMissing {
627 node_name: child.name,
628 prop_name: "redistributor-stride",
629 })?
630 .read_u64(0)
631 .map_err(ErrorKind::Prop)?;
632
633 let gic_reg_property = child
634 .find_property("reg")
635 .map_err(ErrorKind::Prop)?
636 .ok_or(ErrorKind::PropMissing {
637 node_name: child.name,
638 prop_name: "reg",
639 })?;
640 let gic_distributor_base =
641 gic_reg_property.read_u64(0).map_err(ErrorKind::Prop)?;
642 let gic_distributor_size =
643 gic_reg_property.read_u64(1).map_err(ErrorKind::Prop)?;
644 let gic_redistributors_base =
645 gic_reg_property.read_u64(2).map_err(ErrorKind::Prop)?;
646 let gic_redistributors_size =
647 gic_reg_property.read_u64(3).map_err(ErrorKind::Prop)?;
648
649 storage.gic = Some(GicInfo {
650 gic_distributor_base,
651 gic_distributor_size,
652 gic_redistributors_base,
653 gic_redistributors_size,
654 gic_redistributor_stride,
655 })
656 }
657 _ => {
658 parse_compatible(
659 &child,
660 &mut storage.vmbus_vtl0,
661 &mut storage.vmbus_vtl2,
662 &mut storage.com3_serial,
663 )?;
664 }
665 }
666 }
667
668 for (prev, next) in storage.memory.iter().zip(storage.memory.iter().skip(1)) {
670 if prev.range.overlaps(&next.range) {
671 return Err(ErrorKind::MemoryRegOverlap {
672 lower: *prev,
673 upper: *next,
674 });
675 }
676 }
677
678 let vmbus_vtl0_mmio = storage
680 .vmbus_vtl0
681 .as_ref()
682 .map(|info| info.mmio.as_slice())
683 .unwrap_or(&[]);
684
685 let vmbus_vtl2_mmio = storage
686 .vmbus_vtl2
687 .as_ref()
688 .map(|info| info.mmio.as_slice())
689 .unwrap_or(&[]);
690
691 for ram in storage.memory.iter() {
692 for mmio in vmbus_vtl0_mmio {
693 if mmio.overlaps(&ram.range) {
694 return Err(ErrorKind::VmbusMmioOverlapsRam {
695 mmio: *mmio,
696 ram: *ram,
697 });
698 }
699 }
700
701 for mmio in vmbus_vtl2_mmio {
702 if mmio.overlaps(&ram.range) {
703 return Err(ErrorKind::VmbusMmioOverlapsRam {
704 mmio: *mmio,
705 ram: *ram,
706 });
707 }
708 }
709 }
710
711 for vtl0_mmio in vmbus_vtl0_mmio {
712 for vtl2_mmio in vmbus_vtl2_mmio {
713 if vtl0_mmio.overlaps(vtl2_mmio) {
714 return Err(ErrorKind::VmbusMmioOverlapsVmbusMmio {
715 mmio_a: *vtl0_mmio,
716 mmio_b: *vtl2_mmio,
717 });
718 }
719 }
720 }
721
722 let Self {
724 device_tree_size,
725 memory: _,
726 boot_cpuid_phys,
727 cpus: _,
728 vmbus_vtl0: _,
729 vmbus_vtl2: _,
730 command_line: _,
731 com3_serial: _,
732 gic: _,
733 memory_allocation_mode: _,
734 entropy: _,
735 device_dma_page_count: _,
736 nvme_keepalive: _,
737 vtl0_alias_map: _,
738 } = storage;
739
740 *device_tree_size = parser.total_size;
741 *boot_cpuid_phys = parser.boot_cpuid_phys;
742
743 Ok(storage)
744 }
745}
746
747fn parse_compatible<'a>(
748 node: &fdt::parser::Node<'a>,
749 vmbus_vtl0: &mut Option<VmbusInfo>,
750 vmbus_vtl2: &mut Option<VmbusInfo>,
751 com3_serial: &mut bool,
752) -> Result<(), ErrorKind<'a>> {
753 let compatible = node
754 .find_property("compatible")
755 .map_err(ErrorKind::Prop)?
756 .map(|prop| prop.read_str().map_err(ErrorKind::Prop))
757 .transpose()?
758 .unwrap_or("");
759
760 if compatible == "simple-bus" {
761 parse_simple_bus(node, vmbus_vtl0, vmbus_vtl2)?;
762 } else if compatible == "x86-pio-bus" {
763 parse_io_bus(node, com3_serial)?;
764 } else {
765 #[cfg(feature = "tracing")]
766 tracing::warn!(?compatible, ?node.name,
767 "Unrecognized compatible field",
768 );
769 }
770
771 Ok(())
772}
773
774fn parse_vmbus<'a>(node: &fdt::parser::Node<'a>) -> Result<VmbusInfo, ErrorKind<'a>> {
775 let address_cells = node
777 .find_property("#address-cells")
778 .map_err(ErrorKind::Prop)?
779 .ok_or(ErrorKind::PropMissing {
780 node_name: node.name,
781 prop_name: "#address-cells",
782 })?
783 .read_u32(0)
784 .map_err(ErrorKind::Prop)?;
785
786 if address_cells != 2 {
787 return Err(ErrorKind::PropInvalidU32 {
788 node_name: node.name,
789 prop_name: "#address-cells",
790 expected: 2,
791 actual: address_cells,
792 });
793 }
794
795 let size_cells = node
796 .find_property("#size-cells")
797 .map_err(ErrorKind::Prop)?
798 .ok_or(ErrorKind::PropMissing {
799 node_name: node.name,
800 prop_name: "#size-cells",
801 })?
802 .read_u32(0)
803 .map_err(ErrorKind::Prop)?;
804
805 if size_cells != 2 {
806 return Err(ErrorKind::PropInvalidU32 {
807 node_name: node.name,
808 prop_name: "#size-cells",
809 expected: 2,
810 actual: size_cells,
811 });
812 }
813
814 let mmio: ArrayVec<MemoryRange, 2> =
815 match node.find_property("ranges").map_err(ErrorKind::Prop)? {
816 Some(ranges) => {
817 let ranges_tuple_size = size_of::<u64>() * 3;
820 let number_of_ranges = ranges.data.len() / ranges_tuple_size;
821 let mut mmio = ArrayVec::new();
822
823 if number_of_ranges > 2 {
824 return Err(ErrorKind::TooManyVmbusMmioRanges {
825 node_name: node.name,
826 ranges: number_of_ranges,
827 });
828 }
829
830 for i in 0..number_of_ranges {
831 let child_base = ranges.read_u64(i * 3).map_err(ErrorKind::Prop)?;
832 let parent_base = ranges.read_u64(i * 3 + 1).map_err(ErrorKind::Prop)?;
833 let len = ranges.read_u64(i * 3 + 2).map_err(ErrorKind::Prop)?;
834
835 if child_base != parent_base {
836 return Err(ErrorKind::VmbusRangesChildParent {
837 node_name: node.name,
838 child_base,
839 parent_base,
840 });
841 }
842
843 if child_base % HV_PAGE_SIZE != 0 || len % HV_PAGE_SIZE != 0 {
844 return Err(ErrorKind::VmbusRangesNotAligned {
845 node_name: node.name,
846 base: child_base,
847 len,
848 });
849 }
850
851 mmio.push(
852 MemoryRange::try_new(child_base..(child_base + len)).expect("valid range"),
853 );
854 }
855
856 if number_of_ranges > 1 && mmio[0].start() > mmio[1].start() {
859 mmio.swap(0, 1);
860 }
861
862 if number_of_ranges > 1 && mmio[0].overlaps(&mmio[1]) {
863 return Err(ErrorKind::VmbusMmioOverlapsVmbusMmio {
864 mmio_a: mmio[0],
865 mmio_b: mmio[1],
866 });
867 }
868
869 mmio
870 }
871 None => {
872 ArrayVec::new()
874 }
875 };
876
877 let connection_id = node
878 .find_property("microsoft,message-connection-id")
879 .map_err(ErrorKind::Prop)?
880 .ok_or(ErrorKind::PropMissing {
881 node_name: node.name,
882 prop_name: "microsoft,message-connection-id",
883 })?
884 .read_u32(0)
885 .map_err(ErrorKind::Prop)?;
886
887 Ok(VmbusInfo {
888 mmio,
889 connection_id,
890 })
891}
892
893fn parse_simple_bus<'a>(
894 node: &fdt::parser::Node<'a>,
895 vmbus_vtl0: &mut Option<VmbusInfo>,
896 vmbus_vtl2: &mut Option<VmbusInfo>,
897) -> Result<(), ErrorKind<'a>> {
898 if !node
900 .find_property("ranges")
901 .map_err(ErrorKind::Prop)?
902 .ok_or(ErrorKind::PropMissing {
903 node_name: node.name,
904 prop_name: "ranges",
905 })?
906 .data
907 .is_empty()
908 {
909 return Ok(());
910 }
911
912 for child in node.children() {
913 let child = child.map_err(|error| ErrorKind::Node {
914 parent_name: node.name,
915 error,
916 })?;
917
918 let compatible = child
919 .find_property("compatible")
920 .map_err(ErrorKind::Prop)?
921 .map(|prop| prop.read_str().map_err(ErrorKind::Prop))
922 .transpose()?
923 .unwrap_or("");
924
925 if compatible == "microsoft,vmbus" {
926 let vtl_name = igvm_defs::dt::IGVM_DT_VTL_PROPERTY;
927 let vtl = child
928 .find_property(vtl_name)
929 .map_err(ErrorKind::Prop)?
930 .ok_or(ErrorKind::PropMissing {
931 node_name: child.name,
932 prop_name: vtl_name,
933 })?
934 .read_u32(0)
935 .map_err(ErrorKind::Prop)?;
936
937 match vtl {
938 0 => {
939 if vmbus_vtl0.replace(parse_vmbus(&child)?).is_some() {
940 return Err(ErrorKind::MultipleVmbusNode {
941 node_name: child.name,
942 });
943 }
944 }
945 2 => {
946 if vmbus_vtl2.replace(parse_vmbus(&child)?).is_some() {
947 return Err(ErrorKind::MultipleVmbusNode {
948 node_name: child.name,
949 });
950 }
951 }
952 _ => {
953 return Err(ErrorKind::UnexpectedVmbusVtl {
954 node_name: child.name,
955 vtl,
956 });
957 }
958 }
959 }
960 }
961
962 Ok(())
963}
964
965fn parse_io_bus<'a>(
966 node: &fdt::parser::Node<'a>,
967 com3_serial: &mut bool,
968) -> Result<(), ErrorKind<'a>> {
969 for io_bus_child in node.children() {
970 let io_bus_child = io_bus_child.map_err(|error| ErrorKind::Node {
971 parent_name: node.name,
972 error,
973 })?;
974
975 let compatible: &str = io_bus_child
976 .find_property("compatible")
977 .map_err(ErrorKind::Prop)?
978 .map(|prop| prop.read_str().map_err(ErrorKind::Prop))
979 .transpose()?
980 .unwrap_or("");
981
982 let _current_speed = io_bus_child
983 .find_property("current-speed")
984 .map_err(ErrorKind::Prop)?
985 .ok_or(ErrorKind::PropMissing {
986 node_name: io_bus_child.name,
987 prop_name: "current-speed",
988 })?
989 .read_u32(0)
990 .map_err(ErrorKind::Prop)?;
991
992 let reg = io_bus_child
993 .find_property("reg")
994 .map_err(ErrorKind::Prop)?
995 .ok_or(ErrorKind::PropMissing {
996 node_name: io_bus_child.name,
997 prop_name: "reg",
998 })?;
999
1000 let reg_base = reg.read_u64(0).map_err(ErrorKind::Prop)?;
1001 let _reg_len = reg.read_u64(1).map_err(ErrorKind::Prop)?;
1002
1003 if compatible == "ns16550" && reg_base == COM3_REG_BASE {
1007 *com3_serial = true
1008 } else {
1009 #[cfg(feature = "tracing")]
1010 tracing::warn!(?node.name, ?compatible, ?reg_base,
1011 "unrecognized io bus child"
1012 );
1013 }
1014 }
1015
1016 Ok(())
1017}
1018
1019fn validate_property_str<'a>(
1020 child: &fdt::parser::Node<'a>,
1021 name: &'static str,
1022 expected: &'static str,
1023) -> Result<(), ErrorKind<'a>> {
1024 let actual = child
1025 .find_property(name)
1026 .map_err(ErrorKind::Prop)?
1027 .ok_or(ErrorKind::PropMissing {
1028 node_name: child.name,
1029 prop_name: name,
1030 })?
1031 .read_str()
1032 .map_err(ErrorKind::Prop)?;
1033 if actual != expected {
1034 return Err(ErrorKind::PropInvalidStr {
1035 node_name: child.name,
1036 prop_name: name,
1037 expected,
1038 actual,
1039 });
1040 }
1041
1042 Ok(())
1043}
1044
1045fn validate_property_u32<'a>(
1046 child: &fdt::parser::Node<'a>,
1047 name: &'static str,
1048 expected: u32,
1049 index: usize,
1050) -> Result<(), ErrorKind<'a>> {
1051 let actual = child
1052 .find_property(name)
1053 .map_err(ErrorKind::Prop)?
1054 .ok_or(ErrorKind::PropMissing {
1055 node_name: child.name,
1056 prop_name: name,
1057 })?
1058 .read_u32(index)
1059 .map_err(ErrorKind::Prop)?;
1060 if actual != expected {
1061 return Err(ErrorKind::PropInvalidU32 {
1062 node_name: child.name,
1063 prop_name: name,
1064 expected,
1065 actual,
1066 });
1067 }
1068
1069 Ok(())
1070}
1071
1072#[cfg(feature = "inspect")]
1073mod inspect_helpers {
1074 use super::*;
1075
1076 pub(super) fn inspect_memory_map_entry_type(typ: &MemoryMapEntryType) -> impl Inspect + '_ {
1077 inspect::adhoc(|req| match *typ {
1080 MemoryMapEntryType::MEMORY => req.value("MEMORY"),
1081 MemoryMapEntryType::PERSISTENT => req.value("PERSISTENT"),
1082 MemoryMapEntryType::PLATFORM_RESERVED => req.value("PLATFORM_RESERVED"),
1083 MemoryMapEntryType::VTL2_PROTECTABLE => req.value("VTL2_PROTECTABLE"),
1084 _ => req.value(typ.0),
1085 })
1086 }
1087
1088 pub(super) fn mmio_internal(mmio: &[MemoryRange]) -> impl Inspect + '_ {
1089 inspect::iter_by_key(
1090 mmio.iter()
1091 .map(|range| (range, inspect::AsHex(range.len()))),
1092 )
1093 }
1094
1095 pub(super) fn memory_internal(memory: &[MemoryEntry]) -> impl Inspect + '_ {
1096 inspect::iter_by_key(memory.iter().map(|entry| (entry.range, entry)))
1097 }
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102 extern crate alloc;
1103
1104 use super::*;
1105 use alloc::format;
1106 use alloc::vec;
1107 use alloc::vec::Vec;
1108 use fdt::builder::Builder;
1109 use fdt::builder::BuilderConfig;
1110 use fdt::builder::Nest;
1111
1112 type TestParsedDeviceTree = ParsedDeviceTree<32, 32, 1024, 64>;
1113
1114 fn new_vmbus_mmio(mmio: &[MemoryRange]) -> ArrayVec<MemoryRange, 2> {
1115 let mut vec = ArrayVec::new();
1116 vec.try_extend_from_slice(mmio).unwrap();
1117 vec
1118 }
1119
1120 struct VmbusStringIds {
1121 p_address_cells: fdt::builder::StringId,
1122 p_size_cells: fdt::builder::StringId,
1123 p_compatible: fdt::builder::StringId,
1124 p_ranges: fdt::builder::StringId,
1125 p_vtl: fdt::builder::StringId,
1126 p_vmbus_connection_id: fdt::builder::StringId,
1127 }
1128
1129 fn add_vmbus<'a>(
1130 ids: &VmbusStringIds,
1131 bus: Builder<'a, Nest<Nest<()>>>,
1132 vmbus_info: &VmbusInfo,
1133 vtl: u8,
1134 ) -> Builder<'a, Nest<Nest<()>>> {
1135 let mmio = {
1136 let mut ranges = Vec::new();
1137 for entry in &vmbus_info.mmio {
1138 ranges.push(entry.start());
1139 ranges.push(entry.start());
1140 ranges.push(entry.len());
1141 }
1142 ranges
1143 };
1144 let name = if mmio.is_empty() {
1145 format!("vmbus-vtl{vtl}")
1146 } else {
1147 format!("vmbus-vtl{vtl}@{:x}", mmio[0])
1148 };
1149 bus.start_node(&name)
1150 .unwrap()
1151 .add_u32(ids.p_address_cells, 2)
1152 .unwrap()
1153 .add_u32(ids.p_size_cells, 2)
1154 .unwrap()
1155 .add_str(ids.p_compatible, "microsoft,vmbus")
1156 .unwrap()
1157 .add_u64_array(ids.p_ranges, &mmio)
1158 .unwrap()
1159 .add_u32(ids.p_vtl, vtl as u32)
1160 .unwrap()
1161 .add_u32(ids.p_vmbus_connection_id, vmbus_info.connection_id)
1162 .unwrap()
1163 .end_node()
1164 .unwrap()
1165 }
1166
1167 fn build_dt(context: &TestParsedDeviceTree) -> Vec<u8> {
1169 let mut buf = vec![0; 25600];
1170 let mut builder = Builder::new(BuilderConfig {
1171 blob_buffer: &mut buf,
1172 string_table_cap: 1024,
1173 memory_reservations: &[],
1174 })
1175 .expect("can build the DT builder");
1176 let p_address_cells = builder.add_string("#address-cells").unwrap();
1177 let p_size_cells = builder.add_string("#size-cells").unwrap();
1178 let p_model = builder.add_string("model").unwrap();
1179 let p_reg = builder.add_string("reg").unwrap();
1180 let p_ranges = builder.add_string("ranges").unwrap();
1181 let p_device_type = builder.add_string("device_type").unwrap();
1182 let p_status = builder.add_string("status").unwrap();
1183 let p_igvm_type = builder
1184 .add_string(igvm_defs::dt::IGVM_DT_IGVM_TYPE_PROPERTY)
1185 .unwrap();
1186 let p_numa_node_id = builder.add_string("numa-node-id").unwrap();
1187 let p_compatible = builder.add_string("compatible").unwrap();
1188 let p_vmbus_connection_id = builder
1189 .add_string("microsoft,message-connection-id")
1190 .unwrap();
1191 let p_vtl = builder
1192 .add_string(igvm_defs::dt::IGVM_DT_VTL_PROPERTY)
1193 .unwrap();
1194 let p_bootargs = builder.add_string("bootargs").unwrap();
1195 let p_clock_frequency = builder.add_string("clock-frequency").unwrap();
1196 let p_current_speed = builder.add_string("current-speed").unwrap();
1197 let p_interrupts = builder.add_string("interrupts").unwrap();
1198
1199 let mut cpus = builder
1200 .start_node("")
1201 .unwrap()
1202 .add_u32(p_address_cells, 2)
1203 .unwrap() .add_u32(p_size_cells, 2)
1205 .unwrap() .add_str(p_model, "microsoft,hyperv")
1207 .unwrap()
1208 .start_node("cpus")
1209 .unwrap()
1210 .add_u32(p_address_cells, 1)
1211 .unwrap()
1212 .add_u32(p_size_cells, 0)
1213 .unwrap();
1214
1215 for (index, cpu) in context.cpus.iter().enumerate() {
1217 let name = format!("cpu@{:x}", index);
1218 cpus = cpus
1219 .start_node(name.as_ref())
1220 .unwrap()
1221 .add_str(p_device_type, "cpu")
1222 .unwrap()
1223 .add_u32(p_reg, cpu.reg as u32)
1224 .unwrap()
1225 .add_u32(p_numa_node_id, cpu.vnode)
1226 .unwrap()
1227 .add_str(p_status, "okay")
1228 .unwrap()
1229 .end_node()
1230 .unwrap();
1231 }
1232
1233 let mut root = cpus.end_node().unwrap();
1234
1235 for MemoryEntry {
1238 range,
1239 mem_type,
1240 vnode,
1241 } in context.memory.iter().rev()
1242 {
1243 let name = format!("memory@{:x}", range.start());
1244 root = root
1245 .start_node(name.as_ref())
1246 .unwrap()
1247 .add_str(p_device_type, "memory")
1248 .unwrap()
1249 .add_u64_array(p_reg, &[range.start(), range.len()])
1250 .unwrap()
1251 .add_u32(p_igvm_type, mem_type.0 as u32)
1252 .unwrap()
1253 .add_u32(p_numa_node_id, *vnode)
1254 .unwrap()
1255 .end_node()
1256 .unwrap();
1257 }
1258
1259 if let Some(gic) = &context.gic {
1261 let p_interrupt_cells = root.add_string("#interrupt-cells").unwrap();
1262 let p_redist_regions = root.add_string("#redistributor-regions").unwrap();
1263 let p_redist_stride = root.add_string("redistributor-stride").unwrap();
1264 let p_interrupt_controller = root.add_string("interrupt-controller").unwrap();
1265 let p_phandle = root.add_string("phandle").unwrap();
1266 let name = format!("intc@{}", gic.gic_distributor_base);
1267 root = root
1268 .start_node(name.as_ref())
1269 .unwrap()
1270 .add_str(p_compatible, "arm,gic-v3")
1271 .unwrap()
1272 .add_u32(p_redist_regions, 1)
1273 .unwrap()
1274 .add_u64(p_redist_stride, gic.gic_redistributor_stride)
1275 .unwrap()
1276 .add_u64_array(
1277 p_reg,
1278 &[
1279 gic.gic_distributor_base,
1280 gic.gic_distributor_size,
1281 gic.gic_redistributors_base,
1282 gic.gic_redistributors_size,
1283 ],
1284 )
1285 .unwrap()
1286 .add_u32(p_address_cells, 2)
1287 .unwrap()
1288 .add_u32(p_size_cells, 2)
1289 .unwrap()
1290 .add_u32(p_interrupt_cells, 3)
1291 .unwrap()
1292 .add_null(p_interrupt_controller)
1293 .unwrap()
1294 .add_u32(p_phandle, 1)
1295 .unwrap()
1296 .add_null(p_ranges)
1297 .unwrap()
1298 .end_node()
1299 .unwrap();
1300 }
1301
1302 let mut simple_bus = root
1304 .start_node("bus")
1305 .unwrap()
1306 .add_str(p_compatible, "simple-bus")
1307 .unwrap()
1308 .add_u32(p_address_cells, 2)
1309 .unwrap()
1310 .add_u32(p_size_cells, 2)
1311 .unwrap()
1312 .add_prop_array(p_ranges, &[])
1313 .unwrap();
1314
1315 let vmbus_ids = VmbusStringIds {
1316 p_address_cells,
1317 p_size_cells,
1318 p_compatible,
1319 p_ranges,
1320 p_vtl,
1321 p_vmbus_connection_id,
1322 };
1323
1324 if let Some(vmbus) = &context.vmbus_vtl0 {
1326 simple_bus = add_vmbus(&vmbus_ids, simple_bus, vmbus, 0);
1327 }
1328
1329 if let Some(vmbus) = &context.vmbus_vtl2 {
1331 simple_bus = add_vmbus(&vmbus_ids, simple_bus, vmbus, 2);
1332 }
1333
1334 root = simple_bus.end_node().unwrap();
1335
1336 if context.com3_serial {
1338 let mut io_port_bus = root
1339 .start_node("io-bus")
1340 .unwrap()
1341 .add_str(p_compatible, "x86-pio-bus")
1342 .unwrap()
1343 .add_u32(p_address_cells, 1)
1344 .unwrap()
1345 .add_u32(p_size_cells, 0)
1346 .unwrap()
1347 .add_prop_array(p_ranges, &[])
1348 .unwrap();
1349
1350 let serial_name = format!("serial@{:x}", COM3_REG_BASE);
1351 io_port_bus = io_port_bus
1352 .start_node(&serial_name)
1353 .unwrap()
1354 .add_str(p_compatible, "ns16550")
1355 .unwrap()
1356 .add_u32(p_clock_frequency, 0)
1357 .unwrap()
1358 .add_u32(p_current_speed, 115200)
1359 .unwrap()
1360 .add_u64_array(p_reg, &[COM3_REG_BASE, 0x8])
1361 .unwrap()
1362 .add_u64_array(p_interrupts, &[4])
1363 .unwrap()
1364 .end_node()
1365 .unwrap();
1366
1367 root = io_port_bus.end_node().unwrap();
1368 }
1369
1370 root = root
1372 .start_node("chosen")
1373 .unwrap()
1374 .add_str(p_bootargs, context.command_line.as_ref())
1375 .unwrap()
1376 .end_node()
1377 .unwrap();
1378
1379 let p_memory_allocation_mode = root.add_string("memory-allocation-mode").unwrap();
1381 let p_memory_allocation_size = root.add_string("memory-size").unwrap();
1382 let p_mmio_allocation_size = root.add_string("mmio-size").unwrap();
1383 let p_device_dma_page_count = root.add_string("total-pages").unwrap();
1384 let mut openhcl = root.start_node("openhcl").unwrap();
1385
1386 let memory_alloc_str = match context.memory_allocation_mode {
1387 MemoryAllocationMode::Host => "host",
1388 MemoryAllocationMode::Vtl2 {
1389 memory_size,
1390 mmio_size,
1391 } => {
1392 if let Some(memory_size) = memory_size {
1394 openhcl = openhcl
1395 .add_u64(p_memory_allocation_size, memory_size)
1396 .unwrap();
1397 }
1398 if let Some(mmio_size) = mmio_size {
1399 openhcl = openhcl.add_u64(p_mmio_allocation_size, mmio_size).unwrap();
1400 }
1401 "vtl2"
1402 }
1403 };
1404
1405 openhcl = openhcl
1406 .add_str(p_memory_allocation_mode, memory_alloc_str)
1407 .unwrap();
1408
1409 if let Some(device_dma_page_count) = context.device_dma_page_count {
1411 openhcl = openhcl
1412 .start_node("device-dma")
1413 .unwrap()
1414 .add_u64(p_device_dma_page_count, device_dma_page_count)
1415 .unwrap()
1416 .end_node()
1417 .unwrap();
1418 }
1419
1420 root = openhcl.end_node().unwrap();
1421
1422 let bytes_used = root
1423 .end_node()
1424 .unwrap()
1425 .build(context.boot_cpuid_phys)
1426 .unwrap();
1427 buf.truncate(bytes_used);
1428
1429 buf
1430 }
1431
1432 fn create_parsed(
1434 dt_size: usize,
1435 memory: &[MemoryEntry],
1436 cpus: &[CpuEntry],
1437 bsp: u32,
1438 vmbus_vtl0: Option<VmbusInfo>,
1439 vmbus_vtl2: Option<VmbusInfo>,
1440 command_line: &str,
1441 com3_serial: bool,
1442 gic: Option<GicInfo>,
1443 memory_allocation_mode: MemoryAllocationMode,
1444 device_dma_page_count: Option<u64>,
1445 ) -> TestParsedDeviceTree {
1446 let mut context = TestParsedDeviceTree::new();
1447 context.device_tree_size = dt_size;
1448 context.boot_cpuid_phys = bsp;
1449 write!(context.command_line, "{command_line}").unwrap();
1450 context.com3_serial = com3_serial;
1451 context.vmbus_vtl0 = vmbus_vtl0;
1452 context.vmbus_vtl2 = vmbus_vtl2;
1453 context.memory.try_extend_from_slice(memory).unwrap();
1454 context.cpus.try_extend_from_slice(cpus).unwrap();
1455 context.gic = gic;
1456 context.memory_allocation_mode = memory_allocation_mode;
1457 context.device_dma_page_count = device_dma_page_count;
1458 context
1459 }
1460
1461 #[test]
1462 fn test_basic_dt() {
1463 let orig = create_parsed(
1464 2608,
1465 &[
1466 MemoryEntry {
1467 range: MemoryRange::try_new(0..(1024 * HV_PAGE_SIZE)).unwrap(),
1468 mem_type: MemoryMapEntryType::MEMORY,
1469 vnode: 0,
1470 },
1471 MemoryEntry {
1472 range: MemoryRange::try_new((1024 * HV_PAGE_SIZE)..(4024 * HV_PAGE_SIZE))
1473 .unwrap(),
1474 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
1475 vnode: 0,
1476 },
1477 MemoryEntry {
1478 range: MemoryRange::try_new((14024 * HV_PAGE_SIZE)..(102400 * HV_PAGE_SIZE))
1479 .unwrap(),
1480 mem_type: MemoryMapEntryType::MEMORY,
1481 vnode: 0,
1482 },
1483 ],
1484 &[
1485 CpuEntry { reg: 12, vnode: 0 },
1486 CpuEntry { reg: 42, vnode: 0 },
1487 CpuEntry { reg: 23, vnode: 0 },
1488 CpuEntry { reg: 24, vnode: 0 },
1489 ],
1490 42,
1491 Some(VmbusInfo {
1492 mmio: new_vmbus_mmio(&[
1493 MemoryRange::try_new((4024 * HV_PAGE_SIZE)..(4096 * HV_PAGE_SIZE)).unwrap(),
1494 MemoryRange::try_new((102400 * HV_PAGE_SIZE)..(102800 * HV_PAGE_SIZE)).unwrap(),
1495 ]),
1496 connection_id: 1,
1497 }),
1498 Some(VmbusInfo {
1499 mmio: new_vmbus_mmio(&[MemoryRange::try_new(
1500 (102800 * HV_PAGE_SIZE)..(102900 * HV_PAGE_SIZE),
1501 )
1502 .unwrap()]),
1503 connection_id: 4,
1504 }),
1505 "THIS_IS_A_BOOT_ARG=1",
1506 false,
1507 Some(GicInfo {
1508 gic_distributor_base: 0x20000,
1509 gic_distributor_size: 0x10000,
1510 gic_redistributors_base: 0x40000,
1511 gic_redistributors_size: 0x60000,
1512 gic_redistributor_stride: 0x20000,
1513 }),
1514 MemoryAllocationMode::Host,
1515 Some(1234),
1516 );
1517
1518 let dt = build_dt(&orig);
1519 let mut parsed = TestParsedDeviceTree::new();
1520 let parsed = TestParsedDeviceTree::parse(&dt, &mut parsed).unwrap();
1521 assert_eq!(&orig, parsed);
1522 }
1523
1524 #[test]
1525 fn test_numa_dt() {
1526 let orig = create_parsed(
1527 2352,
1528 &[
1529 MemoryEntry {
1530 range: MemoryRange::try_new(0..(1024 * HV_PAGE_SIZE)).unwrap(),
1531 mem_type: MemoryMapEntryType::MEMORY,
1532 vnode: 0,
1533 },
1534 MemoryEntry {
1535 range: MemoryRange::try_new((1024 * HV_PAGE_SIZE)..(2048 * HV_PAGE_SIZE))
1536 .unwrap(),
1537 mem_type: MemoryMapEntryType::MEMORY,
1538 vnode: 1,
1539 },
1540 MemoryEntry {
1541 range: MemoryRange::try_new((2048 * HV_PAGE_SIZE)..(3072 * HV_PAGE_SIZE))
1542 .unwrap(),
1543 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
1544 vnode: 0,
1545 },
1546 MemoryEntry {
1547 range: MemoryRange::try_new((3072 * HV_PAGE_SIZE)..(4096 * HV_PAGE_SIZE))
1548 .unwrap(),
1549 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
1550 vnode: 1,
1551 },
1552 MemoryEntry {
1553 range: MemoryRange::try_new((4096 * HV_PAGE_SIZE)..(51200 * HV_PAGE_SIZE))
1554 .unwrap(),
1555 mem_type: MemoryMapEntryType::MEMORY,
1556 vnode: 0,
1557 },
1558 MemoryEntry {
1559 range: MemoryRange::try_new((51200 * HV_PAGE_SIZE)..(102400 * HV_PAGE_SIZE))
1560 .unwrap(),
1561 mem_type: MemoryMapEntryType::MEMORY,
1562 vnode: 1,
1563 },
1564 ],
1565 &[
1566 CpuEntry { reg: 12, vnode: 0 },
1567 CpuEntry { reg: 42, vnode: 1 },
1568 CpuEntry { reg: 23, vnode: 0 },
1569 CpuEntry { reg: 24, vnode: 1 },
1570 ],
1571 23,
1572 None,
1573 None,
1574 "",
1575 false,
1576 None,
1577 MemoryAllocationMode::Vtl2 {
1578 memory_size: Some(1000 * 1024 * 1024), mmio_size: Some(128 * 1024 * 1024), },
1581 None,
1582 );
1583
1584 let dt = build_dt(&orig);
1585 let mut parsed = TestParsedDeviceTree::new();
1586 let parsed = TestParsedDeviceTree::parse(&dt, &mut parsed).unwrap();
1587 assert_eq!(&orig, parsed);
1588 }
1589
1590 #[test]
1593 fn test_overlapping_memory() {
1594 let bad = create_parsed(
1596 0,
1597 &[
1598 MemoryEntry {
1599 range: MemoryRange::try_new(0..(1024 * HV_PAGE_SIZE)).unwrap(),
1600 mem_type: MemoryMapEntryType::MEMORY,
1601 vnode: 0,
1602 },
1603 MemoryEntry {
1604 range: MemoryRange::try_new(4096..(1024 * HV_PAGE_SIZE)).unwrap(),
1605 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
1606 vnode: 0,
1607 },
1608 MemoryEntry {
1609 range: MemoryRange::try_new((14024 * HV_PAGE_SIZE)..(102400 * HV_PAGE_SIZE))
1610 .unwrap(),
1611 mem_type: MemoryMapEntryType::MEMORY,
1612 vnode: 0,
1613 },
1614 ],
1615 &[
1616 CpuEntry { reg: 12, vnode: 0 },
1617 CpuEntry { reg: 42, vnode: 0 },
1618 CpuEntry { reg: 23, vnode: 0 },
1619 CpuEntry { reg: 24, vnode: 0 },
1620 ],
1621 42,
1622 None,
1623 None,
1624 "THIS_IS_A_BOOT_ARG=1",
1625 false,
1626 None,
1627 MemoryAllocationMode::Host,
1628 None,
1629 );
1630
1631 let dt = build_dt(&bad);
1632 let mut parsed = TestParsedDeviceTree::new();
1633 assert!(matches!(
1634 TestParsedDeviceTree::parse(&dt, &mut parsed),
1635 Err(Error(ErrorKind::MemoryRegOverlap { .. }))
1636 ));
1637
1638 let bad = create_parsed(
1640 0,
1641 &[
1642 MemoryEntry {
1643 range: MemoryRange::try_new(4096..(1024 * HV_PAGE_SIZE)).unwrap(),
1644 mem_type: MemoryMapEntryType::MEMORY,
1645 vnode: 0,
1646 },
1647 MemoryEntry {
1648 range: MemoryRange::try_new(0..(102400 * HV_PAGE_SIZE)).unwrap(),
1649 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
1650 vnode: 0,
1651 },
1652 ],
1653 &[
1654 CpuEntry { reg: 12, vnode: 0 },
1655 CpuEntry { reg: 42, vnode: 0 },
1656 CpuEntry { reg: 23, vnode: 0 },
1657 CpuEntry { reg: 24, vnode: 0 },
1658 ],
1659 42,
1660 None,
1661 None,
1662 "THIS_IS_A_BOOT_ARG=1",
1663 false,
1664 None,
1665 MemoryAllocationMode::Host,
1666 None,
1667 );
1668
1669 let dt = build_dt(&bad);
1670 let mut parsed = TestParsedDeviceTree::new();
1671 assert!(matches!(
1672 TestParsedDeviceTree::parse(&dt, &mut parsed),
1673 Err(Error(ErrorKind::MemoryRegOverlap { .. }))
1674 ));
1675
1676 let bad = create_parsed(
1678 0,
1679 &[MemoryEntry {
1680 range: MemoryRange::try_new(0..(202400 * HV_PAGE_SIZE)).unwrap(),
1681 mem_type: MemoryMapEntryType::MEMORY,
1682 vnode: 0,
1683 }],
1684 &[
1685 CpuEntry { reg: 12, vnode: 0 },
1686 CpuEntry { reg: 42, vnode: 0 },
1687 CpuEntry { reg: 23, vnode: 0 },
1688 CpuEntry { reg: 24, vnode: 0 },
1689 ],
1690 42,
1691 Some(VmbusInfo {
1692 mmio: new_vmbus_mmio(&[MemoryRange::try_new(
1693 (4024 * HV_PAGE_SIZE)..(4096 * HV_PAGE_SIZE),
1694 )
1695 .unwrap()]),
1696 connection_id: 1,
1697 }),
1698 Some(VmbusInfo {
1699 mmio: new_vmbus_mmio(&[MemoryRange::try_new(
1700 (102800 * HV_PAGE_SIZE)..(102900 * HV_PAGE_SIZE),
1701 )
1702 .unwrap()]),
1703 connection_id: 4,
1704 }),
1705 "THIS_IS_A_BOOT_ARG=1",
1706 false,
1707 None,
1708 MemoryAllocationMode::Host,
1709 None,
1710 );
1711
1712 let dt = build_dt(&bad);
1713 let mut parsed = TestParsedDeviceTree::new();
1714 assert!(matches!(
1715 TestParsedDeviceTree::parse(&dt, &mut parsed),
1716 Err(Error(ErrorKind::VmbusMmioOverlapsRam { .. }))
1717 ));
1718
1719 let bad = create_parsed(
1721 0,
1722 &[MemoryEntry {
1723 range: MemoryRange::try_new(0..(1024 * HV_PAGE_SIZE)).unwrap(),
1724 mem_type: MemoryMapEntryType::MEMORY,
1725 vnode: 0,
1726 }],
1727 &[
1728 CpuEntry { reg: 12, vnode: 0 },
1729 CpuEntry { reg: 42, vnode: 0 },
1730 CpuEntry { reg: 23, vnode: 0 },
1731 CpuEntry { reg: 24, vnode: 0 },
1732 ],
1733 42,
1734 Some(VmbusInfo {
1735 mmio: new_vmbus_mmio(&[
1736 MemoryRange::try_new((4000 * HV_PAGE_SIZE)..(4096 * HV_PAGE_SIZE)).unwrap(),
1737 MemoryRange::EMPTY,
1738 ]),
1739 connection_id: 1,
1740 }),
1741 Some(VmbusInfo {
1742 mmio: new_vmbus_mmio(&[
1743 MemoryRange::try_new((4020 * HV_PAGE_SIZE)..(102900 * HV_PAGE_SIZE)).unwrap(),
1744 MemoryRange::EMPTY,
1745 ]),
1746 connection_id: 4,
1747 }),
1748 "THIS_IS_A_BOOT_ARG=1",
1749 false,
1750 None,
1751 MemoryAllocationMode::Host,
1752 None,
1753 );
1754
1755 let dt = build_dt(&bad);
1756 let mut parsed = TestParsedDeviceTree::new();
1757 assert!(matches!(
1758 TestParsedDeviceTree::parse(&dt, &mut parsed),
1759 Err(Error(ErrorKind::VmbusMmioOverlapsVmbusMmio { .. }))
1760 ));
1761 }
1762
1763 #[test]
1765 fn test_com3_serial_output() {
1766 let orig = create_parsed(
1767 2560,
1768 &[
1769 MemoryEntry {
1770 range: MemoryRange::try_new(0..(1024 * HV_PAGE_SIZE)).unwrap(),
1771 mem_type: MemoryMapEntryType::MEMORY,
1772 vnode: 0,
1773 },
1774 MemoryEntry {
1775 range: MemoryRange::try_new((1024 * HV_PAGE_SIZE)..(4024 * HV_PAGE_SIZE))
1776 .unwrap(),
1777 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
1778 vnode: 0,
1779 },
1780 MemoryEntry {
1781 range: MemoryRange::try_new((14024 * HV_PAGE_SIZE)..(102400 * HV_PAGE_SIZE))
1782 .unwrap(),
1783 mem_type: MemoryMapEntryType::MEMORY,
1784 vnode: 0,
1785 },
1786 ],
1787 &[
1788 CpuEntry { reg: 12, vnode: 0 },
1789 CpuEntry { reg: 42, vnode: 0 },
1790 CpuEntry { reg: 23, vnode: 0 },
1791 CpuEntry { reg: 24, vnode: 0 },
1792 ],
1793 42,
1794 Some(VmbusInfo {
1795 mmio: new_vmbus_mmio(&[
1796 MemoryRange::try_new((4024 * HV_PAGE_SIZE)..(4096 * HV_PAGE_SIZE)).unwrap(),
1797 MemoryRange::try_new((102400 * HV_PAGE_SIZE)..(102800 * HV_PAGE_SIZE)).unwrap(),
1798 ]),
1799 connection_id: 1,
1800 }),
1801 Some(VmbusInfo {
1802 mmio: new_vmbus_mmio(&[MemoryRange::try_new(
1803 (102800 * HV_PAGE_SIZE)..(102900 * HV_PAGE_SIZE),
1804 )
1805 .unwrap()]),
1806 connection_id: 4,
1807 }),
1808 "THIS_IS_A_BOOT_ARG=1",
1809 true,
1810 None,
1811 MemoryAllocationMode::Host,
1812 None,
1813 );
1814
1815 let dt = build_dt(&orig);
1816 let mut parsed = TestParsedDeviceTree::new();
1817 let parsed = TestParsedDeviceTree::parse(&dt, &mut parsed).unwrap();
1818
1819 assert_eq!(&orig, parsed);
1820 assert!(parsed.com3_serial);
1821 }
1822}