mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 17:03:23 +00:00
Add virtio net device driver
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
2985cdced6
commit
7304e06c88
17
services/comps/network/Cargo.toml
Normal file
17
services/comps/network/Cargo.toml
Normal 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"] }
|
71
services/comps/network/src/driver.rs
Normal file
71
services/comps/network/src/driver.rs
Normal 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
|
||||
}
|
||||
}
|
59
services/comps/network/src/lib.rs
Normal file
59
services/comps/network/src/lib.rs
Normal 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))
|
||||
}
|
103
services/comps/network/src/virtio.rs
Normal file
103
services/comps/network/src/virtio.rs
Normal 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user