hermit/drivers/net/virtio/
pci.rs

1//! A module containing a virtio network driver.
2//!
3//! The module contains ...
4
5use alloc::vec::Vec;
6use core::str::FromStr;
7
8use pci_types::CommandRegister;
9use smoltcp::phy::ChecksumCapabilities;
10use volatile::VolatileRef;
11
12use crate::arch::pci::PciConfigRegion;
13use crate::drivers::net::virtio::{CtrlQueue, NetDevCfg, RxQueues, TxQueues, VirtioNetDriver};
14use crate::drivers::pci::PciDevice;
15use crate::drivers::virtio::error::{self, VirtioError};
16use crate::drivers::virtio::transport::pci;
17use crate::drivers::virtio::transport::pci::{PciCap, UniCapsColl};
18
19// Backend-dependent interface for Virtio network driver
20impl VirtioNetDriver {
21	fn map_cfg(cap: &PciCap) -> Option<NetDevCfg> {
22		let dev_cfg = pci::map_dev_cfg::<virtio::net::Config>(cap)?;
23
24		let dev_cfg = VolatileRef::from_ref(dev_cfg);
25
26		Some(NetDevCfg {
27			raw: dev_cfg,
28			dev_id: cap.dev_id(),
29			features: virtio::net::F::empty(),
30		})
31	}
32
33	/// Instantiates a new (VirtioNetDriver)[VirtioNetDriver] struct, by checking the available
34	/// configuration structures and moving them into the struct.
35	pub(crate) fn new(
36		caps_coll: UniCapsColl,
37		device: &PciDevice<PciConfigRegion>,
38	) -> Result<Self, error::VirtioNetError> {
39		let device_id = device.device_id();
40		let UniCapsColl {
41			com_cfg,
42			notif_cfg,
43			isr_cfg,
44			dev_cfg_list,
45			..
46		} = caps_coll;
47
48		let Some(dev_cfg) = dev_cfg_list.iter().find_map(VirtioNetDriver::map_cfg) else {
49			error!("No dev config. Aborting!");
50			return Err(error::VirtioNetError::NoDevCfg(device_id));
51		};
52
53		let mtu = if let Some(my_mtu) = hermit_var!("HERMIT_MTU") {
54			u16::from_str(&my_mtu).unwrap()
55		} else {
56			// fallback to the default MTU
57			1514
58		};
59
60		let send_vqs = TxQueues::new(Vec::new(), &dev_cfg);
61		let recv_vqs = RxQueues::new(Vec::new(), &dev_cfg);
62		Ok(VirtioNetDriver {
63			dev_cfg,
64			com_cfg,
65			isr_stat: isr_cfg,
66			notif_cfg,
67			ctrl_vq: CtrlQueue::new(None),
68			recv_vqs,
69			send_vqs,
70			num_vqs: 0,
71			mtu,
72			irq: device.get_irq().unwrap(),
73			checksums: ChecksumCapabilities::default(),
74		})
75	}
76
77	/// Initializes virtio network device by mapping configuration layout to
78	/// respective structs (configuration structs are:
79	/// [ComCfg](structs.comcfg.html), [NotifCfg](structs.notifcfg.html)
80	/// [IsrStatus](structs.isrstatus.html), [PciCfg](structs.pcicfg.html)
81	/// [ShMemCfg](structs.ShMemCfg)).
82	///
83	/// Returns a driver instance of
84	/// [VirtioNetDriver](structs.virtionetdriver.html) or an [VirtioError](enums.virtioerror.html).
85	pub(crate) fn init(
86		device: &PciDevice<PciConfigRegion>,
87	) -> Result<VirtioNetDriver, VirtioError> {
88		// enable bus master mode
89		device.set_command(CommandRegister::BUS_MASTER_ENABLE);
90
91		let mut drv = match pci::map_caps(device) {
92			Ok(caps) => match VirtioNetDriver::new(caps, device) {
93				Ok(driver) => driver,
94				Err(vnet_err) => {
95					error!("Initializing new network driver failed. Aborting!");
96					return Err(VirtioError::NetDriver(vnet_err));
97				}
98			},
99			Err(err) => {
100				error!("Mapping capabilities failed. Aborting!");
101				return Err(err);
102			}
103		};
104
105		match drv.init_dev() {
106			Ok(()) => info!(
107				"Network device with id {:x}, has been initialized by driver!",
108				drv.get_dev_id()
109			),
110			Err(vnet_err) => {
111				drv.set_failed();
112				return Err(VirtioError::NetDriver(vnet_err));
113			}
114		}
115
116		if drv.is_link_up() {
117			info!("Virtio-net link is up after initialization.");
118		} else {
119			info!("Virtio-net link is down after initialization!");
120		}
121
122		Ok(drv)
123	}
124}