acpi/
madt.rs

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/// Represents the MADT - this contains the MADT header fields. You can then iterate over a `Madt`
33/// to read each entry from it.
34///
35/// In modern versions of ACPI, the MADT can detail one of four interrupt models:
36/// - The ancient dual-i8259 legacy PIC model
37/// - The Advanced Programmable Interrupt Controller (APIC) model
38/// - The Streamlined Advanced Programmable Interrupt Controller (SAPIC) model (for Itanium systems)
39/// - The Generic Interrupt Controller (GIC) model (for ARM systems)
40///
41/// The MADT is a variable-sized structure consisting of a static header and then a variable number of entries.
42/// This type only contains the static portion, and then uses pointer arithmetic to parse the following entries.
43/// To make this sound, this type is `!Unpin` - this prevents you from getting anything other than a `Pin<&Madt>`
44/// out of a `PhysicalMapping`, thereby preventing a `Madt` from being moved before [`Madt::entries`] is called.
45#[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
54/// ### Safety: Implementation properly represents a valid MADT.
55unsafe 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        /*
82         * We first do a pass through the MADT to determine which interrupt model is being used.
83         */
84        for entry in self.entries() {
85            match entry {
86                MadtEntry::LocalApic(_) |
87                MadtEntry::LocalX2Apic(_) |
88                MadtEntry::IoApic(_) |
89                MadtEntry::InterruptSourceOverride(_) |
90                MadtEntry::NmiSource(_) |   // TODO: is this one used by more than one model?
91                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        // Do a pass over the entries so we know how much space we should reserve in the vectors
148        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)?; // Subtract one for the BSP
167        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                    /*
179                     * The first processor is the BSP. Subsequent ones are APs. If we haven't found
180                     * the BSP yet, this must be it.
181                     */
182                    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    /*
342     * The iterator can only have at most `u32::MAX` remaining bytes, because the length of the
343     * whole SDT can only be at most `u32::MAX`.
344     */
345    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                        /*
396                         * These entry types are reserved by the ACPI standard. We should skip them
397                         * if they appear in a real MADT.
398                         */
399                        0x11..=0x7f => {}
400
401                        /*
402                         * These entry types are reserved for OEM use. Atm, we just skip them too.
403                         * TODO: work out if we should ever do anything else here
404                         */
405                        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, // 0 - ISA bus
469    pub irq: u8, // This is bus-relative
470    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, // Describes which LINTn is the NMI connected to
489}
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/// If this entry is present, the system has an I/O SAPIC, which must be used instead of the I/O
500/// APIC.
501#[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    /// This string can be used to associate this local SAPIC to a processor defined in the
523    /// namespace when the `_UID` object is a string. It is a null-terminated ASCII string, and so
524    /// this field will be `'\0'` if the string is not present, otherwise it extends from the
525    /// address of this field.
526    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/// This field will appear for ARM processors that support ACPI and use the Generic Interrupt
563/// Controller. In the GICC interrupt model, each logical process has a Processor Device object in
564/// the namespace, and uses this structure to convey its GIC information.
565#[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    /// SPE overflow Interrupt.
585    ///
586    /// ACPI 6.3 defined this field. It is zero in prior versions or
587    /// if this processor does not support SPE.
588    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    /// The GIC version
602    ///     0x00: Fall back to hardware discovery
603    ///     0x01: GICv1
604    ///     0x02: GICv2
605    ///     0x03: GICv3
606    ///     0x04: GICv4
607    ///     0x05-0xff: Reserved for future use
608    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}