WIP: implement igbe driver

This commit is contained in:
Mark Poliakov 2025-02-10 09:15:25 +02:00
parent f1becafaaf
commit 0a46e6f57c
11 changed files with 1022 additions and 31 deletions

64
Cargo.lock generated
View File

@ -17,7 +17,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
"thiserror",
]
@ -193,7 +193,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -213,7 +213,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -315,7 +315,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -386,7 +386,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -479,7 +479,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -515,7 +515,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -567,7 +567,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -787,7 +787,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -1004,7 +1004,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -1612,7 +1612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
dependencies = [
"proc-macro2",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -1898,7 +1898,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -1992,9 +1992,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.96"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
@ -2009,7 +2009,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -2062,7 +2062,7 @@ checksum = "888d0c3c6db53c0fdab160d2ed5e12ba745383d3e85813f2ea0f2b1475ab553f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -2082,7 +2082,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -2130,7 +2130,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -2352,7 +2352,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
"wasm-bindgen-shared",
]
@ -2374,7 +2374,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -2689,6 +2689,21 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_igbe"
version = "0.1.0"
dependencies = [
"device-api",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_net_core",
"ygg_driver_pci",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_loopback"
version = "0.1.0"
@ -2912,6 +2927,7 @@ dependencies = [
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_net_core",
"ygg_driver_net_igbe",
"ygg_driver_net_loopback",
"ygg_driver_net_rtl81xx",
"ygg_driver_nvme",
@ -2959,7 +2975,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
"synstructure",
]
@ -2981,7 +2997,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]
[[package]]
@ -3001,7 +3017,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
"synstructure",
]
@ -3024,5 +3040,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
"syn 2.0.98",
]

View File

@ -67,6 +67,7 @@ kernel-arch-x86_64.workspace = true
kernel-arch-x86.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_net_igbe.path = "driver/net/igbe"
acpi.workspace = true

View File

@ -622,12 +622,21 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
dma_allocator: dma.clone(),
};
let instance = driver.probe(&device.info, &dma)?;
match driver.probe(&device.info, &dma) {
Ok(instance) => {
unsafe { instance.clone().init(cx) }?;
unsafe { instance.clone().init(cx) }?;
device.device.replace(instance);
device.driver_name.replace(driver.driver_name());
device.device.replace(instance);
device.driver_name.replace(driver.driver_name());
}
Err(error) => {
log::error!(
"{} ({}): {error:?}",
device.info.address,
driver.driver_name()
);
}
}
}
Ok(())

View File

@ -14,7 +14,7 @@ pub macro pci_driver_match {
}
pub macro pci_driver(
matches: [$($kind:ident $match:tt),+],
matches: [$($kind:ident $match:tt),+ $(,)?],
driver: $driver:tt
) {
#[link_section = ".init_array"]

View File

@ -339,7 +339,7 @@ pub trait PciConfigurationSpace {
Some(PciBaseAddress::Memory32(w0 & !0xF))
}
// TODO can 64-bit BARs not be on a 64-bit boundary?
2 => todo!(),
2 => None,
_ => unimplemented!(),
},
1 => todo!(),

View File

@ -0,0 +1,17 @@
[package]
name = "ygg_driver_net_igbe"
version = "0.1.0"
edition = "2024"
[dependencies]
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
ygg_driver_pci.path = "../../bus/pci"
ygg_driver_net_core.path = "../core"
log.workspace = true
tock-registers.workspace = true

View File

