Skip to main content

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}