Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite network interface #334

Merged
merged 12 commits into from
May 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ readme = "README.md"
default-run = "moros"

[features]
default = ["video", "rtl8139"]
default = ["video"]
video = []
serial = []
rtl8139 = []
pcnet = []

[dependencies]
acpi = "4.1.0"
Expand Down
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ setup:
rustup default nightly
cargo install bootimage

output = video
keyboard = qwerty
nic = rtl8139
output = video, # video, serial
keyboard = qwerty # qwerty, azerty, dvorak
nic = rtl8139 # rtl8139, pcnet

export MOROS_KEYBOARD = $(keyboard)

Expand Down Expand Up @@ -38,7 +38,7 @@ $(img):
image: $(img)
touch src/lib.rs
env | grep MOROS
cargo bootimage --no-default-features --features $(output),$(nic) --release
cargo bootimage --no-default-features --features $(output) --release
dd conv=notrunc if=$(bin) of=$(img)

opts = -m 32 -cpu max -nic model=$(nic) -hda $(img) -soundhw pcspk
Expand All @@ -50,7 +50,7 @@ qemu:
qemu-system-x86_64 $(opts)

test:
cargo test --release --lib --no-default-features --features serial,$(nic) -- \
cargo test --release --lib --no-default-features --features serial -- \
-m 32 -display none -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04

clean:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Install the required tools with `make setup` or the following commands:

Build the image to `disk.img`:

$ make image output=video keyboard=qwerty nic=rtl8139
$ make image output=video keyboard=qwerty

Run MOROS in QEMU:

Expand Down
240 changes: 201 additions & 39 deletions src/sys/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,90 +1,252 @@
use crate::{sys, usr};

use alloc::collections::BTreeMap;
use alloc::sync::Arc;
use core::sync::atomic::{AtomicU64, Ordering};
use alloc::vec::Vec;
use alloc::vec;
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use lazy_static::lazy_static;
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::DeviceCapabilities;
use smoltcp::phy::{Device, Medium};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address};
use spin::Mutex;

// TODO: Support dyn Interface
pub type Interface<T> = smoltcp::iface::Interface<'static, T>;
mod rtl8139;
mod pcnet;

#[cfg(feature = "rtl8139")]
pub mod rtl8139;
pub type Interface = smoltcp::iface::Interface<'static, EthernetDevice>;

#[cfg(feature = "rtl8139")]
lazy_static! {
pub static ref IFACE: Mutex<Option<Interface<rtl8139::RTL8139>>> = Mutex::new(None);
pub static ref IFACE: Mutex<Option<Interface>> = Mutex::new(None);
}

