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
66pub struct MadtInfo {
67    pub local_apic_address: u32,
68    pub flags: u32,
69    pub local_apics: [Option<LocalApicEntry>; 32],
70    pub local_apic_count: usize,
71    pub io_apics: [Option<IoApicEntry>; 4],
72    pub io_apic_count: usize,
73    pub overrides: [Option<InterruptSourceOverride>; 16],
74    pub override_count: usize,
75}
76
77#[derive(Clone, Copy, Debug)]
78pub struct LocalApicEntry {
79    pub processor: u8,
80    pub apic_id: u8,
81    pub flags: u32,
82}
83
84#[derive(Clone, Copy, Debug)]
85pub struct IoApicEntry {
86    pub id: u8,
87    pub address: u32,
88    pub gsi_base: u32,
89}
90
91#[derive(Clone, Copy, Debug)]
92pub struct InterruptSourceOverride {
93    pub bus_source: u8,
94    pub irq_source: u8,
95    pub gsi: u32,
96    pub flags: u16,
97}
98
99impl InterruptSourceOverride {
100    /// Get the polarity from the flags field.
101    /// 0b00 = conforms to bus, 0b01 = active high, 0b11 = active low
102    pub fn polarity(&self) -> u8 {
103        (self.flags & 0x03) as u8
104    }
105
106    /// Get the trigger mode from the flags field.
107    /// 0b00 = conforms to bus, 0b01 = edge, 0b11 = level
108    pub fn trigger_mode(&self) -> u8 {
109        ((self.flags >> 2) & 0x03) as u8
110    }
111}
112
113impl MadtInfo {
114    /// Look up an IRQ's GSI and polarity/trigger, applying source overrides.
115    ///
116    /// Returns (gsi, polarity, trigger_mode) for the given legacy IRQ.
117    /// If no override exists, returns (irq as gsi, 0 = conform, 0 = conform).
118    pub fn irq_to_gsi(&self, irq: u8) -> (u32, u8, u8) {
119        for i in 0..self.override_count {
120            if let Some(ref ovr) = self.overrides[i] {
121                if ovr.irq_source == irq {
122                    return (ovr.gsi, ovr.polarity(), ovr.trigger_mode());
123                }
124            }
125        }
126        // No override: GSI == IRQ, conform to bus defaults
127        (irq as u32, 0, 0)
128    }
129}
130
131/// Parses madt.
132pub fn parse_madt() -> Option<MadtInfo> {
133    let madt_ptr = super::find_table(MADT_SIGNATURE)? as *const MadtAcpiTable;
134    let madt = unsafe { &*madt_ptr };
135    if madt.header.length < core::mem::size_of::<MadtAcpiTable>() as u32 {
136        log::error!("ACPI: MADT length smaller than header");
137        return None;
138    }
139    let madt_len = madt.header.length as usize;
140    let mut sum: u8 = 0;
141    for i in 0..madt_len {
142        sum = sum.wrapping_add(unsafe { *((madt_ptr as *const u8).add(i)) });
143    }
144    if sum != 0 {
145        log::error!("ACPI: MADT checksum failed");
146        return None;
147    }
148
149    let mut info = MadtInfo {
150        local_apic_address: madt.local_apic_phys_addr,
151        flags: madt.flags,
152        local_apics: [None; 32],
153        local_apic_count: 0,
154        io_apics: [None; 4],
155        io_apic_count: 0,
156        overrides: [None; 16],
157        override_count: 0,
158    };
159
160    let total_length = madt_len;
161    let entries_start = madt_ptr as usize + core::mem::size_of::<MadtAcpiTable>();
162    let entries_end = madt_ptr as usize + total_length;
163    let mut offset = entries_start;
164
165    while offset + 2 <= entries_end {
166        let record = unsafe { &*(offset as *const EntryRecord) };
167        let entry_type = record.typ;
168        let entry_size = record.size as usize;
169
170        if entry_size < 2 || offset + entry_size > entries_end {
171            break;
172        }
173
174        match entry_type {
175            0 => {
176                if info.local_apic_count < info.local_apics.len() {
177                    let entry = unsafe { &*(offset as *const MadtLocalApic) };
178                    info.local_apics[info.local_apic_count] = Some(LocalApicEntry {
179                        processor: entry.processor,
180                        apic_id: entry.apic_id,
181                        flags: entry.flags,
182                    });
183                    info.local_apic_count += 1;
184                }
185            }
186            1 => {
187                if info.io_apic_count < info.io_apics.len() {
188                    let entry = unsafe { &*(offset as *const MadtIoApic) };
189                    info.io_apics[info.io_apic_count] = Some(IoApicEntry {
190                        id: entry.id,
191                        address: entry.address,
192                        gsi_base: entry.gsi_base,
193                    });
194                    info.io_apic_count += 1;
195                }
196            }
197            2 => {
198                if info.override_count < info.overrides.len() {
199                    let entry = unsafe { &*(offset as *const MadtIntSrcOverride) };
200                    info.overrides[info.override_count] = Some(InterruptSourceOverride {
201                        bus_source: entry.bus_source,
202                        irq_source: entry.irq_source,
203                        gsi: entry.gsi,
204                        flags: entry.flags,
205                    });
206                    info.override_count += 1;
207                }
208            }
209            _ => {}
210        }
211        offset += entry_size;
212    }
213
214    Some(info)
215}