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 let rx_ring_region = alloc
144 .alloc_dma(NUM_RX * core::mem::size_of::<LegacyRxDesc>())
145 .map_err(|_| NetError::NotReady)?;
146 ptr::write_bytes(rx_ring_region.virt, 0, rx_ring_region.size);
147 let rx_descs = rx_ring_region.virt as *mut LegacyRxDesc;
148
149 let mut rx_bufs = [DmaRegion::ZERO; NUM_RX];
150 for rx_buf in rx_bufs.iter_mut().take(NUM_RX) {
151 let buf = alloc
152 .alloc_dma(RX_BUF_SIZE)
153 .map_err(|_| NetError::NotReady)?;
154 ptr::write_bytes(buf.virt, 0, RX_BUF_SIZE);
155 *rx_buf = buf;
156 }
157
158 wr(mmio_base, regs::RDBAL, rx_ring_region.phys as u32);
159 wr(mmio_base, regs::RDBAH, (rx_ring_region.phys >> 32) as u32);
160 wr(mmio_base, regs::RDLEN, rx_ring_region.size as u32);
161 wr(mmio_base, regs::RDH, 0);
162 wr(mmio_base, regs::RDT, (NUM_RX - 1) as u32);
163
164 for (i, buf) in rx_bufs.iter().enumerate().take(NUM_RX) {
165 (*rx_descs.add(i)).addr = buf.phys;
166 }
167
168 let tx_ring_region = alloc
170 .alloc_dma(NUM_TX * core::mem::size_of::<LegacyTxDesc>())
171 .map_err(|_| NetError::NotReady)?;
172 ptr::write_bytes(tx_ring_region.virt, 0, tx_ring_region.size);
173 let tx_descs = tx_ring_region.virt as *mut LegacyTxDesc;
174
175 wr(mmio_base, regs::TDBAL, tx_ring_region.phys as u32);
176 wr(mmio_base, regs::TDBAH, (tx_ring_region.phys >> 32) as u32);
177 wr(mmio_base, regs::TDLEN, tx_ring_region.size as u32);
178 wr(mmio_base, regs::TDH, 0);
179 wr(mmio_base, regs::TDT, 0);
180
181 let mut tx_bufs = [DmaRegion::ZERO; NUM_TX];
183 for tx_buf in tx_bufs.iter_mut().take(NUM_TX) {
184 let buf = alloc
185 .alloc_dma(net_core::MTU)
186 .map_err(|_| NetError::NotReady)?;
187 ptr::write_bytes(buf.virt, 0, net_core::MTU);
188 *tx_buf = buf;
189 }
190
191 wr(
193 mmio_base,
194 regs::TCTL,
195 tctl::EN | tctl::PSP | (0x10 << tctl::CT_SHIFT) | (0x40 << tctl::COLD_SHIFT),
196 );
197
198 wr(
201 mmio_base,
202 regs::RCTL,
203 rctl::EN | rctl::BAM | rctl::BSIZE_4096 | rctl::SECRC,
204 );
205
206 let c = rd(mmio_base, regs::CTRL);
208 wr(mmio_base, regs::CTRL, c | ctrl::SLU);
209
210 wr(mmio_base, regs::ITR, 488);
225 wr(mmio_base, regs::RDTR, 0);
226 wr(mmio_base, regs::RADV, 128);
227 wr(mmio_base, regs::TIDV, 0);
228 wr(mmio_base, regs::TADV, 64);
229
230 wr(
231 mmio_base,
232 regs::IMS,
233 int_bits::RXT0
234 | int_bits::LSC
235 | int_bits::RXDMT0
236 | int_bits::RXO
237 | int_bits::TXDW
238 | int_bits::TXQE,
239 );
240 let status = rd(mmio_base, regs::STATUS);
241 let link_up = (status & 0x02) != 0;
242
243 Ok(Self {
244 mmio: mmio_base,
245 rx: RxRing::new(rx_descs, NUM_RX),
246 rx_bufs,
247 rx_ring_phys: rx_ring_region.phys,
248 tx: TxRing::new(tx_descs, NUM_TX),
249 tx_bufs,
250 tx_ring_phys: tx_ring_region.phys,
251 mac,
252 link_up,
253 stats: E1000Stats::new(),
254 tx_since_last_reclaim: 0,
255 last_tdh: 0,
256 })
257 }
258 }
259
260 pub fn mac_address(&self) -> [u8; 6] {
261 self.mac
262 }
263
264 pub fn link_up(&self) -> bool {
265 self.link_up
266 }
267
268 pub fn check_link(&mut self) -> bool {
269 unsafe {
270 let status = rd(self.mmio, regs::STATUS);
271 self.link_up = (status & 0x02) != 0;
272 }
273 self.link_up
274 }
275
276 pub fn stats(&self) -> &E1000Stats {
277 &self.stats
278 }
279
280 fn recycle_rx_desc(&mut self, idx: usize) {
282 self.rx.desc_mut(idx).clear_status();
283 self.rx
284 .desc_mut(idx)
285 .set_buffer_addr(self.rx_bufs[idx].phys);
286 let new_tail = self.rx.advance();
287 unsafe {
288 wr(self.mmio, regs::RDT, new_tail as u32);
289 }
290 }
291
292 pub fn receive(&mut self, buf: &mut [u8]) -> Result<usize, NetError> {
293 if !self.check_link() {
294 return Err(NetError::LinkDown);
295 }
296
297 let (idx, pkt_len) = self.rx.poll().ok_or(NetError::NoPacket)?;
298
299 let err = self.rx.desc(idx).errors;
301 let st = self.rx.desc(idx).status;
302
303 if (err & rx_errors::CE) != 0 {
304 log::warn!("e1000: RX CRC error on descriptor {}", idx);
305 self.recycle_rx_desc(idx);
306 self.stats.rx_crc_errors += 1;
307 self.stats.rx_errors += 1;
308 return Err(NetError::NoPacket);
309 }
310 if (err & rx_errors::SE) != 0 {
311 log::warn!("e1000: RX symbol error on descriptor {}", idx);
312 self.recycle_rx_desc(idx);
313 self.stats.rx_errors += 1;
314 return Err(NetError::NoPacket);
315 }
316 if (err & rx_errors::TCPE) != 0 {
317 log::trace!("e1000: RX TCP/UDP checksum error on descriptor {}", idx);
318 }
319 if (err & rx_errors::IPE) != 0 {
320 log::trace!("e1000: RX IP checksum error on descriptor {}", idx);
321 }
322 if (st & rx_status::EOP) == 0 {
323 log::warn!(
324 "e1000: RX descriptor {} missing EOP : fragment dropped",
325 idx
326 );
327 self.recycle_rx_desc(idx);
328 self.stats.rx_length_errors += 1;
329 self.stats.rx_errors += 1;
330 return Err(NetError::NoPacket);
331 }
332
333 let len = (pkt_len as usize).min(RX_BUF_SIZE);
338 if buf.len() < len {
339 self.recycle_rx_desc(idx);
340 return Err(NetError::BufferTooSmall);
341 }
342
343 unsafe {
344 ptr::copy_nonoverlapping(self.rx_bufs[idx].virt, buf.as_mut_ptr(), len);
345 }
346
347 self.recycle_rx_desc(idx);
348 self.stats.rx_ok += 1;
349 Ok(len)
350 }
351
352 pub fn transmit(&mut self, buf: &[u8]) -> Result<(), NetError> {
353 if !self.check_link() {
354 return Err(NetError::LinkDown);
355 }
356
357 if buf.len() > net_core::MTU {
358 return Err(NetError::BufferTooSmall);
359 }
360
361 let idx = self.tx.tail();
362
363 self.reclaim_completed_tx_buffers();
365
366 if self.tx.desc(idx).cmd != 0 && !self.tx.is_done(idx) {
368 self.stats.tx_dropped += 1;
369 return Err(NetError::TxQueueFull);
370 }
371
372 unsafe {
374 ptr::copy_nonoverlapping(buf.as_ptr(), self.tx_bufs[idx].virt, buf.len());
375 }
376
377 let _submitted = self.tx.submit(self.tx_bufs[idx].phys, buf.len() as u16);
378 unsafe {
379 wr(self.mmio, regs::TDT, self.tx.tail() as u32);
380 }
381
382 self.tx_since_last_reclaim += 1;
383 self.stats.tx_ok += 1;
384 Ok(())
385 }
386
387 fn reclaim_completed_tx_buffers(&mut self) {
391 let head = unsafe { rd(self.mmio, regs::TDH) } as usize % NUM_TX;
393 let tail = self.tx.tail();
394
395 let mut idx = head;
396 while idx != tail {
397 if self.tx.is_done(idx) {
398 let st = self.tx.desc(idx).status;
399 if (st & tx_status::LC) != 0 {
400 log::warn!("e1000: TX late collision at descriptor {}", idx);
401 self.stats.tx_late_collision += 1;
402 self.stats.tx_errors += 1;
403 }
404 if (st & tx_status::TU) != 0 {
405 log::warn!("e1000: TX underrun at descriptor {}", idx);
406 self.stats.tx_underrun += 1;
407 self.stats.tx_errors += 1;
408 }
409 self.tx.desc_mut(idx).clear();
411 }
412 idx = (idx + 1) % NUM_TX;
413 }
414 self.tx_since_last_reclaim = 0;
415 }
416
417 pub fn is_transmit_complete(&self) -> bool {
419 let idx = self.tx.tail();
420 self.tx.is_done(idx)
421 }
422
423 pub fn wait_for_transmit(&self) {
425 while !self.is_transmit_complete() {
426 core::hint::spin_loop();
427 }
428 }
429
430 pub fn tx_is_done(&self, idx: usize) -> bool {
431 self.tx.is_done(idx % NUM_TX)
432 }
433
434 pub fn handle_interrupt(&mut self) -> u32 {
437 let icr = unsafe { rd(self.mmio, regs::ICR) };
438
439 if (icr & int_bits::LSC) != 0 {
440 let status = unsafe { rd(self.mmio, regs::STATUS) };
441 let was_up = self.link_up;
442 self.link_up = (status & 0x02) != 0;
443 if was_up != self.link_up {
444 log::info!("e1000: link {}", if self.link_up { "up" } else { "down" });
445 }
446 }
447
448 if (icr & (int_bits::RXT0 | int_bits::RXDMT0 | int_bits::RXO)) != 0 {
449 log::trace!("e1000: RX interrupt (icr={:#x})", icr);
450 }
451
452 if (icr & int_bits::TXDW) != 0 {
453 self.reclaim_completed_tx_buffers();
454 log::trace!("e1000: TX descriptor writeback");
455 }
456
457 if (icr & int_bits::TXQE) != 0 {
458 log::trace!("e1000: TX queue empty");
459 }
460
461 if (icr & int_bits::RXO) != 0 {
462 log::warn!("e1000: RX overflow : descriptor ring full");
463 }
464
465 icr
466 }
467
468 pub fn watchdog_tick(&mut self, alloc: &dyn DmaAllocator) -> bool {
480 if self.tx_since_last_reclaim < WATCHDOG_TX_THRESHOLD {
481 return false;
482 }
483
484 let tdh = unsafe { rd(self.mmio, regs::TDH) } as usize;
485 let tail = self.tx.tail();
486
487 if tdh != tail {
488 if tdh == self.last_tdh {
490 log::warn!(
491 "e1000: watchdog : TX stalled (TDH={} TDT={} last_tdh={}), resetting",
492 tdh,
493 tail,
494 self.last_tdh
495 );
496 self.reinit_hardware(alloc);
497 self.stats.watchdog_resets += 1;
498 return true;
499 }
500 self.last_tdh = tdh;
502 self.tx_since_last_reclaim = 0;
503 } else {
504 self.tx_since_last_reclaim = 0;
506 }
507 false
508 }
509
510 fn reinit_hardware(&mut self, _alloc: &dyn DmaAllocator) {
513 unsafe {
514 wr(self.mmio, regs::IMC, 0xFFFF_FFFF);
516 let _ = rd(self.mmio, regs::ICR);
517
518 let c = rd(self.mmio, regs::CTRL);
520 wr(self.mmio, regs::CTRL, c | ctrl::RST);
521 for _ in 0..RESET_MAX_POLLS {
522 if rd(self.mmio, regs::CTRL) & ctrl::RST == 0 {
523 break;
524 }
525 core::hint::spin_loop();
526 }
527
528 wr(self.mmio, regs::RDBAL, self.rx_ring_phys as u32);
530 wr(self.mmio, regs::RDBAH, (self.rx_ring_phys >> 32) as u32);
531 wr(
532 self.mmio,
533 regs::RDLEN,
534 (NUM_RX * core::mem::size_of::<LegacyRxDesc>()) as u32,
535 );
536 wr(self.mmio, regs::RDH, 0);
537 wr(self.mmio, regs::RDT, (NUM_RX - 1) as u32);
538
539 wr(self.mmio, regs::TDBAL, self.tx_ring_phys as u32);
540 wr(self.mmio, regs::TDBAH, (self.tx_ring_phys >> 32) as u32);
541 wr(
542 self.mmio,
543 regs::TDLEN,
544 (NUM_TX * core::mem::size_of::<LegacyTxDesc>()) as u32,
545 );
546 wr(self.mmio, regs::TDH, 0);
547 wr(self.mmio, regs::TDT, 0);
548
549 wr(
551 self.mmio,
552 regs::IMS,
553 int_bits::RXT0
554 | int_bits::LSC
555 | int_bits::RXDMT0
556 | int_bits::RXO
557 | int_bits::TXDW
558 | int_bits::TXQE,
559 );
560
561 let c = rd(self.mmio, regs::CTRL);
563 wr(self.mmio, regs::CTRL, c | ctrl::SLU);
564
565 wr(
567 self.mmio,
568 regs::RCTL,
569 rctl::EN | rctl::BAM | rctl::BSIZE_4096 | rctl::SECRC,
570 );
571
572 wr(
574 self.mmio,
575 regs::TCTL,
576 tctl::EN | tctl::PSP | (0x10 << tctl::CT_SHIFT) | (0x40 << tctl::COLD_SHIFT),
577 );
578
579 wr(self.mmio, regs::ITR, 488);
581 wr(self.mmio, regs::RDTR, 0);
582 wr(self.mmio, regs::RADV, 128);
583 wr(self.mmio, regs::TIDV, 0);
584 wr(self.mmio, regs::TADV, 64);
585 }
586
587 self.tx_since_last_reclaim = 0;
588 self.last_tdh = 0;
589 self.check_link();
590 }
591
592 unsafe fn read_mac(base: u64) -> Result<[u8; 6], NetError> {
593 let ral = rd(base, regs::RAL0);
594 let rah = rd(base, regs::RAH0);
595 log::trace!("e1000: RAL0={:#x} RAH0={:#x}", ral, rah);
596
597 if ral != 0 || rah != 0 {
598 return Ok([
599 (ral) as u8,
600 (ral >> 8) as u8,
601 (ral >> 16) as u8,
602 (ral >> 24) as u8,
603 (rah) as u8,
604 (rah >> 8) as u8,
605 ]);
606 }
607
608 log::trace!("e1000: RAL/RAH empty : reading MAC words from EEPROM");
609 let mut mac = [0u8; 6];
610 for i in 0u32..3 {
611 let w = Self::eeprom_read(base, i as u8)?;
612 mac[(i * 2) as usize] = w as u8;
613 mac[(i * 2 + 1) as usize] = (w >> 8) as u8;
614 }
615 if mac == [0; 6] || mac == [0xFF; 6] {
616 return Err(NetError::NotReady);
617 }
618 Ok(mac)
619 }
620
621 unsafe fn eeprom_read(base: u64, addr: u8) -> Result<u16, NetError> {
622 log::trace!("e1000: EEPROM read addr={}", addr);
623 wr(
624 base,
625 regs::EERD,
626 eerd::START | ((addr as u32) << eerd::ADDR_SHIFT),
627 );
628 for poll in 0..EEPROM_MAX_POLLS {
629 let v = rd(base, regs::EERD);
630 if v & eerd::DONE != 0 {
631 let data = ((v >> eerd::DATA_SHIFT) & 0xFFFF) as u16;
632 log::trace!(
633 "e1000: EEPROM addr={} ok after {} polls data={:#x}",
634 addr,
635 poll + 1,
636 data
637 );
638 return Ok(data);
639 }
640 core::hint::spin_loop();
641 }
642 log::warn!(
643 "e1000: EEPROM addr={} timeout after {} EERD polls",
644 addr,
645 EEPROM_MAX_POLLS
646 );
647 Err(NetError::NotReady)
648 }
649}