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}