@ -0,0 +1,414 @@
#![no_std]
use core::mem::{self, MaybeUninit};
use alloc::{sync::Arc, vec::Vec};
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_util::{
sync::{IrqSafeSpinlock, Spinlock},
OneTimeInit,
};
use regs::{Regs, ICR};
use tock_registers::interfaces::Writeable;
use ygg_driver_net_core::{
interface::{NetworkDevice, NetworkInterfaceType},
register_interface, Packet,
};
use ygg_driver_pci::{
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
};
use yggdrasil_abi::net::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);
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;
}
}
struct Igbe {
regs: IrqSafeSpinlock<Regs>,
base: PciBaseAddress,
dma: Arc<dyn DmaAllocator>,
pci: PciDeviceInfo,
mac: OneTimeInit<MacAddress>,
rx_ring: OneTimeInit<Spinlock<RxRing>>,
tx_ring: OneTimeInit<IrqSafeSpinlock<TxRing>>,
nic: OneTimeInit<u32>,
}
impl Igbe {
pub fn new(
dma: Arc<dyn DmaAllocator>,
base: PciBaseAddress,
regs: Regs,
pci: PciDeviceInfo,
) -> Self {
Self {
dma,
base,
pci,
mac: OneTimeInit::new(),
regs: IrqSafeSpinlock::new(regs),
rx_ring: OneTimeInit::new(),
tx_ring: OneTimeInit::new(),
nic: OneTimeInit::new(),
}
}
}
impl Device for Igbe {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let msi_info = self
.pci
.map_interrupt(InterruptAffinity::Any, self.clone())?;
let rx_ring = RxRing::with_capacity(&*self.dma, 128, 2048 + 16)?;
let tx_ring = TxRing::with_capacity(&*self.dma, 128)?;
let mut regs = self.regs.lock();
regs.disable_interrupts();
let mac = regs.read_mac()?;
self.mac.init(mac);
regs.reset(1000000)?;
// Intel 8257x manuals say an additional interrupt disable is needed after a global reset
regs.disable_interrupts();
regs.set_link_up()?;
// Initialize Rx
regs.initialize_receiver(&rx_ring);
regs.initialize_transmitter(&tx_ring);
// If MSI(-x) was initialized, notify the NIC about it
if let Some(msi_info) = msi_info {
regs.initialize_ivar(msi_info.vector);
}
self.rx_ring.init(Spinlock::new(rx_ring));
self.tx_ring.init(IrqSafeSpinlock::new(tx_ring));
let nic = register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.nic.init(nic.id());
regs.enable_interrupts();
Ok(())
}
fn display_name(&self) -> &str {
"Intel Gigabit Ethernet"
}
}
impl InterruptHandler for Igbe {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let mut regs = self.regs.lock();
let cause = regs.interrupt_cause();
if cause.get() == 0 {
return false;
}
regs.clear_interrupts(cause.get());
let mut any = false;
if cause.matches_all(ICR::LSC::SET) {
let status = regs.read_link();
log::info!("igbe: link is {status}");
any = true;
}
if cause.matches_all(ICR::RXT0::SET) {
let mut rx = self.rx_ring.get().lock();
let nic = *self.nic.get();
let head = regs.rx_queue_head();
let tail = rx.handle_rx(&*self.dma, head, |packet, _| {
let packet = Packet::new(packet, 0, nic);
ygg_driver_net_core::receive_packet(packet).ok();
});
regs.set_rx_queue_tail(tail);
any = true;
}
if cause.matches_any(&[ICR::TXQE::SET, ICR::TXDW::SET]) {
let mut tx = self.tx_ring.get().lock();
let head = regs.tx_queue_head();
tx.handle_tx(head);
any = true;
}
if !any {
log::info!("igbe: unhandled irq {:#x}", cause.get());
}
any
}
}
impl NetworkDevice for Igbe {
fn read_hardware_address(&self) -> MacAddress {
*self.mac.get()
}
fn packet_prefix_size(&self) -> usize {
0
}
fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
DmaBuffer::new_uninit_slice(&*self.dma, len + 4)
}
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
let mut tx = self.tx_ring.get().lock();
let Ok(head) = tx.tx_now(buffer) else {
return Err(Error::WouldBlock);
};
let mut regs = self.regs.lock();
regs.set_tx_queue_tail(head);
Ok(())
}
}
pci_driver! {
matches: [
device (0x8086:0x10C9), // 82576 GbE
device (0x8086:0x1502), // 82579LM GbE (Lewisville)
],
driver: {
fn probe(
&self,
info: &PciDeviceInfo,
dma: &Arc<dyn DmaAllocator>,
) -> Result<Arc<dyn Device>, Error> {
let base = info
.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 {
PciBaseAddress::Memory32(_) | PciBaseAddress::Memory64(_) => {
command |= PciCommandRegister::ENABLE_MEMORY.bits();
command &= !PciCommandRegister::ENABLE_IO.bits();
}
PciBaseAddress::Io(_) => {
command |= PciCommandRegister::ENABLE_IO.bits();
command &= !PciCommandRegister::ENABLE_MEMORY.bits();
}
}
info.config_space.set_command(command);
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
let regs = unsafe { Regs::map(base) }?;
let device = Igbe::new(dma.clone(), base, regs, info.clone());
Ok(Arc::new(device))
}
fn driver_name(&self) -> &str {
"igbe"
}
}
}

View File

