diff --git a/Cargo.lock b/Cargo.lock index 69722a682a..3965b23e13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1377,6 +1377,20 @@ dependencies = [ "redox_syscall 0.2.16", ] +[[package]] +name = "rtl8139d" +version = "0.1.0" +dependencies = [ + "bitflags 1.2.1", + "log", + "netutils", + "pcid", + "redox-daemon", + "redox-log", + "redox_event", + "redox_syscall 0.3.5", +] + [[package]] name = "rtl8168d" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2ccefbc9b4..5182ebc37b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "pcid", "pcspkrd", "ps2d", + "rtl8139d", "rtl8168d", "sb16d", "vboxd", diff --git a/rtl8139d/Cargo.toml b/rtl8139d/Cargo.toml new file mode 100644 index 0000000000..48d15ec8ba --- /dev/null +++ b/rtl8139d/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rtl8139d" +version = "0.1.0" +edition = "2018" + +[dependencies] +bitflags = "1" +log = "0.4" +netutils = { git = "https://gitlab.redox-os.org/redox-os/netutils.git", branch = "redox-unix" } +redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" } +redox_syscall = "0.3" +redox-daemon = "0.1" +redox-log = "0.1" + +pcid = { path = "../pcid" } diff --git a/rtl8139d/config.toml b/rtl8139d/config.toml new file mode 100644 index 0000000000..92e19426e9 --- /dev/null +++ b/rtl8139d/config.toml @@ -0,0 +1,6 @@ +[[drivers]] +name = "RTL8139 NIC" +class = 2 +ids = { 0x10ec = [0x8139] } +command = ["rtl8139d"] +use_channel = true diff --git a/rtl8139d/src/device.rs b/rtl8139d/src/device.rs new file mode 100644 index 0000000000..0351b8bb95 --- /dev/null +++ b/rtl8139d/src/device.rs @@ -0,0 +1,349 @@ +use std::mem; +use std::convert::TryInto; +use std::collections::BTreeMap; + +use netutils::setcfg; +use syscall::error::{Error, EACCES, EBADF, EINVAL, EIO, EMSGSIZE, EWOULDBLOCK, Result}; +use syscall::flag::{EventFlags, O_NONBLOCK}; +use syscall::io::{Dma, Mmio, Io, ReadOnly}; +use syscall::scheme::SchemeBlockMut; + +const RX_BUFFER_SIZE: usize = 64 * 1024; + +const RXSTS_ROK: u16 = 1 << 0; + +const TSD_TOK: u32 = 1 << 15; +const TSD_OWN: u32 = 1 << 13; +const TSD_SIZE_MASK: u32 = 0x1FFF; + +const CR_RST: u8 = 1 << 4; +const CR_RE: u8 = 1 << 3; +const CR_TE: u8 = 1 << 2; +const CR_BUFE: u8 = 1 << 0; + +const IMR_TOK: u16 = 1 << 2; +const IMR_ROK: u16 = 1 << 0; + +const RCR_RBLEN_8K: u32 = 0b00 << 11; +const RCR_RBLEN_16K: u32 = 0b01 << 11; +const RCR_RBLEN_32K: u32 = 0b10 << 11; +const RCR_RBLEN_64K: u32 = 0b11 << 11; +const RCR_RBLEN_MASK: u32 = 0b11 << 11; +const RCR_AER: u32 = 1 << 5; +const RCR_AR: u32 = 1 << 4; +const RCR_AB: u32 = 1 << 3; +const RCR_AM: u32 = 1 << 2; +const RCR_APM: u32 = 1 << 1; +const RCR_AAP: u32 = 1 << 0; + +#[repr(packed)] +struct Regs { + mac: [Mmio; 2], + mar: [Mmio; 2], + tsd: [Mmio; 4], + tsad: [Mmio; 4], + rbstart: Mmio, + erbcr: ReadOnly>, + ersr: ReadOnly>, + cr: Mmio, + capr: Mmio, + cbr: ReadOnly>, + imr: Mmio, + isr: Mmio, + tcr: Mmio, + rcr: Mmio, + tctr: Mmio, + mpc: Mmio, + cr_9346: Mmio, + config0: Mmio, + config1: Mmio, + rsvd_53: ReadOnly>, + timer_int: Mmio, + msr: Mmio, + config2: Mmio, + config3: Mmio, + rsvd_5b: ReadOnly>, + mulint: Mmio, + rerid: ReadOnly>, + rsvd_5f: ReadOnly>, + tsts: ReadOnly>, + _todo: [ReadOnly>; 158], +} + +impl Regs { + unsafe fn from_base(base: usize) -> &'static mut Self { + assert_eq!(mem::size_of::(), 256); + + let regs = &mut *(base as *mut Regs); + + assert_eq!(®s.mac[0] as *const _ as usize - base, 0x00); + assert_eq!(®s.mac[1] as *const _ as usize - base, 0x04); + assert_eq!(®s.mar[0] as *const _ as usize - base, 0x08); + assert_eq!(®s.mar[1] as *const _ as usize - base, 0x0C); + assert_eq!(®s.tsd[0] as *const _ as usize - base, 0x10); + assert_eq!(®s.tsd[1] as *const _ as usize - base, 0x14); + assert_eq!(®s.tsd[2] as *const _ as usize - base, 0x18); + assert_eq!(®s.tsd[3] as *const _ as usize - base, 0x1C); + assert_eq!(®s.tsad[0] as *const _ as usize - base, 0x20); + assert_eq!(®s.tsad[1] as *const _ as usize - base, 0x24); + assert_eq!(®s.tsad[2] as *const _ as usize - base, 0x28); + assert_eq!(®s.tsad[3] as *const _ as usize - base, 0x2C); + assert_eq!(®s.rbstart as *const _ as usize - base, 0x30); + assert_eq!(®s.erbcr as *const _ as usize - base, 0x34); + assert_eq!(®s.ersr as *const _ as usize - base, 0x36); + assert_eq!(®s.cr as *const _ as usize - base, 0x37); + assert_eq!(®s.capr as *const _ as usize - base, 0x38); + assert_eq!(®s.cbr as *const _ as usize - base, 0x3A); + assert_eq!(®s.imr as *const _ as usize - base, 0x3C); + assert_eq!(®s.isr as *const _ as usize - base, 0x3E); + assert_eq!(®s.tcr as *const _ as usize - base, 0x40); + assert_eq!(®s.rcr as *const _ as usize - base, 0x44); + assert_eq!(®s.tctr as *const _ as usize - base, 0x48); + assert_eq!(®s.mpc as *const _ as usize - base, 0x4C); + assert_eq!(®s.cr_9346 as *const _ as usize - base, 0x50); + assert_eq!(®s.config0 as *const _ as usize - base, 0x51); + assert_eq!(®s.config1 as *const _ as usize - base, 0x52); + assert_eq!(®s.rsvd_53 as *const _ as usize - base, 0x53); + assert_eq!(®s.timer_int as *const _ as usize - base, 0x54); + assert_eq!(®s.msr as *const _ as usize - base, 0x58); + assert_eq!(®s.config2 as *const _ as usize - base, 0x59); + assert_eq!(®s.config3 as *const _ as usize - base, 0x5A); + assert_eq!(®s.rsvd_5b as *const _ as usize - base, 0x5B); + assert_eq!(®s.mulint as *const _ as usize - base, 0x5C); + assert_eq!(®s.rerid as *const _ as usize - base, 0x5E); + assert_eq!(®s.rsvd_5f as *const _ as usize - base, 0x5F); + assert_eq!(®s.tsts as *const _ as usize - base, 0x60); + + regs + } +} + +pub struct Rtl8139 { + regs: &'static mut Regs, + receive_buffer: Dma<[Mmio; RX_BUFFER_SIZE + 16]>, + receive_i: usize, + transmit_buffer: [Dma<[Mmio; 1792]>; 4], + transmit_i: usize, + next_id: usize, + pub handles: BTreeMap +} + +impl SchemeBlockMut for Rtl8139 { + fn open(&mut self, _path: &str, flags: usize, uid: u32, _gid: u32) -> Result> { + if uid == 0 { + self.next_id += 1; + self.handles.insert(self.next_id, flags); + Ok(Some(self.next_id)) + } else { + Err(Error::new(EACCES)) + } + } + + fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { + if ! buf.is_empty() { + return Err(Error::new(EINVAL)); + } + + let flags = { + let flags = self.handles.get(&id).ok_or(Error::new(EBADF))?; + *flags + }; + self.next_id += 1; + self.handles.insert(self.next_id, flags); + Ok(Some(self.next_id)) + } + + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let flags = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + if !self.regs.cr.readf(CR_BUFE) { + let rxsts = + (self.rx(0) as u16) | + (self.rx(1) as u16) << 8; + + let size_with_crc = + (self.rx(2) as usize) | + (self.rx(3) as usize) << 8; + + let res = if (rxsts & RXSTS_ROK) == RXSTS_ROK { + let mut i = 0; + while i < buf.len() && i < size_with_crc.saturating_sub(4) { + buf[i] = self.rx(4 + i as u16); + i += 1; + } + Ok(Some(i)) + } else { + //TODO: better error types + eprintln!("rtl8139d: invalid receive status 0x{:X}", rxsts); + Err(Error::new(EIO)) + }; + + self.receive_i = (self.receive_i + 4 + size_with_crc).next_multiple_of(4) % RX_BUFFER_SIZE; + let capr = self.receive_i.wrapping_sub(16) as u16; + self.regs.capr.write(capr); + + res + } else if flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EWOULDBLOCK)) + } else { + Ok(None) + } + } + + fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + let _flags = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + loop { + if self.transmit_i >= 4 { + self.transmit_i = 0; + } + + if self.regs.tsd[self.transmit_i].readf(TSD_OWN) { + let data = &mut self.transmit_buffer[self.transmit_i]; + + if buf.len() > data.len() { + return Err(Error::new(EMSGSIZE)); + } + + let mut i = 0; + while i < buf.len() && i < data.len() { + data[i].write(buf[i]); + i += 1; + } + + self.regs.tsad[self.transmit_i].write(data.physical() as u32); + assert_eq!(i as u32, i as u32 & TSD_SIZE_MASK); + self.regs.tsd[self.transmit_i].write(i as u32 & TSD_SIZE_MASK); + + //TODO: wait for TSD_TOK or error + + self.transmit_i += 1; + + return Ok(Some(i)); + } + + std::hint::spin_loop(); + } + } + + fn fevent(&mut self, id: usize, _flags: EventFlags) -> Result> { + let _flags = self.handles.get(&id).ok_or(Error::new(EBADF))?; + Ok(Some(EventFlags::empty())) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let _flags = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + let mut i = 0; + let scheme_path = b"network:"; + while i < buf.len() && i < scheme_path.len() { + buf[i] = scheme_path[i]; + i += 1; + } + Ok(Some(i)) + } + + fn fsync(&mut self, id: usize) -> Result> { + let _flags = self.handles.get(&id).ok_or(Error::new(EBADF))?; + Ok(Some(0)) + } + + fn close(&mut self, id: usize) -> Result> { + self.handles.remove(&id).ok_or(Error::new(EBADF))?; + Ok(Some(0)) + } +} + +impl Rtl8139 { + pub unsafe fn new(base: usize) -> Result { + let regs = Regs::from_base(base); + + let mut module = Rtl8139 { + regs: regs, + //TODO: limit to 32-bit + receive_buffer: Dma::zeroed().map(|dma| dma.assume_init())?, + receive_i: 0, + //TODO: limit to 32-bit + transmit_buffer: (0..4) + .map(|_| Dma::zeroed().map(|dma| dma.assume_init())) + .collect::>>()? + .try_into() + .unwrap_or_else(|_| unreachable!()), + transmit_i: 0, + next_id: 0, + handles: BTreeMap::new(), + }; + + module.init(); + + Ok(module) + } + + pub unsafe fn irq(&mut self) -> bool { + // Read and then clear the ISR + let isr = self.regs.isr.read(); + self.regs.isr.write(isr); + let imr = self.regs.imr.read(); + (isr & imr) != 0 + } + + fn rx(&self, offset: u16) -> u8 { + let index = (self.receive_i + offset as usize) % RX_BUFFER_SIZE; + self.receive_buffer[index].read() + } + + pub fn next_read(&self) -> usize { + if !self.regs.cr.readf(CR_BUFE) { + let rxsts = + (self.rx(0) as u16) | + (self.rx(1) as u16) << 8; + + let size_with_crc = + (self.rx(2) as usize) | + (self.rx(3) as usize) << 8; + + if (rxsts & RXSTS_ROK) == RXSTS_ROK { + size_with_crc.saturating_sub(4) + } else { + 0 + } + } else { + 0 + } + } + + pub unsafe fn init(&mut self) { + let mac_low = self.regs.mac[0].read(); + let mac_high = self.regs.mac[1].read(); + let mac = [mac_low as u8, + (mac_low >> 8) as u8, + (mac_low >> 16) as u8, + (mac_low >> 24) as u8, + mac_high as u8, + (mac_high >> 8) as u8]; + println!(" - MAC: {:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + let _ = setcfg("mac", &format!("{:>02X}-{:>02X}-{:>02X}-{:>02X}-{:>02X}-{:>02X}\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); + + // Reset - this will disable tx and rx, reinitialize FIFOs, and set the system buffer pointer to the initial value + println!(" - Reset"); + self.regs.cr.writef(CR_RST, true); + while self.regs.cr.readf(CR_RST) { + core::hint::spin_loop(); + } + + // Set up rx buffer + println!(" - Receive buffer"); + self.regs.rbstart.write(self.receive_buffer.physical() as u32); + + println!(" - Interrupt mask"); + self.regs.imr.write(IMR_TOK | IMR_ROK); + + println!(" - Receive configuration"); + self.regs.rcr.write(RCR_RBLEN_64K | RCR_AB | RCR_AM | RCR_APM | RCR_AAP); + + println!(" - Enable RX and TX"); + self.regs.cr.writef(CR_RE | CR_TE, true); + + println!(" - Complete!"); + } +} diff --git a/rtl8139d/src/main.rs b/rtl8139d/src/main.rs new file mode 100644 index 0000000000..49edbd9598 --- /dev/null +++ b/rtl8139d/src/main.rs @@ -0,0 +1,469 @@ +#![feature(int_roundings)] + +extern crate event; +extern crate netutils; +extern crate syscall; + +use std::cell::RefCell; +use std::convert::{TryFrom, TryInto}; +use std::{env, process}; +use std::fs::File; +use std::io::{ErrorKind, Read, Result, Write}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::ptr::NonNull; +use std::sync::Arc; + +use event::EventQueue; +use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo, SubdriverArguments}; +use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector}; +use pcid_interface::msi::{MsixCapability, MsixTableEntry}; +use redox_log::{RedoxLogger, OutputBuilder}; +use syscall::{EventFlags, Packet, SchemeBlockMut, PHYSMAP_NO_CACHE, PHYSMAP_WRITE}; +use syscall::io::Io; + +pub mod device; + +fn setup_logging() -> Option<&'static RedoxLogger> { + let mut logger = RedoxLogger::new() + .with_output( + OutputBuilder::stderr() + .with_filter(log::LevelFilter::Info) // limit global output to important info + .with_ansi_escape_codes() + .flush_on_newline(true) + .build() + ); + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("usb", "host", "rtl8139.log") { + Ok(b) => logger = logger.with_output( + // TODO: Add a configuration file for this + b.with_filter(log::LevelFilter::Info) + .flush_on_newline(true) + .build() + ), + Err(error) => eprintln!("Failed to create rtl8139.log: {}", error), + } + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("usb", "host", "rtl8139.ansi.log") { + Ok(b) => logger = logger.with_output( + b.with_filter(log::LevelFilter::Info) + .with_ansi_escape_codes() + .flush_on_newline(true) + .build() + ), + Err(error) => eprintln!("Failed to create rtl8139.ansi.log: {}", error), + } + + match logger.enable() { + Ok(logger_ref) => { + eprintln!("rtl8139d: enabled logger"); + Some(logger_ref) + } + Err(error) => { + eprintln!("rtl8139d: failed to set default logger: {}", error); + None + } + } +} + +use std::ops::{Add, Div, Rem}; +pub fn div_round_up(a: T, b: T) -> T +where + T: Add + Div + Rem + PartialEq + From + Copy, +{ + if a % b != T::from(0u8) { + a / b + T::from(1u8) + } else { + a / b + } +} + +pub struct MsixInfo { + pub virt_table_base: NonNull, + pub virt_pba_base: NonNull, + pub capability: MsixCapability, +} + +impl MsixInfo { + pub unsafe fn table_entry_pointer_unchecked(&mut self, k: usize) -> &mut MsixTableEntry { + &mut *self.virt_table_base.as_ptr().offset(k as isize) + } + pub fn table_entry_pointer(&mut self, k: usize) -> &mut MsixTableEntry { + assert!(k < self.capability.table_size() as usize); + unsafe { self.table_entry_pointer_unchecked(k) } + } + pub unsafe fn pba_pointer_unchecked(&mut self, k: usize) -> &mut u64 { + &mut *self.virt_pba_base.as_ptr().offset(k as isize) + } + pub fn pba_pointer(&mut self, k: usize) -> &mut u64 { + assert!(k < self.capability.table_size() as usize); + unsafe { self.pba_pointer_unchecked(k) } + } + pub fn pba(&mut self, k: usize) -> bool { + let byte = k / 64; + let bit = k % 64; + *self.pba_pointer(byte) & (1 << bit) != 0 + } +} + +#[cfg(target_arch = "x86_64")] +fn get_int_method(pcid_handle: &mut PcidServerHandle) -> Option { + let pci_config = pcid_handle.fetch_config().expect("rtl8139d: failed to fetch config"); + + let irq = pci_config.func.legacy_interrupt_line; + + let all_pci_features = pcid_handle.fetch_all_features().expect("rtl8139d: failed to fetch pci features"); + log::info!("PCI FEATURES: {:?}", all_pci_features); + + let (has_msi, mut msi_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msi(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + let (has_msix, mut msix_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msix(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + + if has_msi && !msi_enabled && !has_msix { + msi_enabled = true; + } + if has_msix && !msix_enabled { + msix_enabled = true; + } + + if msi_enabled && !msix_enabled { + use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix}; + + let capability = match pcid_handle.feature_info(PciFeature::Msi).expect("rtl8139d: failed to retrieve the MSI capability structure from pcid") { + PciFeatureInfo::Msi(s) => s, + PciFeatureInfo::MsiX(_) => panic!(), + }; + // TODO: Allow allocation of up to 32 vectors. + + // TODO: Find a way to abstract this away, potantially as a helper module for + // pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc.. + + let destination_id = read_bsp_apic_id().expect("rtl8139d: failed to read BSP apic id"); + let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8"); + let msg_addr = x86_64_msix::message_address(lapic_id, false, false); + + let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("rtl8139d: failed to allocate interrupt vector").expect("rtl8139d: no interrupt vectors left"); + let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector); + + let set_feature_info = MsiSetFeatureInfo { + multi_message_enable: Some(0), + message_address: Some(msg_addr), + message_upper_address: Some(0), + message_data: Some(msg_data as u16), + mask_bits: None, + }; + pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("rtl8139d: failed to set feature info"); + + pcid_handle.enable_feature(PciFeature::Msi).expect("rtl8139d: failed to enable MSI"); + log::info!("Enabled MSI"); + + Some(interrupt_handle) + } else if msix_enabled { + let capability = match pcid_handle.feature_info(PciFeature::MsiX).expect("rtl8139d: failed to retrieve the MSI-X capability structure from pcid") { + PciFeatureInfo::Msi(_) => panic!(), + PciFeatureInfo::MsiX(s) => s, + }; + let table_size = capability.table_size(); + let table_base = capability.table_base_pointer(pci_config.func.bars); + let table_min_length = table_size * 16; + let pba_min_length = div_round_up(table_size, 8); + + let pba_base = capability.pba_base_pointer(pci_config.func.bars); + + let bir = capability.table_bir() as usize; + let bar = pci_config.func.bars[bir]; + let bar_size = pci_config.func.bar_sizes[bir] as u64; + + let bar_ptr = match bar { + pcid_interface::PciBar::Memory32(ptr) => match ptr { + 0 => panic!("BAR {} is mapped to address 0", bir), + _ => ptr as u64, + }, + pcid_interface::PciBar::Memory64(ptr) => match ptr { + 0 => panic!("BAR {} is mapped to address 0", bir), + _ => ptr, + }, + other => panic!("Expected memory bar, found {}", other), + }; + + let address = unsafe { + syscall::physmap(bar_ptr as usize, bar_size as usize, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) + .expect("rtl8139d: failed to map address") + }; + + if !(bar_ptr..bar_ptr + bar_size).contains(&(table_base as u64 + table_min_length as u64)) { + panic!("Table {:#x}{:#x} outside of BAR {:#x}:{:#x}", table_base, table_base + table_min_length as usize, bar_ptr, bar_ptr + bar_size); + } + + if !(bar_ptr..bar_ptr + bar_size).contains(&(pba_base as u64 + pba_min_length as u64)) { + panic!("PBA {:#x}{:#x} outside of BAR {:#x}:{:#X}", pba_base, pba_base + pba_min_length as usize, bar_ptr, bar_ptr + bar_size); + } + + let virt_table_base = ((table_base - bar_ptr as usize) + address) as *mut MsixTableEntry; + let virt_pba_base = ((pba_base - bar_ptr as usize) + address) as *mut u64; + + let mut info = MsixInfo { + virt_table_base: NonNull::new(virt_table_base).unwrap(), + virt_pba_base: NonNull::new(virt_pba_base).unwrap(), + capability, + }; + + // Allocate one msi vector. + + let method = { + use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix}; + + // primary interrupter + let k = 0; + + assert_eq!(std::mem::size_of::(), 16); + let table_entry_pointer = info.table_entry_pointer(k); + + let destination_id = read_bsp_apic_id().expect("rtl8139d: failed to read BSP apic id"); + let lapic_id = u8::try_from(destination_id).expect("rtl8139d: CPU id couldn't fit inside u8"); + let rh = false; + let dm = false; + let addr = x86_64_msix::message_address(lapic_id, rh, dm); + + let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("rtl8139d: failed to allocate interrupt vector").expect("rtl8139d: no interrupt vectors left"); + let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector); + + table_entry_pointer.addr_lo.write(addr); + table_entry_pointer.addr_hi.write(0); + table_entry_pointer.msg_data.write(msg_data); + table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false); + + Some(interrupt_handle) + }; + + pcid_handle.enable_feature(PciFeature::MsiX).expect("rtl8139d: failed to enable MSI-X"); + log::info!("Enabled MSI-X"); + + method + } else if pci_config.func.legacy_interrupt_pin.is_some() { + // legacy INTx# interrupt pins. + Some(File::open(format!("irq:{}", irq)).expect("rtl8139d: failed to open legacy IRQ file")) + } else { + // no interrupts at all + None + } +} + +//TODO: MSI on non-x86_64? +#[cfg(not(target_arch = "x86_64"))] +fn get_int_method(pcid_handle: &mut PcidServerHandle) -> Option { + let pci_config = pcid_handle.fetch_config().expect("rtl8139d: failed to fetch config"); + let irq = pci_config.func.legacy_interrupt_line; + + if pci_config.func.legacy_interrupt_pin.is_some() { + // legacy INTx# interrupt pins. + Some(File::open(format!("irq:{}", irq)).expect("rtl8139d: failed to open legacy IRQ file")) + } else { + // no interrupts at all + None + } +} + +fn handle_update( + socket: &mut File, + device: &mut device::Rtl8139, + todo: &mut Vec, +) -> Result { + // Handle any blocked packets + let mut i = 0; + while i < todo.len() { + if let Some(a) = device.handle(&todo[i]) { + let mut packet = todo.remove(i); + packet.a = a; + socket.write(&packet)?; + } else { + i += 1; + } + } + + // Check that the socket is empty + loop { + let mut packet = Packet::default(); + match socket.read(&mut packet) { + Ok(0) => return Ok(true), + Ok(_) => (), + Err(err) => { + if err.kind() == ErrorKind::WouldBlock { + break; + } else { + return Err(err); + } + } + } + + if let Some(a) = device.handle(&packet) { + packet.a = a; + socket.write(&packet)?; + } else { + todo.push(packet); + } + } + + Ok(false) +} + +fn find_bar(pci_config: &SubdriverArguments) -> Option<(usize, usize)> { + // RTL8139 uses BAR2, RTL8169 uses BAR1, search in that order + for &barnum in &[2, 1] { + match pci_config.func.bars[barnum] { + pcid_interface::PciBar::Memory32(ptr) => match ptr { + 0 => log::warn!("BAR {} is mapped to address 0", barnum), + _ => return Some(( + ptr.try_into().unwrap(), + pci_config.func.bar_sizes[barnum].try_into().unwrap() + )), + }, + pcid_interface::PciBar::Memory64(ptr) => match ptr { + 0 => log::warn!("BAR {} is mapped to address 0", barnum), + _ => return Some(( + ptr.try_into().unwrap(), + pci_config.func.bar_sizes[barnum].try_into().unwrap() + )), + }, + other => log::warn!("BAR {} is {} instead of memory BAR", barnum, other), + } + } + None +} + +fn daemon(daemon: redox_daemon::Daemon) -> ! { + let _logger_ref = setup_logging(); + + let mut pcid_handle = PcidServerHandle::connect_default().expect("rtl8139d: failed to setup channel to pcid"); + + let pci_config = pcid_handle.fetch_config().expect("rtl8139d: failed to fetch config"); + + let mut name = pci_config.func.name(); + name.push_str("_rtl8139"); + + let (bar_ptr, bar_size) = find_bar(&pci_config).expect("rtl8139d: failed to find BAR"); + log::info!(" + RTL8139 {} on: {:#X} size: {}", name, bar_ptr, bar_size); + + let address = unsafe { + syscall::physmap(bar_ptr, bar_size, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) + .expect("rtl8139d: failed to map address") + }; + + let socket_fd = syscall::open( + ":network", + syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, + ) + .expect("rtl8139d: failed to create network scheme"); + let socket = Arc::new(RefCell::new(unsafe { + File::from_raw_fd(socket_fd as RawFd) + })); + + //TODO: MSI-X + let mut irq_file = get_int_method(&mut pcid_handle).expect("rtl8139d: no interrupt file"); + + { + let device = Arc::new(RefCell::new(unsafe { + device::Rtl8139::new(address).expect("rtl8139d: failed to allocate device") + })); + + let mut event_queue = + EventQueue::::new().expect("rtl8139d: failed to create event queue"); + + syscall::setrens(0, 0).expect("rtl8139d: failed to enter null namespace"); + + daemon.ready().expect("rtl8139d: failed to mark daemon as ready"); + + let todo = Arc::new(RefCell::new(Vec::::new())); + + let device_irq = device.clone(); + let socket_irq = socket.clone(); + let todo_irq = todo.clone(); + event_queue + .add( + irq_file.as_raw_fd(), + move |_event| -> Result> { + let mut irq = [0; 8]; + irq_file.read(&mut irq)?; + //TODO: This may be causing spurious interrupts + if unsafe { device_irq.borrow_mut().irq() } { + irq_file.write(&mut irq)?; + + if handle_update( + &mut socket_irq.borrow_mut(), + &mut device_irq.borrow_mut(), + &mut todo_irq.borrow_mut(), + )? { + return Ok(Some(0)); + } + + let next_read = device_irq.borrow().next_read(); + if next_read > 0 { + return Ok(Some(next_read)); + } + } + Ok(None) + }, + ) + .expect("rtl8139d: failed to catch events on IRQ file"); + + let device_packet = device.clone(); + let socket_packet = socket.clone(); + event_queue + .add(socket_fd as RawFd, move |_event| -> Result> { + if handle_update( + &mut socket_packet.borrow_mut(), + &mut device_packet.borrow_mut(), + &mut todo.borrow_mut(), + )? { + return Ok(Some(0)); + } + + let next_read = device_packet.borrow().next_read(); + if next_read > 0 { + return Ok(Some(next_read)); + } + + Ok(None) + }) + .expect("rtl8139d: failed to catch events on scheme file"); + + let send_events = |event_count| { + for (handle_id, _handle) in device.borrow().handles.iter() { + socket + .borrow_mut() + .write(&Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: *handle_id, + c: syscall::flag::EVENT_READ.bits(), + d: event_count, + }) + .expect("rtl8139d: failed to write event"); + } + }; + + for event_count in event_queue + .trigger_all(event::Event { fd: 0, flags: EventFlags::empty() }) + .expect("rtl8139d: failed to trigger events") + { + send_events(event_count); + } + + loop { + let event_count = event_queue.run().expect("rtl8139d: failed to handle events"); + if event_count == 0 { + //TODO: Handle todo + break; + } + send_events(event_count); + } + } + process::exit(0); +} + +fn main() { + redox_daemon::Daemon::new(daemon).expect("rtl8139d: failed to create daemon"); +} diff --git a/rtl8168d/src/device.rs b/rtl8168d/src/device.rs index ff6231c32c..df906089ff 100644 --- a/rtl8168d/src/device.rs +++ b/rtl8168d/src/device.rs @@ -6,7 +6,7 @@ use std::convert::TryInto; use std::collections::BTreeMap; use netutils::setcfg; -use syscall::error::{Error, EACCES, EBADF, EINVAL, EWOULDBLOCK, Result}; +use syscall::error::{Error, EACCES, EBADF, EINVAL, EMSGSIZE, EWOULDBLOCK, Result}; use syscall::flag::{EventFlags, O_NONBLOCK}; use syscall::io::{Dma, Mmio, Io, ReadOnly}; use syscall::scheme::SchemeBlockMut; @@ -152,6 +152,10 @@ impl SchemeBlockMut for Rtl8168 { if ! td.ctrl.readf(OWN) { let data = &mut self.transmit_buffer[self.transmit_i]; + if buf.len() > data.len() { + return Err(Error::new(EMSGSIZE)); + } + let mut i = 0; while i < buf.len() && i < data.len() { data[i].write(buf[i]);