1use acpi::cedt::Cedt;
8use acpi::dsdt;
9use acpi::ssdt::Ssdt;
10use acpi_spec::madt::InterruptPolarity;
11use acpi_spec::madt::InterruptTriggerMode;
12use cache_topology::CacheTopology;
13use chipset::ioapic;
14use chipset::psp;
15use inspect::Inspect;
16use memory_range::MemoryRange;
17use std::collections::BTreeMap;
18use thiserror::Error;
19use vm_topology::memory::MemoryLayout;
20use vm_topology::pcie::PcieHostBridge;
21use vm_topology::processor::ArchTopology;
22use vm_topology::processor::ProcessorTopology;
23use vm_topology::processor::aarch64::Aarch64Topology;
24use vm_topology::processor::x86::X86Topology;
25use x86defs::apic::APIC_BASE_ADDRESS;
26use zerocopy::IntoBytes;
27
28#[derive(Debug, Clone)]
30pub struct AcpiSmmuConfig {
31 pub rc_index: u32,
35 pub segment: u16,
39 pub base: u64,
41 pub event_gsiv: u32,
43 pub gerr_gsiv: u32,
45 pub reserved_iova_ranges: Vec<MemoryRange>,
50}
51
52pub struct BuiltAcpiTables {
54 pub rsdp: Vec<u8>,
56 pub tables: Vec<u8>,
58}
59
60pub struct SlitInfo {
62 pub num_nodes: usize,
64 pub distances: Vec<(u32, u32, u8)>,
67}
68
69#[derive(Debug, Clone, Copy)]
77pub struct GenericInitiator {
78 pub segment: u16,
80 pub bus: u8,
82 pub device: u8,
84 pub function: u8,
86 pub vnode: u32,
88}
89
90pub struct AcpiTablesBuilder<'a, T: AcpiTopology> {
92 pub processor_topology: &'a ProcessorTopology<T>,
97 pub mem_layout: &'a MemoryLayout,
99 pub cache_topology: Option<&'a CacheTopology>,
103 pub pcie_host_bridges: &'a Vec<PcieHostBridge>,
107 pub slit_info: Option<&'a SlitInfo>,
111 pub generic_initiators: &'a [GenericInitiator],
114 pub arch: AcpiArchConfig,
116}
117
118#[derive(Clone, Debug)]
120pub struct AmdIommuAcpiConfig {
121 pub device_id: u16,
123 pub capability_offset: u16,
125 pub mmio_base: u64,
127 pub pci_segment: u16,
129 pub ivhd_features: u64,
131 pub start_bus: u8,
133 pub end_bus: u8,
135}
136
137#[derive(Clone, Debug)]
141pub struct AmdIommuIvrsConfig {
142 pub pa_size: u8,
144 pub va_size: u8,
146 pub iommus: Vec<AmdIommuAcpiConfig>,
148 pub ioapic_rid: Option<u16>,
155}
156
157#[derive(Clone, Debug)]
159pub struct IntelVtdAcpiConfig {
160 pub mmio_base: u64,
162 pub pci_segment: u16,
164 pub start_bus: u8,
166 pub device_scopes: Vec<IntelVtdDeviceScope>,
170}
171
172#[derive(Clone, Debug)]
177pub struct IntelVtdDeviceScope {
178 pub devfn: u8,
180 pub is_bridge: bool,
183}
184
185#[derive(Clone, Debug)]
189pub struct IntelVtdDmarConfig {
190 pub host_address_width: u8,
192 pub units: Vec<IntelVtdAcpiConfig>,
194 pub ioapic_rid: Option<u16>,
200}
201
202#[derive(Clone, Debug)]
207pub enum X86IommuAcpiConfig {
208 AmdVi(AmdIommuIvrsConfig),
210 IntelVtd(IntelVtdDmarConfig),
212}
213
214pub enum AcpiArchConfig {
216 X86 {
218 with_ioapic: bool,
220 with_pic: bool,
222 with_pit: bool,
224 with_psp: bool,
226 pm_base: u16,
228 acpi_irq: u32,
230 iommu: Option<X86IommuAcpiConfig>,
234 },
235 Aarch64 {
237 hypervisor_vendor_identity: u64,
240 virt_timer_ppi: u32,
242 smmu: Vec<AcpiSmmuConfig>,
245 },
246}
247
248pub const OEM_INFO: acpi::builder::OemInfo = acpi::builder::OemInfo {
249 oem_id: *b"HVLITE",
250 oem_tableid: *b"HVLITETB",
251 oem_revision: 0,
252 creator_id: *b"MSHV",
253 creator_revision: 0,
254};
255
256#[derive(Debug, Error)]
258pub enum PcieAcpiBuildError {
259 #[error("invalid CXL host-bridge CEDT entry for uid {uid}")]
260 CedtHostBridge {
261 uid: u32,
262 #[source]
263 source: acpi::cedt::CedtHostBridgeError,
264 },
265 #[error("failed to serialize CEDT ACPI table")]
266 CedtSerialize(#[source] acpi::cedt::CedtSerializeError),
267}
268
269pub struct BuiltPcieAcpiTables {
271 pub ssdt: Vec<u8>,
273 pub cedt: Option<Vec<u8>>,
275}
276
277pub fn build_pcie_acpi_tables(
279 pcie_host_bridges: &[PcieHostBridge],
280) -> Result<BuiltPcieAcpiTables, PcieAcpiBuildError> {
281 let mut ssdt = Ssdt::new();
282 let mut cedt = Cedt::new();
283 let mut has_cedt_entries = false;
284
285 for bridge in pcie_host_bridges {
286 ssdt.add_pcie(acpi::ssdt::PcieHostBridgeEntry {
287 index: bridge.index,
288 segment: bridge.segment,
289 start_bus: bridge.start_bus,
290 end_bus: bridge.end_bus,
291 ecam_range: bridge.ecam_range,
292 low_mmio: bridge.low_mmio,
293 high_mmio: bridge.high_mmio,
294 cxl: bridge.cxl.is_some(),
295 vnode: bridge.vnode,
296 preserve_boot_config: bridge.preserve_boot_config,
297 });
298
299 if let Some(cxl) = &bridge.cxl {
300 if let Err(source) = cedt.add_cxl_host_bridge(
301 bridge.index,
302 cxl.hdm_range,
303 cxl.chbcr_range,
304 cxl.hdm_window_restrictions.bits(),
305 ) {
306 return Err(PcieAcpiBuildError::CedtHostBridge {
307 uid: bridge.index,
308 source,
309 });
310 } else {
311 has_cedt_entries = true;
312 }
313 }
314 }
315
316 let cedt = if has_cedt_entries {
317 match cedt.to_bytes() {
318 Ok(table) => Some(table),
319 Err(source) => {
320 return Err(PcieAcpiBuildError::CedtSerialize(source));
321 }
322 }
323 } else {
324 None
325 };
326
327 Ok(BuiltPcieAcpiTables {
328 ssdt: ssdt.to_bytes(),
329 cedt,
330 })
331}
332
333pub trait AcpiTopology: ArchTopology + Inspect + Sized {
334 fn extend_srat(topology: &ProcessorTopology<Self>, srat: &mut Vec<u8>);
335 fn extend_madt(topology: &ProcessorTopology<Self>, madt: &mut Vec<u8>);
336 fn needs_iort(_topology: &ProcessorTopology<Self>) -> bool {
337 false
338 }
339 fn iort_its_id(_topology: &ProcessorTopology<Self>) -> Option<u32> {
343 None
344 }
345}
346
347const MAX_LEGACY_APIC_ID: u32 = 0xfe;
352
353const X86_IOAPIC_ID: u8 = 0;
355
356impl AcpiTopology for X86Topology {
357 fn extend_srat(topology: &ProcessorTopology<Self>, srat: &mut Vec<u8>) {
358 for vp in topology.vps_arch() {
359 if vp.apic_id <= MAX_LEGACY_APIC_ID {
360 srat.extend_from_slice(
361 acpi_spec::srat::SratApic::new(vp.apic_id as u8, vp.base.vnode).as_bytes(),
362 );
363 } else {
364 srat.extend_from_slice(
365 acpi_spec::srat::SratX2Apic::new(vp.apic_id, vp.base.vnode).as_bytes(),
366 );
367 }
368 }
369 }
370
371 fn extend_madt(topology: &ProcessorTopology<Self>, madt: &mut Vec<u8>) {
372 madt.extend_from_slice(acpi_spec::madt::MadtLocalNmiSource::new().as_bytes());
374
375 for vp in topology.vps_arch() {
376 let uid = vp.base.vp_index.index() + 1;
377 if vp.apic_id <= MAX_LEGACY_APIC_ID && uid <= u8::MAX.into() {
378 madt.extend_from_slice(
379 acpi_spec::madt::MadtApic {
380 apic_id: vp.apic_id as u8,
381 acpi_processor_uid: uid as u8,
382 flags: acpi_spec::madt::MADT_APIC_ENABLED,
383 ..acpi_spec::madt::MadtApic::new()
384 }
385 .as_bytes(),
386 );
387 } else {
388 madt.extend_from_slice(
389 acpi_spec::madt::MadtX2Apic {
390 x2_apic_id: vp.apic_id,
391 acpi_processor_uid: uid,
392 flags: acpi_spec::madt::MADT_APIC_ENABLED,
393 ..acpi_spec::madt::MadtX2Apic::new()
394 }
395 .as_bytes(),
396 );
397 }
398 }
399 }
400}
401
402impl AcpiTopology for Aarch64Topology {
403 fn extend_srat(topology: &ProcessorTopology<Self>, srat: &mut Vec<u8>) {
404 for vp in topology.vps_arch() {
405 srat.extend_from_slice(
406 acpi_spec::srat::SratGicc::new(vp.base.vp_index.index() + 1, vp.base.vnode)
407 .as_bytes(),
408 );
409 }
410 }
411
412 fn extend_madt(topology: &ProcessorTopology<Self>, madt: &mut Vec<u8>) {
413 use vm_topology::processor::aarch64::GicVersion;
414
415 let gic_acpi_version: u8 = match topology.gic_version() {
416 GicVersion::V2 { .. } => 2,
417 GicVersion::V3 { .. } => 3,
418 };
419
420 madt.extend_from_slice(
421 acpi_spec::madt::MadtGicd::new(0, topology.gic_distributor_base(), gic_acpi_version)
422 .as_bytes(),
423 );
424 for vp in topology.vps_arch() {
425 let uid = vp.base.vp_index.index() + 1;
426
427 let mpidr = u64::from(vp.mpidr) & u64::from(aarch64defs::MpidrEl1::AFFINITY_MASK);
429
430 let mut gicc = acpi_spec::madt::MadtGicc::new(uid, mpidr);
431
432 if let Some(gicr) = vp.gicr {
433 gicc.gicr_base_address = gicr.into();
434 }
435
436 if let GicVersion::V2 { cpu_interface_base } = topology.gic_version() {
437 gicc.base_address = cpu_interface_base.into();
438 }
439
440 if let Some(pmu_gsiv) = topology.pmu_gsiv() {
441 gicc.performance_monitoring_gsiv = pmu_gsiv.into();
442 }
443 madt.extend_from_slice(gicc.as_bytes());
444 }
445
446 if let vm_topology::processor::aarch64::GicMsiController::V2m(v2m) = topology.gic_msi() {
448 madt.extend_from_slice(
449 acpi_spec::madt::MadtGicMsiFrame::new(
450 0,
451 v2m.frame_base,
452 v2m.spi_base as u16,
453 v2m.spi_count as u16,
454 )
455 .as_bytes(),
456 );
457 }
458
459 if let vm_topology::processor::aarch64::GicMsiController::Its(its) = topology.gic_msi() {
461 madt.extend_from_slice(acpi_spec::madt::MadtGicIts::new(0, its.its_base).as_bytes());
462 }
463 }
464
465 fn needs_iort(_topology: &ProcessorTopology<Self>) -> bool {
466 true
467 }
468
469 fn iort_its_id(topology: &ProcessorTopology<Self>) -> Option<u32> {
470 match topology.gic_msi() {
471 vm_topology::processor::aarch64::GicMsiController::Its(_) => Some(0),
472 _ => None,
473 }
474 }
475}
476
477impl<T: AcpiTopology> AcpiTablesBuilder<'_, T> {
478 fn with_srat<F, R>(&self, f: F) -> R
479 where
480 F: FnOnce(&acpi::builder::Table<'_>) -> R,
481 {
482 let mut srat_extra: Vec<u8> = Vec::new();
483 T::extend_srat(self.processor_topology, &mut srat_extra);
484 for range in self.mem_layout.ram() {
485 srat_extra.extend_from_slice(
486 acpi_spec::srat::SratMemory::new(
487 range.range.start(),
488 range.range.len(),
489 range.vnode,
490 )
491 .as_bytes(),
492 );
493 }
494 for gi in self.generic_initiators {
495 srat_extra.extend_from_slice(
496 acpi_spec::srat::SratGenericInitiator::new_pci(
497 gi.segment,
498 gi.bus,
499 gi.device,
500 gi.function,
501 gi.vnode,
502 )
503 .as_bytes(),
504 );
505 }
506
507 (f)(&acpi::builder::Table::new_dyn(
508 acpi_spec::srat::SRAT_REVISION,
509 None,
510 &acpi_spec::srat::SratHeader::new(),
511 &[srat_extra.as_slice()],
512 ))
513 }
514
515 fn build_slit_matrix(info: &SlitInfo) -> Vec<u8> {
516 let n = info.num_nodes;
517 let mut matrix = vec![0u8; n * n];
518 for i in 0..n {
520 for j in 0..n {
521 matrix[i * n + j] = if i == j { 10 } else { 20 };
522 }
523 }
524 for &(src, dst, distance) in &info.distances {
526 matrix[src as usize * n + dst as usize] = distance;
527 }
528 matrix
529 }
530
531 fn with_slit<F, R>(&self, info: &SlitInfo, f: F) -> R
532 where
533 F: FnOnce(&acpi::builder::Table<'_>) -> R,
534 {
535 let matrix = Self::build_slit_matrix(info);
536 let header = acpi_spec::slit::SlitHeader::new(info.num_nodes as u64);
537 (f)(&acpi::builder::Table::new_dyn(
538 acpi_spec::slit::SLIT_REVISION,
539 None,
540 &header,
541 &[matrix.as_slice()],
542 ))
543 }
544
545 fn with_madt<F, R>(&self, f: F) -> R
546 where
547 F: FnOnce(&acpi::builder::Table<'_>) -> R,
548 {
549 let mut madt_extra: Vec<u8> = Vec::new();
550
551 if let AcpiArchConfig::X86 {
552 with_ioapic,
553 acpi_irq,
554 with_pit,
555 ..
556 } = self.arch
557 {
558 if with_ioapic {
559 madt_extra.extend_from_slice(
560 acpi_spec::madt::MadtIoApic {
561 io_apic_id: X86_IOAPIC_ID,
562 io_apic_address: ioapic::IOAPIC_DEVICE_MMIO_REGION_BASE_ADDRESS as u32,
563 ..acpi_spec::madt::MadtIoApic::new()
564 }
565 .as_bytes(),
566 );
567 }
568
569 madt_extra.extend_from_slice(
571 acpi_spec::madt::MadtInterruptSourceOverride::new(
572 acpi_irq.try_into().expect("should be in range"),
573 acpi_irq,
574 Some(InterruptPolarity::ActiveHigh),
575 Some(InterruptTriggerMode::Level),
576 )
577 .as_bytes(),
578 );
579
580 if with_pit {
581 madt_extra.extend_from_slice(
583 acpi_spec::madt::MadtInterruptSourceOverride::new(0, 2, None, None).as_bytes(),
584 );
585 }
586 }
587
588 T::extend_madt(self.processor_topology, &mut madt_extra);
589
590 let (apic_addr, flags) = match self.arch {
591 AcpiArchConfig::X86 { with_pic, .. } => (
592 APIC_BASE_ADDRESS,
593 if with_pic {
594 acpi_spec::madt::MADT_PCAT_COMPAT
595 } else {
596 0
597 },
598 ),
599 AcpiArchConfig::Aarch64 { .. } => (0u32, 0u32),
600 };
601
602 (f)(&acpi::builder::Table::new_dyn(
603 5,
604 None,
605 &acpi_spec::madt::Madt { apic_addr, flags },
606 &[madt_extra.as_slice()],
607 ))
608 }
609
610 fn with_mcfg<F, R>(&self, f: F) -> R
611 where
612 F: FnOnce(&acpi::builder::Table<'_>) -> R,
613 {
614 let mut mcfg_extra: Vec<u8> = Vec::new();
615 for bridge in self.pcie_host_bridges {
616 let ecam_region_offset = (bridge.start_bus as u64) * 256 * 4096;
630 mcfg_extra.extend_from_slice(
631 acpi_spec::mcfg::McfgSegmentBusRange::new(
632 bridge.ecam_range.start().wrapping_sub(ecam_region_offset),
633 bridge.segment,
634 bridge.start_bus,
635 bridge.end_bus,
636 )
637 .as_bytes(),
638 )
639 }
640
641 (f)(&acpi::builder::Table::new_dyn(
642 acpi_spec::mcfg::MCFG_REVISION,
643 None,
644 &acpi_spec::mcfg::McfgHeader::new(),
645 &[mcfg_extra.as_slice()],
646 ))
647 }
648
649 fn with_iort<F, R>(&self, f: F) -> R
650 where
651 F: FnOnce(&acpi::builder::Table<'_>) -> R,
652 {
653 use acpi_spec::iort;
654
655 let its_id = T::iort_its_id(self.processor_topology);
656 let has_its = its_id.is_some();
657 let smmu_configs: &[AcpiSmmuConfig] = match &self.arch {
658 AcpiArchConfig::Aarch64 { smmu, .. } => smmu.as_slice(),
659 _ => &[],
660 };
661 let its_node_count: u32 = if has_its { 1 } else { 0 };
662 let smmu_node_count = smmu_configs.len() as u32;
663 let rmr_node_count = smmu_configs
665 .iter()
666 .filter(|cfg| !cfg.reserved_iova_ranges.is_empty())
667 .count() as u32;
668 let node_count =
669 its_node_count + smmu_node_count + self.pcie_host_bridges.len() as u32 + rmr_node_count;
670
671 let mut iort_extra: Vec<u8> = Vec::new();
672
673 let its_group_offset = iort::IORT_NODE_OFFSET;
676 if let Some(id) = its_id {
677 iort_extra.extend_from_slice(iort::IortItsGroup::new(0, 1).as_bytes());
678 iort_extra.extend_from_slice(&id.to_ne_bytes());
680 }
681
682 let mut smmu_rc_offsets: Vec<(u32, u32)> = Vec::new();
685 for cfg in smmu_configs {
686 let smmu_node_offset = iort::IORT_NODE_OFFSET + iort_extra.len() as u32;
687 smmu_rc_offsets.push((cfg.rc_index, smmu_node_offset));
688
689 if has_its {
690 let smmu = iort::IortSmmuV3::new_with_device_id_mapping(
707 cfg.rc_index,
708 cfg.base,
709 2,
710 cfg.event_gsiv,
711 cfg.gerr_gsiv,
712 1, );
714 iort_extra.extend_from_slice(smmu.as_bytes());
715
716 iort_extra.extend_from_slice(
721 iort::IortIdMapping::new(
722 0, 0xFFFF, (cfg.segment as u32) << 16, its_group_offset, 0, )
728 .as_bytes(),
729 );
730
731 iort_extra.extend_from_slice(
733 iort::IortIdMapping::new(
734 0, 0, 0, its_group_offset, iort::IORT_ID_SINGLE_MAPPING, )
740 .as_bytes(),
741 );
742 } else {
743 let smmu =
744 iort::IortSmmuV3::new(cfg.rc_index, cfg.base, 0, cfg.event_gsiv, cfg.gerr_gsiv);
745 iort_extra.extend_from_slice(smmu.as_bytes());
746 }
747 }
748
749 for bridge in self.pcie_host_bridges {
750 let smmu_offset = smmu_rc_offsets
755 .iter()
756 .find(|(idx, _)| *idx == bridge.index)
757 .map(|(_, off)| *off);
758
759 let (rc_mapping_count, rc_target_offset, rc_has_smmu) = if let Some(off) = smmu_offset {
760 (1, off, true)
761 } else if has_its {
762 (1, its_group_offset, false)
763 } else {
764 (0, 0, false)
765 };
766
767 let rc = iort::IortPciRootComplex::new(bridge.index, bridge.segment, rc_mapping_count);
768 iort_extra.extend_from_slice(rc.as_bytes());
769
770 if rc_mapping_count > 0 {
771 let output_base = if rc_has_smmu {
777 0
778 } else {
779 (bridge.segment as u32) << 16
780 };
781
782 iort_extra.extend_from_slice(
783 iort::IortIdMapping::new(
784 0, 0xFFFF, output_base, rc_target_offset, 0, )
790 .as_bytes(),
791 );
792 }
793 }
794
795 for (cfg_idx, cfg) in smmu_configs.iter().enumerate() {
799 if cfg.reserved_iova_ranges.is_empty() {
800 continue;
801 }
802 let smmu_offset = smmu_rc_offsets
803 .iter()
804 .find(|(idx, _)| *idx == cfg.rc_index)
805 .map(|(_, off)| *off)
806 .expect("RMR config references a valid SMMU");
807
808 let rmr_count = cfg.reserved_iova_ranges.len() as u32;
809 let mapping_count = 1u32;
812 let rmr = iort::IortRmr::new(
813 cfg_idx as u32 + 0x1000, 0, rmr_count,
816 mapping_count,
817 );
818 iort_extra.extend_from_slice(rmr.as_bytes());
819
820 iort_extra.extend_from_slice(
823 iort::IortIdMapping::new(
824 0, 0xFFFF, 0, smmu_offset, 0, )
830 .as_bytes(),
831 );
832
833 for &range in &cfg.reserved_iova_ranges {
835 iort_extra.extend_from_slice(
836 iort::IortRmrDescriptor::new(range.start(), range.len()).as_bytes(),
837 );
838 }
839 }
840
841 (f)(&acpi::builder::Table::new_dyn(
842 iort::IORT_REVISION,
843 None,
844 &iort::Iort::new(node_count),
845 &[iort_extra.as_slice()],
846 ))
847 }
848
849 fn should_build_iort(&self) -> bool {
850 T::needs_iort(self.processor_topology) && !self.pcie_host_bridges.is_empty()
851 }
852
853 fn with_ivrs<F, R>(&self, ivrs_config: &AmdIommuIvrsConfig, f: F) -> R
854 where
855 F: FnOnce(&acpi::builder::Table<'_>) -> R,
856 {
857 use acpi_spec::ivrs;
858
859 let mut ivrs_extra: Vec<u8> = Vec::new();
860
861 for config in &ivrs_config.iommus {
862 let mut dev_entries_size = 2 * size_of::<ivrs::IvhdDeviceEntry4>();
867
868 let ioapic_special = ivrs_config.ioapic_rid.and_then(|ioapic_rid| {
873 let ioapic_bus = (ioapic_rid >> 8) as u8;
874 (config.pci_segment == 0
875 && (config.start_bus..=config.end_bus).contains(&ioapic_bus))
876 .then(|| {
877 dev_entries_size += size_of::<ivrs::IvhdSpecialDeviceEntry8>();
878 ivrs::IvhdSpecialDeviceEntry8::ioapic(ioapic_rid, X86_IOAPIC_ID)
879 })
880 });
881
882 let ivhd_total = size_of::<ivrs::IvhdType11>() + dev_entries_size;
883
884 let ivhd = ivrs::IvhdType11::new(
891 config.device_id,
892 config.capability_offset,
893 config.mmio_base,
894 config.pci_segment,
895 config.ivhd_features,
896 )
897 .with_length(ivhd_total as u16)
898 .with_flags(0); ivrs_extra.extend_from_slice(ivhd.as_bytes());
901
902 let start_bdf = (config.start_bus as u16) << 8;
903 let end_bdf = ((config.end_bus as u16) << 8) | 0xFF;
904 ivrs_extra
905 .extend_from_slice(ivrs::IvhdDeviceEntry4::range_start(start_bdf, 0).as_bytes());
906 ivrs_extra.extend_from_slice(ivrs::IvhdDeviceEntry4::range_end(end_bdf).as_bytes());
907
908 if let Some(entry) = &ioapic_special {
909 ivrs_extra.extend_from_slice(entry.as_bytes());
910 }
911 }
912
913 let iv_info = ivrs::IvInfo::new()
914 .with_efr_sup(true)
915 .with_pa_size(ivrs_config.pa_size)
916 .with_va_size(ivrs_config.va_size);
917
918 (f)(&acpi::builder::Table::new_dyn(
919 ivrs::IVRS_REVISION,
920 None,
921 &ivrs::Ivrs::new(u32::from(iv_info)),
922 &[ivrs_extra.as_slice()],
923 ))
924 }
925
926 fn with_dmar<F, R>(&self, dmar_config: &IntelVtdDmarConfig, f: F) -> R
927 where
928 F: FnOnce(&acpi::builder::Table<'_>) -> R,
929 {
930 use acpi_spec::dmar;
931 use acpi_spec::dmar::DmarDevicePath;
932
933 let mut dmar_extra: Vec<u8> = Vec::new();
934 if let Some(ioapic_rid) = dmar_config.ioapic_rid {
935 let ioapic_bus = (ioapic_rid >> 8) as u8;
936 let matching_units = dmar_config
937 .units
938 .iter()
939 .filter(|config| config.pci_segment == 0 && config.start_bus == ioapic_bus)
940 .count();
941 assert_eq!(
942 matching_units, 1,
943 "VT-d IOAPIC RID {ioapic_rid:#06x} must be covered by exactly one segment-0 DRHD"
944 );
945 }
946
947 for config in &dmar_config.units {
948 let per_scope_size = size_of::<dmar::DmarDeviceScope>() + size_of::<DmarDevicePath>();
951 let ioapic_devfn = dmar_config.ioapic_rid.and_then(|ioapic_rid| {
952 let ioapic_bus = (ioapic_rid >> 8) as u8;
953 (config.pci_segment == 0 && config.start_bus == ioapic_bus)
954 .then_some(ioapic_rid as u8)
955 });
956 let ioapic_scope_count = if ioapic_devfn.is_some() { 1 } else { 0 };
957 let total_scope_size =
958 per_scope_size * (config.device_scopes.len() + ioapic_scope_count);
959 let drhd_total = size_of::<dmar::DmarDrhd>() + total_scope_size;
960
961 let drhd = dmar::DmarDrhd::new(
962 0, config.pci_segment,
964 config.mmio_base,
965 )
966 .with_length(drhd_total as u16);
967
968 dmar_extra.extend_from_slice(drhd.as_bytes());
969
970 for scope in &config.device_scopes {
971 let scope_type = if scope.is_bridge {
972 dmar::DEVICE_SCOPE_PCI_SUB_HIERARCHY
973 } else {
974 dmar::DEVICE_SCOPE_PCI_ENDPOINT
975 };
976 dmar_extra.extend_from_slice(
977 dmar::DmarDeviceScope::new(scope_type, config.start_bus).as_bytes(),
978 );
979 dmar_extra.extend_from_slice(
980 DmarDevicePath {
981 device: scope.devfn >> 3,
982 function: scope.devfn & 0x7,
983 }
984 .as_bytes(),
985 );
986 }
987
988 if let Some(ioapic_devfn) = ioapic_devfn {
989 let mut scope =
990 dmar::DmarDeviceScope::new(dmar::DEVICE_SCOPE_IOAPIC, config.start_bus);
991 scope.enumeration_id = X86_IOAPIC_ID;
992 dmar_extra.extend_from_slice(scope.as_bytes());
993 dmar_extra.extend_from_slice(
994 DmarDevicePath {
995 device: ioapic_devfn >> 3,
996 function: ioapic_devfn & 0x7,
997 }
998 .as_bytes(),
999 );
1000 }
1001 }
1002
1003 let haw = dmar_config.host_address_width - 1;
1005
1006 (f)(&acpi::builder::Table::new_dyn(
1007 dmar::DMAR_REVISION,
1008 None,
1009 &dmar::Dmar::new(haw, dmar::DMAR_FLAGS_INTR_REMAP),
1010 &[dmar_extra.as_slice()],
1011 ))
1012 }
1013
1014 fn with_pptt<F, R>(&self, f: F) -> R
1015 where
1016 F: FnOnce(&acpi::builder::Table<'_>) -> R,
1017 {
1018 use acpi_spec::pptt;
1019
1020 let cache = self.cache_topology.expect("cache topology is required");
1021
1022 let current_offset =
1023 |pptt_extra: &[u8]| (size_of::<acpi_spec::Header>() + pptt_extra.len()) as u32;
1024
1025 let cache_for = |pptt_extra: &mut Vec<u8>, level: u8, cache_type, next: Option<u32>| {
1026 let descriptor = cache
1027 .caches
1028 .iter()
1029 .find(|d| d.level == level && d.cache_type == cache_type)?;
1030 let offset = current_offset(pptt_extra);
1031 pptt_extra.extend_from_slice(
1032 pptt::PpttCache {
1033 flags: u32::from(
1034 pptt::PpttCacheFlags::new()
1035 .with_size_valid(true)
1036 .with_associativity_valid(true)
1037 .with_cache_type_valid(true)
1038 .with_line_size_valid(true),
1039 )
1040 .into(),
1041 size: descriptor.size.into(),
1042 associativity: descriptor.associativity.unwrap_or(0) as u8,
1043 attributes: pptt::PpttCacheAttributes::new().with_cache_type(match descriptor
1044 .cache_type
1045 {
1046 cache_topology::CacheType::Data => pptt::PPTT_CACHE_TYPE_DATA,
1047 cache_topology::CacheType::Instruction => pptt::PPTT_CACHE_TYPE_INSTRUCTION,
1048 cache_topology::CacheType::Unified => pptt::PPTT_CACHE_TYPE_UNIFIED,
1049 }),
1050 line_size: (descriptor.line_size as u16).into(),
1051 next_level: next.unwrap_or(0).into(),
1052 ..pptt::PpttCache::new()
1053 }
1054 .as_bytes(),
1055 );
1056 Some(offset)
1057 };
1058
1059 let mut pptt_extra = Vec::new();
1060 let mut sockets = BTreeMap::new();
1061 let smt_enabled = self.processor_topology.smt_enabled();
1062
1063 for vp in self.processor_topology.vps() {
1064 let acpi_processor_id = vp.vp_index.index() + 1;
1065 let info = self.processor_topology.vp_topology(vp.vp_index);
1066
1067 let &mut (socket_offset, ref mut cores) =
1068 sockets.entry(info.socket).or_insert_with(|| {
1069 let l3 =
1070 cache_for(&mut pptt_extra, 3, cache_topology::CacheType::Unified, None);
1071 let socket_offset = current_offset(&pptt_extra);
1072 pptt_extra.extend_from_slice(
1073 pptt::PpttProcessor {
1074 flags: u32::from(
1075 pptt::PpttProcessorFlags::new().with_physical_package(true),
1076 )
1077 .into(),
1078 ..pptt::PpttProcessor::new(l3.is_some() as u8)
1079 }
1080 .as_bytes(),
1081 );
1082
1083 if let Some(l3) = l3 {
1084 pptt_extra.extend_from_slice(&l3.to_ne_bytes());
1085 }
1086
1087 (socket_offset, BTreeMap::new())
1088 });
1089
1090 let core_offset = *cores.entry(info.core).or_insert_with(|| {
1091 let l2 = cache_for(&mut pptt_extra, 2, cache_topology::CacheType::Unified, None);
1092 let l1i = cache_for(
1093 &mut pptt_extra,
1094 1,
1095 cache_topology::CacheType::Instruction,
1096 l2,
1097 );
1098 let l1d = cache_for(&mut pptt_extra, 1, cache_topology::CacheType::Data, l2);
1099
1100 let core_offset = current_offset(&pptt_extra);
1101 pptt_extra.extend_from_slice(
1102 pptt::PpttProcessor {
1103 flags: u32::from(
1104 pptt::PpttProcessorFlags::new()
1105 .with_acpi_processor_uid_valid(!smt_enabled),
1106 )
1107 .into(),
1108 acpi_processor_id: if !smt_enabled {
1109 acpi_processor_id.into()
1110 } else {
1111 0u32.into()
1112 },
1113 parent: socket_offset.into(),
1114 ..pptt::PpttProcessor::new(l1i.is_some() as u8 + l1d.is_some() as u8)
1115 }
1116 .as_bytes(),
1117 );
1118
1119 if let Some(l1) = l1i {
1120 pptt_extra.extend_from_slice(&l1.to_ne_bytes());
1121 }
1122 if let Some(l1) = l1d {
1123 pptt_extra.extend_from_slice(&l1.to_ne_bytes());
1124 }
1125
1126 core_offset
1127 });
1128
1129 if smt_enabled {
1130 pptt_extra.extend_from_slice(
1131 pptt::PpttProcessor {
1132 flags: u32::from(
1133 pptt::PpttProcessorFlags::new().with_acpi_processor_uid_valid(true),
1134 )
1135 .into(),
1136 acpi_processor_id: acpi_processor_id.into(),
1137 parent: core_offset.into(),
1138 ..pptt::PpttProcessor::new(0)
1139 }
1140 .as_bytes(),
1141 )
1142 }
1143 }
1144
1145 (f)(&acpi::builder::Table::new_dyn(
1146 1,
1147 None,
1148 &pptt::Pptt {},
1149 &[pptt_extra.as_slice()],
1150 ))
1151 }
1152
1153 pub fn build_acpi_tables<F>(&self, gpa: u64, add_devices_to_dsdt: F) -> BuiltAcpiTables
1159 where
1160 F: FnOnce(&mut dsdt::Dsdt),
1161 {
1162 let mut dsdt_data = dsdt::Dsdt::new();
1163 dsdt_data.add_object(&dsdt::NamedObject::new(
1165 b"\\_S0",
1166 &dsdt::Package(vec![0, 0]),
1167 ));
1168 dsdt_data.add_object(&dsdt::NamedObject::new(
1170 b"\\_S5",
1171 &dsdt::Package(vec![0, 0]),
1172 ));
1173 add_devices_to_dsdt(&mut dsdt_data);
1175 for proc_index in 1..self.processor_topology.vp_count() + 1 {
1178 let c = (b'P' + (proc_index / 1000) as u8) as char;
1181 let name = &format!("{c}{:03}", proc_index % 1000);
1182 let mut proc = dsdt::Device::new(name.as_bytes());
1183 proc.add_object(&dsdt::NamedString::new(b"_HID", b"ACPI0007"));
1184 proc.add_object(&dsdt::NamedInteger::new(b"_UID", proc_index as u64));
1185 let mut method = dsdt::Method::new(b"_STA");
1186 method.add_operation(&dsdt::ReturnOp {
1187 result: dsdt::encode_integer(0xf),
1188 });
1189 proc.add_object(&method);
1190 dsdt_data.add_object(&proc);
1191 }
1192
1193 self.build_acpi_tables_inner(gpa, &dsdt_data.to_bytes())
1194 }
1195
1196 fn build_acpi_tables_inner(&self, gpa: u64, dsdt: &[u8]) -> BuiltAcpiTables {
1197 let mut b = acpi::builder::Builder::new(gpa + 0x1000, OEM_INFO);
1198
1199 let dsdt = b.append_raw(dsdt);
1200
1201 if let AcpiArchConfig::X86 {
1202 pm_base, acpi_irq, ..
1203 } = self.arch
1204 {
1205 use acpi_spec::fadt::AddressSpaceId;
1206 use acpi_spec::fadt::AddressWidth;
1207 use acpi_spec::fadt::GenericAddress;
1208
1209 b.append(&acpi::builder::Table::new(
1210 6,
1211 None,
1212 &acpi_spec::fadt::Fadt {
1213 flags: acpi_spec::fadt::FADT_WBINVD
1214 | acpi_spec::fadt::FADT_PROC_C1
1215 | acpi_spec::fadt::FADT_PWR_BUTTON
1216 | acpi_spec::fadt::FADT_SLP_BUTTON
1217 | acpi_spec::fadt::FADT_RTC_S4
1218 | acpi_spec::fadt::FADT_TMR_VAL_EXT
1219 | acpi_spec::fadt::FADT_RESET_REG_SUP
1220 | acpi_spec::fadt::FADT_USE_PLATFORM_CLOCK,
1221 x_dsdt: dsdt,
1222 sci_int: acpi_irq as u16,
1223 p_lvl2_lat: 101, p_lvl3_lat: 1001, pm1_evt_len: 4,
1226 x_pm1a_evt_blk: GenericAddress {
1227 addr_space_id: AddressSpaceId::SystemIo,
1228 register_bit_width: 32,
1229 register_bit_offset: 0,
1230 access_size: AddressWidth::Word,
1231 address: (pm_base + chipset::pm::DynReg::STATUS.0 as u16).into(),
1232 },
1233 pm1_cnt_len: 2,
1234 x_pm1a_cnt_blk: GenericAddress {
1235 addr_space_id: AddressSpaceId::SystemIo,
1236 register_bit_width: 16,
1237 register_bit_offset: 0,
1238 access_size: AddressWidth::Word,
1239 address: (pm_base + chipset::pm::DynReg::CONTROL.0 as u16).into(),
1240 },
1241 gpe0_blk_len: 4,
1242 x_gpe0_blk: GenericAddress {
1243 addr_space_id: AddressSpaceId::SystemIo,
1244 register_bit_width: 32,
1245 register_bit_offset: 0,
1246 access_size: AddressWidth::Word,
1247 address: (pm_base + chipset::pm::DynReg::GEN_PURPOSE_STATUS.0 as u16)
1248 .into(),
1249 },
1250 reset_reg: GenericAddress {
1251 addr_space_id: AddressSpaceId::SystemIo,
1252 register_bit_width: 8,
1253 register_bit_offset: 0,
1254 access_size: AddressWidth::Byte,
1255 address: (pm_base + chipset::pm::DynReg::RESET.0 as u16).into(),
1256 },
1257 reset_value: chipset::pm::RESET_VALUE,
1258 pm_tmr_len: 4,
1259 x_pm_tmr_blk: GenericAddress {
1260 addr_space_id: AddressSpaceId::SystemIo,
1261 register_bit_width: 32,
1262 register_bit_offset: 0,
1263 access_size: AddressWidth::Dword,
1264 address: (pm_base + chipset::pm::DynReg::TIMER.0 as u16).into(),
1265 },
1266 ..Default::default()
1267 },
1268 ));
1269 }
1270
1271 if let AcpiArchConfig::Aarch64 {
1272 hypervisor_vendor_identity,
1273 ..
1274 } = self.arch
1275 {
1276 b.append(&acpi::builder::Table::new(
1277 6,
1278 None,
1279 &acpi_spec::fadt::Fadt {
1280 flags: acpi_spec::fadt::FADT_HW_REDUCED_ACPI,
1281 arm_boot_arch: 0x0003, minor_version: 3,
1283 hypervisor_vendor_identity,
1284 x_dsdt: dsdt,
1285 ..Default::default()
1286 },
1287 ));
1288 }
1289
1290 if let AcpiArchConfig::X86 { with_psp: true, .. } = self.arch {
1291 use acpi_spec::aspt;
1292 use acpi_spec::aspt::Aspt;
1293 use acpi_spec::aspt::AsptStructHeader;
1294
1295 b.append(&acpi::builder::Table::new_dyn(
1296 1,
1297 None,
1298 &Aspt { num_structs: 3 },
1299 &[
1300 AsptStructHeader::new::<aspt::structs::AspGlobalRegisters>().as_bytes(),
1302 aspt::structs::AspGlobalRegisters {
1303 _reserved: 0,
1304 feature_register_address: psp::PSP_MMIO_ADDRESS + psp::reg::FEATURE,
1305 interrupt_enable_register_address: psp::PSP_MMIO_ADDRESS + psp::reg::INT_EN,
1306 interrupt_status_register_address: psp::PSP_MMIO_ADDRESS
1307 + psp::reg::INT_STS,
1308 }
1309 .as_bytes(),
1310 AsptStructHeader::new::<aspt::structs::SevMailboxRegisters>().as_bytes(),
1312 aspt::structs::SevMailboxRegisters {
1313 mailbox_interrupt_id: 1,
1314 _reserved: [0; 3],
1315 cmd_resp_register_address: psp::PSP_MMIO_ADDRESS + psp::reg::CMD_RESP,
1316 cmd_buf_addr_lo_register_address: psp::PSP_MMIO_ADDRESS
1317 + psp::reg::CMD_BUF_ADDR_LO,
1318 cmd_buf_addr_hi_register_address: psp::PSP_MMIO_ADDRESS
1319 + psp::reg::CMD_BUF_ADDR_HI,
1320 }
1321 .as_bytes(),
1322 AsptStructHeader::new::<aspt::structs::AcpiMailboxRegisters>().as_bytes(),
1324 aspt::structs::AcpiMailboxRegisters {
1325 _reserved1: 0,
1326 cmd_resp_register_address: psp::PSP_MMIO_ADDRESS + psp::reg::ACPI_CMD_RESP,
1327 _reserved2: [0; 2],
1328 }
1329 .as_bytes(),
1330 ],
1331 ));
1332 }
1333
1334 self.with_madt(|t| b.append(t));
1335 self.with_srat(|t| b.append(t));
1336 if let Some(info) = self.slit_info {
1337 self.with_slit(info, |t| b.append(t));
1338 }
1339 if !self.pcie_host_bridges.is_empty() {
1340 self.with_mcfg(|t| b.append(t));
1341
1342 if self.should_build_iort() {
1343 self.with_iort(|t| b.append(t));
1344 }
1345
1346 let pcie_tables = build_pcie_acpi_tables(self.pcie_host_bridges)
1347 .expect("PCIe ACPI table build should not fail");
1348 b.append_raw(&pcie_tables.ssdt);
1349 if let Some(cedt) = pcie_tables.cedt {
1350 b.append_raw(&cedt);
1351 }
1352 }
1353
1354 if self.cache_topology.is_some() {
1355 self.with_pptt(|t| b.append(t));
1356 }
1357
1358 if let AcpiArchConfig::X86 {
1359 iommu: Some(X86IommuAcpiConfig::AmdVi(ivrs_config)),
1360 ..
1361 } = &self.arch
1362 {
1363 self.with_ivrs(ivrs_config, |t| b.append(t));
1364 }
1365
1366 if let AcpiArchConfig::X86 {
1367 iommu: Some(X86IommuAcpiConfig::IntelVtd(dmar_config)),
1368 ..
1369 } = &self.arch
1370 {
1371 self.with_dmar(dmar_config, |t| b.append(t));
1372 }
1373
1374 if matches!(self.arch, AcpiArchConfig::Aarch64 { .. }) {
1375 self.with_gtdt(|t| b.append(t));
1376 }
1377
1378 let (rsdp, tables) = b.build();
1379
1380 BuiltAcpiTables { rsdp, tables }
1381 }
1382
1383 pub fn build_madt(&self) -> Vec<u8> {
1386 self.with_madt(|t| t.to_vec(&OEM_INFO))
1387 }
1388
1389 pub fn build_srat(&self) -> Vec<u8> {
1392 self.with_srat(|t| t.to_vec(&OEM_INFO))
1393 }
1394
1395 pub fn build_slit(&self) -> Option<Vec<u8>> {
1398 self.slit_info
1399 .map(|info| self.with_slit(info, |t| t.to_vec(&OEM_INFO)))
1400 }
1401
1402 pub fn build_mcfg(&self) -> Vec<u8> {
1405 self.with_mcfg(|t| t.to_vec(&OEM_INFO))
1406 }
1407
1408 pub fn build_iort(&self) -> Option<Vec<u8>> {
1411 self.should_build_iort()
1412 .then(|| self.with_iort(|t| t.to_vec(&OEM_INFO)))
1413 }
1414
1415 pub fn build_ivrs(&self) -> Option<Vec<u8>> {
1418 if let AcpiArchConfig::X86 {
1419 iommu: Some(X86IommuAcpiConfig::AmdVi(ivrs_config)),
1420 ..
1421 } = &self.arch
1422 {
1423 return Some(self.with_ivrs(ivrs_config, |t| t.to_vec(&OEM_INFO)));
1424 }
1425 None
1426 }
1427
1428 pub fn build_dmar(&self) -> Option<Vec<u8>> {
1431 if let AcpiArchConfig::X86 {
1432 iommu: Some(X86IommuAcpiConfig::IntelVtd(dmar_config)),
1433 ..
1434 } = &self.arch
1435 {
1436 return Some(self.with_dmar(dmar_config, |t| t.to_vec(&OEM_INFO)));
1437 }
1438 None
1439 }
1440
1441 pub fn build_pptt(&self) -> Vec<u8> {
1447 self.with_pptt(|t| t.to_vec(&OEM_INFO))
1448 }
1449
1450 fn with_gtdt<R>(&self, f: impl FnOnce(&acpi::builder::Table<'_>) -> R) -> R {
1451 let virt_timer_ppi = if let AcpiArchConfig::Aarch64 { virt_timer_ppi, .. } = self.arch {
1452 virt_timer_ppi
1453 } else {
1454 0
1455 };
1456 (f)(&acpi::builder::Table::new(
1457 3,
1458 None,
1459 &acpi_spec::gtdt::Gtdt {
1460 cnt_control_base: 0xFFFF_FFFF_FFFF_FFFF,
1461 virtual_el1_timer_gsiv: virt_timer_ppi,
1462 virtual_el1_timer_flags: acpi_spec::gtdt::GTDT_TIMER_ACTIVE_LOW,
1463 cnt_read_base: 0xFFFF_FFFF_FFFF_FFFF,
1464 ..Default::default()
1465 },
1466 ))
1467 }
1468
1469 pub fn build_gtdt(&self) -> Vec<u8> {
1470 self.with_gtdt(|t| t.to_vec(&OEM_INFO))
1471 }
1472}
1473
1474#[cfg(test)]
1475mod test {
1476 use super::*;
1477 use acpi_spec::madt::MadtParser;
1478 use acpi_spec::mcfg::parse_mcfg;
1479 use virt::VpIndex;
1480 use virt::VpInfo;
1481 use vm_topology::processor::TopologyBuilder;
1482 use vm_topology::processor::x86::X86VpInfo;
1483
1484 const KB: u64 = 1024;
1485 const MB: u64 = 1024 * KB;
1486 const GB: u64 = 1024 * MB;
1487 const TB: u64 = 1024 * GB;
1488
1489 const MMIO: [MemoryRange; 2] = [
1490 MemoryRange::new(GB..2 * GB),
1491 MemoryRange::new(3 * GB..4 * GB),
1492 ];
1493
1494 fn new_mem() -> MemoryLayout {
1495 MemoryLayout::new(TB, &MMIO, &[], &[], None).unwrap()
1496 }
1497
1498 fn new_builder<'a>(
1499 mem_layout: &'a MemoryLayout,
1500 processor_topology: &'a ProcessorTopology<X86Topology>,
1501 pcie_host_bridges: &'a Vec<PcieHostBridge>,
1502 ) -> AcpiTablesBuilder<'a, X86Topology> {
1503 AcpiTablesBuilder {
1504 processor_topology,
1505 mem_layout,
1506 cache_topology: None,
1507 pcie_host_bridges,
1508 slit_info: None,
1509 generic_initiators: &[],
1510 arch: AcpiArchConfig::X86 {
1511 with_ioapic: true,
1512 with_pic: false,
1513 with_pit: false,
1514 with_psp: false,
1515 pm_base: 1234,
1516 acpi_irq: 2,
1517 iommu: None,
1518 },
1519 }
1520 }
1521
1522 #[test]
1524 fn test_basic_madt_cpu() {
1525 let mem = new_mem();
1526 let topology = TopologyBuilder::new_x86().build(16).unwrap();
1527 let pcie = vec![];
1528 let builder = new_builder(&mem, &topology, &pcie);
1529 let madt = builder.build_madt();
1530
1531 let entries = MadtParser::new(&madt).unwrap().parse_apic_ids().unwrap();
1532 assert_eq!(entries, (0..16).map(Some).collect::<Vec<_>>());
1533
1534 let topology = TopologyBuilder::new_x86()
1535 .apic_id_offset(13)
1536 .build(16)
1537 .unwrap();
1538 let builder = new_builder(&mem, &topology, &pcie);
1539 let madt = builder.build_madt();
1540
1541 let entries = MadtParser::new(&madt).unwrap().parse_apic_ids().unwrap();
1542 assert_eq!(entries, (13..29).map(Some).collect::<Vec<_>>());
1543
1544 let apic_ids = [12, 58, 4823, 36];
1545 let topology = TopologyBuilder::new_x86()
1546 .build_with_vp_info(apic_ids.iter().enumerate().map(|(uid, apic)| X86VpInfo {
1547 base: VpInfo {
1548 vp_index: VpIndex::new(uid as u32),
1549 vnode: 0,
1550 },
1551 apic_id: *apic,
1552 }))
1553 .unwrap();
1554 let builder = new_builder(&mem, &topology, &pcie);
1555 let madt = builder.build_madt();
1556
1557 let entries = MadtParser::new(&madt).unwrap().parse_apic_ids().unwrap();
1558 assert_eq!(
1559 entries,
1560 apic_ids.iter().map(|e| Some(*e)).collect::<Vec<_>>()
1561 );
1562 }
1563
1564 #[test]
1565 fn test_basic_pcie_topology() {
1566 let mem = new_mem();
1567 let topology = TopologyBuilder::new_x86().build(16).unwrap();
1568 let pcie_host_bridges = vec![
1569 PcieHostBridge {
1570 index: 0,
1571 segment: 0,
1572 start_bus: 0,
1573 end_bus: 255,
1574 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1575 low_mmio: MemoryRange::new(0..0),
1576 high_mmio: MemoryRange::new(0..0),
1577 cxl: None,
1578 vnode: None,
1579 preserve_bars: false,
1580 preserve_boot_config: false,
1581 },
1582 PcieHostBridge {
1583 index: 1,
1584 segment: 1,
1585 start_bus: 32,
1586 end_bus: 63,
1587 ecam_range: MemoryRange::new(5 * GB..5 * GB + 32 * 256 * 4096),
1588 low_mmio: MemoryRange::new(0..0),
1589 high_mmio: MemoryRange::new(0..0),
1590 cxl: None,
1591 vnode: None,
1592 preserve_bars: false,
1593 preserve_boot_config: false,
1594 },
1595 ];
1596
1597 let builder = new_builder(&mem, &topology, &pcie_host_bridges);
1598 let mcfg = builder.build_mcfg();
1599
1600 let mut i = 0;
1601 let _ = parse_mcfg(&mcfg, |sbr| match i {
1602 0 => {
1603 assert_eq!(sbr.ecam_base, 0);
1604 assert_eq!(sbr.segment, 0);
1605 assert_eq!(sbr.start_bus, 0);
1606 assert_eq!(sbr.end_bus, 255);
1607 i += 1;
1608 }
1609 1 => {
1610 assert_eq!(sbr.ecam_base, 5 * GB - 32 * 256 * 4096);
1611 assert_eq!(sbr.segment, 1);
1612 assert_eq!(sbr.start_bus, 32);
1613 assert_eq!(sbr.end_bus, 63);
1614 i += 1;
1615 }
1616 _ => panic!("only expected two MCFG segment bus range entries"),
1617 })
1618 .unwrap();
1619 }
1620
1621 fn new_aarch64_its_topology() -> ProcessorTopology<Aarch64Topology> {
1622 use vm_topology::processor::aarch64::Aarch64PlatformConfig;
1623 use vm_topology::processor::aarch64::GicItsInfo;
1624 use vm_topology::processor::aarch64::GicMsiController;
1625 use vm_topology::processor::aarch64::GicVersion;
1626
1627 TopologyBuilder::new_aarch64(Aarch64PlatformConfig {
1628 gic_distributor_base: 0xffff0000,
1629 gic_version: GicVersion::V3 {
1630 redistributors_base: 0xefff0000,
1631 },
1632 gic_msi: GicMsiController::Its(GicItsInfo {
1633 its_base: 0xeffc0000,
1634 }),
1635 pmu_gsiv: None,
1636 virt_timer_ppi: 20,
1637 gic_nr_irqs: 992,
1638 })
1639 .build(2)
1640 .unwrap()
1641 }
1642
1643 fn new_aarch64_builder<'a>(
1644 mem_layout: &'a MemoryLayout,
1645 processor_topology: &'a ProcessorTopology<Aarch64Topology>,
1646 pcie_host_bridges: &'a Vec<PcieHostBridge>,
1647 ) -> AcpiTablesBuilder<'a, Aarch64Topology> {
1648 AcpiTablesBuilder {
1649 processor_topology,
1650 mem_layout,
1651 cache_topology: None,
1652 pcie_host_bridges,
1653 slit_info: None,
1654 generic_initiators: &[],
1655 arch: AcpiArchConfig::Aarch64 {
1656 hypervisor_vendor_identity: 0,
1657 virt_timer_ppi: 20,
1658 smmu: vec![],
1659 },
1660 }
1661 }
1662
1663 fn u32_at(data: &[u8], offset: usize) -> u32 {
1664 u32::from_ne_bytes(data[offset..offset + 4].try_into().unwrap())
1665 }
1666
1667 fn checksum(data: &[u8]) -> u8 {
1668 data.iter().fold(0, |sum, byte| sum.wrapping_add(*byte))
1669 }
1670
1671 fn contains_signature(data: &[u8], signature: &[u8; 4]) -> bool {
1672 data.windows(signature.len())
1673 .any(|window| window == signature)
1674 }
1675
1676 #[test]
1677 fn test_aarch64_iort_with_its() {
1678 use acpi_spec::iort;
1679
1680 let mem = new_mem();
1681 let topology = new_aarch64_its_topology();
1682 let pcie_host_bridges = vec![
1683 PcieHostBridge {
1684 index: 0,
1685 segment: 0,
1686 start_bus: 0,
1687 end_bus: 255,
1688 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1689 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
1690 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
1691 cxl: None,
1692 vnode: None,
1693 preserve_bars: false,
1694 preserve_boot_config: false,
1695 },
1696 PcieHostBridge {
1697 index: 7,
1698 segment: 3,
1699 start_bus: 32,
1700 end_bus: 63,
1701 ecam_range: MemoryRange::new(5 * GB..5 * GB + 32 * 256 * 4096),
1702 low_mmio: MemoryRange::new(0xe0000000..0xe4000000),
1703 high_mmio: MemoryRange::new(0x1040000000..0x1080000000),
1704 cxl: None,
1705 vnode: None,
1706 preserve_bars: false,
1707 preserve_boot_config: false,
1708 },
1709 ];
1710 let builder = new_aarch64_builder(&mem, &topology, &pcie_host_bridges);
1711
1712 let data = builder.build_iort().unwrap();
1713
1714 assert_eq!(&data[0..4], b"IORT");
1716 assert_eq!(u32_at(&data, 4) as usize, data.len());
1717 assert_eq!(checksum(&data), 0);
1718
1719 assert_eq!(u32_at(&data, 36), 3);
1721 assert_eq!(u32_at(&data, 40), iort::IORT_NODE_OFFSET);
1722
1723 let its_node = iort::IORT_NODE_OFFSET as usize;
1725 assert_eq!(data[its_node], iort::IORT_NODE_TYPE_ITS_GROUP);
1726 assert_eq!(u32_at(&data, its_node + 16), 1);
1728 assert_eq!(u32_at(&data, its_node + 20), 0);
1730
1731 let rc0 = its_node + 24;
1733 assert_eq!(data[rc0], iort::IORT_NODE_TYPE_PCI_ROOT_COMPLEX);
1734 assert_eq!(u32_at(&data, rc0 + 4), 0); assert_eq!(u32_at(&data, rc0 + 8), 1); assert_eq!(u32_at(&data, rc0 + 28), 0);
1738 let mapping0 = rc0 + 36;
1740 assert_eq!(u32_at(&data, mapping0), 0); assert_eq!(u32_at(&data, mapping0 + 4), 0xFFFF); assert_eq!(u32_at(&data, mapping0 + 8), 0); assert_eq!(u32_at(&data, mapping0 + 12), iort::IORT_NODE_OFFSET); let rc1 = mapping0 + 20;
1747 assert_eq!(data[rc1], iort::IORT_NODE_TYPE_PCI_ROOT_COMPLEX);
1748 assert_eq!(u32_at(&data, rc1 + 4), 7); assert_eq!(u32_at(&data, rc1 + 28), 3); let mapping1 = rc1 + 36;
1751 assert_eq!(u32_at(&data, mapping1 + 8), 3 << 16); }
1753
1754 #[test]
1755 fn test_iort_not_built_for_x86() {
1756 let mem = new_mem();
1757 let topology = TopologyBuilder::new_x86().build(1).unwrap();
1758 let pcie_host_bridges = vec![PcieHostBridge {
1759 index: 0,
1760 segment: 0,
1761 start_bus: 0,
1762 end_bus: 255,
1763 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1764 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
1765 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
1766 cxl: None,
1767 vnode: None,
1768 preserve_bars: false,
1769 preserve_boot_config: false,
1770 }];
1771 let builder = new_builder(&mem, &topology, &pcie_host_bridges);
1772 assert!(builder.build_iort().is_none());
1773
1774 let tables = builder.build_acpi_tables(0x100000, |_| {});
1775 assert!(!contains_signature(&tables.tables, b"IORT"));
1776 }
1777
1778 #[test]
1779 fn test_iort_not_built_without_pcie() {
1780 let mem = new_mem();
1781 let topology = new_aarch64_its_topology();
1782 let empty: Vec<PcieHostBridge> = Vec::new();
1783 let builder = new_aarch64_builder(&mem, &topology, &empty);
1784 assert!(builder.build_iort().is_none());
1785 }
1786
1787 #[test]
1788 fn test_aarch64_acpi_tables_include_iort() {
1789 let mem = new_mem();
1790 let topology = new_aarch64_its_topology();
1791 let pcie_host_bridges = vec![PcieHostBridge {
1792 index: 0,
1793 segment: 0,
1794 start_bus: 0,
1795 end_bus: 255,
1796 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1797 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
1798 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
1799 cxl: None,
1800 vnode: None,
1801 preserve_bars: false,
1802 preserve_boot_config: false,
1803 }];
1804 let builder = new_aarch64_builder(&mem, &topology, &pcie_host_bridges);
1805
1806 let tables = builder.build_acpi_tables(0x100000, |_| {});
1807 assert!(contains_signature(&tables.tables, b"MCFG"));
1808 assert!(contains_signature(&tables.tables, b"IORT"));
1809 }
1810
1811 fn new_aarch64_builder_with_smmu<'a>(
1812 mem_layout: &'a MemoryLayout,
1813 processor_topology: &'a ProcessorTopology<Aarch64Topology>,
1814 pcie_host_bridges: &'a Vec<PcieHostBridge>,
1815 smmu_base: u64,
1816 ) -> AcpiTablesBuilder<'a, Aarch64Topology> {
1817 AcpiTablesBuilder {
1818 processor_topology,
1819 mem_layout,
1820 cache_topology: None,
1821 pcie_host_bridges,
1822 slit_info: None,
1823 generic_initiators: &[],
1824 arch: AcpiArchConfig::Aarch64 {
1825 hypervisor_vendor_identity: 0,
1826 virt_timer_ppi: 20,
1827 smmu: vec![AcpiSmmuConfig {
1828 rc_index: 0,
1829 segment: 0,
1830 base: smmu_base,
1831 event_gsiv: 35,
1832 gerr_gsiv: 36,
1833 reserved_iova_ranges: Vec::new(),
1834 }],
1835 },
1836 }
1837 }
1838
1839 fn u64_at(data: &[u8], offset: usize) -> u64 {
1840 u64::from_ne_bytes(data[offset..offset + 8].try_into().unwrap())
1841 }
1842
1843 fn u16_at(data: &[u8], offset: usize) -> u16 {
1844 u16::from_ne_bytes(data[offset..offset + 2].try_into().unwrap())
1845 }
1846
1847 #[test]
1848 fn test_acpi_tables_include_cedt_when_cxl_bridge_present() {
1849 let mem = new_mem();
1850 let topology = TopologyBuilder::new_x86().build(1).unwrap();
1851 let pcie_host_bridges = vec![PcieHostBridge {
1852 index: 0,
1853 segment: 0,
1854 start_bus: 0,
1855 end_bus: 255,
1856 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1857 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
1858 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
1859 cxl: Some(vm_topology::pcie::PcieHostBridgeCxlInfo {
1860 chbcr_range: MemoryRange::new(0x1040000000..0x1040010000),
1861 hdm_range: MemoryRange::new(0x1000000000..0x1040000000),
1862 hdm_window_restrictions: Default::default(),
1863 }),
1864 vnode: None,
1865 preserve_bars: false,
1866 preserve_boot_config: false,
1867 }];
1868 let builder = new_builder(&mem, &topology, &pcie_host_bridges);
1869
1870 let tables = builder.build_acpi_tables(0x100000, |_| {});
1871 assert!(contains_signature(&tables.tables, b"CEDT"));
1872 }
1873
1874 #[test]
1875 fn test_iort_with_smmu_and_its() {
1876 use acpi_spec::iort;
1877
1878 let mem = new_mem();
1879 let topology = new_aarch64_its_topology();
1880 let smmu_base: u64 = 0xEFFA_0000;
1881 let pcie_host_bridges = vec![PcieHostBridge {
1882 index: 0,
1883 segment: 0,
1884 start_bus: 0,
1885 end_bus: 255,
1886 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1887 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
1888 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
1889 cxl: None,
1890 vnode: None,
1891 preserve_bars: false,
1892 preserve_boot_config: false,
1893 }];
1894 let builder = new_aarch64_builder_with_smmu(&mem, &topology, &pcie_host_bridges, smmu_base);
1895
1896 let data = builder.build_iort().unwrap();
1897
1898 assert_eq!(&data[0..4], b"IORT");
1900 assert_eq!(u32_at(&data, 4) as usize, data.len());
1901 assert_eq!(checksum(&data), 0);
1902
1903 assert_eq!(u32_at(&data, 36), 3);
1905
1906 let its_node = iort::IORT_NODE_OFFSET as usize;
1908 assert_eq!(data[its_node], iort::IORT_NODE_TYPE_ITS_GROUP);
1909 let its_group_size = 24usize; let smmu_node = its_node + its_group_size;
1913 assert_eq!(data[smmu_node], iort::IORT_NODE_TYPE_SMMUV3);
1914 assert_eq!(u64_at(&data, smmu_node + 16), smmu_base);
1916 assert_eq!(
1918 u32_at(&data, smmu_node + 24),
1919 iort::IORT_SMMUV3_FLAG_COHACC | iort::IORT_SMMUV3_FLAG_DEVICEID_VALID
1920 );
1921 assert_eq!(u32_at(&data, smmu_node + 36), 0);
1923 assert_eq!(u32_at(&data, smmu_node + 8), 2);
1925 assert_eq!(u32_at(&data, smmu_node + 64), 1);
1927 let smmu_node_len = u16_at(&data, smmu_node + 1) as usize;
1929 let smmu_mapping_0 = smmu_node + 68; assert_eq!(u32_at(&data, smmu_mapping_0 + 12), iort::IORT_NODE_OFFSET); assert_eq!(u32_at(&data, smmu_mapping_0 + 16), 0); let smmu_mapping_1 = smmu_mapping_0 + 20; assert_eq!(u32_at(&data, smmu_mapping_1 + 12), iort::IORT_NODE_OFFSET); assert_eq!(
1936 u32_at(&data, smmu_mapping_1 + 16),
1937 iort::IORT_ID_SINGLE_MAPPING
1938 ); let rc_node = smmu_node + smmu_node_len;
1942 assert_eq!(data[rc_node], iort::IORT_NODE_TYPE_PCI_ROOT_COMPLEX);
1943 assert_eq!(u32_at(&data, rc_node + 8), 1); let rc_mapping = rc_node + 36;
1946 assert_eq!(u32_at(&data, rc_mapping), 0); assert_eq!(u32_at(&data, rc_mapping + 4), 0xFFFF); assert_eq!(u32_at(&data, rc_mapping + 8), 0); assert_eq!(u32_at(&data, rc_mapping + 12), smmu_node as u32); }
1951
1952 #[test]
1953 fn test_iort_with_smmu_multi_rc() {
1954 use acpi_spec::iort;
1955
1956 let mem = new_mem();
1957 let topology = new_aarch64_its_topology();
1958 let smmu_base: u64 = 0xEFFA_0000;
1959 let pcie_host_bridges = vec![
1960 PcieHostBridge {
1961 index: 0,
1962 segment: 0,
1963 start_bus: 0,
1964 end_bus: 255,
1965 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
1966 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
1967 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
1968 cxl: None,
1969 vnode: None,
1970 preserve_bars: false,
1971 preserve_boot_config: false,
1972 },
1973 PcieHostBridge {
1974 index: 1,
1975 segment: 2,
1976 start_bus: 0,
1977 end_bus: 63,
1978 ecam_range: MemoryRange::new(5 * GB..5 * GB + 64 * 256 * 4096),
1979 low_mmio: MemoryRange::new(0xe0000000..0xe4000000),
1980 high_mmio: MemoryRange::new(0x1040000000..0x1080000000),
1981 cxl: None,
1982 vnode: None,
1983 preserve_bars: false,
1984 preserve_boot_config: false,
1985 },
1986 ];
1987 let builder = new_aarch64_builder_with_smmu(&mem, &topology, &pcie_host_bridges, smmu_base);
1988
1989 let data = builder.build_iort().unwrap();
1990
1991 assert_eq!(u32_at(&data, 36), 4);
1993 assert_eq!(checksum(&data), 0);
1994
1995 let its_node = iort::IORT_NODE_OFFSET as usize;
1997 let its_group_size = 24usize;
1998
1999 let smmu_node = its_node + its_group_size;
2001 assert_eq!(data[smmu_node], iort::IORT_NODE_TYPE_SMMUV3);
2002 let smmu_node_len = u16_at(&data, smmu_node + 1) as usize;
2003
2004 let rc0 = smmu_node + smmu_node_len;
2006 assert_eq!(data[rc0], iort::IORT_NODE_TYPE_PCI_ROOT_COMPLEX);
2007 let rc0_mapping = rc0 + 36;
2008 assert_eq!(u32_at(&data, rc0_mapping + 8), 0); assert_eq!(u32_at(&data, rc0_mapping + 12), smmu_node as u32); let rc0_len = u16_at(&data, rc0 + 1) as usize;
2013 let rc1 = rc0 + rc0_len;
2014 assert_eq!(data[rc1], iort::IORT_NODE_TYPE_PCI_ROOT_COMPLEX);
2015 let rc1_mapping = rc1 + 36;
2016 assert_eq!(u32_at(&data, rc1_mapping + 8), 2 << 16); assert_eq!(u32_at(&data, rc1_mapping + 12), its_node as u32); }
2019
2020 #[test]
2021 fn test_iort_without_smmu_unchanged() {
2022 use acpi_spec::iort;
2024
2025 let mem = new_mem();
2026 let topology = new_aarch64_its_topology();
2027 let pcie_host_bridges = vec![PcieHostBridge {
2028 index: 0,
2029 segment: 0,
2030 start_bus: 0,
2031 end_bus: 255,
2032 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
2033 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
2034 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
2035 cxl: None,
2036 vnode: None,
2037 preserve_bars: false,
2038 preserve_boot_config: false,
2039 }];
2040 let builder = new_aarch64_builder(&mem, &topology, &pcie_host_bridges);
2041
2042 let data = builder.build_iort().unwrap();
2043
2044 assert_eq!(u32_at(&data, 36), 2);
2046
2047 let its_node = iort::IORT_NODE_OFFSET as usize;
2049 let rc_node = its_node + 24; assert_eq!(data[rc_node], iort::IORT_NODE_TYPE_PCI_ROOT_COMPLEX);
2051 let rc_mapping = rc_node + 36;
2052 assert_eq!(u32_at(&data, rc_mapping + 12), iort::IORT_NODE_OFFSET); }
2054
2055 #[test]
2056 fn test_iort_smmuv3_node_fields() {
2057 use acpi_spec::iort;
2058
2059 let mem = new_mem();
2060 let topology = new_aarch64_its_topology();
2061 let smmu_base: u64 = 0xEFFA_0000;
2062 let pcie_host_bridges = vec![PcieHostBridge {
2063 index: 0,
2064 segment: 0,
2065 start_bus: 0,
2066 end_bus: 255,
2067 ecam_range: MemoryRange::new(0..256 * 256 * 4096),
2068 low_mmio: MemoryRange::new(0xdc000000..0xe0000000),
2069 high_mmio: MemoryRange::new(0x1000000000..0x1040000000),
2070 cxl: None,
2071 vnode: None,
2072 preserve_bars: false,
2073 preserve_boot_config: false,
2074 }];
2075 let builder = new_aarch64_builder_with_smmu(&mem, &topology, &pcie_host_bridges, smmu_base);
2076
2077 let data = builder.build_iort().unwrap();
2078
2079 let smmu_node = iort::IORT_NODE_OFFSET as usize + 24; assert_eq!(data[smmu_node], iort::IORT_NODE_TYPE_SMMUV3);
2082 assert_eq!(data[smmu_node + 3], iort::IORT_SMMUV3_REVISION);
2084 assert_eq!(u64_at(&data, smmu_node + 16), smmu_base);
2086 assert_eq!(
2088 u32_at(&data, smmu_node + 24),
2089 iort::IORT_SMMUV3_FLAG_COHACC | iort::IORT_SMMUV3_FLAG_DEVICEID_VALID
2090 );
2091 assert_eq!(u32_at(&data, smmu_node + 28), 0);
2093 assert_eq!(u64_at(&data, smmu_node + 32), 0);
2095 assert_eq!(
2097 u32_at(&data, smmu_node + 40),
2098 iort::IORT_SMMUV3_MODEL_GENERIC
2099 );
2100 assert_eq!(u32_at(&data, smmu_node + 44), 35); assert_eq!(u32_at(&data, smmu_node + 48), 0); assert_eq!(u32_at(&data, smmu_node + 52), 36); assert_eq!(u32_at(&data, smmu_node + 56), 0); }
2106
2107 fn set_amd_iommu(
2108 builder: &mut AcpiTablesBuilder<'_, X86Topology>,
2109 configs: Vec<AmdIommuAcpiConfig>,
2110 ) {
2111 if let AcpiArchConfig::X86 { iommu, .. } = &mut builder.arch {
2112 *iommu = Some(X86IommuAcpiConfig::AmdVi(AmdIommuIvrsConfig {
2113 pa_size: 48,
2114 va_size: 48,
2115 iommus: configs,
2116 ioapic_rid: None,
2117 }));
2118 } else {
2119 panic!("expected X86 arch config");
2120 }
2121 }
2122
2123 fn set_amd_iommu_with_ioapic(
2124 builder: &mut AcpiTablesBuilder<'_, X86Topology>,
2125 configs: Vec<AmdIommuAcpiConfig>,
2126 ioapic_rid: Option<u16>,
2127 ) {
2128 if let AcpiArchConfig::X86 { iommu, .. } = &mut builder.arch {
2129 *iommu = Some(X86IommuAcpiConfig::AmdVi(AmdIommuIvrsConfig {
2130 pa_size: 48,
2131 va_size: 48,
2132 iommus: configs,
2133 ioapic_rid,
2134 }));
2135 } else {
2136 panic!("expected X86 arch config");
2137 }
2138 }
2139
2140 #[test]
2141 fn test_ivrs_basic() {
2142 let mem = new_mem();
2143 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2144 let pcie = vec![];
2145 let mut builder = new_builder(&mem, &topology, &pcie);
2146 set_amd_iommu(
2147 &mut builder,
2148 vec![AmdIommuAcpiConfig {
2149 device_id: 0x0000, capability_offset: 0x40,
2151 mmio_base: 0xFD00_0000,
2152 pci_segment: 0,
2153 ivhd_features: 0xC0,
2154 start_bus: 0,
2155 end_bus: 255,
2156 }],
2157 );
2158
2159 let ivrs = builder.build_ivrs().unwrap();
2160
2161 assert_eq!(&ivrs[0..4], b"IVRS");
2163 assert_eq!(checksum(&ivrs), 0);
2165
2166 let ivhd_offset = 48;
2169 assert_eq!(ivrs[ivhd_offset], 0x11); let dev_id = u16::from_ne_bytes(ivrs[ivhd_offset + 4..ivhd_offset + 6].try_into().unwrap());
2173 assert_eq!(dev_id, 0x0000);
2174
2175 let cap_offset =
2177 u16::from_ne_bytes(ivrs[ivhd_offset + 6..ivhd_offset + 8].try_into().unwrap());
2178 assert_eq!(cap_offset, 0x40);
2179
2180 let mmio_base =
2182 u64::from_ne_bytes(ivrs[ivhd_offset + 8..ivhd_offset + 16].try_into().unwrap());
2183 assert_eq!(mmio_base, 0xFD00_0000);
2184
2185 let efr = u64::from_ne_bytes(ivrs[ivhd_offset + 24..ivhd_offset + 32].try_into().unwrap());
2187 assert_eq!(efr, 0xC0); let dev_entry_offset = ivhd_offset + 40;
2192 assert_eq!(ivrs[dev_entry_offset], 0x03); assert_eq!(ivrs[dev_entry_offset + 4], 0x04); }
2195
2196 #[test]
2197 fn test_ivrs_not_generated_when_disabled() {
2198 let mem = new_mem();
2199 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2200 let pcie = vec![];
2201 let builder = new_builder(&mem, &topology, &pcie);
2202
2203 assert!(builder.build_ivrs().is_none());
2205
2206 let tables = builder.build_acpi_tables(0x100000, |_| {});
2207 assert!(!contains_signature(&tables.tables, b"IVRS"));
2208 }
2209
2210 #[test]
2211 fn test_ivrs_in_acpi_tables() {
2212 let mem = new_mem();
2213 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2214 let pcie = vec![];
2215 let mut builder = new_builder(&mem, &topology, &pcie);
2216 set_amd_iommu(
2217 &mut builder,
2218 vec![AmdIommuAcpiConfig {
2219 device_id: 0x0000,
2220 capability_offset: 0x40,
2221 mmio_base: 0xFD00_0000,
2222 pci_segment: 0,
2223 ivhd_features: 0xC0,
2224 start_bus: 0,
2225 end_bus: 255,
2226 }],
2227 );
2228
2229 let tables = builder.build_acpi_tables(0x100000, |_| {});
2230 assert!(contains_signature(&tables.tables, b"IVRS"));
2231 }
2232
2233 #[test]
2234 fn test_ivrs_iommu_fields() {
2235 let mem = new_mem();
2236 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2237 let pcie = vec![];
2238 let mut builder = new_builder(&mem, &topology, &pcie);
2239 set_amd_iommu(
2240 &mut builder,
2241 vec![AmdIommuAcpiConfig {
2242 device_id: 0x1234,
2243 capability_offset: 0x80,
2244 mmio_base: 0xFE00_0000,
2245 pci_segment: 1,
2246 ivhd_features: 0xC0,
2247 start_bus: 0,
2248 end_bus: 255,
2249 }],
2250 );
2251
2252 let ivrs = builder.build_ivrs().unwrap();
2253
2254 let ivhd_offset = 48;
2255 let dev_id = u16::from_ne_bytes(ivrs[ivhd_offset + 4..ivhd_offset + 6].try_into().unwrap());
2257 assert_eq!(dev_id, 0x1234);
2258
2259 let cap_offset =
2261 u16::from_ne_bytes(ivrs[ivhd_offset + 6..ivhd_offset + 8].try_into().unwrap());
2262 assert_eq!(cap_offset, 0x80);
2263
2264 let mmio_base =
2266 u64::from_ne_bytes(ivrs[ivhd_offset + 8..ivhd_offset + 16].try_into().unwrap());
2267 assert_eq!(mmio_base, 0xFE00_0000);
2268
2269 let pci_seg =
2271 u16::from_ne_bytes(ivrs[ivhd_offset + 16..ivhd_offset + 18].try_into().unwrap());
2272 assert_eq!(pci_seg, 1);
2273 }
2274
2275 #[test]
2276 fn test_ivrs_multiple_iommus() {
2277 let mem = new_mem();
2278 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2279 let pcie = vec![];
2280 let mut builder = new_builder(&mem, &topology, &pcie);
2281 set_amd_iommu(
2282 &mut builder,
2283 vec![
2284 AmdIommuAcpiConfig {
2285 device_id: 0x0000,
2286 capability_offset: 0x40,
2287 mmio_base: 0xFD00_0000,
2288 pci_segment: 0,
2289 ivhd_features: 0xC0,
2290 start_bus: 0,
2291 end_bus: 127,
2292 },
2293 AmdIommuAcpiConfig {
2294 device_id: 0x0000,
2295 capability_offset: 0x40,
2296 mmio_base: 0xFD00_4000,
2297 pci_segment: 1,
2298 ivhd_features: 0xC0,
2299 start_bus: 0,
2300 end_bus: 255,
2301 },
2302 ],
2303 );
2304
2305 let ivrs = builder.build_ivrs().unwrap();
2306
2307 assert_eq!(&ivrs[0..4], b"IVRS");
2309 assert_eq!(checksum(&ivrs), 0);
2310
2311 let ivhd0_offset = 48;
2313 assert_eq!(ivrs[ivhd0_offset], 0x11); let ivhd0_len =
2317 u16::from_ne_bytes(ivrs[ivhd0_offset + 2..ivhd0_offset + 4].try_into().unwrap());
2318
2319 let mmio0 = u64::from_ne_bytes(
2321 ivrs[ivhd0_offset + 8..ivhd0_offset + 16]
2322 .try_into()
2323 .unwrap(),
2324 );
2325 assert_eq!(mmio0, 0xFD00_0000);
2326 let seg0 = u16::from_ne_bytes(
2327 ivrs[ivhd0_offset + 16..ivhd0_offset + 18]
2328 .try_into()
2329 .unwrap(),
2330 );
2331 assert_eq!(seg0, 0);
2332
2333 let ivhd1_offset = ivhd0_offset + ivhd0_len as usize;
2335 assert_eq!(ivrs[ivhd1_offset], 0x11); let mmio1 = u64::from_ne_bytes(
2339 ivrs[ivhd1_offset + 8..ivhd1_offset + 16]
2340 .try_into()
2341 .unwrap(),
2342 );
2343 assert_eq!(mmio1, 0xFD00_4000);
2344 let seg1 = u16::from_ne_bytes(
2345 ivrs[ivhd1_offset + 16..ivhd1_offset + 18]
2346 .try_into()
2347 .unwrap(),
2348 );
2349 assert_eq!(seg1, 1);
2350 }
2351
2352 fn set_intel_vtd(
2353 builder: &mut AcpiTablesBuilder<'_, X86Topology>,
2354 configs: Vec<IntelVtdAcpiConfig>,
2355 ) {
2356 set_intel_vtd_with_ioapic(builder, configs, None);
2357 }
2358
2359 fn set_intel_vtd_with_ioapic(
2360 builder: &mut AcpiTablesBuilder<'_, X86Topology>,
2361 configs: Vec<IntelVtdAcpiConfig>,
2362 ioapic_rid: Option<u16>,
2363 ) {
2364 if let AcpiArchConfig::X86 { iommu, .. } = &mut builder.arch {
2365 *iommu = Some(X86IommuAcpiConfig::IntelVtd(IntelVtdDmarConfig {
2366 host_address_width: 48,
2367 units: configs,
2368 ioapic_rid,
2369 }));
2370 } else {
2371 panic!("expected X86 arch config");
2372 }
2373 }
2374
2375 #[test]
2376 fn test_dmar_basic() {
2377 let mem = new_mem();
2378 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2379 let pcie = vec![];
2380 let mut builder = new_builder(&mem, &topology, &pcie);
2381 set_intel_vtd(
2382 &mut builder,
2383 vec![IntelVtdAcpiConfig {
2384 mmio_base: 0xFED9_0000,
2385 pci_segment: 0,
2386 start_bus: 0,
2387 device_scopes: vec![
2388 IntelVtdDeviceScope {
2389 devfn: 0x00,
2390 is_bridge: true,
2391 },
2392 IntelVtdDeviceScope {
2393 devfn: 0x01,
2394 is_bridge: true,
2395 },
2396 ],
2397 }],
2398 );
2399
2400 let dmar = builder.build_dmar().unwrap();
2401
2402 assert_eq!(&dmar[0..4], b"DMAR");
2404 assert_eq!(checksum(&dmar), 0);
2406
2407 let body_offset = 36;
2409 assert_eq!(dmar[body_offset], 47);
2411 assert_eq!(dmar[body_offset + 1], 0x01);
2413
2414 let drhd_offset = 48;
2416 let struct_type =
2418 u16::from_ne_bytes(dmar[drhd_offset..drhd_offset + 2].try_into().unwrap());
2419 assert_eq!(struct_type, 0x0000);
2420
2421 assert_eq!(dmar[drhd_offset + 4], 0);
2423
2424 let reg_base =
2426 u64::from_ne_bytes(dmar[drhd_offset + 8..drhd_offset + 16].try_into().unwrap());
2427 assert_eq!(reg_base, 0xFED9_0000);
2428
2429 let scope0_offset = drhd_offset + 16;
2431 assert_eq!(dmar[scope0_offset], 2);
2433 assert_eq!(dmar[scope0_offset + 5], 0);
2435 assert_eq!(dmar[scope0_offset + 6], 0);
2437 assert_eq!(dmar[scope0_offset + 7], 0);
2438
2439 let scope1_offset = scope0_offset + 8;
2441 assert_eq!(dmar[scope1_offset], 2);
2443 assert_eq!(dmar[scope1_offset + 5], 0);
2445 assert_eq!(dmar[scope1_offset + 6], 0);
2447 assert_eq!(dmar[scope1_offset + 7], 1);
2448 }
2449
2450 #[test]
2451 fn test_dmar_ioapic_scope() {
2452 let mem = new_mem();
2453 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2454 let pcie = vec![];
2455 let mut builder = new_builder(&mem, &topology, &pcie);
2456 set_intel_vtd_with_ioapic(
2457 &mut builder,
2458 vec![IntelVtdAcpiConfig {
2459 mmio_base: 0xFED9_0000,
2460 pci_segment: 0,
2461 start_bus: 0,
2462 device_scopes: vec![IntelVtdDeviceScope {
2463 devfn: 0x00,
2464 is_bridge: true,
2465 }],
2466 }],
2467 Some(0x00A0),
2468 );
2469
2470 let dmar = builder.build_dmar().unwrap();
2471 assert_eq!(checksum(&dmar), 0);
2472
2473 let drhd_offset = 48;
2474 let drhd_len =
2475 u16::from_ne_bytes(dmar[drhd_offset + 2..drhd_offset + 4].try_into().unwrap());
2476 assert_eq!(drhd_len, 32);
2477
2478 let ioapic_scope_offset = drhd_offset + 16 + 8;
2479 assert_eq!(
2480 dmar[ioapic_scope_offset],
2481 acpi_spec::dmar::DEVICE_SCOPE_IOAPIC
2482 );
2483 assert_eq!(dmar[ioapic_scope_offset + 4], 0);
2484 assert_eq!(dmar[ioapic_scope_offset + 5], 0);
2485 assert_eq!(dmar[ioapic_scope_offset + 6], 0x14);
2486 assert_eq!(dmar[ioapic_scope_offset + 7], 0);
2487 }
2488
2489 #[test]
2490 #[should_panic(expected = "must be covered by exactly one segment-0 DRHD")]
2491 fn test_dmar_ioapic_rid_must_be_covered() {
2492 let mem = new_mem();
2493 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2494 let pcie = vec![];
2495 let mut builder = new_builder(&mem, &topology, &pcie);
2496 set_intel_vtd_with_ioapic(
2497 &mut builder,
2498 vec![IntelVtdAcpiConfig {
2499 mmio_base: 0xFED9_0000,
2500 pci_segment: 1,
2501 start_bus: 0,
2502 device_scopes: vec![IntelVtdDeviceScope {
2503 devfn: 0x00,
2504 is_bridge: true,
2505 }],
2506 }],
2507 Some(0x00A0),
2508 );
2509
2510 let _ = builder.build_dmar();
2511 }
2512
2513 #[test]
2514 fn test_dmar_not_generated_when_disabled() {
2515 let mem = new_mem();
2516 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2517 let pcie = vec![];
2518 let builder = new_builder(&mem, &topology, &pcie);
2519
2520 assert!(builder.build_dmar().is_none());
2521
2522 let tables = builder.build_acpi_tables(0x100000, |_| {});
2523 assert!(!contains_signature(&tables.tables, b"DMAR"));
2524 }
2525
2526 #[test]
2527 fn test_dmar_in_acpi_tables() {
2528 let mem = new_mem();
2529 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2530 let pcie = vec![];
2531 let mut builder = new_builder(&mem, &topology, &pcie);
2532 set_intel_vtd(
2533 &mut builder,
2534 vec![IntelVtdAcpiConfig {
2535 mmio_base: 0xFED9_0000,
2536 pci_segment: 0,
2537 start_bus: 0,
2538 device_scopes: vec![IntelVtdDeviceScope {
2539 devfn: 0x00,
2540 is_bridge: true,
2541 }],
2542 }],
2543 );
2544
2545 let tables = builder.build_acpi_tables(0x100000, |_| {});
2546 assert!(contains_signature(&tables.tables, b"DMAR"));
2547 }
2548
2549 #[test]
2550 fn test_dmar_multiple_units() {
2551 let mem = new_mem();
2552 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2553 let pcie = vec![];
2554 let mut builder = new_builder(&mem, &topology, &pcie);
2555 set_intel_vtd(
2556 &mut builder,
2557 vec![
2558 IntelVtdAcpiConfig {
2559 mmio_base: 0xFED9_0000,
2560 pci_segment: 0,
2561 start_bus: 0,
2562 device_scopes: vec![IntelVtdDeviceScope {
2563 devfn: 0x00,
2564 is_bridge: true,
2565 }],
2566 },
2567 IntelVtdAcpiConfig {
2568 mmio_base: 0xFED9_1000,
2569 pci_segment: 1,
2570 start_bus: 128,
2571 device_scopes: vec![IntelVtdDeviceScope {
2572 devfn: 0x00,
2573 is_bridge: true,
2574 }],
2575 },
2576 ],
2577 );
2578
2579 let dmar = builder.build_dmar().unwrap();
2580
2581 assert_eq!(&dmar[0..4], b"DMAR");
2582 assert_eq!(checksum(&dmar), 0);
2583
2584 let drhd0_offset = 48;
2586 let drhd0_len =
2587 u16::from_ne_bytes(dmar[drhd0_offset + 2..drhd0_offset + 4].try_into().unwrap());
2588 let reg_base0 = u64::from_ne_bytes(
2589 dmar[drhd0_offset + 8..drhd0_offset + 16]
2590 .try_into()
2591 .unwrap(),
2592 );
2593 assert_eq!(reg_base0, 0xFED9_0000);
2594 let seg0 = u16::from_ne_bytes(dmar[drhd0_offset + 6..drhd0_offset + 8].try_into().unwrap());
2595 assert_eq!(seg0, 0);
2596
2597 let drhd1_offset = drhd0_offset + drhd0_len as usize;
2599 let reg_base1 = u64::from_ne_bytes(
2600 dmar[drhd1_offset + 8..drhd1_offset + 16]
2601 .try_into()
2602 .unwrap(),
2603 );
2604 assert_eq!(reg_base1, 0xFED9_1000);
2605 let seg1 = u16::from_ne_bytes(dmar[drhd1_offset + 6..drhd1_offset + 8].try_into().unwrap());
2606 assert_eq!(seg1, 1);
2607
2608 let scope1_offset = drhd1_offset + 16;
2610 assert_eq!(dmar[scope1_offset + 5], 128);
2611 }
2612
2613 #[test]
2618 fn test_ivrs_ioapic_special_entry_placement() {
2619 let mem = new_mem();
2620 let topology = TopologyBuilder::new_x86().build(4).unwrap();
2621 let pcie = vec![];
2622 let mut builder = new_builder(&mem, &topology, &pcie);
2623 let ioapic_rid = 0x00A0u16;
2625 set_amd_iommu_with_ioapic(
2626 &mut builder,
2627 vec![
2628 AmdIommuAcpiConfig {
2630 device_id: 0x0000,
2631 capability_offset: 0x40,
2632 mmio_base: 0xFD00_4000,
2633 pci_segment: 1,
2634 ivhd_features: 0xC0,
2635 start_bus: 0,
2636 end_bus: 255,
2637 },
2638 AmdIommuAcpiConfig {
2640 device_id: 0x0000,
2641 capability_offset: 0x40,
2642 mmio_base: 0xFD00_0000,
2643 pci_segment: 0,
2644 ivhd_features: 0xC0,
2645 start_bus: 0,
2646 end_bus: 127,
2647 },
2648 ],
2649 Some(ioapic_rid),
2650 );
2651
2652 let ivrs = builder.build_ivrs().unwrap();
2653 assert_eq!(&ivrs[0..4], b"IVRS");
2654 assert_eq!(checksum(&ivrs), 0);
2655
2656 let ivhd0_offset = 48;
2660 assert_eq!(ivrs[ivhd0_offset], 0x11);
2661 let ivhd0_len =
2662 u16::from_ne_bytes(ivrs[ivhd0_offset + 2..ivhd0_offset + 4].try_into().unwrap());
2663 assert_eq!(ivhd0_len as usize, 40 + 2 * 4);
2664
2665 let ivhd1_offset = ivhd0_offset + ivhd0_len as usize;
2668 assert_eq!(ivrs[ivhd1_offset], 0x11);
2669 let ivhd1_len =
2670 u16::from_ne_bytes(ivrs[ivhd1_offset + 2..ivhd1_offset + 4].try_into().unwrap());
2671 assert_eq!(ivhd1_len as usize, 40 + 2 * 4 + 8);
2672 let seg1 = u16::from_ne_bytes(
2673 ivrs[ivhd1_offset + 16..ivhd1_offset + 18]
2674 .try_into()
2675 .unwrap(),
2676 );
2677 assert_eq!(seg1, 0);
2678
2679 let special = ivhd1_offset + 40 + 2 * 4;
2681 assert_eq!(ivrs[special], 0x48); let src_rid = u16::from_ne_bytes(ivrs[special + 5..special + 7].try_into().unwrap());
2684 assert_eq!(src_rid, ioapic_rid);
2685 assert_eq!(ivrs[special + 7], 0x01);
2687 }
2688}