Add MADT NMI types 0x4/0x5/0xA and LVT NMI programming

Add MadtLocalApicNmi (type 0x4), MadtLapicAddressOverride (type 0x5), and MadtLocalX2ApicNmi (type 0xA) structs with compile-time size assertions. Add enum variants and iterator cases for all three. Implement set_lvt_nmi() on LocalApic for both xAPIC (LINT0/LINT1 at offsets 0x350/0x360) and x2APIC (MSRs 0x835/0x836) with NMI delivery mode, polarity, and trigger mode from MADT flags. Process NMI entries in x86.rs MADT loop to configure per-CPU LVT NMI registers. Parse and log LAPIC address override (64-bit) for future use.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-12 22:15:41 +01:00
parent 7db7dc9e72
commit 01a6bc8d2e
+194
View File
@@ -378,3 +378,197 @@ index 59e35265..80a40a01 100644
let signature = get_sdt_signature(sdt);
if let Some(ref mut ptrs) = *(SDT_POINTERS.write()) {
diff --git a/src/acpi/madt/mod.rs b/src/acpi/madt/mod.rs
index abcdef12..bcdef123 100644
--- a/src/acpi/madt/mod.rs
+++ b/src/acpi/madt/mod.rs
@@ -153,10 +153,45 @@ pub struct MadtLocalX2Apic {
_reserved: u16,
pub x2apic_id: u32,
pub flags: u32,
pub processor_uid: u32,
}
+/// MADT Local APIC NMI (entry type 0x4)
+/// Configures NMI routing to a processor's LINT0/LINT1 pin.
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+pub struct MadtLocalApicNmi {
+ pub processor: u8, // 0xFF = all processors
+ pub flags: u16, // bits 0-1: polarity, bits 2-3: trigger mode
+ pub nmi_pin: u8, // 0 = LINT0, 1 = LINT1
+}
+
+/// MADT Local APIC Address Override (entry type 0x5)
+/// Provides 64-bit override for the 32-bit local APIC address.
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+pub struct MadtLapicAddressOverride {
+ _reserved: u16,
+ pub local_apic_address: u64,
+}
+
+/// MADT Local x2APIC NMI (entry type 0xA)
+/// x2APIC equivalent of type 0x4 for APIC IDs >= 255.
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+pub struct MadtLocalX2ApicNmi {
+ _reserved: u16,
+ pub processor_uid: u32, // 0xFFFFFFFF = all processors
+ pub flags: u16,
+ pub nmi_pin: u8, // 0 = LINT0, 1 = LINT1
+ _reserved2: u8,
+}
+
+const _: () = assert!(size_of::<MadtLocalApicNmi>() == 4);
+const _: () = assert!(size_of::<MadtLapicAddressOverride>() == 10);
+const _: () = assert!(size_of::<MadtLocalX2ApicNmi>() == 10);
+
/// MADT Entries
#[derive(Debug)]
#[allow(dead_code)]
pub enum MadtEntry {
@@ -172,6 +207,12 @@ pub enum MadtEntry {
Gicd(&'static MadtGicd),
InvalidGicd(usize),
LocalX2Apic(&'static MadtLocalX2Apic),
InvalidLocalX2Apic(usize),
+ LocalApicNmi(&'static MadtLocalApicNmi),
+ InvalidLocalApicNmi(usize),
+ LapicAddressOverride(&'static MadtLapicAddressOverride),
+ InvalidLapicAddressOverride(usize),
+ LocalX2ApicNmi(&'static MadtLocalX2ApicNmi),
+ InvalidLocalX2ApicNmi(usize),
Unknown(u8),
}
diff --git a/src/acpi/madt/mod.rs b/src/acpi/madt/mod.rs
index bcdef123..cdef1234 100644
--- a/src/acpi/madt/mod.rs
+++ b/src/acpi/madt/mod.rs
@@ -235,12 +235,40 @@ impl Iterator for MadtIter {
0x9 => {
if entry_len == size_of::<MadtLocalX2Apic>() + 2 {
MadtEntry::LocalX2Apic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtLocalX2Apic)
})
} else {
MadtEntry::InvalidLocalX2Apic(entry_len)
}
}
+ 0x4 => {
+ if entry_len == size_of::<MadtLocalApicNmi>() + 2 {
+ MadtEntry::LocalApicNmi(unsafe {
+ &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApicNmi)
+ })
+ } else {
+ MadtEntry::InvalidLocalApicNmi(entry_len)
+ }
+ }
+ 0x5 => {
+ if entry_len == size_of::<MadtLapicAddressOverride>() + 2 {
+ MadtEntry::LapicAddressOverride(unsafe {
+ &*((self.sdt.data_address() + self.i + 2)
+ as *const MadtLapicAddressOverride)
+ })
+ } else {
+ MadtEntry::InvalidLapicAddressOverride(entry_len)
+ }
+ }
+ 0xA => {
+ if entry_len == size_of::<MadtLocalX2ApicNmi>() + 2 {
+ MadtEntry::LocalX2ApicNmi(unsafe {
+ &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalX2ApicNmi)
+ })
+ } else {
+ MadtEntry::InvalidLocalX2ApicNmi(entry_len)
+ }
+ }
_ => MadtEntry::Unknown(entry_type),
};
diff --git a/src/arch/x86_shared/device/local_apic.rs b/src/arch/x86_shared/device/local_apic.rs
index bfcc9a80..c0ffee01 100644
--- a/src/arch/x86_shared/device/local_apic.rs
+++ b/src/arch/x86_shared/device/local_apic.rs
@@ -7,9 +7,12 @@ use crate::{
arch::cpuid::cpuid,
ipi::IpiKind,
memory::{map_device_memory, PhysicalAddress},
percpu::PercpuBlock,
};
+const IA32_X2APIC_LVT_LINT0: u32 = 0x835;
+const IA32_X2APIC_LVT_LINT1: u32 = 0x836;
+
#[derive(Clone, Copy, Debug)]
pub struct ApicId(u32);
@@ -260,9 +262,31 @@ impl LocalApic {
unsafe fn setup_error_int(&mut self) {
unsafe {
let vector = 49u32;
self.set_lvt_error(vector);
}
}
+
+ /// Configure the LVT entry for NMI delivery on LINT0 or LINT1.
+ /// `nmi_pin`: 0 = LINT0, 1 = LINT1
+ /// `flags`: MADT NMI flags — bits 0-1 polarity, bits 2-3 trigger mode
+ pub unsafe fn set_lvt_nmi(&mut self, nmi_pin: u8, flags: u16) {
+ let delivery_nmi: u32 = 0b100 << 8;
+ let polarity = if flags & 0x3 == 2 { 1u32 << 13 } else { 0 };
+ let trigger = if (flags >> 2) & 0x3 == 2 { 1u32 << 15 } else { 0 };
+ let lvt = delivery_nmi | polarity | trigger;
+
+ if self.x2 {
+ let msr = if nmi_pin == 0 {
+ IA32_X2APIC_LVT_LINT0
+ } else {
+ IA32_X2APIC_LVT_LINT1
+ };
+ wrmsr(msr, lvt.into());
+ } else {
+ let offset: u32 = if nmi_pin == 0 { 0x350 } else { 0x360 };
+ self.write(offset, lvt);
+ }
+ }
}
#[repr(u8)]
diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs
index 4203fec6..faceb00c 100644
--- a/src/acpi/madt/arch/x86.rs
+++ b/src/acpi/madt/arch/x86.rs
@@ -132,6 +132,31 @@ pub(super) fn init(madt: Madt) {
RmmA::invalidate_all();
}
- }
+ } else if let MadtEntry::LocalApicNmi(nmi) = madt_entry {
+ let target_id = nmi.processor;
+ if target_id == 0xFF {
+ debug!(" NMI: all processors, pin={}, flags={:#x}", nmi.nmi_pin, nmi.flags);
+ unsafe { local_apic.set_lvt_nmi(nmi.nmi_pin, nmi.flags); }
+ } else {
+ let my_apic_id = local_apic.id().get() as u8;
+ if target_id == my_apic_id {
+ debug!(" NMI: processor {}, pin={}, flags={:#x}", target_id, nmi.nmi_pin, nmi.flags);
+ unsafe { local_apic.set_lvt_nmi(nmi.nmi_pin, nmi.flags); }
+ }
+ }
+ } else if let MadtEntry::LocalX2ApicNmi(nmi) = madt_entry {
+ let target_uid = nmi.processor_uid;
+ if target_uid == 0xFFFFFFFF {
+ debug!(" x2APIC NMI: all processors, pin={}, flags={:#x}", nmi.nmi_pin, nmi.flags);
+ unsafe { local_apic.set_lvt_nmi(nmi.nmi_pin, nmi.flags); }
+ } else {
+ debug!(" x2APIC NMI: uid {}, pin={}, flags={:#x}", target_uid, nmi.nmi_pin, nmi.flags);
+ unsafe { local_apic.set_lvt_nmi(nmi.nmi_pin, nmi.flags); }
+ }
+ } else if let MadtEntry::LapicAddressOverride(addr) = madt_entry {
+ if addr.local_apic_address != 0 {
+ debug!(" LAPIC address override: {:#x}", addr.local_apic_address);
+ }
+ }
}
// Unmap trampoline