Skip to content

Commit

Permalink
Make the ethernet driver-client interface generic over smoltcp::phy::…
Browse files Browse the repository at this point in the history
…Driver
  • Loading branch information
protoben committed Sep 15, 2023
1 parent 0ef3bf5 commit 2c61425
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 45 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/pds/virtio-net-driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ sel4cp-http-server-example-virtio-hal-impl = { path = "../../helpers/virtio-hal-
sel4cp-http-server-example-virtio-net-driver-interface-types = { path = "./interface-types" }
sel4cp-message = { git = "https://github.com/coliasgroup/rust-seL4" }
virtio-drivers = { version = "0.5.0", default-features = false, features = ["alloc"] }
smoltcp = { version = "0.10.0", default-features = false, features = ["proto-ipv4","medium-ethernet","socket-raw"] }
259 changes: 214 additions & 45 deletions crates/pds/virtio-net-driver/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![no_std]
#![no_main]
#![feature(never_type)]
#![feature(let_chains)]

use core::ptr::NonNull;

Expand All @@ -12,14 +13,17 @@ use virtio_drivers::{
},
};

use sel4_externally_shared::ExternallySharedRef;
use sel4_shared_ring_buffer::{RingBuffer, RingBuffers};
use sel4cp::{memory_region_symbol, protection_domain, var, Channel, Handler, MessageInfo};
use sel4cp_message::MessageInfoExt as _;

use sel4_externally_shared::ExternallySharedRef;
use sel4_shared_ring_buffer::{RingBuffers, RingBuffer};
use sel4cp_http_server_example_virtio_hal_impl::HalImpl;
use sel4cp_http_server_example_virtio_net_driver_interface_types::*;

use smoltcp::phy::{self, RxToken, TxToken};
use smoltcp::time::Instant;

const DEVICE: Channel = Channel::new(0);

This comment has been minimized.

Copy link
@podhrmic

podhrmic Sep 25, 2023

IMHO these channels should be autogenerated from the system description, and included as a module. See seL4/microkit#69

const CLIENT: Channel = Channel::new(1);

