1use crate::binary_reader::{BinaryReader, DowncastFrom};
8use crate::structures::{
9 ACPI_SRAT_FLAG_ENABLED, ACPI_SRAT_TYPE_MEMORY_AFFINITY, ACPI_SRAT_TYPE_PROCESSOR_AFFINITY,
10 ACPI_SRAT_TYPE_PROCESSOR_X2APIC_AFFINITY, AcpiSratMemoryAffinityEntry,
11 AcpiSratProcessorAffinityEntry, AcpiSratProcessorX2ApicAffinityEntry, AcpiSratTable,
12 AcpiSubTableHeader,
13};
14use crate::{AcpiParserInterface, get_table_by_type};
15use zx_status::Status;
16
17pub const K_ACPI_MAX_NUMA_REGIONS: usize = 5;
18
19#[repr(C)]
20#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
21pub struct AcpiNumaRegion {
23 pub base_address: u64,
24 pub length: u64,
25}
26
27#[repr(C)]
28#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
29pub struct AcpiNumaDomain {
31 pub domain: u32,
32 pub memory: [AcpiNumaRegion; K_ACPI_MAX_NUMA_REGIONS],
33 pub memory_count: u8,
34}
35
36impl_downcast_from!(AcpiSubTableHeader =>
37 AcpiSratMemoryAffinityEntry,
38 AcpiSratProcessorAffinityEntry,
39 AcpiSratProcessorX2ApicAffinityEntry,
40);
41
42pub fn enumerate_cpu_numa_pairs_from_srat<F>(
44 srat: &AcpiSratTable,
45 mut callback: F,
46) -> Result<(), Status>
47where
48 F: FnMut(&AcpiNumaDomain, u32),
49{
50 const K_MAX_NUMA_DOMAINS: usize = 10;
51 let mut domains = [AcpiNumaDomain::default(); K_MAX_NUMA_DOMAINS];
52 for (i, domain) in domains.iter_mut().enumerate() {
53 domain.domain = i as u32;
54 }
55
56 let mut reader = BinaryReader::from_payload_of_struct(srat);
58 while !reader.is_empty() {
59 let sub_header = reader.read::<AcpiSubTableHeader>().ok_or(Status::INTERNAL)?;
60 if sub_header.r#type != ACPI_SRAT_TYPE_MEMORY_AFFINITY {
61 continue;
62 }
63 let mem = unsafe { AcpiSratMemoryAffinityEntry::downcast_from(sub_header) }
65 .ok_or(Status::INTERNAL)?;
66
67 let flags = mem.flags;
68 if (flags & ACPI_SRAT_FLAG_ENABLED) == 0 {
69 continue;
70 }
71
72 let proximity_domain = mem.proximity_domain as usize;
73 if proximity_domain >= K_MAX_NUMA_DOMAINS {
74 return Err(Status::NOT_SUPPORTED);
75 }
76
77 let domain = &mut domains[proximity_domain];
78 if domain.memory_count as usize >= K_ACPI_MAX_NUMA_REGIONS {
79 return Err(Status::NOT_SUPPORTED);
80 }
81
82 let base_low = mem.base_address_low as u64;
83 let base_high = mem.base_address_high as u64;
84 let length_low = mem.length_low as u64;
85 let length_high = mem.length_high as u64;
86 let base = (base_high << 32) | base_low;
87 let length = (length_high << 32) | length_low;
88
89 domain.memory[domain.memory_count as usize] = AcpiNumaRegion { base_address: base, length };
90 domain.memory_count += 1;
91 }
92
93 reader = BinaryReader::from_payload_of_struct(srat);
95 while !reader.is_empty() {
96 let sub_header = reader.read::<AcpiSubTableHeader>().ok_or(Status::INTERNAL)?;
97 if sub_header.r#type == ACPI_SRAT_TYPE_PROCESSOR_AFFINITY {
98 let cpu = unsafe { AcpiSratProcessorAffinityEntry::downcast_from(sub_header) }
100 .ok_or(Status::INTERNAL)?;
101
102 let flags = cpu.flags;
103 if (flags & ACPI_SRAT_FLAG_ENABLED) == 0 {
104 continue;
105 }
106
107 let domain = cpu.proximity_domain() as usize;
108 if domain >= K_MAX_NUMA_DOMAINS {
109 return Err(Status::INTERNAL);
110 }
111
112 let apic_id = cpu.apic_id as u32;
113 callback(&domains[domain], apic_id);
114 } else if sub_header.r#type == ACPI_SRAT_TYPE_PROCESSOR_X2APIC_AFFINITY {
115 let cpu = unsafe { AcpiSratProcessorX2ApicAffinityEntry::downcast_from(sub_header) }
117 .ok_or(Status::INTERNAL)?;
118
119 let flags = cpu.flags;
120 if (flags & ACPI_SRAT_FLAG_ENABLED) == 0 {
121 continue;
122 }
123
124 let domain = cpu.proximity_domain as usize;
125 if domain >= K_MAX_NUMA_DOMAINS {
126 return Err(Status::INTERNAL);
127 }
128
129 let x2apic_id = cpu.x2apic_id;
130 callback(&domains[domain], x2apic_id);
131 }
132 }
133
134 Ok(())
135}
136
137pub fn enumerate_cpu_numa_pairs<F>(
139 parser: &dyn AcpiParserInterface,
140 callback: F,
141) -> Result<(), Status>
142where
143 F: FnMut(&AcpiNumaDomain, u32),
144{
145 let srat = get_table_by_type::<AcpiSratTable>(parser).ok_or(Status::NOT_FOUND)?;
146 enumerate_cpu_numa_pairs_from_srat(srat, callback)
147}