virtio_drivers/device/net/
dev_raw.rs

1use super::{Config, EthernetAddress, Features, VirtioNetHdr, VirtioNetHdrLegacy};
2use super::{MIN_BUFFER_LEN, QUEUE_RECEIVE, QUEUE_TRANSMIT, SUPPORTED_FEATURES};
3use crate::config::read_config;
4use crate::hal::Hal;
5use crate::queue::VirtQueue;
6use crate::transport::{InterruptStatus, Transport};
7use crate::{Error, Result};
8use core::mem::size_of;
9use log::{debug, info, warn};
10use zerocopy::IntoBytes;
11
12/// Raw driver for a VirtIO network device.
13///
14/// This is a raw version of the VirtIONet driver. It provides non-blocking
15/// methods for transmitting and receiving raw slices, without the buffer
16/// management. For more higher-level functions such as receive buffer backing,
17/// see [`VirtIONet`].
18///
19/// [`VirtIONet`]: super::VirtIONet
20pub struct VirtIONetRaw<H: Hal, T: Transport, const QUEUE_SIZE: usize> {
21    transport: T,
22    mac: EthernetAddress,
23    recv_queue: VirtQueue<H, QUEUE_SIZE>,
24    send_queue: VirtQueue<H, QUEUE_SIZE>,
25    /// Whether `num_buffers` is missing in the `virtio_net_hdr` struct.
26    pub(crate) legacy_header: bool,
27}
28
29impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZE> {
30    /// Create a new VirtIO-Net driver.
31    pub fn new(mut transport: T) -> Result<Self> {
32        let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
33        info!("negotiated_features {:?}", negotiated_features);
34
35        // Read configuration space.
36        let mac = transport.read_consistent(|| read_config!(transport, Config, mac))?;
37        let status = read_config!(transport, Config, status)?;
38        debug!("Got MAC={:02x?}, status={:?}", mac, status);
39
40        let send_queue = VirtQueue::new(
41            &mut transport,
42            QUEUE_TRANSMIT,
43            negotiated_features.contains(Features::RING_INDIRECT_DESC),
44            negotiated_features.contains(Features::RING_EVENT_IDX),
45        )?;
46        let recv_queue = VirtQueue::new(
47            &mut transport,
48            QUEUE_RECEIVE,
49            negotiated_features.contains(Features::RING_INDIRECT_DESC),
50            negotiated_features.contains(Features::RING_EVENT_IDX),
51        )?;
52
53        transport.finish_init();
54
55        Ok(VirtIONetRaw {
56            transport,
57            mac,
58            recv_queue,
59            send_queue,
60            legacy_header: !negotiated_features.contains(Features::VERSION_1)
61                && !negotiated_features.contains(Features::MRG_RXBUF),
62        })
63    }
64
65    /// Acknowledge interrupt.
66    pub fn ack_interrupt(&mut self) -> InterruptStatus {
67        self.transport.ack_interrupt()
68    }
69
70    /// Disable interrupts.
71    pub fn disable_interrupts(&mut self) {
72        self.send_queue.set_dev_notify(false);
73        self.recv_queue.set_dev_notify(false);
74    }
75
76    /// Enable interrupts.
77    pub fn enable_interrupts(&mut self) {
78        self.send_queue.set_dev_notify(true);
79        self.recv_queue.set_dev_notify(true);
80    }
81
82    /// Get MAC address.
83    pub fn mac_address(&self) -> EthernetAddress {
84        self.mac
85    }
86
87    /// Whether can send packet.
88    pub fn can_send(&self) -> bool {
89        self.send_queue.available_desc() >= 2
90    }
91
92    /// Whether the length of the receive buffer is valid.
93    fn check_rx_buf_len(rx_buf: &[u8]) -> Result<()> {
94        if rx_buf.len() < MIN_BUFFER_LEN {
95            warn!("Receive buffer len {} is too small", rx_buf.len());
96            Err(Error::InvalidParam)
97        } else {
98            Ok(())
99        }
100    }
101
102    /// Whether the length of the transmit buffer is valid.
103    fn check_tx_buf_len(&self, tx_buf: &[u8]) -> Result<()> {
104        let hdr_size = if self.legacy_header {
105            size_of::<VirtioNetHdrLegacy>()
106        } else {
107            size_of::<VirtioNetHdr>()
108        };
109        if tx_buf.len() < hdr_size {
110            warn!("Transmit buffer len {} is too small", tx_buf.len());
111            Err(Error::InvalidParam)
112        } else {
113            Ok(())
114        }
115    }
116
117    /// Fill the header of the `buffer` with [`VirtioNetHdr`].
118    ///
119    /// If the `buffer` is not large enough, it returns [`Error::InvalidParam`].
120    pub fn fill_buffer_header(&self, buffer: &mut [u8]) -> Result<usize> {
121        macro_rules! fill {
122            ($hdr:ty) => {{
123                if buffer.len() < size_of::<$hdr>() {
124                    return Err(Error::InvalidParam);
125                }
126                let header = <$hdr>::default();
127                buffer[..size_of::<$hdr>()].copy_from_slice(header.as_bytes());
128                Ok(size_of::<$hdr>())
129            }};
130        }
131        if self.legacy_header {
132            fill!(VirtioNetHdrLegacy)
133        } else {
134            fill!(VirtioNetHdr)
135        }
136    }
137
138    /// Submits a request to transmit a buffer immediately without waiting for
139    /// the transmission to complete.
140    ///
141    /// It will submit request to the VirtIO net device and return a token
142    /// identifying the position of the first descriptor in the chain. If there
143    /// are not enough descriptors to allocate, then it returns
144    /// [`Error::QueueFull`].
145    ///
146    /// The caller needs to fill the `tx_buf` with a header by calling
147    /// [`fill_buffer_header`] before transmission. Then it calls [`poll_transmit`]
148    /// with the returned token to check whether the device has finished handling
149    /// the request. Once it has, the caller must call [`transmit_complete`] with
150    /// the same buffer before reading the result (transmitted length).
151    ///
152    /// # Safety
153    ///
154    /// `tx_buf` is still borrowed by the underlying VirtIO net device even after
155    /// this method returns. Thus, it is the caller's responsibility to guarantee
156    /// that they are not accessed before the request is completed in order to
157    /// avoid data races.
158    ///
159    /// [`fill_buffer_header`]: Self::fill_buffer_header
160    /// [`poll_transmit`]: Self::poll_transmit
161    /// [`transmit_complete`]: Self::transmit_complete
162    pub unsafe fn transmit_begin(&mut self, tx_buf: &[u8]) -> Result<u16> {
163        self.check_tx_buf_len(tx_buf)?;
164        let token = self.send_queue.add(&[tx_buf], &mut [])?;
165        if self.send_queue.should_notify() {
166            self.transport.notify(QUEUE_TRANSMIT);
167        }
168        Ok(token)
169    }
170
171    /// Fetches the token of the next completed transmission request from the
172    /// used ring and returns it, without removing it from the used ring. If
173    /// there are no pending completed requests it returns [`None`].
174    pub fn poll_transmit(&mut self) -> Option<u16> {
175        self.send_queue.peek_used()
176    }
177
178    /// Completes a transmission operation which was started by [`transmit_begin`].
179    /// Returns number of bytes transmitted.
180    ///
181    /// # Safety
182    ///
183    /// The same buffer must be passed in again as was passed to
184    /// [`transmit_begin`] when it returned the token.
185    ///
186    /// [`transmit_begin`]: Self::transmit_begin
187    pub unsafe fn transmit_complete(&mut self, token: u16, tx_buf: &[u8]) -> Result<usize> {
188        let len = self.send_queue.pop_used(token, &[tx_buf], &mut [])?;
189        Ok(len as usize)
190    }
191
192    /// Submits a request to receive a buffer immediately without waiting for
193    /// the reception to complete.
194    ///
195    /// It will submit request to the VirtIO net device and return a token
196    /// identifying the position of the first descriptor in the chain. If there
197    /// are not enough descriptors to allocate, then it returns
198    /// [`Error::QueueFull`].
199    ///
200    /// The caller can then call [`poll_receive`] with the returned token to
201    /// check whether the device has finished handling the request. Once it has,
202    /// the caller must call [`receive_complete`] with the same buffer before
203    /// reading the response.
204    ///
205    /// # Safety
206    ///
207    /// `rx_buf` is still borrowed by the underlying VirtIO net device even after
208    /// this method returns. Thus, it is the caller's responsibility to guarantee
209    /// that they are not accessed before the request is completed in order to
210    /// avoid data races.
211    ///
212    /// [`poll_receive`]: Self::poll_receive
213    /// [`receive_complete`]: Self::receive_complete
214    pub unsafe fn receive_begin(&mut self, rx_buf: &mut [u8]) -> Result<u16> {
215        Self::check_rx_buf_len(rx_buf)?;
216        let token = self.recv_queue.add(&[], &mut [rx_buf])?;
217        if self.recv_queue.should_notify() {
218            self.transport.notify(QUEUE_RECEIVE);
219        }
220        Ok(token)
221    }
222
223    /// Fetches the token of the next completed reception request from the
224    /// used ring and returns it, without removing it from the used ring. If
225    /// there are no pending completed requests it returns [`None`].
226    pub fn poll_receive(&self) -> Option<u16> {
227        self.recv_queue.peek_used()
228    }
229
230    /// Completes a transmission operation which was started by [`receive_begin`].
231    ///
232    /// After completion, the `rx_buf` will contain a header followed by the
233    /// received packet. It returns the length of the header and the length of
234    /// the packet.
235    ///
236    /// # Safety
237    ///
238    /// The same buffer must be passed in again as was passed to
239    /// [`receive_begin`] when it returned the token.
240    ///
241    /// [`receive_begin`]: Self::receive_begin
242    pub unsafe fn receive_complete(
243        &mut self,
244        token: u16,
245        rx_buf: &mut [u8],
246    ) -> Result<(usize, usize)> {
247        let len = self.recv_queue.pop_used(token, &[], &mut [rx_buf])? as usize;
248        let hdr_size = if self.legacy_header {
249            size_of::<VirtioNetHdrLegacy>()
250        } else {
251            size_of::<VirtioNetHdr>()
252        };
253        let packet_len = len.checked_sub(hdr_size).ok_or(Error::IoError)?;
254        Ok((hdr_size, packet_len))
255    }
256
257    /// Sends a packet to the network, and blocks until the request completed.
258    pub fn send(&mut self, tx_buf: &[u8]) -> Result {
259        macro_rules! send {
260            ($header:expr) => {{
261                let header = $header;
262                if tx_buf.is_empty() {
263                    // Special case sending an empty packet, to avoid adding an empty buffer to the
264                    // virtqueue.
265                    self.send_queue.add_notify_wait_pop(
266                        &[header.as_bytes()],
267                        &mut [],
268                        &mut self.transport,
269                    )?;
270                } else {
271                    self.send_queue.add_notify_wait_pop(
272                        &[header.as_bytes(), tx_buf],
273                        &mut [],
274                        &mut self.transport,
275                    )?;
276                }
277            }};
278        }
279        if self.legacy_header {
280            send!(VirtioNetHdrLegacy::default())
281        } else {
282            send!(VirtioNetHdr::default())
283        };
284        Ok(())
285    }
286
287    /// Blocks and waits for a packet to be received.
288    ///
289    /// After completion, the `rx_buf` will contain a header followed by the
290    /// received packet. It returns the length of the header and the length of
291    /// the packet.
292    pub fn receive_wait(&mut self, rx_buf: &mut [u8]) -> Result<(usize, usize)> {
293        // SAFETY: After calling `receive_begin`, `rx_buf` is not accessed
294        // until calling `receive_complete` when the request is complete.
295        let token = unsafe { self.receive_begin(rx_buf)? };
296        while self.poll_receive().is_none() {
297            core::hint::spin_loop();
298        }
299        // SAFETY: This `rx_buf` is the same one passed to `receive_begin`.
300        unsafe { self.receive_complete(token, rx_buf) }
301    }
302}
303
304impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> Drop for VirtIONetRaw<H, T, QUEUE_SIZE> {
305    fn drop(&mut self) {
306        // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
307        // after they have been freed.
308        self.transport.queue_unset(QUEUE_RECEIVE);
309        self.transport.queue_unset(QUEUE_TRANSMIT);
310    }
311}