@ -0,0 +1,516 @@
#![allow(non_snake_case)]
use core::{cell::UnsafeCell, marker::PhantomData, time};
use libk::error::Error;
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy, RegisterLongName};
use ygg_driver_net_core::ethernet::{Duplex, EthernetLinkState, EthernetSpeed};
use ygg_driver_pci::PciBaseAddress;
use yggdrasil_abi::net::MacAddress;
use TXDCTL::LWTHRESH;
use crate::{RxRing, TxRing};
enum Inner {
Memory(RawDeviceMemoryMapping),
Io(u16),
}
enum Eeprom {
Present,
MappedMemory(RawDeviceMemoryMapping),
MappedIo(u16),
}
pub struct Regs {
inner: Inner,
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;
}
register_bitfields! {
u32,
pub CTRL [
SLU OFFSET(6) NUMBITS(1) [],
RST OFFSET(26) NUMBITS(1) [],
RFCE OFFSET(27) NUMBITS(1) [],
TFCE OFFSET(28) NUMBITS(1) [],
],
pub STATUS [
FD OFFSET(0) NUMBITS(1) [],
LU OFFSET(1) NUMBITS(1) [],
SPEED OFFSET(6) NUMBITS(2) [],
],
pub MDIC [
DATA OFFSET(0) NUMBITS(16) [],
REGADD OFFSET(16) NUMBITS(5) [],
PHYADD OFFSET(21) NUMBITS(5) [],
OP OFFSET(26) NUMBITS(2) [
Write = 0b01,
Read = 0b10
],
R OFFSET(28) NUMBITS(1) [],
I OFFSET(29) NUMBITS(1) [],
E OFFSET(30) NUMBITS(1) [],
],
pub ICR [
TXDW OFFSET(0) NUMBITS(1) [],
TXQE OFFSET(1) NUMBITS(1) [],
LSC OFFSET(2) NUMBITS(1) [],
RXT0 OFFSET(7) NUMBITS(1) [],
],
pub IMS [
TXDW OFFSET(0) NUMBITS(1) [],
TXQE OFFSET(1) NUMBITS(1) [],
LSC OFFSET(2) NUMBITS(1) [],
RXT0 OFFSET(7) NUMBITS(1) [],
],
pub RCTL [
EN OFFSET(1) NUMBITS(1) [],
UPE OFFSET(3) NUMBITS(1) [],
MPE OFFSET(4) NUMBITS(1) [],
BAM OFFSET(15) NUMBITS(1) [],
BSIZE OFFSET(16) NUMBITS(2) [
SIZE_2048 = 0b00,
SIZE_1024_16384 = 0b01,
SIZE_512_8192 = 0b10,
SIZE_256_4096 = 0b11
],
BSEX OFFSET(25) NUMBITS(1) [],
],
pub RDLEN0 [
LEN0 OFFSET(7) NUMBITS(13) [],
],
pub CPUVEC [
CPUVEC OFFSET(0) NUMBITS(32) [],
],
pub TCTL [
EN OFFSET(1) NUMBITS(1) [],
PSP OFFSET(3) NUMBITS(1) [],
CT OFFSET(4) NUMBITS(8) [],
COLD OFFSET(12) NUMBITS(10) [],
MULR OFFSET(28) NUMBITS(1) [],
],
pub TDLEN [
LEN OFFSET(7) NUMBITS(13) [],
],
pub TIDV [
IDV OFFSET(0) NUMBITS(16) [],
],
pub TXDCTL [
PTHRESH OFFSET(0) NUMBITS(6) [],
HTHRESH OFFSET(8) NUMBITS(8) [],
WTHRESH OFFSET(16) NUMBITS(6) [],
GRAN OFFSET(24) NUMBITS(1) [],
LWTHRESH OFFSET(25) NUMBITS(7) [],
],
pub TADV [
IDV OFFSET(0) NUMBITS(16) [],
],
}
pub mod IMC {
pub struct Register;
}
pub mod RDBAL0 {
pub struct Register;
}
pub mod RDBAH0 {
pub struct Register;
}
pub mod RDH0 {
pub struct Register;
}
pub mod RDT0 {
pub struct Register;
}
pub mod TIPG {
pub struct Register;
}
pub mod TDBAL {
pub struct Register;
}
pub mod TDBAH {
pub struct Register;
}
pub mod TDH {
pub struct Register;
}
pub mod TDT {
pub struct Register;
}
pub mod IVAR {
pub struct Register;
}
impl Reg for CTRL::Register {
const OFFSET: u16 = 0x0000;
}
impl Reg for STATUS::Register {
const OFFSET: u16 = 0x0008;
}
impl Reg for MDIC::Register {
const OFFSET: u16 = 0x0020;
}
impl Reg for ICR::Register {
const OFFSET: u16 = 0x00C0;
}
impl Reg for IMS::Register {
const OFFSET: u16 = 0x00D0;
}
impl Reg for IMC::Register {
const OFFSET: u16 = 0x00D8;
}
impl Reg for RCTL::Register {
const OFFSET: u16 = 0x0100;
}
impl Reg for RDBAL0::Register {
const OFFSET: u16 = 0x2800;
}
impl Reg for RDBAH0::Register {
const OFFSET: u16 = 0x2804;
}
impl Reg for RDLEN0::Register {
const OFFSET: u16 = 0x2808;
}
impl Reg for RDH0::Register {
const OFFSET: u16 = 0x2810;
}
impl Reg for RDT0::Register {
const OFFSET: u16 = 0x2818;
}
impl Reg for CPUVEC::Register {
const OFFSET: u16 = 0x2C10;
}
impl Reg for TCTL::Register {
const OFFSET: u16 = 0x0400;
}
impl Reg for TIPG::Register {
const OFFSET: u16 = 0x0410;
}
impl Reg for TDBAL::Register {
const OFFSET: u16 = 0x3800;
}
impl Reg for TDBAH::Register {
const OFFSET: u16 = 0x3804;
}
impl Reg for TDLEN::Register {
const OFFSET: u16 = 0x3808;
}
impl Reg for TDH::Register {
const OFFSET: u16 = 0x3810;
}
impl Reg for TDT::Register {
const OFFSET: u16 = 0x3818;
}
impl Reg for TIDV::Register {
const OFFSET: u16 = 0x3820;
}
impl Reg for TXDCTL::Register {
const OFFSET: u16 = 0x3828;
}
impl Reg for TADV::Register {
const OFFSET: u16 = 0x382C;
}
impl Reg for IVAR::Register {
const OFFSET: u16 = 0x1700;
}
impl Inner {
unsafe fn map(bar: PciBaseAddress) -> Result<Self, Error> {
let memory_base = match bar {
PciBaseAddress::Memory64(base) => PhysicalAddress::from_u64(base),
PciBaseAddress::Memory32(base) => PhysicalAddress::from_u32(base),
PciBaseAddress::Io(io_base) => {
return Ok(Self::Io(io_base));
}
};
RawDeviceMemoryMapping::map(memory_base.into_u64(), 0x4000, Default::default())
.map(Self::Memory)
}
fn get<R: Reg>(&mut self) -> u32 {
// let reg = reg as u16;
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!(),
}
}
fn extract<R: Reg + RegisterLongName>(&mut self) -> LocalRegisterCopy<u32, R> {
LocalRegisterCopy::new(self.get::<R>())
}
fn write<R: Reg + RegisterLongName>(&mut 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);
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!(),
}
}
fn modify<R: Reg + RegisterLongName>(&mut 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 {
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
// }
}
impl Eeprom {
fn read(&mut self, inner: &mut Inner, addr: u8) -> u16 {
match self {
Self::Present => todo!(),
Self::MappedMemory(mapping) => {
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);
let word = unsafe { ptr.read_volatile() };
(word >> shift) as u16
}
&mut Self::MappedIo(io_base) => todo!(),
}
}
}
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 {
// 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())?;
Eeprom::MappedMemory(mapping)
}
&Inner::Io(io) => todo!(),
};
//};
Ok(Self { inner, eeprom })
}
fn read_eeprom(&mut self, addr: u8) -> u16 {
self.eeprom.read(&mut self.inner, addr)
}
pub fn read_mac(&mut self) -> Result<MacAddress, Error> {
let w0 = self.read_eeprom(0);
let w1 = self.read_eeprom(1);
let w2 = self.read_eeprom(2);
if w0 == 0 && w1 == 0 && w2 == 0 {
log::error!("Could not read EEPROM MAC address");
return Err(Error::InvalidArgument);
}
let w0 = w0.to_le_bytes();
let w1 = w1.to_le_bytes();
let w2 = w2.to_le_bytes();
let octets = [w0[0], w0[1], w1[0], w1[1], w2[0], w2[1]];
Ok(MacAddress::from(octets))
}
pub fn reset(&mut self, mut timeout_cycles: u64) -> 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)
}
}
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();
}
Ok(())
}
pub fn read_link(&mut self) -> EthernetLinkState {
let status = self.inner.extract::<STATUS::Register>();
if status.matches_all(STATUS::LU::SET) {
let duplex = if status.matches_all(STATUS::FD::SET) {
Duplex::Full
} else {
Duplex::Half
};
let speed = match status.read(STATUS::SPEED) {
0b00 => EthernetSpeed::Speed10,
0b01 => EthernetSpeed::Speed100,
0b10 | 0b11 => EthernetSpeed::Speed1000,
_ => unreachable!(),
};
EthernetLinkState::Up(speed, duplex)
} else {
EthernetLinkState::Down
}
}
pub fn disable_interrupts(&mut self) {
self.inner.set::<IMC::Register>(0xFFFFFFFF);
}
pub fn clear_interrupts(&mut self, cause: u32) {
let _ = self.inner.get::<CPUVEC::Register>();
self.inner.set::<ICR::Register>(cause);
}
pub fn enable_interrupts(&mut self) {
self.inner.set::<IMS::Register>(0xFFFFFFFF);
// self.inner
// .modify(IMS::LSC::SET + IMS::RXT0::SET + IMS::TXDW::SET + IMS::TXQE::SET);
}
pub fn interrupt_cause(&mut self) -> LocalRegisterCopy<u32, ICR::Register> {
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);
self.inner
.set::<RDBAH0::Register>((rx_queue_base >> 32) as u32);
self.inner
.write(RDLEN0::LEN0.val((rx_ring.descriptors.len() / 8) as u32));
self.inner.set::<RDH0::Register>(0);
self.inner
.set::<RDT0::Register>((rx_ring.descriptors.len() - 1) as u32);
self.inner.write(
RCTL::EN::SET
+ RCTL::BSIZE::SIZE_2048
+ RCTL::BAM::SET
+ RCTL::UPE::SET
+ RCTL::MPE::SET,
);
}
pub fn initialize_transmitter(&mut self, tx_ring: &TxRing) {
let tx_queue_base = tx_ring.descriptors.bus_address().into_u64();
self.inner.set::<TDBAL::Register>(tx_queue_base as u32);
self.inner
.set::<TDBAH::Register>((tx_queue_base >> 32) as u32);
self.inner
.write(TDLEN::LEN.val((tx_ring.descriptors.len() / 8) as u32));
self.inner.set::<TDT::Register>(0);
self.inner.set::<TDH::Register>(0);
self.inner.set::<TIPG::Register>(0x0060200A);
self.inner.write(TIDV::IDV.val(100));
self.inner.write(TADV::IDV.val(100));
self.inner
.write(TCTL::EN::SET + TCTL::CT.val(15) + TCTL::COLD.val(63) + TCTL::PSP::SET);
}
pub fn initialize_ivar(&mut self, vector: usize) {
// Setup vector info for Tx/Rx queues 0
let mut val = 0;
for i in 0..4 {
val |= (0x80 | (vector as u32 & 0xFF)) << (8 * i);
}
self.inner.set::<IVAR::Register>(val);
}
pub fn rx_queue_head(&mut self) -> u16 {
self.inner.get::<RDH0::Register>() as u16
}
pub fn set_rx_queue_tail(&mut self, tail: u16) {
self.inner.set::<RDT0::Register>(tail as u32);
}
pub fn tx_queue_head(&mut self) -> u16 {
self.inner.get::<TDH::Register>() as u16
}
pub fn set_tx_queue_tail(&mut self, tail: u16) {
self.inner.set::<TDT::Register>(tail as u32);
}
}

