igbe: cleanup igbe code

This commit is contained in:
Mark Poliakov 2025-02-11 18:36:41 +02:00
parent 90edc4c8ed
commit b836cf7fc7
3 changed files with 311 additions and 309 deletions

View File

@ -1,24 +1,20 @@
#![no_std]
use core::mem::{self, MaybeUninit};
use core::{mem::MaybeUninit, time::Duration};
use alloc::{sync::Arc, vec::Vec};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
};
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk::{dma::DmaBuffer, error::Error};
use libk_util::{
sync::{IrqSafeSpinlock, Spinlock},
OneTimeInit,
};
use regs::{Regs, ICR};
use tock_registers::interfaces::Writeable;
use ring::{RxRing, TxRing};
use ygg_driver_net_core::{
interface::{NetworkDevice, NetworkInterfaceType},
register_interface, RxPacket,
@ -26,209 +22,17 @@ use ygg_driver_net_core::{
use ygg_driver_pci::{
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
PciBaseAddress, PciConfigurationSpace,
};
use yggdrasil_abi::net::{link::LinkState, MacAddress};
extern crate alloc;
mod regs;
struct RxRing {
descriptors: DmaBuffer<[RxDescriptor]>,
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
buffer_size: usize,
tail: u16,
}
struct TxRing {
descriptors: DmaBuffer<[TxDescriptor]>,
buffers: Vec<Option<DmaBuffer<[u8]>>>,
// Consumer end
tail: u16,
// Producer end
head: u16,
}
#[repr(C)]
struct RxDescriptor {
address: BusAddress,
length: u16,
checksum: u16,
status: u8,
errors: u8,
special: u16,
}
#[repr(C)]
struct TxDescriptor {
address: BusAddress,
length: u16,
cso: u8,
cmd: u8,
sta: u8,
_0: u8,
css: u8,
special: u8,
}
impl RxRing {
pub fn with_capacity(
dma: &dyn DmaAllocator,
capacity: usize,
buffer_size: usize,
) -> Result<Self, Error> {
let buffers = (0..capacity)
.map(|_| DmaBuffer::new_uninit_slice(dma, buffer_size))
.collect::<Result<Vec<_>, _>>()?;
let descriptors = DmaBuffer::new_slice_with(
dma,
|i| RxDescriptor::new(buffers[i].bus_address(), buffer_size as u16),
capacity,
)?;
Ok(Self {
descriptors,
buffers,
tail: 0,
buffer_size,
})
}
// TODO move to background task/softirq to reduce amount of code run by the irq handler
pub fn handle_rx<F: FnMut(DmaBuffer<[u8]>, usize)>(
&mut self,
dma: &dyn DmaAllocator,
head: u16,
mut handler: F,
) -> u16 {
let capacity = self.descriptors.len();
while self.tail != head {
let index = self.tail as usize;
// Replace the buffer
let new_buffer = DmaBuffer::new_uninit_slice(dma, self.buffer_size).unwrap();
let new_buffer_address = new_buffer.bus_address();
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
let descriptor = &mut self.descriptors[index];
if descriptor.errors & !1 != 0 {
log::warn!("igbe: drop erroneous packet {:#x}", descriptor.errors);
} else {
let len = descriptor.length as usize;
handler(buffer, len);
}
// Replace the descriptor
*descriptor = RxDescriptor::new(new_buffer_address, self.buffer_size as u16);
self.tail = (self.tail + 1) & (capacity as u16 - 1);
}
(self.tail + capacity as u16 - 1) & (capacity as u16 - 1)
}
}
impl TxRing {
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
let buffers = (0..capacity).map(|_| None).collect::<Vec<_>>();
let descriptors = DmaBuffer::new_slice_with(dma, |_| TxDescriptor::empty(), capacity)?;
Ok(Self {
descriptors,
buffers,
head: 0,
tail: 0,
})
}
pub fn handle_tx(&mut self, head: u16) {
self.tail = self.head;
}
pub fn tx_now(&mut self, buffer: DmaBuffer<[u8]>) -> Result<u16, DmaBuffer<[u8]>> {
// Queue full
let capacity = self.descriptors.len();
if (self.head + 1) & (capacity as u16 - 1) == self.tail {
log::warn!("igbe: tx queue full");
return Err(buffer);
}
let index = self.head as usize;
let descriptor = &mut self.descriptors[index];
// Only generate interrupts for every 1/4th of the buffer
let quarter = capacity / 4;
descriptor.setup_tx(
buffer.bus_address(),
buffer.len() as u16 - 4,
index % quarter == quarter - 1,
);
self.descriptors.cache_flush_element(index, true);
self.buffers[index] = Some(buffer);
self.head = (self.head + 1) & (capacity as u16 - 1);
Ok(self.head)
}
}
impl RxDescriptor {
pub fn new(address: BusAddress, length: u16) -> Self {
Self {
address,
length,
checksum: 0,
status: 0,
errors: 0,
special: 0,
}
}
}
impl TxDescriptor {
// Descriptor done
const STA_DD: u8 = 1 << 0;
// End of packet
const CMD_EOP: u8 = 1 << 0;
// Insert MAC FCS
const CMD_IFCS: u8 = 1 << 1;
// Insert checksum
const CMD_IC: u8 = 1 << 2;
// Report status
const CMD_RS: u8 = 1 << 3;
pub const fn empty() -> Self {
Self {
address: BusAddress::ZERO,
length: 0,
cso: 0,
cmd: 0,
sta: Self::STA_DD,
_0: 0,
css: 0,
special: 0,
}
}
pub fn setup_tx(&mut self, address: BusAddress, length: u16, ioc: bool) {
let mut cmd = Self::CMD_EOP | Self::CMD_IFCS;
if ioc {
cmd |= Self::CMD_RS;
}
self.address = address;
self.length = length;
self.css = 0;
self.cso = 0;
self.sta = 0;
self.cmd = cmd;
self.special = 0;
}
}
mod ring;
struct Igbe {
regs: IrqSafeSpinlock<Regs>,
base: PciBaseAddress,
dma: Arc<dyn DmaAllocator>,
pci: PciDeviceInfo,
@ -239,15 +43,9 @@ struct Igbe {
}
impl Igbe {
pub fn new(
dma: Arc<dyn DmaAllocator>,
base: PciBaseAddress,
regs: Regs,
pci: PciDeviceInfo,
) -> Self {
pub fn new(dma: Arc<dyn DmaAllocator>, regs: Regs, pci: PciDeviceInfo) -> Self {
Self {
dma,
base,
pci,
mac: OneTimeInit::new(),
regs: IrqSafeSpinlock::new(regs),
@ -273,7 +71,7 @@ impl Device for Igbe {
regs.disable_interrupts();
let mac = regs.read_mac()?;
self.mac.init(mac);
regs.reset(1000000)?;
regs.reset(Duration::from_millis(200))?;
// Intel 8257x manuals say an additional interrupt disable is needed after a global reset
regs.disable_interrupts();
regs.set_link_up()?;
@ -390,24 +188,20 @@ pci_driver! {
.config_space
.bar(0).ok_or(Error::InvalidArgument)?;
let mut command = info.config_space.command();
command |= (PciCommandRegister::BUS_MASTER | PciCommandRegister::DISABLE_INTERRUPTS).bits();
match base {
let use_mmio = match base {
PciBaseAddress::Memory32(_) | PciBaseAddress::Memory64(_) => {
command |= PciCommandRegister::ENABLE_MEMORY.bits();
command &= !PciCommandRegister::ENABLE_IO.bits();
true
}
PciBaseAddress::Io(_) => {
command |= PciCommandRegister::ENABLE_IO.bits();
command &= !PciCommandRegister::ENABLE_MEMORY.bits();
false
}
}
info.config_space.set_command(command);
};
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
info.set_command(true, use_mmio, !use_mmio, true);
let regs = unsafe { Regs::map(base) }?;
let device = Igbe::new(dma.clone(), base, regs, info.clone());
let device = Igbe::new(dma.clone(), regs, info.clone());
Ok(Arc::new(device))
}

View File

@ -1,26 +1,35 @@
#![allow(non_snake_case)]
use core::{cell::UnsafeCell, marker::PhantomData, time};
use core::time::Duration;
use libk::error::Error;
use libk::{
error::Error,
task::runtime::{psleep, pwait},
};
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy, RegisterLongName};
use ygg_driver_net_core::ephy::{MdioBus, PhyAccess, GBESR};
use ygg_driver_pci::PciBaseAddress;
use yggdrasil_abi::net::{
link::{Duplex, EthernetLinkState, EthernetSpeed},
MacAddress,
};
use TXDCTL::LWTHRESH;
use crate::{RxRing, TxRing};
enum Inner {
Memory(RawDeviceMemoryMapping),
// TODO I/O register access not yet implemented
#[allow(unused)]
Io(u16),
}
enum Eeprom {
// TODO not yet handled
#[allow(unused)]
Present,
MappedMemory(RawDeviceMemoryMapping),
// TODO I/O register access not yet implemented
#[allow(unused)]
MappedIo(u16),
}
@ -29,17 +38,6 @@ pub struct Regs {
eeprom: Eeprom,
}
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
// #[repr(u16)]
// pub enum Register {
// CTRL = 0x0000,
// STATUS = 0x0008,
// EEPROM = 0x0014,
// IMS = 0x00D0,
//
// RCTL = 0x0100,
// }
pub trait Reg {
const OFFSET: u16;
}
@ -241,95 +239,139 @@ impl Inner {
}
};
RawDeviceMemoryMapping::map(memory_base.into_u64(), 0x4000, Default::default())
.map(Self::Memory)
unsafe {
RawDeviceMemoryMapping::map(memory_base.into_u64(), 0x4000, Default::default())
.map(Self::Memory)
}
}
fn get<R: Reg>(&mut self) -> u32 {
// let reg = reg as u16;
fn get<R: Reg>(&self) -> u32 {
match self {
Self::Memory(mapping) => {
let ptr: *const u32 =
core::ptr::with_exposed_provenance(mapping.address + R::OFFSET as usize);
unsafe { ptr.read_volatile() }
}
Self::Io(io) => todo!(),
Self::Io(_io_base) => todo!(),
}
}
fn extract<R: Reg + RegisterLongName>(&mut self) -> LocalRegisterCopy<u32, R> {
fn extract<R: Reg + RegisterLongName>(&self) -> LocalRegisterCopy<u32, R> {
LocalRegisterCopy::new(self.get::<R>())
}
fn write<R: Reg + RegisterLongName>(&mut self, value: FieldValue<u32, R>) {
fn write<R: Reg + RegisterLongName>(&self, value: FieldValue<u32, R>) {
self.set::<R>(value.value);
}
fn set<R: Reg>(&mut self, value: u32) {
// log::info!("write({:#x}, {:#x})", R::OFFSET, value);
fn set<R: Reg>(&self, value: u32) {
match self {
Self::Memory(mapping) => {
let ptr: *mut u32 =
core::ptr::with_exposed_provenance_mut(mapping.address + R::OFFSET as usize);
unsafe { ptr.write_volatile(value) };
}
Self::Io(io) => todo!(),
Self::Io(_io_base) => todo!(),
}
}
fn modify<R: Reg + RegisterLongName>(&mut self, modify: FieldValue<u32, R>) {
fn modify<R: Reg + RegisterLongName>(&self, modify: FieldValue<u32, R>) {
let mut value = self.get::<R>();
value &= !modify.mask();
value |= modify.value;
self.set::<R>(value);
}
fn matches_all<R: Reg + RegisterLongName>(&mut self, pattern: FieldValue<u32, R>) -> bool {
fn matches_all<R: Reg + RegisterLongName>(&self, pattern: FieldValue<u32, R>) -> bool {
pattern.matches_all(self.get::<R>())
}
// fn detect_eeprom(&mut self, mut timeout_cycles: u64) -> bool {
// self.write(Register::EEPROM, 0x01);
// while timeout_cycles > 0 && self.read(Register::EEPROM) & 0x10 == 0 {
// core::hint::spin_loop();
// timeout_cycles -= 1;
// }
// timeout_cycles > 0
// }
fn matches_any<R: Reg + RegisterLongName>(&self, pattern: &[FieldValue<u32, R>]) -> bool {
let value = self.get::<R>();
for pattern in pattern {
if pattern.matches_all(value) {
return true;
}
}
false
}
}
impl Eeprom {
fn read(&mut self, inner: &mut Inner, addr: u8) -> u16 {
fn read(&self, _inner: &mut Inner, addr: u8) -> u16 {
match self {
Self::Present => todo!(),
Self::MappedMemory(mapping) => {
// EEPROM registers are read in words, not bytes/halves
let shift = (addr as usize & 1) << 4;
let reg = (addr as usize & !1) << 1;
let ptr: *const u32 = core::ptr::with_exposed_provenance(mapping.address + reg);
// This is safe, region is mapped as device memory
let word = unsafe { ptr.read_volatile() };
(word >> shift) as u16
}
&mut Self::MappedIo(io_base) => todo!(),
&Self::MappedIo(_io_base) => todo!(),
}
}
}
impl MdioBus for Regs {
fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error> {
self.inner.write(
MDIC::PHYADD.val(phyaddr as u32) + MDIC::REGADD.val(regaddr as u32) + MDIC::OP::Read,
);
pwait(
Duration::from_millis(100),
Duration::from_millis(10),
|| self.inner.matches_any(&[MDIC::R::SET, MDIC::E::SET]),
)?;
let mdic = self.inner.extract();
if mdic.matches_all(MDIC::E::SET) {
return Err(Error::InvalidOperation);
}
Ok(mdic.read(MDIC::DATA) as u16)
}
fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error> {
self.inner.write(
MDIC::PHYADD.val(phyaddr as u32)
+ MDIC::REGADD.val(regaddr as u32)
+ MDIC::DATA.val(value as u32)
+ MDIC::OP::Write,
);
pwait(
Duration::from_millis(100),
Duration::from_millis(10),
|| self.inner.matches_any(&[MDIC::R::SET, MDIC::E::SET]),
)?;
if self.inner.matches_all(MDIC::E::SET) {
return Err(Error::InvalidOperation);
}
Ok(())
}
}
impl Regs {
pub unsafe fn map(bar: PciBaseAddress) -> Result<Self, Error> {
let mut inner = Inner::map(bar)?;
// let eeprom = if inner.detect_eeprom(10000) {
// Eeprom::Present
// } else {
let inner = unsafe { Inner::map(bar) }?;
// Map EEPROM registers
let eeprom = match &inner {
Inner::Memory(mapping) => {
let base = mapping.physical_base + ((mapping.address & 0xFFF) + 0x5400) as u64;
let mapping = RawDeviceMemoryMapping::map(base, 0x10, Default::default())?;
let mapping =
unsafe { RawDeviceMemoryMapping::map(base, 0x10, Default::default()) }?;
Eeprom::MappedMemory(mapping)
}
&Inner::Io(io) => todo!(),
&Inner::Io(_io_base) => todo!(),
};
//};
Ok(Self { inner, eeprom })
}
@ -353,38 +395,25 @@ impl Regs {
Ok(MacAddress::from(octets))
}
pub fn reset(&mut self, mut timeout_cycles: u64) -> Result<(), Error> {
pub fn reset(&mut self, timeout: Duration) -> Result<(), Error> {
self.inner.write(CTRL::RST::SET);
for _ in 0..10000 {
core::hint::spin_loop();
}
while timeout_cycles > 0 && self.inner.matches_all(CTRL::RST::SET) {
timeout_cycles -= 1;
}
if timeout_cycles > 0 {
Ok(())
} else {
Err(Error::TimedOut)
}
psleep(Duration::from_millis(10));
pwait(timeout, Duration::from_millis(10), || {
self.inner.matches_all(CTRL::RST::CLEAR)
})
}
pub fn set_link_up(&mut self) -> Result<(), Error> {
self.inner
.modify(CTRL::SLU::SET + CTRL::RFCE::SET + CTRL::TFCE::SET);
// Reset the PHY
self.mdio_write(0x00, 1 << 15, 1000)?;
for _ in 0..1000 {
core::hint::spin_loop();
}
// Start auto-negotiation
self.mdio_write(0x00, (1 << 12) | (1 << 9), 1000)?;
for _ in 0..1000 {
core::hint::spin_loop();
}
let phy = PhyAccess::new(self, 0x01);
let (id0, id1) = phy.id()?;
log::info!("PHY {:04x}:{:04x}", id0, id1);
phy.reset(Duration::from_millis(200))?;
phy.setup_link(true, GBESR::empty())?;
Ok(())
}
@ -431,27 +460,6 @@ impl Regs {
self.inner.extract()
}
pub fn mdio_write(&mut self, reg: u8, value: u16, mut timeout: u64) -> Result<(), Error> {
self.inner.write(
MDIC::DATA.val(value as u32)
+ MDIC::PHYADD.val(1)
+ MDIC::REGADD.val(reg as u32)
+ MDIC::OP::Write,
);
while timeout > 0 && self.inner.matches_all(MDIC::R::CLEAR + MDIC::E::CLEAR) {
timeout -= 1;
}
if timeout > 0 {
Ok(())
} else {
Err(Error::TimedOut)
}
}
pub fn mdio_read(&mut self, reg: u8, mut timeout: u64) -> Result<u16, Error> {
todo!()
}
pub fn initialize_receiver(&mut self, rx_ring: &RxRing) {
let rx_queue_base = rx_ring.descriptors.bus_address().into_u64();
self.inner.set::<RDBAL0::Register>(rx_queue_base as u32);

View File

@ -0,0 +1,200 @@
use core::mem::{self, MaybeUninit};
use alloc::vec::Vec;
use device_api::dma::DmaAllocator;
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
pub(crate) struct RxRing {
pub(crate) descriptors: DmaBuffer<[RxDescriptor]>,
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
buffer_size: usize,
tail: u16,
}
pub(crate) struct TxRing {
pub(crate) descriptors: DmaBuffer<[TxDescriptor]>,
buffers: Vec<Option<DmaBuffer<[u8]>>>,
// Consumer end
tail: u16,
// Producer end
head: u16,
}
#[repr(C)]
pub(crate) struct RxDescriptor {
address: BusAddress,
length: u16,
checksum: u16,
status: u8,
errors: u8,
special: u16,
}
#[repr(C)]
pub(crate) struct TxDescriptor {
address: BusAddress,
length: u16,
cso: u8,
cmd: u8,
sta: u8,
_0: u8,
css: u8,
special: u8,
}
impl RxRing {
pub fn with_capacity(
dma: &dyn DmaAllocator,
capacity: usize,
buffer_size: usize,
) -> Result<Self, Error> {
let buffers = (0..capacity)
.map(|_| DmaBuffer::new_uninit_slice(dma, buffer_size))
.collect::<Result<Vec<_>, _>>()?;
let descriptors = DmaBuffer::new_slice_with(
dma,
|i| RxDescriptor::new(buffers[i].bus_address(), buffer_size as u16),
capacity,
)?;
Ok(Self {
descriptors,
buffers,
tail: 0,
buffer_size,
})
}
// TODO move to background task/softirq to reduce amount of code run by the irq handler
pub fn handle_rx<F: FnMut(DmaBuffer<[u8]>, usize)>(
&mut self,
dma: &dyn DmaAllocator,
head: u16,
mut handler: F,
) -> u16 {
let capacity = self.descriptors.len();
while self.tail != head {
let index = self.tail as usize;
// Replace the buffer
let new_buffer = DmaBuffer::new_uninit_slice(dma, self.buffer_size).unwrap();
let new_buffer_address = new_buffer.bus_address();
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
let descriptor = &mut self.descriptors[index];
if descriptor.errors & !1 != 0 {
log::warn!("igbe: drop erroneous packet {:#x}", descriptor.errors);
} else {
let len = descriptor.length as usize;
handler(buffer, len);
}
// Replace the descriptor
*descriptor = RxDescriptor::new(new_buffer_address, self.buffer_size as u16);
self.tail = (self.tail + 1) & (capacity as u16 - 1);
}
(self.tail + capacity as u16 - 1) & (capacity as u16 - 1)
}
}
impl TxRing {
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
let buffers = (0..capacity).map(|_| None).collect::<Vec<_>>();
let descriptors = DmaBuffer::new_slice_with(dma, |_| TxDescriptor::empty(), capacity)?;
Ok(Self {
descriptors,
buffers,
head: 0,
tail: 0,
})
}
pub fn handle_tx(&mut self, head: u16) {
self.tail = head;
}
pub fn tx_now(&mut self, buffer: DmaBuffer<[u8]>) -> Result<u16, DmaBuffer<[u8]>> {
// Queue full
let capacity = self.descriptors.len();
if (self.head + 1) & (capacity as u16 - 1) == self.tail {
log::warn!("igbe: tx queue full");
return Err(buffer);
}
let index = self.head as usize;
let descriptor = &mut self.descriptors[index];
// Only generate interrupts for every 1/4th of the buffer
let quarter = capacity / 4;
descriptor.setup_tx(
buffer.bus_address(),
buffer.len() as u16 - 4,
index % quarter == quarter - 1,
);
self.descriptors.cache_flush_element(index, true);
self.buffers[index] = Some(buffer);
self.head = (self.head + 1) & (capacity as u16 - 1);
Ok(self.head)
}
}
impl RxDescriptor {
pub fn new(address: BusAddress, length: u16) -> Self {
Self {
address,
length,
checksum: 0,
status: 0,
errors: 0,
special: 0,
}
}
}
impl TxDescriptor {
// Descriptor done
const STA_DD: u8 = 1 << 0;
// End of packet
const CMD_EOP: u8 = 1 << 0;
// Insert MAC FCS
const CMD_IFCS: u8 = 1 << 1;
// // Insert checksum
// const CMD_IC: u8 = 1 << 2;
// Report status
const CMD_RS: u8 = 1 << 3;
pub const fn empty() -> Self {
Self {
address: BusAddress::ZERO,
length: 0,
cso: 0,
cmd: 0,
sta: Self::STA_DD,
_0: 0,
css: 0,
special: 0,
}
}
pub fn setup_tx(&mut self, address: BusAddress, length: u16, ioc: bool) {
let mut cmd = Self::CMD_EOP | Self::CMD_IFCS;
if ioc {
cmd |= Self::CMD_RS;
}
self.address = address;
self.length = length;
self.css = 0;
self.cso = 0;
self.sta = 0;
self.cmd = cmd;
self.special = 0;
}
}