hermit/drivers/vsock/
mod.rs

1#![allow(dead_code)]
2
3#[cfg(feature = "pci")]
4pub mod pci;
5
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8use core::mem;
9
10use pci_types::InterruptLine;
11use virtio::FeatureBits;
12use virtio::vsock::Hdr;
13
14use crate::config::VIRTIO_MAX_QUEUE_SIZE;
15use crate::drivers::Driver;
16use crate::drivers::virtio::error::VirtioVsockError;
17#[cfg(feature = "pci")]
18use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg};
19use crate::drivers::virtio::virtqueue::split::SplitVq;
20use crate::drivers::virtio::virtqueue::{
21	AvailBufferToken, BufferElem, BufferType, UsedBufferToken, Virtq, VqIndex, VqSize,
22};
23#[cfg(feature = "pci")]
24use crate::drivers::vsock::pci::VsockDevCfgRaw;
25use crate::mm::device_alloc::DeviceAlloc;
26
27fn fill_queue(vq: &mut dyn Virtq, num_packets: u16, packet_size: u32) {
28	for _ in 0..num_packets {
29		let buff_tkn = match AvailBufferToken::new(
30			vec![],
31			vec![
32				BufferElem::Sized(Box::<Hdr, _>::new_uninit_in(DeviceAlloc)),
33				BufferElem::Vector(Vec::with_capacity_in(
34					packet_size.try_into().unwrap(),
35					DeviceAlloc,
36				)),
37			],
38		) {
39			Ok(tkn) => tkn,
40			Err(_vq_err) => {
41				error!("Setup of network queue failed, which should not happen!");
42				panic!("setup of network queue failed!");
43			}
44		};
45
46		// BufferTokens are directly provided to the queue
47		// TransferTokens are directly dispatched
48		// Transfers will be awaited at the queue
49		if let Err(err) = vq.dispatch(buff_tkn, false, BufferType::Direct) {
50			error!("{err:#?}");
51			break;
52		}
53	}
54}
55
56pub(crate) struct RxQueue {
57	vq: Option<Box<dyn Virtq>>,
58	packet_size: u32,
59}
60
61impl RxQueue {
62	pub fn new() -> Self {
63		Self {
64			vq: None,
65
66			packet_size: crate::VSOCK_PACKET_SIZE,
67		}
68	}
69
70	pub fn add(&mut self, mut vq: Box<dyn Virtq>) {
71		const BUFF_PER_PACKET: u16 = 2;
72		let num_packets: u16 = u16::from(vq.size()) / BUFF_PER_PACKET;
73		info!("num_packets {num_packets}");
74		fill_queue(vq.as_mut(), num_packets, self.packet_size);
75
76		self.vq = Some(vq);
77	}
78
79	pub fn enable_notifs(&mut self) {
80		if let Some(ref mut vq) = self.vq {
81			vq.enable_notifs();
82		}
83	}
84
85	pub fn disable_notifs(&mut self) {
86		if let Some(ref mut vq) = self.vq {
87			vq.disable_notifs();
88		}
89	}
90
91	fn get_next(&mut self) -> Option<UsedBufferToken> {
92		self.vq.as_mut().unwrap().try_recv().ok()
93	}
94
95	pub fn process_packet<F>(&mut self, mut f: F)
96	where
97		F: FnMut(&Hdr, &[u8]),
98	{
99		while let Some(mut buffer_tkn) = self.get_next() {
100			let header = buffer_tkn
101				.used_recv_buff
102				.pop_front_downcast::<Hdr>()
103				.unwrap();
104			let packet = buffer_tkn.used_recv_buff.pop_front_vec().unwrap();
105
106			if let Some(ref mut vq) = self.vq {
107				f(&header, &packet[..]);
108
109				fill_queue(vq.as_mut(), 1, self.packet_size);
110			} else {
111				panic!("Invalid length of receive queue");
112			}
113		}
114	}
115}
116
117pub(crate) struct TxQueue {
118	vq: Option<Box<dyn Virtq>>,
119	/// Indicates, whether the Driver/Device are using multiple
120	/// queues for communication.
121	packet_length: u32,
122}
123
124impl TxQueue {
125	pub fn new() -> Self {
126		Self {
127			vq: None,
128			packet_length: crate::VSOCK_PACKET_SIZE + mem::size_of::<Hdr>() as u32,
129		}
130	}
131
132	pub fn add(&mut self, vq: Box<dyn Virtq>) {
133		self.vq = Some(vq);
134	}
135
136	pub fn enable_notifs(&mut self) {
137		if let Some(ref mut vq) = self.vq {
138			vq.enable_notifs();
139		}
140	}
141
142	pub fn disable_notifs(&mut self) {
143		if let Some(ref mut vq) = self.vq {
144			vq.disable_notifs();
145		}
146	}
147
148	fn poll(&mut self) {
149		if let Some(ref mut vq) = self.vq {
150			while vq.try_recv().is_ok() {}
151		}
152	}
153
154	/// Provides a slice to copy the packet and transfer the packet
155	/// to the send queue. The caller has to create the header
156	/// for the vsock interface.
157	pub fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
158	where
159		F: FnOnce(&mut [u8]) -> R,
160	{
161		// We need to poll to get the queue to remove elements from the table and make space for
162		// what we are about to add
163		self.poll();
164		if let Some(ref mut vq) = self.vq {
165			assert!(len < usize::try_from(self.packet_length).unwrap());
166			let mut packet = Vec::with_capacity_in(len, DeviceAlloc);
167			let result = unsafe {
168				let result = f(packet.spare_capacity_mut().assume_init_mut());
169				packet.set_len(len);
170				result
171			};
172
173			let buff_tkn = AvailBufferToken::new(vec![BufferElem::Vector(packet)], vec![]).unwrap();
174
175			vq.dispatch(buff_tkn, false, BufferType::Direct).unwrap();
176
177			result
178		} else {
179			panic!("Unable to get send queue");
180		}
181	}
182}
183
184pub(crate) struct EventQueue {
185	vq: Option<Box<dyn Virtq>>,
186	packet_size: u32,
187}
188
189impl EventQueue {
190	pub fn new() -> Self {
191		Self {
192			vq: None,
193			packet_size: 128u32,
194		}
195	}
196
197	/// Adds a given queue to the underlying vector and populates the queue with RecvBuffers.
198	///
199	/// Queues are all populated according to Virtio specification v1.1. - 5.1.6.3.1
200	fn add(&mut self, mut vq: Box<dyn Virtq>) {
201		const BUFF_PER_PACKET: u16 = 2;
202		let num_packets: u16 = u16::from(vq.size()) / BUFF_PER_PACKET;
203		fill_queue(vq.as_mut(), num_packets, self.packet_size);
204		self.vq = Some(vq);
205	}
206
207	pub fn enable_notifs(&mut self) {
208		if let Some(ref mut vq) = self.vq {
209			vq.enable_notifs();
210		}
211	}
212
213	pub fn disable_notifs(&mut self) {
214		if let Some(ref mut vq) = self.vq {
215			vq.disable_notifs();
216		}
217	}
218}
219
220/// A wrapper struct for the raw configuration structure.
221/// Handling the right access to fields, as some are read-only
222/// for the driver.
223pub(crate) struct VsockDevCfg {
224	pub raw: &'static VsockDevCfgRaw,
225	pub dev_id: u16,
226	pub features: virtio::vsock::F,
227}
228
229pub(crate) struct VirtioVsockDriver {
230	pub(super) dev_cfg: VsockDevCfg,
231	pub(super) com_cfg: ComCfg,
232	pub(super) isr_stat: IsrStatus,
233	pub(super) notif_cfg: NotifCfg,
234	pub(super) irq: InterruptLine,
235
236	pub(super) event_vq: EventQueue,
237	pub(super) recv_vq: RxQueue,
238	pub(super) send_vq: TxQueue,
239}
240
241impl Driver for VirtioVsockDriver {
242	fn get_interrupt_number(&self) -> InterruptLine {
243		self.irq
244	}
245
246	fn get_name(&self) -> &'static str {
247		"virtio"
248	}
249}
250
251impl VirtioVsockDriver {
252	#[cfg(feature = "pci")]
253	pub fn get_dev_id(&self) -> u16 {
254		self.dev_cfg.dev_id
255	}
256
257	#[inline]
258	pub fn get_cid(&self) -> u64 {
259		self.dev_cfg.raw.guest_cid
260	}
261
262	#[cfg(feature = "pci")]
263	pub fn set_failed(&mut self) {
264		self.com_cfg.set_failed();
265	}
266
267	pub fn disable_interrupts(&mut self) {
268		// For send and receive queues?
269		// Only for receive? Because send is off anyway?
270		self.recv_vq.disable_notifs();
271	}
272
273	pub fn enable_interrupts(&mut self) {
274		// For send and receive queues?
275		// Only for receive? Because send is off anyway?
276		self.recv_vq.enable_notifs();
277	}
278
279	pub fn handle_interrupt(&mut self) {
280		let status = self.isr_stat.is_queue_interrupt();
281
282		#[cfg(not(feature = "pci"))]
283		if status.contains(virtio::mmio::InterruptStatus::CONFIGURATION_CHANGE_NOTIFICATION) {
284			info!("Configuration changes are not possible! Aborting");
285			todo!("Implement possibility to change config on the fly...")
286		}
287
288		#[cfg(feature = "pci")]
289		if status.contains(virtio::pci::IsrStatus::DEVICE_CONFIGURATION_INTERRUPT) {
290			info!("Configuration changes are not possible! Aborting");
291			todo!("Implement possibility to change config on the fly...")
292		}
293
294		self.isr_stat.acknowledge();
295	}
296
297	/// Negotiates a subset of features, understood and wanted by both the OS
298	/// and the device.
299	fn negotiate_features(
300		&mut self,
301		driver_features: virtio::vsock::F,
302	) -> Result<(), VirtioVsockError> {
303		let device_features = virtio::vsock::F::from(self.com_cfg.dev_features());
304
305		if device_features.requirements_satisfied() {
306			info!("Feature set wanted by vsock driver are in conformance with specification.");
307		} else {
308			return Err(VirtioVsockError::FeatureRequirementsNotMet(device_features));
309		}
310
311		if device_features.contains(driver_features) {
312			// If device supports subset of features write feature set to common config
313			self.com_cfg.set_drv_features(driver_features.into());
314			Ok(())
315		} else {
316			Err(VirtioVsockError::IncompatibleFeatureSets(
317				driver_features,
318				device_features,
319			))
320		}
321	}
322
323	/// Initializes the device in adherence to specification. Returns Some(VirtioVsockError)
324	/// upon failure and None in case everything worked as expected.
325	///
326	/// See Virtio specification v1.1. - 3.1.1.
327	///                      and v1.1. - 5.10.6
328	pub fn init_dev(&mut self) -> Result<(), VirtioVsockError> {
329		// Reset
330		self.com_cfg.reset_dev();
331
332		// Indicate device, that OS noticed it
333		self.com_cfg.ack_dev();
334
335		// Indicate device, that driver is able to handle it
336		self.com_cfg.set_drv();
337
338		let features = virtio::vsock::F::VERSION_1;
339		self.negotiate_features(features)?;
340
341		// Indicates the device, that the current feature set is final for the driver
342		// and will not be changed.
343		self.com_cfg.features_ok();
344
345		// Checks if the device has accepted final set. This finishes feature negotiation.
346		if self.com_cfg.check_features() {
347			info!(
348				"Features have been negotiated between virtio socket device {:x} and driver.",
349				self.dev_cfg.dev_id
350			);
351			// Set feature set in device config fur future use.
352			self.dev_cfg.features = features;
353		} else {
354			return Err(VirtioVsockError::FailFeatureNeg(self.dev_cfg.dev_id));
355		}
356
357		// create the queues and tell device about them
358		self.recv_vq.add(Box::new(
359			SplitVq::new(
360				&mut self.com_cfg,
361				&self.notif_cfg,
362				VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
363				VqIndex::from(0u16),
364				self.dev_cfg.features.into(),
365			)
366			.unwrap(),
367		));
368		// Interrupt for receiving packets is wanted
369		self.recv_vq.enable_notifs();
370
371		self.send_vq.add(Box::new(
372			SplitVq::new(
373				&mut self.com_cfg,
374				&self.notif_cfg,
375				VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
376				VqIndex::from(1u16),
377				self.dev_cfg.features.into(),
378			)
379			.unwrap(),
380		));
381		// Interrupt for communicating that a sended packet left, is not needed
382		self.send_vq.disable_notifs();
383
384		// create the queues and tell device about them
385		self.event_vq.add(Box::new(
386			SplitVq::new(
387				&mut self.com_cfg,
388				&self.notif_cfg,
389				VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
390				VqIndex::from(2u16),
391				self.dev_cfg.features.into(),
392			)
393			.unwrap(),
394		));
395		// Interrupt for event packets is wanted
396		self.event_vq.enable_notifs();
397
398		// At this point the device is "live"
399		self.com_cfg.drv_ok();
400
401		Ok(())
402	}
403
404	#[inline]
405	pub fn process_packet<F>(&mut self, f: F)
406	where
407		F: FnMut(&Hdr, &[u8]),
408	{
409		self.recv_vq.process_packet(f);
410	}
411
412	/// Provides a slice to copy the packet and transfer the packet
413	/// to the send queue. The caller has to creatde the header
414	/// for the vsock interface.
415	#[inline]
416	pub fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
417	where
418		F: FnOnce(&mut [u8]) -> R,
419	{
420		self.send_vq.send_packet(len, f)
421	}
422}
423
424/// Error module of virtio socket device driver.
425pub mod error {
426	/// Virtio socket device error enum.
427	#[derive(Debug, Copy, Clone)]
428	pub enum VirtioVsockError {
429		NoDevCfg(u16),
430		NoComCfg(u16),
431		NoIsrCfg(u16),
432		NoNotifCfg(u16),
433		FailFeatureNeg(u16),
434		/// Set of features does not adhere to the requirements of features
435		/// indicated by the specification
436		FeatureRequirementsNotMet(virtio::vsock::F),
437		/// The first u64 contains the feature bits wanted by the driver.
438		/// but which are incompatible with the device feature set, second u64.
439		IncompatibleFeatureSets(virtio::vsock::F, virtio::vsock::F),
440	}
441}