zodiac 0.2.0

Rust OS framework that offers safe interfaces for kernel development
Documentation
use acpi::fadt::Fadt;
use acpi::platform::interrupt::Apic;
use acpi::{AcpiError, AcpiHandler, PhysicalMapping};
use acpi::{AcpiTables, AmlTable, HpetInfo};
use acpi::{InterruptModel, PciConfigRegions};
use alloc::alloc::Global;
use alloc::boxed::Box;
use core::ptr::NonNull;
use limine::request::RsdpRequest;
use spin::Lazy;
use x86_64::instructions::port::Port;

use crate::mem::{MMUFlags, PhysicalMemoryAllocOptions, VirtualMemorySpace};
use crate::mem::{PageSize, convert_physical_to_virtual};

mod power;
pub use power::{reboot, shutdown};

#[used]
#[unsafe(link_section = ".requests")]
static RSDP_REQUEST: RsdpRequest = RsdpRequest::new();

pub static ACPI: Lazy<Acpi> = Lazy::new(|| init().unwrap());

#[allow(dead_code)]
pub struct Acpi<'a> {
    pub apic: Apic<'a, Global>,
    pub hpet_info: HpetInfo,
    pub pci_regions: PciConfigRegions<'a, Global>,
    pub fadt: Fadt,
    pub aml_table: AmlTable,
}

fn init() -> Result<Acpi<'static>, AcpiError> {
    let response = RSDP_REQUEST.get_response().unwrap();

    let acpi_tables = unsafe {
        let rsdp_address = response.address();
        let tables = AcpiTables::from_rsdp(AcpiMemHandler, rsdp_address)?;
        Box::leak(Box::new(tables))
    };

    let apic = match acpi_tables.platform_info()?.interrupt_model {
        InterruptModel::Apic(apic) => apic,
        InterruptModel::Unknown => panic!("No APIC support!"),
        _ => panic!("ACPI does not have interrupt model info!"),
    };

    let fadt = *acpi_tables.find_table::<Fadt>()?;

    let pm1a = fadt.pm1a_control_block().unwrap();
    let mut pm1a_port = Port::<u16>::new(pm1a.address as u16);

    unsafe {
        if fadt.smi_cmd_port != 0
            && (fadt.acpi_enable == 0 || fadt.acpi_disable == 0)
            && pm1a_port.read() & 1 == 0
        {
            Port::new(fadt.smi_cmd_port as u16).write(fadt.acpi_enable);
            while pm1a_port.read() & 1 == 0 {}
        }
    }

    Ok(Acpi {
        apic,
        hpet_info: HpetInfo::new(acpi_tables)?,
        pci_regions: PciConfigRegions::new(acpi_tables)?,
        fadt,
        aml_table: acpi_tables.dsdt()?,
    })
}

#[derive(Clone)]
struct AcpiMemHandler;

impl AcpiHandler for AcpiMemHandler {
    unsafe fn map_physical_region<T>(
        &self,
        origin_physical_address: usize,
        size: usize,
    ) -> PhysicalMapping<Self, T> {
        let physical_address = PageSize::Size4K.align_down(origin_physical_address);
        let virtual_address = convert_physical_to_virtual(physical_address);

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

        let pm = PhysicalMemoryAllocOptions::default()
            .count(page_count)
            .contiguous(true)
            .address(physical_address)
            .allocate()
            .unwrap();

        let vm_space = VirtualMemorySpace::new_kernel();
        let _ = vm_space
            .cursor(virtual_address, PageSize::Size4K)
            .unwrap()
            .map(&pm, MMUFlags::READ | MMUFlags::WRITE);

        unsafe {
            PhysicalMapping::new(
                origin_physical_address,
                NonNull::new_unchecked(virtual_address as *mut T),
                size,
                size,
                self.clone(),
            )
        }
    }

    fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}