1use super::sdt::Sdt;
6use zerocopy::FromBytes;
7
8pub const MADT_SIGNATURE: &[u8; 4] = b"APIC";
9
10#[derive(Clone, Copy, Debug, FromBytes)]
12#[repr(C, packed)]
13pub struct MadtAcpiTable {
14 pub header: Sdt,
15 pub local_apic_phys_addr: u32,
16 pub flags: u32,
17}
18
19impl MadtAcpiTable {
20 pub fn get() -> Option<&'static MadtAcpiTable> {
22 unsafe { super::find_table(MADT_SIGNATURE).map(|ptr| &*(ptr as *const MadtAcpiTable)) }
23 }
24}
25
26#[derive(Clone, Copy, Debug, FromBytes)]
28#[repr(C, packed)]
29struct EntryRecord {
30 typ: u8,
31 size: u8,
32}
33
34#[derive(Copy, Clone, Debug, FromBytes)]
36#[repr(C, packed)]
37pub struct MadtLocalApic {
38 _header: EntryRecord,
39 pub processor: u8,
40 pub apic_id: u8,
41 pub flags: u32,
42}
43
44#[derive(Copy, Clone, Debug, FromBytes)]
46#[repr(C, packed)]
47pub struct MadtIoApic {
48 _header: EntryRecord,
49 pub id: u8,
50 _reserved: u8,
51 pub address: u32,
52 pub gsi_base: u32,
53}
54
55#[derive(Copy, Clone, Debug, FromBytes)]
57#[repr(C, packed)]
58pub struct MadtIntSrcOverride {
59 _header: EntryRecord,
60 pub bus_source: u8,
61 pub irq_source: u8,
62 pub gsi: u32,
63 pub flags: u16,
64}
65
66#[derive(Copy, Clone, Debug, FromBytes)]
69#[repr(C, packed)]
70pub struct MadtLocalApicOverride {
71 _header: EntryRecord,
72 pub _reserved: u16,
73 pub phys_addr: u64,
74 pub flags: u32,
75}
76
77pub struct MadtInfo {
78 pub local_apic_address: u64,
79 pub flags: u32,
80 pub local_apics: [Option<LocalApicEntry>; 32],
81 pub local_apic_count: usize,
82 pub io_apics: [Option<IoApicEntry>; 4],
83 pub io_apic_count: usize,
84 pub overrides: [Option<InterruptSourceOverride>; 16],
85 pub override_count: usize,
86}
87
88#[derive(Clone, Copy, Debug)]
89pub struct LocalApicEntry {
90 pub processor: u8,
91 pub apic_id: u8,
92 pub flags: u32,
93}
94
95#[derive(Clone, Copy, Debug)]
96pub struct IoApicEntry {
97 pub id: u8,
98 pub address: u32,
99 pub gsi_base: u32,
100}
101
102#[derive(Clone, Copy, Debug)]
103pub struct InterruptSourceOverride {
104 pub bus_source: u8,
105 pub irq_source: u8,
106 pub gsi: u32,
107 pub flags: u16,
108}
109
110impl InterruptSourceOverride {
111 pub fn polarity(&self) -> u8 {
114 (self.flags & 0x03) as u8
115 }
116
117 pub fn trigger_mode(&self) -> u8 {
120 ((self.flags >> 2) & 0x03) as u8
121 }
122}
123
124impl MadtInfo {
125 pub fn irq_to_gsi(&self, irq: u8) -> (u32, u8, u8) {
130 for i in 0..self.override_count {
131 if let Some(ref ovr) = self.overrides[i] {
132 if ovr.irq_source == irq {
133 return (ovr.gsi, ovr.polarity(), ovr.trigger_mode());
134 }
135 }
136 }
137 (irq as u32, 0, 0)
139 }
140}
141
142pub fn parse_madt() -> Option<MadtInfo> {
144 let madt_ptr = super::find_table(MADT_SIGNATURE)? as *const MadtAcpiTable;
145 let madt = unsafe { &*madt_ptr };
146 if madt.header.length < core::mem::size_of::<MadtAcpiTable>() as u32 {
147 log::error!("ACPI: MADT length smaller than header");
148 return None;
149 }
150 let madt_len = madt.header.length as usize;
151 let mut sum: u8 = 0;
152 for i in 0..madt_len {
153 sum = sum.wrapping_add(unsafe { *((madt_ptr as *const u8).add(i)) });
154 }
155 if sum != 0 {
156 log::error!("ACPI: MADT checksum failed");
157 return None;
158 }
159
160 let mut info = MadtInfo {
161 local_apic_address: madt.local_apic_phys_addr as u64,
162 flags: madt.flags,
163 local_apics: [None; 32],
164 local_apic_count: 0,
165 io_apics: [None; 4],
166 io_apic_count: 0,
167 overrides: [None; 16],
168 override_count: 0,
169 };
170
171 let total_length = madt_len;
172 let entries_start = madt_ptr as usize + core::mem::size_of::<MadtAcpiTable>();
173 let entries_end = madt_ptr as usize + total_length;
174 let mut offset = entries_start;
175
176 while offset + 2 <= entries_end {
177 let record = unsafe { &*(offset as *const EntryRecord) };
178 let entry_type = record.typ;
179 let entry_size = record.size as usize;
180
181 if entry_size < 2 || offset + entry_size > entries_end {
182 break;
183 }
184
185 match entry_type {
186 0 => {
187 if info.local_apic_count < info.local_apics.len() {
188 let entry = unsafe { &*(offset as *const MadtLocalApic) };
189 if entry.flags & 1 == 0 {
193 log::info!(
194 "MADT: skipping disabled Local APIC id={} processor={}",
195 entry.apic_id,
196 entry.processor,
197 );
198 } else {
199 info.local_apics[info.local_apic_count] = Some(LocalApicEntry {
200 processor: entry.processor,
201 apic_id: entry.apic_id,
202 flags: entry.flags,
203 });
204 info.local_apic_count += 1;
205 }
206 }
207 }
208 1 => {
209 if info.io_apic_count < info.io_apics.len() {
210 let entry = unsafe { &*(offset as *const MadtIoApic) };
211 info.io_apics[info.io_apic_count] = Some(IoApicEntry {
212 id: entry.id,
213 address: entry.address,
214 gsi_base: entry.gsi_base,
215 });
216 info.io_apic_count += 1;
217 }
218 }
219 2 => {
220 if info.override_count < info.overrides.len() {
221 let entry = unsafe { &*(offset as *const MadtIntSrcOverride) };
222 info.overrides[info.override_count] = Some(InterruptSourceOverride {
223 bus_source: entry.bus_source,
224 irq_source: entry.irq_source,
225 gsi: entry.gsi,
226 flags: entry.flags,
227 });
228 info.override_count += 1;
229 }
230 }
231 5 => {
232 let entry = unsafe { &*(offset as *const MadtLocalApicOverride) };
233 let lapic_override_addr =
234 unsafe { core::ptr::read_unaligned(core::ptr::addr_of!((*entry).phys_addr)) };
235 if entry.flags & 1 != 0 {
236 log::info!(
237 "MADT: Local APIC address override 0x{:X}",
238 lapic_override_addr
239 );
240 info.local_apic_address = lapic_override_addr;
241 }
242 }
243 _ => {}
244 }
245 offset += entry_size;
246 }
247
248 Some(info)
249}