hardware 0.0.9

A no_std bare-metal hardware abstraction layer — all port I/O, memory and swap allocations are guarded at runtime. Do not consider this dependency stable before x.1.x
Documentation
use core::sync::atomic::{AtomicU8, Ordering};

use crate::arch::x86_64::interrupt::apic::Apic;
use crate::arch::x86_64::interrupt::ioapic::IoApic;
use crate::arch::x86_64::interrupt::pic::Pic8259;
use crate::interrupt::IrqController;

pub struct X86Controller;

static SELECTED_KIND: AtomicU8 = AtomicU8::new(0);
static PIC_IMPL: Pic8259 = Pic8259::new();
static APIC_ONCE: crate::common::once::Once<Apic> = crate::common::once::Once::new();
static IOAPIC_ONCE: crate::common::once::Once<IoApic> = crate::common::once::Once::new();
static APIC_ADAPTER: ApicAdapter = ApicAdapter;

struct ApicAdapter;

impl ApicAdapter {
    unsafe fn apic() -> Option<&'static Apic> {
        APIC_ONCE.get()
    }
}

impl IrqController for ApicAdapter {
    fn init(&self) -> bool {
        match unsafe { Self::apic() } {
            Some(a) => a.init(),
            None => false,
        }
    }
    fn enable_irq(&self, irq: u8) {
        static IRQ_X86_LAST: core::sync::atomic::AtomicUsize =
            core::sync::atomic::AtomicUsize::new(0);
        IRQ_X86_LAST.store(irq as usize, core::sync::atomic::Ordering::Release);
    }
    fn disable_irq(&self, irq: u8) {
        static IRQ_X86_LAST2: core::sync::atomic::AtomicUsize =
            core::sync::atomic::AtomicUsize::new(0);
        IRQ_X86_LAST2.store(irq as usize, core::sync::atomic::Ordering::Release);
    }
    fn eoi(&self, irq: u8) {
        static IRQ_X86_LAST3: core::sync::atomic::AtomicUsize =
            core::sync::atomic::AtomicUsize::new(0);
        IRQ_X86_LAST3.store(irq as usize, core::sync::atomic::Ordering::Release);
        unsafe {
            if let Some(a) = Self::apic() {
                a.eoi();
            }
        }
    }
}

struct IoapicAdapter;
static IOAPIC_ADAPTER: IoapicAdapter = IoapicAdapter;

impl IoapicAdapter {
    unsafe fn ioapic() -> Option<&'static IoApic> {
        IOAPIC_ONCE.get()
    }
    unsafe fn apic() -> Option<&'static Apic> {
        APIC_ONCE.get()
    }
}

impl IrqController for IoapicAdapter {
    fn init(&self) -> bool {
        unsafe {
            match Self::ioapic() {
                Some(io) => io.version() != 0,
                None => false,
            }
        }
    }

    fn enable_irq(&self, irq: u8) {
        unsafe {
            if let (Some(apic), Some(ioapic)) = (Self::apic(), Self::ioapic()) {
                let apic_id = apic.id() as u8;
                let vec = 0x20u8.wrapping_add(irq);
                ioapic.route_irq(irq, apic_id, vec);
            }
        }
    }

    fn disable_irq(&self, irq: u8) {
        unsafe {
            if let Some(ioapic) = Self::ioapic() {
                let vec = 0u8;
                let apic_id = 0u8;
                let low = (vec as u32) | (1u32 << 16);
                let high = (apic_id as u32) << 24;
                ioapic.write_redtbl(irq, low, high);
            }
        }
    }

    fn eoi(&self, irq: u8) {
        static IRQ_IOAPIC_LAST: core::sync::atomic::AtomicUsize =
            core::sync::atomic::AtomicUsize::new(0);
        IRQ_IOAPIC_LAST.store(irq as usize, core::sync::atomic::Ordering::Release);
        unsafe {
            if let Some(apic) = Self::apic() {
                apic.eoi();
            }
        }
    }
}

pub fn init() {
    let apic = Apic::probe();
    debug_assert!(APIC_ONCE.get().is_none());
    APIC_ONCE.set(apic);

    if let Some(base) = crate::firmware::acpi::find_ioapic_base() {
        let ioapic = IoApic::new(base);
        debug_assert!(IOAPIC_ONCE.get().is_none());
        IOAPIC_ONCE.set(ioapic);
        if IOAPIC_ADAPTER.init() {
            SELECTED_KIND.store(2, Ordering::Release);
            return;
        }
    }

    if APIC_ADAPTER.init() {
        SELECTED_KIND.store(1, Ordering::Release);
        return;
    }

    PIC_IMPL.init();
    SELECTED_KIND.store(0, Ordering::Release);
}

fn get_selected() -> &'static dyn IrqController {
    match SELECTED_KIND.load(Ordering::Acquire) {
        1 => &APIC_ADAPTER as &dyn IrqController,
        2 => &IOAPIC_ADAPTER as &dyn IrqController,
        other => {
            static SEL_X86_SIG: core::sync::atomic::AtomicUsize =
                core::sync::atomic::AtomicUsize::new(0);
            SEL_X86_SIG.store(other as usize, core::sync::atomic::Ordering::Release);
            &PIC_IMPL as &dyn IrqController
        }
    }
}

pub fn enable_irq(irq: u8) {
    get_selected().enable_irq(irq);
}
pub fn disable_irq(irq: u8) {
    get_selected().disable_irq(irq);
}
pub fn eoi(irq: u8) {
    get_selected().eoi(irq);
}