diff --git a/Cargo.lock b/Cargo.lock index f1b12b8f..097c4ddd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8bc780ba..e2f4f206 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -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 diff --git a/kernel/driver/bus/pci/src/lib.rs b/kernel/driver/bus/pci/src/lib.rs index adb69213..616fdaef 100644 --- a/kernel/driver/bus/pci/src/lib.rs +++ b/kernel/driver/bus/pci/src/lib.rs @@ -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(()) diff --git a/kernel/driver/bus/pci/src/macros.rs b/kernel/driver/bus/pci/src/macros.rs index 6b8786f6..bb7e3f09 100644 --- a/kernel/driver/bus/pci/src/macros.rs +++ b/kernel/driver/bus/pci/src/macros.rs @@ -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"] diff --git a/kernel/driver/bus/pci/src/space/mod.rs b/kernel/driver/bus/pci/src/space/mod.rs index 0c53156f..bd71551f 100644 --- a/kernel/driver/bus/pci/src/space/mod.rs +++ b/kernel/driver/bus/pci/src/space/mod.rs @@ -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!(), diff --git a/kernel/driver/net/igbe/Cargo.toml b/kernel/driver/net/igbe/Cargo.toml new file mode 100644 index 00000000..ac947342 --- /dev/null +++ b/kernel/driver/net/igbe/Cargo.toml @@ -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 diff --git a/kernel/driver/net/igbe/src/lib.rs b/kernel/driver/net/igbe/src/lib.rs new file mode 100644 index 00000000..932574ad --- /dev/null +++ b/kernel/driver/net/igbe/src/lib.rs @@ -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]>>, + buffer_size: usize, + tail: u16, +} + +struct TxRing { + descriptors: DmaBuffer<[TxDescriptor]>, + buffers: Vec>>, + // 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 { + let buffers = (0..capacity) + .map(|_| DmaBuffer::new_uninit_slice(dma, buffer_size)) + .collect::, _>>()?; + 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, 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 { + let buffers = (0..capacity).map(|_| None).collect::>(); + 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> { + // 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, + base: PciBaseAddress, + dma: Arc, + pci: PciDeviceInfo, + + mac: OneTimeInit, + rx_ring: OneTimeInit>, + tx_ring: OneTimeInit>, + nic: OneTimeInit, +} + +impl Igbe { + pub fn new( + dma: Arc, + 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, _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, _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]>, 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, + ) -> Result, 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" + } + } +} diff --git a/kernel/driver/net/igbe/src/regs.rs b/kernel/driver/net/igbe/src/regs.rs new file mode 100644 index 00000000..722d3b84 --- /dev/null +++ b/kernel/driver/net/igbe/src/regs.rs @@ -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 { + 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(&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(&mut self) -> LocalRegisterCopy { + LocalRegisterCopy::new(self.get::()) + } + + fn write(&mut self, value: FieldValue) { + self.set::(value.value); + } + + fn set(&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(&mut self, modify: FieldValue) { + let mut value = self.get::(); + value &= !modify.mask(); + value |= modify.value; + self.set::(value); + } + + fn matches_all(&mut self, pattern: FieldValue) -> bool { + pattern.matches_all(self.get::()) + } + + // 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 { + 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 { + 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::(); + + 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::(0xFFFFFFFF); + } + + pub fn clear_interrupts(&mut self, cause: u32) { + let _ = self.inner.get::(); + self.inner.set::(cause); + } + + pub fn enable_interrupts(&mut self) { + self.inner.set::(0xFFFFFFFF); + // self.inner + // .modify(IMS::LSC::SET + IMS::RXT0::SET + IMS::TXDW::SET + IMS::TXQE::SET); + } + + pub fn interrupt_cause(&mut self) -> LocalRegisterCopy { + 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 { + todo!() + } + + pub fn initialize_receiver(&mut self, rx_ring: &RxRing) { + let rx_queue_base = rx_ring.descriptors.bus_address().into_u64(); + self.inner.set::(rx_queue_base as u32); + self.inner + .set::((rx_queue_base >> 32) as u32); + + self.inner + .write(RDLEN0::LEN0.val((rx_ring.descriptors.len() / 8) as u32)); + self.inner.set::(0); + self.inner + .set::((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::(tx_queue_base as u32); + self.inner + .set::((tx_queue_base >> 32) as u32); + + self.inner + .write(TDLEN::LEN.val((tx_ring.descriptors.len() / 8) as u32)); + self.inner.set::(0); + self.inner.set::(0); + + self.inner.set::(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::(val); + } + + pub fn rx_queue_head(&mut self) -> u16 { + self.inner.get::() as u16 + } + + pub fn set_rx_queue_tail(&mut self, tail: u16) { + self.inner.set::(tail as u32); + } + + pub fn tx_queue_head(&mut self) -> u16 { + self.inner.get::() as u16 + } + + pub fn set_tx_queue_tail(&mut self, tail: u16) { + self.inner.set::(tail as u32); + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index b66ee3b5..bcba46bf 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -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; diff --git a/lib/qemu/src/device.rs b/lib/qemu/src/device.rs index e772189d..3829ecc5 100644 --- a/lib/qemu/src/device.rs +++ b/lib/qemu/src/device.rs @@ -6,6 +6,7 @@ use crate::IntoArgs; pub enum QemuNic { VirtioPci { mac: Option }, Rtl8139 { mac: Option }, + IntelGigabit { mac: Option }, } #[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); + } } } } diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index cfb5eac7..68cd8bc2 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -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,