From f0b5d517932a803f7a2c3d73711b55d4d9f665c8 Mon Sep 17 00:00:00 2001
From: 4lDO2 <4lDO2@protonmail.com>
Date: Sat, 18 Apr 2020 16:36:18 +0200
Subject: [PATCH] Use the I/O APIC when applicable.

---
 src/acpi/madt.rs                 |   8 +-
 src/acpi/mod.rs                  |   2 +-
 src/acpi/rsdp.rs                 |  32 ++-
 src/arch/x86_64/device/ioapic.rs | 382 +++++++++++++++++++++++++++++++
 src/arch/x86_64/device/mod.rs    |   7 +-
 src/arch/x86_64/device/pic.rs    |   9 +
 src/arch/x86_64/interrupt/irq.rs | 119 ++++++++--
 src/arch/x86_64/start.rs         |   5 +-
 8 files changed, 526 insertions(+), 38 deletions(-)
 create mode 100644 src/arch/x86_64/device/ioapic.rs

diff --git a/src/acpi/madt.rs b/src/acpi/madt.rs
index d8904899..67743589 100644
--- a/src/acpi/madt.rs
+++ b/src/acpi/madt.rs
@@ -15,13 +15,16 @@ use crate::interrupt;
 use crate::start::{kstart_ap, CPU_COUNT, AP_READY};
 
 /// The Multiple APIC Descriptor Table
-#[derive(Debug)]
+#[derive(Clone, Copy, Debug)]
 pub struct Madt {
     sdt: &'static Sdt,
     pub local_address: u32,
     pub flags: u32
 }
 
