1use crate::{
2 sdt::{ExtendedField, SdtHeader, Signature},
3 AcpiError,
4 AcpiTable,
5};
6use bit_field::BitField;
7use core::{
8 marker::{PhantomData, PhantomPinned},
9 mem,
10 pin::Pin,
11};
12
13#[cfg(feature = "allocator_api")]
14use crate::{
15 platform::{
16 interrupt::{InterruptModel, Polarity, TriggerMode},
17 ProcessorInfo,
18 },
19 AcpiResult,
20};
21
22#[derive(Debug)]
23pub enum MadtError {
24 UnexpectedEntry,
25 InterruptOverrideEntryHasInvalidBus,
26 InvalidLocalNmiLine,
27 MpsIntiInvalidPolarity,
28 MpsIntiInvalidTriggerMode,
29 WakeupApsTimeout,
30}
31
32#[repr(C, packed)]
46#[derive(Debug)]
47pub struct Madt {
48 pub header: SdtHeader,
49 pub local_apic_address: u32,
50 pub flags: u32,
51 _pinned: PhantomPinned,
52}
53
54unsafe impl AcpiTable for Madt {
56 const SIGNATURE: Signature = Signature::MADT;
57
58 fn header(&self) -> &SdtHeader {
59 &self.header
60 }
61}
62
63impl Madt {
64 pub fn get_mpwk_mailbox_addr(self: Pin<&Self>) -> Result<u64, AcpiError> {
65 for entry in self.entries() {
66 if let MadtEntry::MultiprocessorWakeup(entry) = entry {
67 return Ok(entry.mailbox_address);
68 }
69 }
70 Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))
71 }
72
73 #[cfg(feature = "allocator_api")]
74 pub fn parse_interrupt_model_in<'a, A>(
75 self: Pin<&Self>,
76 allocator: A,
77 ) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
78 where
79 A: core::alloc::Allocator + Clone,
80 {
81 for entry in self.entries() {
85 match entry {
86 MadtEntry::LocalApic(_) |
87 MadtEntry::LocalX2Apic(_) |
88 MadtEntry::IoApic(_) |
89 MadtEntry::InterruptSourceOverride(_) |
90 MadtEntry::NmiSource(_) | MadtEntry::LocalApicNmi(_) |
92 MadtEntry::X2ApicNmi(_) |
93 MadtEntry::LocalApicAddressOverride(_) => {
94 return self.parse_apic_model_in(allocator);
95 }
96
97 MadtEntry::IoSapic(_) |
98 MadtEntry::LocalSapic(_) |
99 MadtEntry::PlatformInterruptSource(_) => {
100 unimplemented!();
101 }
102
103 MadtEntry::Gicc(_) |
104 MadtEntry::Gicd(_) |
105 MadtEntry::GicMsiFrame(_) |
106 MadtEntry::GicRedistributor(_) |
107 MadtEntry::GicInterruptTranslationService(_) => {
108 unimplemented!();
109 }
110
111 MadtEntry::MultiprocessorWakeup(_) => ()
112 }
113 }
114
115 Ok((InterruptModel::Unknown, None))
116 }
117
118 #[cfg(feature = "allocator_api")]
119 fn parse_apic_model_in<'a, A>(
120 self: Pin<&Self>,
121 allocator: A,
122 ) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
123 where
124 A: core::alloc::Allocator + Clone,
125 {
126 use crate::platform::{
127 interrupt::{
128 Apic,
129 InterruptSourceOverride,
130 IoApic,
131 LocalInterruptLine,
132 NmiLine,
133 NmiProcessor,
134 NmiSource,
135 },
136 Processor,
137 ProcessorState,
138 };
139
140 let mut local_apic_address = self.local_apic_address as u64;
141 let mut io_apic_count = 0;
142 let mut iso_count = 0;
143 let mut nmi_source_count = 0;
144 let mut local_nmi_line_count = 0;
145 let mut processor_count = 0usize;
146
147 for entry in self.entries() {
149 match entry {
150 MadtEntry::IoApic(_) => io_apic_count += 1,
151 MadtEntry::InterruptSourceOverride(_) => iso_count += 1,
152 MadtEntry::NmiSource(_) => nmi_source_count += 1,
153 MadtEntry::LocalApicNmi(_) => local_nmi_line_count += 1,
154 MadtEntry::X2ApicNmi(_) => local_nmi_line_count += 1,
155 MadtEntry::LocalApic(_) => processor_count += 1,
156 MadtEntry::LocalX2Apic(_) => processor_count += 1,
157 _ => (),
158 }
159 }
160
161 let mut io_apics = crate::ManagedSlice::new_in(io_apic_count, allocator.clone())?;
162 let mut interrupt_source_overrides = crate::ManagedSlice::new_in(iso_count, allocator.clone())?;
163 let mut nmi_sources = crate::ManagedSlice::new_in(nmi_source_count, allocator.clone())?;
164 let mut local_apic_nmi_lines = crate::ManagedSlice::new_in(local_nmi_line_count, allocator.clone())?;
165 let mut application_processors =
166 crate::ManagedSlice::new_in(processor_count.saturating_sub(1), allocator)?; let mut boot_processor = None;
168
169 io_apic_count = 0;
170 iso_count = 0;
171 nmi_source_count = 0;
172 local_nmi_line_count = 0;
173 processor_count = 0;
174
175 for entry in self.entries() {
176 match entry {
177 MadtEntry::LocalApic(entry) => {
178 let is_ap = boot_processor.is_some();
183 let is_disabled = !{ entry.flags }.get_bit(0);
184
185 let state = match (is_ap, is_disabled) {
186 (_, true) => ProcessorState::Disabled,
187 (true, false) => ProcessorState::WaitingForSipi,
188 (false, false) => ProcessorState::Running,
189 };
190
191 let processor = Processor {
192 processor_uid: entry.processor_id as u32,
193 local_apic_id: entry.apic_id as u32,
194 state,
195 is_ap,
196 };
197
198 if is_ap {
199 application_processors[processor_count] = processor;
200 processor_count += 1;
201 } else {
202 boot_processor = Some(processor);
203 }
204 }
205
206 MadtEntry::LocalX2Apic(entry) => {
207 let is_ap = boot_processor.is_some();
208 let is_disabled = !{ entry.flags }.get_bit(0);
209
210 let state = match (is_ap, is_disabled) {
211 (_, true) => ProcessorState::Disabled,
212 (true, false) => ProcessorState::WaitingForSipi,
213 (false, false) => ProcessorState::Running,
214 };
215
216 let processor = Processor {
217 processor_uid: entry.processor_uid,
218 local_apic_id: entry.x2apic_id,
219 state,
220 is_ap,
221 };
222
223 if is_ap {
224 application_processors[processor_count] = processor;
225 processor_count += 1;
226 } else {
227 boot_processor = Some(processor);
228 }
229 }
230
231 MadtEntry::IoApic(entry) => {
232 io_apics[io_apic_count] = IoApic {
233 id: entry.io_apic_id,
234 address: entry.io_apic_address,
235 global_system_interrupt_base: entry.global_system_interrupt_base,
236 };
237 io_apic_count += 1;
238 }
239
240 MadtEntry::InterruptSourceOverride(entry) => {
241 if entry.bus != 0 {
242 return Err(AcpiError::InvalidMadt(MadtError::InterruptOverrideEntryHasInvalidBus));
243 }
244
245 let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
246
247 interrupt_source_overrides[iso_count] = InterruptSourceOverride {
248 isa_source: entry.irq,
249 global_system_interrupt: entry.global_system_interrupt,
250 polarity,
251 trigger_mode,
252 };
253 iso_count += 1;
254 }
255
256 MadtEntry::NmiSource(entry) => {
257 let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
258
259 nmi_sources[nmi_source_count] = NmiSource {
260 global_system_interrupt: entry.global_system_interrupt,
261 polarity,
262 trigger_mode,
263 };
264 nmi_source_count += 1;
265 }
266
267 MadtEntry::LocalApicNmi(entry) => {
268 local_apic_nmi_lines[local_nmi_line_count] = NmiLine {
269 processor: if entry.processor_id == 0xff {
270 NmiProcessor::All
271 } else {
272 NmiProcessor::ProcessorUid(entry.processor_id as u32)
273 },
274 line: match entry.nmi_line {
275 0 => LocalInterruptLine::Lint0,
276 1 => LocalInterruptLine::Lint1,
277 _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
278 },
279 };
280 local_nmi_line_count += 1;
281 }
282
283 MadtEntry::X2ApicNmi(entry) => {
284 local_apic_nmi_lines[local_nmi_line_count] = NmiLine {
285 processor: if entry.processor_uid == 0xffffffff {
286 NmiProcessor::All
287 } else {
288 NmiProcessor::ProcessorUid(entry.processor_uid)
289 },
290 line: match entry.nmi_line {
291 0 => LocalInterruptLine::Lint0,
292 1 => LocalInterruptLine::Lint1,
293 _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
294 },
295 };
296 local_nmi_line_count += 1;
297 }
298
299 MadtEntry::LocalApicAddressOverride(entry) => {
300 local_apic_address = entry.local_apic_address;
301 }
302
303 MadtEntry::MultiprocessorWakeup(_) => {}
304
305 _ => {
306 return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry));
307 }
308 }
309 }
310
311 Ok((
312 InterruptModel::Apic(Apic::new(
313 local_apic_address,
314 io_apics,
315 local_apic_nmi_lines,
316 interrupt_source_overrides,
317 nmi_sources,
318 self.supports_8259(),
319 )),
320 Some(ProcessorInfo::new(boot_processor.unwrap(), application_processors)),
321 ))
322 }
323
324 pub fn entries(self: Pin<&Self>) -> MadtEntryIter<'_> {
325 let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Madt as *const u8 };
326 MadtEntryIter {
327 pointer: unsafe { ptr.add(mem::size_of::<Madt>()) },
328 remaining_length: self.header.length - mem::size_of::<Madt>() as u32,
329 _phantom: PhantomData,
330 }
331 }
332
333 pub fn supports_8259(&self) -> bool {
334 { self.flags }.get_bit(0)
335 }
336}
337
338#[derive(Debug)]
339pub struct MadtEntryIter<'a> {
340 pointer: *const u8,
341 remaining_length: u32,
346 _phantom: PhantomData<&'a ()>,
347}
348
349#[derive(Debug)]
350pub enum MadtEntry<'a> {
351 LocalApic(&'a LocalApicEntry),
352 IoApic(&'a IoApicEntry),
353 InterruptSourceOverride(&'a InterruptSourceOverrideEntry),
354 NmiSource(&'a NmiSourceEntry),
355 LocalApicNmi(&'a LocalApicNmiEntry),
356 LocalApicAddressOverride(&'a LocalApicAddressOverrideEntry),
357 IoSapic(&'a IoSapicEntry),
358 LocalSapic(&'a LocalSapicEntry),
359 PlatformInterruptSource(&'a PlatformInterruptSourceEntry),
360 LocalX2Apic(&'a LocalX2ApicEntry),
361 X2ApicNmi(&'a X2ApicNmiEntry),
362 Gicc(&'a GiccEntry),
363 Gicd(&'a GicdEntry),
364 GicMsiFrame(&'a GicMsiFrameEntry),
365 GicRedistributor(&'a GicRedistributorEntry),
366 GicInterruptTranslationService(&'a GicInterruptTranslationServiceEntry),
367 MultiprocessorWakeup(&'a MultiprocessorWakeupEntry),
368}
369
370impl<'a> Iterator for MadtEntryIter<'a> {
371 type Item = MadtEntry<'a>;
372
373 fn next(&mut self) -> Option<Self::Item> {
374 while self.remaining_length > 0 {
375 let entry_pointer = self.pointer;
376 let header = unsafe { *(self.pointer as *const EntryHeader) };
377
378 self.pointer = unsafe { self.pointer.offset(header.length as isize) };
379 self.remaining_length -= header.length as u32;
380
381 macro_rules! construct_entry {
382 ($entry_type:expr,
383 $entry_pointer:expr,
384 $(($value:expr => $variant:path as $type:ty)),*
385 ) => {
386 match $entry_type {
387 $(
388 $value => {
389 return Some($variant(unsafe {
390 &*($entry_pointer as *const $type)
391 }))
392 }
393 )*
394
395 0x11..=0x7f => {}
400
401 0x80..=0xff => {}
406 }
407 }
408 }
409
410 #[rustfmt::skip]
411 construct_entry!(
412 header.entry_type,
413 entry_pointer,
414 (0x0 => MadtEntry::LocalApic as LocalApicEntry),
415 (0x1 => MadtEntry::IoApic as IoApicEntry),
416 (0x2 => MadtEntry::InterruptSourceOverride as InterruptSourceOverrideEntry),
417 (0x3 => MadtEntry::NmiSource as NmiSourceEntry),
418 (0x4 => MadtEntry::LocalApicNmi as LocalApicNmiEntry),
419 (0x5 => MadtEntry::LocalApicAddressOverride as LocalApicAddressOverrideEntry),
420 (0x6 => MadtEntry::IoSapic as IoSapicEntry),
421 (0x7 => MadtEntry::LocalSapic as LocalSapicEntry),
422 (0x8 => MadtEntry::PlatformInterruptSource as PlatformInterruptSourceEntry),
423 (0x9 => MadtEntry::LocalX2Apic as LocalX2ApicEntry),
424 (0xa => MadtEntry::X2ApicNmi as X2ApicNmiEntry),
425 (0xb => MadtEntry::Gicc as GiccEntry),
426 (0xc => MadtEntry::Gicd as GicdEntry),
427 (0xd => MadtEntry::GicMsiFrame as GicMsiFrameEntry),
428 (0xe => MadtEntry::GicRedistributor as GicRedistributorEntry),
429 (0xf => MadtEntry::GicInterruptTranslationService as GicInterruptTranslationServiceEntry),
430 (0x10 => MadtEntry::MultiprocessorWakeup as MultiprocessorWakeupEntry)
431 );
432 }
433
434 None
435 }
436}
437
438#[derive(Clone, Copy, Debug)]
439#[repr(C, packed)]
440pub struct EntryHeader {
441 pub entry_type: u8,
442 pub length: u8,
443}
444
445#[derive(Clone, Copy, Debug)]
446#[repr(C, packed)]
447pub struct LocalApicEntry {
448 pub header: EntryHeader,
449 pub processor_id: u8,
450 pub apic_id: u8,
451 pub flags: u32,
452}
453
454#[derive(Clone, Copy, Debug)]
455#[repr(C, packed)]
456pub struct IoApicEntry {
457 pub header: EntryHeader,
458 pub io_apic_id: u8,
459 _reserved: u8,
460 pub io_apic_address: u32,
461 pub global_system_interrupt_base: u32,
462}
463
464#[derive(Clone, Copy, Debug)]
465#[repr(C, packed)]
466pub struct InterruptSourceOverrideEntry {
467 pub header: EntryHeader,
468 pub bus: u8, pub irq: u8, pub global_system_interrupt: u32,
471 pub flags: u16,
472}
473
474#[derive(Clone, Copy, Debug)]
475#[repr(C, packed)]
476pub struct NmiSourceEntry {
477 pub header: EntryHeader,
478 pub flags: u16,
479 pub global_system_interrupt: u32,
480}
481
482#[derive(Clone, Copy, Debug)]
483#[repr(C, packed)]
484pub struct LocalApicNmiEntry {
485 pub header: EntryHeader,
486 pub processor_id: u8,
487 pub flags: u16,
488 pub nmi_line: u8, }
490
491#[derive(Clone, Copy, Debug)]
492#[repr(C, packed)]
493pub struct LocalApicAddressOverrideEntry {
494 pub header: EntryHeader,
495 _reserved: u16,
496 pub local_apic_address: u64,
497}
498
499#[derive(Clone, Copy, Debug)]
502#[repr(C, packed)]
503pub struct IoSapicEntry {
504 pub header: EntryHeader,
505 pub io_apic_id: u8,
506 _reserved: u8,
507 pub global_system_interrupt_base: u32,
508 pub io_sapic_address: u64,
509}
510
511#[derive(Clone, Copy, Debug)]
512#[repr(C, packed)]
513pub struct LocalSapicEntry {
514 pub header: EntryHeader,
515 pub processor_id: u8,
516 pub local_sapic_id: u8,
517 pub local_sapic_eid: u8,
518 _reserved: [u8; 3],
519 pub flags: u32,
520 pub processor_uid: u32,
521
522 processor_uid_string: u8,
527}
528
529#[derive(Clone, Copy, Debug)]
530#[repr(C, packed)]
531pub struct PlatformInterruptSourceEntry {
532 pub header: EntryHeader,
533 pub flags: u16,
534 pub interrupt_type: u8,
535 pub processor_id: u8,
536 pub processor_eid: u8,
537 pub io_sapic_vector: u8,
538 pub global_system_interrupt: u32,
539 pub platform_interrupt_source_flags: u32,
540}
541
542#[derive(Clone, Copy, Debug)]
543#[repr(C, packed)]
544pub struct LocalX2ApicEntry {
545 pub header: EntryHeader,
546 _reserved: u16,
547 pub x2apic_id: u32,
548 pub flags: u32,
549 pub processor_uid: u32,
550}
551
552#[derive(Clone, Copy, Debug)]
553#[repr(C, packed)]
554pub struct X2ApicNmiEntry {
555 pub header: EntryHeader,
556 pub flags: u16,
557 pub processor_uid: u32,
558 pub nmi_line: u8,
559 _reserved: [u8; 3],
560}
561
562#[derive(Clone, Copy, Debug)]
566#[repr(C, packed)]
567pub struct GiccEntry {
568 pub header: EntryHeader,
569 _reserved1: u16,
570 pub cpu_interface_number: u32,
571 pub processor_uid: u32,
572 pub flags: u32,
573 pub parking_protocol_version: u32,
574 pub performance_interrupt_gsiv: u32,
575 pub parked_address: u64,
576 pub gic_registers_address: u64,
577 pub gic_virtual_registers_address: u64,
578 pub gic_hypervisor_registers_address: u64,
579 pub vgic_maintenance_interrupt: u32,
580 pub gicr_base_address: u64,
581 pub mpidr: u64,
582 pub processor_power_efficiency_class: u8,
583 _reserved2: u8,
584 pub spe_overflow_interrupt: u16,
589 pub trbe_interrupt: ExtendedField<u16, 6>,
590}
591
592#[derive(Clone, Copy, Debug)]
593#[repr(C, packed)]
594pub struct GicdEntry {
595 pub header: EntryHeader,
596 _reserved1: u16,
597 pub gic_id: u32,
598 pub physical_base_address: u64,
599 pub system_vector_base: u32,
600
601 pub gic_version: u8,
609 _reserved2: [u8; 3],
610}
611
612#[derive(Clone, Copy, Debug)]
613#[repr(C, packed)]
614pub struct GicMsiFrameEntry {
615 pub header: EntryHeader,
616 _reserved: u16,
617 pub frame_id: u32,
618 pub physical_base_address: u64,
619 pub flags: u32,
620 pub spi_count: u16,
621 pub spi_base: u16,
622}
623
624#[derive(Clone, Copy, Debug)]
625#[repr(C, packed)]
626pub struct GicRedistributorEntry {
627 pub header: EntryHeader,
628 _reserved: u16,
629 pub discovery_range_base_address: u64,
630 pub discovery_range_length: u32,
631}
632
633#[derive(Clone, Copy, Debug)]
634#[repr(C, packed)]
635pub struct GicInterruptTranslationServiceEntry {
636 pub header: EntryHeader,
637 _reserved1: u16,
638 pub id: u32,
639 pub physical_base_address: u64,
640 _reserved2: u32,
641}
642
643#[derive(Clone, Copy, Debug)]
644#[repr(C, packed)]
645pub struct MultiprocessorWakeupEntry {
646 pub header: EntryHeader,
647 pub mailbox_version: u16,
648 _reserved: u32,
649 pub mailbox_address: u64,
650}
651
652#[derive(Debug, PartialEq, Eq)]
653pub enum MpProtectedModeWakeupCommand {
654 Noop = 0,
655 Wakeup = 1,
656 Sleep = 2,
657 AcceptPages = 3,
658}
659
660impl From<u16> for MpProtectedModeWakeupCommand {
661 fn from(value: u16) -> Self {
662 match value {
663 0 => MpProtectedModeWakeupCommand::Noop,
664 1 => MpProtectedModeWakeupCommand::Wakeup,
665 2 => MpProtectedModeWakeupCommand::Sleep,
666 3 => MpProtectedModeWakeupCommand::AcceptPages,
667 _ => panic!("Invalid value for MpProtectedModeWakeupCommand"),
668 }
669 }
670}
671
672#[repr(C)]
673pub struct MultiprocessorWakeupMailbox {
674 pub command: u16,
675 _reserved: u16,
676 pub apic_id: u32,
677 pub wakeup_vector: u64,
678 pub reserved_for_os: [u64; 254],
679 reserved_for_firmware: [u64; 256],
680}
681
682#[cfg(feature = "allocator_api")]
683fn parse_mps_inti_flags(flags: u16) -> crate::AcpiResult<(Polarity, TriggerMode)> {
684 let polarity = match flags.get_bits(0..2) {
685 0b00 => Polarity::SameAsBus,
686 0b01 => Polarity::ActiveHigh,
687 0b11 => Polarity::ActiveLow,
688 _ => return Err(crate::AcpiError::InvalidMadt(MadtError::MpsIntiInvalidPolarity)),
689 };
690
691 let trigger_mode = match flags.get_bits(2..4) {
692 0b00 => TriggerMode::SameAsBus,
693 0b01 => TriggerMode::Edge,
694 0b11 => TriggerMode::Level,
695 _ => return Err(crate::AcpiError::InvalidMadt(MadtError::MpsIntiInvalidTriggerMode)),
696 };
697
698 Ok((polarity, trigger_mode))
699}