dev/pci: make PCI driver an external crate
This commit is contained in:
parent
6aa18a1fa2
commit
2da98eaaa8
@ -18,6 +18,9 @@ memtables = { path = "lib/memtables" }
|
|||||||
vmalloc = { path = "lib/vmalloc" }
|
vmalloc = { path = "lib/vmalloc" }
|
||||||
device-api-macros = { path = "lib/device-api/macros" }
|
device-api-macros = { path = "lib/device-api/macros" }
|
||||||
|
|
||||||
|
# Drivers
|
||||||
|
ygg_driver_pci = { path = "driver/bus/pci" }
|
||||||
|
|
||||||
atomic_enum = "0.2.0"
|
atomic_enum = "0.2.0"
|
||||||
bitflags = "2.3.3"
|
bitflags = "2.3.3"
|
||||||
linked_list_allocator = "0.10.5"
|
linked_list_allocator = "0.10.5"
|
||||||
|
16
driver/bus/pci/Cargo.toml
Normal file
16
driver/bus/pci/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "ygg_driver_pci"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||||
|
device-api = { path = "../../../lib/device-api", features = ["derive"] }
|
||||||
|
kernel-util = { path = "../../../lib/kernel-util" }
|
||||||
|
|
||||||
|
log = "0.4.20"
|
||||||
|
bitflags = "2.3.3"
|
||||||
|
|
||||||
|
acpi = { git = "https://github.com/alnyan/acpi.git", package = "acpi", branch = "acpi-system" }
|
@ -1,15 +1,14 @@
|
|||||||
//! PCI capability structures and queries
|
//! PCI capability structures and queries
|
||||||
|
|
||||||
use abi::error::Error;
|
|
||||||
use device_api::interrupt::{MessageInterruptController, MsiHandler};
|
use device_api::interrupt::{MessageInterruptController, MsiHandler};
|
||||||
use kernel_util::mem::{
|
use kernel_util::mem::{
|
||||||
address::{FromRaw, PhysicalAddress},
|
address::{FromRaw, PhysicalAddress},
|
||||||
device::DeviceMemoryIoMut,
|
device::DeviceMemoryIoMut,
|
||||||
};
|
};
|
||||||
|
use yggdrasil_abi::error::Error;
|
||||||
use crate::device::bus::pci::PciBaseAddress;
|
|
||||||
|
|
||||||
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||||
|
use crate::PciBaseAddress;
|
||||||
|
|
||||||
/// MSI-X capability query
|
/// MSI-X capability query
|
||||||
pub struct MsiXCapability;
|
pub struct MsiXCapability;
|
||||||
@ -61,7 +60,7 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
|
|||||||
return Err(Error::InvalidOperation);
|
return Err(Error::InvalidOperation);
|
||||||
};
|
};
|
||||||
|
|
||||||
debugln!("MSI-X table address: {:#x}", base + table_offset);
|
log::debug!("MSI-X table address: {:#x}", base + table_offset);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
DeviceMemoryIoMut::map_slice(PhysicalAddress::from_raw(base + table_offset), table_size)
|
DeviceMemoryIoMut::map_slice(PhysicalAddress::from_raw(base + table_offset), table_size)
|
@ -1,8 +1,12 @@
|
|||||||
//! PCI/PCIe bus interfaces
|
//! PCI/PCIe bus interfaces
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use acpi_lib::mcfg::McfgEntry;
|
use acpi::mcfg::McfgEntry;
|
||||||
use alloc::{boxed::Box, rc::Rc, vec::Vec};
|
use alloc::{rc::Rc, vec::Vec};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use device_api::Device;
|
use device_api::Device;
|
||||||
use kernel_util::{
|
use kernel_util::{
|
||||||
@ -18,8 +22,6 @@ pub use space::{
|
|||||||
ecam::PciEcam, PciConfigSpace, PciConfigurationSpace, PciLegacyConfigurationSpace,
|
ecam::PciEcam, PciConfigSpace, PciConfigurationSpace, PciLegacyConfigurationSpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::device::nvme;
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Command register of the PCI configuration space
|
/// Command register of the PCI configuration space
|
||||||
pub struct PciCommandRegister: u16 {
|
pub struct PciCommandRegister: u16 {
|
||||||
@ -100,10 +102,15 @@ pub struct PciDeviceInfo {
|
|||||||
pub config_space: PciConfigSpace,
|
pub config_space: PciConfigSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interface for "taking" PCI devices from the bus
|
pub enum PciMatch {
|
||||||
pub trait FromPciBus: Sized {
|
Generic(fn(&PciDeviceInfo) -> bool),
|
||||||
/// Constructs an instance of a driver for the device using the information provided
|
Class(u8, Option<u8>, Option<u8>),
|
||||||
fn from_pci_bus(info: &PciDeviceInfo) -> Result<Self, Error>;
|
}
|
||||||
|
|
||||||
|
pub struct PciDriver {
|
||||||
|
name: &'static str,
|
||||||
|
check: PciMatch,
|
||||||
|
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to store PCI bus devices which were enumerated by the kernel
|
/// Used to store PCI bus devices which were enumerated by the kernel
|
||||||
@ -296,28 +303,76 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
|||||||
|
|
||||||
let config = &device.info.config_space;
|
let config = &device.info.config_space;
|
||||||
|
|
||||||
debugln!(
|
log::debug!(
|
||||||
"{}: {:04x}:{:04x}",
|
"{}: {:04x}:{:04x}",
|
||||||
device.info.address,
|
device.info.address,
|
||||||
config.vendor_id(),
|
config.vendor_id(),
|
||||||
config.device_id()
|
config.device_id()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Match by class
|
let class = config.class_code();
|
||||||
match (config.class_code(), config.subclass(), config.prog_if()) {
|
let subclass = config.subclass();
|
||||||
(0x01, 0x08, 0x02) => {
|
let prog_if = config.prog_if();
|
||||||
let dev = Box::leak(Box::new(nvme::NvmeController::from_pci_bus(&device.info)?));
|
|
||||||
|
let drivers = PCI_DRIVERS.lock();
|
||||||
|
for driver in drivers.iter() {
|
||||||
|
if driver
|
||||||
|
.check
|
||||||
|
.check_device(&device.info, class, subclass, prog_if)
|
||||||
|
{
|
||||||
|
// TODO add the device to the bus
|
||||||
|
log::debug!(" -> {:?}", driver.name);
|
||||||
|
let device = (driver.probe)(&device.info)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
dev.init()?;
|
device.init()?;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
_ => {
|
log::debug!(" -> No driver");
|
||||||
debugln!(" -> No driver");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PciMatch {
|
||||||
|
pub fn check_device(&self, info: &PciDeviceInfo, class: u8, subclass: u8, prog_if: u8) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Generic(f) => f(info),
|
||||||
|
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
|
||||||
|
class_ == class && subclass_ == subclass && prog_if_ == prog_if
|
||||||
|
}
|
||||||
|
&Self::Class(class_, Some(subclass_), _) => class_ == class && subclass_ == subclass,
|
||||||
|
&Self::Class(class_, _, _) => class_ == class,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_class_driver(
|
||||||
|
name: &'static str,
|
||||||
|
class: u8,
|
||||||
|
subclass: Option<u8>,
|
||||||
|
prog_if: Option<u8>,
|
||||||
|
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
|
||||||
|
) {
|
||||||
|
PCI_DRIVERS.lock().push(PciDriver {
|
||||||
|
name,
|
||||||
|
check: PciMatch::Class(class, subclass, prog_if),
|
||||||
|
probe,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_generic_driver(
|
||||||
|
name: &'static str,
|
||||||
|
check: fn(&PciDeviceInfo) -> bool,
|
||||||
|
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
|
||||||
|
) {
|
||||||
|
PCI_DRIVERS.lock().push(PciDriver {
|
||||||
|
name,
|
||||||
|
check: PciMatch::Generic(check),
|
||||||
|
probe,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static PCI_DRIVERS: IrqSafeSpinlock<Vec<PciDriver>> = IrqSafeSpinlock::new(Vec::new());
|
||||||
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|
@ -1,6 +1,5 @@
|
|||||||
use crate::device::bus::pci::PciStatusRegister;
|
|
||||||
|
|
||||||
use super::{PciAddress, PciBaseAddress, PciCapability, PciCapabilityId, PciEcam};
|
use super::{PciAddress, PciBaseAddress, PciCapability, PciCapabilityId, PciEcam};
|
||||||
|
use crate::PciStatusRegister;
|
||||||
|
|
||||||
pub(super) mod ecam;
|
pub(super) mod ecam;
|
||||||
|
|
@ -21,6 +21,7 @@ use kernel_util::{
|
|||||||
util::OneTimeInit,
|
util::OneTimeInit,
|
||||||
};
|
};
|
||||||
use yboot_proto::{v1::AvailableMemoryRegion, LoadProtocolV1};
|
use yboot_proto::{v1::AvailableMemoryRegion, LoadProtocolV1};
|
||||||
|
use ygg_driver_pci::PciBusManager;
|
||||||
|
|
||||||
mod acpi;
|
mod acpi;
|
||||||
mod apic;
|
mod apic;
|
||||||
@ -49,8 +50,8 @@ use crate::{
|
|||||||
debug::{self, LogLevel},
|
debug::{self, LogLevel},
|
||||||
device::{
|
device::{
|
||||||
self,
|
self,
|
||||||
bus::pci::PciBusManager,
|
|
||||||
display::{console, fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
|
display::{console, fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
|
||||||
|
nvme,
|
||||||
tty::CombinedTerminal,
|
tty::CombinedTerminal,
|
||||||
},
|
},
|
||||||
fs::{
|
fs::{
|
||||||
@ -384,6 +385,16 @@ impl X86_64 {
|
|||||||
Cpu::init_local(LocalApic::new(), cpu_id as _);
|
Cpu::init_local(LocalApic::new(), cpu_id as _);
|
||||||
|
|
||||||
if cpu_id == 0 {
|
if cpu_id == 0 {
|
||||||
|
// Register the PCI drivers
|
||||||
|
// TODO make this implicit init
|
||||||
|
ygg_driver_pci::register_class_driver(
|
||||||
|
"NVMe Host Controller",
|
||||||
|
0x01,
|
||||||
|
Some(0x08),
|
||||||
|
Some(0x02),
|
||||||
|
nvme::probe,
|
||||||
|
);
|
||||||
|
|
||||||
match self.boot_data.get() {
|
match self.boot_data.get() {
|
||||||
&BootData::YBoot(data) => {
|
&BootData::YBoot(data) => {
|
||||||
let start = PhysicalAddress::from_raw(data.initrd_address);
|
let start = PhysicalAddress::from_raw(data.initrd_address);
|
||||||
|
@ -2,5 +2,3 @@
|
|||||||
|
|
||||||
#[cfg(feature = "device-tree")]
|
#[cfg(feature = "device-tree")]
|
||||||
pub mod simple_bus;
|
pub mod simple_bus;
|
||||||
|
|
||||||
pub mod pci;
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use core::{mem::size_of, time::Duration};
|
use core::{mem::size_of, time::Duration};
|
||||||
|
|
||||||
use abi::error::Error;
|
use abi::error::Error;
|
||||||
use alloc::{collections::BTreeMap, vec::Vec};
|
use alloc::{boxed::Box, collections::BTreeMap, vec::Vec};
|
||||||
use device_api::{interrupt::MsiHandler, Device};
|
use device_api::{interrupt::MsiHandler, Device};
|
||||||
use kernel_util::{
|
use kernel_util::{
|
||||||
mem::{
|
mem::{
|
||||||
@ -17,20 +17,19 @@ use tock_registers::{
|
|||||||
register_bitfields, register_structs,
|
register_bitfields, register_structs,
|
||||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||||
};
|
};
|
||||||
|
use ygg_driver_pci::{
|
||||||
|
capability::{MsiXCapability, MsiXEntry},
|
||||||
|
PciBaseAddress, PciCommandRegister, PciConfigurationSpace, PciDeviceInfo,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{Architecture, ARCHITECTURE},
|
arch::{Architecture, ARCHITECTURE},
|
||||||
device::{
|
device::nvme::{
|
||||||
bus::pci::{
|
command::{
|
||||||
capability::MsiXCapability, PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
IdentifyActiveNamespaceIdListRequest, IdentifyControllerRequest, IoRead, IoWrite,
|
||||||
},
|
|
||||||
nvme::{
|
|
||||||
command::{
|
|
||||||
IdentifyActiveNamespaceIdListRequest, IdentifyControllerRequest, IoRead, IoWrite,
|
|
||||||
},
|
|
||||||
drive::NvmeDrive,
|
|
||||||
queue::{CompletionQueueEntry, SubmissionQueueEntry},
|
|
||||||
},
|
},
|
||||||
|
drive::NvmeDrive,
|
||||||
|
queue::{CompletionQueueEntry, SubmissionQueueEntry},
|
||||||
},
|
},
|
||||||
task::runtime,
|
task::runtime,
|
||||||
};
|
};
|
||||||
@ -41,8 +40,6 @@ use self::{
|
|||||||
queue::QueuePair,
|
queue::QueuePair,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::bus::pci::{capability::MsiXEntry, FromPciBus, PciDeviceInfo};
|
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
mod drive;
|
mod drive;
|
||||||
mod error;
|
mod error;
|
||||||
@ -358,48 +355,51 @@ impl Device for NvmeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromPciBus for NvmeController {
|
// impl FromPciBus for NvmeController {
|
||||||
fn from_pci_bus(info: &PciDeviceInfo) -> Result<Self, Error> {
|
// fn from_pci_bus(info: &PciDeviceInfo) -> Result<Self, Error> {
|
||||||
let PciBaseAddress::Memory(bar0) = info.config_space.bar(0).unwrap() else {
|
// }
|
||||||
panic!();
|
// }
|
||||||
};
|
|
||||||
|
|
||||||
// TODO also support MSI
|
|
||||||
let mut msix = info.config_space.capability::<MsiXCapability>().unwrap();
|
|
||||||
let mut vt = msix.vector_table()?;
|
|
||||||
|
|
||||||
for vector in vt.iter_mut() {
|
|
||||||
vector.set_masked(true);
|
|
||||||
}
|
|
||||||
msix.set_enabled(true);
|
|
||||||
|
|
||||||
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
|
|
||||||
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
|
|
||||||
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
|
|
||||||
info.config_space.set_command(cmd.bits());
|
|
||||||
|
|
||||||
let regs = unsafe { DeviceMemoryIo::<Regs>::map(PhysicalAddress::from_raw(bar0)) }?;
|
|
||||||
|
|
||||||
// Disable the controller
|
|
||||||
regs.CC.modify(CC::ENABLE::CLEAR);
|
|
||||||
|
|
||||||
let doorbell_shift = regs.CAP.read(CAP::DSTRD) as usize + 1;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
regs: IrqSafeSpinlock::new(regs),
|
|
||||||
admin_q: OneTimeInit::new(),
|
|
||||||
ioqs: OneTimeInit::new(),
|
|
||||||
vector_table: IrqSafeSpinlock::new(vt),
|
|
||||||
drive_table: IrqSafeSpinlock::new(BTreeMap::new()),
|
|
||||||
controller_id: OneTimeInit::new(),
|
|
||||||
doorbell_shift,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static NVME_CONTROLLERS: IrqSafeSpinlock<Vec<&'static NvmeController>> =
|
static NVME_CONTROLLERS: IrqSafeSpinlock<Vec<&'static NvmeController>> =
|
||||||
IrqSafeSpinlock::new(Vec::new());
|
IrqSafeSpinlock::new(Vec::new());
|
||||||
|
|
||||||
|
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
|
||||||
|
let PciBaseAddress::Memory(bar0) = info.config_space.bar(0).unwrap() else {
|
||||||
|
panic!();
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO also support MSI
|
||||||
|
let mut msix = info.config_space.capability::<MsiXCapability>().unwrap();
|
||||||
|
let mut vt = msix.vector_table()?;
|
||||||
|
|
||||||
|
for vector in vt.iter_mut() {
|
||||||
|
vector.set_masked(true);
|
||||||
|
}
|
||||||
|
msix.set_enabled(true);
|
||||||
|
|
||||||
|
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
|
||||||
|
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
|
||||||
|
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
|
||||||
|
info.config_space.set_command(cmd.bits());
|
||||||
|
|
||||||
|
let regs = unsafe { DeviceMemoryIo::<Regs>::map(PhysicalAddress::from_raw(bar0)) }?;
|
||||||
|
|
||||||
|
// Disable the controller
|
||||||
|
regs.CC.modify(CC::ENABLE::CLEAR);
|
||||||
|
|
||||||
|
let doorbell_shift = regs.CAP.read(CAP::DSTRD) as usize + 1;
|
||||||
|
|
||||||
|
Ok(Box::leak(Box::new(NvmeController {
|
||||||
|
regs: IrqSafeSpinlock::new(regs),
|
||||||
|
admin_q: OneTimeInit::new(),
|
||||||
|
ioqs: OneTimeInit::new(),
|
||||||
|
vector_table: IrqSafeSpinlock::new(vt),
|
||||||
|
drive_table: IrqSafeSpinlock::new(BTreeMap::new()),
|
||||||
|
controller_id: OneTimeInit::new(),
|
||||||
|
doorbell_shift,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_nvme_controller(ctrl: &'static NvmeController) {
|
pub fn register_nvme_controller(ctrl: &'static NvmeController) {
|
||||||
let mut list = NVME_CONTROLLERS.lock();
|
let mut list = NVME_CONTROLLERS.lock();
|
||||||
let id = list.len();
|
let id = list.len();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user