1use crate::host_params::MAX_VTL2_RAM_RANGES;
7use arrayvec::ArrayVec;
8use host_fdt_parser::MemoryEntry;
9#[cfg(test)]
10use igvm_defs::MemoryMapEntryType;
11use loader_defs::shim::MemoryVtlType;
12use memory_range::MemoryRange;
13use memory_range::RangeWalkResult;
14use memory_range::walk_ranges;
15use thiserror::Error;
16
17const PAGE_SIZE_4K: u64 = 4096;
18
19pub const MAX_RESERVED_MEM_RANGES: usize = 6 + sidecar_defs::MAX_NODES;
22
23const MAX_MEMORY_RANGES: usize = MAX_VTL2_RAM_RANGES + MAX_RESERVED_MEM_RANGES;
24
25const MAX_ADDRESS_RANGES: usize = MAX_MEMORY_RANGES * 2;
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub enum ReservedMemoryType {
31 Vtl2Config,
33 Vtl2Reserved,
36 SidecarImage,
38 SidecarNode,
40 Vtl2GpaPool,
44 TdxPageTables,
46}
47
48impl From<ReservedMemoryType> for MemoryVtlType {
49 fn from(r: ReservedMemoryType) -> Self {
50 match r {
51 ReservedMemoryType::Vtl2Config => MemoryVtlType::VTL2_CONFIG,
52 ReservedMemoryType::SidecarImage => MemoryVtlType::VTL2_SIDECAR_IMAGE,
53 ReservedMemoryType::SidecarNode => MemoryVtlType::VTL2_SIDECAR_NODE,
54 ReservedMemoryType::Vtl2Reserved => MemoryVtlType::VTL2_RESERVED,
55 ReservedMemoryType::Vtl2GpaPool => MemoryVtlType::VTL2_GPA_POOL,
56 ReservedMemoryType::TdxPageTables => MemoryVtlType::VTL2_TDX_PAGE_TABLES,
57 }
58 }
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62enum AddressUsage {
63 Free,
65 Used,
67 Reserved(ReservedMemoryType),
69}
70
71#[derive(Debug)]
72struct AddressRange {
73 range: MemoryRange,
74 vnode: u32,
75 usage: AddressUsage,
76}
77
78impl From<AddressUsage> for MemoryVtlType {
79 fn from(usage: AddressUsage) -> Self {
80 match usage {
81 AddressUsage::Free => MemoryVtlType::VTL2_RAM,
82 AddressUsage::Used => MemoryVtlType::VTL2_RAM,
83 AddressUsage::Reserved(r) => r.into(),
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy)]
89pub struct AllocatedRange {
90 pub range: MemoryRange,
91 pub vnode: u32,
92}
93
94#[derive(Debug, Error)]
95pub enum Error {
96 #[error("ram len {len} greater than maximum {max}")]
97 RamLen { len: u64, max: u64 },
98 #[error("already initialized")]
99 AlreadyInitialized,
100 #[error(
101 "reserved range {reserved:#x?}, type {typ:?} outside of bootshim used {bootshim_used:#x?}"
102 )]
103 ReservedRangeOutsideBootshimUsed {
104 reserved: MemoryRange,
105 typ: ReservedMemoryType,
106 bootshim_used: MemoryRange,
107 },
108}
109
110#[derive(Debug)]
111pub struct AddressSpaceManager {
112 address_space: ArrayVec<AddressRange, MAX_ADDRESS_RANGES>,
114
115 vtl2_pool: bool,
117}
118
119pub struct AddressSpaceManagerBuilder<'a, I: Iterator<Item = MemoryRange>> {
121 manager: &'a mut AddressSpaceManager,
122 vtl2_ram: &'a [MemoryEntry],
123 bootshim_used: MemoryRange,
124 vtl2_config: I,
125 reserved_range: Option<MemoryRange>,
126 sidecar_image: Option<MemoryRange>,
127 page_tables: Option<MemoryRange>,
128}
129
130impl<'a, I: Iterator<Item = MemoryRange>> AddressSpaceManagerBuilder<'a, I> {
131 pub fn new(
140 manager: &'a mut AddressSpaceManager,
141 vtl2_ram: &'a [MemoryEntry],
142 bootshim_used: MemoryRange,
143 vtl2_config: I,
144 ) -> AddressSpaceManagerBuilder<'a, I> {
145 AddressSpaceManagerBuilder {
146 manager,
147 vtl2_ram,
148 bootshim_used,
149 vtl2_config,
150 reserved_range: None,
151 sidecar_image: None,
152 page_tables: None,
153 }
154 }
155
156 pub fn with_reserved_range(mut self, reserved_range: MemoryRange) -> Self {
158 self.reserved_range = Some(reserved_range);
159 self
160 }
161
162 pub fn with_sidecar_image(mut self, sidecar_image: MemoryRange) -> Self {
164 self.sidecar_image = Some(sidecar_image);
165 self
166 }
167
168 pub fn with_page_tables(mut self, page_tables: MemoryRange) -> Self {
170 self.page_tables = Some(page_tables);
171 self
172 }
173
174 pub fn init(self) -> Result<&'a mut AddressSpaceManager, Error> {
176 let Self {
177 manager,
178 vtl2_ram,
179 bootshim_used,
180 vtl2_config,
181 reserved_range,
182 sidecar_image,
183 page_tables,
184 } = self;
185
186 if vtl2_ram.len() > MAX_VTL2_RAM_RANGES {
187 return Err(Error::RamLen {
188 len: vtl2_ram.len() as u64,
189 max: MAX_VTL2_RAM_RANGES as u64,
190 });
191 }
192
193 if !manager.address_space.is_empty() {
194 return Err(Error::AlreadyInitialized);
195 }
196
197 let mut reserved: ArrayVec<(MemoryRange, ReservedMemoryType), 5> = ArrayVec::new();
199 reserved.extend(vtl2_config.map(|r| (r, ReservedMemoryType::Vtl2Config)));
200 reserved.extend(
201 reserved_range
202 .into_iter()
203 .map(|r| (r, ReservedMemoryType::Vtl2Reserved)),
204 );
205 reserved.extend(
206 sidecar_image
207 .into_iter()
208 .map(|r| (r, ReservedMemoryType::SidecarImage)),
209 );
210 reserved.extend(
211 page_tables
212 .into_iter()
213 .map(|r| (r, ReservedMemoryType::TdxPageTables)),
214 );
215 reserved.sort_unstable_by_key(|(r, _)| r.start());
216
217 let mut used_ranges: ArrayVec<(MemoryRange, AddressUsage), 10> = ArrayVec::new();
218
219 for (entry, r) in walk_ranges(
222 core::iter::once((bootshim_used, AddressUsage::Used)),
223 reserved.iter().cloned(),
224 ) {
225 match r {
226 RangeWalkResult::Left(_) => {
227 used_ranges.push((entry, AddressUsage::Used));
228 }
229 RangeWalkResult::Both(_, reserved_type) => {
230 used_ranges.push((entry, AddressUsage::Reserved(reserved_type)));
231 }
232 RangeWalkResult::Right(typ) => {
233 return Err(Error::ReservedRangeOutsideBootshimUsed {
234 reserved: entry,
235 typ,
236 bootshim_used,
237 });
238 }
239 RangeWalkResult::Neither => {}
240 }
241 }
242
243 assert!(manager.address_space.is_empty());
245 for (entry, r) in walk_ranges(
246 vtl2_ram.iter().map(|e| (e.range, e.vnode)),
247 used_ranges.iter().map(|(r, usage)| (*r, usage)),
248 ) {
249 match r {
250 RangeWalkResult::Left(vnode) => {
251 manager.address_space.push(AddressRange {
253 range: entry,
254 vnode,
255 usage: AddressUsage::Free,
256 });
257 }
258 RangeWalkResult::Both(vnode, usage) => {
259 manager.address_space.push(AddressRange {
261 range: entry,
262 vnode,
263 usage: *usage,
264 });
265 }
266 RangeWalkResult::Right(usage) => {
267 panic!("vtl2 range {entry:#x?} used by {usage:?} not contained in vtl2 ram");
268 }
269 RangeWalkResult::Neither => {}
270 }
271 }
272
273 Ok(manager)
274 }
275}
276
277impl AddressSpaceManager {
278 pub const fn new_const() -> Self {
279 Self {
280 address_space: ArrayVec::new_const(),
281 vtl2_pool: false,
282 }
283 }
284
285 fn allocate_range(
288 &mut self,
289 index: usize,
290 len: u64,
291 usage: AddressUsage,
292 allocation_policy: AllocationPolicy,
293 ) -> AllocatedRange {
294 assert!(usage != AddressUsage::Free);
295 let range = self.address_space.get_mut(index).expect("valid index");
296 assert_eq!(range.usage, AddressUsage::Free);
297 assert!(range.range.len() >= len);
298
299 let (used, remainder) = match allocation_policy {
300 AllocationPolicy::LowMemory => {
301 range.range.split_at_offset(len)
303 }
304 AllocationPolicy::HighMemory => {
305 let offset = range.range.len() - len;
307 let (remainder, used) = range.range.split_at_offset(offset);
308 (used, remainder)
309 }
310 };
311
312 let remainder = if !remainder.is_empty() {
313 Some(AddressRange {
314 range: remainder,
315 vnode: range.vnode,
316 usage: AddressUsage::Free,
317 })
318 } else {
319 None
320 };
321
322 range.usage = usage;
324 range.range = used;
325 let allocated = AllocatedRange {
326 range: used,
327 vnode: range.vnode,
328 };
329
330 if let Some(remainder) = remainder {
331 match allocation_policy {
332 AllocationPolicy::LowMemory => {
333 self.address_space.insert(index + 1, remainder);
336 }
337 AllocationPolicy::HighMemory => {
338 self.address_space.insert(index, remainder);
341 }
342 }
343 }
344
345 allocated
346 }
347
348 pub fn allocate(
358 &mut self,
359 required_vnode: Option<u32>,
360 len: u64,
361 allocation_type: AllocationType,
362 allocation_policy: AllocationPolicy,
363 ) -> Option<AllocatedRange> {
364 if len == 0 {
365 return None;
366 }
367
368 let len = len.div_ceil(PAGE_SIZE_4K) * PAGE_SIZE_4K;
371
372 fn find_index<'a>(
373 mut iter: impl Iterator<Item = (usize, &'a AddressRange)>,
374 preferred_vnode: Option<u32>,
375 len: u64,
376 ) -> Option<usize> {
377 iter.find_map(|(index, range)| {
378 if range.usage == AddressUsage::Free
379 && range.range.len() >= len
380 && preferred_vnode.map(|pv| pv == range.vnode).unwrap_or(true)
381 {
382 Some(index)
383 } else {
384 None
385 }
386 })
387 }
388
389 let index = {
391 let iter = self.address_space.iter().enumerate();
392 match allocation_policy {
393 AllocationPolicy::LowMemory => find_index(iter, required_vnode, len),
394 AllocationPolicy::HighMemory => find_index(iter.rev(), required_vnode, len),
395 }
396 };
397
398 let alloc = index.map(|index| {
399 self.allocate_range(
400 index,
401 len,
402 match allocation_type {
403 AllocationType::GpaPool => {
404 AddressUsage::Reserved(ReservedMemoryType::Vtl2GpaPool)
405 }
406 AllocationType::SidecarNode => {
407 AddressUsage::Reserved(ReservedMemoryType::SidecarNode)
408 }
409 },
410 allocation_policy,
411 )
412 });
413
414 if allocation_type == AllocationType::GpaPool && alloc.is_some() {
415 self.vtl2_pool = true;
416 }
417
418 alloc
419 }
420
421 pub fn vtl2_ranges(&self) -> impl Iterator<Item = (MemoryRange, MemoryVtlType)> + use<'_> {
423 memory_range::merge_adjacent_ranges(
424 self.address_space.iter().map(|r| (r.range, r.usage.into())),
425 )
426 }
427
428 pub fn reserved_vtl2_ranges(
431 &self,
432 ) -> impl Iterator<Item = (MemoryRange, ReservedMemoryType)> + use<'_> {
433 self.address_space.iter().filter_map(|r| match r.usage {
434 AddressUsage::Reserved(typ) => Some((r.range, typ)),
435 _ => None,
436 })
437 }
438
439 pub fn has_vtl2_pool(&self) -> bool {
441 self.vtl2_pool
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
446pub enum AllocationType {
447 GpaPool,
448 SidecarNode,
449}
450
451pub enum AllocationPolicy {
452 LowMemory,
454 #[allow(dead_code)]
457 HighMemory,
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 #[test]
465 fn test_allocate() {
466 let mut address_space = AddressSpaceManager::new_const();
467 let vtl2_ram = &[MemoryEntry {
468 range: MemoryRange::new(0x0..0x20000),
469 vnode: 0,
470 mem_type: MemoryMapEntryType::MEMORY,
471 }];
472
473 AddressSpaceManagerBuilder::new(
474 &mut address_space,
475 vtl2_ram,
476 MemoryRange::new(0x0..0xF000),
477 [
478 MemoryRange::new(0x3000..0x4000),
479 MemoryRange::new(0x5000..0x6000),
480 ]
481 .iter()
482 .cloned(),
483 )
484 .with_reserved_range(MemoryRange::new(0x8000..0xA000))
485 .with_sidecar_image(MemoryRange::new(0xA000..0xC000))
486 .init()
487 .unwrap();
488
489 let range = address_space
490 .allocate(
491 None,
492 0x1000,
493 AllocationType::GpaPool,
494 AllocationPolicy::HighMemory,
495 )
496 .unwrap();
497 assert_eq!(range.range, MemoryRange::new(0x1F000..0x20000));
498 assert!(address_space.has_vtl2_pool());
499
500 let range = address_space
501 .allocate(
502 None,
503 0x2000,
504 AllocationType::GpaPool,
505 AllocationPolicy::HighMemory,
506 )
507 .unwrap();
508 assert_eq!(range.range, MemoryRange::new(0x1D000..0x1F000));
509
510 let range = address_space
511 .allocate(
512 None,
513 0x3000,
514 AllocationType::GpaPool,
515 AllocationPolicy::LowMemory,
516 )
517 .unwrap();
518 assert_eq!(range.range, MemoryRange::new(0xF000..0x12000));
519
520 let range = address_space
521 .allocate(
522 None,
523 0x1000,
524 AllocationType::GpaPool,
525 AllocationPolicy::LowMemory,
526 )
527 .unwrap();
528 assert_eq!(range.range, MemoryRange::new(0x12000..0x13000));
529 }
530
531 #[test]
533 fn test_allocate_numa() {
534 let mut address_space = AddressSpaceManager::new_const();
535 let vtl2_ram = &[
536 MemoryEntry {
537 range: MemoryRange::new(0x0..0x20000),
538 vnode: 0,
539 mem_type: MemoryMapEntryType::MEMORY,
540 },
541 MemoryEntry {
542 range: MemoryRange::new(0x20000..0x40000),
543 vnode: 1,
544 mem_type: MemoryMapEntryType::MEMORY,
545 },
546 MemoryEntry {
547 range: MemoryRange::new(0x40000..0x60000),
548 vnode: 2,
549 mem_type: MemoryMapEntryType::MEMORY,
550 },
551 MemoryEntry {
552 range: MemoryRange::new(0x60000..0x80000),
553 vnode: 3,
554 mem_type: MemoryMapEntryType::MEMORY,
555 },
556 ];
557
558 AddressSpaceManagerBuilder::new(
559 &mut address_space,
560 vtl2_ram,
561 MemoryRange::new(0x0..0x10000),
562 [
563 MemoryRange::new(0x3000..0x4000),
564 MemoryRange::new(0x5000..0x6000),
565 ]
566 .iter()
567 .cloned(),
568 )
569 .with_reserved_range(MemoryRange::new(0x8000..0xA000))
570 .with_sidecar_image(MemoryRange::new(0xA000..0xC000))
571 .init()
572 .unwrap();
573
574 let range = address_space
575 .allocate(
576 Some(0),
577 0x1000,
578 AllocationType::GpaPool,
579 AllocationPolicy::HighMemory,
580 )
581 .unwrap();
582 assert_eq!(range.range, MemoryRange::new(0x1F000..0x20000));
583 assert_eq!(range.vnode, 0);
584
585 let range = address_space
586 .allocate(
587 Some(0),
588 0x2000,
589 AllocationType::SidecarNode,
590 AllocationPolicy::HighMemory,
591 )
592 .unwrap();
593 assert_eq!(range.range, MemoryRange::new(0x1D000..0x1F000));
594 assert_eq!(range.vnode, 0);
595
596 let range = address_space
597 .allocate(
598 Some(2),
599 0x3000,
600 AllocationType::GpaPool,
601 AllocationPolicy::HighMemory,
602 )
603 .unwrap();
604 assert_eq!(range.range, MemoryRange::new(0x5D000..0x60000));
605 assert_eq!(range.vnode, 2);
606
607 let range = address_space
609 .allocate(
610 Some(3),
611 0x20000,
612 AllocationType::SidecarNode,
613 AllocationPolicy::HighMemory,
614 )
615 .unwrap();
616 assert_eq!(range.range, MemoryRange::new(0x60000..0x80000));
617 assert_eq!(range.vnode, 3);
618
619 let range = address_space.allocate(
620 Some(3),
621 0x1000,
622 AllocationType::SidecarNode,
623 AllocationPolicy::HighMemory,
624 );
625 assert!(
626 range.is_none(),
627 "allocation should fail, no space left for node 3"
628 );
629 }
630
631 #[test]
633 fn test_unaligned_allocations() {
634 let mut address_space = AddressSpaceManager::new_const();
635 let vtl2_ram = &[MemoryEntry {
636 range: MemoryRange::new(0x0..0x20000),
637 vnode: 0,
638 mem_type: MemoryMapEntryType::MEMORY,
639 }];
640
641 AddressSpaceManagerBuilder::new(
642 &mut address_space,
643 vtl2_ram,
644 MemoryRange::new(0x0..0xF000),
645 [
646 MemoryRange::new(0x3000..0x4000),
647 MemoryRange::new(0x5000..0x6000),
648 ]
649 .iter()
650 .cloned(),
651 )
652 .with_reserved_range(MemoryRange::new(0x8000..0xA000))
653 .with_sidecar_image(MemoryRange::new(0xA000..0xC000))
654 .init()
655 .unwrap();
656
657 let range = address_space
658 .allocate(
659 None,
660 0x1001,
661 AllocationType::GpaPool,
662 AllocationPolicy::HighMemory,
663 )
664 .unwrap();
665 assert_eq!(range.range, MemoryRange::new(0x1E000..0x20000));
666
667 let range = address_space
668 .allocate(
669 None,
670 0xFFF,
671 AllocationType::GpaPool,
672 AllocationPolicy::HighMemory,
673 )
674 .unwrap();
675 assert_eq!(range.range, MemoryRange::new(0x1D000..0x1E000));
676
677 let range = address_space.allocate(
678 None,
679 0,
680 AllocationType::GpaPool,
681 AllocationPolicy::HighMemory,
682 );
683 assert!(range.is_none());
684 }
685
686 #[test]
688 fn test_invalid_init_ranges() {
689 let vtl2_ram = [MemoryEntry {
690 range: MemoryRange::new(0x0..0x20000),
691 vnode: 0,
692 mem_type: MemoryMapEntryType::MEMORY,
693 }];
694 let bootshim_used = MemoryRange::new(0x0..0xF000);
695
696 let mut address_space = AddressSpaceManager::new_const();
698
699 let result = AddressSpaceManagerBuilder::new(
700 &mut address_space,
701 &vtl2_ram,
702 bootshim_used,
703 [MemoryRange::new(0x10000..0x11000)].iter().cloned(), )
705 .init();
706
707 assert!(matches!(
708 result,
709 Err(Error::ReservedRangeOutsideBootshimUsed { .. })
710 ));
711
712 let mut address_space = AddressSpaceManager::new_const();
715 let result = AddressSpaceManagerBuilder::new(
716 &mut address_space,
717 &vtl2_ram,
718 bootshim_used,
719 [MemoryRange::new(0xE000..0x10000)].iter().cloned(), )
721 .init();
722
723 assert!(matches!(
724 result,
725 Err(Error::ReservedRangeOutsideBootshimUsed { .. })
726 ));
727 }
728}