Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7107c03488 |
Generated
+1
-201
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
)+
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user