Expand All @@ -29,7 +33,7 @@ const NET_BUFFER_LEN: usize = 2048;
#[protection_domain(
heap_size = 512 * 1024,
)]
fn init() -> HandlerImpl {
fn init() -> PhyDeviceHandler<virtio_phy::Device> {
HalImpl::init(
*var!(virtio_net_driver_dma_size: usize = 0),
*var!(virtio_net_driver_dma_vaddr: usize = 0),
Expand All @@ -44,7 +48,10 @@ fn init() -> HandlerImpl {
.unwrap();
let transport = unsafe { MmioTransport::new(header) }.unwrap();
assert_eq!(transport.device_type(), DeviceType::Network);
VirtIONet::<HalImpl, MmioTransport, NET_QUEUE_SIZE>::new(transport, NET_BUFFER_LEN).unwrap()
virtio_phy::Device::new(
VirtIONet::<HalImpl, MmioTransport, NET_QUEUE_SIZE>::new(transport, NET_BUFFER_LEN)
.unwrap()
)
};

let client_region = unsafe {
Expand All @@ -53,7 +60,7 @@ fn init() -> HandlerImpl {
)
};

let client_client_dma_region_paddr = *var!(virtio_net_client_dma_paddr: usize = 0);
let client_region_paddr = *var!(virtio_net_client_dma_paddr: usize = 0);

let rx_ring_buffers = unsafe {
RingBuffers::<'_, fn() -> Result<(), !>>::new(
Expand All @@ -73,84 +80,246 @@ fn init() -> HandlerImpl {
)
};

dev.ack_interrupt();
dev.irq_ack();
DEVICE.irq_ack().unwrap();

HandlerImpl {
PhyDeviceHandler::new(
dev,
client_region,
client_client_dma_region_paddr,
client_region_paddr,
rx_ring_buffers,
tx_ring_buffers,
)
}

mod virtio_phy {
use super::*;

extern crate alloc;
use alloc::rc::Rc;
use core::cell::RefCell;

use virtio_drivers::device::net::{self, RxBuffer};

use smoltcp::phy;
use smoltcp::time::Instant;

pub type VirtIONet = net::VirtIONet<HalImpl, MmioTransport, {NET_QUEUE_SIZE}>;

pub struct RxToken {
dev_inner: Rc<RefCell<VirtIONet>>,
// NOTE This is an option so we can call mem::take on it for implementing
// Drop. This is necessary because virtio's deallocation function moves
// its argument, which we can't normally do in Drop::drop.
buf: Option<RxBuffer>,
}

impl phy::RxToken for RxToken {
fn consume<R, F: FnOnce(&mut [u8]) -> R>(mut self, f: F) -> R {
// XXX: Why is this mut? We could avoid calling packet_mut by
// creating a temporary vector, but this would add a copy.
f(self.buf.as_mut().unwrap().packet_mut())
}
}

impl Drop for RxToken {
fn drop(&mut self) {
self.dev_inner.borrow_mut().recycle_rx_buffer(self.buf.take().unwrap());
}
}

pub struct TxToken {
dev_inner: Rc<RefCell<VirtIONet>>,
}

impl phy::TxToken for TxToken {
fn consume<R, F: FnOnce(&mut [u8]) -> R>(self, len: usize, f: F) -> R {
let mut dev = self.dev_inner.borrow_mut();

let mut buf = dev.new_tx_buffer(len);
let res = f(buf.packet_mut());
// XXX How can we avoid panicking here? Appears to fail only if the
// queue is full.
dev.send(buf).expect("Failed to send buffer");

res
}
}

pub struct Device {
dev_inner: Rc<RefCell<VirtIONet>>,
}

impl Device {
pub fn new(dev: VirtIONet) -> Self {
Self {
dev_inner: Rc::new(RefCell::new(dev)),
}
}
}

impl phy::Device for Device {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken;

fn receive(
&mut self,
_timestamp: Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let mut dev = self.dev_inner.borrow_mut();

if !dev.can_recv() || !dev.can_send() {
return None;
}

let rx_buf = dev.receive().ok()?;

Some((
RxToken {
dev_inner: self.dev_inner.clone(),
buf: Some(rx_buf),
},
TxToken {
dev_inner: self.dev_inner.clone(),
},
))
}

fn transmit(
&mut self,
_timestamp: Instant,
) -> Option<Self::TxToken<'_>> {
if !self.dev_inner.borrow().can_send() {
return None;
}

Some(TxToken {
dev_inner: self.dev_inner.clone(),
})
}

fn capabilities(&self) -> phy::DeviceCapabilities {
// XXX What are these for virtio?
phy::DeviceCapabilities::default()
}
}

impl IrqAck for Device {
fn irq_ack(&mut self) {
self.dev_inner.borrow_mut().ack_interrupt();
}
}

impl HasMac for Device {
fn mac_address(&self) -> [u8; 6] {
self.dev_inner.borrow().mac_address()
}
}
}

pub trait IrqAck {
fn irq_ack(&mut self);
}

pub trait HasMac {
fn mac_address(&self) -> [u8; 6];
}

// XXX How do we attach this to a a token in another module. Notice, no self
// argument...
fn notify_client() -> Result<(), !> {
CLIENT.notify();
Ok::<_, !>(())
}

struct HandlerImpl {
dev: VirtIONet<HalImpl, MmioTransport, NET_QUEUE_SIZE>,
struct PhyDeviceHandler<Device> {
dev: Device,
client_region: ExternallySharedRef<'static, [u8]>,
client_client_dma_region_paddr: usize,
client_region_paddr: usize,
rx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>,
tx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>,
}

impl Handler for HandlerImpl {
impl<Device> PhyDeviceHandler<Device> {
pub fn new(
dev: Device,
client_region: ExternallySharedRef<'static, [u8]>,
client_region_paddr: usize,
rx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>,
tx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>,
) -> Self {
// XXX We could maybe initialize DMA here, so we don't need to do
// it in main. Also maybe initialize the ring buffers.
Self {
dev,
client_region,
client_region_paddr,
rx_ring_buffers,
tx_ring_buffers,
}
}
}

impl<Device: phy::Device + IrqAck + HasMac> Handler for PhyDeviceHandler<Device> {
type Error = !;

fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> {
match channel {
DEVICE | CLIENT => {
let mut notify_rx = false;

while self.dev.can_recv() && !self.rx_ring_buffers.free().is_empty() {
let rx_buf = self.dev.receive().unwrap();
let desc = self.rx_ring_buffers.free_mut().dequeue().unwrap();
let desc_len = usize::try_from(desc.len()).unwrap();
assert!(desc_len >= rx_buf.packet_len());
let buf_range = {
let start = desc.encoded_addr() - self.client_client_dma_region_paddr;
start..start + rx_buf.packet_len()
};
self.client_region
.as_mut_ptr()
.index(buf_range)
.copy_from_slice(rx_buf.packet());
self.dev.recycle_rx_buffer(rx_buf).unwrap();
self.rx_ring_buffers.used_mut().enqueue(desc).unwrap();
notify_rx = true;
}
while !self.rx_ring_buffers.free().is_empty()
&& let Some((rx_tok, _tx_tok)) = self.dev.receive(Instant::ZERO) {
let desc = self.rx_ring_buffers.free_mut().dequeue().unwrap();
let desc_len = usize::try_from(desc.len()).unwrap();

rx_tok.consume(|rx_buf| {
assert!(desc_len >= rx_buf.len());
let buf_range = {
let start = desc.encoded_addr() - self.client_region_paddr;
start..start + rx_buf.len()
};
self.client_region
.as_mut_ptr()
.index(buf_range)
.copy_from_slice(&rx_buf);
});

self.rx_ring_buffers.used_mut().enqueue(desc).unwrap();
notify_rx = true;
}

if notify_rx {
self.rx_ring_buffers.notify().unwrap();
}

let mut notify_tx = false;

while !self.tx_ring_buffers.free().is_empty() && self.dev.can_send() {
let desc = self.tx_ring_buffers.free_mut().dequeue().unwrap();
let buf_range = {
let start = desc.encoded_addr() - self.client_client_dma_region_paddr;
start..start + usize::try_from(desc.len()).unwrap()
};
let mut tx_buf = self.dev.new_tx_buffer(buf_range.len());
self.client_region
.as_ptr()
.index(buf_range)
.copy_into_slice(tx_buf.packet_mut());
self.dev.send(tx_buf).unwrap();
self.tx_ring_buffers.used_mut().enqueue(desc).unwrap();
notify_tx = true;
}
while !self.tx_ring_buffers.free().is_empty()
&& let Some(tx_tok) = self.dev.transmit(Instant::ZERO) {
let desc = self.tx_ring_buffers.free_mut().dequeue().unwrap();
let tx_len = usize::try_from(desc.len()).unwrap();

tx_tok.consume(tx_len, |tx_buf| {
let buf_range = {
let start = desc.encoded_addr() - self.client_region_paddr;
start..start + tx_len
};
self.client_region
.as_ptr()
.index(buf_range)
.copy_into_slice(tx_buf);
});

self.tx_ring_buffers.used_mut().enqueue(desc).unwrap();
notify_tx = true;
}

if notify_tx {
self.tx_ring_buffers.notify().unwrap();
}

self.dev.ack_interrupt();
self.dev.irq_ack();
DEVICE.irq_ack().unwrap();
}
_ => {
Expand Down

0 comments on commit 2c61425

Please sign in to comment.