1use memory_range::MemoryRange;
7use memory_range::subtract_ranges;
8use thiserror::Error;
9
10const PAGE_SIZE: u64 = 4096;
11const FOUR_GB: u64 = 0x1_0000_0000;
12
13#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
15#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
16#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
17pub struct MemoryRangeWithNode {
18 pub range: MemoryRange,
20 pub vnode: u32,
22}
23
24impl core::fmt::Display for MemoryRangeWithNode {
25 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26 write!(f, "{}({})", self.range, self.vnode)
27 }
28}
29
30#[derive(Debug, Clone)]
32#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
33pub struct MemoryLayout {
34 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges_with_metadata"))]
35 ram: Vec<MemoryRangeWithNode>,
36 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges"))]
37 mmio: Vec<MemoryRange>,
38 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges"))]
39 pci_ecam: Vec<MemoryRange>,
40 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges"))]
41 pci_mmio: Vec<MemoryRange>,
42 vtl2_range: Option<MemoryRange>,
45}
46
47#[cfg(feature = "inspect")]
48fn inspect_ranges(ranges: &[MemoryRange]) -> impl '_ + inspect::Inspect {
49 inspect::iter_by_key(ranges.iter().map(|range| {
50 (
51 range.to_string(),
52 inspect::adhoc(|i| {
53 i.respond().hex("length", range.len());
54 }),
55 )
56 }))
57}
58
59#[cfg(feature = "inspect")]
60fn inspect_ranges_with_metadata(ranges: &[MemoryRangeWithNode]) -> impl '_ + inspect::Inspect {
61 inspect::iter_by_key(ranges.iter().map(|range| {
62 (
63 range.range.to_string(),
64 inspect::adhoc(|i| {
65 i.respond()
66 .hex("length", range.range.len())
67 .hex("vnode", range.vnode);
68 }),
69 )
70 }))
71}
72
73#[derive(Debug, Error)]
75pub enum Error {
76 #[error("invalid memory size")]
78 BadSize,
79 #[error("invalid NUMA node memory size")]
81 BadNumaSize,
82 #[error("empty NUMA memory sizes")]
84 EmptyNumaSizes,
85 #[error("invalid MMIO gap configuration")]
87 BadMmioGaps,
88 #[error("invalid memory or MMIO ranges")]
90 BadMemoryRanges,
91 #[error("vtl2 range is below end of ram")]
93 Vtl2RangeBeforeEndOfRam,
94}
95
96fn validate_ranges(ranges: &[MemoryRange]) -> Result<(), Error> {
97 validate_ranges_core(ranges, |x| x)
98}
99
100fn validate_ranges_with_metadata(ranges: &[MemoryRangeWithNode]) -> Result<(), Error> {
101 validate_ranges_core(ranges, |x| &x.range)
102}
103
104fn validate_ranges_core<T>(ranges: &[T], getter: impl Fn(&T) -> &MemoryRange) -> Result<(), Error> {
107 if ranges.iter().any(|x| getter(x).is_empty())
108 || !ranges.iter().zip(ranges.iter().skip(1)).all(|(x, y)| {
109 let x = getter(x);
110 let y = getter(y);
111 x <= y && !x.overlaps(y)
112 })
113 {
114 return Err(Error::BadMemoryRanges);
115 }
116
117 Ok(())
118}
119
120#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122pub enum AddressType {
123 Ram,
125 Mmio,
127 PciEcam,
129 PciMmio,
131}
132
133impl MemoryLayout {
134 pub fn new(
146 ram_size: u64,
147 mmio_gaps: &[MemoryRange],
148 pci_ecam_gaps: &[MemoryRange],
149 pci_mmio_gaps: &[MemoryRange],
150 vtl2_range: Option<MemoryRange>,
151 ) -> Result<Self, Error> {
152 if ram_size == 0 || ram_size & (PAGE_SIZE - 1) != 0 {
153 return Err(Error::BadSize);
154 }
155 Self::new_with_numa(
156 &[ram_size],
157 mmio_gaps,
158 pci_ecam_gaps,
159 pci_mmio_gaps,
160 vtl2_range,
161 )
162 }
163
164 pub fn new_with_numa(
175 numa_mem_sizes: &[u64],
176 mmio_gaps: &[MemoryRange],
177 pci_ecam_gaps: &[MemoryRange],
178 pci_mmio_gaps: &[MemoryRange],
179 vtl2_range: Option<MemoryRange>,
180 ) -> Result<Self, Error> {
181 if numa_mem_sizes.is_empty() {
182 return Err(Error::EmptyNumaSizes);
183 }
184
185 for &size in numa_mem_sizes {
186 if size == 0 || size & (PAGE_SIZE - 1) != 0 {
187 return Err(Error::BadNumaSize);
188 }
189 }
190
191 let ram_size: u64 = numa_mem_sizes
192 .iter()
193 .try_fold(0u64, |acc, &s| acc.checked_add(s))
194 .ok_or(Error::BadSize)?;
195
196 validate_ranges(mmio_gaps)?;
197 validate_ranges(pci_ecam_gaps)?;
198 validate_ranges(pci_mmio_gaps)?;
199
200 let mut combined_gaps = mmio_gaps
201 .iter()
202 .chain(pci_ecam_gaps)
203 .chain(pci_mmio_gaps)
204 .copied()
205 .collect::<Vec<_>>();
206 combined_gaps.sort();
207 validate_ranges(&combined_gaps)?;
208
209 let available = subtract_ranges(
210 [MemoryRange::new(0..MemoryRange::MAX_ADDRESS)],
211 combined_gaps,
212 );
213
214 let mut ram = Vec::new();
216 let mut remaining = ram_size;
217 let mut node_idx = 0;
218 let mut node_remaining = numa_mem_sizes[0];
219
220 for range in available {
221 let range_size = remaining.min(range.len());
222 let mut offset = 0;
223
224 while offset < range_size {
225 if node_remaining == 0 {
226 node_idx += 1;
227 node_remaining = *numa_mem_sizes
228 .get(node_idx)
229 .expect("node budget exhausted before all RAM placed");
230 }
231
232 let piece = (range_size - offset).min(node_remaining);
233 let start = range.start() + offset;
234 ram.push(MemoryRangeWithNode {
235 range: MemoryRange::new(start..start + piece),
236 vnode: node_idx as u32,
237 });
238 offset += piece;
239 node_remaining -= piece;
240 }
241
242 remaining -= range_size;
243
244 if remaining == 0 {
245 break;
246 }
247 }
248
249 Self::build(
250 ram,
251 mmio_gaps.to_vec(),
252 pci_ecam_gaps.to_vec(),
253 pci_mmio_gaps.to_vec(),
254 vtl2_range,
255 )
256 }
257
258 pub fn new_from_ranges(
264 memory: &[MemoryRangeWithNode],
265 gaps: &[MemoryRange],
266 ) -> Result<Self, Error> {
267 validate_ranges_with_metadata(memory)?;
268 validate_ranges(gaps)?;
269 Self::build(memory.to_vec(), gaps.to_vec(), vec![], vec![], None)
270 }
271
272 pub fn new_from_resolved_ranges(
282 ram: Vec<MemoryRangeWithNode>,
283 mmio_gaps: Vec<MemoryRange>,
284 pci_ecam_gaps: Vec<MemoryRange>,
285 pci_mmio_gaps: Vec<MemoryRange>,
286 vtl2_range: Option<MemoryRange>,
287 ) -> Result<Self, Error> {
288 validate_ranges_with_metadata(&ram)?;
289 let non_empty_mmio: Vec<_> = mmio_gaps
292 .iter()
293 .copied()
294 .filter(|r| !r.is_empty())
295 .collect();
296 validate_ranges(&non_empty_mmio)?;
297 validate_ranges(&pci_ecam_gaps)?;
298 validate_ranges(&pci_mmio_gaps)?;
299
300 Self::build(ram, mmio_gaps, pci_ecam_gaps, pci_mmio_gaps, vtl2_range)
301 }
302
303 fn build(
307 ram: Vec<MemoryRangeWithNode>,
308 mmio: Vec<MemoryRange>,
309 pci_ecam: Vec<MemoryRange>,
310 pci_mmio: Vec<MemoryRange>,
311 vtl2_range: Option<MemoryRange>,
312 ) -> Result<Self, Error> {
313 let mut all_ranges = ram
317 .iter()
318 .map(|x| &x.range)
319 .chain(&mmio)
320 .chain(&vtl2_range)
321 .chain(&pci_ecam)
322 .chain(&pci_mmio)
323 .copied()
324 .filter(|r| !r.is_empty())
325 .collect::<Vec<_>>();
326
327 all_ranges.sort();
328 validate_ranges(&all_ranges)?;
329
330 if all_ranges
331 .iter()
332 .zip(all_ranges.iter().skip(1))
333 .any(|(x, y)| x.overlaps(y))
334 {
335 return Err(Error::BadMemoryRanges);
336 }
337
338 let last_ram_entry = ram.last().ok_or(Error::BadMemoryRanges)?;
339 let end_of_ram = last_ram_entry.range.end();
340
341 if let Some(range) = vtl2_range {
342 if range.start() < end_of_ram {
343 return Err(Error::Vtl2RangeBeforeEndOfRam);
344 }
345 }
346
347 Ok(Self {
348 ram,
349 mmio,
350 pci_ecam,
351 pci_mmio,
352 vtl2_range,
353 })
354 }
355
356 pub fn mmio(&self) -> &[MemoryRange] {
358 &self.mmio
359 }
360
361 pub fn ram(&self) -> &[MemoryRangeWithNode] {
363 &self.ram
364 }
365
366 pub fn vtl2_range(&self) -> Option<MemoryRange> {
370 self.vtl2_range
371 }
372
373 pub fn ram_size(&self) -> u64 {
375 self.ram.iter().map(|r| r.range.len()).sum()
376 }
377
378 pub fn end_of_ram(&self) -> u64 {
380 self.ram.last().expect("mmio set").range.end()
382 }
383
384 pub fn ram_below_4gb(&self) -> u64 {
386 self.ram
387 .iter()
388 .filter(|r| r.range.end() < FOUR_GB)
389 .map(|r| r.range.len())
390 .sum()
391 }
392
393 pub fn ram_above_4gb(&self) -> u64 {
395 self.ram
396 .iter()
397 .filter(|r| r.range.end() >= FOUR_GB)
398 .map(|r| r.range.len())
399 .sum()
400 }
401
402 pub fn ram_above_high_mmio(&self) -> Option<u64> {
406 if self.mmio.len() != 2 {
407 return None;
408 }
409
410 Some(
411 self.ram
412 .iter()
413 .filter(|r| r.range.start() >= self.mmio[1].end())
414 .map(|r| r.range.len())
415 .sum(),
416 )
417 }
418
419 pub fn max_ram_below_4gb(&self) -> Option<u64> {
423 Some(
424 self.ram
425 .iter()
426 .rev()
427 .find(|r| r.range.end() < FOUR_GB)?
428 .range
429 .end(),
430 )
431 }
432
433 pub fn end_of_layout(&self) -> u64 {
435 [
436 self.mmio
437 .iter()
438 .filter(|r| !r.is_empty())
439 .map(|r| r.end())
440 .max()
441 .unwrap_or(0),
442 self.end_of_ram(),
443 self.pci_ecam.last().map(|r| r.end()).unwrap_or(0),
444 self.pci_mmio.last().map(|r| r.end()).unwrap_or(0),
445 ]
446 .into_iter()
447 .max()
448 .unwrap()
449 }
450
451 pub fn probe_address(&self, address: u64) -> Option<AddressType> {
457 let ranges = self
458 .ram
459 .iter()
460 .map(|r| (&r.range, AddressType::Ram))
461 .chain(self.mmio.iter().map(|r| (r, AddressType::Mmio)))
462 .chain(self.pci_ecam.iter().map(|r| (r, AddressType::PciEcam)))
463 .chain(self.pci_mmio.iter().map(|r| (r, AddressType::PciMmio)));
464
465 for (range, address_type) in ranges {
466 if range.contains_addr(address) {
467 return Some(address_type);
468 }
469 }
470
471 None
472 }
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478
479 const KB: u64 = 1024;
480 const MB: u64 = 1024 * KB;
481 const GB: u64 = 1024 * MB;
482 const TB: u64 = 1024 * GB;
483
484 #[test]
485 fn layout() {
486 let mmio = &[
487 MemoryRange::new(GB..2 * GB),
488 MemoryRange::new(3 * GB..4 * GB),
489 ];
490 let ram = &[
491 MemoryRangeWithNode {
492 range: MemoryRange::new(0..GB),
493 vnode: 0,
494 },
495 MemoryRangeWithNode {
496 range: MemoryRange::new(2 * GB..3 * GB),
497 vnode: 0,
498 },
499 MemoryRangeWithNode {
500 range: MemoryRange::new(4 * GB..TB + 2 * GB),
501 vnode: 0,
502 },
503 ];
504
505 let layout = MemoryLayout::new(TB, mmio, &[], &[], None).unwrap();
506 assert_eq!(
507 layout.ram(),
508 &[
509 MemoryRangeWithNode {
510 range: MemoryRange::new(0..GB),
511 vnode: 0
512 },
513 MemoryRangeWithNode {
514 range: MemoryRange::new(2 * GB..3 * GB),
515 vnode: 0
516 },
517 MemoryRangeWithNode {
518 range: MemoryRange::new(4 * GB..TB + 2 * GB),
519 vnode: 0
520 },
521 ]
522 );
523 assert_eq!(layout.mmio(), mmio);
524 assert_eq!(layout.ram_size(), TB);
525 assert_eq!(layout.end_of_ram(), TB + 2 * GB);
526 assert_eq!(layout.end_of_layout(), TB + 2 * GB);
527
528 let layout = MemoryLayout::new_from_ranges(ram, mmio).unwrap();
529 assert_eq!(
530 layout.ram(),
531 &[
532 MemoryRangeWithNode {
533 range: MemoryRange::new(0..GB),
534 vnode: 0
535 },
536 MemoryRangeWithNode {
537 range: MemoryRange::new(2 * GB..3 * GB),
538 vnode: 0
539 },
540 MemoryRangeWithNode {
541 range: MemoryRange::new(4 * GB..TB + 2 * GB),
542 vnode: 0
543 },
544 ]
545 );
546 assert_eq!(layout.mmio(), mmio);
547 assert_eq!(layout.ram_size(), TB);
548 assert_eq!(layout.end_of_ram(), TB + 2 * GB);
549 assert_eq!(layout.end_of_layout(), TB + 2 * GB);
550 }
551
552 #[test]
553 fn bad_layout() {
554 MemoryLayout::new(TB + 1, &[], &[], &[], None).unwrap_err();
555 let mmio = &[
556 MemoryRange::new(3 * GB..4 * GB),
557 MemoryRange::new(GB..2 * GB),
558 ];
559 MemoryLayout::new(TB, mmio, &[], &[], None).unwrap_err();
560
561 MemoryLayout::new_from_ranges(&[], mmio).unwrap_err();
562
563 let ram = &[MemoryRangeWithNode {
564 range: MemoryRange::new(0..GB),
565 vnode: 0,
566 }];
567 MemoryLayout::new_from_ranges(ram, mmio).unwrap_err();
568
569 let ram = &[MemoryRangeWithNode {
570 range: MemoryRange::new(0..GB + MB),
571 vnode: 0,
572 }];
573 let mmio = &[
574 MemoryRange::new(GB..2 * GB),
575 MemoryRange::new(3 * GB..4 * GB),
576 ];
577 MemoryLayout::new_from_ranges(ram, mmio).unwrap_err();
578
579 let mmio = &[
580 MemoryRange::new(GB..2 * GB),
581 MemoryRange::new(3 * GB..4 * GB),
582 ];
583 let pci_ecam = &[MemoryRange::new(GB..GB + MB)];
584 MemoryLayout::new(TB, mmio, pci_ecam, &[], None).unwrap_err();
585
586 let mmio = &[
587 MemoryRange::new(GB..2 * GB),
588 MemoryRange::new(3 * GB..4 * GB),
589 ];
590 let pci_mmio = &[MemoryRange::new(GB..GB + MB)];
591 MemoryLayout::new(TB, mmio, &[], pci_mmio, None).unwrap_err();
592
593 let pci_ecam = &[MemoryRange::new(GB..GB + MB)];
594 let pci_mmio = &[MemoryRange::new(GB..GB + MB)];
595 MemoryLayout::new(TB, &[], pci_ecam, pci_mmio, None).unwrap_err();
596 }
597
598 #[test]
599 fn resolved_ranges_constructor() {
600 let ram = vec![
601 MemoryRangeWithNode {
602 range: MemoryRange::new(0..GB),
603 vnode: 0,
604 },
605 MemoryRangeWithNode {
606 range: MemoryRange::new(2 * GB..3 * GB),
607 vnode: 1,
608 },
609 ];
610 let mmio = vec![MemoryRange::new(GB..2 * GB)];
611 let pci_ecam = vec![MemoryRange::new(4 * GB..4 * GB + MB)];
612 let pci_mmio = vec![MemoryRange::new(5 * GB..6 * GB)];
613
614 let layout = MemoryLayout::new_from_resolved_ranges(
615 ram.clone(),
616 mmio.clone(),
617 pci_ecam.clone(),
618 pci_mmio.clone(),
619 None,
620 )
621 .unwrap();
622
623 assert_eq!(layout.ram(), ram);
624 assert_eq!(layout.mmio(), mmio);
625 assert_eq!(layout.probe_address(4 * GB), Some(AddressType::PciEcam));
626 assert_eq!(layout.probe_address(5 * GB), Some(AddressType::PciMmio));
627 }
628
629 #[test]
630 fn resolved_ranges_reject_overlap_with_fixed_ranges() {
631 let ram = vec![MemoryRangeWithNode {
632 range: MemoryRange::new(0..2 * GB),
633 vnode: 0,
634 }];
635 let mmio = vec![MemoryRange::new(GB..2 * GB)];
636
637 assert!(MemoryLayout::new_from_resolved_ranges(ram, mmio, vec![], vec![], None).is_err());
638 }
639
640 #[test]
641 fn resolved_ranges_validate_vtl2_against_ram_end() {
642 let ram = vec![
643 MemoryRangeWithNode {
644 range: MemoryRange::new(0..GB),
645 vnode: 0,
646 },
647 MemoryRangeWithNode {
648 range: MemoryRange::new(3 * GB..4 * GB),
649 vnode: 0,
650 },
651 ];
652 let mmio = vec![MemoryRange::new(GB..2 * GB)];
653 let vtl2_range = MemoryRange::new(2 * GB..2 * GB + MB);
654
655 assert!(matches!(
656 MemoryLayout::new_from_resolved_ranges(ram, mmio, vec![], vec![], Some(vtl2_range)),
657 Err(Error::Vtl2RangeBeforeEndOfRam)
658 ));
659 }
660
661 #[test]
662 fn pci_ranges() {
663 let mmio = &[MemoryRange::new(3 * GB..4 * GB)];
664 let pci_ecam = &[MemoryRange::new(2 * TB - GB..2 * TB)];
665 let pci_mmio = &[
666 MemoryRange::new(2 * GB..3 * GB),
667 MemoryRange::new(5 * GB..6 * GB),
668 ];
669
670 let layout = MemoryLayout::new(TB, mmio, pci_ecam, pci_mmio, None).unwrap();
671 assert_eq!(
672 layout.ram(),
673 &[
674 MemoryRangeWithNode {
675 range: MemoryRange::new(0..2 * GB),
676 vnode: 0,
677 },
678 MemoryRangeWithNode {
679 range: MemoryRange::new(4 * GB..5 * GB),
680 vnode: 0,
681 },
682 MemoryRangeWithNode {
683 range: MemoryRange::new(6 * GB..TB + 3 * GB),
684 vnode: 0,
685 },
686 ]
687 );
688 assert_eq!(layout.end_of_layout(), 2 * TB);
689
690 assert_eq!(layout.probe_address(2 * GB), Some(AddressType::PciMmio));
691 assert_eq!(
692 layout.probe_address(2 * GB + MB),
693 Some(AddressType::PciMmio)
694 );
695 assert_eq!(layout.probe_address(5 * GB), Some(AddressType::PciMmio));
696 assert_eq!(
697 layout.probe_address(5 * GB + MB),
698 Some(AddressType::PciMmio)
699 );
700 assert_eq!(
701 layout.probe_address(2 * TB - GB),
702 Some(AddressType::PciEcam)
703 );
704 }
705
706 #[test]
707 fn probe_address() {
708 let mmio = &[
709 MemoryRange::new(GB..2 * GB),
710 MemoryRange::new(3 * GB..4 * GB),
711 ];
712 let ram = &[
713 MemoryRangeWithNode {
714 range: MemoryRange::new(0..GB),
715 vnode: 0,
716 },
717 MemoryRangeWithNode {
718 range: MemoryRange::new(2 * GB..3 * GB),
719 vnode: 0,
720 },
721 MemoryRangeWithNode {
722 range: MemoryRange::new(4 * GB..TB + 2 * GB),
723 vnode: 0,
724 },
725 ];
726
727 let layout = MemoryLayout::new_from_ranges(ram, mmio).unwrap();
728
729 assert_eq!(layout.probe_address(0), Some(AddressType::Ram));
730 assert_eq!(layout.probe_address(256), Some(AddressType::Ram));
731 assert_eq!(layout.probe_address(2 * GB), Some(AddressType::Ram));
732 assert_eq!(layout.probe_address(4 * GB), Some(AddressType::Ram));
733 assert_eq!(layout.probe_address(TB), Some(AddressType::Ram));
734 assert_eq!(layout.probe_address(TB + 1), Some(AddressType::Ram));
735
736 assert_eq!(layout.probe_address(GB), Some(AddressType::Mmio));
737 assert_eq!(layout.probe_address(GB + 123), Some(AddressType::Mmio));
738 assert_eq!(layout.probe_address(3 * GB), Some(AddressType::Mmio));
739
740 assert_eq!(layout.probe_address(TB + 2 * GB), None);
741 assert_eq!(layout.probe_address(TB + 3 * GB), None);
742 assert_eq!(layout.probe_address(4 * TB), None);
743 }
744
745 #[test]
746 fn numa_two_nodes_even_split() {
747 let mmio = &[MemoryRange::new(2 * GB..3 * GB)];
749 let layout = MemoryLayout::new_with_numa(&[2 * GB, 2 * GB], mmio, &[], &[], None).unwrap();
750 assert_eq!(
751 layout.ram(),
752 &[
753 MemoryRangeWithNode {
754 range: MemoryRange::new(0..2 * GB),
755 vnode: 0,
756 },
757 MemoryRangeWithNode {
758 range: MemoryRange::new(3 * GB..5 * GB),
759 vnode: 1,
760 },
761 ]
762 );
763 assert_eq!(layout.ram_size(), 4 * GB);
764 }
765
766 #[test]
767 fn numa_two_nodes_mid_chunk_split() {
768 let mmio = &[MemoryRange::new(3 * GB..4 * GB)];
773 let layout = MemoryLayout::new_with_numa(&[2 * GB, 2 * GB], mmio, &[], &[], None).unwrap();
774 assert_eq!(
775 layout.ram(),
776 &[
777 MemoryRangeWithNode {
778 range: MemoryRange::new(0..2 * GB),
779 vnode: 0,
780 },
781 MemoryRangeWithNode {
782 range: MemoryRange::new(2 * GB..3 * GB),
783 vnode: 1,
784 },
785 MemoryRangeWithNode {
786 range: MemoryRange::new(4 * GB..5 * GB),
787 vnode: 1,
788 },
789 ]
790 );
791 assert_eq!(layout.ram_size(), 4 * GB);
792 }
793
794 #[test]
795 fn numa_three_nodes() {
796 let layout = MemoryLayout::new_with_numa(&[GB, GB, GB], &[], &[], &[], None).unwrap();
798 assert_eq!(
799 layout.ram(),
800 &[
801 MemoryRangeWithNode {
802 range: MemoryRange::new(0..GB),
803 vnode: 0,
804 },
805 MemoryRangeWithNode {
806 range: MemoryRange::new(GB..2 * GB),
807 vnode: 1,
808 },
809 MemoryRangeWithNode {
810 range: MemoryRange::new(2 * GB..3 * GB),
811 vnode: 2,
812 },
813 ]
814 );
815 }
816
817 #[test]
818 fn numa_single_node_matches_new() {
819 let mmio = &[
821 MemoryRange::new(GB..2 * GB),
822 MemoryRange::new(3 * GB..4 * GB),
823 ];
824 let layout_new = MemoryLayout::new(TB, mmio, &[], &[], None).unwrap();
825 let layout_numa = MemoryLayout::new_with_numa(&[TB], mmio, &[], &[], None).unwrap();
826 assert_eq!(layout_new.ram(), layout_numa.ram());
827 }
828
829 #[test]
830 fn numa_bad_inputs() {
831 MemoryLayout::new_with_numa(&[], &[], &[], &[], None).unwrap_err();
833 MemoryLayout::new_with_numa(&[GB + 1], &[], &[], &[], None).unwrap_err();
835 MemoryLayout::new_with_numa(&[0], &[], &[], &[], None).unwrap_err();
837 MemoryLayout::new_with_numa(&[GB, 0], &[], &[], &[], None).unwrap_err();
839 }
840}