1#![no_std]
2
3use core::ptr;
4use intel_ethernet::{
5 ctrl, eerd, int_bits, rctl, regs, rx_errors, rx_status, tctl, tx_status, LegacyRxDesc,
6 LegacyTxDesc,
7};
8use net_core::NetError;
9use nic_buffers::{DmaAllocator, DmaRegion};
10use nic_queues::{RxDescriptor, RxRing, TxDescriptor, TxRing};
11
12pub const NUM_RX: usize = 128;
13pub const NUM_TX: usize = 128;
14pub const RX_BUF_SIZE: usize = 4096;
15
16const RESET_MAX_POLLS: u32 = 200_000;
17const EEPROM_MAX_POLLS: u32 = 50_000;
18
19const WATCHDOG_TX_THRESHOLD: u64 = 10_000;
22
23pub const E1000_DEVICE_IDS: &[u16] = &[0x100E, 0x100F, 0x10D3, 0x153A, 0x1539];
24pub const INTEL_VENDOR: u16 = 0x8086;
25
26pub struct E1000Stats {
28 pub rx_ok: u64,
29 pub rx_errors: u64,
30 pub rx_crc_errors: u64,
31 pub rx_length_errors: u64,
32 pub tx_ok: u64,
33 pub tx_errors: u64,
34 pub tx_dropped: u64,
35 pub tx_late_collision: u64,
36 pub tx_underrun: u64,
37 pub watchdog_resets: u64,
38}
39
40impl E1000Stats {
41 const fn new() -> Self {
42 Self {
43 rx_ok: 0,
44 rx_errors: 0,
45 rx_crc_errors: 0,
46 rx_length_errors: 0,
47 tx_ok: 0,
48 tx_errors: 0,
49 tx_dropped: 0,
50 tx_late_collision: 0,
51 tx_underrun: 0,
52 watchdog_resets: 0,
53 }
54 }
55}
56
57pub struct E1000Nic {
58 mmio: u64,
59 rx: RxRing<LegacyRxDesc>,
60 rx_bufs: [DmaRegion; NUM_RX],
61 rx_ring_phys: u64,
62 tx: TxRing<LegacyTxDesc>,
63 tx_bufs: [DmaRegion; NUM_TX],
64 tx_ring_phys: u64,
65 mac: [u8; 6],
66 link_up: bool,
67 stats: E1000Stats,
68 tx_since_last_reclaim: u64,
69 last_tdh: usize,
71}
72
73unsafe impl Send for E1000Nic {}
76
77#[inline]
78unsafe fn rd(base: u64, reg: usize) -> u32 {
79 ptr::read_volatile((base + reg as u64) as *const u32)
80}
81
82#[inline]
83unsafe fn wr(base: u64, reg: usize, val: u32) {
84 ptr::write_volatile((base + reg as u64) as *mut u32, val)
85}
86
87impl E1000Nic {
88 pub fn init(mmio_base: u64, alloc: &dyn DmaAllocator) -> Result<Self, NetError> {
89 unsafe {
90 let c = rd(mmio_base, regs::CTRL);
92 log::trace!("e1000: assert CTRL.RST (ctrl={:#x})", c);
93 wr(mmio_base, regs::CTRL, c | ctrl::RST);
94 let mut reset_done = false;
95 for poll in 0..RESET_MAX_POLLS {
96 let ctrl = rd(mmio_base, regs::CTRL);
97 if ctrl & ctrl::RST == 0 {
98 log::trace!(
99 "e1000: reset complete after {} polls (ctrl={:#x})",
100 poll + 1,
101 ctrl
102 );
103 reset_done = true;
104 break;
105 }
106 core::hint::spin_loop();
107 }
108 if !reset_done {
109 log::warn!(
110 "e1000: reset timeout after {} CTRL polls (RST never cleared)",
111 RESET_MAX_POLLS
112 );
113 return Err(NetError::NotReady);
114 }
115
116 wr(mmio_base, regs::IMC, 0xFFFF_FFFF);
118 let _ = rd(mmio_base, regs::ICR);
119
120 log::trace!("e1000: read MAC");
122 let mac = Self::read_mac(mmio_base)?;
123 log::trace!(
124 "e1000: MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
125 mac[0],
126 mac[1],
127 mac[2],
128 mac[3],
129 mac[4],
130 mac[5]
131 );
132
133 wr(mmio_base, regs::VET, 0x8100);
138 for i in 0..128u32 {
141 wr(mmio_base, regs::VFTA + (i as usize) * 4, 0);
142 }
143
144 let rx_ring_region = alloc
146 .alloc_dma(NUM_RX * core::mem::size_of::<LegacyRxDesc>())
147 .map_err(|_| NetError::NotReady)?;
148 ptr::write_bytes(rx_ring_region.virt, 0, rx_ring_region.size);
149 let rx_descs = rx_ring_region.virt as *mut LegacyRxDesc;
150
151 let mut rx_bufs = [DmaRegion::ZERO; NUM_RX];
152 for rx_buf in rx_bufs.iter_mut().take(NUM_RX) {
153 let buf = alloc
154 .alloc_dma(RX_BUF_SIZE)
155 .map_err(|_| NetError::NotReady)?;
156 ptr::write_bytes(buf.virt, 0, RX_BUF_SIZE);
157 *rx_buf = buf;
158 }
159
160 wr(mmio_base, regs::RDBAL, rx_ring_region.phys as u32);
161 wr(mmio_base, regs::RDBAH, (rx_ring_region.phys >> 32) as u32);
162 wr(mmio_base, regs::RDLEN, rx_ring_region.size as u32);
163 wr(mmio_base, regs::RDH, 0);
164 wr(mmio_base, regs::RDT, (NUM_RX - 1) as u32);
165
166 for (i, buf) in rx_bufs.iter().enumerate().take(NUM_RX) {
167 (*rx_descs.add(i)).addr = buf.phys;
168 }
169
170 let tx_ring_region = alloc
172 .alloc_dma(NUM_TX * core::mem::size_of::<LegacyTxDesc>())
173 .map_err(|_| NetError::NotReady)?;
174 ptr::write_bytes(tx_ring_region.virt, 0, tx_ring_region.size);
175 let tx_descs = tx_ring_region.virt as *mut LegacyTxDesc;
176
177 wr(mmio_base, regs::TDBAL, tx_ring_region.phys as u32);
178 wr(mmio_base, regs::TDBAH, (tx_ring_region.phys >> 32) as u32);
179 wr(mmio_base, regs::TDLEN, tx_ring_region.size as u32);
180 wr(mmio_base, regs::TDH, 0);
181 wr(mmio_base, regs::TDT, 0);
182
183 let mut tx_bufs = [DmaRegion::ZERO; NUM_TX];
185 for tx_buf in tx_bufs.iter_mut().take(NUM_TX) {
186 let buf = alloc
187 .alloc_dma(net_core::MTU)
188 .map_err(|_| NetError::NotReady)?;
189 ptr::write_bytes(buf.virt, 0, net_core::MTU);
190 *tx_buf = buf;
191 }
192
193 wr(
195 mmio_base,
196 regs::TCTL,
197 tctl::EN | tctl::PSP | (0x10 << tctl::CT_SHIFT) | (0x40 << tctl::COLD_SHIFT),
198 );
199
200 wr(
203 mmio_base,
204 regs::RCTL,
205 rctl::EN | rctl::BAM | rctl::BSIZE_4096 | rctl::SECRC,
206 );
207
208 let c = rd(mmio_base, regs::CTRL);
210 wr(mmio_base, regs::CTRL, c | ctrl::SLU);
211
212 wr(mmio_base, regs::ITR, 488);
227 wr(mmio_base, regs::RDTR, 0);
228 wr(mmio_base, regs::RADV, 128);
229 wr(mmio_base, regs::TIDV, 0);
230 wr(mmio_base, regs::TADV, 64);
231
232 wr(
233 mmio_base,
234 regs::IMS,
235 int_bits::RXT0
236 | int_bits::LSC
237 | int_bits::RXDMT0
238 | int_bits::RXO
239 | int_bits::TXDW
240 | int_bits::TXQE,
241 );
242 let status = rd(mmio_base, regs::STATUS);
243 let link_up = (status & 0x02) != 0;
244
245 Ok(Self {
246 mmio: mmio_base,
247 rx: RxRing::new(rx_descs, NUM_RX),
248 rx_bufs,
249 rx_ring_phys: rx_ring_region.phys,
250 tx: TxRing::new(tx_descs, NUM_TX),
251 tx_bufs,
252 tx_ring_phys: tx_ring_region.phys,
253 mac,
254 link_up,
255 stats: E1000Stats::new(),
256 tx_since_last_reclaim: 0,
257 last_tdh: 0,
258 })
259 }
260 }
261
262 pub fn mac_address(&self) -> [u8; 6] {
263 self.mac
264 }
265
266 pub fn link_up(&self) -> bool {
267 self.link_up
268 }
269
270 pub fn check_link(&mut self) -> bool {
271 unsafe {
272 let status = rd(self.mmio, regs::STATUS);
273 self.link_up = (status & 0x02) != 0;
274 }
275 self.link_up
276 }
277
278 pub fn stats(&self) -> &E1000Stats {
279 &self.stats
280 }
281
282 fn recycle_rx_desc(&mut self, idx: usize) {
284 self.rx.desc_mut(idx).clear_status();
285 self.rx
286 .desc_mut(idx)
287 .set_buffer_addr(self.rx_bufs[idx].phys);
288 let new_tail = self.rx.advance();
289 unsafe {
290 wr(self.mmio, regs::RDT, new_tail as u32);
291 }
292 }
293
294 pub fn receive(&mut self, buf: &mut [u8]) -> Result<usize, NetError> {
295 if !self.check_link() {
296 return Err(NetError::LinkDown);
297 }
298
299 let (idx, pkt_len) = self.rx.poll().ok_or(NetError::NoPacket)?;
300
301 let err = self.rx.desc(idx).errors;
303 let st = self.rx.desc(idx).status;
304
305 if (err & rx_errors::CE) != 0 {
306 log::warn!("e1000: RX CRC error on descriptor {}", idx);
307 self.recycle_rx_desc(idx);
308 self.stats.rx_crc_errors += 1;
309 self.stats.rx_errors += 1;
310 return Err(NetError::NoPacket);
311 }
312 if (err & rx_errors::SE) != 0 {
313 log::warn!("e1000: RX symbol error on descriptor {}", idx);
314 self.recycle_rx_desc(idx);
315 self.stats.rx_errors += 1;
316 return Err(NetError::NoPacket);
317 }
318 if (err & rx_errors::TCPE) != 0 {
319 log::trace!("e1000: RX TCP/UDP checksum error on descriptor {}", idx);
320 }
321 if (err & rx_errors::IPE) != 0 {
322 log::trace!("e1000: RX IP checksum error on descriptor {}", idx);
323 }
324 if (st & rx_status::EOP) == 0 {
325 log::warn!(
326 "e1000: RX descriptor {} missing EOP : fragment dropped",
327 idx
328 );
329 self.recycle_rx_desc(idx);
330 self.stats.rx_length_errors += 1;
331 self.stats.rx_errors += 1;
332 return Err(NetError::NoPacket);
333 }
334
335 let len = pkt_len as usize;
336 if buf.len() < len {
337 self.recycle_rx_desc(idx);
338 return Err(NetError::BufferTooSmall);
339 }
340
341 unsafe {
342 ptr::copy_nonoverlapping(self.rx_bufs[idx].virt, buf.as_mut_ptr(), len);
343 }
344
345 self.recycle_rx_desc(idx);
346 self.stats.rx_ok += 1;
347 Ok(len)
348 }
349
350 pub fn transmit(&mut self, buf: &[u8]) -> Result<(), NetError> {
351 if !self.check_link() {
352 return Err(NetError::LinkDown);
353 }
354
355 if buf.len() > net_core::MTU {
356 return Err(NetError::BufferTooSmall);
357 }
358
359 let idx = self.tx.tail();
360
361 self.reclaim_completed_tx_buffers();
363
364 if self.tx.desc(idx).cmd != 0 && !self.tx.is_done(idx) {
366 self.stats.tx_dropped += 1;
367 return Err(NetError::TxQueueFull);
368 }
369
370 unsafe {
372 ptr::copy_nonoverlapping(buf.as_ptr(), self.tx_bufs[idx].virt, buf.len());
373 }
374
375 let _submitted = self.tx.submit(self.tx_bufs[idx].phys, buf.len() as u16);
376 unsafe {
377 wr(self.mmio, regs::TDT, self.tx.tail() as u32);
378 }
379
380 self.tx_since_last_reclaim += 1;
381 self.stats.tx_ok += 1;
382 Ok(())
383 }
384
385 fn reclaim_completed_tx_buffers(&mut self) {
389 let head = unsafe { rd(self.mmio, regs::TDH) } as usize % NUM_TX;
391 let tail = self.tx.tail();
392
393 let mut idx = head;
394 while idx != tail {
395 if self.tx.is_done(idx) {
396 let st = self.tx.desc(idx).status;
397 if (st & tx_status::LC) != 0 {
398 log::warn!("e1000: TX late collision at descriptor {}", idx);
399 self.stats.tx_late_collision += 1;
400 self.stats.tx_errors += 1;
401 }
402 if (st & tx_status::TU) != 0 {
403 log::warn!("e1000: TX underrun at descriptor {}", idx);
404 self.stats.tx_underrun += 1;
405 self.stats.tx_errors += 1;
406 }
407 self.tx.desc_mut(idx).clear();
409 }
410 idx = (idx + 1) % NUM_TX;
411 }
412 self.tx_since_last_reclaim = 0;
413 }
414
415 pub fn is_transmit_complete(&self) -> bool {
417 let idx = self.tx.tail();
418 self.tx.is_done(idx)
419 }
420
421 pub fn wait_for_transmit(&self) {
423 while !self.is_transmit_complete() {
424 core::hint::spin_loop();
425 }
426 }
427
428 pub fn tx_is_done(&self, idx: usize) -> bool {
429 self.tx.is_done(idx % NUM_TX)
430 }
431
432 pub fn handle_interrupt(&mut self) -> u32 {
435 let icr = unsafe { rd(self.mmio, regs::ICR) };
436
437 if (icr & int_bits::LSC) != 0 {
438 let status = unsafe { rd(self.mmio, regs::STATUS) };
439 let was_up = self.link_up;
440 self.link_up = (status & 0x02) != 0;
441 if was_up != self.link_up {
442 log::info!("e1000: link {}", if self.link_up { "up" } else { "down" });
443 }
444 }
445
446 if (icr & (int_bits::RXT0 | int_bits::RXDMT0 | int_bits::RXO)) != 0 {
447 log::trace!("e1000: RX interrupt (icr={:#x})", icr);
448 }
449
450 if (icr & int_bits::TXDW) != 0 {
451 self.reclaim_completed_tx_buffers();
452 log::trace!("e1000: TX descriptor writeback");
453 }
454
455 if (icr & int_bits::TXQE) != 0 {
456 log::trace!("e1000: TX queue empty");
457 }
458
459 if (icr & int_bits::RXO) != 0 {
460 log::warn!("e1000: RX overflow : descriptor ring full");
461 }
462
463 icr
464 }
465
466 pub fn watchdog_tick(&mut self, alloc: &dyn DmaAllocator) -> bool {
478 if self.tx_since_last_reclaim < WATCHDOG_TX_THRESHOLD {
479 return false;
480 }
481
482 let tdh = unsafe { rd(self.mmio, regs::TDH) } as usize;
483 let tail = self.tx.tail();
484
485 if tdh != tail {
486 if tdh == self.last_tdh {
488 log::warn!(
489 "e1000: watchdog : TX stalled (TDH={} TDT={} last_tdh={}), resetting",
490 tdh,
491 tail,
492 self.last_tdh
493 );
494 self.reinit_hardware(alloc);
495 self.stats.watchdog_resets += 1;
496 return true;
497 }
498 self.last_tdh = tdh;
500 self.tx_since_last_reclaim = 0;
501 } else {
502 self.tx_since_last_reclaim = 0;
504 }
505 false
506 }
507
508 fn reinit_hardware(&mut self, _alloc: &dyn DmaAllocator) {
511 unsafe {
512 wr(self.mmio, regs::IMC, 0xFFFF_FFFF);
514 let _ = rd(self.mmio, regs::ICR);
515
516 let c = rd(self.mmio, regs::CTRL);
518 wr(self.mmio, regs::CTRL, c | ctrl::RST);
519 for _ in 0..RESET_MAX_POLLS {
520 if rd(self.mmio, regs::CTRL) & ctrl::RST == 0 {
521 break;
522 }
523 core::hint::spin_loop();
524 }
525
526 wr(self.mmio, regs::RDBAL, self.rx_ring_phys as u32);
528 wr(self.mmio, regs::RDBAH, (self.rx_ring_phys >> 32) as u32);
529 wr(
530 self.mmio,
531 regs::RDLEN,
532 (NUM_RX * core::mem::size_of::<LegacyRxDesc>()) as u32,
533 );
534 wr(self.mmio, regs::RDH, 0);
535 wr(self.mmio, regs::RDT, (NUM_RX - 1) as u32);
536
537 wr(self.mmio, regs::TDBAL, self.tx_ring_phys as u32);
538 wr(self.mmio, regs::TDBAH, (self.tx_ring_phys >> 32) as u32);
539 wr(
540 self.mmio,
541 regs::TDLEN,
542 (NUM_TX * core::mem::size_of::<LegacyTxDesc>()) as u32,
543 );
544 wr(self.mmio, regs::TDH, 0);
545 wr(self.mmio, regs::TDT, 0);
546
547 wr(
549 self.mmio,
550 regs::IMS,
551 int_bits::RXT0
552 | int_bits::LSC
553 | int_bits::RXDMT0
554 | int_bits::RXO
555 | int_bits::TXDW
556 | int_bits::TXQE,
557 );
558
559 let c = rd(self.mmio, regs::CTRL);
561 wr(self.mmio, regs::CTRL, c | ctrl::SLU);
562
563 wr(
565 self.mmio,
566 regs::RCTL,
567 rctl::EN | rctl::BAM | rctl::BSIZE_4096 | rctl::SECRC,
568 );
569
570 wr(
572 self.mmio,
573 regs::TCTL,
574 tctl::EN | tctl::PSP | (0x10 << tctl::CT_SHIFT) | (0x40 << tctl::COLD_SHIFT),
575 );
576
577 wr(self.mmio, regs::ITR, 488);
579 wr(self.mmio, regs::RDTR, 0);
580 wr(self.mmio, regs::RADV, 128);
581 wr(self.mmio, regs::TIDV, 0);
582 wr(self.mmio, regs::TADV, 64);
583 }
584
585 self.tx_since_last_reclaim = 0;
586 self.last_tdh = 0;
587 self.check_link();
588 }
589
590 unsafe fn read_mac(base: u64) -> Result<[u8; 6], NetError> {
591 let ral = rd(base, regs::RAL0);
592 let rah = rd(base, regs::RAH0);
593 log::trace!("e1000: RAL0={:#x} RAH0={:#x}", ral, rah);
594
595 if ral != 0 || rah != 0 {
596 return Ok([
597 (ral) as u8,
598 (ral >> 8) as u8,
599 (ral >> 16) as u8,
600 (ral >> 24) as u8,
601 (rah) as u8,
602 (rah >> 8) as u8,
603 ]);
604 }
605
606 log::trace!("e1000: RAL/RAH empty : reading MAC words from EEPROM");
607 let mut mac = [0u8; 6];
608 for i in 0u32..3 {
609 let w = Self::eeprom_read(base, i as u8)?;
610 mac[(i * 2) as usize] = w as u8;
611 mac[(i * 2 + 1) as usize] = (w >> 8) as u8;
612 }
613 if mac == [0; 6] || mac == [0xFF; 6] {
614 return Err(NetError::NotReady);
615 }
616 Ok(mac)
617 }
618
619 unsafe fn eeprom_read(base: u64, addr: u8) -> Result<u16, NetError> {
620 log::trace!("e1000: EEPROM read addr={}", addr);
621 wr(
622 base,
623 regs::EERD,
624 eerd::START | ((addr as u32) << eerd::ADDR_SHIFT),
625 );
626 for poll in 0..EEPROM_MAX_POLLS {
627 let v = rd(base, regs::EERD);
628 if v & eerd::DONE != 0 {
629 let data = ((v >> eerd::DATA_SHIFT) & 0xFFFF) as u16;
630 log::trace!(
631 "e1000: EEPROM addr={} ok after {} polls data={:#x}",
632 addr,
633 poll + 1,
634 data
635 );
636 return Ok(data);
637 }
638 core::hint::spin_loop();
639 }
640 log::warn!(
641 "e1000: EEPROM addr={} timeout after {} EERD polls",
642 addr,
643 EEPROM_MAX_POLLS
644 );
645 Err(NetError::NotReady)
646 }
647}