Compare commits
5 Commits
baed9c4868
...
2f46a36255
Author | SHA1 | Date | |
---|---|---|---|
2f46a36255 | |||
50a760985b | |||
b567995466 | |||
6e7a42c2cb | |||
9e48530e62 |
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -57,7 +57,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "acpi-system"
|
name = "acpi-system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/alnyan/acpi-system.git#2e57911ab3df43dac7227a3fa458eac6981bbbf9"
|
source = "git+https://github.com/alnyan/acpi-system.git#1dfbf5d22d9227ccdd02e670d1adebca64173040"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acpi",
|
"acpi",
|
||||||
"aml",
|
"aml",
|
||||||
@ -1838,9 +1838,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
@ -2229,9 +2229,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.15"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
|
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
@ -2531,9 +2531,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.24"
|
version = "0.6.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
|
checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -2734,6 +2734,7 @@ dependencies = [
|
|||||||
"acpi",
|
"acpi",
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"device-api",
|
"device-api",
|
||||||
|
"kernel-arch-x86",
|
||||||
"libk",
|
"libk",
|
||||||
"libk-mm",
|
"libk-mm",
|
||||||
"libk-util",
|
"libk-util",
|
||||||
|
@ -202,7 +202,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
|||||||
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
|
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
|
||||||
info.config_space.set_command(cmd.bits());
|
info.config_space.set_command(cmd.bits());
|
||||||
|
|
||||||
info.init_interrupts(PreferredInterruptMode::Msi)?;
|
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||||
|
|
||||||
// // TODO support regular PCI interrupts (ACPI dependency)
|
// // TODO support regular PCI interrupts (ACPI dependency)
|
||||||
// let Some(mut msi) = info.config_space.capability::<MsiCapability>() else {
|
// let Some(mut msi) = info.config_space.capability::<MsiCapability>() else {
|
||||||
|
@ -428,7 +428,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
|||||||
.as_memory()
|
.as_memory()
|
||||||
.expect("Expected a memory BAR0");
|
.expect("Expected a memory BAR0");
|
||||||
|
|
||||||
info.init_interrupts(PreferredInterruptMode::Msi)?;
|
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||||
|
|
||||||
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
|
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
|
||||||
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
|
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
|
||||||
|
@ -18,6 +18,7 @@ tock-registers.workspace = true
|
|||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
ygg_driver_acpi.path = "../../acpi"
|
ygg_driver_acpi.path = "../../acpi"
|
||||||
acpi.workspace = true
|
acpi.workspace = true
|
||||||
|
kernel-arch-x86.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
//! PCI capability structures and queries
|
//! PCI capability structures and queries
|
||||||
|
|
||||||
|
use core::mem::offset_of;
|
||||||
|
|
||||||
use alloc::{sync::Arc, vec, vec::Vec};
|
use alloc::{sync::Arc, vec, vec::Vec};
|
||||||
use device_api::interrupt::{
|
use device_api::interrupt::{
|
||||||
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
|
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
|
||||||
};
|
};
|
||||||
|
use kernel_arch_x86::intrinsics;
|
||||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||||
use tock_registers::{
|
use tock_registers::{
|
||||||
interfaces::{Readable, Writeable},
|
interfaces::{Readable, Writeable},
|
||||||
@ -11,6 +14,8 @@ use tock_registers::{
|
|||||||
};
|
};
|
||||||
use yggdrasil_abi::error::Error;
|
use yggdrasil_abi::error::Error;
|
||||||
|
|
||||||
|
use crate::PciBaseAddress;
|
||||||
|
|
||||||
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||||
|
|
||||||
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
|
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
|
||||||
@ -41,6 +46,9 @@ pub trait VirtioCapability {
|
|||||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
|
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Power management capability entry
|
||||||
|
pub struct PowerManagementCapability;
|
||||||
|
|
||||||
/// MSI-X capability query
|
/// MSI-X capability query
|
||||||
pub struct MsiXCapability;
|
pub struct MsiXCapability;
|
||||||
|
|
||||||
@ -57,6 +65,15 @@ pub struct VirtioNotifyConfigCapability;
|
|||||||
/// VirtIO interrupt status
|
/// VirtIO interrupt status
|
||||||
pub struct VirtioInterruptStatusCapability;
|
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
|
/// Represents an entry in MSI-X vector table
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct MsiXEntry {
|
pub struct MsiXEntry {
|
||||||
@ -68,8 +85,20 @@ pub struct MsiXEntry {
|
|||||||
pub control: ReadWrite<u32>,
|
pub control: ReadWrite<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum MsiXVectorTableAccess<'a> {
|
||||||
|
Memory(DeviceMemoryIoMut<'a, [MsiXEntry]>),
|
||||||
|
Io(u16),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct MsiXVectorTable<'a> {
|
pub struct MsiXVectorTable<'a> {
|
||||||
vectors: DeviceMemoryIoMut<'a, [MsiXEntry]>,
|
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
|
/// MSI-X capability data structure
|
||||||
@ -122,6 +151,19 @@ impl<T: VirtioCapability> PciCapability for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl PciCapability for MsiXCapability {
|
||||||
const ID: PciCapabilityId = PciCapabilityId::MsiX;
|
const ID: PciCapabilityId = PciCapabilityId::MsiX;
|
||||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
|
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
|
||||||
@ -246,6 +288,40 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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> {
|
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
|
||||||
// TODO use pending bits as well
|
// TODO use pending bits as well
|
||||||
/// Maps and returns the vector table associated with the device's MSI-X capability
|
/// Maps and returns the vector table associated with the device's MSI-X capability
|
||||||
@ -260,13 +336,27 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
|
|||||||
let Some(base) = self.space.bar(bir) else {
|
let Some(base) = self.space.bar(bir) else {
|
||||||
return Err(Error::DoesNotExist);
|
return Err(Error::DoesNotExist);
|
||||||
};
|
};
|
||||||
let Some(base) = base.as_memory() else {
|
|
||||||
return Err(Error::InvalidOperation);
|
|
||||||
};
|
|
||||||
|
|
||||||
log::debug!("MSI-X table address: {:#x}", base.add(table_offset));
|
match base {
|
||||||
|
PciBaseAddress::Memory32(mem32) => unsafe {
|
||||||
unsafe { MsiXVectorTable::from_raw_parts(base.add(table_offset), table_size) }
|
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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the global enable status for the device's MSI-X capability. If set, regular IRQs
|
/// Changes the global enable status for the device's MSI-X capability. If set, regular IRQs
|
||||||
@ -292,15 +382,79 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
&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 {
|
||||||
|
&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 {
|
||||||
|
&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 {
|
||||||
|
&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<'_> {
|
impl MsiXVectorTable<'_> {
|
||||||
unsafe fn from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
|
unsafe fn memory_from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
|
||||||
let vectors = DeviceMemoryIoMut::map_slice(base, len, Default::default())?;
|
let vectors = DeviceMemoryIoMut::map_slice(base, len, Default::default())?;
|
||||||
Ok(Self { vectors })
|
Ok(Self {
|
||||||
|
access: MsiXVectorTableAccess::Memory(vectors),
|
||||||
|
len,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
pub fn mask_all(&mut self) {
|
||||||
for vector in self.vectors.iter_mut() {
|
for i in 0..self.len {
|
||||||
vector.set_masked(true);
|
self.access.set_vector_masked(i, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,26 +478,15 @@ impl MsiXVectorTable<'_> {
|
|||||||
|
|
||||||
for (i, info) in range.iter().enumerate() {
|
for (i, info) in range.iter().enumerate() {
|
||||||
let index = i + start;
|
let index = i + start;
|
||||||
self.vectors[index].address.set(info.address as _);
|
self.access.write_address(index, info.address as _);
|
||||||
self.vectors[index].data.set(info.value);
|
self.access.write_data(index, info.value);
|
||||||
self.vectors[index].set_masked(false);
|
self.access.set_vector_masked(index, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(range)
|
Ok(range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MsiXEntry {
|
|
||||||
/// If set, prevents the MSI-X interrupt from being delivered
|
|
||||||
fn set_masked(&mut self, masked: bool) {
|
|
||||||
if masked {
|
|
||||||
self.control.set(self.control.get() | 1);
|
|
||||||
} else {
|
|
||||||
self.control.set(self.control.get() & !1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
|
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
|
||||||
pub fn register<C: MessageInterruptController + ?Sized>(
|
pub fn register<C: MessageInterruptController + ?Sized>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -11,7 +11,7 @@ use yggdrasil_abi::error::Error;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
|
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
|
||||||
PciAddress, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
|
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes a PCI device
|
/// Describes a PCI device
|
||||||
@ -19,6 +19,16 @@ use crate::{
|
|||||||
pub struct PciDeviceInfo {
|
pub struct PciDeviceInfo {
|
||||||
/// Address of the device
|
/// Address of the device
|
||||||
pub address: PciAddress,
|
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
|
/// Configuration space access method
|
||||||
pub config_space: PciConfigSpace,
|
pub config_space: PciConfigSpace,
|
||||||
/// Describes the PCI segment this device is a part of
|
/// Describes the PCI segment this device is a part of
|
||||||
@ -43,7 +53,7 @@ pub enum PciInterruptPin {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum PreferredInterruptMode {
|
pub enum PreferredInterruptMode {
|
||||||
Msi,
|
Msi(bool),
|
||||||
Legacy,
|
Legacy,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +85,7 @@ pub enum PciMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct PciDriver {
|
pub struct PciDriver {
|
||||||
|
#[allow(unused)]
|
||||||
pub(crate) name: &'static str,
|
pub(crate) name: &'static str,
|
||||||
pub(crate) check: PciMatch,
|
pub(crate) check: PciMatch,
|
||||||
pub(crate) probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
|
pub(crate) probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
|
||||||
@ -83,33 +94,82 @@ pub struct PciDriver {
|
|||||||
/// Used to store PCI bus devices which were enumerated by the kernel
|
/// Used to store PCI bus devices which were enumerated by the kernel
|
||||||
pub struct PciBusDevice {
|
pub struct PciBusDevice {
|
||||||
pub(crate) info: PciDeviceInfo,
|
pub(crate) info: PciDeviceInfo,
|
||||||
pub(crate) driver: Option<Arc<dyn Device>>,
|
pub(crate) device: Option<Arc<dyn Device>>,
|
||||||
|
pub(crate) driver_name: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PciDeviceInfo {
|
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> {
|
pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> {
|
||||||
self.interrupt_config
|
self.interrupt_config
|
||||||
.try_init_with(|| {
|
.try_init_with(|| {
|
||||||
let configured_mode =
|
let configured_mode = if self.segment.has_msi
|
||||||
if self.segment.has_msi && preferred_mode == PreferredInterruptMode::Msi {
|
&& let PreferredInterruptMode::Msi(want_msix) = preferred_mode
|
||||||
if let Some(mut msix) = self.config_space.capability::<MsiXCapability>() {
|
{
|
||||||
let mut vt = msix.vector_table().unwrap();
|
// Try MSI-X first
|
||||||
|
let mut result = None;
|
||||||
|
if want_msix
|
||||||
|
&& let Some(mut msix) = self.config_space.capability::<MsiXCapability>()
|
||||||
|
{
|
||||||
|
if let Ok(mut vt) = msix.vector_table() {
|
||||||
vt.mask_all();
|
vt.mask_all();
|
||||||
|
|
||||||
msix.set_function_mask(false);
|
msix.set_function_mask(false);
|
||||||
msix.set_enabled(true);
|
msix.set_enabled(true);
|
||||||
|
|
||||||
ConfiguredInterruptMode::MsiX(vt)
|
result = Some(ConfiguredInterruptMode::MsiX(vt));
|
||||||
} else if self.config_space.capability::<MsiCapability>().is_some() {
|
|
||||||
ConfiguredInterruptMode::Msi
|
|
||||||
} else {
|
|
||||||
self.legacy_interrupt_mode()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then try MSI
|
||||||
|
if result.is_none() && self.config_space.capability::<MsiCapability>().is_some()
|
||||||
|
{
|
||||||
|
result = Some(ConfiguredInterruptMode::Msi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And then fall back to legacy
|
||||||
|
if let Some(result) = result {
|
||||||
|
result
|
||||||
} else {
|
} else {
|
||||||
// Ignore preferred_mode, the only supported is Legacy
|
|
||||||
self.legacy_interrupt_mode()
|
self.legacy_interrupt_mode()
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
// Ignore preferred_mode, the only supported is Legacy
|
||||||
|
self.legacy_interrupt_mode()
|
||||||
|
};
|
||||||
IrqSafeRwLock::new(InterruptConfig {
|
IrqSafeRwLock::new(InterruptConfig {
|
||||||
preferred_mode,
|
preferred_mode,
|
||||||
configured_mode,
|
configured_mode,
|
||||||
|
@ -33,6 +33,14 @@ impl PciInterruptMap {
|
|||||||
interrupt.address.function as u16,
|
interrupt.address.function as u16,
|
||||||
aml_pin,
|
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)?;
|
.ok_or(Error::DoesNotExist)?;
|
||||||
|
|
||||||
let trigger = match aml_route.trigger {
|
let trigger = match aml_route.trigger {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! PCI/PCIe bus interfaces
|
//! PCI/PCIe bus interfaces
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![feature(let_chains)]
|
||||||
#![allow(clippy::missing_transmute_annotations)]
|
#![allow(clippy::missing_transmute_annotations)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@ -8,20 +9,22 @@ use core::fmt;
|
|||||||
|
|
||||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||||
use acpi::mcfg::McfgEntry;
|
use acpi::mcfg::McfgEntry;
|
||||||
|
use alloc::{format, sync::Arc, vec::Vec};
|
||||||
|
|
||||||
use alloc::{sync::Arc, vec::Vec};
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciMatch};
|
use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciMatch};
|
||||||
use device_api::device::Device;
|
use device_api::device::Device;
|
||||||
use interrupt::PciInterruptMap;
|
use interrupt::PciInterruptMap;
|
||||||
|
use libk::fs::sysfs::{self, object::KObject};
|
||||||
use libk_mm::address::PhysicalAddress;
|
use libk_mm::address::PhysicalAddress;
|
||||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||||
use space::legacy;
|
use space::legacy;
|
||||||
use yggdrasil_abi::error::Error;
|
use yggdrasil_abi::{error::Error, primitive_enum};
|
||||||
|
|
||||||
pub mod capability;
|
pub mod capability;
|
||||||
pub mod device;
|
pub mod device;
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
|
mod nodes;
|
||||||
mod space;
|
mod space;
|
||||||
|
|
||||||
pub use space::{
|
pub use space::{
|
||||||
@ -32,6 +35,7 @@ pub use space::{
|
|||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Command register of the PCI configuration space
|
/// Command register of the PCI configuration space
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
pub struct PciCommandRegister: u16 {
|
pub struct PciCommandRegister: u16 {
|
||||||
/// If set, I/O access to the device is enabled
|
/// If set, I/O access to the device is enabled
|
||||||
const ENABLE_IO = 1 << 0;
|
const ENABLE_IO = 1 << 0;
|
||||||
@ -76,20 +80,28 @@ pub enum PciBaseAddress {
|
|||||||
Io(u16),
|
Io(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unique ID assigned to PCI capability structures
|
primitive_enum! {
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
pub enum PciCapabilityId: u8 {
|
||||||
#[non_exhaustive]
|
PowerManagement = 0x01,
|
||||||
#[repr(u8)]
|
Msi = 0x05,
|
||||||
pub enum PciCapabilityId {
|
VendorSpecific = 0x09,
|
||||||
/// MSI (32-bit or 64-bit)
|
MsiX = 0x11,
|
||||||
Msi = 0x05,
|
}
|
||||||
/// Vendor-specific capability
|
|
||||||
VendorSpecific = 0x09,
|
|
||||||
/// MSI-X
|
|
||||||
MsiX = 0x11,
|
|
||||||
/// Unknown capability missing from this list
|
|
||||||
Unknown,
|
|
||||||
}
|
}
|
||||||
|
// /// Unique ID assigned to PCI capability structures
|
||||||
|
// #[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
// #[non_exhaustive]
|
||||||
|
// #[repr(u8)]
|
||||||
|
// pub enum PciCapabilityId {
|
||||||
|
// /// MSI (32-bit or 64-bit)
|
||||||
|
// Msi = 0x05,
|
||||||
|
// /// Vendor-specific capability
|
||||||
|
// VendorSpecific = 0x09,
|
||||||
|
// /// MSI-X
|
||||||
|
// MsiX = 0x11,
|
||||||
|
// /// Unknown capability missing from this list
|
||||||
|
// Unknown,
|
||||||
|
// }
|
||||||
|
|
||||||
/// Interface used for querying PCI capabilities
|
/// Interface used for querying PCI capabilities
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@ -211,7 +223,7 @@ pub struct PciSegmentInfo {
|
|||||||
pub struct PciBusSegment {
|
pub struct PciBusSegment {
|
||||||
allocator: Option<BusAddressAllocator>,
|
allocator: Option<BusAddressAllocator>,
|
||||||
info: Arc<PciSegmentInfo>,
|
info: Arc<PciSegmentInfo>,
|
||||||
devices: Vec<PciBusDevice>,
|
devices: Vec<Arc<KObject<IrqSafeSpinlock<PciBusDevice>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -243,6 +255,14 @@ impl PciBaseAddress {
|
|||||||
_ => None,
|
_ => 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 {
|
impl PciBusSegment {
|
||||||
@ -351,13 +371,39 @@ impl PciBusSegment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
let info = PciDeviceInfo {
|
||||||
address,
|
address,
|
||||||
|
vendor_id,
|
||||||
|
device_id,
|
||||||
|
class,
|
||||||
|
subclass,
|
||||||
|
prog_if,
|
||||||
segment: self.info.clone(),
|
segment: self.info.clone(),
|
||||||
config_space: config,
|
config_space: config,
|
||||||
interrupt_config: Arc::new(OneTimeInit::new()),
|
interrupt_config: Arc::new(OneTimeInit::new()),
|
||||||
};
|
};
|
||||||
self.devices.push(PciBusDevice { info, driver: None });
|
|
||||||
|
let object = nodes::make_sysfs_object(PciBusDevice {
|
||||||
|
info,
|
||||||
|
driver_name: None,
|
||||||
|
device: 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -393,9 +439,9 @@ impl PciBusManager {
|
|||||||
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
|
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
|
||||||
/// drivers
|
/// drivers
|
||||||
pub fn setup_bus_devices() -> Result<(), Error> {
|
pub fn setup_bus_devices() -> Result<(), Error> {
|
||||||
log::info!("Setting up bus devices");
|
// log::info!("Setting up bus devices");
|
||||||
Self::walk_bus_devices(|device| {
|
Self::walk_bus_devices(|device| {
|
||||||
log::info!("Set up {}", device.info.address);
|
// log::info!("Set up {}", device.info.address);
|
||||||
setup_bus_device(device)?;
|
setup_bus_device(device)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
})
|
})
|
||||||
@ -410,7 +456,8 @@ impl PciBusManager {
|
|||||||
|
|
||||||
for segment in this.segments.iter_mut() {
|
for segment in this.segments.iter_mut() {
|
||||||
for device in segment.devices.iter_mut() {
|
for device in segment.devices.iter_mut() {
|
||||||
if !f(device)? {
|
let mut device = device.lock();
|
||||||
|
if !f(&mut *device)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,39 +605,29 @@ impl PciConfigurationSpace for PciConfigSpace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
||||||
if device.driver.is_some() {
|
if device.device.is_some() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = &device.info.config_space;
|
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"{}: {:04x}:{:04x}",
|
"{}: {:04x}:{:04x}",
|
||||||
device.info.address,
|
device.info.address,
|
||||||
config.vendor_id(),
|
device.info.vendor_id,
|
||||||
config.device_id()
|
device.info.device_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let class = config.class_code();
|
|
||||||
let subclass = config.subclass();
|
|
||||||
let prog_if = config.prog_if();
|
|
||||||
|
|
||||||
let drivers = PCI_DRIVERS.lock();
|
let drivers = PCI_DRIVERS.lock();
|
||||||
for driver in drivers.iter() {
|
for driver in drivers.iter() {
|
||||||
if driver
|
if driver.check.check_device(&device.info) {
|
||||||
.check
|
|
||||||
.check_device(&device.info, class, subclass, prog_if)
|
|
||||||
{
|
|
||||||
// TODO add the device to the bus
|
// TODO add the device to the bus
|
||||||
log::debug!(" -> {:?}", driver.name);
|
// log::debug!(" -> {:?}", driver.name);
|
||||||
let instance = (driver.probe)(&device.info)?;
|
let instance = (driver.probe)(&device.info)?;
|
||||||
|
|
||||||
unsafe { instance.clone().init() }?;
|
unsafe { instance.clone().init() }?;
|
||||||
|
|
||||||
device.driver.replace(instance);
|
device.device.replace(instance);
|
||||||
|
device.driver_name.replace(driver.name);
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
log::debug!(" -> No driver");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,17 +635,19 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PciMatch {
|
impl PciMatch {
|
||||||
pub fn check_device(&self, info: &PciDeviceInfo, class: u8, subclass: u8, prog_if: u8) -> bool {
|
pub fn check_device(&self, info: &PciDeviceInfo) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Generic(f) => f(info),
|
Self::Generic(f) => f(info),
|
||||||
&Self::Vendor(vendor_, device_) => {
|
&Self::Vendor(vendor_, device_) => {
|
||||||
info.config_space.vendor_id() == vendor_ && info.config_space.device_id() == device_
|
info.vendor_id == vendor_ && info.device_id == device_
|
||||||
}
|
}
|
||||||
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
|
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
|
||||||
class_ == class && subclass_ == subclass && prog_if_ == prog_if
|
class_ == info.class && subclass_ == info.subclass && prog_if_ == info.prog_if
|
||||||
}
|
}
|
||||||
&Self::Class(class_, Some(subclass_), _) => class_ == class && subclass_ == subclass,
|
&Self::Class(class_, Some(subclass_), _) => {
|
||||||
&Self::Class(class_, _, _) => class_ == class,
|
class_ == info.class && subclass_ == info.subclass
|
||||||
|
}
|
||||||
|
&Self::Class(class_, _, _) => class_ == info.class,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -654,3 +693,4 @@ pub fn register_generic_driver(
|
|||||||
|
|
||||||
static PCI_DRIVERS: IrqSafeSpinlock<Vec<PciDriver>> = IrqSafeSpinlock::new(Vec::new());
|
static PCI_DRIVERS: IrqSafeSpinlock<Vec<PciDriver>> = IrqSafeSpinlock::new(Vec::new());
|
||||||
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|
||||||
|
static PCI_SYSFS_NODE: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||||
|
138
kernel/driver/bus/pci/src/nodes.rs
Normal file
138
kernel/driver/bus/pci/src/nodes.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
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_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::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
|
||||||
|
}
|
@ -75,12 +75,12 @@ pub struct CapabilityIterator<'s, S: PciConfigurationSpace + ?Sized> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'_, S> {
|
impl<S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'_, S> {
|
||||||
type Item = (PciCapabilityId, usize, usize);
|
type Item = (Option<PciCapabilityId>, usize, usize);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let offset = self.current? & !0x3;
|
let offset = self.current? & !0x3;
|
||||||
|
|
||||||
let id = unsafe { core::mem::transmute(self.space.read_u8(offset)) };
|
let id = PciCapabilityId::try_from(self.space.read_u8(offset)).ok();
|
||||||
let len = self.space.read_u8(offset + 2);
|
let len = self.space.read_u8(offset + 2);
|
||||||
let next_pointer = self.space.read_u8(offset + 1);
|
let next_pointer = self.space.read_u8(offset + 1);
|
||||||
|
|
||||||
@ -374,7 +374,7 @@ pub trait PciConfigurationSpace {
|
|||||||
/// Locates a capability within this configuration space
|
/// Locates a capability within this configuration space
|
||||||
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
|
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
|
||||||
self.capability_iter().find_map(|(id, offset, len)| {
|
self.capability_iter().find_map(|(id, offset, len)| {
|
||||||
if id == C::ID && C::check(self, offset, len) {
|
if id.map_or(false, |id| id == C::ID) && C::check(self, offset, len) {
|
||||||
Some(C::data(self, offset, len))
|
Some(C::data(self, offset, len))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use core::mem::size_of;
|
use core::{fmt, mem::size_of};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use bytemuck::Pod;
|
use bytemuck::Pod;
|
||||||
@ -27,6 +27,37 @@ pub struct L2Packet {
|
|||||||
pub data: Arc<PageBox<[u8]>>,
|
pub data: Arc<PageBox<[u8]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines an Ethernet link speed
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum EthernetSpeed {
|
||||||
|
/// 1Gbps link
|
||||||
|
Speed1000,
|
||||||
|
/// 100Mbps link
|
||||||
|
Speed100,
|
||||||
|
/// 10Mbps link
|
||||||
|
Speed10,
|
||||||
|
/// Link speed not available/unknown
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines whether an Ethernet link is capable of transmiting data both ways simultaneously
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Duplex {
|
||||||
|
/// Half-duplex link with multiplexed Tx and Rx
|
||||||
|
Half,
|
||||||
|
/// Full-duplex link capable of simultaneous Tx and Rx
|
||||||
|
Full,
|
||||||
|
/// Duplex mode not available/unknown
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the state of an Ethernet link
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum EthernetLinkState {
|
||||||
|
Up(EthernetSpeed, Duplex),
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
impl L2Packet {
|
impl L2Packet {
|
||||||
pub fn ethernet_frame(&self) -> &EthernetFrame {
|
pub fn ethernet_frame(&self) -> &EthernetFrame {
|
||||||
bytemuck::from_bytes(
|
bytemuck::from_bytes(
|
||||||
@ -78,3 +109,39 @@ pub fn handle(packet: L2Packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EthernetSpeed {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let text = match self {
|
||||||
|
Self::Speed10 => "10Mbps",
|
||||||
|
Self::Speed100 => "100Mbps",
|
||||||
|
Self::Speed1000 => "1Gbps",
|
||||||
|
Self::Unknown => "N/A",
|
||||||
|
};
|
||||||
|
f.write_str(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Duplex {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let text = match self {
|
||||||
|
Self::Half => "half-duplex",
|
||||||
|
Self::Full => "full-duplex",
|
||||||
|
Self::Unknown => "N/A duplex mode",
|
||||||
|
};
|
||||||
|
f.write_str(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EthernetLinkState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Up(speed, duplex) => {
|
||||||
|
write!(f, "up, speed {speed}, {duplex}")
|
||||||
|
}
|
||||||
|
Self::Down => {
|
||||||
|
write!(f, "down")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ use alloc::sync::Arc;
|
|||||||
use device_api::device::Device;
|
use device_api::device::Device;
|
||||||
use libk::error::Error;
|
use libk::error::Error;
|
||||||
use rtl8139::Rtl8139;
|
use rtl8139::Rtl8139;
|
||||||
|
use rtl8168::Rtl8168;
|
||||||
use ygg_driver_pci::{
|
use ygg_driver_pci::{
|
||||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||||
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
||||||
@ -12,13 +13,28 @@ use ygg_driver_pci::{
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod rtl8139;
|
pub mod rtl8139;
|
||||||
|
pub mod rtl8168;
|
||||||
|
|
||||||
pub fn probe_8169(_info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
pub fn probe_8168(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||||
todo!()
|
let base = info
|
||||||
|
.config_space
|
||||||
|
.bar(2)
|
||||||
|
.and_then(PciBaseAddress::as_memory)
|
||||||
|
.ok_or(Error::InvalidArgument)?;
|
||||||
|
|
||||||
|
// if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
|
||||||
|
// power.set_device_power_state(DevicePowerState::D0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Enable MMIO + interrupts + bus mastering
|
||||||
|
info.set_command(true, true, false, true);
|
||||||
|
|
||||||
|
let device = Rtl8168::new(base, info.clone())?;
|
||||||
|
Ok(Arc::new(device))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probe_8139(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
pub fn probe_8139(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||||
info.init_interrupts(PreferredInterruptMode::Msi)?;
|
info.init_interrupts(PreferredInterruptMode::Msi(false))?;
|
||||||
|
|
||||||
// Enable MMIO + interrupts + bus mastering
|
// Enable MMIO + interrupts + bus mastering
|
||||||
let mut command = info.config_space.command();
|
let mut command = info.config_space.command();
|
||||||
|
722
kernel/driver/net/rtl81xx/src/rtl8168.rs
Normal file
722
kernel/driver/net/rtl81xx/src/rtl8168.rs
Normal file
@ -0,0 +1,722 @@
|
|||||||
|
use core::{
|
||||||
|
mem::{self, MaybeUninit},
|
||||||
|
sync::atomic::{self, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{sync::Arc, vec::Vec};
|
||||||
|
use device_api::{device::Device, interrupt::InterruptHandler};
|
||||||
|
use libk::error::Error;
|
||||||
|
use libk_mm::{
|
||||||
|
address::{AsPhysicalAddress, PhysicalAddress},
|
||||||
|
device::DeviceMemoryIo,
|
||||||
|
PageBox,
|
||||||
|
};
|
||||||
|
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||||
|
use tock_registers::{
|
||||||
|
interfaces::{ReadWriteable, Readable, Writeable},
|
||||||
|
register_bitfields, register_structs,
|
||||||
|
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||||
|
};
|
||||||
|
use ygg_driver_net_core::{
|
||||||
|
ethernet::{Duplex, EthernetLinkState, EthernetSpeed},
|
||||||
|
interface::{NetworkDevice, NetworkInterfaceType},
|
||||||
|
Packet,
|
||||||
|
};
|
||||||
|
use ygg_driver_pci::device::{PciDeviceInfo, PreferredInterruptMode};
|
||||||
|
use yggdrasil_abi::{bitflags, net::MacAddress};
|
||||||
|
|
||||||
|
register_bitfields! {
|
||||||
|
u8,
|
||||||
|
CR [
|
||||||
|
/// Software reset bit. Set by driver, cleared by device
|
||||||
|
RST OFFSET(4) NUMBITS(1) [],
|
||||||
|
/// Rx enable
|
||||||
|
RE OFFSET(3) NUMBITS(1) [],
|
||||||
|
/// Tx enable
|
||||||
|
TE OFFSET(2) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
TPPOLL [
|
||||||
|
HPQ OFFSET(7) NUMBITS(1) [],
|
||||||
|
NPQ OFFSET(6) NUMBITS(1) [],
|
||||||
|
FSWInt OFFSET(0) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
PHYSTATUS [
|
||||||
|
TXFLOW OFFSET(6) NUMBITS(1) [],
|
||||||
|
RXFLOW OFFSET(5) NUMBITS(1) [],
|
||||||
|
MF1000 OFFSET(4) NUMBITS(1) [],
|
||||||
|
M100 OFFSET(3) NUMBITS(1) [],
|
||||||
|
M10 OFFSET(2) NUMBITS(1) [],
|
||||||
|
LINKSTS OFFSET(1) NUMBITS(1) [],
|
||||||
|
FULLDUP OFFSET(0) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
register_bitfields! {
|
||||||
|
u16,
|
||||||
|
IMR_ISR [
|
||||||
|
TIMEOUT OFFSET(14) NUMBITS(1) [],
|
||||||
|
FEMP OFFSET(9) NUMBITS(1) [],
|
||||||
|
SWINT OFFSET(8) NUMBITS(1) [],
|
||||||
|
TDU OFFSET(7) NUMBITS(1) [],
|
||||||
|
FOVW OFFSET(6) NUMBITS(1) [],
|
||||||
|
LINKCHG OFFSET(5) NUMBITS(1) [],
|
||||||
|
RDU OFFSET(4) NUMBITS(1) [],
|
||||||
|
TER OFFSET(3) NUMBITS(1) [],
|
||||||
|
TOK OFFSET(2) NUMBITS(1) [],
|
||||||
|
RER OFFSET(1) NUMBITS(1) [],
|
||||||
|
ROK OFFSET(0) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
CPCR [
|
||||||
|
/// (rtl8169sc) PCI endianness mode
|
||||||
|
ENDIAN OFFSET(9) NUMBITS(1) [
|
||||||
|
Little = 0,
|
||||||
|
Big = 1,
|
||||||
|
],
|
||||||
|
/// (no docs found) TBD
|
||||||
|
MACSTATDIS OFFSET(7) NUMBITS(1) [],
|
||||||
|
/// Rx VLAN de-tagging enable
|
||||||
|
RXVLAN OFFSET(6) NUMBITS(1) [],
|
||||||
|
/// Rx checksum offload enable
|
||||||
|
RXCHKSUM OFFSET(5) NUMBITS(1) [],
|
||||||
|
/// PCI dual address cycle enable
|
||||||
|
DAC OFFSET(4) NUMBITS(1) [],
|
||||||
|
/// PCI multiple read/write enable
|
||||||
|
MULRW OFFSET(3) NUMBITS(1) [],
|
||||||
|
|
||||||
|
// Not documented, found in OpenBSD's re driver
|
||||||
|
/// C+ Rx mode enable
|
||||||
|
RXENB OFFSET(1) NUMBITS(1) [],
|
||||||
|
/// C+ Tx mode enable
|
||||||
|
TXENB OFFSET(0) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
register_bitfields! {
|
||||||
|
u32,
|
||||||
|
TCR [
|
||||||
|
HWVERID0 OFFSET(28) NUMBITS(2) [],
|
||||||
|
HWVERID1 OFFSET(26) NUMBITS(1) [],
|
||||||
|
IFG OFFSET(24) NUMBITS(2) [],
|
||||||
|
HWVERID2 OFFSET(23) NUMBITS(1) [],
|
||||||
|
TX_NOCRC OFFSET(16) NUMBITS(1) [],
|
||||||
|
MXDMA OFFSET(8) NUMBITS(1) [
|
||||||
|
Burst16 = 0b000,
|
||||||
|
Burst32 = 0b001,
|
||||||
|
Burst64 = 0b010,
|
||||||
|
Burst128 = 0b011,
|
||||||
|
Burst256 = 0b100,
|
||||||
|
Burst512 = 0b101,
|
||||||
|
Unlimited = 0b111,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
RCR [
|
||||||
|
RXFTH OFFSET(13) NUMBITS(3) [
|
||||||
|
Threshold64 = 0b010,
|
||||||
|
Threshold128 = 0b011,
|
||||||
|
Threshold256 = 0b100,
|
||||||
|
Threshold512 = 0b101,
|
||||||
|
Threshold1024 = 0b110,
|
||||||
|
NoThreshold = 0b111,
|
||||||
|
],
|
||||||
|
EARLYOFFV2 OFFSET(11) NUMBITS(1) [],
|
||||||
|
MXDMA OFFSET(8) NUMBITS(3) [
|
||||||
|
Burst64 = 0b010,
|
||||||
|
Burst128 = 0b011,
|
||||||
|
Burst256 = 0b100,
|
||||||
|
Burst512 = 0b101,
|
||||||
|
Burst1024 = 0b110,
|
||||||
|
Unlimited = 0b111
|
||||||
|
],
|
||||||
|
R9356SEL OFFSET(6) NUMBITS(1) [],
|
||||||
|
AER OFFSET(5) NUMBITS(1) [],
|
||||||
|
AR OFFSET(4) NUMBITS(1) [],
|
||||||
|
AB OFFSET(3) NUMBITS(1) [],
|
||||||
|
AM OFFSET(2) NUMBITS(1) [],
|
||||||
|
APM OFFSET(1) NUMBITS(1) [],
|
||||||
|
AAP OFFSET(0) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
PHYAR [
|
||||||
|
FLAG OFFSET(31) NUMBITS(1) [
|
||||||
|
Read = 0,
|
||||||
|
Write = 1,
|
||||||
|
],
|
||||||
|
REGADDR OFFSET(16) NUMBITS(5) [],
|
||||||
|
DATA OFFSET(0) NUMBITS(16) [],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
register_structs! {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
Regs {
|
||||||
|
(0x0000 => IDRn: [ReadWrite<u32>; 2]),
|
||||||
|
(0x0008 => MARn: [ReadWrite<u32>; 2]),
|
||||||
|
(0x0010 => DTCCR: ReadWrite<u64>),
|
||||||
|
(0x0018 => _0),
|
||||||
|
(0x0020 => TNPDS: [ReadWrite<u32>; 2]),
|
||||||
|
(0x0028 => THPDS: [ReadWrite<u32>; 2]),
|
||||||
|
(0x0030 => _1),
|
||||||
|
(0x0037 => CR: ReadWrite<u8, CR::Register>),
|
||||||
|
(0x0038 => TPPOLL: WriteOnly<u8, TPPOLL::Register>),
|
||||||
|
(0x0039 => _2),
|
||||||
|
(0x003C => IMR: ReadWrite<u16, IMR_ISR::Register>),
|
||||||
|
(0x003E => ISR: ReadWrite<u16, IMR_ISR::Register>),
|
||||||
|
(0x0040 => TCR: ReadWrite<u32, TCR::Register>),
|
||||||
|
(0x0044 => RCR: ReadWrite<u32, RCR::Register>),
|
||||||
|
(0x0048 => TCTR: ReadWrite<u32>),
|
||||||
|
(0x004C => MPKT: ReadWrite<u32>),
|
||||||
|
(0x0050 => R9346CR: ReadWrite<u8>),
|
||||||
|
(0x0051 => CONFIG0: ReadWrite<u8>),
|
||||||
|
(0x0052 => CONFIG1: ReadWrite<u8>),
|
||||||
|
(0x0053 => CONFIG2: ReadWrite<u8>),
|
||||||
|
(0x0054 => CONFIG3: ReadWrite<u8>),
|
||||||
|
(0x0055 => CONFIG4: ReadWrite<u8>),
|
||||||
|
(0x0056 => CONFIG5: ReadWrite<u8>),
|
||||||
|
(0x0057 => _4),
|
||||||
|
(0x0058 => TIMERINT: ReadWrite<u32>),
|
||||||
|
(0x005C => _5),
|
||||||
|
(0x0060 => PHYAR: ReadWrite<u32, PHYAR::Register>),
|
||||||
|
(0x0064 => _6),
|
||||||
|
(0x006C => PHYSTATUS: ReadOnly<u8, PHYSTATUS::Register>),
|
||||||
|
(0x006D => _7),
|
||||||
|
(0x0084 => WAKEUPn: [ReadWrite<u32>; 16]),
|
||||||
|
(0x00C4 => CRCn: [ReadWrite<u16>; 5]),
|
||||||
|
(0x00CE => _8),
|
||||||
|
(0x00DA => RMS: ReadWrite<u16>),
|
||||||
|
(0x00DC => _9),
|
||||||
|
(0x00E0 => CPCR: ReadWrite<u16, CPCR::Register>),
|
||||||
|
(0x00E2 => _10),
|
||||||
|
(0x00E4 => RDSAR: [ReadWrite<u32>; 2]),
|
||||||
|
(0x00EC => MTPS: ReadWrite<u8>),
|
||||||
|
(0x00ED => _11),
|
||||||
|
(0x00F0 => MISC: ReadWrite<u32>),
|
||||||
|
(0x00F4 => _12),
|
||||||
|
(0x0100 => @END),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
struct ChipFlags: u32 {
|
||||||
|
const MACSTAT: bit 0;
|
||||||
|
const NEED_PHY_RESET: bit 1;
|
||||||
|
const RXDV_GATED: bit 2;
|
||||||
|
const EARLYOFFV2: bit 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
enum Revision {
|
||||||
|
rtl8168gu,
|
||||||
|
rtl8168h,
|
||||||
|
other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Descriptor {
|
||||||
|
cmd: u32,
|
||||||
|
vlan_cmd: u32,
|
||||||
|
address: PhysicalAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RxRing {
|
||||||
|
entries: PageBox<[Descriptor]>,
|
||||||
|
buffers: Vec<PageBox<[MaybeUninit<u8>]>>,
|
||||||
|
rd: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TxRing {
|
||||||
|
entries: PageBox<[Descriptor]>,
|
||||||
|
buffers: Vec<Option<PageBox<[u8]>>>,
|
||||||
|
wr: usize,
|
||||||
|
rd: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Rtl8168 {
|
||||||
|
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||||
|
mac: MacAddress,
|
||||||
|
pci: PciDeviceInfo,
|
||||||
|
|
||||||
|
nic: OneTimeInit<u32>,
|
||||||
|
rx: OneTimeInit<IrqSafeSpinlock<RxRing>>,
|
||||||
|
tx: OneTimeInit<IrqSafeSpinlock<TxRing>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RxRing {
|
||||||
|
pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
|
||||||
|
let buffers = (0..capacity)
|
||||||
|
.map(|_| PageBox::new_uninit_slice(4096))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let entries = PageBox::new_slice_with(
|
||||||
|
|i| {
|
||||||
|
Descriptor::new_rx(
|
||||||
|
unsafe { buffers[i].as_physical_address() },
|
||||||
|
i == capacity - 1,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
capacity,
|
||||||
|
)?;
|
||||||
|
unsafe { core::arch::asm!("wbinvd") };
|
||||||
|
Ok(Self {
|
||||||
|
entries,
|
||||||
|
buffers,
|
||||||
|
rd: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume<F: Fn(PageBox<[u8]>)>(&mut self, handler: F) -> Result<usize, Error> {
|
||||||
|
let mut count = 0;
|
||||||
|
loop {
|
||||||
|
let index = self.rd % self.entries.len();
|
||||||
|
let entry = &self.entries[index];
|
||||||
|
|
||||||
|
if entry.is_host_owned() {
|
||||||
|
let new_buffer = PageBox::new_uninit_slice(4096)?;
|
||||||
|
let new_buffer_address = unsafe { new_buffer.as_physical_address() };
|
||||||
|
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
|
||||||
|
let buffer = unsafe { buffer.assume_init_slice() };
|
||||||
|
handler(buffer);
|
||||||
|
|
||||||
|
self.entries[index].setup_rx(new_buffer_address);
|
||||||
|
|
||||||
|
self.rd = self.rd.wrapping_add(1);
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_address(&self) -> PhysicalAddress {
|
||||||
|
unsafe { self.entries.as_physical_address() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxRing {
|
||||||
|
pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
|
||||||
|
let entries =
|
||||||
|
PageBox::new_slice_with(|i| Descriptor::empty_tx(i == capacity - 1), capacity)?;
|
||||||
|
let buffers = (0..capacity).map(|_| None).collect();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
entries,
|
||||||
|
buffers,
|
||||||
|
wr: 0,
|
||||||
|
rd: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_full(&self) -> bool {
|
||||||
|
self.wr.wrapping_add(1) % self.entries.len() == self.rd % self.entries.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, packet: PageBox<[u8]>) -> Result<(), Error> {
|
||||||
|
let packet_base = unsafe { packet.as_physical_address() };
|
||||||
|
let packet_size = packet.len();
|
||||||
|
|
||||||
|
// TODO packet size checks
|
||||||
|
|
||||||
|
if self.is_full() {
|
||||||
|
log::warn!("rtl81xx: tx ring full: wr={}, rd={}", self.wr, self.rd);
|
||||||
|
return Err(Error::WouldBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = self.wr % self.entries.len();
|
||||||
|
self.buffers[index] = Some(packet);
|
||||||
|
self.entries[index].setup_tx(packet_base, packet_size);
|
||||||
|
|
||||||
|
self.wr = self.wr.wrapping_add(1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume(&mut self) -> Result<usize, Error> {
|
||||||
|
let mut count = 0;
|
||||||
|
loop {
|
||||||
|
let index = self.rd % self.entries.len();
|
||||||
|
let entry = &mut self.entries[index];
|
||||||
|
|
||||||
|
if self.rd != self.wr && entry.is_host_owned() {
|
||||||
|
self.rd = self.rd.wrapping_add(1);
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_address(&self) -> PhysicalAddress {
|
||||||
|
unsafe { self.entries.as_physical_address() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor {
|
||||||
|
// Descriptor owned by DMA
|
||||||
|
const CMD_OWN: u32 = 1 << 31;
|
||||||
|
// Last descriptor in the ring
|
||||||
|
const CMD_END: u32 = 1 << 30;
|
||||||
|
// Last segment of a packet
|
||||||
|
const CMD_LS: u32 = 1 << 28;
|
||||||
|
// First segment of a packet
|
||||||
|
const CMD_FS: u32 = 1 << 29;
|
||||||
|
|
||||||
|
pub fn new_rx(buffer: PhysicalAddress, last: bool) -> Self {
|
||||||
|
let mut cmd = Self::CMD_OWN | 0x1000;
|
||||||
|
if last {
|
||||||
|
cmd |= Self::CMD_END;
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
cmd,
|
||||||
|
vlan_cmd: 0,
|
||||||
|
address: buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_tx(last: bool) -> Self {
|
||||||
|
let cmd = if last { Self::CMD_END } else { 0 };
|
||||||
|
Self {
|
||||||
|
cmd,
|
||||||
|
vlan_cmd: 0,
|
||||||
|
address: PhysicalAddress::ZERO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_rx(&mut self, buffer: PhysicalAddress) {
|
||||||
|
let cmd = self.cmd;
|
||||||
|
self.address = buffer;
|
||||||
|
self.vlan_cmd = 0;
|
||||||
|
unsafe {
|
||||||
|
atomic::fence(Ordering::Release);
|
||||||
|
core::arch::asm!("wbinvd");
|
||||||
|
core::ptr::write_volatile(&mut self.cmd, cmd | Self::CMD_OWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_tx(&mut self, buffer: PhysicalAddress, size: usize) {
|
||||||
|
let mut cmd = self.cmd;
|
||||||
|
cmd |= Self::CMD_OWN | Self::CMD_FS | Self::CMD_LS | (size as u32);
|
||||||
|
self.address = buffer;
|
||||||
|
self.vlan_cmd = 0;
|
||||||
|
unsafe {
|
||||||
|
atomic::fence(Ordering::Release);
|
||||||
|
core::arch::asm!("wbinvd");
|
||||||
|
core::ptr::write_volatile(&mut self.cmd, cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_host_owned(&self) -> bool {
|
||||||
|
self.cmd & Self::CMD_OWN == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Regs {
|
||||||
|
const TCR_REVISION_MASK: u32 = 0x7C800000;
|
||||||
|
|
||||||
|
pub fn gmii_write(&self, reg: u8, value: u16, mut timeout: u64) -> Result<(), Error> {
|
||||||
|
self.PHYAR.write(
|
||||||
|
PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write,
|
||||||
|
);
|
||||||
|
while timeout > 0 && self.PHYAR.matches_all(PHYAR::FLAG::SET) {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
timeout -= 1;
|
||||||
|
}
|
||||||
|
if timeout == 0 {
|
||||||
|
Err(Error::TimedOut)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn gmii_read(&self, reg: u8, mut timeout: u64) -> Result<u16, Error> {
|
||||||
|
self.PHYAR
|
||||||
|
.write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if timeout == 0 {
|
||||||
|
return Err(Error::TimedOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = self.PHYAR.extract();
|
||||||
|
if status.matches_all(PHYAR::FLAG::SET) {
|
||||||
|
return Ok(status.read(PHYAR::DATA) as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
core::hint::spin_loop();
|
||||||
|
timeout -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_link_state(&self) -> EthernetLinkState {
|
||||||
|
let phystatus = self.PHYSTATUS.extract();
|
||||||
|
if phystatus.matches_all(PHYSTATUS::LINKSTS::SET) {
|
||||||
|
let duplex = if phystatus.matches_all(PHYSTATUS::FULLDUP::SET) {
|
||||||
|
Duplex::Full
|
||||||
|
} else {
|
||||||
|
Duplex::Half
|
||||||
|
};
|
||||||
|
let (speed, duplex) = if phystatus.matches_all(PHYSTATUS::MF1000::SET) {
|
||||||
|
// 1000MF is always Full-Duplex
|
||||||
|
(EthernetSpeed::Speed1000, Duplex::Full)
|
||||||
|
} else if phystatus.matches_all(PHYSTATUS::M100::SET) {
|
||||||
|
(EthernetSpeed::Speed100, duplex)
|
||||||
|
} else if phystatus.matches_all(PHYSTATUS::M10::SET) {
|
||||||
|
(EthernetSpeed::Speed10, duplex)
|
||||||
|
} else {
|
||||||
|
(EthernetSpeed::Unknown, duplex)
|
||||||
|
};
|
||||||
|
|
||||||
|
EthernetLinkState::Up(speed, duplex)
|
||||||
|
} else {
|
||||||
|
EthernetLinkState::Down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_chip_revision(&self) -> Revision {
|
||||||
|
let rev = self.TCR.get() & Self::TCR_REVISION_MASK;
|
||||||
|
match rev {
|
||||||
|
0x50800000 => Revision::rtl8168gu,
|
||||||
|
0x54000000 => Revision::rtl8168h,
|
||||||
|
_ => Revision::other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_device(&self, mut timeout: u64) -> Result<(), Error> {
|
||||||
|
self.CR.write(CR::RST::SET);
|
||||||
|
|
||||||
|
while timeout > 0 && self.CR.matches_all(CR::RST::SET) {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
timeout -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout == 0 {
|
||||||
|
Err(Error::TimedOut)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_phy(&self, timeout: u64) -> Result<(), Error> {
|
||||||
|
// Set the reset bit in bmcr
|
||||||
|
self.gmii_write(0x00, 1 << 15, timeout)?;
|
||||||
|
for _ in 0..timeout {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
// Disable power off mode + start auto-negotiation
|
||||||
|
self.gmii_write(0x00, 1 << 12, timeout)?;
|
||||||
|
for _ in 0..timeout {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.gmii_write(0x1F, 0x00, timeout)?;
|
||||||
|
self.gmii_write(0x0E, 0x00, timeout)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[inline]
|
||||||
|
// pub fn lock_config(&self) {
|
||||||
|
// self.R9346CR.set(0x00);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[inline]
|
||||||
|
// pub fn unlock_config(&self) {
|
||||||
|
// self.R9346CR.set(0xC0);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rtl8168 {
|
||||||
|
pub fn new(base: PhysicalAddress, info: PciDeviceInfo) -> Result<Self, Error> {
|
||||||
|
let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }?;
|
||||||
|
let mac0 = regs.IDRn[0].get().to_le_bytes();
|
||||||
|
let mac1 = regs.IDRn[1].get().to_le_bytes();
|
||||||
|
let mac = MacAddress::from([mac0[0], mac0[1], mac0[2], mac0[3], mac1[0], mac1[1]]);
|
||||||
|
Ok(Self {
|
||||||
|
regs: IrqSafeSpinlock::new(regs),
|
||||||
|
mac,
|
||||||
|
pci: info,
|
||||||
|
|
||||||
|
nic: OneTimeInit::new(),
|
||||||
|
rx: OneTimeInit::new(),
|
||||||
|
tx: OneTimeInit::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptHandler for Rtl8168 {
|
||||||
|
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
|
||||||
|
let regs = self.regs.lock();
|
||||||
|
let status = regs.ISR.extract();
|
||||||
|
|
||||||
|
if status.get() == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
regs.ISR.set(status.get());
|
||||||
|
|
||||||
|
let mut any = false;
|
||||||
|
if status.matches_all(IMR_ISR::LINKCHG::SET) {
|
||||||
|
let state = regs.get_link_state();
|
||||||
|
log::info!("rtl8168: link is {state}");
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
if status.matches_all(IMR_ISR::ROK::SET) {
|
||||||
|
let mut rx = self.rx.get().lock();
|
||||||
|
let nic = *self.nic.get();
|
||||||
|
let count = rx
|
||||||
|
.consume(|buffer| {
|
||||||
|
// TODO add packet len hint to packets
|
||||||
|
let packet = Packet::new(buffer, 0, nic);
|
||||||
|
ygg_driver_net_core::receive_packet(packet).ok();
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
any |= count != 0;
|
||||||
|
}
|
||||||
|
if status.matches_all(IMR_ISR::TOK::SET) {
|
||||||
|
let mut tx = self.tx.get().lock();
|
||||||
|
let count = tx.consume().unwrap_or(0);
|
||||||
|
any |= count != 0;
|
||||||
|
}
|
||||||
|
if status.matches_all(IMR_ISR::RDU::SET) {
|
||||||
|
log::info!("rtl8168: rdu");
|
||||||
|
any |= true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !any {
|
||||||
|
log::warn!("rtl8168: possibly unhandled IRQ, ISR={:#x}", status.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for Rtl8168 {
|
||||||
|
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
||||||
|
log::info!("Initialize rtl8168");
|
||||||
|
log::info!("MAC: {}", self.mac);
|
||||||
|
|
||||||
|
let rx_ring = RxRing::with_capacity(256)?;
|
||||||
|
let tx_ring = TxRing::with_capacity(256)?;
|
||||||
|
let rx_ring_base = rx_ring.base_address().into_u64();
|
||||||
|
let tx_ring_base = tx_ring.base_address().into_u64();
|
||||||
|
|
||||||
|
let regs = self.regs.lock();
|
||||||
|
let hwrev = regs.get_chip_revision();
|
||||||
|
|
||||||
|
log::info!("Revision: {:?}", hwrev);
|
||||||
|
let flags = hwrev.flags();
|
||||||
|
|
||||||
|
// Perform software reset
|
||||||
|
regs.reset_device(10000000)
|
||||||
|
.inspect_err(|_| log::warn!("rtl81xx: reset timed out"))?;
|
||||||
|
|
||||||
|
// Enable C+ mode for Rx/Tx
|
||||||
|
let mut cpcr = CPCR::TXENB::SET + CPCR::MULRW::SET + CPCR::RXCHKSUM::SET;
|
||||||
|
if flags.contains(ChipFlags::MACSTAT) {
|
||||||
|
cpcr += CPCR::MACSTATDIS::SET;
|
||||||
|
} else {
|
||||||
|
cpcr += CPCR::RXENB::SET;
|
||||||
|
}
|
||||||
|
regs.CPCR.write(cpcr);
|
||||||
|
|
||||||
|
// Reset the PHY
|
||||||
|
if flags.contains(ChipFlags::NEED_PHY_RESET) {
|
||||||
|
regs.reset_phy(10000000)
|
||||||
|
.inspect_err(|_| log::warn!("rtl81xx: PHY reset timed out"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.RDSAR[1].set((rx_ring_base >> 32) as u32);
|
||||||
|
regs.RDSAR[0].set(rx_ring_base as u32);
|
||||||
|
regs.TNPDS[1].set((tx_ring_base >> 32) as u32);
|
||||||
|
regs.TNPDS[0].set(tx_ring_base as u32);
|
||||||
|
|
||||||
|
if flags.contains(ChipFlags::RXDV_GATED) {
|
||||||
|
regs.MISC.set(regs.MISC.get() & !0x80000);
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("rx_ring_base = {rx_ring_base:#x}");
|
||||||
|
|
||||||
|
unsafe { core::arch::asm!("wbinvd") };
|
||||||
|
|
||||||
|
// Setup Tx
|
||||||
|
regs.TCR.modify(TCR::MXDMA::Unlimited);
|
||||||
|
// Setup Rx
|
||||||
|
let mut rcr = RCR::MXDMA::Unlimited
|
||||||
|
+ RCR::RXFTH::NoThreshold
|
||||||
|
+ RCR::AB::SET
|
||||||
|
+ RCR::AM::SET
|
||||||
|
+ RCR::APM::SET;
|
||||||
|
if flags.contains(ChipFlags::EARLYOFFV2) {
|
||||||
|
rcr += RCR::EARLYOFFV2::SET;
|
||||||
|
}
|
||||||
|
regs.RCR.write(rcr);
|
||||||
|
|
||||||
|
// Enable Rx/Tx
|
||||||
|
regs.CR.write(CR::RE::SET + CR::TE::SET);
|
||||||
|
|
||||||
|
// Setup interrupts
|
||||||
|
regs.IMR.set(0xFFFF);
|
||||||
|
regs.ISR.set(0xFFFF);
|
||||||
|
|
||||||
|
// Clear missed packet counter (?)
|
||||||
|
regs.MPKT.set(0);
|
||||||
|
|
||||||
|
regs.MTPS.set(31);
|
||||||
|
regs.RMS.set(0x1FFF);
|
||||||
|
|
||||||
|
regs.CONFIG1.set(regs.CONFIG1.get() | 0x20);
|
||||||
|
|
||||||
|
self.rx.init(IrqSafeSpinlock::new(rx_ring));
|
||||||
|
self.tx.init(IrqSafeSpinlock::new(tx_ring));
|
||||||
|
|
||||||
|
// Bind IRQ
|
||||||
|
self.pci
|
||||||
|
.init_interrupts(PreferredInterruptMode::Msi(false))?;
|
||||||
|
self.pci.map_interrupt(Default::default(), self.clone())?;
|
||||||
|
|
||||||
|
let interface =
|
||||||
|
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
|
||||||
|
self.nic.init(interface.id());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_name(&self) -> &str {
|
||||||
|
"Realtek RTL8111/8168 Gigabit Ethernet Controller"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkDevice for Rtl8168 {
|
||||||
|
fn transmit(&self, packet: PageBox<[u8]>) -> Result<(), Error> {
|
||||||
|
let mut tx = self.tx.get().lock();
|
||||||
|
let regs = self.regs.lock();
|
||||||
|
|
||||||
|
tx.push(packet)?;
|
||||||
|
regs.TPPOLL.write(TPPOLL::NPQ::SET);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn packet_prefix_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_hardware_address(&self) -> MacAddress {
|
||||||
|
self.mac
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Revision {
|
||||||
|
fn flags(&self) -> ChipFlags {
|
||||||
|
match self {
|
||||||
|
Self::rtl8168h | Self::rtl8168gu => {
|
||||||
|
ChipFlags::NEED_PHY_RESET
|
||||||
|
| ChipFlags::MACSTAT
|
||||||
|
| ChipFlags::RXDV_GATED
|
||||||
|
| ChipFlags::EARLYOFFV2
|
||||||
|
}
|
||||||
|
Self::other => ChipFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -103,7 +103,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
|||||||
let regs = unsafe { xhci_lib::Registers::new(bar0.try_into_usize().unwrap(), Mapper::new()) };
|
let regs = unsafe { xhci_lib::Registers::new(bar0.try_into_usize().unwrap(), Mapper::new()) };
|
||||||
let xhci = Arc::new(Xhci::new(regs)?);
|
let xhci = Arc::new(Xhci::new(regs)?);
|
||||||
|
|
||||||
info.init_interrupts(PreferredInterruptMode::Msi)?;
|
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||||
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
|
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
|
||||||
|
|
||||||
Ok(xhci)
|
Ok(xhci)
|
||||||
|
@ -176,7 +176,7 @@ impl<T: Transport + 'static> VirtioNet<T> {
|
|||||||
transmit_count: usize,
|
transmit_count: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let receive_vector = if let Some(pci) = self.pci_device_info.as_ref() {
|
let receive_vector = if let Some(pci) = self.pci_device_info.as_ref() {
|
||||||
pci.init_interrupts(PreferredInterruptMode::Msi)?;
|
pci.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||||
let info = pci.map_interrupt(InterruptAffinity::Any, self.clone())?;
|
let info = pci.map_interrupt(InterruptAffinity::Any, self.clone())?;
|
||||||
|
|
||||||
info.map(|info| info.vector as u16)
|
info.map(|info| info.vector as u16)
|
||||||
|
@ -25,6 +25,10 @@ pub fn device() -> Option<&'static Arc<KObject<()>>> {
|
|||||||
object::DEVICE_OBJECT.try_get()
|
object::DEVICE_OBJECT.try_get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bus() -> Option<&'static Arc<KObject<()>>> {
|
||||||
|
object::BUS_OBJECT.try_get()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
ROOT.init(object::setup_fixed_objects());
|
ROOT.init(object::setup_fixed_objects());
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use libk_util::OneTimeInit;
|
use libk_util::OneTimeInit;
|
||||||
use yggdrasil_abi::{error::Error, io::FileMode};
|
use yggdrasil_abi::{error::Error, io::FileMode};
|
||||||
@ -42,6 +44,20 @@ impl<D> KObject<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<D> Deref for KObject<D> {
|
||||||
|
type Target = D;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> DerefMut for KObject<D> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<D: Send> Send for KObject<D> {}
|
unsafe impl<D: Send> Send for KObject<D> {}
|
||||||
unsafe impl<D: Send> Sync for KObject<D> {}
|
unsafe impl<D: Send> Sync for KObject<D> {}
|
||||||
|
|
||||||
@ -54,6 +70,8 @@ pub static KERNEL_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
|||||||
pub static DEVICE_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
pub static DEVICE_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||||
// `/debug`
|
// `/debug`
|
||||||
pub static DEBUG_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
pub static DEBUG_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||||
|
// `/bus`
|
||||||
|
pub static BUS_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||||
|
|
||||||
fn setup_fixed_object(root: &Arc<KObject<()>>, obj: &OneTimeInit<Arc<KObject<()>>>, name: &str) {
|
fn setup_fixed_object(root: &Arc<KObject<()>>, obj: &OneTimeInit<Arc<KObject<()>>>, name: &str) {
|
||||||
let obj = obj.init(KObject::new(()));
|
let obj = obj.init(KObject::new(()));
|
||||||
@ -66,6 +84,7 @@ pub fn setup_fixed_objects() -> Arc<Node> {
|
|||||||
setup_fixed_object(root, &KERNEL_OBJECT, "kernel");
|
setup_fixed_object(root, &KERNEL_OBJECT, "kernel");
|
||||||
setup_fixed_object(root, &DEVICE_OBJECT, "device");
|
setup_fixed_object(root, &DEVICE_OBJECT, "device");
|
||||||
setup_fixed_object(root, &DEBUG_OBJECT, "debug");
|
setup_fixed_object(root, &DEBUG_OBJECT, "debug");
|
||||||
|
setup_fixed_object(root, &BUS_OBJECT, "bus");
|
||||||
|
|
||||||
root.node.clone()
|
root.node.clone()
|
||||||
}
|
}
|
||||||
|
@ -70,13 +70,13 @@ pub fn register_pci_drivers() {
|
|||||||
Some(0x01),
|
Some(0x01),
|
||||||
ygg_driver_ahci::probe,
|
ygg_driver_ahci::probe,
|
||||||
);
|
);
|
||||||
// ygg_driver_pci::register_class_driver(
|
ygg_driver_pci::register_class_driver(
|
||||||
// "USB xHCI",
|
"USB xHCI",
|
||||||
// 0x0C,
|
0x0C,
|
||||||
// Some(0x03),
|
Some(0x03),
|
||||||
// Some(0x30),
|
Some(0x30),
|
||||||
// ygg_driver_usb_xhci::probe,
|
ygg_driver_usb_xhci::probe,
|
||||||
// );
|
);
|
||||||
ygg_driver_pci::register_vendor_driver(
|
ygg_driver_pci::register_vendor_driver(
|
||||||
"Virtio PCI GPU Device",
|
"Virtio PCI GPU Device",
|
||||||
0x1AF4,
|
0x1AF4,
|
||||||
@ -96,10 +96,10 @@ pub fn register_pci_drivers() {
|
|||||||
ygg_driver_net_rtl81xx::probe_8139,
|
ygg_driver_net_rtl81xx::probe_8139,
|
||||||
);
|
);
|
||||||
ygg_driver_pci::register_vendor_driver(
|
ygg_driver_pci::register_vendor_driver(
|
||||||
"Realtek RTL8169/8110 Gigabit Ethernet",
|
"Realtek RTL8168/8111 Gigabit Ethernet",
|
||||||
0x10EC,
|
0x10EC,
|
||||||
0x8169,
|
0x8168,
|
||||||
ygg_driver_net_rtl81xx::probe_8169,
|
ygg_driver_net_rtl81xx::probe_8168,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ struct Inner {
|
|||||||
pub struct IoApic {
|
pub struct IoApic {
|
||||||
inner: IrqSafeSpinlock<Inner>,
|
inner: IrqSafeSpinlock<Inner>,
|
||||||
isa_redirections: [Option<IsaRedirection>; 16],
|
isa_redirections: [Option<IsaRedirection>; 16],
|
||||||
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>, // table: IrqSafeSpinlock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
|
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
@ -259,7 +259,7 @@ impl MessageInterruptController for LocalApic {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let value = 32 + APIC_MSI_OFFSET + i as u32;
|
let value = 32 + APIC_MSI_OFFSET + i as u32;
|
||||||
let address = Self::base().into_usize() | ((self.id as usize) << 12);
|
let address = 0xFEE00000 | ((self.id as usize) << 12);
|
||||||
|
|
||||||
*msi = MsiInfo {
|
*msi = MsiInfo {
|
||||||
address,
|
address,
|
||||||
|
73
userspace/Cargo.lock
generated
73
userspace/Cargo.lock
generated
@ -331,7 +331,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||||
"rsa",
|
"rsa",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
@ -862,7 +862,7 @@ dependencies = [
|
|||||||
"num-integer",
|
"num-integer",
|
||||||
"num-iter",
|
"num-iter",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@ -941,6 +941,19 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pci-ids"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d88ae3281b415d856e9c2ddbcdd5961e71c1a3e90138512c04d720241853a6af"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
"phf",
|
||||||
|
"phf_codegen",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem-rfc7468"
|
name = "pem-rfc7468"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -956,6 +969,44 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_codegen"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
"rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@ -1045,6 +1096,15 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -1225,7 +1285,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"libterm",
|
"libterm",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"x25519-dalek",
|
"x25519-dalek",
|
||||||
@ -1393,6 +1453,12 @@ dependencies = [
|
|||||||
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@ -1485,6 +1551,7 @@ dependencies = [
|
|||||||
"humansize",
|
"humansize",
|
||||||
"init",
|
"init",
|
||||||
"libterm",
|
"libterm",
|
||||||
|
"pci-ids",
|
||||||
"rand 0.9.0-alpha.1",
|
"rand 0.9.0-alpha.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -21,6 +21,7 @@ chrono.workspace = true
|
|||||||
|
|
||||||
# TODO own impl
|
# TODO own impl
|
||||||
humansize = { version = "2.1.3", features = ["impl_style"] }
|
humansize = { version = "2.1.3", features = ["impl_style"] }
|
||||||
|
pci-ids = { version = "0.2.5" }
|
||||||
|
|
||||||
init = { path = "../init" }
|
init = { path = "../init" }
|
||||||
|
|
||||||
@ -117,9 +118,14 @@ path = "src/sync.rs"
|
|||||||
name = "sleep"
|
name = "sleep"
|
||||||
path = "src/sleep.rs"
|
path = "src/sleep.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "lspci"
|
||||||
|
path = "src/lspci.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "tst"
|
name = "tst"
|
||||||
path = "src/tst.rs"
|
path = "src/tst.rs"
|
||||||
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
248
userspace/sysutils/src/lspci.rs
Normal file
248
userspace/sysutils/src/lspci.rs
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
use std::{fmt, fs, io, path::PathBuf, process::ExitCode, str::FromStr};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use pci_ids::FromId;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
struct Args {
|
||||||
|
#[clap(short, help = "Display numeric IDs only")]
|
||||||
|
numeric: bool,
|
||||||
|
#[clap(short, help = "Print all the information about the devices")]
|
||||||
|
verbose: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PciAddress {
|
||||||
|
bus: u8,
|
||||||
|
device: u8,
|
||||||
|
function: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DeviceId {
|
||||||
|
vendor: u16,
|
||||||
|
device: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
path: PathBuf,
|
||||||
|
address: PciAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Resource {
|
||||||
|
Io(u16),
|
||||||
|
Memory32(u32),
|
||||||
|
Memory64(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PciRegion {
|
||||||
|
index: usize,
|
||||||
|
resource: Resource
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
enum Error {
|
||||||
|
#[error("{0}")]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
#[error("Invalid PCI ID")]
|
||||||
|
InvalidId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PciAddress {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut elements = s.split(':');
|
||||||
|
let bus = elements
|
||||||
|
.next()
|
||||||
|
.and_then(|p| u8::from_str_radix(p, 16).ok())
|
||||||
|
.ok_or(Error::InvalidId)?;
|
||||||
|
let device = elements
|
||||||
|
.next()
|
||||||
|
.and_then(|p| u8::from_str_radix(p, 16).ok())
|
||||||
|
.ok_or(Error::InvalidId)?;
|
||||||
|
let function = elements
|
||||||
|
.next()
|
||||||
|
.and_then(|p| u8::from_str_radix(p, 16).ok())
|
||||||
|
.ok_or(Error::InvalidId)?;
|
||||||
|
if elements.next().is_some() {
|
||||||
|
return Err(Error::InvalidId);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
bus,
|
||||||
|
device,
|
||||||
|
function,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DeviceId {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (vendor, device) = s.split_once(':').ok_or(Error::InvalidId)?;
|
||||||
|
let vendor = u16::from_str_radix(vendor, 16).map_err(|_| Error::InvalidId)?;
|
||||||
|
let device = u16::from_str_radix(device, 16).map_err(|_| Error::InvalidId)?;
|
||||||
|
Ok(Self { vendor, device })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Resource {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (ty, addr) = s.split_once(':').ok_or(Error::InvalidId)?;
|
||||||
|
let addr = addr.strip_prefix("0x").ok_or(Error::InvalidId)?;
|
||||||
|
match ty {
|
||||||
|
"pio" => {
|
||||||
|
let addr = u16::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
|
||||||
|
Ok(Self::Io(addr))
|
||||||
|
},
|
||||||
|
"m32" => {
|
||||||
|
let addr = u32::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
|
||||||
|
Ok(Self::Memory32(addr))
|
||||||
|
},
|
||||||
|
"m64" => {
|
||||||
|
let addr = u64::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
|
||||||
|
Ok(Self::Memory64(addr))
|
||||||
|
},
|
||||||
|
_ => Err(Error::InvalidId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PciRegion {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (bar, resource) = s.split_once(':').ok_or(Error::InvalidId)?;
|
||||||
|
let index = usize::from_str(bar).map_err(|_| Error::InvalidId)?;
|
||||||
|
let resource = Resource::from_str(resource)?;
|
||||||
|
Ok(Self {
|
||||||
|
index, resource
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PciAddress {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{:02x}:{:02x}.{:x}",
|
||||||
|
self.bus, self.device, self.function
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DeviceId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:04x}:{:04x}", self.vendor, self.device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Resource {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::Io(addr) => write!(f, "I/O at {addr:#x}"),
|
||||||
|
Self::Memory32(addr) => write!(f, "Memory (32-bit) at {addr:#x}"),
|
||||||
|
Self::Memory64(addr) => write!(f, "Memory (64-bit) at {addr:#x}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_devices() -> Result<Vec<Device>, Error> {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
for entry in fs::read_dir("/sys/bus/pci")? {
|
||||||
|
let Ok(entry) = entry else { continue };
|
||||||
|
let file_name = entry.file_name();
|
||||||
|
let Some(name) = file_name.to_str() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(address) = PciAddress::from_str(name) {
|
||||||
|
entries.push(Device {
|
||||||
|
path: entry.path(),
|
||||||
|
address,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_device_id(device: &Device) -> Result<DeviceId, Error> {
|
||||||
|
let str = fs::read_to_string(device.path.join("id"))?;
|
||||||
|
DeviceId::from_str(str.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_device_regions(device: &Device) -> Result<Vec<PciRegion>, Error> {
|
||||||
|
let str = fs::read_to_string(device.path.join("resources"))?;
|
||||||
|
let mut regions = Vec::new();
|
||||||
|
for line in str.split('\n') {
|
||||||
|
let line = line.trim();
|
||||||
|
let Ok(region) = PciRegion::from_str(line) else { continue };
|
||||||
|
regions.push(region);
|
||||||
|
}
|
||||||
|
Ok(regions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vendor_id(args: &Args, id: u16) -> String {
|
||||||
|
if args.numeric {
|
||||||
|
format!("{id:04x}")
|
||||||
|
} else {
|
||||||
|
pci_ids::Vendor::from_id(id).map(|v| v.name().into()).unwrap_or_else(|| format!("{id:04x}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_id(args: &Args, vid: u16, pid: u16) -> String {
|
||||||
|
if args.numeric {
|
||||||
|
format!("{pid:04x}")
|
||||||
|
} else {
|
||||||
|
pci_ids::Device::from_vid_pid(vid, pid).map(|v| v.name().into()).unwrap_or_else(|| format!("{pid:04x}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_device(args: &Args, device: &Device) -> Result<(), Error> {
|
||||||
|
let id = read_device_id(device)?;
|
||||||
|
|
||||||
|
print!("{}: ", device.address);
|
||||||
|
|
||||||
|
let vid = vendor_id(args, id.vendor);
|
||||||
|
let pid = device_id(args, id.vendor, id.device);
|
||||||
|
let device_name = if args.numeric {
|
||||||
|
format!("{vid}:{pid}")
|
||||||
|
}else {
|
||||||
|
format!("[{vid}] {pid}")
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{device_name}");
|
||||||
|
|
||||||
|
if args.verbose {
|
||||||
|
for region in read_device_regions(device)? {
|
||||||
|
println!(" BAR {}: {}", region.index, region.resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(args: &Args) -> Result<(), Error> {
|
||||||
|
let devices = list_devices()?;
|
||||||
|
for device in devices {
|
||||||
|
if let Err(error) = print_device(args, &device) {
|
||||||
|
eprintln!("{}: {error}", device.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
match run(&args) {
|
||||||
|
Ok(()) => ExitCode::SUCCESS,
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("{error}");
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,7 @@ const PROGRAMS: &[(&str, &str)] = &[
|
|||||||
("date", "bin/date"),
|
("date", "bin/date"),
|
||||||
("sync", "bin/sync"),
|
("sync", "bin/sync"),
|
||||||
("sleep", "bin/sleep"),
|
("sleep", "bin/sleep"),
|
||||||
|
("lspci", "bin/lspci"),
|
||||||
("tst", "bin/tst"),
|
("tst", "bin/tst"),
|
||||||
// netutils
|
// netutils
|
||||||
("netconf", "sbin/netconf"),
|
("netconf", "sbin/netconf"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user