+pub static mut MADT: Option<Madt> = None;
+pub const FLAG_PCAT: u32 = 1;
+
 impl Madt {
     pub fn init(active_table: &mut ActivePageTable) {
         let madt_sdt = find_sdt("APIC");
@@ -34,6 +37,9 @@ impl Madt {
         };
 
         if let Some(madt) = madt {
+            // safe because no APs have been started yet.
+            unsafe { MADT = Some(madt) };
+
             println!("  APIC: {:>08X}: {}", madt.local_address, madt.flags);
 
             let local_apic = unsafe { &mut LOCAL_APIC };
diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs
index f0f82455..7d79c7ce 100644
--- a/src/acpi/mod.rs
+++ b/src/acpi/mod.rs
@@ -31,7 +31,7 @@ use self::aml::{parse_aml_table, AmlError, AmlValue};
 pub mod hpet;
 mod dmar;
 mod fadt;
-mod madt;
+pub mod madt;
 mod rsdt;
 mod sdt;
 mod xsdt;
diff --git a/src/acpi/rsdp.rs b/src/acpi/rsdp.rs
index c457ef14..fa244fdf 100644
--- a/src/acpi/rsdp.rs
+++ b/src/acpi/rsdp.rs
@@ -1,4 +1,5 @@
 use core::convert::TryFrom;
+use core::mem;
 
 use crate::memory::Frame;
 use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
@@ -36,10 +37,17 @@ impl RSDP {
             type Item = &'a [u8];
 
             fn next(&mut self) -> Option<Self::Item> {
-                let length = <[u8; 4]>::try_from(&self.buf[..4]).ok()?;
+                if self.buf.len() < 4 { return None }
+
+                let length_bytes = <[u8; 4]>::try_from(&self.buf[..4]).ok()?;
+                let length = u32::from_ne_bytes(length_bytes) as usize;
+
                 if (4 + length as usize) > self.buf.len() { return None }
-                self.buf = self.buf[4 + length..];
-                Ok(length)
+
+                let buf = &self.buf[4..4 + length];
+                self.buf = &self.buf[4 + length..];
+
+                Some(buf)
             }
         }
         fn slice_to_rsdp(slice: &[u8]) -> Option<&RSDP> {
@@ -52,20 +60,22 @@ impl RSDP {
             } else { None }
         }
 
-        // first, find an RDSP for ACPI 2.0
-        if let Some(rdsp_2_0) = Iter { buf: area }.filter_map(slice_to_rsdp).filter(|rsdp| rsdp.is_acpi_2_0()) {
-            return Some(rsdp_2_0);
+        // first, find an RSDP for ACPI 2.0
+        if let Some(rsdp_2_0) = (Iter { buf: area }.filter_map(slice_to_rsdp).find(|rsdp| rsdp.is_acpi_2_0())) {
+            return Some(*rsdp_2_0);
         }
 
-        // secondly, find an RDSP for ACPI 1.0
-        if let Some(rdsp_1_0) = Iter { buf: area }.filter_map(slice_to_rsdp).filter(|rsdp| rsdp.is_acpi_1_0()) {
-            return Some(rsdp_1_0);
+        // secondly, find an RSDP for ACPI 1.0
+        if let Some(rsdp_1_0) = (Iter { buf: area }.filter_map(slice_to_rsdp).find(|rsdp| rsdp.is_acpi_1_0())) {
+            return Some(*rsdp_1_0);
         }
+
+        None
     }
     pub fn get_rsdp(active_table: &mut ActivePageTable, already_supplied_rsdps: Option<(u64, u64)>) -> Option<RSDP> {
         if let Some((base, size)) = already_supplied_rsdps {
-            let area = core::slice::from_raw_parts(base as usize as *const u8, size as usize);
-            Self::get_already_supplied_rsdps(area)
+            let area = unsafe { core::slice::from_raw_parts(base as usize as *const u8, size as usize) };
+            Self::get_already_supplied_rsdps(area).or_else(|| Self::get_rsdp_by_searching(active_table))
         } else {
             Self::get_rsdp_by_searching(active_table)
         }
diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_64/device/ioapic.rs
new file mode 100644
index 00000000..2897ed92
--- /dev/null
+++ b/src/arch/x86_64/device/ioapic.rs
@@ -0,0 +1,382 @@
+use core::{fmt, ptr};
+
+use alloc::vec::Vec;
+use spin::Mutex;
+
+#[cfg(feature = "acpi")]
+use crate::acpi::madt::{self, Madt, MadtEntry, MadtIoApic, MadtIntSrcOverride};
+
+use crate::arch::interrupt::irq;
+use crate::memory::Frame;
+use crate::paging::{ActivePageTable, entry::EntryFlags, Page, PhysicalAddress, VirtualAddress};
+use crate::syscall::io::Mmio;
+
+use super::pic;
+
+pub struct IoApicRegs {
+    pointer: *const u32,
+}
+impl IoApicRegs {
+    fn ioregsel(&self) -> *const u32 {
+        self.pointer
+    }
+    fn iowin(&self) -> *const u32 {
+        // offset 0x10
+        unsafe { self.pointer.offset(4) }
+    }
+    fn read_ioregsel(&self) -> u32 {
+        unsafe { ptr::read_volatile::<u32>(self.ioregsel()) }
+    }
+    fn write_ioregsel(&mut self, value: u32) {
+        unsafe { ptr::write_volatile::<u32>(self.ioregsel() as *mut u32, value) }
+    }
+    fn read_iowin(&self) -> u32 {
+        unsafe { ptr::read_volatile::<u32>(self.iowin()) }
+    }
+    fn write_iowin(&mut self, value: u32) {
+        unsafe { ptr::write_volatile::<u32>(self.iowin() as *mut u32, value) }
+    }
+    fn read_reg(&mut self, reg: u8) -> u32 {
+        self.write_ioregsel(reg.into());
+        self.read_iowin()
+    }
+    fn write_reg(&mut self, reg: u8, value: u32) {
+        self.write_ioregsel(reg.into());
+        self.write_iowin(value);
+    }
+    pub fn read_ioapicid(&mut self) -> u32 {
+        self.read_reg(0x00)
+    }
+    pub fn write_ioapicid(&mut self, value: u32) {
+        self.write_reg(0x00, value);
+    }
+    pub fn read_ioapicver(&mut self) -> u32 {
+        self.read_reg(0x01)
+    }
+    pub fn read_ioapicarb(&mut self) -> u32 {
+        self.read_reg(0x02)
+    }
+    pub fn read_ioredtbl(&mut self, idx: u8) -> u64 {
+        assert!(idx < 24);
+        let lo = self.read_reg(0x10 + idx * 2);
+        let hi = self.read_reg(0x10 + idx * 2 + 1);
+
+        u64::from(lo) | (u64::from(hi) << 32)
+    }
+    pub fn write_ioredtbl(&mut self, idx: u8, value: u64) {
+        assert!(idx < 24);
+
+        let lo = value as u32;
+        let hi = (value >> 32) as u32;
+
+        self.write_reg(0x10 + idx * 2, lo);
+        self.write_reg(0x10 + idx * 2 + 1, hi);
+    }
+
+    pub fn max_redirection_table_entries(&mut self) -> u8 {
+        let ver = self.read_ioapicver();
+        ((ver & 0x00FF_0000) >> 16) as u8
+    }
+    pub fn id(&mut self) -> u8 {
+        let id_reg = self.read_ioapicid();
+        ((id_reg & 0x0F00_0000) >> 24) as u8
+    }
+}
+pub struct IoApic {
+    regs: Mutex<IoApicRegs>,
+    gsi_start: u32,
+    count: u8,
+}
+impl IoApic {
+    pub fn new(regs_base: *const u32, gsi_start: u32) -> Self {
+        let mut regs = IoApicRegs { pointer: regs_base };
+        let count = regs.max_redirection_table_entries();
+
+        Self {
+            regs: Mutex::new(regs),
+            gsi_start,
+            count,
+        }
+    }
+    /// Map an interrupt vector to a physical local APIC ID of a processor (thus physical mode).
+    pub fn map(&self, idx: u8, info: MapInfo) {
+        self.regs.lock().write_ioredtbl(idx, info.as_raw())
+    }
+    pub fn set_mask(&self, gsi: u32, mask: bool) {
+        let idx = (gsi - self.gsi_start) as u8;
+        let mut guard = self.regs.lock();
+
+        let mut reg = guard.read_ioredtbl(idx);
+        reg &= !(1 << 16);
+        reg |= u64::from(mask) << 16;
+        guard.write_ioredtbl(idx, reg);
+    }
+}
+#[repr(u8)]
+#[derive(Clone, Copy, Debug)]
+pub enum ApicTriggerMode {
+    Edge = 0,
+    Level = 1,
+}
+#[repr(u8)]
+#[derive(Clone, Copy, Debug)]
+pub enum ApicPolarity {
+    ActiveHigh = 0,
+    ActiveLow = 1,
+}
+#[repr(u8)]
+#[derive(Clone, Copy, Debug)]
+pub enum DestinationMode {
+    Physical = 0,
+    Logical = 1,
+}
+#[repr(u8)]
+#[derive(Clone, Copy, Debug)]
+pub enum DeliveryMode {
+    Fixed =             0b000,
+    LowestPriority =    0b001,
+    Smi =               0b010,
+    Nmi =               0b100,
+    Init =              0b101,
+    ExtInt =            0b111,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct MapInfo {
+    pub dest: u8,
+    pub mask: bool,
+    pub trigger_mode: ApicTriggerMode,
+    pub polarity: ApicPolarity,
+    pub dest_mode: DestinationMode,
+    pub delivery_mode: DeliveryMode,
+    pub vector: u8,
+}
+
+impl MapInfo {
+    pub fn as_raw(&self) -> u64 {
+        assert!(self.vector >= 0x20);
+        assert!(self.vector <= 0xFE);
+
+        // TODO: Check for reserved fields.
+
+        (u64::from(self.dest) << 56)
+            | (u64::from(self.mask) << 16)
+            | ((self.trigger_mode as u64) << 15)
+            | ((self.polarity as u64) << 13)
+            | ((self.dest_mode as u64) << 11)
+            | ((self.delivery_mode as u64) << 8)
+            | u64::from(self.vector)
+    }
+}
+
+impl fmt::Debug for IoApic {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        struct RedirTable<'a>(&'a Mutex<IoApicRegs>);
+
+        impl<'a> fmt::Debug for RedirTable<'a> {
+            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                let mut guard = self.0.lock();
+
+                let count = guard.max_redirection_table_entries();
+                f.debug_list().entries((0..count).map(|i| guard.read_ioredtbl(i))).finish()
+            }
+        }
+
+        f.debug_struct("IoApic")
+            .field("redir_table", &RedirTable(&self.regs))
+            .field("gsi_start", &self.gsi_start)
+            .field("count", &self.count)
+            .finish()
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum TriggerMode {
+    ConformsToSpecs,
+    Edge,
+    Level,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum Polarity {
+    ConformsToSpecs,
+    ActiveHigh,
+    ActiveLow,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct Override {
+    bus_irq: u8,
+    gsi: u32,
+
+    trigger_mode: TriggerMode,
+    polarity: Polarity,
+}
+
+// static mut because only the AP initializes the I/O Apic, and when that is done, it's solely
+// accessed immutably.
+static mut IOAPICS: Option<Vec<IoApic>> = None;
+
+// static mut for the same reason as above
+static mut SRC_OVERRIDES: Option<Vec<Override>> = None;
+
+pub fn ioapics() -> &'static [IoApic] {
+    unsafe {
+        IOAPICS.as_ref().map_or(&[], |vector| &vector[..])
+    }
+}
+pub fn src_overrides() -> &'static [Override] {
+    unsafe {
+        SRC_OVERRIDES.as_ref().map_or(&[], |vector| &vector[..])
+    }
+}
+
+#[cfg(feature = "acpi")]
+pub unsafe fn handle_ioapic(active_table: &mut ActivePageTable, madt_ioapic: &'static MadtIoApic) {
+    // map the I/O APIC registers
+    let frame = Frame::containing_address(PhysicalAddress::new(madt_ioapic.address as usize));
+    let page = Page::containing_address(VirtualAddress::new(madt_ioapic.address as usize + crate::KERNEL_OFFSET));
+
+    assert_eq!(active_table.translate_page(page), None);
+
+    let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::GLOBAL | EntryFlags::WRITABLE | EntryFlags::NO_CACHE);
+    result.flush(active_table);
+
+    let ioapic_registers = page.start_address().get() as *const u32;
+    let mut ioapic = IoApic::new(ioapic_registers, madt_ioapic.gsi_base);
+
+    assert_eq!(ioapic.regs.lock().id(), madt_ioapic.id, "mismatched ACPI MADT I/O APIC ID, and the ID reported by the I/O APIC");
+
+    IOAPICS.get_or_insert_with(Vec::new).push(ioapic);
+}
+#[cfg(feature = "acpi")]
+pub unsafe fn handle_src_override(src_override: &'static MadtIntSrcOverride) {
+    let flags = src_override.flags;
+
+    let polarity_raw = (flags & 0x0003) as u8;
+    let trigger_mode_raw = ((flags & 0x000C) >> 2) as u8;
+
+    let polarity = match polarity_raw {
+        0b00 => Polarity::ConformsToSpecs,
+        0b01 => Polarity::ActiveHigh,
+        0b10 => return, // reserved
+        0b11 => Polarity::ActiveLow,
+
+        _ => unreachable!(),
+    };
+
+    let trigger_mode = match trigger_mode_raw {
+        0b00 => TriggerMode::ConformsToSpecs,
+        0b01 => TriggerMode::Edge,
+        0b10 => return, // reserved
+        0b11 => TriggerMode::Level,
+        _ => unreachable!(),
+    };
+
+    let over = Override {
+        bus_irq: src_override.irq_source,
+        gsi: src_override.gsi_base,
+        polarity,
+        trigger_mode,
+    };
+    SRC_OVERRIDES.get_or_insert_with(Vec::new).push(over);
+}
+
+pub unsafe fn init(active_table: &mut ActivePageTable) {
+    let mut bsp_apic_id = x86::cpuid::CpuId::new().get_feature_info().unwrap().initial_local_apic_id(); // TODO
+
+    // search the madt for all IOAPICs.
+    #[cfg(feature = "acpi")]
+    {
+        let madt: &'static Madt = match madt::MADT.as_ref() {
+            Some(m) => m,
+            // TODO: Parse MP tables too.
+            None => return,
+        };
+        if madt.flags & madt::FLAG_PCAT != 0 {
+            pic::disable();
+        }
+
+        // find all I/O APICs (usually one).
+
+        for entry in madt.iter() {
+            match entry {
+                MadtEntry::IoApic(ioapic) => handle_ioapic(active_table, ioapic),
+                MadtEntry::IntSrcOverride(src_override) => handle_src_override(src_override),
+                _ => (),
+            }
+        }
+    }
+    println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides());
+
+    // map the legacy PC-compatible IRQs (0-15) to 32-47, just like we did with 8259 PIC (if it
+    // wouldn't have been disabled due to this I/O APIC)
+    for legacy_irq in 0..=15 {
+        let (gsi, trigger_mode, polarity) = match get_override(legacy_irq) {
+            Some(over) => (over.gsi, over.trigger_mode, over.polarity),
+            None => {
+                if src_overrides().iter().any(|over| over.gsi == u32::from(legacy_irq) && over.bus_irq != legacy_irq) && !src_overrides().iter().any(|over| over.bus_irq == legacy_irq) {
+                    // there's an IRQ conflict, making this legacy IRQ inaccessible.
+                    continue;
+                }
+                (legacy_irq.into(), TriggerMode::ConformsToSpecs, Polarity::ConformsToSpecs)
+            }
+        };
+        let apic = match find_ioapic(gsi) {
+            Some(ioapic) => ioapic,
+            None => {
+                println!("Unable to find a suitable APIC for legacy IRQ {} (GSI {}). It will not be mapped.", legacy_irq, gsi);
+                continue;
+            }
+        };
+        let redir_tbl_index = (gsi - apic.gsi_start) as u8;
+
+        let map_info = MapInfo {
+            // only send to the BSP
+            dest: bsp_apic_id,
+            dest_mode: DestinationMode::Physical,
+            delivery_mode: DeliveryMode::Fixed,
+            mask: false,
+            polarity: match polarity {
+                Polarity::ActiveHigh => ApicPolarity::ActiveHigh,
+                Polarity::ActiveLow => ApicPolarity::ActiveLow,
+                Polarity::ConformsToSpecs => ApicPolarity::ActiveHigh,
+            },
+            trigger_mode: match trigger_mode {
+                TriggerMode::Edge => ApicTriggerMode::Edge,
+                TriggerMode::Level => ApicTriggerMode::Level,
+                TriggerMode::ConformsToSpecs => ApicTriggerMode::Edge,
+            },
+            vector: 32 + legacy_irq,
+        };
+        apic.map(redir_tbl_index, map_info);
+    }
+    println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides());
+    irq::set_irq_method(irq::IrqMethod::Apic);
+}
+fn get_override(irq: u8) -> Option<&'static Override> {
+    src_overrides().iter().find(|over| over.bus_irq == irq) 
+}
+fn resolve(irq: u8) -> u32 {
+    get_override(irq).map_or(u32::from(irq), |over| over.gsi)
+}
+fn find_ioapic(gsi: u32) -> Option<&'static IoApic> {
+    ioapics().iter().find(|apic| gsi >= apic.gsi_start && gsi < apic.gsi_start + u32::from(apic.count)) 
+}
+
+pub unsafe fn mask(irq: u8) {
+    let gsi = resolve(irq);
+    let apic = match find_ioapic(gsi) {
+        Some(a) => a,
+        None => return,
+    };
+    apic.set_mask(gsi, true);
+}
+pub unsafe fn unmask(irq: u8) {
+    let gsi = resolve(irq);
+    let apic = match find_ioapic(gsi) {
+        Some(a) => a,
+        None => return,
+    };
+    apic.set_mask(gsi, false);
+}
diff --git a/src/arch/x86_64/device/mod.rs b/src/arch/x86_64/device/mod.rs
index 3c876525..656d2b25 100644
--- a/src/arch/x86_64/device/mod.rs
+++ b/src/arch/x86_64/device/mod.rs
@@ -1,6 +1,7 @@
 use crate::paging::ActivePageTable;
 
 pub mod cpu;
