hermit/drivers/net/virtio/
mod.rs

1//! A module containing a virtio network driver.
2//!
3//! The module contains ...
4
5cfg_if::cfg_if! {
6	if #[cfg(feature = "pci")] {
7		mod pci;
8	} else {
9		mod mmio;
10	}
11}
12
13use alloc::boxed::Box;
14use alloc::vec::Vec;
15
16use smoltcp::phy::{Checksum, ChecksumCapabilities};
17use smoltcp::wire::{ETHERNET_HEADER_LEN, EthernetFrame, Ipv4Packet, Ipv6Packet};
18use virtio::net::{ConfigVolatileFieldAccess, Hdr, HdrF};
19use virtio::{DeviceConfigSpace, FeatureBits};
20use volatile::VolatileRef;
21use volatile::access::ReadOnly;
22
23use self::constants::MAX_NUM_VQ;
24use self::error::VirtioNetError;
25use crate::config::VIRTIO_MAX_QUEUE_SIZE;
26use crate::drivers::net::NetworkDriver;
27#[cfg(not(feature = "pci"))]
28use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
29#[cfg(feature = "pci")]
30use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg};
31use crate::drivers::virtio::virtqueue::packed::PackedVq;
32use crate::drivers::virtio::virtqueue::split::SplitVq;
33use crate::drivers::virtio::virtqueue::{
34	AvailBufferToken, BufferElem, BufferType, UsedBufferToken, Virtq, VqIndex, VqSize,
35};
36use crate::drivers::{Driver, InterruptLine};
37use crate::executor::device::{RxToken, TxToken};
38use crate::mm::device_alloc::DeviceAlloc;
39
40/// A wrapper struct for the raw configuration structure.
41/// Handling the right access to fields, as some are read-only
42/// for the driver.
43pub(crate) struct NetDevCfg {
44	pub raw: VolatileRef<'static, virtio::net::Config, ReadOnly>,
45	pub dev_id: u16,
46	pub features: virtio::net::F,
47}
48
49pub struct CtrlQueue(Option<Box<dyn Virtq>>);
50
51impl CtrlQueue {
52	pub fn new(vq: Option<Box<dyn Virtq>>) -> Self {
53		CtrlQueue(vq)
54	}
55}
56
57pub struct RxQueues {
58	vqs: Vec<Box<dyn Virtq>>,
59	packet_size: u32,
60}
61
62impl RxQueues {
63	pub fn new(vqs: Vec<Box<dyn Virtq>>, dev_cfg: &NetDevCfg) -> Self {
64		// See Virtio specification v1.1 - 5.1.6.3.1
65		//
66		let packet_size = if dev_cfg.features.contains(virtio::net::F::MRG_RXBUF) {
67			1514
68		} else {
69			dev_cfg.raw.as_ptr().mtu().read().to_ne().into()
70		};
71
72		Self { vqs, packet_size }
73	}
74
75	/// Takes care of handling packets correctly which need some processing after being received.
76	/// This currently include nothing. But in the future it might include among others:
77	/// * Calculating missing checksums
78	/// * Merging receive buffers, by simply checking the poll_queue (if VIRTIO_NET_F_MRG_BUF)
79	fn post_processing(_buffer_tkn: &mut UsedBufferToken) -> Result<(), VirtioNetError> {
80		Ok(())
81	}
82
83	/// Adds a given queue to the underlying vector and populates the queue with RecvBuffers.
84	///
85	/// Queues are all populated according to Virtio specification v1.1. - 5.1.6.3.1
86	fn add(&mut self, mut vq: Box<dyn Virtq>) {
87		const BUFF_PER_PACKET: u16 = 2;
88		let num_packets: u16 = u16::from(vq.size()) / BUFF_PER_PACKET;
89		fill_queue(vq.as_mut(), num_packets, self.packet_size);
90		self.vqs.push(vq);
91	}
92
93	fn get_next(&mut self) -> Option<UsedBufferToken> {
94		self.vqs[0].try_recv().ok()
95	}
96
97	fn enable_notifs(&mut self) {
98		for vq in &mut self.vqs {
99			vq.enable_notifs();
100		}
101	}
102
103	fn disable_notifs(&mut self) {
104		for vq in &mut self.vqs {
105			vq.disable_notifs();
106		}
107	}
108
109	fn has_packet(&self) -> bool {
110		self.vqs.iter().any(|vq| vq.has_used_buffers())
111	}
112}
113
114fn fill_queue(vq: &mut dyn Virtq, num_packets: u16, packet_size: u32) {
115	for _ in 0..num_packets {
116		let buff_tkn = match AvailBufferToken::new(
117			vec![],
118			vec![
119				BufferElem::Sized(Box::<Hdr, _>::new_uninit_in(DeviceAlloc)),
120				BufferElem::Vector(Vec::with_capacity_in(
121					packet_size.try_into().unwrap(),
122					DeviceAlloc,
123				)),
124			],
125		) {
126			Ok(tkn) => tkn,
127			Err(_vq_err) => {
128				error!("Setup of network queue failed, which should not happen!");
129				panic!("setup of network queue failed!");
130			}
131		};
132
133		// BufferTokens are directly provided to the queue
134		// TransferTokens are directly dispatched
135		// Transfers will be awaited at the queue
136		if let Err(err) = vq.dispatch(buff_tkn, false, BufferType::Direct) {
137			error!("{err:#?}");
138			break;
139		}
140	}
141}
142
143/// Structure which handles transmission of packets and delegation
144/// to the respective queue structures.
145pub struct TxQueues {
146	vqs: Vec<Box<dyn Virtq>>,
147	/// Indicates, whether the Driver/Device are using multiple
148	/// queues for communication.
149	packet_length: u32,
150}
151
152impl TxQueues {
153	pub fn new(vqs: Vec<Box<dyn Virtq>>, dev_cfg: &NetDevCfg) -> Self {
154		let packet_length = if dev_cfg.features.contains(virtio::net::F::GUEST_TSO4)
155			| dev_cfg.features.contains(virtio::net::F::GUEST_TSO6)
156			| dev_cfg.features.contains(virtio::net::F::GUEST_UFO)
157		{
158			0x0001_000e
159		} else {
160			dev_cfg.raw.as_ptr().mtu().read().to_ne().into()
161		};
162
163		Self { vqs, packet_length }
164	}
165	#[allow(dead_code)]
166	fn enable_notifs(&mut self) {
167		for vq in &mut self.vqs {
168			vq.enable_notifs();
169		}
170	}
171
172	#[allow(dead_code)]
173	fn disable_notifs(&mut self) {
174		for vq in &mut self.vqs {
175			vq.disable_notifs();
176		}
177	}
178
179	fn poll(&mut self) {
180		for vq in &mut self.vqs {
181			// We don't do anything with the buffers but we need to receive them for the
182			// ring slots to be emptied and the memory from the previous transfers to be freed.
183			while vq.try_recv().is_ok() {}
184		}
185	}
186
187	fn add(&mut self, vq: Box<dyn Virtq>) {
188		// Currently we are doing nothing with the additional queues. They are inactive and might be used in the
189		// future
190		self.vqs.push(vq);
191	}
192}
193
194/// Virtio network driver struct.
195///
196/// Struct allows to control devices virtqueues as also
197/// the device itself.
198pub(crate) struct VirtioNetDriver {
199	pub(super) dev_cfg: NetDevCfg,
200	pub(super) com_cfg: ComCfg,
201	pub(super) isr_stat: IsrStatus,
202	pub(super) notif_cfg: NotifCfg,
203
204	pub(super) ctrl_vq: CtrlQueue,
205	pub(super) recv_vqs: RxQueues,
206	pub(super) send_vqs: TxQueues,
207
208	pub(super) num_vqs: u16,
209	pub(super) mtu: u16,
210	pub(super) irq: InterruptLine,
211	pub(super) checksums: ChecksumCapabilities,
212}
213
214impl NetworkDriver for VirtioNetDriver {
215	/// Returns the mac address of the device.
216	/// If VIRTIO_NET_F_MAC is not set, the function panics currently!
217	fn get_mac_address(&self) -> [u8; 6] {
218		if self.dev_cfg.features.contains(virtio::net::F::MAC) {
219			self.com_cfg
220				.device_config_space()
221				.read_config_with(|| self.dev_cfg.raw.as_ptr().mac().read())
222		} else {
223			unreachable!("Currently VIRTIO_NET_F_MAC must be negotiated!")
224		}
225	}
226
227	/// Returns the current MTU of the device.
228	fn get_mtu(&self) -> u16 {
229		self.mtu
230	}
231
232	fn get_checksums(&self) -> ChecksumCapabilities {
233		self.checksums.clone()
234	}
235
236	#[allow(dead_code)]
237	fn has_packet(&self) -> bool {
238		self.recv_vqs.has_packet()
239	}
240
241	/// Provides smoltcp a slice to copy the IP packet and transfer the packet
242	/// to the send queue.
243	fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
244	where
245		F: FnOnce(&mut [u8]) -> R,
246	{
247		// We need to poll to get the queue to remove elements from the table and make space for
248		// what we are about to add
249		self.send_vqs.poll();
250
251		assert!(len < usize::try_from(self.send_vqs.packet_length).unwrap());
252		let mut packet = Vec::with_capacity_in(len, DeviceAlloc);
253		let result = unsafe {
254			let result = f(packet.spare_capacity_mut().assume_init_mut());
255			packet.set_len(len);
256			result
257		};
258
259		let mut header = Box::new_in(<Hdr as Default>::default(), DeviceAlloc);
260		// If a checksum isn't necessary, we have inform the host within the header
261		// see Virtio specification 5.1.6.2
262		if !self.checksums.tcp.tx() || !self.checksums.udp.tx() {
263			header.flags = HdrF::NEEDS_CSUM;
264			let ethernet_frame: smoltcp::wire::EthernetFrame<&[u8]> =
265				EthernetFrame::new_unchecked(&packet);
266			let packet_header_len: u16;
267			let protocol;
268			match ethernet_frame.ethertype() {
269				smoltcp::wire::EthernetProtocol::Ipv4 => {
270					let packet = Ipv4Packet::new_unchecked(ethernet_frame.payload());
271					packet_header_len = packet.header_len().into();
272					protocol = Some(packet.next_header());
273				}
274				smoltcp::wire::EthernetProtocol::Ipv6 => {
275					let packet = Ipv6Packet::new_unchecked(ethernet_frame.payload());
276					packet_header_len = packet.header_len().try_into().unwrap();
277					protocol = Some(packet.next_header());
278				}
279				_ => {
280					packet_header_len = 0;
281					protocol = None;
282				}
283			}
284			header.csum_start =
285				(u16::try_from(ETHERNET_HEADER_LEN).unwrap() + packet_header_len).into();
286			header.csum_offset = match protocol {
287				Some(smoltcp::wire::IpProtocol::Tcp) => 16,
288				Some(smoltcp::wire::IpProtocol::Udp) => 6,
289				_ => 0,
290			}
291			.into();
292		}
293
294		let buff_tkn = AvailBufferToken::new(
295			vec![BufferElem::Sized(header), BufferElem::Vector(packet)],
296			vec![],
297		)
298		.unwrap();
299
300		self.send_vqs.vqs[0]
301			.dispatch(buff_tkn, false, BufferType::Direct)
302			.unwrap();
303
304		result
305	}
306
307	fn receive_packet(&mut self) -> Option<(RxToken, TxToken)> {
308		let mut buffer_tkn = self.recv_vqs.get_next()?;
309		RxQueues::post_processing(&mut buffer_tkn)
310			.inspect_err(|vnet_err| warn!("Post processing failed. Err: {vnet_err:?}"))
311			.ok()?;
312		let first_header = buffer_tkn.used_recv_buff.pop_front_downcast::<Hdr>()?;
313		let first_packet = buffer_tkn.used_recv_buff.pop_front_vec()?;
314		trace!("Header: {first_header:?}");
315
316		// According to VIRTIO spec v1.2 sec. 5.1.6.3.2, "num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not negotiated."
317		// Unfortunately, NVIDIA MLX5 does not comply with this requirement and we have to manually set the value to the correct one.
318		let num_buffers = if self.dev_cfg.features.contains(virtio::net::F::MRG_RXBUF) {
319			first_header.num_buffers.to_ne()
320		} else {
321			1
322		};
323
324		let mut packets = Vec::with_capacity(num_buffers.into());
325		packets.push(first_packet);
326
327		for _ in 1..num_buffers {
328			let mut buffer_tkn = self.recv_vqs.get_next().unwrap();
329			RxQueues::post_processing(&mut buffer_tkn)
330				.inspect_err(|vnet_err| warn!("Post processing failed. Err: {vnet_err:?}"))
331				.ok()?;
332			let _header = buffer_tkn.used_recv_buff.pop_front_downcast::<Hdr>()?;
333			let packet = buffer_tkn.used_recv_buff.pop_front_vec()?;
334			packets.push(packet);
335		}
336
337		fill_queue(
338			self.recv_vqs.vqs[0].as_mut(),
339			num_buffers,
340			self.recv_vqs.packet_size,
341		);
342
343		let vec_data = packets.into_iter().flatten().collect();
344
345		Some((RxToken::new(vec_data), TxToken::new()))
346	}
347
348	fn set_polling_mode(&mut self, value: bool) {
349		if value {
350			self.disable_interrupts();
351		} else {
352			self.enable_interrupts();
353		}
354	}
355
356	fn handle_interrupt(&mut self) {
357		let status = self.isr_stat.is_queue_interrupt();
358
359		#[cfg(not(feature = "pci"))]
360		if status.contains(virtio::mmio::InterruptStatus::CONFIGURATION_CHANGE_NOTIFICATION) {
361			info!("Configuration changes are not possible! Aborting");
362			todo!("Implement possibility to change config on the fly...")
363		}
364
365		#[cfg(feature = "pci")]
366		if status.contains(virtio::pci::IsrStatus::DEVICE_CONFIGURATION_INTERRUPT) {
367			info!("Configuration changes are not possible! Aborting");
368			todo!("Implement possibility to change config on the fly...")
369		}
370
371		self.isr_stat.acknowledge();
372	}
373}
374
375impl Driver for VirtioNetDriver {
376	fn get_interrupt_number(&self) -> InterruptLine {
377		self.irq
378	}
379
380	fn get_name(&self) -> &'static str {
381		"virtio"
382	}
383}
384
385// Backend-independent interface for Virtio network driver
386impl VirtioNetDriver {
387	#[cfg(feature = "pci")]
388	pub fn get_dev_id(&self) -> u16 {
389		self.dev_cfg.dev_id
390	}
391
392	#[cfg(feature = "pci")]
393	pub fn set_failed(&mut self) {
394		self.com_cfg.set_failed();
395	}
396
397	/// Returns the current status of the device, if VIRTIO_NET_F_STATUS
398	/// has been negotiated. Otherwise assumes an active device.
399	#[cfg(not(feature = "pci"))]
400	pub fn dev_status(&self) -> virtio::net::S {
401		if self.dev_cfg.features.contains(virtio::net::F::STATUS) {
402			self.dev_cfg.raw.as_ptr().status().read()
403		} else {
404			virtio::net::S::LINK_UP
405		}
406	}
407
408	/// Returns the links status.
409	/// If feature VIRTIO_NET_F_STATUS has not been negotiated, then we assume the link is up!
410	#[cfg(feature = "pci")]
411	pub fn is_link_up(&self) -> bool {
412		if self.dev_cfg.features.contains(virtio::net::F::STATUS) {
413			self.dev_cfg
414				.raw
415				.as_ptr()
416				.status()
417				.read()
418				.contains(virtio::net::S::LINK_UP)
419		} else {
420			true
421		}
422	}
423
424	#[allow(dead_code)]
425	pub fn is_announce(&self) -> bool {
426		if self.dev_cfg.features.contains(virtio::net::F::STATUS) {
427			self.dev_cfg
428				.raw
429				.as_ptr()
430				.status()
431				.read()
432				.contains(virtio::net::S::ANNOUNCE)
433		} else {
434			false
435		}
436	}
437
438	/// Returns the maximal number of virtqueue pairs allowed. This is the
439	/// dominant setting to define the number of virtqueues for the network
440	/// device and overrides the num_vq field in the common config.
441	///
442	/// Returns 1 (i.e. minimum number of pairs) if VIRTIO_NET_F_MQ is not set.
443	#[allow(dead_code)]
444	pub fn get_max_vq_pairs(&self) -> u16 {
445		if self.dev_cfg.features.contains(virtio::net::F::MQ) {
446			self.dev_cfg
447				.raw
448				.as_ptr()
449				.max_virtqueue_pairs()
450				.read()
451				.to_ne()
452		} else {
453			1
454		}
455	}
456
457	pub fn disable_interrupts(&mut self) {
458		// For send and receive queues?
459		// Only for receive? Because send is off anyway?
460		self.recv_vqs.disable_notifs();
461	}
462
463	pub fn enable_interrupts(&mut self) {
464		// For send and receive queues?
465		// Only for receive? Because send is off anyway?
466		self.recv_vqs.enable_notifs();
467	}
468
469	/// Initializes the device in adherence to specification. Returns Some(VirtioNetError)
470	/// upon failure and None in case everything worked as expected.
471	///
472	/// See Virtio specification v1.1. - 3.1.1.
473	///                      and v1.1. - 5.1.5
474	pub fn init_dev(&mut self) -> Result<(), VirtioNetError> {
475		// Reset
476		self.com_cfg.reset_dev();
477
478		// Indicate device, that OS noticed it
479		self.com_cfg.ack_dev();
480
481		// Indicate device, that driver is able to handle it
482		self.com_cfg.set_drv();
483
484		let minimal_features = virtio::net::F::VERSION_1 | virtio::net::F::MAC;
485
486		// If wanted, push new features into feats here:
487		let mut features = minimal_features
488			// Indirect descriptors can be used
489			| virtio::net::F::INDIRECT_DESC
490			// Packed Vq can be used
491			| virtio::net::F::RING_PACKED
492			| virtio::net::F::NOTIFICATION_DATA
493			// Host should avoid the creation of checksums
494			| virtio::net::F::CSUM
495			// Guest avoids the creation of checksums
496			| virtio::net::F::GUEST_CSUM
497			// MTU setting can be used
498			| virtio::net::F::MTU
499			// Driver can merge receive buffers
500			| virtio::net::F::MRG_RXBUF
501			// the link status can be announced
502			| virtio::net::F::STATUS
503			// Multiqueue support
504			| virtio::net::F::MQ;
505
506		// Currently the driver does NOT support the features below.
507		// In order to provide functionality for these, the driver
508		// needs to take care of calculating checksum in
509		// RxQueues.post_processing()
510		// | virtio::net::F::GUEST_TSO4
511		// | virtio::net::F::GUEST_TSO6
512
513		// Negotiate features with device. Automatically reduces selected feats in order to meet device capabilities.
514		// Aborts in case incompatible features are selected by the driver or the device does not support min_feat_set.
515		match self.negotiate_features(features) {
516			Ok(()) => info!(
517				"Driver found a subset of features for virtio device {:x}. Features are: {features:?}",
518				self.dev_cfg.dev_id
519			),
520			Err(vnet_err) => {
521				match vnet_err {
522					VirtioNetError::FeatureRequirementsNotMet(features) => {
523						error!(
524							"Network drivers feature set {features:?} does not satisfy rules in section 5.1.3.1 of specification v1.1. Aborting!"
525						);
526						return Err(vnet_err);
527					}
528					VirtioNetError::IncompatibleFeatureSets(drv_feats, dev_feats) => {
529						// Create a new matching feature set for device and driver if the minimal set is met!
530						if !dev_feats.contains(minimal_features) {
531							error!(
532								"Device features set, does not satisfy minimal features needed. Aborting!"
533							);
534							return Err(VirtioNetError::FailFeatureNeg(self.dev_cfg.dev_id));
535						}
536
537						let common_features = drv_feats & dev_feats;
538						if common_features.is_empty() {
539							error!(
540								"Feature negotiation failed with minimal feature set. Aborting!"
541							);
542							return Err(VirtioNetError::FailFeatureNeg(self.dev_cfg.dev_id));
543						}
544						features = common_features;
545
546						match self.negotiate_features(features) {
547							Ok(()) => info!(
548								"Driver found a subset of features for virtio device {:x}. Features are: {features:?}",
549								self.dev_cfg.dev_id
550							),
551							Err(vnet_err) => match vnet_err {
552								VirtioNetError::FeatureRequirementsNotMet(features) => {
553									error!(
554										"Network device offers a feature set {features:?} when used completely does not satisfy rules in section 5.1.3.1 of specification v1.1. Aborting!"
555									);
556									return Err(vnet_err);
557								}
558								_ => {
559									error!(
560										"Feature Set after reduction still not usable. Set: {features:?}. Aborting!"
561									);
562									return Err(vnet_err);
563								}
564							},
565						}
566					}
567					VirtioNetError::FailFeatureNeg(_) => {
568						error!(
569							"Wanted set of features is NOT supported by device. Set: {features:?}"
570						);
571						return Err(vnet_err);
572					}
573					#[cfg(feature = "pci")]
574					VirtioNetError::NoDevCfg(_) => {
575						error!("No device config found.");
576						return Err(vnet_err);
577					}
578				}
579			}
580		}
581
582		// Indicates the device, that the current feature set is final for the driver
583		// and will not be changed.
584		self.com_cfg.features_ok();
585
586		// Checks if the device has accepted final set. This finishes feature negotiation.
587		if self.com_cfg.check_features() {
588			info!(
589				"Features have been negotiated between virtio network device {:x} and driver.",
590				self.dev_cfg.dev_id
591			);
592			// Set feature set in device config fur future use.
593			self.dev_cfg.features = features;
594		} else {
595			return Err(VirtioNetError::FailFeatureNeg(self.dev_cfg.dev_id));
596		}
597
598		self.dev_spec_init()?;
599		info!(
600			"Device specific initialization for Virtio network device {:x} finished",
601			self.dev_cfg.dev_id
602		);
603
604		// At this point the device is "live"
605		self.com_cfg.drv_ok();
606
607		if self.dev_cfg.features.contains(virtio::net::F::CSUM)
608			&& self.dev_cfg.features.contains(virtio::net::F::GUEST_CSUM)
609		{
610			self.checksums.udp = Checksum::None;
611			self.checksums.tcp = Checksum::None;
612		} else if self.dev_cfg.features.contains(virtio::net::F::CSUM) {
613			self.checksums.udp = Checksum::Rx;
614			self.checksums.tcp = Checksum::Rx;
615		} else if self.dev_cfg.features.contains(virtio::net::F::GUEST_CSUM) {
616			self.checksums.udp = Checksum::Tx;
617			self.checksums.tcp = Checksum::Tx;
618		}
619		debug!("{:?}", self.checksums);
620
621		if self.dev_cfg.features.contains(virtio::net::F::MTU) {
622			self.mtu = self.dev_cfg.raw.as_ptr().mtu().read().to_ne();
623		}
624
625		Ok(())
626	}
627
628	/// Negotiates a subset of features, understood and wanted by both the OS
629	/// and the device.
630	fn negotiate_features(
631		&mut self,
632		driver_features: virtio::net::F,
633	) -> Result<(), VirtioNetError> {
634		let device_features = virtio::net::F::from(self.com_cfg.dev_features());
635
636		if device_features.requirements_satisfied() {
637			info!("Feature set wanted by network driver are in conformance with specification.");
638		} else {
639			return Err(VirtioNetError::FeatureRequirementsNotMet(device_features));
640		}
641
642		if device_features.contains(driver_features) {
643			// If device supports subset of features write feature set to common config
644			self.com_cfg.set_drv_features(driver_features.into());
645			Ok(())
646		} else {
647			Err(VirtioNetError::IncompatibleFeatureSets(
648				driver_features,
649				device_features,
650			))
651		}
652	}
653
654	/// Device Specific initialization according to Virtio specifictation v1.1. - 5.1.5
655	fn dev_spec_init(&mut self) -> Result<(), VirtioNetError> {
656		self.virtqueue_init()?;
657		info!("Network driver successfully initialized virtqueues.");
658
659		// Add a control if feature is negotiated
660		if self.dev_cfg.features.contains(virtio::net::F::CTRL_VQ) {
661			if self.dev_cfg.features.contains(virtio::net::F::RING_PACKED) {
662				self.ctrl_vq = CtrlQueue(Some(Box::new(
663					PackedVq::new(
664						&mut self.com_cfg,
665						&self.notif_cfg,
666						VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
667						VqIndex::from(self.num_vqs),
668						self.dev_cfg.features.into(),
669					)
670					.unwrap(),
671				)));
672			} else {
673				self.ctrl_vq = CtrlQueue(Some(Box::new(
674					SplitVq::new(
675						&mut self.com_cfg,
676						&self.notif_cfg,
677						VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
678						VqIndex::from(self.num_vqs),
679						self.dev_cfg.features.into(),
680					)
681					.unwrap(),
682				)));
683			}
684
685			self.ctrl_vq.0.as_mut().unwrap().enable_notifs();
686		}
687
688		Ok(())
689	}
690
691	/// Initialize virtqueues via the queue interface and populates receiving queues
692	fn virtqueue_init(&mut self) -> Result<(), VirtioNetError> {
693		// We are assuming here, that the device single source of truth is the
694		// device specific configuration. Hence we do NOT check if
695		//
696		// max_virtqueue_pairs + 1 < num_queues
697		//
698		// - the plus 1 is due to the possibility of an existing control queue
699		// - the num_queues is found in the ComCfg struct of the device and defines the maximal number
700		// of supported queues.
701		if self.dev_cfg.features.contains(virtio::net::F::MQ) {
702			if self
703				.dev_cfg
704				.raw
705				.as_ptr()
706				.max_virtqueue_pairs()
707				.read()
708				.to_ne() * 2 >= MAX_NUM_VQ
709			{
710				self.num_vqs = MAX_NUM_VQ;
711			} else {
712				self.num_vqs = self
713					.dev_cfg
714					.raw
715					.as_ptr()
716					.max_virtqueue_pairs()
717					.read()
718					.to_ne() * 2;
719			}
720		} else {
721			// Minimal number of virtqueues defined in the standard v1.1. - 5.1.5 Step 1
722			self.num_vqs = 2;
723		}
724
725		// The loop is running from 0 to num_vqs and the indexes are provided to the VqIndex::from function in this way
726		// in order to allow the indexes of the queues to be in a form of:
727		//
728		// index i for receive queue
729		// index i+1 for send queue
730		//
731		// as it is wanted by the network network device.
732		// see Virtio specification v1.1. - 5.1.2
733		// Assure that we have always an even number of queues (i.e. pairs of queues).
734		assert_eq!(self.num_vqs % 2, 0);
735
736		for i in 0..(self.num_vqs / 2) {
737			if self.dev_cfg.features.contains(virtio::net::F::RING_PACKED) {
738				let mut vq = PackedVq::new(
739					&mut self.com_cfg,
740					&self.notif_cfg,
741					VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
742					VqIndex::from(2 * i),
743					self.dev_cfg.features.into(),
744				)
745				.unwrap();
746				// Interrupt for receiving packets is wanted
747				vq.enable_notifs();
748
749				self.recv_vqs.add(Box::from(vq));
750
751				let mut vq = PackedVq::new(
752					&mut self.com_cfg,
753					&self.notif_cfg,
754					VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
755					VqIndex::from(2 * i + 1),
756					self.dev_cfg.features.into(),
757				)
758				.unwrap();
759				// Interrupt for communicating that a sended packet left, is not needed
760				vq.disable_notifs();
761
762				self.send_vqs.add(Box::from(vq));
763			} else {
764				let mut vq = SplitVq::new(
765					&mut self.com_cfg,
766					&self.notif_cfg,
767					VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
768					VqIndex::from(2 * i),
769					self.dev_cfg.features.into(),
770				)
771				.unwrap();
772				// Interrupt for receiving packets is wanted
773				vq.enable_notifs();
774
775				self.recv_vqs.add(Box::from(vq));
776
777				let mut vq = SplitVq::new(
778					&mut self.com_cfg,
779					&self.notif_cfg,
780					VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
781					VqIndex::from(2 * i + 1),
782					self.dev_cfg.features.into(),
783				)
784				.unwrap();
785				// Interrupt for communicating that a sended packet left, is not needed
786				vq.disable_notifs();
787
788				self.send_vqs.add(Box::from(vq));
789			}
790		}
791
792		Ok(())
793	}
794}
795
796pub mod constants {
797	// Configuration constants
798	pub const MAX_NUM_VQ: u16 = 2;
799}
800
801/// Error module of virtios network driver. Containing the (VirtioNetError)[VirtioNetError]
802/// enum.
803pub mod error {
804	/// Network drivers error enum.
805	#[derive(Debug, Copy, Clone)]
806	pub enum VirtioNetError {
807		#[cfg(feature = "pci")]
808		NoDevCfg(u16),
809		FailFeatureNeg(u16),
810		/// Set of features does not adhere to the requirements of features
811		/// indicated by the specification
812		FeatureRequirementsNotMet(virtio::net::F),
813		/// The first field contains the feature bits wanted by the driver.
814		/// but which are incompatible with the device feature set, second field.
815		IncompatibleFeatureSets(virtio::net::F, virtio::net::F),
816	}
817}