Add virtio net device driver

This commit is contained in:
Jianfeng Jiang
2023-05-30 16:34:28 +08:00
committed by Tate, Hongliang Tian
parent 2985cdced6
commit 7304e06c88
20 changed files with 875 additions and 15 deletions

View File

@ -0,0 +1,17 @@
[package]
name = "jinux-network"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
component = { path = "../../libs/comp-sys/component" }
jinux-frame = { path = "../../../framework/jinux-frame" }
jinux-virtio = { path = "../virtio" }
jinux-util = { path = "../../libs/jinux-util" }
jinux-pci = { path = "../pci" }
spin = "0.9.4"
ringbuf = { version = "0.3.2", default-features = false, features = ["alloc"] }
log = "0.4"
smoltcp = { version = "0.9.1", default-features = false, features = ["alloc", "log", "medium-ethernet", "medium-ip", "proto-dhcpv4", "proto-ipv4", "proto-igmp", "socket-icmp", "socket-udp", "socket-tcp", "socket-raw", "socket-dhcpv4"] }

View File

@ -0,0 +1,71 @@
use alloc::vec;
use smoltcp::phy::{self, Medium};
use crate::VirtioNet;
use jinux_virtio::device::network::{
buffer::{RxBuffer, TxBuffer},
device::NetworkDevice,
};
impl phy::Device for VirtioNet {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken<'a>;
fn receive(
&mut self,
_timestamp: smoltcp::time::Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.can_receive() {
let device = self.device_mut();
let rx_buffer = device.receive().unwrap();
Some((RxToken(rx_buffer), TxToken(device)))
} else {
None
}
}
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
if self.can_send() {
let device = self.device_mut();
Some(TxToken(device))
} else {
None
}
}
fn capabilities(&self) -> phy::DeviceCapabilities {
let mut caps = phy::DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(1);
caps.medium = Medium::Ethernet;
caps
}
}
pub struct RxToken(RxBuffer);
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let packet_but = self.0.packet_mut();
let res = f(packet_but);
res
}
}
pub struct TxToken<'a>(&'a mut NetworkDevice);
impl<'a> phy::TxToken for TxToken<'a> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut buffer = vec![0u8; len];
let res = f(&mut buffer);
let tx_buffer = TxBuffer::new(&buffer);
self.0.send(tx_buffer).expect("Send packet failed");
res
}
}

View File

@ -0,0 +1,59 @@
#![no_std]
#![forbid(unsafe_code)]
#![feature(trait_alias)]
extern crate alloc;
use alloc::sync::Arc;
use alloc::vec::Vec;
use component::init_component;
use component::ComponentInitError;
use core::any::Any;
use jinux_frame::sync::SpinLock;
use jinux_virtio::device::network::device::EthernetAddr;
use jinux_virtio::VirtioDeviceType;
use spin::Once;
mod driver;
mod virtio;
pub use virtio::VirtioNet;
pub trait NetworkDevice: Send + Sync + Any {
fn irq_number(&self) -> u8;
fn name(&self) -> &'static str;
fn mac_addr(&self) -> EthernetAddr;
}
pub trait NetDeviceIrqHandler = Fn(u8) + Send + Sync + 'static;
pub(crate) static NETWORK_IRQ_HANDLERS: Once<SpinLock<Vec<Arc<dyn NetDeviceIrqHandler>>>> =
Once::new();
#[init_component]
fn init() -> Result<(), ComponentInitError> {
NETWORK_IRQ_HANDLERS.call_once(|| SpinLock::new(Vec::new()));
Ok(())
}
pub fn probe_virtio_net() -> Result<VirtioNet, ComponentInitError> {
let network_devices = {
let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap();
virtio.get_device(VirtioDeviceType::Network)
};
for device in network_devices {
let virtio_net = VirtioNet::new(device);
// FIXME: deal with multiple net devices
return Ok(virtio_net);
}
Err(ComponentInitError::Unknown)
}
pub fn register_net_device_irq_handler(callback: impl NetDeviceIrqHandler) {
NETWORK_IRQ_HANDLERS
.get()
.unwrap()
.lock()
.push(Arc::new(callback))
}

View File

@ -0,0 +1,103 @@
use jinux_frame::offset_of;
use jinux_frame::sync::SpinLock;
use jinux_frame::trap::TrapFrame;
use jinux_pci::msix::MSIX;
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::device::network::device::{self, EthernetAddr};
use jinux_virtio::PCIVirtioDevice;
use jinux_virtio::VirtioPciCommonCfg;
use log::debug;
use crate::{NetworkDevice, NETWORK_IRQ_HANDLERS};
pub struct VirtioNet {
/// Network Device
device: device::NetworkDevice,
/// Own common cfg to avoid other devices access this frame
_common_cfg: InFramePtr<VirtioPciCommonCfg>,
_msix: SpinLock<MSIX>,
irq_number: u8,
}
impl NetworkDevice for VirtioNet {
fn irq_number(&self) -> u8 {
self.irq_number
}
fn name(&self) -> &'static str {
"virtio net"
}
fn mac_addr(&self) -> EthernetAddr {
self.device.mac_addr()
}
}
impl VirtioNet {
pub(crate) fn new(virtio_device: PCIVirtioDevice) -> Self {
let device = if let jinux_virtio::device::VirtioDevice::Network(network_device) =
virtio_device.device
{
network_device
} else {
panic!("Invalid device type")
};
let common_cfg = virtio_device.common_cfg;
let mut msix = virtio_device.msix;
let config_msix_vector =
common_cfg.read_at(offset_of!(VirtioPciCommonCfg, config_msix_vector)) as usize;
let mut network_irq_num = 0;
for i in 0..msix.table_size as usize {
let msix_entry = msix.table.get_mut(i).unwrap();
if !msix_entry.irq_handle.is_empty() {
panic!("msix already have irq functions");
}
if config_msix_vector == i {
debug!(
"network config space change irq number = {}",
msix_entry.irq_handle.num()
);
msix_entry.irq_handle.on_active(config_space_change);
} else {
network_irq_num = msix_entry.irq_handle.num();
msix_entry.irq_handle.on_active(handle_network_event);
}
}
debug_assert!(network_irq_num != 0);
debug!("Network device irq num = {}", network_irq_num);
let device = VirtioNet {
device,
_common_cfg: common_cfg,
irq_number: network_irq_num,
_msix: SpinLock::new(msix),
};
device
}
pub(crate) fn can_receive(&self) -> bool {
self.device.can_receive()
}
pub(crate) fn can_send(&self) -> bool {
self.device.can_send()
}
pub(crate) fn device_mut(&mut self) -> &mut device::NetworkDevice {
&mut self.device
}
}
/// Interrupt handler if network device config space changes
fn config_space_change(_: &TrapFrame) {
debug!("network device config space change");
}
/// Interrupt handler if network device receives some packet
fn handle_network_event(trap_frame: &TrapFrame) {
let irq_num = trap_frame.trap_num as u8;
for callback in NETWORK_IRQ_HANDLERS.get().unwrap().lock().iter() {
callback(irq_num);
}
}