+pub mod ioapic;
 pub mod local_apic;
 pub mod pic;
 pub mod pit;
@@ -9,10 +10,14 @@ pub mod serial;
 #[cfg(feature = "acpi")]
 pub mod hpet;
 
-pub unsafe fn init(active_table: &mut ActivePageTable){
+pub unsafe fn init(active_table: &mut ActivePageTable) {
     pic::init();
     local_apic::init(active_table);
 }
+pub unsafe fn init_after_acpi(active_table: &mut ActivePageTable)  {
+    // this will disable the IOAPIC if needed.
+    ioapic::init(active_table);
+}
 
 #[cfg(feature = "acpi")]
 unsafe fn init_hpet() -> bool {
diff --git a/src/arch/x86_64/device/pic.rs b/src/arch/x86_64/device/pic.rs
index 88f57097..2518550d 100644
--- a/src/arch/x86_64/device/pic.rs
+++ b/src/arch/x86_64/device/pic.rs
@@ -1,4 +1,5 @@
 use crate::syscall::io::{Io, Pio};
+use crate::arch::interrupt::irq;
 
 pub static mut MASTER: Pic = Pic::new(0x20);
 pub static mut SLAVE: Pic = Pic::new(0xA0);
@@ -27,6 +28,14 @@ pub unsafe fn init() {
     // Ack remaining interrupts
     MASTER.ack();
     SLAVE.ack();
+
+    // probably already set to PIC, but double-check
+    irq::set_irq_method(irq::IrqMethod::Pic);
+}
+
+pub unsafe fn disable() {
+    MASTER.data.write(0xFF);
+    SLAVE.data.write(0xFF);
 }
 
 pub struct Pic {
diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs
index 72c29726..9b4ba742 100644
--- a/src/arch/x86_64/interrupt/irq.rs
+++ b/src/arch/x86_64/interrupt/irq.rs
@@ -1,7 +1,7 @@
 use core::sync::atomic::{AtomicUsize, Ordering};
 
 use crate::context::timeout;
-use crate::device::{local_apic, pic};
+use crate::device::{local_apic, ioapic, pic};
 use crate::device::serial::{COM1, COM2};
 use crate::ipi::{ipi, IpiKind, IpiTarget};
 use crate::scheme::debug::debug_input;
@@ -11,38 +11,99 @@ use crate::{context, ptrace, time};
 #[thread_local]
 pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
 
+#[repr(u8)]
+pub enum IrqMethod {
+    Pic = 0,
+    Apic = 1,
+}
+
+static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize);
+
+pub fn set_irq_method(method: IrqMethod) {
+    IRQ_METHOD.store(method as usize, core::sync::atomic::Ordering::Release);
+}
+
+fn irq_method() -> IrqMethod {
+    let raw = IRQ_METHOD.load(core::sync::atomic::Ordering::Acquire);
+    
+    match raw {
+        0 => IrqMethod::Pic,
+        1 => IrqMethod::Apic,
+        _ => unreachable!(),
+    }
+}
+
 extern {
+    // triggers irq scheme
     fn irq_trigger(irq: u8);
 }
 
+/// Notify the IRQ scheme that an IRQ has been registered. This should mask the IRQ until the
+/// scheme user unmasks it ("acknowledges" it).
 unsafe fn trigger(irq: u8) {
+    match irq_method() {
+        IrqMethod::Pic => if irq < 16 { pic_mask(irq) },
+        IrqMethod::Apic => ioapic_mask(irq),
+    }
+    irq_trigger(irq);
+}
+/// Unmask the IRQ. This is called from the IRQ scheme, which does this when a user process has
+/// processed the IRQ.
+pub unsafe fn acknowledge(irq: usize) {
+    match irq_method() {
+        IrqMethod::Pic => if irq < 16 { pic_unmask(irq) },
+        IrqMethod::Apic => ioapic_unmask(irq),
+    }
+}
+/// Sends an end-of-interrupt, so that the interrupt controller can go on to the next one.
+pub unsafe fn eoi(irq: u8) {
+    match irq_method() {
+        IrqMethod::Pic => if irq < 16 { pic_eoi(irq) },
+        IrqMethod::Apic => lapic_eoi(),
+    }
+}
 
-    if irq < 16 {
-        if irq >= 8 {
-            pic::SLAVE.mask_set(irq - 8);
-            pic::MASTER.ack();
-            pic::SLAVE.ack();
-        } else {
-            pic::MASTER.mask_set(irq);
-            pic::MASTER.ack();
-        }
+unsafe fn pic_mask(irq: u8) {
+    debug_assert!(irq < 16);
+
+    if irq >= 8 {
+        pic::SLAVE.mask_set(irq - 8);
+    } else {
+        pic::MASTER.mask_set(irq);
     }
+}
 
-    irq_trigger(irq);
+unsafe fn ioapic_mask(irq: u8) {
+    ioapic::mask(irq);
+}
+
+
+unsafe fn pic_eoi(irq: u8) {
+    debug_assert!(irq < 16);
+
+    if irq >= 8 {
+        pic::MASTER.ack();
+        pic::SLAVE.ack();
+    } else {
+        pic::MASTER.ack();
+    }
 }
 unsafe fn lapic_eoi() {
     local_apic::LOCAL_APIC.eoi()
 }
 
-pub unsafe fn acknowledge(irq: usize) {
-    if irq < 16 {
-        if irq >= 8 {
-            pic::SLAVE.mask_clear(irq as u8 - 8);
-        } else {
-            pic::MASTER.mask_clear(irq as u8);
-        }
+unsafe fn pic_unmask(irq: usize) {
+    debug_assert!(irq < 16);
+
+    if irq >= 8 {
+        pic::SLAVE.mask_clear(irq as u8 - 8);
+    } else {
+        pic::MASTER.mask_clear(irq as u8);
     }
 }
+unsafe fn ioapic_unmask(irq: usize) {
+    ioapic::unmask(irq as u8);
+}
 
 interrupt_stack!(pit, stack, {
     // Saves CPU time by not sending IRQ event irq_trigger(0);
@@ -56,7 +117,7 @@ interrupt_stack!(pit, stack, {
         offset.0 += sum / 1_000_000_000;
     }
 
-    pic::MASTER.ack();
+    eoi(0);
 
     // Wake up other CPUs
     ipi(IpiKind::Pit, IpiTarget::Other);
@@ -72,68 +133,80 @@ interrupt_stack!(pit, stack, {
 
 interrupt!(keyboard, {
     trigger(1);
+    eoi(1);
 });
 
 interrupt!(cascade, {
     // No need to do any operations on cascade
-    pic::MASTER.ack();
+    eoi(2);
 });
 
 interrupt!(com2, {
     while let Some(c) = COM2.lock().receive() {
         debug_input(c);
     }
-    pic::MASTER.ack();
+    eoi(3);
 });
 
 interrupt!(com1, {
     while let Some(c) = COM1.lock().receive() {
         debug_input(c);
     }
-    pic::MASTER.ack();
+    eoi(4);
 });
 
 interrupt!(lpt2, {
+    eoi(5);
     trigger(5);
 });
 
 interrupt!(floppy, {
+    eoi(6);
     trigger(6);
 });
 
 interrupt!(lpt1, {
+    eoi(7);
     trigger(7);
 });
 
 interrupt!(rtc, {
+    eoi(8);
     trigger(8);
 });
 
 interrupt!(pci1, {
+    eoi(9);
     trigger(9);
 });
 
 interrupt!(pci2, {
+    eoi(10);
     trigger(10);
 });
 
 interrupt!(pci3, {
+    eoi(11);
     trigger(11);
 });
 
 interrupt!(mouse, {
+    eoi(12);
     trigger(12);
 });
 
 interrupt!(fpu, {
+    eoi(13);
     trigger(13);
 });
 
 interrupt!(ata1, {
+    eoi(14);
     trigger(14);
 });
 
 interrupt!(ata2, {
+    eoi(15);
     trigger(15);
 });
 interrupt!(lapic_timer, {
@@ -154,7 +227,7 @@ interrupt!(calib_pit, {
         offset.0 += sum / 1_000_000_000;
     }
 
-    pic::MASTER.ack();
+    eoi(0);
 });
 // XXX: This would look way prettier using const generics.
 
diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs
index 4cb7333d..12afd596 100644
--- a/src/arch/x86_64/start.rs
+++ b/src/arch/x86_64/start.rs
@@ -176,7 +176,10 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
 
         // Read ACPI tables, starts APs
         #[cfg(feature = "acpi")]
-        acpi::init(&mut active_table, extended_kargs.as_ref().map(|kargs| (kargs.acpi_rsdps_base, kargs.acpi_rsdps_size)));
+        {
+            acpi::init(&mut active_table, extended_kargs.as_ref().map(|kargs| (kargs.acpi_rsdps_base, kargs.acpi_rsdps_size)));
+            device::init_after_acpi(&mut active_table);
+        }
 
         // Initialize all of the non-core devices not otherwise needed to complete initialization
         device::init_noncore();
-- 
GitLab