zodiac 0.1.0

Rust OS framework that offers safe interfaces for kernel development
Documentation
use core::sync::atomic::{AtomicBool, Ordering};
use x86_64::instructions::port::Port;

use derive_more::Deref;
use spin::{Lazy, Mutex};
use x2apic::lapic::{LocalApic, LocalApicBuilder, TimerMode};

use super::acpi::ACPI;
use crate::{
    hal::context::TrapFrame,
    mem::{
        MMUFlags, PageSize, PhysicalAddress, PhysicalMemoryAllocOptions, VirtualMemorySpace,
        convert_physical_to_virtual,
    },
    task::schedule,
    trap::Irq,
};

static APIC_INIT: AtomicBool = AtomicBool::new(false);

#[derive(Deref)]
pub struct LockedLocalApic(Mutex<LocalApic>);

unsafe impl Send for LockedLocalApic {}
unsafe impl Sync for LockedLocalApic {}

fn timer_handler(frame: &mut TrapFrame) {
    schedule(frame);
}

static TIMER_IRQ: Lazy<Irq> = Lazy::new(|| Irq::allocate(timer_handler).unwrap());
static APIC_ERROR_IRQ: Lazy<Irq> = Lazy::new(|| Irq::allocate(|_frame| {}).unwrap());
static SPURIOUS_IRQ: Lazy<Irq> = Lazy::new(|| Irq::allocate(|_frame| {}).unwrap());

pub static LAPIC: Lazy<LockedLocalApic> = Lazy::new(|| unsafe {
    let origin_physical_address = ACPI.apic.local_apic_address as PhysicalAddress;
    let physical_address = PageSize::Size4K.align_down(origin_physical_address);
    log::trace!(
        "Initializing LAPIC at physical address {:#x}, timer vector: {}, spurious vector: {}, error vector: {}",
        physical_address,
        TIMER_IRQ.as_int_vector(),
        SPURIOUS_IRQ.as_int_vector(),
        APIC_ERROR_IRQ.as_int_vector()
    );
    let virtual_address = convert_physical_to_virtual(physical_address);

    let page_count = PageSize::Size4K.align_up(origin_physical_address + 0x1000 - physical_address)
        / PageSize::Size4K as usize;

    let physical_memory = PhysicalMemoryAllocOptions::default()
        .contiguous(true)
        .address(physical_address)
        .count(page_count)
        .allocate()
        .unwrap();
    let vm_space = VirtualMemorySpace::new_kernel();

    vm_space
        .cursor(virtual_address, PageSize::Size4K)
        .unwrap()
        .map(&physical_memory, MMUFlags::READ | MMUFlags::WRITE)
        .unwrap();

    let mut lapic = LocalApicBuilder::new()
        .timer_vector(TIMER_IRQ.as_int_vector() as usize)
        .timer_mode(TimerMode::OneShot)
        .timer_initial(0)
        .error_vector(APIC_ERROR_IRQ.as_int_vector() as usize)
        .spurious_vector(SPURIOUS_IRQ.as_int_vector() as usize)
        .set_xapic_base(virtual_address as u64)
        .build()
        .unwrap_or_else(|err| panic!("Failed to build local APIC: {:#?}", err));

    lapic.enable();
    lapic.disable_timer();

    log::trace!("Lapic Initialized");

    LockedLocalApic(Mutex::new(lapic))
});

pub fn ap_init() {
    while !APIC_INIT.load(Ordering::SeqCst) {
        core::hint::spin_loop();
    }
    unsafe {
        LAPIC.lock().enable();
        LAPIC.lock().disable_timer();
    }
}

pub fn end_of_interrupt() {
    unsafe {
        LAPIC.lock().end_of_interrupt();
    }
}

unsafe fn disable_pic() {
    unsafe {
        Port::<u8>::new(0x21).write(0xff);
        Port::<u8>::new(0xa1).write(0xff);
    }
}

pub fn init() {
    unsafe {
        disable_pic();
        APIC_INIT.store(true, Ordering::SeqCst);
    }
}