#[cfg(feature = "rtl8139")]
pub fn init() {
rtl8139::init();
#[derive(Clone)]
pub enum EthernetDevice {
RTL8139(rtl8139::Device),
PCNET(pcnet::Device),
//E2000,
//VirtIO,
}

#[cfg(feature = "pcnet")]
pub mod pcnet;
pub trait EthernetDeviceIO {
fn config(&self) -> Arc<Config>;
fn stats(&self) -> Arc<Stats>;
fn receive_packet(&mut self) -> Option<Vec<u8>>;
fn transmit_packet(&mut self, len: usize);
fn next_tx_buffer(&mut self, len: usize) -> &mut [u8];
}

#[cfg(feature = "pcnet")]
lazy_static! {
pub static ref IFACE: Mutex<Option<Interface<pcnet::PCNET>>> = Mutex::new(None);
impl EthernetDeviceIO for EthernetDevice {
fn config(&self) -> Arc<Config> {
match self {
EthernetDevice::RTL8139(dev) => dev.config(),
EthernetDevice::PCNET(dev) => dev.config(),
}
}

fn stats(&self) -> Arc<Stats> {
match self {
EthernetDevice::RTL8139(dev) => dev.stats(),
EthernetDevice::PCNET(dev) => dev.stats(),
}
}

fn receive_packet(&mut self) -> Option<Vec<u8>> {
match self {
EthernetDevice::RTL8139(dev) => dev.receive_packet(),
EthernetDevice::PCNET(dev) => dev.receive_packet(),
}
}

fn transmit_packet(&mut self, len: usize) {
match self {
EthernetDevice::RTL8139(dev) => dev.transmit_packet(len),
EthernetDevice::PCNET(dev) => dev.transmit_packet(len),
}
}

fn next_tx_buffer(&mut self, len: usize) -> &mut [u8] {
match self {
EthernetDevice::RTL8139(dev) => dev.next_tx_buffer(len),
EthernetDevice::PCNET(dev) => dev.next_tx_buffer(len),
}
}
}

#[cfg(feature = "pcnet")]
pub fn init() {
pcnet::init();
impl<'a> smoltcp::phy::Device<'a> for EthernetDevice {
type RxToken = RxToken;
type TxToken = TxToken;

fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1500;
caps.max_burst_size = Some(1);
caps
}

fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
if let Some(buffer) = self.receive_packet() {
if self.config().is_debug_enabled() {
debug!("NET Packet Received");
usr::hex::print_hex(&buffer);
}
self.stats().rx_add(buffer.len() as u64);
let rx = RxToken { buffer };
let tx = TxToken { device: self.clone() };
Some((rx, tx))
} else {
None
}
}

fn transmit(&'a mut self) -> Option<Self::TxToken> {
let tx = TxToken { device: self.clone() };
Some(tx)
}
}

struct InnerStats {
rx_bytes_count: AtomicU64,
tx_bytes_count: AtomicU64,
rx_packets_count: AtomicU64,
tx_packets_count: AtomicU64,
#[doc(hidden)]
pub struct RxToken {
buffer: Vec<u8>,
}

impl InnerStats {
impl smoltcp::phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> smoltcp::Result<R> where F: FnOnce(&mut [u8]) -> smoltcp::Result<R> {
f(&mut self.buffer)
}
}

#[doc(hidden)]
pub struct TxToken {
device: EthernetDevice,
}
impl smoltcp::phy::TxToken for TxToken {
fn consume<R, F>(mut self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R> where F: FnOnce(&mut [u8]) -> smoltcp::Result<R> {
let config = self.device.config();
let mut buf = self.device.next_tx_buffer(len);
let res = f(&mut buf);
if res.is_ok() {
if config.is_debug_enabled() {
debug!("NET Packet Transmitted");
usr::hex::print_hex(&buf);
}
self.device.transmit_packet(len);
self.device.stats().tx_add(len as u64);
}
res
}
}

pub struct Config {
debug: AtomicBool,
mac: Mutex<Option<EthernetAddress>>,
}

impl Config {
fn new() -> Self {
Self {
rx_bytes_count: AtomicU64::new(0),
tx_bytes_count: AtomicU64::new(0),
rx_packets_count: AtomicU64::new(0),
tx_packets_count: AtomicU64::new(0),
debug: AtomicBool::new(false),
mac: Mutex::new(None),
}
}

fn is_debug_enabled(&self) -> bool {
self.debug.load(Ordering::Relaxed)
}

pub fn enable_debug(&self) {
self.debug.store(true, Ordering::Relaxed);
}

pub fn disable_debug(&self) {
self.debug.store(false, Ordering::Relaxed)
}

fn mac(&self) -> Option<EthernetAddress> {
*self.mac.lock()
}

fn update_mac(&self, mac: EthernetAddress) {
*self.mac.lock() = Some(mac);
}
}

#[derive(Clone)]
pub struct Stats {
stats: Arc<InnerStats>
rx_bytes_count: AtomicU64,
tx_bytes_count: AtomicU64,
rx_packets_count: AtomicU64,
tx_packets_count: AtomicU64,
}

impl Stats {
fn new() -> Self {
Self {
stats: Arc::new(InnerStats::new())
rx_bytes_count: AtomicU64::new(0),
tx_bytes_count: AtomicU64::new(0),
rx_packets_count: AtomicU64::new(0),
tx_packets_count: AtomicU64::new(0),
}
}

pub fn rx_bytes_count(&self) -> u64 {
self.stats.rx_bytes_count.load(Ordering::Relaxed)
self.rx_bytes_count.load(Ordering::Relaxed)
}

pub fn tx_bytes_count(&self) -> u64 {
self.stats.tx_bytes_count.load(Ordering::Relaxed)
self.tx_bytes_count.load(Ordering::Relaxed)
}

pub fn rx_packets_count(&self) -> u64 {
self.stats.rx_packets_count.load(Ordering::Relaxed)
self.rx_packets_count.load(Ordering::Relaxed)
}

pub fn tx_packets_count(&self) -> u64 {
self.stats.tx_packets_count.load(Ordering::Relaxed)
self.tx_packets_count.load(Ordering::Relaxed)
}

pub fn rx_add(&self, bytes_count: u64) {
self.stats.rx_packets_count.fetch_add(1, Ordering::SeqCst);
self.stats.rx_bytes_count.fetch_add(bytes_count, Ordering::SeqCst);
self.rx_packets_count.fetch_add(1, Ordering::SeqCst);
self.rx_bytes_count.fetch_add(bytes_count, Ordering::SeqCst);
}

pub fn tx_add(&self, bytes_count: u64) {
self.stats.tx_packets_count.fetch_add(1, Ordering::SeqCst);
self.stats.tx_bytes_count.fetch_add(bytes_count, Ordering::SeqCst);
self.tx_packets_count.fetch_add(1, Ordering::SeqCst);
self.tx_bytes_count.fetch_add(bytes_count, Ordering::SeqCst);
}
}

fn find_pci_io_base(vendor_id: u16, device_id: u16) -> Option<u16> {
if let Some(mut pci_device) = sys::pci::find_device(vendor_id, device_id) {
pci_device.enable_bus_mastering();
let io_base = (pci_device.base_addresses[0] as u16) & 0xFFF0;
Some(io_base)
} else {
None
}
}

pub fn init() {
let add_interface = |device: EthernetDevice, name| {
if let Some(mac) = device.config().mac() {
log!("NET {} MAC {}\n", name, mac);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let routes = Routes::new(BTreeMap::new());
let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)];
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs).routes(routes);
if medium == Medium::Ethernet {
builder = builder.hardware_addr(mac.into()).neighbor_cache(neighbor_cache);
}
let iface = builder.finalize();
*IFACE.lock() = Some(iface);
}
};
if let Some(io_base) = find_pci_io_base(0x10EC, 0x8139) {
add_interface(EthernetDevice::RTL8139(rtl8139::Device::new(io_base)), "RTL8139");
}
if let Some(io_base) = find_pci_io_base(0x1022, 0x2000) {
add_interface(EthernetDevice::PCNET(pcnet::Device::new(io_base)), "PCNET");
}
}
Loading