Add rtl8139 driver
This commit is contained in:
Generated
+14
@@ -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"
|
||||
|
||||
@@ -14,6 +14,7 @@ members = [
|
||||
"pcid",
|
||||
"pcspkrd",
|
||||
"ps2d",
|
||||
"rtl8139d",
|
||||
"rtl8168d",
|
||||
"sb16d",
|
||||
"vboxd",
|
||||
|
||||
@@ -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" }
|
||||
@@ -0,0 +1,6 @@
|
||||
[[drivers]]
|
||||
name = "RTL8139 NIC"
|
||||
class = 2
|
||||
ids = { 0x10ec = [0x8139] }
|
||||
command = ["rtl8139d"]
|
||||
use_channel = true
|
||||
@@ -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<u32>; 2],
|
||||
mar: [Mmio<u32>; 2],
|
||||
tsd: [Mmio<u32>; 4],
|
||||
tsad: [Mmio<u32>; 4],
|
||||
rbstart: Mmio<u32>,
|
||||
erbcr: ReadOnly<Mmio<u16>>,
|
||||
ersr: ReadOnly<Mmio<u8>>,
|
||||
cr: Mmio<u8>,
|
||||
capr: Mmio<u16>,
|
||||
cbr: ReadOnly<Mmio<u16>>,
|
||||
imr: Mmio<u16>,
|
||||
isr: Mmio<u16>,
|
||||
tcr: Mmio<u32>,
|
||||
rcr: Mmio<u32>,
|
||||
tctr: Mmio<u32>,
|
||||
mpc: Mmio<u32>,
|
||||
cr_9346: Mmio<u8>,
|
||||
config0: Mmio<u8>,
|
||||
config1: Mmio<u8>,
|
||||
rsvd_53: ReadOnly<Mmio<u8>>,
|
||||
timer_int: Mmio<u32>,
|
||||
msr: Mmio<u8>,
|
||||
config2: Mmio<u8>,
|
||||
config3: Mmio<u8>,
|
||||
rsvd_5b: ReadOnly<Mmio<u8>>,
|
||||
mulint: Mmio<u16>,
|
||||
rerid: ReadOnly<Mmio<u8>>,
|
||||
rsvd_5f: ReadOnly<Mmio<u8>>,
|
||||
tsts: ReadOnly<Mmio<u16>>,
|
||||
_todo: [ReadOnly<Mmio<u8>>; 158],
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
unsafe fn from_base(base: usize) -> &'static mut Self {
|
||||
assert_eq!(mem::size_of::<Regs>(), 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<u8>; RX_BUFFER_SIZE + 16]>,
|
||||
receive_i: usize,
|
||||
transmit_buffer: [Dma<[Mmio<u8>; 1792]>; 4],
|
||||
transmit_i: usize,
|
||||
next_id: usize,
|
||||
pub handles: BTreeMap<usize, usize>
|
||||
}
|
||||
|
||||
impl SchemeBlockMut for Rtl8139 {
|
||||
fn open(&mut self, _path: &str, flags: usize, uid: u32, _gid: u32) -> Result<Option<usize>> {
|
||||
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<Option<usize>> {
|
||||
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<Option<usize>> {
|
||||
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<Option<usize>> {
|
||||
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<Option<EventFlags>> {
|
||||
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<Option<usize>> {
|
||||
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<Option<usize>> {
|
||||
let _flags = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(0))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles.remove(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Rtl8139 {
|
||||
pub unsafe fn new(base: usize) -> Result<Self> {
|
||||
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::<Result<Vec<_>>>()?
|
||||
.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!");
|
||||
}
|
||||
}
|
||||
@@ -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<T>(a: T, b: T) -> T
|
||||
where
|
||||
T: Add<Output = T> + Div<Output = T> + Rem<Output = T> + PartialEq + From<u8> + Copy,
|
||||
{
|
||||
if a % b != T::from(0u8) {
|
||||
a / b + T::from(1u8)
|
||||
} else {
|
||||
a / b
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MsixInfo {
|
||||
pub virt_table_base: NonNull<MsixTableEntry>,
|
||||
pub virt_pba_base: NonNull<u64>,
|
||||
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<File> {
|
||||
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::<MsixTableEntry>(), 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<File> {
|
||||
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<Packet>,
|
||||
) -> Result<bool> {
|
||||
// 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::<usize>::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::<Packet>::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<Option<usize>> {
|
||||
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<Option<usize>> {
|
||||
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");
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user