Skip to main content

strat9_kernel/acpi/
madt.rs

1//! Support for the MADT ACPI table,
2//! which includes interrupt and multicore info.
3//! Inspired by Theseus OS.
4
5use super::sdt::Sdt;
6use zerocopy::FromBytes;
7
8pub const MADT_SIGNATURE: &[u8; 4] = b"APIC";
9
10/// The fixed-size components of the MADT ACPI table.
11#[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    /// Finds the MADT and returns a reference to it.
21    pub fn get() -> Option<&'static MadtAcpiTable> {
22        unsafe { super::find_table(MADT_SIGNATURE).map(|ptr| &*(ptr as *const MadtAcpiTable)) }
23    }
24}
25
26/// A MADT entry record, which precedes each actual MADT entry.
27#[derive(Clone, Copy, Debug, FromBytes)]
28#[repr(C, packed)]
29struct EntryRecord {
30    typ: u8,
31    size: u8,
32}
33
34/// MADT Local APIC entry (Type 0)
35#[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/// MADT I/O APIC entry (Type 1)
45#[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/// MADT Interrupt Source Override (Type 2)
56#[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/// MADT Local APIC Address Override (Type 5)
67/// Replaces the 32-bit address in the MADT header with a 64-bit address.
68#[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    /// Get the polarity from the flags field.
112    /// 0b00 = conforms to bus, 0b01 = active high, 0b11 = active low
113    pub fn polarity(&self) -> u8 {
114        (self.flags & 0x03) as u8
115    }
116
117    /// Get the trigger mode from the flags field.
118    /// 0b00 = conforms to bus, 0b01 = edge, 0b11 = level
119    pub fn trigger_mode(&self) -> u8 {
120        ((self.flags >> 2) & 0x03) as u8
121    }
122}
123
124impl MadtInfo {
125    /// Look up an IRQ's GSI and polarity/trigger, applying source overrides.
126    ///
127    /// Returns (gsi, polarity, trigger_mode) for the given legacy IRQ.
128    /// If no override exists, returns (irq as gsi, 0 = conform, 0 = conform).
129    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        // No override: GSI == IRQ, conform to bus defaults
138        (irq as u32, 0, 0)
139    }
140}
141
142/// Parses madt.
143pub 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                    // MADT flags bit 0: 1 = enabled, 0 = disabled (usable).
190                    // Skip disabled APICs to avoid sending INIT+SIPI to a
191                    // processor that will never respond (hangs on real hardware).
192                    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}