ixgbe_driver/descriptor.rs
1//! Hardware descriptor definitions for the Intel 82599 NIC.
2//!
3//! This module contains the descriptor structures used by the ixgbe hardware to
4//! manage packet transmission and reception. The descriptors are shared memory
5//! structures that are written by the driver and read by the hardware (or vice versa).
6//!
7//! # Descriptor Types
8//!
9//! - [`AdvancedRxDescriptor`]: Receive descriptor for inbound packets
10//! - [`AdvancedTxDescriptor`]: Transmit descriptor for outbound packets
11//!
12//! # Advanced Descriptors
13//!
14//! The Intel 82599 uses "advanced" descriptors which provide additional features
15//! including:
16//!
17//! - Checksum offload
18//! - VLAN insertion/stripping
19//! - RSS (Receive Side Scaling) hash
20//! - Flow Director filtering
21//!
22//! # Descriptor Lifecycle
23//!
24//! ## RX Descriptors
25//! 1. Driver allocates a descriptor ring and sets packet buffer addresses
26//! 2. Hardware writes received packets to the buffers and updates descriptor status
27//! 3. Driver reads completed descriptors and processes received packets
28//! 4. Driver resets descriptors and returns them to the hardware
29//!
30//! ## TX Descriptors
31//! 1. Driver writes packet address, length, and command flags to a descriptor
32//! 2. Hardware reads the descriptor, transmits the packet, and writes completion status
33//! 3. Driver polls for completion and can reuse the descriptor
34//!
35//! # References
36//!
37//! IntelĀ® 82599 10 GbE Controller Datasheet - Section 7.1 (Descriptor Formats)
38
39use bit_field::BitField;
40use volatile::Volatile;
41
42// Transmit descriptor bits
43/// Tx Command: End of Packet
44pub const TX_CMD_EOP: u8 = 1 << 0;
45/// Tx Command: Insert MAC FCS
46pub const TX_CMD_IFCS: u8 = 1 << 1;
47/// Tx Command: Insert Checksum
48pub const TX_CMD_IC: u8 = 1 << 2;
49/// Tx Command: Report Status
50pub const TX_CMD_RS: u8 = 1 << 3;
51/// Tx Command: Report Packet Sent
52pub const TX_CMD_RPS: u8 = 1 << 4;
53/// Tx Command: Descriptor Extension (Advanced format)
54pub const TX_CMD_DEXT: u8 = 1 << 5;
55/// Tx Command: VLAN Packet Enable
56pub const TX_CMD_VLE: u8 = 1 << 6;
57/// Tx Command: Interrupt Delay Enable
58pub const TX_CMD_IDE: u8 = 1 << 7;
59/// Tx Status: descriptor Done
60pub const TX_STATUS_DD: u8 = 1 << 0;
61/// Tx Descriptor Type: advanced
62pub const TX_DTYP_ADV: u8 = 0x3 << 4;
63/// Tx Descriptor paylen shift
64/// The paylen is located at bit 46 in the upper 64 bits of the advanced Tx descriptor.
65/// Since we have divided the upper 64 bits into 4 parts (u16,u8,u8,u32),
66/// the paylen is then located at bit 14 of the upper 32 bits of the descriptor.
67pub const TX_PAYLEN_SHIFT: u8 = 46 - 32; //(actual offset - offset of variable)
68
69// Receive descriptor bits
70/// Rx Status: Descriptor Done
71pub const RX_STATUS_DD: u8 = 1 << 0;
72/// Rx Status: End of Packet
73pub const RX_STATUS_EOP: u8 = 1 << 1;
74
75/// refer: [Theseus](https://github.com/theseus-os/Theseus/blob/theseus_main/kernel/intel_ethernet/src/descriptors.rs#L218-L219)
76/// Advanced Receive Descriptor used in the Ixgbe driver.
77/// It has 2 modes: Read and Write Back, both of which use the whole 128 bits.
78/// There is one receive descriptor per receive buffer that can be converted between these 2 modes.
79/// Read contains the addresses that the driver writes.
80/// Write Back contains information the hardware writes on receiving a packet.
81/// More information can be found in the 82599 datasheet.
82pub struct AdvancedRxDescriptor {
83 /// Starting physical address of the receive bufffer for the packet.
84 pub packet_buffer_address: Volatile<u64>,
85 /// Starting physical address of the receive buffer for the header.
86 /// This field will only be used if header splitting is enabled.
87 pub header_buffer_address: Volatile<u64>,
88}
89
90impl AdvancedRxDescriptor {
91 /// Initializes a receive descriptor by clearing its status
92 /// and setting the descriptor's physical address.
93 ///
94 /// # Arguments
95 /// * `packet_buffer_address`: starting physical address of the receive buffer.
96 pub fn init(&mut self) {
97 self.packet_buffer_address.write(0);
98 self.header_buffer_address.write(0);
99 }
100
101 /// Updates the descriptor's physical address.
102 ///
103 /// # Arguments
104 /// * `packet_buffer_address`: starting physical address of the receive buffer.
105 pub fn set_packet_address(&mut self, packet_buffer_address: u64) {
106 self.packet_buffer_address.write(packet_buffer_address);
107 }
108
109 /// Clears the status bits of the descriptor.
110 pub fn reset_status(&mut self) {
111 self.header_buffer_address.write(0);
112 }
113
114 /// Returns true if the descriptor has a received packet copied to its buffer.
115 pub fn descriptor_done(&self) -> bool {
116 (self.get_ext_status() & RX_STATUS_DD as u64) == RX_STATUS_DD as u64
117 }
118
119 /// Returns true if the descriptor's packet buffer is the last in a frame.
120 pub fn end_of_packet(&self) -> bool {
121 (self.get_ext_status() & RX_STATUS_EOP as u64) == RX_STATUS_EOP as u64
122 }
123
124 /// The length of the packet in the descriptor's packet buffer.
125 pub fn length(&self) -> u64 {
126 self.get_pkt_len()
127 }
128
129 /// Write Back mode function for the Advanced Receive Descriptor.
130 /// Returns the packet type that was used for the Receive Side Scaling hash function.
131 pub fn get_rss_type(&self) -> u64 {
132 self.packet_buffer_address.read().get_bits(0..3)
133 }
134
135 /// Write Back mode function for the Advanced Receive Descriptor.
136 /// Returns the packet type as identified by the hardware.
137 pub fn get_packet_type(&self) -> u64 {
138 self.packet_buffer_address.read().get_bits(4..16)
139 }
140
141 /// Write Back mode function for the Advanced Receive Descriptor.
142 /// Returns the number of Receive Side Coalesced packets that start in this descriptor.
143 pub fn get_rsccnt(&self) -> u64 {
144 self.packet_buffer_address.read().get_bits(17..20)
145 }
146
147 /// Write Back mode function for the Advanced Receive Descriptor.
148 /// Returns the size of the packet header in bytes.
149 pub fn get_hdr_len(&self) -> u64 {
150 self.packet_buffer_address.read().get_bits(21..30)
151 }
152
153 /// Write Back mode function for the Advanced Receive Descriptor.
154 /// Returns the Receive Side Scaling hash.
155 pub fn get_rss_hash(&self) -> u64 {
156 self.packet_buffer_address.read().get_bits(32..63)
157 }
158
159 /// Write Back mode function for the Advanced Receive Descriptor.
160 /// Returns the Flow Director Filter ID if the packet matches a filter.
161 pub fn get_fdf_id(&self) -> u64 {
162 self.packet_buffer_address.read().get_bits(32..63)
163 }
164
165 /// Write Back mode function for the Advanced Receive Descriptor.
166 /// Status information indicates whether a descriptor has been used
167 /// and whether the buffer is the last one for a packet
168 pub fn get_ext_status(&self) -> u64 {
169 self.header_buffer_address.read().get_bits(0..19)
170 }
171
172 /// Write Back mode function for the Advanced Receive Descriptor.
173 /// Returns errors reported by hardware for different packet types
174 pub fn get_ext_error(&self) -> u64 {
175 self.header_buffer_address.read().get_bits(20..31)
176 }
177
178 /// Write Back mode function for the Advanced Receive Descriptor.
179 /// Returns the number of bytes posted to the packet buffer
180 pub fn get_pkt_len(&self) -> u64 {
181 self.header_buffer_address.read().get_bits(32..47)
182 }
183
184 /// Write Back mode function for the Advanced Receive Descriptor.
185 /// If the vlan header is stripped from the packet, then the 16 bits of the VLAN tag are posted here
186 pub fn get_vlan_tag(&self) -> u64 {
187 self.header_buffer_address.read().get_bits(48..63)
188 }
189}
190
191/// Advanced Transmit Descriptor used by the `ixgbe` NIC driver.
192///
193/// # Two usage modes
194/// It has 2 modes: Read and Write Back, both of which use the whole 128 bits.
195/// There is one transmit descriptor per transmit buffer; it can be converted between these 2 modes.
196///
197/// Read contains the addresses that the driver writes.
198/// Write Back contains information the hardware writes on receiving a packet.
199///
200/// More information can be found in the 82599 datasheet.
201#[repr(C)]
202pub struct AdvancedTxDescriptor {
203 /// Starting physical address of the receive buffer for the packet.
204 pub packet_buffer_address: Volatile<u64>,
205 /// Length of data buffer
206 pub data_len: Volatile<u16>,
207 /// A multi-part field:
208 /// * `dtyp`: Descriptor Type, occupies bits `[7:4]`,
209 /// * `mac`: options to apply LinkSec and time stamp, occupies bits `[3:2]`.
210 pub dtyp_mac_rsv: Volatile<u8>,
211 /// Command bits
212 pub dcmd: Volatile<u8>,
213 /// A multi-part field:
214 /// * `paylen`: the size in bytes of the data buffer in host memory.
215 /// not including the fields that the hardware adds), occupies bits `[31:14]`.
216 /// * `popts`: options to offload checksum calculation, occupies bits `[13:8]`.
217 /// * `sta`: status of the descriptor (whether it's in use or not), occupies bits `[3:0]`.
218 pub paylen_popts_cc_idx_sta: Volatile<u32>,
219}
220
221impl AdvancedTxDescriptor {
222 /// Initializes a transmit descriptor by clearing all of its values.
223 pub fn init(&mut self) {
224 self.packet_buffer_address.write(0);
225 self.paylen_popts_cc_idx_sta.write(0);
226 self.dcmd.write(0);
227 self.dtyp_mac_rsv.write(0);
228 self.data_len.write(0);
229 }
230
231 /// Updates the transmit descriptor to send the packet.
232 /// We assume that one transmit descriptor will be used to send one packet.
233 ///
234 /// # Arguments
235 /// * `transmit_buffer_addr`: physical address of the transmit buffer.
236 /// * `transmit_buffer_length`: length of packet we want to send.
237 pub fn send(&mut self, transmit_buffer_addr: u64, transmit_buffer_length: u16) {
238 self.packet_buffer_address.write(transmit_buffer_addr);
239 self.data_len.write(transmit_buffer_length);
240 self.dtyp_mac_rsv.write(TX_DTYP_ADV);
241 self.paylen_popts_cc_idx_sta
242 .write((transmit_buffer_length as u32) << TX_PAYLEN_SHIFT);
243 self.dcmd
244 .write(TX_CMD_DEXT | TX_CMD_RS | TX_CMD_IFCS | TX_CMD_EOP);
245 }
246
247 /// Polls the Descriptor Done bit until the packet has been sent.
248 #[allow(clippy::while_immutable_condition)]
249 pub fn wait_for_packet_tx(&self) {
250 while (self.paylen_popts_cc_idx_sta.read() as u8 & TX_STATUS_DD) == 0 {}
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_tx_command_constants() {
260 assert_eq!(TX_CMD_EOP, 1 << 0);
261 assert_eq!(TX_CMD_IFCS, 1 << 1);
262 assert_eq!(TX_CMD_IC, 1 << 2);
263 assert_eq!(TX_CMD_RS, 1 << 3);
264 assert_eq!(TX_CMD_RPS, 1 << 4);
265 assert_eq!(TX_CMD_DEXT, 1 << 5);
266 assert_eq!(TX_CMD_VLE, 1 << 6);
267 assert_eq!(TX_CMD_IDE, 1 << 7);
268 }
269
270 #[test]
271 fn test_tx_status_constants() {
272 assert_eq!(TX_STATUS_DD, 1 << 0);
273 }
274
275 #[test]
276 fn test_tx_descriptor_type() {
277 assert_eq!(TX_DTYP_ADV, 0x3 << 4);
278 }
279
280 #[test]
281 fn test_tx_paylen_shift() {
282 assert_eq!(TX_PAYLEN_SHIFT, 46 - 32);
283 assert_eq!(TX_PAYLEN_SHIFT, 14);
284 }
285
286 #[test]
287 fn test_rx_status_constants() {
288 assert_eq!(RX_STATUS_DD, 1 << 0);
289 assert_eq!(RX_STATUS_EOP, 1 << 1);
290 }
291
292 #[test]
293 fn test_tx_command_bit_operations() {
294 // Test bit field operations
295 let mut cmd: u8 = 0;
296
297 cmd |= TX_CMD_EOP;
298 assert!(cmd & TX_CMD_EOP != 0);
299
300 cmd |= TX_CMD_IFCS;
301 assert!(cmd & TX_CMD_IFCS != 0);
302
303 cmd |= TX_CMD_DEXT | TX_CMD_RS | TX_CMD_EOP;
304 assert!(cmd & TX_CMD_EOP != 0);
305 assert!(cmd & TX_CMD_RS != 0);
306 assert!(cmd & TX_CMD_DEXT != 0);
307 }
308
309 #[test]
310 fn test_rx_status_bit_operations() {
311 // Test bit field operations
312 let mut status: u8 = 0;
313
314 status |= RX_STATUS_DD;
315 assert!(status & RX_STATUS_DD != 0);
316
317 status |= RX_STATUS_EOP;
318 assert!(status & RX_STATUS_EOP != 0);
319
320 // Check combined status
321 let combined = RX_STATUS_DD | RX_STATUS_EOP;
322 assert!(combined & RX_STATUS_DD != 0);
323 assert!(combined & RX_STATUS_EOP != 0);
324 }
325}