From 36041e43d4293a2eff5db583e0fcbcaa01a3c029 Mon Sep 17 00:00:00 2001 From: Zejun Zhao Date: Mon, 3 Feb 2025 12:49:27 +0000 Subject: [PATCH 1/3] acpi: add support for SRAT --- acpi/src/lib.rs | 1 + acpi/src/madt.rs | 2 + acpi/src/platform/mod.rs | 97 ++++++++++-- acpi/src/srat.rs | 335 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 418 insertions(+), 17 deletions(-) create mode 100644 acpi/src/srat.rs diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index 0b254df1..0f01d963 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -73,6 +73,7 @@ pub mod mcfg; pub mod rsdp; pub mod sdt; pub mod spcr; +pub mod srat; #[cfg(feature = "allocator_api")] mod managed_slice; diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs index 9f1427dd..75bf0d98 100644 --- a/acpi/src/madt.rs +++ b/acpi/src/madt.rs @@ -191,6 +191,7 @@ impl Madt { let processor = Processor { processor_uid: entry.processor_id as u32, local_apic_id: entry.apic_id as u32, + proximity_domain: None, state, is_ap, }; @@ -216,6 +217,7 @@ impl Madt { let processor = Processor { processor_uid: entry.processor_uid, local_apic_id: entry.x2apic_id, + proximity_domain: None, state, is_ap, }; diff --git a/acpi/src/platform/mod.rs b/acpi/src/platform/mod.rs index bf4097c3..eb8b4eda 100644 --- a/acpi/src/platform/mod.rs +++ b/acpi/src/platform/mod.rs @@ -1,15 +1,7 @@ pub mod interrupt; use crate::{ - address::GenericAddress, - fadt::Fadt, - madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox}, - AcpiError, - AcpiHandler, - AcpiResult, - AcpiTables, - ManagedSlice, - PowerProfile, + address::GenericAddress, fadt::Fadt, madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox}, srat::Srat, AcpiError, AcpiHandler, AcpiResult, AcpiTables, ManagedSlice, PowerProfile }; use core::{alloc::Allocator, mem, ptr}; use interrupt::InterruptModel; @@ -35,6 +27,8 @@ pub struct Processor { /// The ID of the local APIC of the processor. Will be less than `256` if the APIC is being used, but can be /// greater than this if the X2APIC is being used. pub local_apic_id: u32, + /// Proximity domain to which the processor belongs to. + pub proximity_domain: Option, /// The state of this processor. Check that the processor is not `Disabled` before attempting to bring it up! pub state: ProcessorState, @@ -45,6 +39,19 @@ pub struct Processor { pub is_ap: bool, } +impl Processor { + fn attach_affinity(&mut self, topology: &ProcessorTopology) + where + A: Allocator, + { + for affinity in topology.processor_affinities.iter() { + if affinity.local_apic_id == self.local_apic_id { + self.proximity_domain = Some(affinity.proximity_domain); + } + } + } +} + #[derive(Debug, Clone)] pub struct ProcessorInfo<'a, A> where @@ -62,6 +69,44 @@ where pub(crate) fn new(boot_processor: Processor, application_processors: ManagedSlice<'a, Processor, A>) -> Self { Self { boot_processor, application_processors } } + + fn attach_affinity(&mut self, topology: &ProcessorTopology<'a, A>) { + self.boot_processor.attach_affinity(topology); + for application_processor in self.application_processors.iter_mut() { + application_processor.attach_affinity(topology); + } + } +} + +pub struct ProcessorAffinity { + pub local_apic_id: u32, + pub proximity_domain: u32, + pub is_enabled: bool, +} + +pub struct ProcessorTopology<'a, A> +where + A: Allocator, +{ + pub processor_affinities: ManagedSlice<'a, ProcessorAffinity, A>, +} + +#[derive(Debug, Clone)] +pub struct MemoryRange { + pub base_address: u64, + pub length: u64, + pub proximity_domain: Option, + pub hot_pluggable: bool, + pub non_volatile: bool, + pub is_enabled: bool, +} + +#[derive(Debug, Clone)] +pub struct MemoryInfo<'a, A> +where + A: Allocator, +{ + pub memory_ranges: ManagedSlice<'a, MemoryRange, A>, } /// Information about the ACPI Power Management Timer (ACPI PM Timer). @@ -95,6 +140,7 @@ where /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the /// interrupt model. That information is stored here, if present. pub processor_info: Option>, + pub memory_info: Option>, pub pm_timer: Option, /* * TODO: we could provide a nice view of the hardware register blocks in the FADT here. @@ -119,17 +165,34 @@ where where H: AcpiHandler, { - let fadt = tables.find_table::()?; - let power_profile = fadt.power_profile(); + let (power_profile, pm_timer) = { + let fadt = tables.find_table::()?; + (fadt.power_profile(), PmTimer::new(&fadt)?) + }; + + let (interrupt_model, processor_info) = { + let madt = tables.find_table::(); + match madt { + Ok(madt) => madt.get().parse_interrupt_model_in(allocator.clone())?, + Err(_) => (InterruptModel::Unknown, None), + } + }; - let madt = tables.find_table::(); - let (interrupt_model, processor_info) = match madt { - Ok(madt) => madt.get().parse_interrupt_model_in(allocator)?, - Err(_) => (InterruptModel::Unknown, None), + let (processor_info, memory_info) = { + let srat = tables.find_table::(); + match srat { + Ok(srat) => { + let (processor_topology, memory_info) = srat.get().parse_topology_in(allocator)?; + (processor_info.map(|mut processor_info| { + processor_info.attach_affinity(&processor_topology); + processor_info + }), Some(memory_info)) + } + Err(_) => (processor_info, None), + } }; - let pm_timer = PmTimer::new(&fadt)?; - Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer }) + Ok(PlatformInfo { power_profile, interrupt_model, processor_info, memory_info, pm_timer }) } } diff --git a/acpi/src/srat.rs b/acpi/src/srat.rs new file mode 100644 index 00000000..51d33896 --- /dev/null +++ b/acpi/src/srat.rs @@ -0,0 +1,335 @@ +use core::{marker::{PhantomData, PhantomPinned}, mem, pin::Pin}; +use core::alloc::Allocator; + +use bitflags::bitflags; + +use crate::{platform::{MemoryInfo, ProcessorTopology}, sdt::{SdtHeader, Signature}, AcpiResult, AcpiTable}; + +/// System Resource Affinity Table (SRAT). +/// +/// This optional table provides information that allows OSPM to associate the following types of +/// devices with system locality / proximity domains and clock domains: +/// - processors, +/// - memory ranges (including those provided by hot-added memory devices), and +/// - generic initiators (e.g. heterogeneous processors and accelerators, GPUs, and I/O devices +/// with integrated compute or DMA engines). +#[repr(C, packed)] +pub struct Srat { + header: SdtHeader, + _reserved_1: u32, + _reserved_2: u64, + _pinned: PhantomPinned, +} + +/// ### Safety: Implementation properly represents a valid SRAT. +unsafe impl AcpiTable for Srat { + const SIGNATURE: Signature = Signature::SRAT; + + fn header(&self) -> &SdtHeader { + &self.header + } +} + +impl Srat { + #[cfg(feature = "allocator_api")] + pub fn parse_topology_in<'a, A>( + self: Pin<&Self>, + allocator: A + ) -> AcpiResult<(ProcessorTopology<'a, A>, MemoryInfo<'a, A>)> + where + A: Allocator + Clone, + { + for entry in self.entries() { + match entry { + SratEntry::LocalApic(_) | + SratEntry::LocalX2Apic(_) => { + return self.parse_apic_topology_in(allocator); + } + SratEntry::Gicc(_) | + SratEntry::GicIts(_) => { + unimplemented!(); + } + SratEntry::Memory(_) | + SratEntry::GenericInitiator(_) => {} + }; + } + + unreachable!(); + } + + #[cfg(feature = "allocator_api")] + pub fn parse_apic_topology_in<'a, A>( + self: Pin<&Self>, + allocator: A + ) -> AcpiResult<(ProcessorTopology<'a, A>, MemoryInfo<'a, A>)> + where + A: Allocator + Clone, + { + use crate::{platform::{MemoryRange, ProcessorAffinity}, ManagedSlice}; + + let mut processor_count = 0; + let mut memory_range_count = 0; + + for entry in self.entries() { + match entry { + SratEntry::LocalApic(_) | + SratEntry::LocalX2Apic(_) => processor_count += 1, + SratEntry::Memory(_) => memory_range_count += 1, + _ => (), + } + } + + let mut processor_affinities = ManagedSlice::new_in(processor_count, allocator.clone())?; + let mut memory_ranges = ManagedSlice::new_in(memory_range_count, allocator.clone())?; + + processor_count = 0; + memory_range_count = 0; + + for entry in self.entries() { + match entry { + SratEntry::LocalApic(entry) => { + let local_apic_id = entry.apic_id as u32; + let mut proximity_domain = entry.proximity_domain_low as u32; + for i in 0..3 { + let shift = 8 * (3 - i); + proximity_domain += (entry.proximity_domain_high[i] as u32) << shift; + } + let flags = entry.flags; + let is_enabled = flags.contains(LocalApicAffinityFlags::ENABLED); + let processor_affinity = ProcessorAffinity { local_apic_id, proximity_domain, is_enabled }; + processor_affinities[processor_count] = processor_affinity; + processor_count += 1; + } + SratEntry::LocalX2Apic(entry) => { + let local_apic_id = entry.x2apic_id; + let proximity_domain = entry.proximity_domain; + let flags = entry.flags; + let is_enabled = flags.contains(LocalX2ApicAffinityFlags::ENABLED); + let processor_affinity = ProcessorAffinity { local_apic_id, proximity_domain, is_enabled }; + processor_affinities[processor_count] = processor_affinity; + processor_count += 1; + } + SratEntry::Memory(entry) => { + let flags = entry.flags; + let base_address = entry.base_address_low as u64 + ((entry.base_address_high as u64) << 32); + let length = entry.length_low as u64 + ((entry.length_high as u64) << 32); + let proximity_domain = Some(entry.proximity_domain); + let hot_pluggable = flags.contains(MemoryAffinityFlags::HOT_PLUGGABLE); + let non_volatile = flags.contains(MemoryAffinityFlags::NON_VOLATILE); + let is_enabled = flags.contains(MemoryAffinityFlags::ENABLED); + let memory_range = MemoryRange { + base_address, + length, + proximity_domain, + hot_pluggable, + non_volatile, + is_enabled, + }; + memory_ranges[memory_range_count] = memory_range; + memory_range_count += 1; + } + // TODO: parse information of generic initiators + SratEntry::GenericInitiator(_) => {} + _ => {} + } + } + + let processor_topology = ProcessorTopology { processor_affinities }; + let memory_info = MemoryInfo { memory_ranges }; + Ok((processor_topology, memory_info)) + } + + fn entries(self: Pin<&Self>) -> SratEntryIter { + let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Srat as *const u8 }; + SratEntryIter { + pointer: unsafe { ptr.add(mem::size_of::()) }, + remaining_length: self.header.length - mem::size_of::() as u32, + _phantom: PhantomData, + } + } +} + +struct SratEntryIter<'a> { + pointer: *const u8, + remaining_length: u32, + _phantom: PhantomData<&'a ()>, +} + +impl<'a> Iterator for SratEntryIter<'a> { + type Item = SratEntry<'a>; + + fn next(&mut self) -> Option { + if self.remaining_length > 0 { + let entry_pointer = self.pointer; + let entry_header = unsafe { *(self.pointer as *const AffinityEntryHeader) }; + + self.pointer = unsafe { self.pointer.offset(entry_header.length as isize) }; + self.remaining_length -= entry_header.length as u32; + + match entry_header.r#type { + AffinityEntryType::LocalApic => Some(SratEntry::LocalApic(unsafe { &*(entry_pointer as *const LocalApicAffinityEntry) })), + AffinityEntryType::Memory => Some(SratEntry::Memory(unsafe { &*(entry_pointer as *const MemoryAffinityEntry) })), + AffinityEntryType::LocalX2Apic => Some(SratEntry::LocalX2Apic(unsafe { &*(entry_pointer as *const LocalX2ApicAffinityEntry) })), + AffinityEntryType::Gicc => Some(SratEntry::Gicc(unsafe { &*(entry_pointer as *const GiccAffinityEntry) })), + AffinityEntryType::GicIts => Some(SratEntry::GicIts(unsafe { &*(entry_pointer as *const GicItsAffinityEntry) })), + AffinityEntryType::GenericInitiator => Some(SratEntry::GenericInitiator(unsafe { &*(entry_pointer as *const GenericInitiatorAffinityEntry) })), + } + } else { + None + } + } +} + +enum SratEntry <'a>{ + LocalApic(&'a LocalApicAffinityEntry), + Memory(&'a MemoryAffinityEntry), + LocalX2Apic(&'a LocalX2ApicAffinityEntry), + // TODO: remove the attributes after we can parse these entries. + #[allow(unused)] + Gicc(&'a GiccAffinityEntry), + #[allow(unused)] + GicIts(&'a GicItsAffinityEntry), + #[allow(unused)] + GenericInitiator(&'a GenericInitiatorAffinityEntry), +} + +#[derive(Clone, Copy)] +#[repr(C, packed)] +struct AffinityEntryHeader { + r#type: AffinityEntryType, + length: u8, +} + +#[allow(unused)] +#[derive(Clone, Copy)] +#[repr(u8)] +enum AffinityEntryType { + LocalApic = 0, + Memory = 1, + LocalX2Apic = 2, + Gicc = 3, + GicIts = 4, + GenericInitiator = 5, +} + +#[repr(C, packed)] +struct LocalApicAffinityEntry { + header: AffinityEntryHeader, + proximity_domain_low: u8, + apic_id: u8, + flags: LocalApicAffinityFlags, + local_sapic_eid: u8, + proximity_domain_high: [u8; 3], + clock_domain: u32, +} + +bitflags! { + #[derive(Clone, Copy)] + struct LocalApicAffinityFlags: u32 { + const ENABLED = 1; + } +} + +#[repr(C, packed)] +struct MemoryAffinityEntry { + header: AffinityEntryHeader, + proximity_domain: u32, + _reserved_1: u16, + base_address_low: u32, + base_address_high: u32, + length_low: u32, + length_high: u32, + _reserved_2: u32, + flags: MemoryAffinityFlags, + _reserved_3: u64, +} + +bitflags! { + #[derive(Clone, Copy)] + struct MemoryAffinityFlags: u32 { + const ENABLED = 1; + const HOT_PLUGGABLE = 1 << 1; + const NON_VOLATILE = 1 << 2; + } +} + +#[repr(C, packed)] +struct LocalX2ApicAffinityEntry { + header: AffinityEntryHeader, + _reserved_1: u16, + proximity_domain: u32, + x2apic_id: u32, + flags: LocalX2ApicAffinityFlags, + clock_domain: u32, + _reserved_2: u32, +} + +type LocalX2ApicAffinityFlags = LocalApicAffinityFlags; + +#[repr(C, packed)] +struct GiccAffinityEntry { + header: AffinityEntryHeader, + proximity_domain: u32, + acpi_processor_uid: u32, + flags: GiccAffinityFlags, + clock_domain: u32, +} + +type GiccAffinityFlags = LocalApicAffinityFlags; + +#[repr(C, packed)] +struct GicItsAffinityEntry { + header: AffinityEntryHeader, + proximity_domain: u32, + _reserved: u16, + its_id: u32, +} + +#[repr(C, packed)] +struct GenericInitiatorAffinityEntry { + header: AffinityEntryHeader, + _reserved_1: u8, + device_handle_type: DeviceHandleType, + proximity_domain: u32, + device_handle: DeviceHandle, + flags: GenericInitiatorAffinityFlags, + _reserved_2: u32, +} + +#[allow(unused)] +#[repr(u8)] +#[non_exhaustive] +enum DeviceHandleType { + Acpi = 0, + Pci = 1, +} + +// TODO: remove this attribute after we can parse generic initiator affinity entry. +#[allow(unused)] +#[repr(C)] +enum DeviceHandle { + Acpi(AcpiDeviceHandle), + Pci(PciDeviceHandle), +} + +#[repr(C, packed)] +struct AcpiDeviceHandle { + acpi_hid: u64, + acpi_uid: u32, + _reserved: u32, +} + +#[repr(C, packed)] +struct PciDeviceHandle { + pci_segment: u16, + pci_bdf_number: u16, + _reserved: [u32; 3], +} + +bitflags! { + #[derive(Clone, Copy)] + struct GenericInitiatorAffinityFlags: u32 { + const ENABLED = 1; + const ARCHITECTURAL_TRANSACTIONS = 1 << 1; + } +} From db8a7a675e7202e42fc17ed5601c19e0dfae7a5b Mon Sep 17 00:00:00 2001 From: Zejun Zhao Date: Mon, 10 Feb 2025 18:49:17 +0000 Subject: [PATCH 2/3] aml_tester: fix some lint warnings --- aml_tester/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aml_tester/src/main.rs b/aml_tester/src/main.rs index 50fab073..a17c247a 100644 --- a/aml_tester/src/main.rs +++ b/aml_tester/src/main.rs @@ -13,7 +13,7 @@ use aml::{AmlContext, DebugVerbosity}; use clap::{Arg, ArgAction, ArgGroup}; use std::{ cell::RefCell, - collections::{HashMap, HashSet}, + collections::HashSet, ffi::OsStr, fs::{self, File}, io::{Read, Write}, From 5264dd3c846600701089c076be4ed81bda143945 Mon Sep 17 00:00:00 2001 From: Zejun Zhao Date: Mon, 10 Feb 2025 18:49:32 +0000 Subject: [PATCH 3/3] aml: fix some lint warnings --- aml/src/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aml/src/value.rs b/aml/src/value.rs index 4da19842..21da8f8f 100644 --- a/aml/src/value.rs +++ b/aml/src/value.rs @@ -560,7 +560,7 @@ impl Args { } let mut args: Vec> = list.into_iter().map(Option::Some).collect(); - args.extend(core::iter::repeat(None).take(7 - args.len())); + args.extend(core::iter::repeat_n(None, 7 - args.len())); Ok(Args(args.try_into().unwrap())) }