net: implement rtl8168 driver
This commit is contained in:
parent
50a760985b
commit
2f46a36255
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -57,7 +57,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "acpi-system"
|
||||
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 = [
|
||||
"acpi",
|
||||
"aml",
|
||||
@ -1838,9 +1838,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@ -2229,9 +2229,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@ -2531,9 +2531,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.24"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
|
||||
checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -2734,6 +2734,7 @@ dependencies = [
|
||||
"acpi",
|
||||
"bitflags 2.8.0",
|
||||
"device-api",
|
||||
"kernel-arch-x86",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
|
@ -202,7 +202,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
|
||||
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)
|
||||
// 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()
|
||||
.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());
|
||||
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
|
||||
|
@ -18,6 +18,7 @@ tock-registers.workspace = true
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
ygg_driver_acpi.path = "../../acpi"
|
||||
acpi.workspace = true
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1,9 +1,12 @@
|
||||
//! PCI capability structures and queries
|
||||
|
||||
use core::mem::offset_of;
|
||||
|
||||
use alloc::{sync::Arc, vec, vec::Vec};
|
||||
use device_api::interrupt::{
|
||||
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
|
||||
};
|
||||
use kernel_arch_x86::intrinsics;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
@ -11,6 +14,8 @@ use tock_registers::{
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::PciBaseAddress;
|
||||
|
||||
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
/// Power management capability entry
|
||||
pub struct PowerManagementCapability;
|
||||
|
||||
/// MSI-X capability query
|
||||
pub struct MsiXCapability;
|
||||
|
||||
@ -57,6 +65,15 @@ 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 {
|
||||
@ -68,8 +85,20 @@ pub struct MsiXEntry {
|
||||
pub control: ReadWrite<u32>,
|
||||
}
|
||||
|
||||
enum MsiXVectorTableAccess<'a> {
|
||||
Memory(DeviceMemoryIoMut<'a, [MsiXEntry]>),
|
||||
Io(u16),
|
||||
}
|
||||
|
||||
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
|
||||
@ -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 {
|
||||
const ID: PciCapabilityId = PciCapabilityId::MsiX;
|
||||
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> {
|
||||
// TODO use pending bits as well
|
||||
/// 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 {
|
||||
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));
|
||||
|
||||
unsafe { MsiXVectorTable::from_raw_parts(base.add(table_offset), table_size) }
|
||||
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,
|
||||
)
|
||||
},
|
||||
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
|
||||
@ -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<'_> {
|
||||
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())?;
|
||||
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) {
|
||||
for vector in self.vectors.iter_mut() {
|
||||
vector.set_masked(true);
|
||||
for i in 0..self.len {
|
||||
self.access.set_vector_masked(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,26 +478,15 @@ impl MsiXVectorTable<'_> {
|
||||
|
||||
for (i, info) in range.iter().enumerate() {
|
||||
let index = i + start;
|
||||
self.vectors[index].address.set(info.address as _);
|
||||
self.vectors[index].data.set(info.value);
|
||||
self.vectors[index].set_masked(false);
|
||||
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 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> {
|
||||
pub fn register<C: MessageInterruptController + ?Sized>(
|
||||
&mut self,
|
||||
|
@ -11,7 +11,7 @@ use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
|
||||
PciAddress, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
|
||||
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
|
||||
};
|
||||
|
||||
/// Describes a PCI device
|
||||
@ -53,7 +53,7 @@ pub enum PciInterruptPin {
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum PreferredInterruptMode {
|
||||
Msi,
|
||||
Msi(bool),
|
||||
Legacy,
|
||||
}
|
||||
|
||||
@ -85,6 +85,7 @@ pub enum PciMatch {
|
||||
}
|
||||
|
||||
pub struct PciDriver {
|
||||
#[allow(unused)]
|
||||
pub(crate) name: &'static str,
|
||||
pub(crate) check: PciMatch,
|
||||
pub(crate) probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
|
||||
@ -98,29 +99,77 @@ pub struct PciBusDevice {
|
||||
}
|
||||
|
||||
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 self.segment.has_msi && preferred_mode == PreferredInterruptMode::Msi {
|
||||
if let Some(mut msix) = self.config_space.capability::<MsiXCapability>() {
|
||||
let mut vt = msix.vector_table().unwrap();
|
||||
|
||||
let configured_mode = if self.segment.has_msi
|
||||
&& let PreferredInterruptMode::Msi(want_msix) = preferred_mode
|
||||
{
|
||||
// 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();
|
||||
|
||||
msix.set_function_mask(false);
|
||||
msix.set_enabled(true);
|
||||
|
||||
ConfiguredInterruptMode::MsiX(vt)
|
||||
} else if self.config_space.capability::<MsiCapability>().is_some() {
|
||||
ConfiguredInterruptMode::Msi
|
||||
} else {
|
||||
self.legacy_interrupt_mode()
|
||||
result = Some(ConfiguredInterruptMode::MsiX(vt));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Ignore preferred_mode, the only supported is Legacy
|
||||
self.legacy_interrupt_mode()
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Ignore preferred_mode, the only supported is Legacy
|
||||
self.legacy_interrupt_mode()
|
||||
};
|
||||
IrqSafeRwLock::new(InterruptConfig {
|
||||
preferred_mode,
|
||||
configured_mode,
|
||||
|
@ -33,6 +33,14 @@ impl PciInterruptMap {
|
||||
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 {
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! PCI/PCIe bus interfaces
|
||||
#![no_std]
|
||||
#![feature(let_chains)]
|
||||
#![allow(clippy::missing_transmute_annotations)]
|
||||
|
||||
extern crate alloc;
|
||||
@ -18,7 +19,7 @@ use libk::fs::sysfs::{self, object::KObject};
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use space::legacy;
|
||||
use yggdrasil_abi::error::Error;
|
||||
use yggdrasil_abi::{error::Error, primitive_enum};
|
||||
|
||||
pub mod capability;
|
||||
pub mod device;
|
||||
@ -34,6 +35,7 @@ pub use space::{
|
||||
|
||||
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;
|
||||
@ -78,20 +80,28 @@ pub enum PciBaseAddress {
|
||||
Io(u16),
|
||||
}
|
||||
|
||||
/// 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,
|
||||
primitive_enum! {
|
||||
pub enum PciCapabilityId: u8 {
|
||||
PowerManagement = 0x01,
|
||||
Msi = 0x05,
|
||||
VendorSpecific = 0x09,
|
||||
MsiX = 0x11,
|
||||
}
|
||||
}
|
||||
// /// 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
|
||||
#[allow(unused)]
|
||||
@ -429,9 +439,9 @@ impl PciBusManager {
|
||||
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
|
||||
/// drivers
|
||||
pub fn setup_bus_devices() -> Result<(), Error> {
|
||||
log::info!("Setting up bus devices");
|
||||
// log::info!("Setting up bus devices");
|
||||
Self::walk_bus_devices(|device| {
|
||||
log::info!("Set up {}", device.info.address);
|
||||
// log::info!("Set up {}", device.info.address);
|
||||
setup_bus_device(device)?;
|
||||
Ok(true)
|
||||
})
|
||||
@ -610,7 +620,7 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
||||
for driver in drivers.iter() {
|
||||
if driver.check.check_device(&device.info) {
|
||||
// TODO add the device to the bus
|
||||
log::debug!(" -> {:?}", driver.name);
|
||||
// log::debug!(" -> {:?}", driver.name);
|
||||
let instance = (driver.probe)(&device.info)?;
|
||||
|
||||
unsafe { instance.clone().init() }?;
|
||||
|
@ -107,10 +107,13 @@ pub(crate) fn make_sysfs_object(
|
||||
for (capability, offset, _) in state.info.config_space.capability_iter() {
|
||||
write!(output, "{offset:04x}:").ok();
|
||||
match capability {
|
||||
PciCapabilityId::Msi => write!(output, "msi").ok(),
|
||||
PciCapabilityId::MsiX => write!(output, "msix").ok(),
|
||||
PciCapabilityId::VendorSpecific => write!(output, "vendor-specific").ok(),
|
||||
PciCapabilityId::Unknown => write!(output, "unknown").ok(),
|
||||
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();
|
||||
}
|
||||
|
@ -75,12 +75,12 @@ pub struct CapabilityIterator<'s, S: PciConfigurationSpace + ?Sized> {
|
||||
}
|
||||
|
||||
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> {
|
||||
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 next_pointer = self.space.read_u8(offset + 1);
|
||||
|
||||
@ -374,7 +374,7 @@ pub trait PciConfigurationSpace {
|
||||
/// 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 == 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))
|
||||
} else {
|
||||
None
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::mem::size_of;
|
||||
use core::{fmt, mem::size_of};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use bytemuck::Pod;
|
||||
@ -27,6 +27,37 @@ pub struct L2Packet {
|
||||
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 {
|
||||
pub fn ethernet_frame(&self) -> &EthernetFrame {
|
||||
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 libk::error::Error;
|
||||
use rtl8139::Rtl8139;
|
||||
use rtl8168::Rtl8168;
|
||||
use ygg_driver_pci::{
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
||||
@ -12,13 +13,28 @@ use ygg_driver_pci::{
|
||||
extern crate alloc;
|
||||
|
||||
pub mod rtl8139;
|
||||
pub mod rtl8168;
|
||||
|
||||
pub fn probe_8169(_info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||
todo!()
|
||||
pub fn probe_8168(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||
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> {
|
||||
info.init_interrupts(PreferredInterruptMode::Msi)?;
|
||||
info.init_interrupts(PreferredInterruptMode::Msi(false))?;
|
||||
|
||||
// Enable MMIO + interrupts + bus mastering
|
||||
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 xhci = Arc::new(Xhci::new(regs)?);
|
||||
|
||||
info.init_interrupts(PreferredInterruptMode::Msi)?;
|
||||
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
|
||||
|
||||
Ok(xhci)
|
||||
|
@ -176,7 +176,7 @@ impl<T: Transport + 'static> VirtioNet<T> {
|
||||
transmit_count: usize,
|
||||
) -> Result<(), Error> {
|
||||
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())?;
|
||||
|
||||
info.map(|info| info.vector as u16)
|
||||
|
@ -70,13 +70,13 @@ pub fn register_pci_drivers() {
|
||||
Some(0x01),
|
||||
ygg_driver_ahci::probe,
|
||||
);
|
||||
// ygg_driver_pci::register_class_driver(
|
||||
// "USB xHCI",
|
||||
// 0x0C,
|
||||
// Some(0x03),
|
||||
// Some(0x30),
|
||||
// ygg_driver_usb_xhci::probe,
|
||||
// );
|
||||
ygg_driver_pci::register_class_driver(
|
||||
"USB xHCI",
|
||||
0x0C,
|
||||
Some(0x03),
|
||||
Some(0x30),
|
||||
ygg_driver_usb_xhci::probe,
|
||||
);
|
||||
ygg_driver_pci::register_vendor_driver(
|
||||
"Virtio PCI GPU Device",
|
||||
0x1AF4,
|
||||
@ -96,10 +96,10 @@ pub fn register_pci_drivers() {
|
||||
ygg_driver_net_rtl81xx::probe_8139,
|
||||
);
|
||||
ygg_driver_pci::register_vendor_driver(
|
||||
"Realtek RTL8169/8110 Gigabit Ethernet",
|
||||
"Realtek RTL8168/8111 Gigabit Ethernet",
|
||||
0x10EC,
|
||||
0x8169,
|
||||
ygg_driver_net_rtl81xx::probe_8169,
|
||||
0x8168,
|
||||
ygg_driver_net_rtl81xx::probe_8168,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ struct Inner {
|
||||
pub struct IoApic {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
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 {
|
||||
|
@ -259,7 +259,7 @@ impl MessageInterruptController for LocalApic {
|
||||
);
|
||||
|
||||
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 {
|
||||
address,
|
||||
|
Loading…
x
Reference in New Issue
Block a user