Compare commits

...

1 Commits

Author SHA1 Message Date
alnyan 7107c03488 WIP 2025-07-28 17:32:22 +03:00
19 changed files with 67 additions and 2750 deletions
Generated
+1 -201
View File
@@ -38,12 +38,6 @@ dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "accessor"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd8b2abd55bf1f9cffbf00fd594566c51a9d31402553284920c1309ca8351086"
[[package]]
name = "acpi"
version = "4.1.1"
@@ -649,7 +643,7 @@ checksum = "581d3afdd654deb68c19fcbe4bc411910cc64067d4a13d8637bda7722cb9c2ea"
dependencies = [
"endian-type-rs",
"fallible-iterator",
"memoffset 0.5.6",
"memoffset",
"num-derive",
"num-traits",
"rustc_version 0.2.3",
@@ -1419,15 +1413,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.8.4"
@@ -1512,12 +1497,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -2561,18 +2540,6 @@ dependencies = [
"rustix",
]
[[package]]
name = "xhci"
version = "0.9.2"
source = "git+https://github.com/rust-osdev/xhci.git#f2254c86d5ba12754259bf0ae483c6c8dc821aad"
dependencies = [
"accessor",
"bit_field",
"num-derive",
"num-traits",
"paste",
]
[[package]]
name = "xtask"
version = "0.1.0"
@@ -2618,26 +2585,6 @@ dependencies = [
"rsdp",
]
[[package]]
name = "ygg_driver_ahci"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"futures-util",
"kernel-fs",
"libk",
"libk-mm",
"libk-util",
"log",
"memoffset 0.9.1",
"static_assertions",
"tock-registers",
"ygg_driver_pci",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_fat32"
version = "0.1.0"
@@ -2680,21 +2627,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_igbe"
version = "0.1.0"
dependencies = [
"device-api",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_net_core",
"ygg_driver_pci",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_loopback"
version = "0.1.0"
@@ -2708,22 +2640,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_rtl81xx"
version = "0.1.0"
dependencies = [
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_net_core",
"ygg_driver_pci",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_stmmac"
version = "0.1.0"
@@ -2741,26 +2657,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_nvme"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"futures-util",
"kernel-arch",
"kernel-fs",
"libk",
"libk-mm",
"libk-util",
"log",
"static_assertions",
"tock-registers",
"ygg_driver_pci",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_pci"
version = "0.1.0"
@@ -2808,94 +2704,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_usb_xhci"
version = "0.1.0"
dependencies = [
"async-trait",
"atomic_enum",
"bytemuck",
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"xhci",
"ygg_driver_pci",
"ygg_driver_usb",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_virtio_blk"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_pci",
"ygg_driver_virtio_core",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_virtio_core"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"device-api",
"kernel-arch-hosted",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_pci",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_virtio_gpu"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"libk",
"libk-mm",
"libk-util",
"log",
"ygg_driver_pci",
"ygg_driver_virtio_core",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_virtio_net"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"bytemuck",
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_net_core",
"ygg_driver_pci",
"ygg_driver_virtio_core",
"yggdrasil-abi",
]
[[package]]
name = "yggdrasil-abi"
version = "0.1.0"
@@ -2950,21 +2758,13 @@ dependencies = [
"vmalloc",
"yboot-proto",
"ygg_driver_acpi",
"ygg_driver_ahci",
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_net_core",
"ygg_driver_net_igbe",
"ygg_driver_net_loopback",
"ygg_driver_net_rtl81xx",
"ygg_driver_net_stmmac",
"ygg_driver_nvme",
"ygg_driver_pci",
"ygg_driver_usb",
"ygg_driver_usb_xhci",
"ygg_driver_virtio_blk",
"ygg_driver_virtio_gpu",
"ygg_driver_virtio_net",
"yggdrasil-abi",
]
+9 -9
View File
@@ -23,18 +23,18 @@ vmalloc.workspace = true
kernel-arch.workspace = true
# Drivers
ygg_driver_input = { path = "driver/input" }
ygg_driver_pci = { path = "driver/bus/pci" }
ygg_driver_usb = { path = "driver/bus/usb" }
ygg_driver_net_core = { path = "driver/net/core" }
ygg_driver_net_loopback = { path = "driver/net/loopback" }
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
ygg_driver_virtio_gpu = { path = "driver/virtio/gpu", features = ["pci"] }
ygg_driver_virtio_blk = { path = "driver/virtio/blk", features = ["pci"] }
ygg_driver_nvme = { path = "driver/block/nvme" }
ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_input = { path = "driver/input" }
ygg_driver_usb_xhci.path = "driver/usb/xhci"
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
# ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
# ygg_driver_virtio_gpu = { path = "driver/virtio/gpu", features = ["pci"] }
# ygg_driver_virtio_blk = { path = "driver/virtio/blk", features = ["pci"] }
# ygg_driver_nvme = { path = "driver/block/nvme" }
# ygg_driver_ahci = { path = "driver/block/ahci" }
# ygg_driver_usb_xhci.path = "driver/usb/xhci"
# ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
memfs = { path = "driver/fs/memfs" }
ext2 = { path = "driver/fs/ext2" }
@@ -68,7 +68,7 @@ kernel-arch-x86_64.workspace = true
kernel-arch-x86.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_net_igbe.path = "driver/net/igbe"
# ygg_driver_net_igbe.path = "driver/net/igbe"
acpi.workspace = true
-613
View File
@@ -1,613 +0,0 @@
//! PCI capability structures and queries
use alloc::{sync::Arc, vec, vec::Vec};
use bitflags::bitflags;
use device_api::interrupt::{
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use tock_registers::{
interfaces::{Readable, Writeable},
registers::{ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
use crate::PciBaseAddress;
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
bitflags! {
pub struct PcieLinkControl: u16 {
const ASPM_DISABLE = 0 << 0;
// Active state power management control
const ASPM_MASK = 0x3 << 0;
// Enable clock power management
const ECPM = 1 << 8;
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
use core::mem::offset_of;
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
use kernel_arch_x86::intrinsics;
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
fn from_space_offset(space: &'s S, offset: usize) -> Self;
fn space(&self) -> &'s S;
fn offset(&self) -> usize;
fn bar_index(&self) -> Option<usize> {
let value = self.space().read_u8(self.offset() + 4);
(value <= 0x5).then_some(value as _)
}
fn bar_offset(&self) -> usize {
let value = self.space().read_u32(self.offset() + 8);
value as _
}
fn length(&self) -> usize {
let value = self.space().read_u32(self.offset() + 12);
value as _
}
}
pub trait VirtioCapability {
const CFG_TYPE: u8;
const MIN_LEN: usize = 0;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
}
/// Power management capability entry
pub struct PowerManagementCapability;
/// MSI-X capability query
pub struct MsiXCapability;
/// MSI capability query
pub struct MsiCapability;
/// PCIe capability
pub struct PciExpressCapability;
// VirtIO-over-PCI capabilities
/// VirtIO PCI configuration access
pub struct VirtioDeviceConfigCapability;
/// VirtIO common configuration
pub struct VirtioCommonConfigCapability;
/// VirtIO notify configuration
pub struct VirtioNotifyConfigCapability;
/// VirtIO interrupt status
pub struct VirtioInterruptStatusCapability;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DevicePowerState {
D0,
D1,
D2,
D3Cold,
D3Hot,
}
/// Represents an entry in MSI-X vector table
#[repr(C)]
pub struct MsiXEntry {
/// Address to which the value is written on interrupt
pub address: WriteOnly<u64>,
/// Value which is written to trigger an interrupt
pub data: WriteOnly<u32>,
/// Vector control word
pub control: ReadWrite<u32>,
}
enum MsiXVectorTableAccess<'a> {
Memory(DeviceMemoryIoMut<'a, [MsiXEntry]>),
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
Io(u16),
}
pub struct MsiXVectorTable<'a> {
access: MsiXVectorTableAccess<'a>,
len: usize,
}
/// PCI Power Management capability data structure
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
/// MSI-X capability data structure
pub struct MsiXData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
/// MSI capability data structure
pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
/// PCI Express capability data structure
pub struct PcieData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
impl<T: VirtioCapability> PciCapability for T {
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
let cfg_type = space.read_u8(offset + 3);
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
}
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
T::Output::from_space_offset(space, offset)
}
}
impl PciCapability for PowerManagementCapability {
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PowerManagementData { space, offset }
}
}
impl PciCapability for MsiXCapability {
const ID: PciCapabilityId = PciCapabilityId::MsiX;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
MsiXData { space, offset }
}
}
impl PciCapability for MsiCapability {
const ID: PciCapabilityId = PciCapabilityId::Msi;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
MsiData { space, offset }
}
}
impl PciCapability for PciExpressCapability {
const ID: PciCapabilityId = PciCapabilityId::PciExpress;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PcieData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PcieData { space, offset }
}
}
impl VirtioCapability for VirtioDeviceConfigCapability {
const CFG_TYPE: u8 = 0x04;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioDeviceConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioCommonConfigCapability {
const CFG_TYPE: u8 = 0x01;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioCommonConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioNotifyConfigCapability {
const CFG_TYPE: u8 = 0x02;
const MIN_LEN: usize = 0x14;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
pub fn offset_multiplier(&self) -> usize {
self.space.read_u32(self.offset + 16) as usize
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioNotifyConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioInterruptStatusCapability {
const CFG_TYPE: u8 = 0x03;
const MIN_LEN: usize = 1;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> {
pub fn read_status(&self) -> (bool, bool) {
todo!()
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioInterruptStatusData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
pub fn set_device_power_state(&self, state: DevicePowerState) {
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
let current = self.get_device_power_state();
if state == current {
return;
}
log::info!("Set device power state: {state:?}");
match state {
DevicePowerState::D0 => {
// power = 0b00 | PME_EN
self.space.write_u16(self.offset + 4, pmcsr);
}
_ => {
log::warn!("TODO: {state:?} power state");
}
}
}
pub fn set_pme_en(&self, state: bool) {
let pmcsr = self.space.read_u16(self.offset + 4);
let new = if state {
pmcsr | (1 << 8)
} else {
pmcsr & !(1 << 8)
};
if pmcsr == new {
return;
}
log::info!("Set PMCSR.PME_En = {state}");
self.space.write_u16(self.offset + 4, new);
}
pub fn get_device_power_state(&self) -> DevicePowerState {
let pmcsr = self.space.read_u16(self.offset + 4);
match pmcsr & 0x3 {
0b00 => DevicePowerState::D0,
0b01 => DevicePowerState::D1,
0b10 => DevicePowerState::D2,
0b11 => DevicePowerState::D3Hot,
_ => unreachable!(),
}
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
// TODO use pending bits as well
/// Maps and returns the vector table associated with the device's MSI-X capability
pub fn vector_table<'a>(&self) -> Result<MsiXVectorTable<'a>, Error> {
let w0 = self.space.read_u16(self.offset + 2);
let dw1 = self.space.read_u32(self.offset + 4);
let table_size = (w0 as usize & 0x3FF) + 1;
let bir = dw1 as usize & 0x3;
let table_offset = dw1 as usize & !0x3;
let Some(base) = self.space.bar(bir) else {
return Err(Error::DoesNotExist);
};
match base {
PciBaseAddress::Memory32(mem32) => unsafe {
log::info!("MSI-X table address: {:#x}", mem32 + table_offset as u32);
MsiXVectorTable::memory_from_raw_parts(
PhysicalAddress::from_u32(mem32).add(table_offset),
table_size,
)
},
PciBaseAddress::Memory64(mem64) => unsafe {
log::info!("MSI-X table address: {:#x}", mem64 + table_offset as u64);
MsiXVectorTable::memory_from_raw_parts(
PhysicalAddress::from_u64(mem64).add(table_offset),
table_size,
)
},
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
PciBaseAddress::Io(io) => unsafe {
log::info!("MSI-X table I/O: {:#x}", io + table_offset as u16);
MsiXVectorTable::io_from_raw_parts(io + table_offset as u16, table_size)
},
#[cfg(any(not(any(target_arch = "x86", target_arch = "x86_64")), rust_analyzer))]
PciBaseAddress::Io(_) => Err(Error::DoesNotExist),
}
}
/// Changes the global enable status for the device's MSI-X capability. If set, regular IRQs
/// are not generated.
pub fn set_enabled(&mut self, enabled: bool) {
let mut w0 = self.space.read_u32(self.offset);
if enabled {
w0 |= 1 << 31;
} else {
w0 &= !(1 << 31);
}
self.space.write_u32(self.offset, w0);
}
pub fn set_function_mask(&mut self, masked: bool) {
let mut w0 = self.space.read_u32(self.offset);
if masked {
w0 |= 1 << 30;
} else {
w0 &= !(1 << 30);
}
self.space.write_u32(self.offset, w0);
}
}
impl MsiXVectorTableAccess<'_> {
fn set_vector_masked(&mut self, vector: usize, masked: bool) {
let old = self.read_control(vector);
let new = if masked { old | 1 } else { old & !1 };
if old != new {
self.write_control(vector, new);
}
}
fn read_control(&mut self, vector: usize) -> u32 {
match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
&mut Self::Io(base) => unsafe {
let a = base
+ (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, control)) as u16;
intrinsics::inl(a)
},
Self::Memory(vectors) => vectors[vector].control.get(),
}
}
fn write_address(&mut self, vector: usize, value: u64) {
match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
&mut Self::Io(base) => unsafe {
let a = base + (vector * size_of::<MsiXEntry>()) as u16;
intrinsics::outl(a, value as u32);
intrinsics::outl(a + 4, (value >> 32) as u32);
},
Self::Memory(vectors) => vectors[vector].address.set(value),
}
}
fn write_data(&mut self, vector: usize, value: u32) {
match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
&mut Self::Io(base) => unsafe {
let a =
base + (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, data)) as u16;
intrinsics::outl(a, value)
},
Self::Memory(vectors) => vectors[vector].data.set(value),
}
}
fn write_control(&mut self, vector: usize, value: u32) {
match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
&mut Self::Io(base) => unsafe {
let a = base
+ (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, control)) as u16;
intrinsics::outl(a, value)
},
Self::Memory(vectors) => vectors[vector].control.set(value),
}
}
}
impl MsiXVectorTable<'_> {
unsafe fn memory_from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
let vectors = DeviceMemoryIoMut::map_slice(base, len, Default::default())?;
Ok(Self {
access: MsiXVectorTableAccess::Memory(vectors),
len,
})
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
unsafe fn io_from_raw_parts(base: u16, len: usize) -> Result<Self, Error> {
Ok(Self {
access: MsiXVectorTableAccess::Io(base),
len,
})
}
pub fn mask_all(&mut self) {
for i in 0..self.len {
self.access.set_vector_masked(i, true);
}
}
pub fn register_range(
&mut self,
start: usize,
end: usize,
ic: &Arc<dyn MessageInterruptController>,
affinity: InterruptAffinity,
handler: Arc<dyn InterruptHandler>,
) -> Result<Vec<MsiInfo>, Error> {
assert!(end > start);
let mut range = vec![
MsiInfo {
affinity,
..Default::default()
};
end - start
];
ic.clone().register_msi_range(&mut range, handler)?;
for (i, info) in range.iter().enumerate() {
let index = i + start;
self.access.write_address(index, info.address as _);
self.access.write_data(index, info.value);
self.access.set_vector_masked(index, false);
}
Ok(range)
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
pub fn set_enabled(&mut self, enabled: bool) {
let mut w0 = self.space.read_u16(self.offset + 2);
if enabled {
w0 |= 1 << 0;
} else {
w0 &= !(1 << 0);
}
self.space.write_u16(self.offset + 2, w0);
}
pub fn register(
&mut self,
ic: &Arc<dyn MessageInterruptController>,
affinity: InterruptAffinity,
handler: Arc<dyn InterruptHandler>,
) -> Result<MsiInfo, Error> {
let info = ic.clone().register_msi(affinity, handler)?;
let mut w0 = self.space.read_u16(self.offset + 2);
// Enable the vector first
w0 |= 1 << 0;
// Reset to one vector
w0 &= !(0x7 << 4);
self.space.write_u16(self.offset + 2, w0);
if info.value > u16::MAX as u32 {
log::warn!("Could not setup a MSI: value={:#x} > u16", info.value);
return Err(Error::InvalidOperation);
}
if info.address > u32::MAX as usize {
if w0 & (1 << 7) == 0 {
log::warn!(
"Could not setup a MSI: address={:#x} and MSI is not 64 bit capable",
info.address
);
return Err(Error::InvalidOperation);
}
todo!("FIXME: PCI 64-bit addresses");
// self.space
// .write_u32(self.offset + 8, (info.address >> 32) as u32);
}
self.space.write_u32(self.offset + 4, info.address as u32);
self.space.write_u16(self.offset + 12, info.value as u16);
Ok(info)
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PcieData<'s, S> {
pub fn link_control(&self) -> PcieLinkControl {
PcieLinkControl::from_bits_retain(self.space.read_u16(self.offset + 0x10))
}
pub fn set_link_control(&mut self, value: PcieLinkControl) {
self.space.write_u16(self.offset + 0x10, value.bits());
}
}
-325
View File
@@ -1,325 +0,0 @@
use core::ops::Range;
use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::Device,
interrupt::{
ExternalInterruptController, InterruptAffinity, InterruptHandler, Irq, IrqOptions,
MessageInterruptController, MsiInfo,
},
};
use libk::device::external_interrupt_controller;
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use yggdrasil_abi::error::Error;
use crate::{
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
driver::PciDriver,
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
};
/// Describes a PCI device
#[derive(Clone)]
pub struct PciDeviceInfo {
/// Address of the device
pub address: PciAddress,
/// Class field of the configuration space
pub class: u8,
/// Subclass field of the configuration space
pub subclass: u8,
/// Prog IF field of the configuration space
pub prog_if: u8,
/// Vendor ID field of the configuration space
pub vendor_id: u16,
/// Device ID field of the configuration space
pub device_id: u16,
/// Configuration space access method
pub config_space: PciConfigSpace,
/// Describes the PCI segment this device is a part of
pub segment: Arc<PciSegmentInfo>,
pub(crate) interrupt_config: Arc<OneTimeInit<IrqSafeRwLock<InterruptConfig>>>,
}
pub struct InterruptConfig {
#[allow(unused)]
preferred_mode: PreferredInterruptMode,
configured_mode: ConfiguredInterruptMode,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum PciInterruptPin {
A,
B,
C,
D,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PreferredInterruptMode {
Msi(bool),
Legacy,
}
enum ConfiguredInterruptMode {
MsiX(
Arc<dyn MessageInterruptController>,
MsiXVectorTable<'static>,
),
Msi(Arc<dyn MessageInterruptController>),
LegacyPin(Arc<dyn ExternalInterruptController>, PciInterruptPin),
#[cfg_attr(not(target_arch = "x86"), allow(unused))]
LegacyLine(Arc<dyn ExternalInterruptController>, u8),
None,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct PciInterrupt {
pub address: PciAddress,
pub pin: PciInterruptPin,
}
#[derive(Clone, Copy, Debug)]
pub struct PciInterruptRoute {
pub number: u32,
pub options: IrqOptions,
}
#[derive(Clone)]
pub struct PciMsiRoute {
// TODO `msi-base`
pub controller: Arc<dyn MessageInterruptController>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PciDeviceState {
None,
Probed,
Initialized,
Failed,
}
/// Used to store PCI bus devices which were enumerated by the kernel
pub struct PciBusDevice {
pub(crate) info: PciDeviceInfo,
pub(crate) device: Option<Arc<dyn Device>>,
pub(crate) driver: Option<&'static dyn PciDriver>,
pub(crate) state: PciDeviceState,
}
impl PciDeviceInfo {
pub fn set_command(
&self,
enable_irq: bool,
enable_mem: bool,
enable_io: bool,
enable_bus_master: bool,
) {
let command = PciCommandRegister::from_bits_retain(self.config_space.command());
let mut new = command;
if enable_irq {
new &= !PciCommandRegister::DISABLE_INTERRUPTS;
} else {
new |= PciCommandRegister::DISABLE_INTERRUPTS;
}
if enable_mem {
new |= PciCommandRegister::ENABLE_MEMORY;
} else {
new &= !PciCommandRegister::ENABLE_MEMORY;
}
if enable_io {
new |= PciCommandRegister::ENABLE_IO;
} else {
new &= !PciCommandRegister::ENABLE_IO;
}
if enable_bus_master {
new |= PciCommandRegister::BUS_MASTER;
} else {
new &= !PciCommandRegister::BUS_MASTER;
}
if new != command {
self.config_space.set_command(new.bits());
}
}
pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> {
self.interrupt_config
.try_init_with(|| {
let configured_mode = if let PreferredInterruptMode::Msi(want_msix) = preferred_mode
&& let Some(msi_route) = self.segment.msi_translation_map.map_msi(self.address)
{
// Try to setup MSI (or MSI-x, if requested)
let mut result = None;
if want_msix
&& let Some(mut msix) = self.config_space.capability::<MsiXCapability>()
{
if let Ok(mut vt) = msix.vector_table() {
if let Some(mut msi) = self.config_space.capability::<MsiCapability>() {
msi.set_enabled(false);
}
vt.mask_all();
msix.set_function_mask(false);
msix.set_enabled(true);
result = Some(ConfiguredInterruptMode::MsiX(
msi_route.controller.clone(),
vt,
));
}
}
// Fall back to MSI if MSI-x is not available or not requested
if result.is_none() && self.config_space.capability::<MsiCapability>().is_some()
{
result = Some(ConfiguredInterruptMode::Msi(msi_route.controller));
}
// Fall back to legacy IRQ if nothing else works
if let Some(result) = result {
result
} else {
self.legacy_interrupt_mode()
}
} else {
// MSI not requested or segment does not have MSI functionality
self.legacy_interrupt_mode()
};
IrqSafeRwLock::new(InterruptConfig {
preferred_mode,
configured_mode,
})
})
.expect("Possible bug: double-initialization of PCI(e) interrupt config");
Ok(())
}
fn legacy_interrupt_mode(&self) -> ConfiguredInterruptMode {
let Ok(intc) = external_interrupt_controller() else {
return ConfiguredInterruptMode::None;
};
// TODO this should be retrieved from interrupt map
#[cfg(any(target_arch = "x86", rust_analyzer))]
{
if let Some(irq) = self.config_space.interrupt_line() {
return ConfiguredInterruptMode::LegacyLine(intc.clone(), irq);
}
}
match self.config_space.interrupt_pin() {
Some(pin) => ConfiguredInterruptMode::LegacyPin(intc.clone(), pin),
None => ConfiguredInterruptMode::None,
}
}
pub fn map_interrupt(
&self,
affinity: InterruptAffinity,
handler: Arc<dyn InterruptHandler>,
) -> Result<Option<MsiInfo>, Error> {
let mut irq = self.interrupt_config.get().write();
match &mut irq.configured_mode {
ConfiguredInterruptMode::Msi(controller) => {
let mut msi = self
.config_space
.capability::<MsiCapability>()
.ok_or(Error::InvalidOperation)?;
let info = msi.register(controller, affinity, handler)?;
Ok(Some(info))
}
ConfiguredInterruptMode::MsiX(controller, msix) => {
let info = msix.register_range(0, 1, controller, affinity, handler)?;
Ok(Some(info[0]))
}
ConfiguredInterruptMode::LegacyPin(intc, pin) => {
self.try_map_legacy(intc.as_ref(), *pin, handler)?;
Ok(None)
}
ConfiguredInterruptMode::LegacyLine(intc, irq) => {
self.try_map_legacy_line(intc.as_ref(), *irq, handler)?;
Ok(None)
}
ConfiguredInterruptMode::None => Err(Error::InvalidOperation),
}
}
pub fn map_interrupt_multiple(
&self,
vector_range: Range<usize>,
affinity: InterruptAffinity,
handler: Arc<dyn InterruptHandler>,
) -> Result<Vec<MsiInfo>, Error> {
let mut irq = self.interrupt_config.get().write();
let start = vector_range.start;
let end = vector_range.end;
match &mut irq.configured_mode {
ConfiguredInterruptMode::MsiX(controller, msix) => {
msix.register_range(start, end, controller, affinity, handler)
}
_ => Err(Error::InvalidOperation),
}
}
fn try_map_legacy(
&self,
intc: &dyn ExternalInterruptController,
pin: PciInterruptPin,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let src = PciInterrupt {
address: self.address,
pin,
};
let route = self
.segment
.irq_translation_map
.map_interrupt(&src)
.inspect_err(|e| log::warn!("Could not map PCI IRQ {pin:?}: {e:?}"))?;
log::debug!(
"PCI {} pin {:?} -> system IRQ #{}",
src.address,
src.pin,
route.number
);
let irq = Irq::External(route.number);
intc.register_irq(irq, route.options, handler)?;
intc.enable_irq(irq)
}
fn try_map_legacy_line(
&self,
intc: &dyn ExternalInterruptController,
line: u8,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
log::debug!("PCI {} -> IRQ#{}", self.address, line);
let irq = Irq::External(line as u32);
intc.register_irq(irq, Default::default(), handler)?;
intc.enable_irq(irq)
}
}
impl TryFrom<u32> for PciInterruptPin {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::A),
2 => Ok(Self::B),
3 => Ok(Self::C),
4 => Ok(Self::D),
_ => Err(()),
}
}
}
-63
View File
@@ -1,63 +0,0 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::{device::Device, dma::DmaAllocator};
use libk::error::Error;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use crate::device::PciDeviceInfo;
pub enum PciMatch {
Generic(fn(&PciDeviceInfo) -> bool),
Vendor(u16, u16),
Class(u8, Option<u8>, Option<u8>),
}
pub struct PciDriverMatch {
pub driver: &'static dyn PciDriver,
pub check: PciMatch,
}
pub trait PciDriver: Sync {
fn probe(
&self,
info: &PciDeviceInfo,
dma: &Arc<dyn DmaAllocator>,
) -> Result<Arc<dyn Device>, Error>;
fn driver_name(&self) -> &str;
}
impl PciMatch {
pub fn check_device(&self, info: &PciDeviceInfo) -> bool {
match self {
Self::Generic(f) => f(info),
&Self::Vendor(vendor_, device_) => {
info.vendor_id == vendor_ && info.device_id == device_
}
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
class_ == info.class && subclass_ == info.subclass && prog_if_ == info.prog_if
}
&Self::Class(class_, Some(subclass_), _) => {
class_ == info.class && subclass_ == info.subclass
}
&Self::Class(class_, _, _) => class_ == info.class,
}
}
}
pub fn register_match(pmatch: PciMatch, driver: &'static dyn PciDriver) {
DRIVERS.write().push(PciDriverMatch {
check: pmatch,
driver,
});
}
pub fn lookup_driver(info: &PciDeviceInfo) -> Option<&'static dyn PciDriver> {
DRIVERS.read().iter().find_map(|pmatch| {
if pmatch.check.check_device(info) {
Some(pmatch.driver)
} else {
None
}
})
}
static DRIVERS: IrqSafeRwLock<Vec<PciDriverMatch>> = IrqSafeRwLock::new(Vec::new());
-141
View File
@@ -1,141 +0,0 @@
use core::fmt;
use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
use device_api::interrupt::MessageInterruptController;
use libk::error::Error;
use crate::{
device::{PciInterrupt, PciInterruptRoute, PciMsiRoute},
PciAddress,
};
#[derive(Debug)]
pub enum PciInterruptMap {
Fixed(BTreeMap<PciInterrupt, PciInterruptRoute>),
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
Acpi(alloc::string::String),
Legacy,
}
// TODO device-tree also provides a "msi-base" value, which is ignored and assumed to be zero for
// now
pub struct PciFixedMsiMapping {
pub start_address: PciAddress,
pub end_address: PciAddress,
pub controller: Arc<dyn MessageInterruptController>,
}
pub struct PciFixedMsiMap {
pub entries: Vec<PciFixedMsiMapping>,
}
pub enum PciMsiMap {
Fixed(PciFixedMsiMap),
Identity(Arc<dyn MessageInterruptController>),
Legacy,
}
impl PciInterruptMap {
pub fn map_interrupt(&self, interrupt: &PciInterrupt) -> Result<PciInterruptRoute, Error> {
match self {
Self::Fixed(map) => map.get(interrupt).cloned().ok_or(Error::DoesNotExist),
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
Self::Acpi(aml_object_name) => {
use device_api::interrupt::{IrqLevel, IrqOptions, IrqTrigger};
use crate::device::PciInterruptPin;
let aml_pin = match interrupt.pin {
PciInterruptPin::A => ygg_driver_acpi::PciPin::IntA,
PciInterruptPin::B => ygg_driver_acpi::PciPin::IntB,
PciInterruptPin::C => ygg_driver_acpi::PciPin::IntC,
PciInterruptPin::D => ygg_driver_acpi::PciPin::IntD,
};
let aml_route = ygg_driver_acpi::get_pci_route(
aml_object_name.as_str(),
interrupt.address.device as u16,
interrupt.address.function as u16,
aml_pin,
)
.or_else(|| {
ygg_driver_acpi::get_pci_route(
aml_object_name.as_str(),
interrupt.address.device as u16,
0xFFFF,
aml_pin,
)
})
.ok_or(Error::DoesNotExist)?;
let trigger = match aml_route.trigger {
ygg_driver_acpi::InterruptTrigger::Edge => IrqTrigger::Edge,
ygg_driver_acpi::InterruptTrigger::Level => IrqTrigger::Level,
};
let level = match aml_route.polarity {
ygg_driver_acpi::InterruptPolarity::ActiveLow => IrqLevel::ActiveLow,
ygg_driver_acpi::InterruptPolarity::ActiveHigh => IrqLevel::ActiveHigh,
};
Ok(PciInterruptRoute {
options: IrqOptions { trigger, level },
number: aml_route.irq,
})
}
Self::Legacy => todo!(),
}
}
}
impl PciMsiMap {
pub fn map_msi(&self, address: PciAddress) -> Option<PciMsiRoute> {
match self {
Self::Fixed(map) => map.map_msi(address),
Self::Identity(controller) => Some(PciMsiRoute {
controller: controller.clone(),
}),
Self::Legacy => None,
}
}
}
impl fmt::Debug for PciMsiMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Legacy => f.debug_struct("Legacy").finish(),
Self::Fixed(map) => f
.debug_struct("Fixed")
.field("entries", &map.entries)
.finish(),
Self::Identity(_) => f.debug_struct("Identity").finish(),
}
}
}
impl PciFixedMsiMap {
pub fn map_msi(&self, address: PciAddress) -> Option<PciMsiRoute> {
for entry in self.entries.iter() {
if entry.contains(address) {
let route = PciMsiRoute {
controller: entry.controller.clone(),
};
return Some(route);
}
}
None
}
}
impl PciFixedMsiMapping {
pub fn contains(&self, address: PciAddress) -> bool {
self.start_address <= address && self.end_address > address
}
}
impl fmt::Debug for PciFixedMsiMapping {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PciFixedMsiMapping")
.field("start_address", &self.start_address)
.field("end_address", &self.end_address)
.finish()
}
}
+11 -675
View File
@@ -1,684 +1,20 @@
//! PCI/PCIe bus interfaces
#![no_std]
#![feature(let_chains, decl_macro)]
#![allow(clippy::missing_transmute_annotations, clippy::identity_op)]
extern crate alloc;
use core::fmt;
use alloc::sync::Arc;
use device_api::device::Device;
use libk_util::OneTimeInit;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
use acpi::mcfg::McfgEntry;
use alloc::{format, sync::Arc, vec::Vec};
pub struct PciEndpointDevice {}
use bitflags::bitflags;
use device::{PciBusDevice, PciDeviceInfo, PciDeviceState};
use device_api::{device::DeviceInitContext, dma::DmaAllocator};
use interrupt::{PciInterruptMap, PciMsiMap};
use libk::{
dma::DummyDmaAllocator,
fs::sysfs::{self, object::KObject},
};
use libk_mm::address::PhysicalAddress;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use space::legacy;
use yggdrasil_abi::{error::Error, primitive_enum};
#[cfg(target_arch = "x86_64")]
use device_api::interrupt::MessageInterruptController;
pub mod capability;
pub mod device;
pub mod driver;
pub mod interrupt;
pub mod macros;
mod nodes;
mod space;
pub use space::{
ecam::PciEcam,
legacy::{LegacyPciAccess, PciLegacyConfigurationSpace},
PciConfigSpace, PciConfigurationSpace,
};
bitflags! {
/// Command register of the PCI configuration space
#[derive(PartialEq, Clone, Copy)]
pub struct PciCommandRegister: u16 {
/// If set, I/O access to the device is enabled
const ENABLE_IO = 1 << 0;
/// If set, memory-mapped access to the device is enabled
const ENABLE_MEMORY = 1 << 1;
/// If set, the device can generate PCI bus accesses on its own
const BUS_MASTER = 1 << 2;
/// If set, interrupts are masked from being raised
const DISABLE_INTERRUPTS = 1 << 10;
}
pub enum PciEndpointImpl {
Device(OneTimeInit<PciEndpointDevice>),
Bridge(PciBridge),
}
bitflags! {
/// Status register of the PCI configuration space
pub struct PciStatusRegister: u16 {
/// Read-only. If set, the configuration space has a pointer to the capabilities list.
const CAPABILITIES_LIST = 1 << 4;
}
pub struct PciBridge {}
pub struct PciEndpoint {
pub imp: PciEndpointImpl,
}
/// Represents the address of a single object on a bus (or the bus itself)
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct PciAddress {
/// PCIe segment group, ignored (?) with PCI
pub segment: u8,
/// Bus number
pub bus: u8,
/// Slot/device number
pub device: u8,
/// Function number
pub function: u8,
}
/// Address provided by PCI configuration space Base Address Register
#[derive(Debug, Clone, Copy)]
pub enum PciBaseAddress {
/// 32-bit memory address
Memory32(u32),
/// 64-bit memory address
Memory64(u64),
/// I/O space address
Io(u16),
}
primitive_enum! {
pub enum PciCapabilityId: u8 {
PowerManagement = 0x01,
Msi = 0x05,
VendorSpecific = 0x09,
PciExpress = 0x10,
MsiX = 0x11,
}
}
/// Interface used for querying PCI capabilities
#[allow(unused)]
pub trait PciCapability {
/// Capability ID
const ID: PciCapabilityId;
/// Wrapper for accessing the capability data structure
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a>;
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
true
}
/// Constructs an access wrapper for this capability with given offset
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
len: usize,
) -> Self::CapabilityData<'s, S>;
}
struct BusAddressAllocator {
pci_base_64: u64,
pci_base_32: u32,
// pci_base_io: u16,
host_base_64: PhysicalAddress,
host_base_32: PhysicalAddress,
// host_base_io: PhysicalAddress,
size_64: usize,
size_32: usize,
// size_io: usize,
offset_64: u64,
offset_32: u32,
}
#[derive(Debug)]
pub struct PciSegmentInfo {
pub segment_number: u8,
pub bus_number_start: u8,
pub bus_number_end: u8,
pub ecam_phys_base: Option<PhysicalAddress>,
pub irq_translation_map: PciInterruptMap,
pub msi_translation_map: PciMsiMap,
}
/// Represents a single PCIe bus segment
pub struct PciBusSegment {
allocator: Option<BusAddressAllocator>,
info: Arc<PciSegmentInfo>,
devices: Vec<Arc<KObject<IrqSafeSpinlock<PciBusDevice>>>>,
}
#[derive(Debug)]
pub enum PciRangeType {
Configuration,
Io,
Memory32,
Memory64,
}
pub struct PciAddressRange {
pub ty: PciRangeType,
pub bus_number: u8,
pub pci_base: u64,
pub host_base: PhysicalAddress,
pub size: usize,
}
/// Manager struct to store and control all PCI devices in the system
pub struct PciBusManager {
segments: Vec<PciBusSegment>,
}
#[cfg_attr(
any(target_arch = "x86_64", target_arch = "x86", target_arch = "riscv64"),
allow(dead_code)
)]
impl BusAddressAllocator {
pub fn from_ranges(ranges: &[PciAddressRange]) -> Self {
let mut range_32 = None;
let mut range_64 = None;
// let mut range_io = None;
for range in ranges {
let range_val = (range.pci_base, range.host_base, range.size);
match range.ty {
// PciRangeType::Io if range_io.is_none() => {
// range_io.replace(range_val);
// }
PciRangeType::Memory32 if range_32.is_none() => {
range_32.replace(range_val);
}
PciRangeType::Memory64 if range_64.is_none() => {
range_64.replace(range_val);
}
_ => (),
}
}
let (pci_base_32, host_base_32, size_32) = range_32.unwrap();
let (pci_base_64, host_base_64, size_64) = range_64.unwrap();
// let (pci_base_io, host_base_io, size_io) = range_io.unwrap();
Self {
pci_base_64,
pci_base_32: pci_base_32.try_into().unwrap(),
// pci_base_io: pci_base_io.try_into().unwrap(),
host_base_64,
host_base_32,
// host_base_io,
size_64,
size_32,
// size_io,
offset_64: 0,
offset_32: 0,
}
}
pub fn allocate(&mut self, ty: PciRangeType, size: usize) -> (PciBaseAddress, PhysicalAddress) {
match ty {
PciRangeType::Io => todo!(),
PciRangeType::Memory32 => {
if self.offset_32 as usize + size >= self.size_32 {
todo!();
}
let bar = PciBaseAddress::Memory32(self.pci_base_32 + self.offset_32);
let host = self.host_base_32.add(self.offset_32 as usize);
self.offset_32 += size as u32;
(bar, host)
}
PciRangeType::Memory64 => {
if self.offset_64 as usize + size >= self.size_64 {
todo!();
}
let bar = PciBaseAddress::Memory64(self.pci_base_64 + self.offset_64);
let host = self.host_base_64.add(self.offset_64 as usize);
self.offset_64 += size as u64;
(bar, host)
}
PciRangeType::Configuration => unimplemented!(),
}
}
}
impl PciBaseAddress {
pub fn as_memory(self) -> Option<PhysicalAddress> {
match self {
Self::Memory32(address) => Some(PhysicalAddress::from_u64(address as u64)),
Self::Memory64(address) => Some(PhysicalAddress::from_u64(address)),
_ => None,
}
}
pub fn is_zero(&self) -> bool {
match *self {
Self::Memory32(base) => base == 0,
Self::Memory64(base) => base == 0,
Self::Io(base) => base == 0,
}
}
}
impl PciBusSegment {
fn probe_config_space(&self, address: PciAddress) -> Result<Option<PciConfigSpace>, Error> {
match self.info.ecam_phys_base {
Some(ecam_phys_base) => Ok(unsafe {
PciEcam::probe_raw_parts(ecam_phys_base, self.info.bus_number_start, address)?
}
.map(PciConfigSpace::Ecam)),
None => Ok(PciLegacyConfigurationSpace::probe(address)?.map(PciConfigSpace::Legacy)),
}
}
fn enumerate_function(&mut self, address: PciAddress) -> Result<(), Error> {
let Some(config) = self.probe_config_space(address)? else {
return Ok(());
};
let header_type = config.header_type();
// Enumerate multi-function devices
if address.function == 0 && header_type & 0x80 != 0 {
for function in 1..8 {
self.enumerate_function(address.with_function(function))?;
}
}
// PCI-to-PCI bridge
// if config.class_code() == 0x06 && config.subclass() == 0x04 {
// let secondary_bus = config.secondary_bus();
// // TODO
// }
if let Some(allocator) = self.allocator.as_mut() {
log::debug!("Remapping BARs for {}", address);
// Find valid BARs
let mut i = 0;
let mut bar_mask = 0;
while i < 6 {
let w0 = config.read_u32(0x10 + i * 4);
let bar_width = match w0 & 1 == 0 {
// Memory BAR
true => match (w0 >> 1) & 3 {
// 32-bit BAR
0 => 1,
// Reserved
1 => unimplemented!(),
// 64-bit BAR
2 => 2,
// Unknown
_ => unreachable!(),
},
false => 1,
};
bar_mask |= 1 << i;
i += bar_width;
}
for i in 0..6 {
if (1 << i) & bar_mask != 0 {
let Some(orig_value) = config.bar(i) else {
continue;
};
let size = unsafe { config.bar_size(i) };
if size != 0 {
log::debug!("BAR{}: size={:#x}", i, size);
match orig_value {
PciBaseAddress::Io(_) => (),
PciBaseAddress::Memory64(_) => {
let (bar, host) = allocator.allocate(PciRangeType::Memory64, size);
let bar_address = bar.as_memory().unwrap();
unsafe {
config.set_bar(i, bar);
}
log::debug!(
"Mapped BAR{} -> pci {:#x} host {:#x}",
i,
bar_address,
host
);
// TODO Don't yet differentiate between Host/PCI addresses, lol
assert_eq!(bar_address, host);
}
PciBaseAddress::Memory32(_) => {
let (bar, host) = allocator.allocate(PciRangeType::Memory32, size);
let bar_address = bar.as_memory().unwrap();
unsafe {
config.set_bar(i, bar);
}
log::debug!(
"Mapped BAR{} -> pci {:#x} host {:#x}",
i,
bar_address,
host
);
// TODO Don't yet differentiate between Host/PCI addresses, lol
assert_eq!(bar_address, host);
}
}
}
}
}
}
let vendor_id = config.vendor_id();
let device_id = config.device_id();
let class = config.class_code();
let subclass = config.subclass();
let prog_if = config.prog_if();
let info = PciDeviceInfo {
address,
vendor_id,
device_id,
class,
subclass,
prog_if,
segment: self.info.clone(),
config_space: config,
interrupt_config: Arc::new(OneTimeInit::new()),
};
let object = nodes::make_sysfs_object(PciBusDevice {
info,
driver: None,
device: None,
state: PciDeviceState::None,
});
let pci_object = PCI_SYSFS_NODE.or_init_with(|| {
let bus_object = sysfs::bus().unwrap();
let pci_object = KObject::new(());
bus_object.add_object("pci", pci_object.clone()).ok();
pci_object
});
let name = format!("{address}");
pci_object.add_object(name, object.clone()).ok();
self.devices.push(object);
Ok(())
}
fn enumerate_bus(&mut self, bus: u8) -> Result<(), Error> {
let address = PciAddress::for_bus(self.info.segment_number, bus);
for i in 0..32 {
let device_address = address.with_device(i);
self.enumerate_function(device_address)?;
}
Ok(())
}
/// Enumerates the bus segment, placing found devices into the manager
pub fn enumerate(&mut self) -> Result<(), Error> {
for bus in self.info.bus_number_start..self.info.bus_number_end {
self.enumerate_bus(bus)?;
}
Ok(())
}
}
impl PciSegmentInfo {
pub fn has_msi(&self) -> bool {
!matches!(self.msi_translation_map, PciMsiMap::Legacy)
}
}
impl PciBusManager {
const fn new() -> Self {
Self {
segments: Vec::new(),
}
}
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
/// drivers
pub fn probe_bus_devices() -> Result<(), Error> {
Self::walk_bus_devices(|device| {
probe_bus_device(device, false)?;
Ok(true)
})
}
pub fn setup_bus_devices(rescan: bool) -> Result<(), Error> {
Self::walk_bus_devices(|device| {
setup_bus_device(device, rescan)?;
Ok(true)
})
}
/// Iterates over the bus devices, calling the function on each of them until either an error
/// or `Ok(false)` is returned
pub fn walk_bus_devices<F: FnMut(&mut PciBusDevice) -> Result<bool, Error>>(
mut f: F,
) -> Result<(), Error> {
let mut this = PCI_MANAGER.lock();
for segment in this.segments.iter_mut() {
for device in segment.devices.iter_mut() {
let mut device = device.lock();
if !f(&mut device)? {
return Ok(());
}
}
}
Ok(())
}
pub fn add_legacy_segment(access: &'static dyn LegacyPciAccess) -> Result<(), Error> {
legacy::PCI.init(access);
let mut bus_segment = PciBusSegment {
info: Arc::new(PciSegmentInfo {
segment_number: 0,
bus_number_start: 0,
bus_number_end: 255,
ecam_phys_base: None,
irq_translation_map: PciInterruptMap::Legacy,
msi_translation_map: PciMsiMap::Legacy,
}),
allocator: None,
devices: Vec::new(),
};
let mut this = PCI_MANAGER.lock();
bus_segment.enumerate()?;
this.segments.push(bus_segment);
Ok(())
}
/// Enumerates a bus segment provided by ACPI MCFG table entry
#[cfg(target_arch = "x86_64")]
pub fn add_segment_from_mcfg(
entry: &McfgEntry,
msi_controller: Arc<dyn MessageInterruptController>,
) -> Result<(), Error> {
let msi_translation_map = PciMsiMap::Identity(msi_controller);
let mut bus_segment = PciBusSegment {
info: Arc::new(PciSegmentInfo {
segment_number: entry.pci_segment_group as u8,
bus_number_start: entry.bus_number_start,
bus_number_end: entry.bus_number_end,
ecam_phys_base: Some(PhysicalAddress::from_u64(entry.base_address)),
// TODO get the segment's PCI root bridge AML name
irq_translation_map: PciInterruptMap::Acpi("\\_SB.PCI0._PRT".into()),
msi_translation_map,
}),
// Firmware done this for us
allocator: None,
devices: Vec::new(),
};
let mut this = PCI_MANAGER.lock();
bus_segment.enumerate()?;
this.segments.push(bus_segment);
Ok(())
}
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub fn add_segment_from_device_tree(
cfg_base: PhysicalAddress,
bus_range: core::ops::Range<u8>,
ranges: Vec<PciAddressRange>,
irq_translation_map: PciInterruptMap,
msi_translation_map: PciMsiMap,
) -> Result<(), Error> {
let mut bus_segment = PciBusSegment {
info: Arc::new(PciSegmentInfo {
segment_number: 0,
bus_number_start: bus_range.start,
bus_number_end: bus_range.end,
ecam_phys_base: Some(cfg_base),
irq_translation_map,
msi_translation_map,
}),
allocator: Some(BusAddressAllocator::from_ranges(&ranges)),
devices: Vec::new(),
};
let mut this = PCI_MANAGER.lock();
bus_segment.enumerate()?;
this.segments.push(bus_segment);
Ok(())
}
}
impl fmt::Display for PciAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.bus, self.device, self.function)
}
}
impl PciAddress {
/// Constructs a [PciAddress] representing a bus
pub const fn for_bus(segment: u8, bus: u8) -> Self {
Self {
segment,
bus,
device: 0,
function: 0,
}
}
/// Constructs a [PciAddress] representing a specific function
pub const fn for_function(segment: u8, bus: u8, device: u8, function: u8) -> Self {
Self {
segment,
bus,
device,
function,
}
}
/// Constructs a [PciAddress] representing a device on a given bus
pub const fn with_device(self, device: u8) -> Self {
Self {
device,
function: 0,
..self
}
}
/// Constructs a [PciAddress] representing a function of a given bus device
pub const fn with_function(self, function: u8) -> Self {
Self { function, ..self }
}
}
impl PciConfigurationSpace for PciConfigSpace {
fn read_u32(&self, offset: usize) -> u32 {
match self {
Self::Ecam(ecam) => ecam.read_u32(offset),
Self::Legacy(legacy) => legacy.read_u32(offset),
}
}
fn write_u32(&self, offset: usize, value: u32) {
match self {
Self::Ecam(ecam) => ecam.write_u32(offset, value),
Self::Legacy(legacy) => legacy.write_u32(offset, value),
}
}
}
fn probe_bus_device(device: &mut PciBusDevice, _rescan: bool) -> Result<(), Error> {
// Already has a driver/device set up
if device.device.is_some() || device.state != PciDeviceState::None {
return Ok(());
}
if let Some(driver) = driver::lookup_driver(&device.info) {
let dma: Arc<dyn DmaAllocator> = Arc::new(DummyDmaAllocator);
match driver.probe(&device.info, &dma) {
Ok(instance) => {
log::info!("{} -> {}", device.info.address, driver.driver_name());
device.device.replace(instance);
device.driver.replace(driver);
device.state = PciDeviceState::Probed;
}
Err(error) => {
log::error!(
"{} ({}) probe error: {error:?}",
device.info.address,
driver.driver_name()
);
}
}
}
Ok(())
}
fn setup_bus_device(device: &mut PciBusDevice, _rescan: bool) -> Result<(), Error> {
// No driver yet (TODO probe if rescan is asked)
let (Some(dev), Some(driver)) = (device.device.as_ref(), device.driver) else {
return Ok(());
};
// Already initialized/failed
if device.state != PciDeviceState::Probed {
return Ok(());
}
let dma: Arc<dyn DmaAllocator> = Arc::new(DummyDmaAllocator);
let cx = DeviceInitContext {
dma_allocator: dma.clone(),
};
match unsafe { dev.clone().init(cx) } {
Ok(()) => {
device.state = PciDeviceState::Initialized;
}
Err(error) => {
log::error!(
"{} ({}) setup error: {error:?}",
device.info.address,
driver.driver_name()
);
device.state = PciDeviceState::Failed;
}
}
Ok(())
}
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
static PCI_SYSFS_NODE: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
-35
View File
@@ -1,35 +0,0 @@
pub macro pci_driver_match {
(class ($class:literal:$subclass:literal:$prog_if:literal)) => {
$crate::driver::PciMatch::Class($class, Some($subclass), Some($prog_if))
},
(class ($class:literal:$subclass:literal)) => {
$crate::driver::PciMatch::Class($class, Some($subclass), None)
},
(class $class:literal) => {
$crate::driver::PciMatch::Class($class, None, None)
},
(device ($vendor:literal:$device:literal)) => {
$crate::driver::PciMatch::Vendor($vendor, $device)
}
}
pub macro pci_driver(
matches: [$($kind:ident $match:tt),+ $(,)?],
driver: $driver:tt
) {
#[link_section = ".init_array"]
#[used]
static __REGISTER_FN: extern "C" fn() = __register_fn;
extern "C" fn __register_fn() {
struct Driver;
impl $crate::driver::PciDriver for Driver $driver
static DRIVER: Driver = Driver;
log::info!("register pci driver: {:?}", $crate::driver::PciDriver::driver_name(&Driver));
$(
let pmatch = $crate::macros::pci_driver_match!($kind $match);
$crate::driver::register_match(pmatch, &DRIVER);
)+
}
}
-139
View File
@@ -1,139 +0,0 @@
use alloc::{format, string::String, sync::Arc};
use libk::{
error::Error,
fs::sysfs::{
attribute::{StringAttribute, StringAttributeOps},
object::KObject,
},
};
use libk_util::sync::IrqSafeSpinlock;
use crate::{device::PciBusDevice, PciBaseAddress, PciCapabilityId, PciConfigurationSpace};
pub(crate) fn make_sysfs_object(
device: PciBusDevice,
) -> Arc<KObject<IrqSafeSpinlock<PciBusDevice>>> {
struct Resources;
struct Capabilities;
struct Driver;
struct Class;
struct Id;
impl StringAttributeOps for Driver {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "driver";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.lock();
if let Some(driver) = state.driver.map(|driver| driver.driver_name()) {
Ok(driver.into())
} else {
Ok("".into())
}
}
}
impl StringAttributeOps for Id {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "id";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.lock();
Ok(format!(
"{:04x}:{:04x}",
state.info.vendor_id, state.info.device_id
))
}
}
impl StringAttributeOps for Class {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "class";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.lock();
Ok(format!(
"{:02x}:{:02x}:{:02x}",
state.info.class, state.info.subclass, state.info.prog_if
))
}
}
impl StringAttributeOps for Resources {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "resources";
const NEWLINE: bool = false;
fn read(state: &Self::Data) -> Result<String, Error> {
use core::fmt::Write;
let state = state.lock();
let mut output = String::new();
for i in 0..6 {
if let Some(bar) = state.info.config_space.bar(i) {
if bar.is_zero() {
continue;
}
match bar {
PciBaseAddress::Io(base) => {
writeln!(output, "{i}:pio:{base:#06x}").ok();
}
PciBaseAddress::Memory32(base) => {
writeln!(output, "{i}:m32:{base:#010x}").ok();
}
PciBaseAddress::Memory64(base) => {
writeln!(output, "{i}:m64:{base:#018x}").ok();
}
}
}
}
if output.is_empty() {
output.push('\n');
}
Ok(output)
}
}
impl StringAttributeOps for Capabilities {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "capabilities";
const NEWLINE: bool = false;
fn read(state: &Self::Data) -> Result<String, Error> {
use core::fmt::Write;
let state = state.lock();
let mut output = String::new();
for (capability, offset, _) in state.info.config_space.capability_iter() {
write!(output, "{offset:04x}:").ok();
match capability {
Some(PciCapabilityId::Msi) => write!(output, "msi").ok(),
Some(PciCapabilityId::MsiX) => write!(output, "msix").ok(),
Some(PciCapabilityId::VendorSpecific) => write!(output, "vendor-specific").ok(),
Some(PciCapabilityId::PciExpress) => write!(output, "pcie").ok(),
Some(PciCapabilityId::PowerManagement) => {
write!(output, "power-management").ok()
}
None => write!(output, "unknown").ok(),
};
writeln!(output).ok();
}
if output.is_empty() {
output.push('\n');
}
Ok(output)
}
}
let object = KObject::new(IrqSafeSpinlock::new(device));
object
.add_attribute(StringAttribute::from(Capabilities))
.ok();
object.add_attribute(StringAttribute::from(Resources)).ok();
object.add_attribute(StringAttribute::from(Driver)).ok();
object.add_attribute(StringAttribute::from(Class)).ok();
object.add_attribute(StringAttribute::from(Id)).ok();
object
}
-60
View File
@@ -1,60 +0,0 @@
//! PCI Express ECAM interface
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryMapping};
use yggdrasil_abi::error::Error;
use super::{PciAddress, PciConfigurationSpace};
/// PCI Express Enhanced Configuration Access Mechanism
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct PciEcam {
mapping: DeviceMemoryMapping,
}
impl PciConfigurationSpace for PciEcam {
fn read_u32(&self, offset: usize) -> u32 {
assert_eq!(offset & 3, 0);
unsafe { ((self.mapping.address() + offset) as *const u32).read_volatile() }
}
fn write_u32(&self, offset: usize, value: u32) {
assert_eq!(offset & 3, 0);
unsafe { ((self.mapping.address() + offset) as *mut u32).write_volatile(value) }
}
}
impl PciEcam {
/// Maps the physical address of a ECAM space for kernel access.
///
/// # Safety
///
/// The `phys_addr` must be a valid ECAM address. The address must not alias any other mapped
/// regions. The address must be aligned to a 4KiB boundary and be valid for accesses within a
/// 4KiB-sized range.
pub unsafe fn map(phys_addr: PhysicalAddress) -> Result<Self, Error> {
let mapping = DeviceMemoryMapping::map(phys_addr, 0x1000, Default::default())?;
Ok(Self { mapping })
}
/// Checks if the ECAM contains a valid device configuration space, mapping and returning a
/// [PciEcam] if it does.
///
/// # Safety
///
/// See [PciEcam::map].
pub unsafe fn probe_raw_parts(
segment_phys_addr: PhysicalAddress,
bus_offset: u8,
address: PciAddress,
) -> Result<Option<Self>, Error> {
let phys_addr = segment_phys_addr.add(
((address.bus - bus_offset) as usize * 256
+ address.device as usize * 8
+ address.function as usize)
* 0x1000,
);
let this = Self::map(phys_addr)?;
Ok(if this.is_valid() { Some(this) } else { None })
}
}
-46
View File
@@ -1,46 +0,0 @@
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::{PciAddress, PciConfigurationSpace};
/// Provides access to the legacy (port I/O-driven) PCI configuration space
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct PciLegacyConfigurationSpace {
address: PciAddress,
}
pub trait LegacyPciAccess {
fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32;
fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32);
}
impl PciConfigurationSpace for PciLegacyConfigurationSpace {
fn read_u32(&self, offset: usize) -> u32 {
PCI.get().read_u32(
self.address.bus,
self.address.device,
self.address.function,
offset as _,
)
}
fn write_u32(&self, offset: usize, value: u32) {
PCI.get().write_u32(
self.address.bus,
self.address.device,
self.address.function,
offset as _,
value,
)
}
}
impl PciLegacyConfigurationSpace {
pub fn probe(address: PciAddress) -> Result<Option<Self>, Error> {
let this = PciLegacyConfigurationSpace { address };
Ok(if this.is_valid() { Some(this) } else { None })
}
}
pub(crate) static PCI: OneTimeInit<&'static dyn LegacyPciAccess> = OneTimeInit::new();
-394
View File
@@ -1,394 +0,0 @@
use alloc::sync::Arc;
use legacy::PciLegacyConfigurationSpace;
use super::{PciAddress, PciBaseAddress, PciCapability, PciCapabilityId, PciEcam};
use crate::{device::PciInterruptPin, PciCommandRegister, PciStatusRegister};
pub(super) mod ecam;
pub(super) mod legacy;
macro_rules! pci_config_field_getter {
($self:ident, u32, $offset:expr) => {
$self.read_u32($offset)
};
($self:ident, u16, $offset:expr) => {
$self.read_u16($offset)
};
($self:ident, u8, $offset:expr) => {
$self.read_u8($offset)
};
}
macro_rules! pci_config_field_setter {
($self:ident, u32, $offset:expr, $value:expr) => {
$self.write_u32($offset, $value)
};
($self:ident, u16, $offset:expr, $value:expr) => {{
$self.write_u16($offset, $value)
}};
($self:ident, u8, $offset:expr, $value:expr) => {
$self.write_u8($offset, $value)
};
}
macro_rules! pci_config_field {
(
$offset:expr => $ty:ident,
$(#[$getter_meta:meta])* $getter:ident
$(, $(#[$setter_meta:meta])* $setter:ident)?
) => {
$(#[$getter_meta])*
fn $getter(&self) -> $ty {
pci_config_field_getter!(self, $ty, $offset)
}
$(
$(#[$setter_meta])*
fn $setter(&self, value: $ty) {
pci_config_field_setter!(self, $ty, $offset, value)
}
)?
};
}
/// Describes a configuration space access method for a PCI device
#[derive(Debug, Clone)]
pub enum PciConfigSpace {
/// Legacy configuration space.
///
/// See [PciLegacyConfigurationSpace].
Legacy(PciLegacyConfigurationSpace),
/// Enhanced Configuration Access Mechanism (PCIe).
///
/// See [PciEcam].
Ecam(PciEcam),
}
pub struct CapabilityIterator<'s, S: PciConfigurationSpace + ?Sized> {
space: &'s S,
current: Option<usize>,
}
impl<S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'_, S> {
type Item = (Option<PciCapabilityId>, usize, usize);
fn next(&mut self) -> Option<Self::Item> {
let offset = self.current? & !0x3;
let id = PciCapabilityId::try_from(self.space.read_u8(offset)).ok();
let len = self.space.read_u8(offset + 2);
let next_pointer = self.space.read_u8(offset + 1);
self.current = if next_pointer != 0 {
Some(next_pointer as usize)
} else {
None
};
Some((id, offset, len as usize))
}
}
/// Interface for accessing the configuration space of a device
pub trait PciConfigurationSpace {
/// Reads a 32-bit value from the device configuration space.
///
/// # Note
///
/// The `offset` must be u32-aligned.
fn read_u32(&self, offset: usize) -> u32;
/// Writes a 32-bit value to the device configuration space.
///
/// # Note
///
/// The `offset` must be u32-aligned.
fn write_u32(&self, offset: usize, value: u32);
/// Reads a 16-bit value from the device configuration space.
///
/// # Note
///
/// The `offset` must be u16-aligned.
fn read_u16(&self, offset: usize) -> u16 {
assert_eq!(offset & 1, 0);
let value = self.read_u32(offset & !3);
(value >> ((offset & 3) * 8)) as u16
}
/// Reads a byte from the device configuration space
fn read_u8(&self, offset: usize) -> u8 {
let value = self.read_u32(offset & !3);
(value >> ((offset & 3) * 8)) as u8
}
/// Writes a 16-bit value to the device configuration space.
///
/// # Note
///
/// The `offset` must be u16-aligned.
fn write_u16(&self, offset: usize, value: u16) {
let shift = ((offset >> 1) & 1) << 4;
assert_eq!(offset & 1, 0);
let mut tmp = self.read_u32(offset & !3);
tmp &= !(0xFFFF << shift);
tmp |= (value as u32) << shift;
self.write_u32(offset & !3, tmp);
}
/// Writes a byte to the device configuration space
fn write_u8(&self, _offset: usize, _value: u16) {
todo!()
}
/// Returns `true` if the device is present on the bus (i.e. configuration space is not filled
/// with only 1's)
fn is_valid(&self) -> bool {
self.vendor_id() != 0xFFFF && self.device_id() != 0xFFFF
}
pci_config_field!(
0x00 => u16,
#[doc = "Returns the Vendor ID"] vendor_id
);
pci_config_field!(0x02 => u16,
#[doc = "Returns the Device ID"] device_id
);
pci_config_field!(
0x04 => u16,
#[doc = "Returns the value of the command register"] command,
#[doc = "Writes to the command word register"] set_command
);
pci_config_field!(
0x06 => u16,
#[doc = "Returns the value of the status register"] status
);
pci_config_field!(
0x08 => u8,
#[doc = "Returns the device Revision ID"]
rev_id
);
pci_config_field!(
0x09 => u8,
#[doc = "Returns the device Prog IF field"]
prog_if
);
pci_config_field!(
0x0A => u8,
#[doc = "Returns the device Subclass field"]
subclass
);
pci_config_field!(
0x0B => u8,
#[doc = "Returns the device Class Code field"]
class_code
);
// ...
pci_config_field!(
0x0E => u8,
#[doc = "Returns the header type of the device"]
header_type
);
pci_config_field!(
0x19 => u8,
#[doc = r#"
Returns the secondary bus number associated with this device
# Note
The function is only valid for devices with `header_type() == 1`
"#]
secondary_bus
);
pci_config_field!(
0x34 => u8,
#[doc =
r"Returns the offset within the configuration space where the Capabilities List
is located. Only valid if the corresponding Status Register bit is set"
]
capability_pointer
);
fn interrupt_pin(&self) -> Option<PciInterruptPin> {
PciInterruptPin::try_from(self.read_u8(0x3D) as u32).ok()
}
fn interrupt_line(&self) -> Option<u8> {
let value = self.read_u8(0x3C);
if value < 16 {
Some(value)
} else {
None
}
}
/// # Safety
///
/// This function is only meant to be called before the device has seen any use by the OS,
/// it has not been tested outside of this use case.
unsafe fn bar_size(&self, index: usize) -> usize {
let cmd = self.command();
// Disable I/O and memory
self.set_command(
cmd & !(PciCommandRegister::ENABLE_IO | PciCommandRegister::ENABLE_MEMORY).bits(),
);
let orig_value = self.bar(index).unwrap();
// TODO preserve prefetch bit
let mask_value = match orig_value {
PciBaseAddress::Io(_) => PciBaseAddress::Io(0xFFFC),
PciBaseAddress::Memory32(_) => PciBaseAddress::Memory32(0xFFFFFFF0),
PciBaseAddress::Memory64(_) => PciBaseAddress::Memory64(0xFFFFFFFFFFFFFFF0),
};
self.set_bar(index, mask_value);
let new_value = self.bar(index).unwrap();
let size = match new_value {
PciBaseAddress::Io(address) if address != 0 => ((!address) + 1) as usize,
PciBaseAddress::Memory32(address) if address != 0 => ((!address) + 1) as usize,
PciBaseAddress::Memory64(address) if address != 0 => ((!address) + 1) as usize,
_ => 0,
};
self.set_bar(index, orig_value);
self.set_command(cmd);
size
}
/// Updates the value of the Base Address Register with given index.
///
/// # Note
///
/// The function is only valid for devices with `header_type() == 0`
///
/// The `index` corresponds to the actual configuration space BAR index.
///
/// # Safety
///
/// Precondition: the device must have memory access disabled through its command register
/// prior to setting a BAR.
unsafe fn set_bar(&self, index: usize, value: PciBaseAddress) {
assert!(index < 6);
match value {
PciBaseAddress::Io(value) => {
self.write_u32(0x10 + index * 4, ((value as u32) & !0x3) | 1)
}
PciBaseAddress::Memory32(address) => self.write_u32(0x10 + index * 4, address & !0xF),
PciBaseAddress::Memory64(address) => {
self.write_u32(0x10 + index * 4, ((address as u32) & !0xF) | (2 << 1));
self.write_u32(0x10 + (index + 1) * 4, (address >> 32) as u32);
}
}
}
/// Returns the value of the Base Address Register with given index.
///
/// # Note
///
/// The function is only valid for devices with `header_type() == 0`
///
/// The `index` corresponds to the actual configuration space BAR index, i.e. if a 64-bit
/// address occupies [BAR0, BAR1] and BAR 1 is requested, the function will return [None].
fn bar(&self, index: usize) -> Option<PciBaseAddress> {
assert!(index < 6);
if index % 2 == 0 {
let w0 = self.read_u32(0x10 + index * 4);
match w0 & 1 {
0 => match (w0 >> 1) & 3 {
0 => {
// 32-bit memory BAR
Some(PciBaseAddress::Memory32(w0 & !0xF))
}
2 => {
// 64-bit memory BAR
let w1 = self.read_u32(0x10 + (index + 1) * 4);
Some(PciBaseAddress::Memory64(
((w1 as u64) << 32) | ((w0 as u64) & !0xF),
))
}
_ => unimplemented!(),
},
1 => Some(PciBaseAddress::Io((w0 as u16) & !0x3)),
_ => unreachable!(),
}
} else {
let prev_w0 = self.read_u32(0x10 + (index - 1) * 4);
if prev_w0 & 0x7 == 0x4 {
// Previous BAR is 64-bit memory and this one is its continuation
return None;
}
let w0 = self.read_u32(0x10 + index * 4);
match w0 & 1 {
0 => match (w0 >> 1) & 3 {
0 => {
// 32-bit memory BAR
Some(PciBaseAddress::Memory32(w0 & !0xF))
}
// TODO can 64-bit BARs not be on a 64-bit boundary?
2 => None,
_ => unimplemented!(),
},
1 => todo!(),
_ => unreachable!(),
}
}
}
/// Returns an iterator over the PCI capabilities
fn capability_iter(&self) -> CapabilityIterator<'_, Self> {
let status = PciStatusRegister::from_bits_retain(self.status());
let current = if status.contains(PciStatusRegister::CAPABILITIES_LIST) {
let ptr = self.capability_pointer() as usize;
if ptr != 0 {
Some(self.capability_pointer() as usize)
} else {
None
}
} else {
// Return an empty iterator
None
};
CapabilityIterator {
space: self,
current,
}
}
/// Locates a capability within this configuration space
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
self.capability_iter().find_map(|(id, offset, len)| {
if id == Some(C::ID) && C::check(self, offset, len) {
Some(C::data(self, offset, len))
} else {
None
}
})
}
}
impl<T: PciConfigurationSpace> PciConfigurationSpace for Arc<T> {
fn read_u32(&self, offset: usize) -> u32 {
T::read_u32(self.as_ref(), offset)
}
fn write_u32(&self, offset: usize, value: u32) {
T::write_u32(self.as_ref(), offset, value);
}
}
+2 -2
View File
@@ -22,7 +22,7 @@ use libk_mm::{
};
use libk_util::OneTimeInit;
use tock_registers::interfaces::Writeable;
use ygg_driver_pci::PciBusManager;
// use ygg_driver_pci::PciBusManager;
use crate::{
arch::{aarch64::gic::Gic, Platform},
@@ -257,7 +257,7 @@ impl AArch64 {
InitSequence::Early,
);
PciBusManager::probe_bus_devices()?;
// PciBusManager::probe_bus_devices()?;
} else {
// BSP already initialized everything needed
// Setup timer and local interrupt controller
+7 -7
View File
@@ -21,7 +21,7 @@ use libk_mm::{
table::EntryLevelExt,
};
use peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc, serial::ComPort};
use ygg_driver_pci::PciBusManager;
// use ygg_driver_pci::PciBusManager;
use crate::fs::{Initrd, INITRD_DATA};
@@ -90,9 +90,9 @@ pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error>
}
pub fn add_legacy_pci() {
if let Err(error) = PciBusManager::add_legacy_segment(&pci::PCI) {
log::error!("Couldn't add legacy x86 PCI: {error:?}");
}
// if let Err(error) = PciBusManager::add_legacy_segment(&pci::PCI) {
// log::error!("Couldn't add legacy x86 PCI: {error:?}");
// }
}
pub fn init_platform_devices(early: EarlyPlatformDevices) {
@@ -120,9 +120,9 @@ pub fn init_platform_devices(early: EarlyPlatformDevices) {
log::error!("COM port IRQ init error: {error:?}");
}
if let Err(error) = PciBusManager::probe_bus_devices() {
log::error!("PCI bus device setup error(s): {error:?}");
}
// if let Err(error) = PciBusManager::probe_bus_devices() {
// log::error!("PCI bus device setup error(s): {error:?}");
// }
}
fn init_clock_source(sources: &[ProbeClockSource]) -> Result<SelectedClockSource, Error> {
+16 -16
View File
@@ -1,6 +1,6 @@
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
use libk_util::sync::IrqSafeSpinlock;
use ygg_driver_pci::LegacyPciAccess;
// use ygg_driver_pci::LegacyPciAccess;
struct LegacyPciInner {
address: IoPort<u32>,
@@ -18,21 +18,21 @@ pub(super) static PCI: LegacyPci = LegacyPci {
}),
};
impl LegacyPciAccess for LegacyPci {
fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
assert_eq!(offset & 0x3, 0);
let inner = self.inner.lock();
inner.address.write(Self::addr(bus, slot, func, offset));
inner.data.write(value);
}
fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
assert_eq!(offset & 0x3, 0);
let inner = self.inner.lock();
inner.address.write(Self::addr(bus, slot, func, offset));
inner.data.read()
}
}
// impl LegacyPciAccess for LegacyPci {
// fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
// assert_eq!(offset & 0x3, 0);
// let inner = self.inner.lock();
// inner.address.write(Self::addr(bus, slot, func, offset));
// inner.data.write(value);
// }
//
// fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
// assert_eq!(offset & 0x3, 0);
// let inner = self.inner.lock();
// inner.address.write(Self::addr(bus, slot, func, offset));
// inner.data.read()
// }
// }
impl LegacyPci {
#[inline]
+7 -10
View File
@@ -33,7 +33,7 @@ use yboot_proto::{
LoadProtocolV1,
};
use ygg_driver_acpi::{AcpiAllocator, AcpiHandlerImpl, EventAction, FixedEvent};
use ygg_driver_pci::PciBusManager;
// use ygg_driver_pci::PciBusManager;
use crate::{
arch::{
@@ -294,9 +294,6 @@ impl X86_64 {
unreachable!("The processor does not support APIC");
};
for _ in 0..10 {
log::info!("TICK");
}
let ioapic = IoApic::from_acpi(&apic_info)?;
register_external_interrupt_controller(ioapic);
@@ -312,12 +309,12 @@ impl X86_64 {
}
if let Ok(mcfg) = acpi.find_table::<Mcfg>() {
for entry in mcfg.entries() {
if let Err(error) = PciBusManager::add_segment_from_mcfg(entry, local_apic.clone())
{
log::error!("Could not add PCI bus segment: {error:?}");
}
}
// for entry in mcfg.entries() {
// if let Err(error) = PciBusManager::add_segment_from_mcfg(entry, local_apic.clone())
// {
// log::error!("Could not add PCI bus segment: {error:?}");
// }
// }
}
Ok(())
+2 -2
View File
@@ -1,6 +1,6 @@
//! Bus devices
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod pci_host_ecam_generic;
// #[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
// pub mod pci_host_ecam_generic;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod simple_bus;
+4 -4
View File
@@ -10,7 +10,7 @@ use libk::{
vfs::{impls::fn_hardlink, IoContext, NodeRef, OwnedFilename},
};
use memfs::MemoryFilesystem;
use ygg_driver_pci::PciBusManager;
// use ygg_driver_pci::PciBusManager;
use crate::{
fs::{FileBlockAllocator, INITRD_DATA},
@@ -57,9 +57,9 @@ pub fn kinit() -> Result<(), Error> {
);
}
// Initialize PCI devices
if let Err(error) = PciBusManager::setup_bus_devices(false) {
log::error!("pci bus setup error: {error:?}");
}
// if let Err(error) = PciBusManager::setup_bus_devices(false) {
// log::error!("pci bus setup error: {error:?}");
// }
assert!(!ArchitectureImpl::interrupt_mask());
runtime::spawn(ygg_driver_usb::bus::bus_handler())?;
+8 -8
View File
@@ -63,17 +63,17 @@ extern crate alloc;
#[cfg(not(rust_analyzer))]
extern crate compiler_builtins;
extern crate ygg_driver_ahci;
extern crate ygg_driver_net_rtl81xx;
extern crate ygg_driver_nvme;
extern crate ygg_driver_usb_xhci;
extern crate ygg_driver_virtio_blk;
extern crate ygg_driver_virtio_gpu;
extern crate ygg_driver_virtio_net;
// extern crate ygg_driver_ahci;
// extern crate ygg_driver_net_rtl81xx;
// extern crate ygg_driver_nvme;
// extern crate ygg_driver_usb_xhci;
// extern crate ygg_driver_virtio_blk;
// extern crate ygg_driver_virtio_gpu;
// extern crate ygg_driver_virtio_net;
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
extern crate ygg_driver_net_igbe;
// extern crate ygg_driver_net_igbe;
} else if #[cfg(target_arch = "riscv64")] {
extern crate ygg_driver_net_stmmac;
}