acpi/platform/
interrupt.rs

1use super::{Processor, ProcessorInfo, ProcessorState};
2use crate::{
3    AcpiError,
4    AcpiTables,
5    Handler,
6    MadtError,
7    sdt::{
8        Signature,
9        madt::{Madt, MadtEntry, parse_mps_inti_flags},
10    },
11};
12use alloc::{alloc::Global, vec::Vec};
13use bit_field::BitField;
14use core::{alloc::Allocator, pin::Pin};
15
16pub use crate::sdt::madt::{Polarity, TriggerMode};
17
18#[derive(Debug, Clone)]
19#[non_exhaustive]
20pub enum InterruptModel<A: Allocator = Global> {
21    /// This model is only chosen when the MADT does not describe another interrupt model. On `x86_64` platforms,
22    /// this probably means only the legacy i8259 PIC is present.
23    Unknown,
24
25    /// Describes an interrupt controller based around the Advanced Programmable Interrupt Controller (any of APIC,
26    /// XAPIC, or X2APIC). These are likely to be found on x86 and x86_64 systems and are made up of a Local APIC
27    /// for each core and one or more I/O APICs to handle external interrupts.
28    Apic(Apic<A>),
29}
30
31impl InterruptModel<Global> {
32    pub fn new<H: Handler>(
33        tables: &AcpiTables<H>,
34    ) -> Result<(InterruptModel<Global>, Option<ProcessorInfo<Global>>), AcpiError> {
35        Self::new_in(tables, Global)
36    }
37}
38
39impl<A: Allocator + Clone> InterruptModel<A> {
40    pub fn new_in<H: Handler>(
41        tables: &AcpiTables<H>,
42        allocator: A,
43    ) -> Result<(InterruptModel<A>, Option<ProcessorInfo<A>>), AcpiError> {
44        let Some(madt) = tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };
45
46        /*
47         * We first do a pass through the MADT to determine which interrupt model is being used.
48         */
49        for entry in madt.get().entries() {
50            match entry {
51                MadtEntry::LocalApic(_)
52                | MadtEntry::LocalX2Apic(_)
53                | MadtEntry::IoApic(_)
54                | MadtEntry::InterruptSourceOverride(_)
55                | MadtEntry::LocalApicNmi(_)
56                | MadtEntry::X2ApicNmi(_)
57                | MadtEntry::LocalApicAddressOverride(_) => {
58                    return Self::from_apic_model_in(madt.get(), allocator);
59                }
60
61                MadtEntry::IoSapic(_) | MadtEntry::LocalSapic(_) | MadtEntry::PlatformInterruptSource(_) => {
62                    unimplemented!();
63                }
64
65                MadtEntry::Gicc(_)
66                | MadtEntry::Gicd(_)
67                | MadtEntry::GicMsiFrame(_)
68                | MadtEntry::GicRedistributor(_)
69                | MadtEntry::GicInterruptTranslationService(_) => {
70                    unimplemented!();
71                }
72
73                MadtEntry::NmiSource(_) => (),
74                MadtEntry::MultiprocessorWakeup(_) => (),
75            }
76        }
77
78        Ok((InterruptModel::Unknown, None))
79    }
80
81    fn from_apic_model_in(
82        madt: Pin<&Madt>,
83        allocator: A,
84    ) -> Result<(InterruptModel<A>, Option<ProcessorInfo<A>>), AcpiError> {
85        let mut local_apic_address = madt.local_apic_address as u64;
86        let mut io_apic_count = 0;
87        let mut iso_count = 0;
88        let mut nmi_source_count = 0;
89        let mut local_nmi_line_count = 0;
90        let mut processor_count = 0usize;
91
92        // Do a pass over the entries so we know how much space we should reserve in the vectors
93        for entry in madt.entries() {
94            match entry {
95                MadtEntry::IoApic(_) => io_apic_count += 1,
96                MadtEntry::InterruptSourceOverride(_) => iso_count += 1,
97                MadtEntry::NmiSource(_) => nmi_source_count += 1,
98                MadtEntry::LocalApicNmi(_) => local_nmi_line_count += 1,
99                MadtEntry::X2ApicNmi(_) => local_nmi_line_count += 1,
100                MadtEntry::LocalApic(_) => processor_count += 1,
101                MadtEntry::LocalX2Apic(_) => processor_count += 1,
102                _ => (),
103            }
104        }
105
106        let mut io_apics = Vec::with_capacity_in(io_apic_count, allocator.clone());
107        let mut interrupt_source_overrides = Vec::with_capacity_in(iso_count, allocator.clone());
108        let mut nmi_sources = Vec::with_capacity_in(nmi_source_count, allocator.clone());
109        let mut local_apic_nmi_lines = Vec::with_capacity_in(local_nmi_line_count, allocator.clone());
110        let mut application_processors = Vec::with_capacity_in(processor_count.saturating_sub(1), allocator); // Subtract one for the BSP
111        let mut boot_processor = None;
112
113        for entry in madt.entries() {
114            match entry {
115                MadtEntry::LocalApic(entry) => {
116                    /*
117                     * The first processor is the BSP. Subsequent ones are APs. If we haven't found
118                     * the BSP yet, this must be it.
119                     */
120                    let is_ap = boot_processor.is_some();
121                    let is_disabled = !{ entry.flags }.get_bit(0);
122
123                    let state = match (is_ap, is_disabled) {
124                        (_, true) => ProcessorState::Disabled,
125                        (true, false) => ProcessorState::WaitingForSipi,
126                        (false, false) => ProcessorState::Running,
127                    };
128
129                    let processor = Processor {
130                        processor_uid: entry.processor_id as u32,
131                        local_apic_id: entry.apic_id as u32,
132                        state,
133                        is_ap,
134                    };
135
136                    if is_ap {
137                        application_processors.push(processor);
138                    } else {
139                        boot_processor = Some(processor);
140                    }
141                }
142
143                MadtEntry::LocalX2Apic(entry) => {
144                    let is_ap = boot_processor.is_some();
145                    let is_disabled = !{ entry.flags }.get_bit(0);
146
147                    let state = match (is_ap, is_disabled) {
148                        (_, true) => ProcessorState::Disabled,
149                        (true, false) => ProcessorState::WaitingForSipi,
150                        (false, false) => ProcessorState::Running,
151                    };
152
153                    let processor = Processor {
154                        processor_uid: entry.processor_uid,
155                        local_apic_id: entry.x2apic_id,
156                        state,
157                        is_ap,
158                    };
159
160                    if is_ap {
161                        application_processors.push(processor);
162                    } else {
163                        boot_processor = Some(processor);
164                    }
165                }
166
167                MadtEntry::IoApic(entry) => {
168                    io_apics.push(IoApic {
169                        id: entry.io_apic_id,
170                        address: entry.io_apic_address,
171                        global_system_interrupt_base: entry.global_system_interrupt_base,
172                    });
173                }
174
175                MadtEntry::InterruptSourceOverride(entry) => {
176                    if entry.bus != 0 {
177                        return Err(AcpiError::InvalidMadt(MadtError::InterruptOverrideEntryHasInvalidBus));
178                    }
179
180                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
181
182                    interrupt_source_overrides.push(InterruptSourceOverride {
183                        isa_source: entry.irq,
184                        global_system_interrupt: entry.global_system_interrupt,
185                        polarity,
186                        trigger_mode,
187                    });
188                }
189
190                MadtEntry::NmiSource(entry) => {
191                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
192
193                    nmi_sources.push(NmiSource {
194                        global_system_interrupt: entry.global_system_interrupt,
195                        polarity,
196                        trigger_mode,
197                    });
198                }
199
200                MadtEntry::LocalApicNmi(entry) => {
201                    local_apic_nmi_lines.push(NmiLine {
202                        processor: if entry.processor_id == 0xff {
203                            NmiProcessor::All
204                        } else {
205                            NmiProcessor::ProcessorUid(entry.processor_id as u32)
206                        },
207                        line: match entry.nmi_line {
208                            0 => LocalInterruptLine::Lint0,
209                            1 => LocalInterruptLine::Lint1,
210                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
211                        },
212                    });
213                }
214
215                MadtEntry::X2ApicNmi(entry) => {
216                    local_apic_nmi_lines.push(NmiLine {
217                        processor: if entry.processor_uid == 0xffffffff {
218                            NmiProcessor::All
219                        } else {
220                            NmiProcessor::ProcessorUid(entry.processor_uid)
221                        },
222                        line: match entry.nmi_line {
223                            0 => LocalInterruptLine::Lint0,
224                            1 => LocalInterruptLine::Lint1,
225                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
226                        },
227                    });
228                }
229
230                MadtEntry::LocalApicAddressOverride(entry) => {
231                    local_apic_address = entry.local_apic_address;
232                }
233
234                MadtEntry::MultiprocessorWakeup(_) => {}
235
236                _ => {
237                    return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry));
238                }
239            }
240        }
241
242        Ok((
243            InterruptModel::Apic(Apic::new(
244                local_apic_address,
245                io_apics,
246                local_apic_nmi_lines,
247                interrupt_source_overrides,
248                nmi_sources,
249                madt.supports_8259(),
250            )),
251            Some(ProcessorInfo::new_in(boot_processor.unwrap(), application_processors)),
252        ))
253    }
254}
255
256#[derive(Debug, Clone, Copy)]
257pub struct IoApic {
258    pub id: u8,
259    /// The physical address at which to access this I/O APIC.
260    pub address: u32,
261    /// The global system interrupt number where this I/O APIC's inputs start.
262    pub global_system_interrupt_base: u32,
263}
264
265#[derive(Debug, Clone, Copy)]
266pub struct NmiLine {
267    pub processor: NmiProcessor,
268    pub line: LocalInterruptLine,
269}
270
271/// Indicates which local interrupt line will be utilized by an external interrupt. Specifically,
272/// these lines directly correspond to their requisite LVT entries in a processor's APIC.
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub enum LocalInterruptLine {
275    Lint0,
276    Lint1,
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub enum NmiProcessor {
281    All,
282    ProcessorUid(u32),
283}
284
285/// Describes a difference in the mapping of an ISA interrupt to how it's mapped in other interrupt
286/// models. For example, if a device is connected to ISA IRQ 0 and IOAPIC input 2, an override will
287/// appear mapping source 0 to GSI 2. Currently these will only be created for ISA interrupt
288/// sources.
289#[derive(Debug, Clone, Copy)]
290pub struct InterruptSourceOverride {
291    pub isa_source: u8,
292    pub global_system_interrupt: u32,
293    pub polarity: Polarity,
294    pub trigger_mode: TriggerMode,
295}
296
297/// Describes a Global System Interrupt that should be enabled as non-maskable. Any source that is
298/// non-maskable can not be used by devices.
299#[derive(Debug, Clone, Copy)]
300pub struct NmiSource {
301    pub global_system_interrupt: u32,
302    pub polarity: Polarity,
303    pub trigger_mode: TriggerMode,
304}
305
306#[derive(Debug, Clone)]
307pub struct Apic<A: Allocator = Global> {
308    pub local_apic_address: u64,
309    pub io_apics: Vec<IoApic, A>,
310    pub local_apic_nmi_lines: Vec<NmiLine, A>,
311    pub interrupt_source_overrides: Vec<InterruptSourceOverride, A>,
312    pub nmi_sources: Vec<NmiSource, A>,
313
314    /// If this field is set, you must remap and mask all the lines of the legacy PIC, even if
315    /// you choose to use the APIC. It's recommended that you do this even if ACPI does not
316    /// require you to.
317    pub also_has_legacy_pics: bool,
318}
319
320impl<A: Allocator> Apic<A> {
321    pub(crate) fn new(
322        local_apic_address: u64,
323        io_apics: Vec<IoApic, A>,
324        local_apic_nmi_lines: Vec<NmiLine, A>,
325        interrupt_source_overrides: Vec<InterruptSourceOverride, A>,
326        nmi_sources: Vec<NmiSource, A>,
327        also_has_legacy_pics: bool,
328    ) -> Self {
329        Self {
330            local_apic_address,
331            io_apics,
332            local_apic_nmi_lines,
333            interrupt_source_overrides,
334            nmi_sources,
335            also_has_legacy_pics,
336        }
337    }
338}