From 2c61425d8712f18a4a60885cf8bb762a3b50ca68 Mon Sep 17 00:00:00 2001 From: Ben Hamlin Date: Fri, 15 Sep 2023 23:51:49 +0000 Subject: [PATCH] Make the ethernet driver-client interface generic over smoltcp::phy::Driver --- Cargo.lock | 1 + crates/pds/virtio-net-driver/Cargo.toml | 1 + crates/pds/virtio-net-driver/src/main.rs | 259 +++++++++++++++++++---- 3 files changed, 216 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bd4ef6..1d0b11b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1372,6 +1372,7 @@ dependencies = [ "sel4cp-http-server-example-virtio-hal-impl", "sel4cp-http-server-example-virtio-net-driver-interface-types", "sel4cp-message", + "smoltcp", "virtio-drivers", ] diff --git a/crates/pds/virtio-net-driver/Cargo.toml b/crates/pds/virtio-net-driver/Cargo.toml index 8a0a4e0..63ae612 100644 --- a/crates/pds/virtio-net-driver/Cargo.toml +++ b/crates/pds/virtio-net-driver/Cargo.toml @@ -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"] } diff --git a/crates/pds/virtio-net-driver/src/main.rs b/crates/pds/virtio-net-driver/src/main.rs index b205648..910dec0 100644 --- a/crates/pds/virtio-net-driver/src/main.rs +++ b/crates/pds/virtio-net-driver/src/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![feature(never_type)] +#![feature(let_chains)] use core::ptr::NonNull; @@ -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); const CLIENT: Channel = Channel::new(1); @@ -29,7 +33,7 @@ const NET_BUFFER_LEN: usize = 2048; #[protection_domain( heap_size = 512 * 1024, )] -fn init() -> HandlerImpl { +fn init() -> PhyDeviceHandler { HalImpl::init( *var!(virtio_net_driver_dma_size: usize = 0), *var!(virtio_net_driver_dma_vaddr: usize = 0), @@ -44,7 +48,10 @@ fn init() -> HandlerImpl { .unwrap(); let transport = unsafe { MmioTransport::new(header) }.unwrap(); assert_eq!(transport.device_type(), DeviceType::Network); - VirtIONet::::new(transport, NET_BUFFER_LEN).unwrap() + virtio_phy::Device::new( + VirtIONet::::new(transport, NET_BUFFER_LEN) + .unwrap() + ) }; let client_region = unsafe { @@ -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( @@ -73,32 +80,187 @@ 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; + + pub struct RxToken { + dev_inner: Rc>, + // 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, + } + + impl phy::RxToken for RxToken { + fn consume 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>, + } + + impl phy::TxToken for TxToken { + fn consume 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>, + } + + 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> { + 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, +struct PhyDeviceHandler { + 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 PhyDeviceHandler { + 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 Handler for PhyDeviceHandler { type Error = !; fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { @@ -106,23 +268,26 @@ impl Handler for HandlerImpl { 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(); @@ -130,27 +295,31 @@ impl Handler for HandlerImpl { 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(); } _ => {