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 Unknown,
24
25 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 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 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); let mut boot_processor = None;
112
113 for entry in madt.entries() {
114 match entry {
115 MadtEntry::LocalApic(entry) => {
116 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 pub address: u32,
261 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#[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#[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#[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 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}