View File

@ -69,6 +69,12 @@ extern crate ygg_driver_usb_xhci;
extern crate ygg_driver_virtio_gpu;
extern crate ygg_driver_virtio_net;
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
extern crate ygg_driver_net_igbe;
}
}
#[macro_use]
pub mod arch;

View File

@ -6,6 +6,7 @@ use crate::IntoArgs;
pub enum QemuNic {
VirtioPci { mac: Option<String> },
Rtl8139 { mac: Option<String> },
IntelGigabit { mac: Option<String> },
}
#[derive(Debug, PartialEq, Eq)]
@ -56,6 +57,15 @@ impl IntoArgs for QemuNic {
}
command.arg(val);
}
Self::IntelGigabit { mac } => {
command.arg("-device");
let mut val = "igb,netdev=net0".to_owned();
if let Some(mac) = mac {
val.push_str(",mac=");
val.push_str(mac);
}
command.arg(val);
}
}
}
}

View File

@ -23,6 +23,7 @@ enum QemuNetworkInterface {
#[default]
VirtioNet,
Rtl8139,
IntelGigabit,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
@ -325,6 +326,7 @@ fn add_devices_from_config(
let nic = match config.network.interface {
QemuNetworkInterface::VirtioNet => QemuNic::VirtioPci { mac },
QemuNetworkInterface::Rtl8139 => QemuNic::Rtl8139 { mac },
QemuNetworkInterface::IntelGigabit => QemuNic::IntelGigabit { mac },
};
devices.push(QemuDevice::NetworkTap {
nic,