Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5c8a0e7

Browse files
committedFeb 10, 2025··
acpi: add support for SRAT
1 parent bc88428 commit 5c8a0e7

File tree

4 files changed

+410
-17
lines changed

4 files changed

+410
-17
lines changed
 

‎acpi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub mod mcfg;
7373
pub mod rsdp;
7474
pub mod sdt;
7575
pub mod spcr;
76+
pub mod srat;
7677

7778
#[cfg(feature = "allocator_api")]
7879
mod managed_slice;

‎acpi/src/madt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ impl Madt {
191191
let processor = Processor {
192192
processor_uid: entry.processor_id as u32,
193193
local_apic_id: entry.apic_id as u32,
194+
proximity_domain: None,
194195
state,
195196
is_ap,
196197
};
@@ -216,6 +217,7 @@ impl Madt {
216217
let processor = Processor {
217218
processor_uid: entry.processor_uid,
218219
local_apic_id: entry.x2apic_id,
220+
proximity_domain: None,
219221
state,
220222
is_ap,
221223
};

‎acpi/src/platform/mod.rs

Lines changed: 80 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
pub mod interrupt;
22

33
use crate::{
4-
address::GenericAddress,
5-
fadt::Fadt,
6-
madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
7-
AcpiError,
8-
AcpiHandler,
9-
AcpiResult,
10-
AcpiTables,
11-
ManagedSlice,
12-
PowerProfile,
4+
address::GenericAddress, fadt::Fadt, madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox}, srat::Srat, AcpiError, AcpiHandler, AcpiResult, AcpiTables, ManagedSlice, PowerProfile
135
};
146
use core::{alloc::Allocator, mem, ptr};
157
use interrupt::InterruptModel;
@@ -35,6 +27,8 @@ pub struct Processor {
3527
/// The ID of the local APIC of the processor. Will be less than `256` if the APIC is being used, but can be
3628
/// greater than this if the X2APIC is being used.
3729
pub local_apic_id: u32,
30+
/// Proximity domain to which the processor belongs to.
31+
pub proximity_domain: Option<u32>,
3832

3933
/// The state of this processor. Check that the processor is not `Disabled` before attempting to bring it up!
4034
pub state: ProcessorState,
@@ -45,6 +39,19 @@ pub struct Processor {
4539
pub is_ap: bool,
4640
}
4741

42+
impl Processor {
43+
fn attach_affinity<'a, A>(&mut self, topology: &ProcessorTopology<'a, A>)
44+
where
45+
A: Allocator,
46+
{
47+
for affinity in topology.processor_affinities.iter() {
48+
if affinity.local_apic_id == self.local_apic_id {
49+
self.proximity_domain = Some(affinity.proximity_domain);
50+
}
51+
}
52+
}
53+
}
54+
4855
#[derive(Debug, Clone)]
4956
pub struct ProcessorInfo<'a, A>
5057
where
@@ -62,6 +69,44 @@ where
6269
pub(crate) fn new(boot_processor: Processor, application_processors: ManagedSlice<'a, Processor, A>) -> Self {
6370
Self { boot_processor, application_processors }
6471
}
72+
73+
fn attach_affinity(&mut self, topology: &ProcessorTopology<'a, A>) {
74+
self.boot_processor.attach_affinity(topology);
75+
for application_processor in self.application_processors.iter_mut() {
76+
application_processor.attach_affinity(topology);
77+
}
78+
}
79+
}
80+
81+
pub struct ProcessorAffinity {
82+
pub local_apic_id: u32,
83+
pub proximity_domain: u32,
84+
pub is_enabled: bool,
85+
}
86+
87+
pub struct ProcessorTopology<'a, A>
88+
where
89+
A: Allocator,
90+
{
91+
pub processor_affinities: ManagedSlice<'a, ProcessorAffinity, A>,
92+
}
93+
94+
#[derive(Debug, Clone)]
95+
pub struct MemoryRange {
96+
pub base_address: u64,
97+
pub length: u64,
98+
pub proximity_domain: Option<u32>,
99+
pub hot_pluggable: bool,
100+
pub non_volatile: bool,
101+
pub is_enabled: bool,
102+
}
103+
104+
#[derive(Debug, Clone)]
105+
pub struct MemoryInfo<'a, A>
106+
where
107+
A: Allocator,
108+
{
109+
pub memory_ranges: ManagedSlice<'a, MemoryRange, A>,
65110
}
66111

