hermit/drivers/net/virtio/
mod.rs1cfg_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
40pub(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 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 fn post_processing(_buffer_tkn: &mut UsedBufferToken) -> Result<(), VirtioNetError> {
80 Ok(())
81 }
82
83 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 if let Err(err) = vq.dispatch(buff_tkn, false, BufferType::Direct) {
137 error!("{err:#?}");
138 break;
139 }
140 }
141}
142
143pub struct TxQueues {
146 vqs: Vec<Box<dyn Virtq>>,
147 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 while vq.try_recv().is_ok() {}
184 }
185 }
186
187 fn add(&mut self, vq: Box<dyn Virtq>) {
188 self.vqs.push(vq);
191 }
192}
193
194pub(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 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 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 fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
244 where
245 F: FnOnce(&mut [u8]) -> R,
246 {
247 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 !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 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
385impl 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 #[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 #[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 #[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 self.recv_vqs.disable_notifs();
461 }
462
463 pub fn enable_interrupts(&mut self) {
464 self.recv_vqs.enable_notifs();
467 }
468
469 pub fn init_dev(&mut self) -> Result<(), VirtioNetError> {
475 self.com_cfg.reset_dev();
477
478 self.com_cfg.ack_dev();
480
481 self.com_cfg.set_drv();
483
484 let minimal_features = virtio::net::F::VERSION_1 | virtio::net::F::MAC;
485
486 let mut features = minimal_features
488 | virtio::net::F::INDIRECT_DESC
490 | virtio::net::F::RING_PACKED
492 | virtio::net::F::NOTIFICATION_DATA
493 | virtio::net::F::CSUM
495 | virtio::net::F::GUEST_CSUM
497 | virtio::net::F::MTU
499 | virtio::net::F::MRG_RXBUF
501 | virtio::net::F::STATUS
503 | virtio::net::F::MQ;
505
506 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 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 self.com_cfg.features_ok();
585
586 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 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 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 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 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 fn dev_spec_init(&mut self) -> Result<(), VirtioNetError> {
656 self.virtqueue_init()?;
657 info!("Network driver successfully initialized virtqueues.");
658
659 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 fn virtqueue_init(&mut self) -> Result<(), VirtioNetError> {
693 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 self.num_vqs = 2;
723 }
724
725 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 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 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 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 vq.disable_notifs();
787
788 self.send_vqs.add(Box::from(vq));
789 }
790 }
791
792 Ok(())
793 }
794}
795
796pub mod constants {
797 pub const MAX_NUM_VQ: u16 = 2;
799}
800
801pub mod error {
804 #[derive(Debug, Copy, Clone)]
806 pub enum VirtioNetError {
807 #[cfg(feature = "pci")]
808 NoDevCfg(u16),
809 FailFeatureNeg(u16),
810 FeatureRequirementsNotMet(virtio::net::F),
813 IncompatibleFeatureSets(virtio::net::F, virtio::net::F),
816 }
817}