virtio/devices/
virtio_net.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5use core::cell::Cell;
6
7use kernel::utilities::cells::OptionalCell;
8use kernel::utilities::registers::{register_bitfields, LocalRegisterCopy};
9use kernel::ErrorCode;
10
11use super::super::devices::{VirtIODeviceDriver, VirtIODeviceType};
12use super::super::queues::split_queue::{SplitVirtqueue, SplitVirtqueueClient, VirtqueueBuffer};
13
14register_bitfields![u64,
15    VirtIONetFeatures [
16        VirtIONetFCsum OFFSET(0) NUMBITS(1),
17        VirtIONetFGuestCsum OFFSET(1) NUMBITS(1),
18        VirtIONetFCtrlGuestOffloads OFFSET(2) NUMBITS(1),
19        VirtIONetFMtu OFFSET(3) NUMBITS(1),
20        VirtIONetFMac OFFSET(5) NUMBITS(1),
21        VirtIONetFGuestTso4 OFFSET(7) NUMBITS(1),
22        VirtIONetFGuestTso6 OFFSET(8) NUMBITS(1),
23        VirtIONetFGuestEcn OFFSET(9) NUMBITS(1),
24        VirtIONetFGuestUfo OFFSET(10) NUMBITS(1),
25        VirtIONetFHostTso4 OFFSET(11) NUMBITS(1),
26        VirtIONetFHostTso6 OFFSET(12) NUMBITS(1),
27        VirtIONetFHostEcn OFFSET(13) NUMBITS(1),
28        VirtIONetFHostUfo OFFSET(14) NUMBITS(1),
29        VirtIONetFMrgRxbuf OFFSET(15) NUMBITS(1),
30        VirtIONetFStatus OFFSET(16) NUMBITS(1),
31        VirtIONetFCtrlVq OFFSET(17) NUMBITS(1),
32        VirtIONetFCtrlRx OFFSET(18) NUMBITS(1),
33        VirtIONetFCtrlVlan OFFSET(19) NUMBITS(1),
34        VirtIONetFGuestAnnounce OFFSET(21) NUMBITS(1),
35        VirtIONetFMq OFFSET(22) NUMBITS(1),
36        VirtIONetFCtrlMacAddr OFFSET(23) NUMBITS(1),
37        // these feature bits would not be passed through the driver, as
38        // they are in a region reserved for future extensions?
39        VirtIONetFRscExt OFFSET(61) NUMBITS(1),
40        VirtIONetFStandby OFFSET(62) NUMBITS(1),
41    ]
42];
43
44pub struct VirtIONet<'a> {
45    id: Cell<usize>,
46    rxqueue: &'a SplitVirtqueue<'static, 'static, 2>,
47    txqueue: &'a SplitVirtqueue<'static, 'static, 2>,
48    tx_header: OptionalCell<&'static mut [u8; 12]>,
49    rx_header: OptionalCell<&'static mut [u8]>,
50    rx_buffer: OptionalCell<&'static mut [u8]>,
51    client: OptionalCell<&'a dyn VirtIONetClient>,
52}
53
54impl<'a> VirtIONet<'a> {
55    pub fn new(
56        id: usize,
57        txqueue: &'a SplitVirtqueue<'static, 'static, 2>,
58        tx_header: &'static mut [u8; 12],
59        rxqueue: &'a SplitVirtqueue<'static, 'static, 2>,
60        rx_header: &'static mut [u8],
61        rx_buffer: &'static mut [u8],
62    ) -> VirtIONet<'a> {
63        txqueue.enable_used_callbacks();
64        rxqueue.enable_used_callbacks();
65
66        VirtIONet {
67            id: Cell::new(id),
68            rxqueue,
69            txqueue,
70            tx_header: OptionalCell::new(tx_header),
71            client: OptionalCell::empty(),
72            rx_header: OptionalCell::new(rx_header),
73            rx_buffer: OptionalCell::new(rx_buffer),
74        }
75    }
76
77    pub fn id(&self) -> usize {
78        self.id.get()
79    }
80
81    pub fn set_client(&self, client: &'a dyn VirtIONetClient) {
82        self.client.set(client);
83    }
84
85    // This is not executed as part of the `device_initialized` hook to avoid
86    // missing any packets if a client has not been registered, and because this
87    // device can be used in a transmit-only fashion before invoking this
88    // function.
89    pub fn enable_rx(&self) {
90        // To start operation, put the receive buffers into the device initially
91        let rx_buffer = self.rx_buffer.take().unwrap();
92        let rx_buffer_len = rx_buffer.len();
93
94        let mut buffer_chain = [
95            Some(VirtqueueBuffer {
96                buf: self.rx_header.take().unwrap(),
97                len: 12,
98                device_writeable: true,
99            }),
100            Some(VirtqueueBuffer {
101                buf: rx_buffer,
102                len: rx_buffer_len,
103                device_writeable: true,
104            }),
105        ];
106
107        self.rxqueue
108            .provide_buffer_chain(&mut buffer_chain)
109            .unwrap();
110    }
111
112    pub fn return_rx_buffer(&self, buf: &'static mut [u8]) {
113        assert!(self.rx_buffer.is_none());
114        assert!(self.rx_header.is_some());
115        self.rx_buffer.replace(buf);
116
117        // Re-register the RX buffer with the Virtqueue:
118        self.enable_rx();
119    }
120
121    pub fn send_packet(
122        &self,
123        packet: &'static mut [u8],
124        packet_len: usize,
125    ) -> Result<(), (&'static mut [u8], ErrorCode)> {
126        // Try to get a hold of the header buffer
127        //
128        // Otherwise, the device is currently busy transmissing a buffer
129        //
130        // TODO: Implement simultaneous transmissions
131        let mut packet_buf = Some(VirtqueueBuffer {
132            buf: packet,
133            len: packet_len,
134            device_writeable: false,
135        });
136
137        let header_buf = self
138            .tx_header
139            .take()
140            .ok_or(ErrorCode::BUSY)
141            .map_err(|ret| (packet_buf.take().unwrap().buf, ret))?;
142
143        // Write the header
144        //
145        // TODO: Can this be done more elegantly using a struct of registers?
146        header_buf[0] = 0; // flags -> we don't want checksumming
147        header_buf[1] = 0; // gso -> no checksumming or fragmentation
148        header_buf[2] = 0; // hdr_len_low
149        header_buf[3] = 0; // hdr_len_high
150        header_buf[4] = 0; // gso_size
151        header_buf[5] = 0; // gso_size
152        header_buf[6] = 0; // csum_start
153        header_buf[7] = 0; // csum_start
154        header_buf[8] = 0; // csum_offset
155        header_buf[9] = 0; // csum_offsetb
156        header_buf[10] = 0; // num_buffers
157        header_buf[11] = 0; // num_buffers
158
159        let mut buffer_chain = [
160            Some(VirtqueueBuffer {
161                buf: header_buf,
162                len: 12,
163                device_writeable: false,
164            }),
165            packet_buf.take(),
166        ];
167
168        self.txqueue
169            .provide_buffer_chain(&mut buffer_chain)
170            .map_err(move |ret| (buffer_chain[1].take().unwrap().buf, ret))?;
171
172        Ok(())
173    }
174}
175
176impl SplitVirtqueueClient<'static> for VirtIONet<'_> {
177    fn buffer_chain_ready(
178        &self,
179        queue_number: u32,
180        buffer_chain: &mut [Option<VirtqueueBuffer<'static>>],
181        bytes_used: usize,
182    ) {
183        if queue_number == self.rxqueue.queue_number().unwrap() {
184            // Received a packet
185
186            let rx_header = buffer_chain[0].take().expect("No header buffer").buf;
187            // TODO: do something with the header
188            self.rx_header.replace(rx_header);
189
190            let rx_buffer = buffer_chain[1].take().expect("No rx content buffer").buf;
191            self.client.map(move |client| {
192                client.packet_received(self.id.get(), rx_buffer, bytes_used - 12)
193            });
194        } else if queue_number == self.txqueue.queue_number().unwrap() {
195            // Sent a packet
196
197            let header_buf = buffer_chain[0].take().expect("No header buffer").buf;
198            self.tx_header.replace(header_buf.try_into().unwrap());
199
200            let packet_buf = buffer_chain[1].take().expect("No packet buffer").buf;
201            self.client
202                .map(move |client| client.packet_sent(self.id.get(), packet_buf));
203        } else {
204            panic!("Callback from unknown queue");
205        }
206    }
207}
208
209impl VirtIODeviceDriver for VirtIONet<'_> {
210    fn negotiate_features(&self, offered_features: u64) -> Option<u64> {
211        let offered_features =
212            LocalRegisterCopy::<u64, VirtIONetFeatures::Register>::new(offered_features);
213        let mut negotiated_features = LocalRegisterCopy::<u64, VirtIONetFeatures::Register>::new(0);
214
215        if offered_features.is_set(VirtIONetFeatures::VirtIONetFMac) {
216            // VIRTIO_NET_F_MAC offered, which means that the device has a MAC
217            // address. Accept this feature, which is required for this driver
218            // for now.
219            negotiated_features.modify(VirtIONetFeatures::VirtIONetFMac::SET);
220        } else {
221            return None;
222        }
223
224        // TODO: QEMU doesn't offer this, but don't we need it? Does QEMU
225        // implicitly provide the feature but not offer it? Find out!
226        // if offered_features & (1 << 15) != 0 {
227        //     // VIRTIO_NET_F_MRG_RXBUF
228        //     //
229        //     // accept
230        //     negotiated_features |= 1 << 15;
231        // } else {
232        //     panic!("Missing NET_F_MRG_RXBUF");
233        // }
234
235        // Ignore everything else
236        Some(negotiated_features.get())
237    }
238
239    fn device_type(&self) -> VirtIODeviceType {
240        VirtIODeviceType::NetworkCard
241    }
242}
243
244pub trait VirtIONetClient {
245    fn packet_sent(&self, id: usize, buffer: &'static mut [u8]);
246    fn packet_received(&self, id: usize, buffer: &'static mut [u8], len: usize);
247}