1use super::PartitionInfo;
7use super::shim_params::ShimParams;
8use crate::cmdline::BootCommandLineOptions;
9use crate::cmdline::SidecarOptions;
10use crate::host_params::COMMAND_LINE_SIZE;
11use crate::host_params::MAX_CPU_COUNT;
12use crate::host_params::MAX_ENTROPY_SIZE;
13use crate::host_params::MAX_NUMA_NODES;
14use crate::host_params::MAX_PARTITION_RAM_RANGES;
15use crate::host_params::MAX_VTL2_RAM_RANGES;
16use crate::host_params::dt::dma_hint::pick_private_pool_size;
17use crate::host_params::mmio::select_vtl2_mmio_range;
18use crate::host_params::shim_params::IsolationType;
19use crate::memory::AddressSpaceManager;
20use crate::memory::AddressSpaceManagerBuilder;
21use crate::memory::AllocationPolicy;
22use crate::memory::AllocationType;
23use crate::single_threaded::OffStackRef;
24use crate::single_threaded::off_stack;
25use arrayvec::ArrayString;
26use arrayvec::ArrayVec;
27use bump_alloc::ALLOCATOR;
28use core::cmp::max;
29use core::fmt::Write;
30use host_fdt_parser::MemoryAllocationMode;
31use host_fdt_parser::MemoryEntry;
32use host_fdt_parser::ParsedDeviceTree;
33use host_fdt_parser::VmbusInfo;
34use hvdef::HV_PAGE_SIZE;
35use igvm_defs::MemoryMapEntryType;
36use loader_defs::paravisor::CommandLinePolicy;
37use loader_defs::shim::MemoryVtlType;
38use loader_defs::shim::PersistedStateHeader;
39use memory_range::MemoryRange;
40use memory_range::subtract_ranges;
41use memory_range::walk_ranges;
42use thiserror::Error;
43use zerocopy::FromBytes;
44
45mod bump_alloc;
46mod dma_hint;
47
48#[derive(Debug, Error)]
50pub enum DtError {
51 #[error("no device tree provided by host")]
53 NoDeviceTree,
54 #[error("host provided device tree is invalid")]
56 DeviceTree(#[source] host_fdt_parser::Error<'static>),
57 #[error("commandline storage is too small to write the parsed command line")]
60 CommandLineSize,
61 #[error("device tree did not contain a vmbus node for VTL2")]
63 Vtl2Vmbus,
64 #[error("device tree did not contain a vmbus node for VTL0")]
66 Vtl0Vmbus,
67 #[error("host provided high MMIO range is insufficient to cover VTL0 and VTL2")]
69 NotEnoughVtl0Mmio,
70 #[error("host provided MMIO range is insufficient to cover VTL2")]
72 NotEnoughVtl2Mmio,
73}
74
75fn allocate_private_pool(
81 address_space: &mut AddressSpaceManager,
82 vtl2_ram: &[MemoryEntry],
83 pool_size_bytes: u64,
84 force_numa_split: bool,
85 enable_vtl2_gpa_pool: crate::cmdline::Vtl2GpaPoolConfig,
86 device_dma_page_count: Option<u64>,
87 vp_count: usize,
88 mem_size: u64,
89) {
90 if !force_numa_split {
100 if let Some(pool) = address_space.allocate(
101 Some(0),
102 pool_size_bytes,
103 AllocationType::GpaPool,
104 AllocationPolicy::HighMemory,
105 ) {
106 log::info!("allocated VTL2 pool at {:#x?}", pool.range);
107 return;
108 }
109 log::info!("node 0 cannot fit full pool, splitting across NUMA nodes");
110 } else {
111 log::info!("forcing VTL2 pool NUMA split across nodes");
112 }
113
114 let mut numa_nodes = off_stack!(ArrayVec<u32, MAX_NUMA_NODES>, ArrayVec::new_const());
118 for entry in vtl2_ram.iter() {
119 match numa_nodes.binary_search(&entry.vnode) {
120 Ok(_) => {}
121 Err(index) => {
122 numa_nodes.insert(index, entry.vnode);
123 }
124 }
125 }
126
127 let num_nodes = numa_nodes.len() as u64;
128 let per_node_size = (pool_size_bytes / num_nodes) & !(HV_PAGE_SIZE - 1);
131 let last_node_size = pool_size_bytes - per_node_size * (num_nodes - 1);
132 let mut remaining = pool_size_bytes;
133
134 if per_node_size == 0 {
139 panic!(
140 "cannot split VTL2 pool of size {pool_size_bytes:#x} bytes across \
141 {num_nodes} nodes, per node size {per_node_size:#x} bytes; \
142 enable_vtl2_gpa_pool={enable_vtl2_gpa_pool:?}, \
143 device_dma_page_count={device_dma_page_count:#x?}, \
144 vp_count={vp_count}, mem_size={mem_size:#x}"
145 );
146 }
147
148 for (i, vnode) in numa_nodes.iter().enumerate() {
149 if remaining == 0 {
150 break;
151 }
152
153 let is_last = i == numa_nodes.len() - 1;
154 let alloc_size = if is_last {
155 last_node_size
156 } else {
157 per_node_size
158 };
159
160 match address_space.allocate(
163 Some(*vnode),
164 alloc_size,
165 AllocationType::GpaPool,
166 AllocationPolicy::HighMemory,
167 ) {
168 Some(pool) => {
169 remaining -= pool.range.len();
170 log::info!(
171 "allocated VTL2 pool on node {} at {:#x?}",
172 vnode,
173 pool.range
174 );
175 }
176 None => {
177 let mut free_ranges = off_stack!(ArrayString<2048>, ArrayString::new_const());
178 for node in numa_nodes.iter() {
179 for range in address_space.free_ranges(*node) {
180 if write!(
181 free_ranges,
182 "n{}:[{:#x?}, {:#x?}) ",
183 node,
184 range.start(),
185 range.end()
186 )
187 .is_err()
188 {
189 let _ = write!(free_ranges, "...");
190 break;
191 }
192 }
193 }
194 let highest_numa_node = vtl2_ram.iter().map(|e| e.vnode).max().unwrap_or(0);
195 panic!(
196 "failed to allocate VTL2 pool on node {vnode}: \
197 need {alloc_size:#x} bytes, pool total {pool_size_bytes:#x} bytes \
198 (enable_vtl2_gpa_pool={enable_vtl2_gpa_pool:?}, \
199 device_dma_page_count={device_dma_page_count:#x?}, \
200 vp_count={vp_count}, mem_size={mem_size:#x}), \
201 highest_numa_node={highest_numa_node}, \
202 free_ranges=[ {}]",
203 free_ranges.as_str()
204 );
205 }
206 }
207 }
208
209 assert_eq!(
210 remaining, 0,
211 "pool allocation arithmetic error: {remaining:#x} bytes unallocated"
212 );
213}
214
215fn allocate_vtl2_ram(
217 params: &ShimParams,
218 partition_memory_map: &[MemoryEntry],
219 ram_size: Option<u64>,
220) -> OffStackRef<'static, impl AsRef<[MemoryEntry]> + use<>> {
221 let mut numa_nodes = off_stack!(ArrayVec<u32, MAX_NUMA_NODES>, ArrayVec::new_const());
224
225 for entry in partition_memory_map.iter() {
226 match numa_nodes.binary_search(&entry.vnode) {
227 Ok(_) => {}
228 Err(index) => {
229 numa_nodes.insert(index, entry.vnode);
230 }
231 }
232 }
233
234 let numa_node_count = numa_nodes.len();
235
236 let vtl2_size = if let Some(ram_size) = ram_size {
237 if ram_size < params.memory_size {
238 panic!(
239 "host provided vtl2 ram size {:x} is smaller than measured size {:x}",
240 ram_size, params.memory_size
241 );
242 }
243 max(ram_size, params.memory_size)
244 } else {
245 params.memory_size
246 };
247
248 const ALIGNMENT_GRANULARITY: u64 = HV_PAGE_SIZE * 8;
252 let ram_per_node = (vtl2_size / numa_node_count as u64).next_multiple_of(ALIGNMENT_GRANULARITY);
253
254 let mut memory_per_node = off_stack!(ArrayVec<u64, MAX_NUMA_NODES>, ArrayVec::new_const());
256 memory_per_node.extend((0..numa_node_count).map(|_| 0));
257 for entry in partition_memory_map.iter() {
258 memory_per_node[entry.vnode as usize] = ram_per_node;
259 }
260
261 let mut vtl2_ram = off_stack!(ArrayVec<MemoryEntry, MAX_NUMA_NODES>, ArrayVec::new_const());
266 let mut free_memory_after_vtl2 = off_stack!(ArrayVec<MemoryEntry, 1024>, ArrayVec::new_const());
267 let file_memory_range = MemoryRange::new(
268 params.memory_start_address..(params.memory_start_address + params.memory_size),
269 );
270
271 for (range, result) in walk_ranges(
272 [(file_memory_range, ())],
273 partition_memory_map.iter().map(|e| (e.range, e)),
274 ) {
275 match result {
276 memory_range::RangeWalkResult::Right(entry) => {
277 free_memory_after_vtl2.push(MemoryEntry {
279 range,
280 mem_type: entry.mem_type,
281 vnode: entry.vnode,
282 });
283 }
284 memory_range::RangeWalkResult::Both(_, entry) => {
285 vtl2_ram.push(MemoryEntry {
287 range,
288 mem_type: entry.mem_type,
289 vnode: entry.vnode,
290 });
291 }
292 memory_range::RangeWalkResult::Left(_) => {
293 panic!("used file range {range:#x?} is not reported as ram by host memmap")
294 }
295 memory_range::RangeWalkResult::Neither => {}
297 }
298 }
299
300 let mut free_memory = off_stack!(ArrayVec<MemoryEntry, 1024>, ArrayVec::new_const());
303 for (range, result) in walk_ranges(
304 params
305 .imported_regions()
306 .filter_map(|(range, _preaccepted)| {
307 if !file_memory_range.contains(&range) {
308 assert!(!file_memory_range.overlaps(&range), "imported range {range:#x?} overlaps vtl2 range and is not fully contained within vtl2 range");
312 Some((range, ()))
313 } else {
314 None
315 }
316 }),
317 free_memory_after_vtl2.iter().map(|e| (e.range, e)),
318 ) {
319 match result {
320 memory_range::RangeWalkResult::Right(entry) => {
321 free_memory.push(MemoryEntry {
322 range,
323 mem_type: entry.mem_type,
324 vnode: entry.vnode,
325 });
326 }
327 memory_range::RangeWalkResult::Left(_) => {
328 #[cfg(target_arch = "x86_64")]
331 if params.isolation_type == IsolationType::Tdx && range.start_4k_gpn() == 0xFFFFF && range.len() == 0x1000 {
332 continue;
333 }
334
335 panic!("launch context range {range:#x?} is not reported as ram by host memmap")
336 }
337 memory_range::RangeWalkResult::Both(_, _) => {
338 }
341 memory_range::RangeWalkResult::Neither => {}
343 }
344 }
345
346 for entry in vtl2_ram.iter() {
348 let mem_req = &mut memory_per_node[entry.vnode as usize];
349
350 if entry.range.len() > *mem_req {
351 log::warn!(
355 "entry {entry:?} is larger than required {mem_req} for vnode {}",
356 entry.vnode
357 );
358 *mem_req = 0;
359 } else {
360 *mem_req -= entry.range.len();
361 }
362 }
363
364 for (node, required_mem) in memory_per_node.iter().enumerate() {
366 let mut required_mem = *required_mem;
367 if required_mem == 0 {
368 continue;
369 }
370
371 for entry in free_memory.iter_mut().rev() {
374 if entry.vnode == node as u32 && !entry.range.is_empty() {
375 assert!(required_mem != 0);
376 let bytes_to_allocate = core::cmp::min(entry.range.len(), required_mem);
377
378 let allocation_start =
382 (entry.range.end() - bytes_to_allocate) & !(ALIGNMENT_GRANULARITY - 1);
383 let offset = allocation_start
384 .saturating_sub(entry.range.start())
385 .min(entry.range.len());
386 let (remaining, alloc) = MemoryRange::split_at_offset(&entry.range, offset);
387
388 entry.range = remaining;
389 vtl2_ram.push(MemoryEntry {
390 range: alloc,
391 mem_type: entry.mem_type,
392 vnode: node as u32,
393 });
394
395 required_mem = required_mem.saturating_sub(alloc.len());
396
397 if required_mem == 0 {
399 break;
400 }
401 }
402 }
403
404 if required_mem != 0 {
405 panic!(
408 "failed to allocate {required_mem:#x} for vnode {node:#x}, no memory remaining for vnode"
409 );
410 }
411 }
412
413 vtl2_ram.sort_unstable_by_key(|e| e.range.start());
415
416 vtl2_ram
417}
418
419fn parse_host_vtl2_ram(
421 params: &ShimParams,
422 memory: &[MemoryEntry],
423) -> OffStackRef<'static, impl AsRef<[MemoryEntry]> + use<>> {
424 let mut vtl2_ram = off_stack!(ArrayVec<MemoryEntry, MAX_NUMA_NODES>, ArrayVec::new_const());
427 if params.isolation_type.is_hardware_isolated() {
428 let vtl2_size = memory.iter().fold(0, |acc, entry| {
431 if entry.mem_type == MemoryMapEntryType::VTL2_PROTECTABLE {
432 acc + entry.range.len()
433 } else {
434 acc
435 }
436 });
437
438 log::info!(
439 "host provided vtl2 ram size is {:x}, measured size is {:x}",
440 vtl2_size,
441 params.memory_size
442 );
443
444 let vtl2_size = max(vtl2_size, params.memory_size);
445 vtl2_ram.push(MemoryEntry {
446 range: MemoryRange::new(
447 params.memory_start_address..(params.memory_start_address + vtl2_size),
448 ),
449 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
450 vnode: 0,
451 });
452 } else {
453 for &entry in memory
454 .iter()
455 .filter(|entry| entry.mem_type == MemoryMapEntryType::VTL2_PROTECTABLE)
456 {
457 vtl2_ram.push(entry);
458 }
459 }
460
461 if vtl2_ram.is_empty() {
462 log::info!("using measured vtl2 ram");
463 vtl2_ram.push(MemoryEntry {
464 range: MemoryRange::try_new(
465 params.memory_start_address..(params.memory_start_address + params.memory_size),
466 )
467 .expect("range is valid"),
468 mem_type: MemoryMapEntryType::VTL2_PROTECTABLE,
469 vnode: 0,
470 });
471 }
472
473 vtl2_ram
474}
475
476fn init_heap(params: &ShimParams) {
477 unsafe {
484 ALLOCATOR.init(params.heap);
485 }
486}
487
488type ParsedDt =
489 ParsedDeviceTree<MAX_PARTITION_RAM_RANGES, MAX_CPU_COUNT, COMMAND_LINE_SIZE, MAX_ENTROPY_SIZE>;
490
491fn add_common_ranges<'a, I: Iterator<Item = MemoryRange>>(
494 params: &ShimParams,
495 mut builder: AddressSpaceManagerBuilder<'a, I>,
496) -> AddressSpaceManagerBuilder<'a, I> {
497 builder = builder.with_log_buffer(params.log_buffer);
499
500 if params.vtl2_reserved_region_size != 0 {
501 builder = builder.with_reserved_range(MemoryRange::new(
502 params.vtl2_reserved_region_start
503 ..(params.vtl2_reserved_region_start + params.vtl2_reserved_region_size),
504 ));
505 }
506
507 if params.sidecar_size != 0 {
508 builder = builder.with_sidecar_image(MemoryRange::new(
509 params.sidecar_base..(params.sidecar_base + params.sidecar_size),
510 ));
511 }
512
513 builder
514}
515
516#[derive(Debug, PartialEq, Eq)]
517struct PartitionTopology {
518 vtl2_ram: &'static [MemoryEntry],
519 vtl0_mmio: ArrayVec<MemoryRange, 2>,
520 vtl2_mmio: ArrayVec<MemoryRange, 2>,
521 memory_allocation_mode: MemoryAllocationMode,
522}
523
524#[derive(Debug, PartialEq, Eq)]
527struct PersistedPartitionTopology {
528 topology: PartitionTopology,
529 sidecar_excluded_cpus: &'static [u32],
530}
531
532fn calculate_default_mmio_size(parsed: &ParsedDt) -> Result<u64, DtError> {
537 const MINIMUM_MMIO_SIZE: u64 = 128 * (1 << 20);
538 const MAXIMUM_MMIO_SIZE: u64 = 1 << 30;
539 let half_high_gap = parsed.vmbus_vtl0.as_ref().ok_or(DtError::Vtl0Vmbus)?.mmio[1].len() / 2;
540 Ok(half_high_gap.clamp(MINIMUM_MMIO_SIZE, MAXIMUM_MMIO_SIZE))
541}
542
543fn topology_from_host_dt(
545 params: &ShimParams,
546 parsed: &ParsedDt,
547 options: &BootCommandLineOptions,
548 address_space: &mut AddressSpaceManager,
549) -> Result<PartitionTopology, DtError> {
550 log::info!("reading topology from host device tree");
551
552 let mut vtl2_ram =
553 off_stack!(ArrayVec<MemoryEntry, MAX_VTL2_RAM_RANGES>, ArrayVec::new_const());
554
555 let memory_allocation_mode = parsed.memory_allocation_mode;
558 match memory_allocation_mode {
559 MemoryAllocationMode::Host => {
560 vtl2_ram
561 .try_extend_from_slice(parse_host_vtl2_ram(params, &parsed.memory).as_ref())
562 .expect("vtl2 ram should only be 64 big");
563 }
564 MemoryAllocationMode::Vtl2 {
565 memory_size,
566 mmio_size: _,
567 } => {
568 vtl2_ram
569 .try_extend_from_slice(
570 allocate_vtl2_ram(params, &parsed.memory, memory_size).as_ref(),
571 )
572 .expect("vtl2 ram should only be 64 big");
573 }
574 }
575
576 let (vtl0_mmio, vtl2_mmio) = if params.isolation_type != IsolationType::None
584 || matches!(
585 parsed.memory_allocation_mode,
586 MemoryAllocationMode::Vtl2 { .. }
587 ) {
588 let host_provided_size = match parsed.memory_allocation_mode {
599 MemoryAllocationMode::Vtl2 { mmio_size, .. } => mmio_size.unwrap_or(0),
600 _ => 0,
601 };
602 let vmbus_vtl2 = parsed.vmbus_vtl2.as_ref().ok_or(DtError::Vtl2Vmbus)?;
603 let vmbus_vtl2_mmio_size = vmbus_vtl2.mmio.iter().map(|r| r.len()).sum::<u64>();
604 let mmio_size = if vmbus_vtl2_mmio_size != 0 {
605 host_provided_size
606 } else {
607 max(host_provided_size, calculate_default_mmio_size(parsed)?)
608 };
609
610 log::info!("allocating vtl2 mmio size {mmio_size:#x} bytes");
611 log::info!("host provided vtl2 mmio ranges are {vmbus_vtl2_mmio_size:#x} bytes");
612
613 let vmbus_vtl0 = parsed.vmbus_vtl0.as_ref().ok_or(DtError::Vtl0Vmbus)?;
614 if vmbus_vtl2_mmio_size != 0 {
615 if vmbus_vtl2_mmio_size < mmio_size {
617 return Err(DtError::NotEnoughVtl2Mmio);
618 }
619
620 log::info!("using host provided vtl2 mmio: {:x?}", vmbus_vtl2.mmio);
621 (vmbus_vtl0.mmio.clone(), vmbus_vtl2.mmio.clone())
622 } else {
623 log::info!("no vtl2 mmio provided by host, allocating from vtl0 mmio");
625 let selected_vtl2_mmio = select_vtl2_mmio_range(&vmbus_vtl0.mmio, mmio_size)?;
626
627 let vtl0_mmio = subtract_ranges(vmbus_vtl0.mmio.iter().cloned(), [selected_vtl2_mmio])
629 .collect::<ArrayVec<MemoryRange, 2>>();
630 let vtl2_mmio = [selected_vtl2_mmio]
631 .into_iter()
632 .collect::<ArrayVec<MemoryRange, 2>>();
633
634 assert_eq!(
639 vtl0_mmio.len(),
640 2,
641 "vtl0 mmio ranges are not 2 {:#x?}",
642 vtl0_mmio
643 );
644
645 log::info!("vtl0 mmio: {vtl0_mmio:x?}, vtl2 mmio: {vtl2_mmio:x?}");
646
647 (vtl0_mmio, vtl2_mmio)
648 }
649 } else {
650 (
651 parsed
652 .vmbus_vtl0
653 .as_ref()
654 .ok_or(DtError::Vtl0Vmbus)?
655 .mmio
656 .clone(),
657 parsed
658 .vmbus_vtl2
659 .as_ref()
660 .ok_or(DtError::Vtl2Vmbus)?
661 .mmio
662 .clone(),
663 )
664 };
665
666 let reclaim_base = params.dt_start();
670 let reclaim_end = params.dt_start() + params.dt_size();
671 let vtl2_config_region_reclaim =
672 MemoryRange::try_new(reclaim_base..reclaim_end).expect("range is valid");
673
674 log::info!("reclaim device tree memory {reclaim_base:x}-{reclaim_end:x}");
675
676 let vtl2_config_region = MemoryRange::new(
678 params.parameter_region_start
679 ..(params.parameter_region_start + params.parameter_region_size),
680 );
681
682 const PERSISTED_REGION_SIZE: u64 = 20 * 4096;
688 let (persisted_state_region, remainder) = params
689 .persisted_state
690 .split_at_offset(PERSISTED_REGION_SIZE);
691 log::info!(
692 "persisted state region sized to {persisted_state_region:#x?}, remainder {remainder:#x?}"
693 );
694
695 let mut address_space_builder = AddressSpaceManagerBuilder::new(
696 address_space,
697 &vtl2_ram,
698 params.used,
699 persisted_state_region,
700 subtract_ranges([vtl2_config_region], [vtl2_config_region_reclaim]),
701 );
702
703 address_space_builder = add_common_ranges(params, address_space_builder);
704
705 address_space_builder
706 .init()
707 .expect("failed to initialize address space manager");
708
709 if params.isolation_type == IsolationType::None {
710 let enable_vtl2_gpa_pool = options.enable_vtl2_gpa_pool;
711 let device_dma_page_count = parsed.device_dma_page_count;
712 let vp_count = parsed.cpu_count();
713 let mem_size = vtl2_ram.iter().map(|e| e.range.len()).sum();
714 if let Some(vtl2_gpa_pool_size) = pick_private_pool_size(
715 enable_vtl2_gpa_pool,
716 device_dma_page_count,
717 vp_count,
718 mem_size,
719 ) {
720 let pool_size_bytes = vtl2_gpa_pool_size * HV_PAGE_SIZE;
723
724 allocate_private_pool(
725 address_space,
726 &vtl2_ram,
727 pool_size_bytes,
728 options.vtl2_gpa_pool_numa_split,
729 enable_vtl2_gpa_pool,
730 device_dma_page_count,
731 vp_count,
732 mem_size,
733 );
734 }
735 }
736
737 Ok(PartitionTopology {
738 vtl2_ram: OffStackRef::<'_, ArrayVec<MemoryEntry, MAX_VTL2_RAM_RANGES>>::leak(vtl2_ram),
739 vtl0_mmio,
740 vtl2_mmio,
741 memory_allocation_mode,
742 })
743}
744
745fn topology_from_persisted_state(
747 header: PersistedStateHeader,
748 params: &ShimParams,
749 parsed: &ParsedDt,
750 address_space: &mut AddressSpaceManager,
751) -> Result<PersistedPartitionTopology, DtError> {
752 log::info!("reading topology from persisted state");
753
754 let protobuf_region =
758 MemoryRange::new(header.protobuf_base..(header.protobuf_base + header.protobuf_region_len));
759 assert!(
760 params.persisted_state.contains(&protobuf_region),
761 "protobuf region {protobuf_region:#x?} is not contained within the persisted state region {:#x?}",
762 params.persisted_state
763 );
764
765 assert!(
767 header.protobuf_payload_len <= header.protobuf_region_len,
768 "protobuf payload len {} is larger than region len {}",
769 header.protobuf_payload_len,
770 header.protobuf_region_len
771 );
772
773 let protobuf_raw = unsafe {
776 core::slice::from_raw_parts(
777 header.protobuf_base as *const u8,
778 header.protobuf_payload_len as usize,
779 )
780 };
781
782 let parsed_protobuf: loader_defs::shim::save_restore::SavedState =
783 bump_alloc::with_global_alloc(|| {
784 log::info!("decoding protobuf of size {}", protobuf_raw.len());
785 mesh_protobuf::decode(protobuf_raw).expect("failed to decode protobuf")
786 });
787
788 let loader_defs::shim::save_restore::SavedState {
789 partition_memory,
790 partition_mmio,
791 cpus_with_mapped_interrupts_no_io,
792 cpus_with_outstanding_io,
793 } = parsed_protobuf;
794
795 log::info!(
796 "persisted state: cpus_with_mapped_interrupts_no_io={:?}, cpus_with_outstanding_io={:?}",
797 cpus_with_mapped_interrupts_no_io,
798 cpus_with_outstanding_io,
799 );
800
801 let mut sidecar_excluded_cpus = off_stack!(ArrayVec<u32, MAX_CPU_COUNT>, ArrayVec::new_const());
802 sidecar_excluded_cpus.clear();
803 for c in cpus_with_outstanding_io
806 .iter()
807 .chain(cpus_with_mapped_interrupts_no_io.iter())
808 .copied()
809 {
810 if let Err(i) = sidecar_excluded_cpus.binary_search(&c) {
811 sidecar_excluded_cpus.insert(i, c);
812 }
813 }
814
815 let memory_allocation_mode = parsed.memory_allocation_mode;
818
819 let mut vtl2_ram =
820 off_stack!(ArrayVec<MemoryEntry, MAX_VTL2_RAM_RANGES>, ArrayVec::new_const());
821
822 let previous_vtl2_ram = partition_memory.iter().filter_map(|entry| {
824 if entry.vtl_type.ram() && entry.vtl_type.vtl2() {
825 Some(MemoryEntry {
826 range: entry.range,
827 mem_type: entry.igvm_type.clone().into(),
828 vnode: entry.vnode,
829 })
830 } else {
831 None
832 }
833 });
834
835 let previous_vtl2_ram = memory_range::merge_adjacent_ranges(
839 previous_vtl2_ram.map(|entry| (entry.range, (entry.mem_type, entry.vnode))),
840 );
841
842 vtl2_ram.extend(
843 previous_vtl2_ram.map(|(range, (mem_type, vnode))| MemoryEntry {
844 range,
845 mem_type,
846 vnode,
847 }),
848 );
849
850 if matches!(memory_allocation_mode, MemoryAllocationMode::Host) {
856 let host_vtl2_ram = parse_host_vtl2_ram(params, &parsed.memory);
857 assert_eq!(
858 vtl2_ram.as_slice(),
859 host_vtl2_ram.as_ref(),
860 "vtl2 ram from persisted state does not match host provided ram"
861 );
862 }
863
864 let persisted_header = partition_memory
870 .iter()
871 .find(|entry| entry.vtl_type == MemoryVtlType::VTL2_PERSISTED_STATE_HEADER)
872 .expect("persisted state header missing");
873 let persisted_protobuf = partition_memory
874 .iter()
875 .find(|entry| entry.vtl_type == MemoryVtlType::VTL2_PERSISTED_STATE_PROTOBUF)
876 .expect("persisted state protobuf region missing");
877 assert_eq!(persisted_header.range.end(), protobuf_region.start());
878 let persisted_state_region =
879 MemoryRange::new(persisted_header.range.start()..persisted_protobuf.range.end());
880
881 let reclaim_base = params.dt_start();
885 let reclaim_end = params.dt_start() + params.dt_size();
886 let vtl2_config_region_reclaim =
887 MemoryRange::try_new(reclaim_base..reclaim_end).expect("range is valid");
888
889 log::info!("reclaim device tree memory {reclaim_base:x}-{reclaim_end:x}");
890
891 let vtl2_config_region = MemoryRange::new(
892 params.parameter_region_start
893 ..(params.parameter_region_start + params.parameter_region_size),
894 );
895
896 let mut address_space_builder = AddressSpaceManagerBuilder::new(
897 address_space,
898 &vtl2_ram,
899 params.used,
900 persisted_state_region,
901 subtract_ranges([vtl2_config_region], [vtl2_config_region_reclaim]),
902 );
903
904 let pool_ranges = partition_memory.iter().filter_map(|entry| {
909 if entry.vtl_type == MemoryVtlType::VTL2_GPA_POOL {
910 Some(entry.range)
911 } else {
912 None
913 }
914 });
915
916 address_space_builder = address_space_builder.with_pool_ranges(pool_ranges);
917
918 address_space_builder = add_common_ranges(params, address_space_builder);
920
921 address_space_builder
922 .init()
923 .expect("failed to initialize address space manager");
924
925 let vtl0_mmio = partition_mmio
927 .iter()
928 .filter_map(|entry| {
929 if entry.vtl_type == MemoryVtlType::VTL0_MMIO {
930 Some(entry.range)
931 } else {
932 None
933 }
934 })
935 .collect::<ArrayVec<MemoryRange, 2>>();
936 let vtl2_mmio = partition_mmio
937 .iter()
938 .filter_map(|entry| {
939 if entry.vtl_type == MemoryVtlType::VTL2_MMIO {
940 Some(entry.range)
941 } else {
942 None
943 }
944 })
945 .collect::<ArrayVec<MemoryRange, 2>>();
946
947 Ok(PersistedPartitionTopology {
948 topology: PartitionTopology {
949 vtl2_ram: OffStackRef::<'_, ArrayVec<MemoryEntry, MAX_VTL2_RAM_RANGES>>::leak(vtl2_ram),
950 vtl0_mmio,
951 vtl2_mmio,
952 memory_allocation_mode,
953 },
954 sidecar_excluded_cpus: OffStackRef::leak(sidecar_excluded_cpus),
955 })
956}
957
958fn read_persisted_region_header(params: &ShimParams) -> Option<PersistedStateHeader> {
962 if params.isolation_type != IsolationType::None {
971 return None;
972 }
973
974 let buf = unsafe {
978 core::slice::from_raw_parts(
979 params.persisted_state.start() as *const u8,
980 size_of::<PersistedStateHeader>(),
981 )
982 };
983
984 let header = PersistedStateHeader::read_from_bytes(buf)
985 .expect("region is page aligned and the correct size");
986
987 if header.magic == PersistedStateHeader::MAGIC {
988 Some(header)
989 } else {
990 None
991 }
992}
993
994impl PartitionInfo {
995 pub fn read_from_dt<'a>(
997 params: &'a ShimParams,
998 storage: &'a mut Self,
999 address_space: &'_ mut AddressSpaceManager,
1000 mut options: BootCommandLineOptions,
1001 can_trust_host: bool,
1002 ) -> Result<&'a mut Self, DtError> {
1003 let dt = params.device_tree();
1004
1005 if dt[0] == 0 {
1006 log::error!("host did not provide a device tree");
1007 return Err(DtError::NoDeviceTree);
1008 }
1009
1010 let mut dt_storage = off_stack!(ParsedDt, ParsedDeviceTree::new());
1011
1012 let parsed = ParsedDeviceTree::parse(dt, &mut *dt_storage).map_err(DtError::DeviceTree)?;
1013
1014 let command_line = params.command_line();
1015
1016 write!(
1018 storage.cmdline,
1019 "{}",
1020 command_line
1021 .command_line()
1022 .expect("measured command line should be valid")
1023 )
1024 .map_err(|_| DtError::CommandLineSize)?;
1025
1026 match command_line.policy {
1027 CommandLinePolicy::STATIC => {
1028 }
1030 CommandLinePolicy::APPEND_CHOSEN if can_trust_host => {
1031 options.parse(&parsed.command_line);
1034 write!(storage.cmdline, " {}", parsed.command_line)
1035 .map_err(|_| DtError::CommandLineSize)?;
1036 }
1037 CommandLinePolicy::APPEND_CHOSEN if !can_trust_host => {
1038 }
1040 _ => unreachable!(),
1041 }
1042
1043 init_heap(params);
1044
1045 let persisted_state_header = read_persisted_region_header(params);
1046 log::info!(
1047 "read_from_dt: persisted_state_header present={}, sidecar={:?}",
1048 persisted_state_header.is_some(),
1049 options.sidecar,
1050 );
1051 let (topology, sidecar_excluded_cpus) = if let Some(header) = persisted_state_header {
1052 log::info!("found persisted state header");
1053 let persisted_topology =
1054 topology_from_persisted_state(header, params, parsed, address_space)?;
1055 (
1056 persisted_topology.topology,
1057 persisted_topology.sidecar_excluded_cpus,
1058 )
1059 } else {
1060 (
1061 topology_from_host_dt(params, parsed, &options, address_space)?,
1062 &[][..],
1063 )
1064 };
1065
1066 let Self {
1067 vtl2_ram,
1068 partition_ram,
1069 isolation,
1070 bsp_reg,
1071 cpus,
1072 sidecar_cpu_overrides,
1073 vmbus_vtl0,
1074 vmbus_vtl2,
1075 cmdline: _,
1076 com3_serial,
1077 gic,
1078 pmu_gsiv,
1079 memory_allocation_mode,
1080 entropy,
1081 vtl0_alias_map,
1082 nvme_keepalive,
1083 boot_options,
1084 } = storage;
1085
1086 if let (SidecarOptions::Enabled { .. }, true) =
1106 (&boot_options.sidecar, !sidecar_excluded_cpus.is_empty())
1107 {
1108 let max_cpu_id = *sidecar_excluded_cpus.iter().max().unwrap() as usize;
1109 if parsed.cpu_count() <= sidecar_cpu_overrides.sidecar_starts_cpu.len()
1110 && max_cpu_id < sidecar_cpu_overrides.sidecar_starts_cpu.len()
1111 {
1112 sidecar_cpu_overrides.per_cpu_state_specified = true;
1114 for &cpu_id in sidecar_excluded_cpus {
1115 sidecar_cpu_overrides.sidecar_starts_cpu[cpu_id as usize] = false;
1116 }
1117 log::info!(
1118 "sidecar: excluding CPUs {:?} due to restored NVMe device state",
1119 sidecar_excluded_cpus,
1120 );
1121 } else {
1122 log::info!(
1124 "sidecar: disabling, too many CPUs for per-CPU state (max id {max_cpu_id})"
1125 );
1126 boot_options.sidecar = SidecarOptions::DisabledServicing;
1127 options.sidecar = SidecarOptions::DisabledServicing;
1128 }
1129 }
1130
1131 vtl2_ram.clear();
1133 vtl2_ram.extend(topology.vtl2_ram.iter().copied());
1134 partition_ram.clear();
1135 partition_ram.extend(parsed.memory.iter().copied());
1136 *memory_allocation_mode = topology.memory_allocation_mode;
1137
1138 *vmbus_vtl0 = VmbusInfo {
1141 connection_id: parsed
1142 .vmbus_vtl0
1143 .as_ref()
1144 .ok_or(DtError::Vtl0Vmbus)?
1145 .connection_id,
1146 mmio: topology.vtl0_mmio,
1147 };
1148 *vmbus_vtl2 = VmbusInfo {
1149 connection_id: parsed
1150 .vmbus_vtl2
1151 .as_ref()
1152 .ok_or(DtError::Vtl2Vmbus)?
1153 .connection_id,
1154 mmio: topology.vtl2_mmio,
1155 };
1156
1157 if can_trust_host {
1159 *vtl0_alias_map = parsed.vtl0_alias_map;
1160 }
1161
1162 *isolation = params.isolation_type;
1163
1164 *bsp_reg = parsed.boot_cpuid_phys;
1165 cpus.extend(parsed.cpus.iter().copied());
1166 *com3_serial = parsed.com3_serial.clone();
1167 *gic = parsed.gic.clone();
1168 *pmu_gsiv = parsed.pmu_gsiv;
1169 *entropy = parsed.entropy.clone();
1170 *nvme_keepalive = parsed.nvme_keepalive;
1171 *boot_options = options;
1172
1173 Ok(storage)
1174 }
1175}