zodiac 0.3.0

Rust OS framework that offers safe interfaces for kernel development
Documentation
use crate::hal::kernel::ACPI;
use crate::mem::{
    MMUFlags, PageSize, PhysicalAddress, PhysicalMemory, VirtualMemorySpace,
    convert_physical_to_virtual,
};
use acpi::PciConfigRegions;
use alloc::alloc::Global;
use alloc::vec::Vec;
use capability::PciCapability;
use core::fmt::Display;
use core::{fmt, ptr};
use device_type::DeviceType;
use pci_types::*;
use spin::{Lazy, Mutex};
use x86_64::VirtAddr;

pub static PCI_DEVICES: Lazy<Mutex<Vec<PciDevice>>> = Lazy::new(|| {
    let acpi = &ACPI;
    let access = PciAccess::new(&acpi.pci_regions);
    let devices = PciResolver::resolve(access);
    devices.iter().for_each(|device| log::info!("{device}"));
    Mutex::new(devices)
});

pub struct PciAccess<'a>(&'a PciConfigRegions<'a, Global>);

impl<'a> PciAccess<'a> {
    pub fn new(regions: &'a PciConfigRegions<'a, Global>) -> Self {
        Self(regions)
    }

    pub fn mmio_address(&self, address: PciAddress, offset: u16) -> VirtAddr {
        let (segment, bus, device, function) = (
            address.segment(),
            address.bus(),
            address.device(),
            address.function(),
        );

        let physical_address = self
            .0
            .physical_address(segment, bus, device, function)
            .expect("Invalid PCI address")
            + offset as u64;

        let virtual_address = convert_physical_to_virtual(physical_address as PhysicalAddress);

        let page_size = PageSize::Size4K;
        let pm =
            PhysicalMemory::containing_address(physical_address as PhysicalAddress, 1, page_size);
        let _ = VirtualMemorySpace::new_kernel()
            .cursor(page_size.align_down(virtual_address), page_size)
            .unwrap()
            .map(&pm, MMUFlags::KERNEL_DATA);

        VirtAddr::new(virtual_address as u64)
    }
}

#[allow(unsafe_code)]
impl ConfigRegionAccess for PciAccess<'_> {
    unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
        let address = self.mmio_address(address, offset);
        unsafe { ptr::read_volatile(address.as_ptr()) }
    }

    unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
        let address = self.mmio_address(address, offset);
        unsafe { ptr::write_volatile(address.as_mut_ptr(), value) };
    }
}

#[derive(Debug, Clone)]
pub struct PciDevice {
    pub address: PciAddress,
    pub vendor_id: VendorId,
    pub device_id: DeviceId,
    pub interface: Interface,
    pub revision: DeviceRevision,
    pub device_type: DeviceType,
    pub bars: [Option<Bar>; MAX_BARS],
}

impl Display for PciDevice {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}:{}.{}: {:?} [{:04x}:{:04x}] (rev: {:02x})",
            self.address.bus(),
            self.address.device(),
            self.address.function(),
            self.device_type,
            self.vendor_id,
            self.device_id,
            self.revision,
        )
    }
}

pub struct PciResolver<'a> {
    access: PciAccess<'a>,
    devices: Vec<PciDevice>,
}

impl<'a> PciResolver<'a> {
    fn resolve(access: PciAccess<'a>) -> Vec<PciDevice> {
        let mut resolver = Self {
            access,
            devices: Vec::new(),
        };

        for region in resolver.access.0.iter() {
            resolver.scan_segment(region.segment_group);
        }

        resolver.devices
    }

    fn scan_segment(&mut self, segment: u16) {
        self.scan_bus(segment, 0);

        let address = PciAddress::new(segment, 0, 0, 0);
        if PciHeader::new(address).has_multiple_functions(&self.access) {
            (1..8).for_each(|i| self.scan_bus(segment, i));
        }
    }

    fn scan_bus(&mut self, segment: u16, bus: u8) {
        (0..32).for_each(|device| {
            let address = PciAddress::new(segment, bus, device, 0);
            self.scan_function(segment, bus, device, 0);

            let header = PciHeader::new(address);
            if header.has_multiple_functions(&self.access) {
                (1..8).for_each(|function| {
                    self.scan_function(segment, bus, device, function);
                });
            }
        });
    }

    fn scan_function(&mut self, segment: u16, bus: u8, device: u8, function: u8) {
        let address = PciAddress::new(segment, bus, device, function);
        let header = PciHeader::new(address);

        let (vendor_id, device_id) = header.id(&self.access);
        let (revision, class, sub_class, interface) = header.revision_and_class(&self.access);

        if vendor_id == 0xffff {
            return;
        }

        let endpoint_bars = |header: &EndpointHeader| {
            let mut bars = [None; 6];
            let mut skip_next = false;

            for (index, bar_slot) in bars.iter_mut().enumerate() {
                if skip_next {
                    skip_next = false;
                    continue;
                }
                let bar = header.bar(index as u8, &self.access);
                if let Some(Bar::Memory64 { .. }) = bar {
                    skip_next = true;
                }
                *bar_slot = bar;
            }

            bars
        };

        match header.header_type(&self.access) {
            HeaderType::Endpoint => {
                let mut endpoint_header = EndpointHeader::from_header(header, &self.access)
                    .expect("Invalid endpoint header");

                let bars = endpoint_bars(&endpoint_header);
                let device_type = DeviceType::from((class, sub_class));

                endpoint_header.capabilities(&self.access).for_each(
                    |capability| match capability {
                        PciCapability::Msi(msi) => {
                            msi.set_enabled(true, &self.access);
                        }
                        PciCapability::MsiX(mut msix) => {
                            msix.set_enabled(true, &self.access);
                        }
                        _ => {}
                    },
                );

                endpoint_header.update_command(&self.access, |command| {
                    command
                        | CommandRegister::BUS_MASTER_ENABLE
                        | CommandRegister::IO_ENABLE
                        | CommandRegister::MEMORY_ENABLE
                });

                let device = PciDevice {
                    address,
                    vendor_id,
                    device_id,
                    interface,
                    device_type,
                    revision,
                    bars,
                };

                self.devices.push(device);
            }
            HeaderType::PciPciBridge => {
                let bridge_header = PciPciBridgeHeader::from_header(header, &self.access)
                    .expect("Invalid PCI-PCI bridge header");

                let start_bus = bridge_header.secondary_bus_number(&self.access);
                let end_bus = bridge_header.subordinate_bus_number(&self.access);
                (start_bus..=end_bus).for_each(|bus_id| self.scan_bus(segment, bus_id));
            }
            _ => {}
        }
    }
}