67112
/// Information about the ACPI Power Management Timer (ACPI PM Timer).
@@ -95,6 +140,7 @@ where
95140
/// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
96141
/// interrupt model. That information is stored here, if present.
97142
pub processor_info: Option<ProcessorInfo<'a, A>>,
143+
pub memory_info: Option<MemoryInfo<'a, A>>,
98144
pub pm_timer: Option<PmTimer>,
99145
/*
100146
* TODO: we could provide a nice view of the hardware register blocks in the FADT here.
@@ -119,17 +165,34 @@ where
119165
where
120166
H: AcpiHandler,
121167
{
122-
let fadt = tables.find_table::<Fadt>()?;
123-
let power_profile = fadt.power_profile();
168+
let (power_profile, pm_timer) = {
169+
let fadt = tables.find_table::<Fadt>()?;
170+
(fadt.power_profile(), PmTimer::new(&fadt)?)
171+
};
172+
173+
let (interrupt_model, processor_info) = {
174+
let madt = tables.find_table::<Madt>();
175+
match madt {
176+
Ok(madt) => madt.get().parse_interrupt_model_in(allocator.clone())?,
177+
Err(_) => (InterruptModel::Unknown, None),
178+
}
179+
};
124180

125-
let madt = tables.find_table::<Madt>();
126-
let (interrupt_model, processor_info) = match madt {
127-
Ok(madt) => madt.get().parse_interrupt_model_in(allocator)?,
128-
Err(_) => (InterruptModel::Unknown, None),
181+
let (processor_info, memory_info) = {
182+
let srat = tables.find_table::<Srat>();
183+
match srat {
184+
Ok(srat) => {
185+
let (processor_topology, memory_info) = srat.get().parse_topology_in(allocator)?;
186+
(processor_info.and_then(|mut processor_info| {
187+
processor_info.attach_affinity(&processor_topology);
188+
Some(processor_info)
189+
}), Some(memory_info))
190+
}
191+
Err(_) => (processor_info, None),
192+
}
129193
};
130-
let pm_timer = PmTimer::new(&fadt)?;
131194

132-
Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer })
195+
Ok(PlatformInfo { power_profile, interrupt_model, processor_info, memory_info, pm_timer })
133196
}
134197
}
135198

‎acpi/src/srat.rs

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
use core::{marker::{PhantomData, PhantomPinned}, mem, pin::Pin};
2+
use core::alloc::Allocator;
3+
4+
use bitflags::bitflags;
5+
6+
use crate::{platform::{MemoryInfo, ProcessorTopology}, sdt::{SdtHeader, Signature}, AcpiResult, AcpiTable};
7+
8+
/// System Resource Affinity Table (SRAT).
9+
///
10+
/// This optional table provides information that allows OSPM to associate the following types of
11+
/// devices with system locality / proximity domains and clock domains:
12+
/// - processors,
13+
/// - memory ranges (including those provided by hot-added memory devices), and
14+
/// - generic initiators (e.g. heterogeneous processors and accelerators, GPUs, and I/O devices
15+
/// with integrated compute or DMA engines).
16+
#[repr(C, packed)]
17+
pub struct Srat {
18+
header: SdtHeader,
19+
_reserved_1: u32,
20+
_reserved_2: u64,
21+
_pinned: PhantomPinned,
22+
}
23+
24+
/// ### Safety: Implementation properly represents a valid SRAT.
25+
unsafe impl AcpiTable for Srat {
26+
const SIGNATURE: Signature = Signature::SRAT;
27+
28+
fn header(&self) -> &SdtHeader {
29+
&self.header
30+
}
31+
}
32+
33+
impl Srat {
34+
#[cfg(feature = "allocator_api")]
35+
pub fn parse_topology_in<'a, A>(
36+
self: Pin<&Self>,
37+
allocator: A
38+
) -> AcpiResult<(ProcessorTopology<'a, A>, MemoryInfo<'a, A>)>
39+
where
40+
A: Allocator + Clone,
41+
{
42+
for entry in self.entries() {
43+
match entry {
44+
SratEntry::LocalApic(_) |
45+
SratEntry::LocalX2Apic(_) => {
46+
return self.parse_apic_topology_in(allocator);
47+
}
48+
SratEntry::Gicc(_) |
49+
SratEntry::GicIts(_) => {
50+
unimplemented!();
51+
}
52+
SratEntry::Memory(_) |
53+
SratEntry::GenericInitiator(_) => {}
54+
};
55+
}
56+
57+
unreachable!();
58+
}
59+
60+
#[cfg(feature = "allocator_api")]
61+
pub fn parse_apic_topology_in<'a, A>(
62+
self: Pin<&Self>,
63+
allocator: A
64+
) -> AcpiResult<(ProcessorTopology<'a, A>, MemoryInfo<'a, A>)>
65+
where
66+
A: Allocator + Clone,
67+
{
68+
use crate::{platform::{MemoryRange, ProcessorAffinity}, ManagedSlice};
69+
70+
let mut processor_count = 0;
71+
let mut memory_range_count = 0;
72+
73+
for entry in self.entries() {
74+
match entry {
75+
SratEntry::LocalApic(_) |
76+
SratEntry::LocalX2Apic(_) => processor_count += 1,
77+
SratEntry::Memory(_) => memory_range_count += 1,
78+
_ => (),
79+
}
80+
}
81+
82+
let mut processor_affinities = ManagedSlice::new_in(processor_count, allocator.clone())?;
83+
let mut memory_ranges = ManagedSlice::new_in(memory_range_count, allocator.clone())?;
84+
85+
processor_count = 0;
86+
memory_range_count = 0;
87+
88+
for entry in self.entries() {
89+
match entry {
90+
SratEntry::LocalApic(entry) => {
91+
let local_apic_id = entry.apic_id as u32;
92+
let mut proximity_domain = entry.proximity_domain_low as u32;
93+
for i in 0..3 {
94+
let shift = 8 * (3 - i);
95+
proximity_domain += (entry.proximity_domain_high[i] as u32) << shift;
96+
}
97+
let flags = entry.flags;
98+
let is_enabled = flags.contains(LocalApicAffinityFlags::ENABLED);
99+
let processor_affinity = ProcessorAffinity { local_apic_id, proximity_domain, is_enabled };
100+
processor_affinities[processor_count] = processor_affinity;
101+
processor_count += 1;
102+
}
103+
SratEntry::LocalX2Apic(entry) => {
104+
let local_apic_id = entry.x2apic_id;
105+
let proximity_domain = entry.proximity_domain;
106+
let flags = entry.flags;
107+
let is_enabled = flags.contains(LocalX2ApicAffinityFlags::ENABLED);
108+
let processor_affinity = ProcessorAffinity { local_apic_id, proximity_domain, is_enabled };
109+
processor_affinities[processor_count] = processor_affinity;
110+
processor_count += 1;
111+
}
112+
SratEntry::Memory(entry) => {
113+
let flags = entry.flags;
114+
let base_address = entry.base_address_low as u64 + ((entry.base_address_high as u64) << 32);
115+
let length = entry.length_low as u64 + ((entry.length_high as u64) << 32);
116+
let proximity_domain = Some(entry.proximity_domain);
117+
let hot_pluggable = flags.contains(MemoryAffinityFlags::HOT_PLUGGABLE);
118+
let non_volatile = flags.contains(MemoryAffinityFlags::NON_VOLATILE);
119+
let is_enabled = flags.contains(MemoryAffinityFlags::ENABLED);
120+
let memory_range = MemoryRange {
121+
base_address,
122+
length,
123+
proximity_domain,
124+
hot_pluggable,
125+
non_volatile,
126+
is_enabled,
127+
};
128+
memory_ranges[memory_range_count] = memory_range;
129+
memory_range_count += 1;
130+
}
131+
// TODO: parse information of generic initiators
132+
SratEntry::GenericInitiator(_) => {}
133+
_ => {}
134+
}
135+
}
136+
137+
let processor_topology = ProcessorTopology { processor_affinities };
138+
let memory_info = MemoryInfo { memory_ranges };
139+
Ok((processor_topology, memory_info))
140+
}
141+
142+
fn entries(self: Pin<&Self>) -> SratEntryIter {
143+
let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Srat as *const u8 };
144+
SratEntryIter {
145+
pointer: unsafe { ptr.add(mem::size_of::<Srat>()) },
146+
remaining_length: self.header.length - mem::size_of::<Srat>() as u32,
147+
_phantom: PhantomData,
148+
}
149+
}
150+
}
151+
152+
struct SratEntryIter<'a> {
153+
pointer: *const u8,
154+
remaining_length: u32,
155+
_phantom: PhantomData<&'a ()>,
156+
}
157+
158+
impl<'a> Iterator for SratEntryIter<'a> {
159+
type Item = SratEntry<'a>;
160+
161+
fn next(&mut self) -> Option<Self::Item> {
162+
if self.remaining_length > 0 {
163+
let entry_pointer = self.pointer;
164+
let entry_header = unsafe { *(self.pointer as *const AffinityEntryHeader) };
165+
166+
self.pointer = unsafe { self.pointer.offset(entry_header.length as isize) };
167+
self.remaining_length -= entry_header.length as u32;
168+
169+
match entry_header.r#type {
170+
AffinityEntryType::LocalApic => Some(SratEntry::LocalApic(unsafe { &*(entry_pointer as *const LocalApicAffinityEntry) })),
171+
AffinityEntryType::Memory => Some(SratEntry::Memory(unsafe { &*(entry_pointer as *const MemoryAffinityEntry) })),
172+
AffinityEntryType::LocalX2Apic => Some(SratEntry::LocalX2Apic(unsafe { &*(entry_pointer as *const LocalX2ApicAffinityEntry) })),
173+
AffinityEntryType::Gicc => Some(SratEntry::Gicc(unsafe { &*(entry_pointer as *const GiccAffinityEntry) })),
174+
AffinityEntryType::GicIts => Some(SratEntry::GicIts(unsafe { &*(entry_pointer as *const GicItsAffinityEntry) })),
175+
AffinityEntryType::GenericInitiator => Some(SratEntry::GenericInitiator(unsafe { &*(entry_pointer as *const GenericInitiatorAffinityEntry) })),
176+
}
177+
} else {
178+
None
179+
}
180+
}
181+
}
182+
183+
enum SratEntry <'a>{
184+
LocalApic(&'a LocalApicAffinityEntry),
185+
Memory(&'a MemoryAffinityEntry),
186+
LocalX2Apic(&'a LocalX2ApicAffinityEntry),
187+
Gicc(&'a GiccAffinityEntry),
188+
GicIts(&'a GicItsAffinityEntry),
189+
GenericInitiator(&'a GenericInitiatorAffinityEntry),
190+
}
191+
192+
#[derive(Clone, Copy)]
193+
#[repr(C, packed)]
194+
struct AffinityEntryHeader {
195+
r#type: AffinityEntryType,
196+
length: u8,
197+
}
198+
199+
#[derive(Clone, Copy)]
200+
#[repr(u8)]
201+
enum AffinityEntryType {
202+
LocalApic = 0,
203+
Memory = 1,
204+
LocalX2Apic = 2,
205+
Gicc = 3,
206+
GicIts = 4,
207+
GenericInitiator = 5,
208+
}
209+
210+
#[repr(C, packed)]
211+
struct LocalApicAffinityEntry {
212+
header: AffinityEntryHeader,
213+
proximity_domain_low: u8,
214+
apic_id: u8,
215+
flags: LocalApicAffinityFlags,
216+
local_sapic_eid: u8,
217+
proximity_domain_high: [u8; 3],
218+
clock_domain: u32,
219+
}
220+
221+
bitflags! {
222+
#[derive(Clone, Copy)]
223+
struct LocalApicAffinityFlags: u32 {
224+
const ENABLED = 1;
225+
}
226+
}
227+
228+
#[repr(C, packed)]
229+
struct MemoryAffinityEntry {
230+
header: AffinityEntryHeader,
231+
proximity_domain: u32,
232+
_reserved_1: u16,
233+
base_address_low: u32,
234+
base_address_high: u32,
235+
length_low: u32,
236+
length_high: u32,
237+
_reserved_2: u32,
238+
flags: MemoryAffinityFlags,
239+
_reserved_3: u64,
240+
}
241+
242+
bitflags! {
243+
#[derive(Clone, Copy)]
244+
struct MemoryAffinityFlags: u32 {
245+
const ENABLED = 1;
246+
const HOT_PLUGGABLE = 1 << 1;
247+
const NON_VOLATILE = 1 << 2;
248+
}
249+
}
250+
251+
#[repr(C, packed)]
252+
struct LocalX2ApicAffinityEntry {
253+
header: AffinityEntryHeader,
254+
_reserved_1: u16,
255+
proximity_domain: u32,
256+
x2apic_id: u32,
257+
flags: LocalX2ApicAffinityFlags,
258+
clock_domain: u32,
259+
_reserved_2: u32,
260+
}
261+
262+
type LocalX2ApicAffinityFlags = LocalApicAffinityFlags;
263+
264+
#[repr(C, packed)]
265+
struct GiccAffinityEntry {
266+
header: AffinityEntryHeader,
267+
proximity_domain: u32,
268+
acpi_processor_uid: u32,
269+
flags: GiccAffinityFlags,
270+
clock_domain: u32,
271+
}
272+
273+
type GiccAffinityFlags = LocalApicAffinityFlags;
274+
275+
#[repr(C, packed)]
276+
struct GicItsAffinityEntry {
277+
header: AffinityEntryHeader,
278+
proximity_domain: u32,
279+
_reserved: u16,
280+
its_id: u32,
281+
}
282+
283+
#[repr(C, packed)]
284+
struct GenericInitiatorAffinityEntry {
285+
header: AffinityEntryHeader,
286+
_reserved_1: u8,
287+
device_handle_type: DeviceHandleType,
288+
proximity_domain: u32,
289+
device_handle: DeviceHandle,
290+
flags: GenericInitiatorAffinityFlags,
291+
_reserved_2: u32,
292+
}
293+
294+
#[repr(u8)]
295+
#[non_exhaustive]
296+
enum DeviceHandleType {
297+
Acpi = 0,
298+
Pci = 1,
299+
}
300+
301+
#[repr(C)]
302+
enum DeviceHandle {
303+
Acpi(AcpiDeviceHandle),
304+
Pci(PciDeviceHandle),
305+
}
306+
307+
#[repr(C, packed)]
308+
struct AcpiDeviceHandle {
309+
acpi_hid: u64,
310+
acpi_uid: u32,
311+
_reserved: u32,
312+
}
313+
314+
#[repr(C, packed)]
315+
struct PciDeviceHandle {
316+
pci_segment: u16,
317+
pci_bdf_number: u16,
318+
_reserved: [u32; 3],
319+
}
320+
321+
bitflags! {
322+
#[derive(Clone, Copy)]
323+
struct GenericInitiatorAffinityFlags: u32 {
324+
const ENABLED = 1;
325+
const ARCHITECTURAL_TRANSACTIONS = 1 << 1;
326+
}
327+
}

0 commit comments

Comments
 (0)
Please sign in to comment.