Skip to content

Commit 060d038

Browse files
author
T. Andrew Davis
committed
Enhance Intel e1000e driver with EEPROM MAC reading and link status checks
1 parent 09faf15 commit 060d038

5 files changed

Lines changed: 235 additions & 27 deletions

File tree

network/asm/drivers/intel/init.s

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,103 @@ asm_intel_read_mac:
309309
jmp .exit
310310

311311
.try_eeprom:
312-
; TODO: Read from EEPROM if RAL/RAH invalid
313-
; For now, return error
314-
mov eax, 1
312+
; Read MAC from EEPROM for I218/82579
313+
; EEPROM words 0, 1, 2 contain MAC address
314+
; Word 0 = bytes 0-1, Word 1 = bytes 2-3, Word 2 = bytes 4-5
315+
316+
; Read EEPROM word 0 (MAC bytes 0-1)
317+
mov rcx, r12
318+
xor edx, edx ; Address 0
319+
call .read_eeprom_word
320+
cmp eax, 0xFFFFFFFF
321+
je .invalid ; EEPROM read failed
322+
mov [r13], al ; Byte 0
323+
shr eax, 8
324+
mov [r13+1], al ; Byte 1
325+
326+
; Read EEPROM word 1 (MAC bytes 2-3)
327+
mov rcx, r12
328+
mov edx, 1 ; Address 1
329+
call .read_eeprom_word
330+
cmp eax, 0xFFFFFFFF
331+
je .invalid
332+
mov [r13+2], al ; Byte 2
333+
shr eax, 8
334+
mov [r13+3], al ; Byte 3
335+
336+
; Read EEPROM word 2 (MAC bytes 4-5)
337+
mov rcx, r12
338+
mov edx, 2 ; Address 2
339+
call .read_eeprom_word
340+
cmp eax, 0xFFFFFFFF
341+
je .invalid
342+
mov [r13+4], al ; Byte 4
343+
shr eax, 8
344+
mov [r13+5], al ; Byte 5
345+
346+
; Validate the MAC we read
347+
mov eax, [r13]
348+
movzx ecx, word [r13+4]
349+
or eax, ecx
350+
jz .invalid ; All zeros
351+
352+
xor eax, eax ; Success
315353
jmp .exit
316354

355+
; Helper: Read one EEPROM word
356+
; Input: RCX = mmio_base, EDX = address (0-2 for MAC)
357+
; Output: EAX = word value, or 0xFFFFFFFF on error
358+
.read_eeprom_word:
359+
push rbx
360+
push r14
361+
push r15
362+
sub rsp, 32
363+
364+
mov r14, rcx ; mmio_base
365+
mov r15d, edx ; address
366+
367+
; Write EERD: address in bits [15:8], start bit [0]
368+
; For 82579/I218: address is bits [15:2], start is bit 0
369+
shl r15d, 2 ; address << 2
370+
or r15d, 1 ; Set START bit
371+
mov edx, r15d
372+
mov rcx, r14
373+
add rcx, EERD
374+
call asm_mmio_write32
375+
376+
; Poll for DONE bit (bit 1) - timeout after ~10ms
377+
mov ebx, 10000 ; iteration count
378+
.eerd_poll:
379+
mov rcx, r14
380+
add rcx, EERD
381+
call asm_mmio_read32
382+
test eax, 2 ; DONE bit
383+
jnz .eerd_done
384+
385+
; Small delay
386+
pause
387+
pause
388+
pause
389+
390+
dec ebx
391+
jnz .eerd_poll
392+
393+
; Timeout
394+
mov eax, 0xFFFFFFFF
395+
jmp .eerd_exit
396+
397+
.eerd_done:
398+
; Data is in bits [31:16]
399+
shr eax, 16
400+
and eax, 0xFFFF
401+
402+
.eerd_exit:
403+
add rsp, 32
404+
pop r15
405+
pop r14
406+
pop rbx
407+
ret
408+
317409
.invalid:
318410
mov eax, 1
319411

network/src/driver/intel/e1000e.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use crate::driver::traits::{DriverInit, NetworkDriver, RxError, TxError};
99
use crate::mainloop::bare_metal::serial_println;
1010
use crate::types::MacAddress;
11+
use crate::asm::drivers::intel::{asm_intel_link_status, LinkStatusResult};
1112

1213
use super::init::{init_e1000e, E1000eConfig, E1000eInitError};
1314
use super::phy::PhyManager;
@@ -183,9 +184,13 @@ impl NetworkDriver for E1000eDriver {
183184

184185
/// Get link status.
185186
fn link_up(&self) -> bool {
186-
// Use cached status to avoid MMIO on every call
187-
// The phy.link_status() call updates the cache
188-
self.phy.cached_link_status().link_up
187+
// Directly read hardware STATUS register via ASM
188+
// We need actual hardware state, not cached values
189+
let mut result = LinkStatusResult::default();
190+
unsafe {
191+
asm_intel_link_status(self.mmio_base, &mut result);
192+
}
193+
result.link_up != 0
189194
}
190195
}
191196

network/src/driver/intel/init.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,26 @@ pub unsafe fn init_e1000e(
211211
// ═══════════════════════════════════════════════════════════════════
212212
let mut mac: MacAddress = [0u8; 6];
213213
let mac_result = asm_intel_read_mac(mmio_base, &mut mac);
214+
215+
// Debug: print raw MAC bytes
216+
serial_print(" [e1000e] MAC read result=");
217+
serial_print_decimal(mac_result);
218+
serial_print(" bytes=[");
219+
serial_print_decimal(mac[0] as u32);
220+
serial_print(",");
221+
serial_print_decimal(mac[1] as u32);
222+
serial_print(",");
223+
serial_print_decimal(mac[2] as u32);
224+
serial_print(",");
225+
serial_print_decimal(mac[3] as u32);
226+
serial_print(",");
227+
serial_print_decimal(mac[4] as u32);
228+
serial_print(",");
229+
serial_print_decimal(mac[5] as u32);
230+
serial_println("]");
231+
214232
if mac_result != 0 {
215-
serial_println(" [e1000e] FAIL: Invalid MAC");
233+
serial_println(" [e1000e] FAIL: Invalid MAC (ASM returned error)");
216234
return Err(E1000eInitError::InvalidMac);
217235
}
218236

@@ -303,9 +321,28 @@ pub unsafe fn init_e1000e(
303321
asm_intel_enable_tx(mmio_base);
304322

305323
// ═══════════════════════════════════════════════════════════════════
306-
// STEP 14: SET LINK UP
324+
// STEP 14: SET LINK UP AND RESTART AUTO-NEGOTIATION
325+
//
326+
// On I218, after all the init steps, we need to:
327+
// 1. Set CTRL.SLU (Set Link Up)
328+
// 2. Restart PHY auto-negotiation
329+
// 3. Give time for link partner negotiation
307330
// ═══════════════════════════════════════════════════════════════════
308331
asm_intel_set_link_up(mmio_base);
332+
333+
// Restart auto-negotiation after setting SLU
334+
serial_println(" [e1000e] Restarting auto-negotiation...");
335+
if let Some(bmcr) = phy_read(mmio_base, regs::PHY_BMCR, config.tsc_freq) {
336+
let new_bmcr = bmcr | regs::BMCR_ANENABLE | regs::BMCR_ANRESTART;
337+
let _ = phy_write(mmio_base, regs::PHY_BMCR, new_bmcr, config.tsc_freq);
338+
}
339+
340+
// Give PHY time to start negotiation (100ms)
341+
let delay_start = crate::asm::core::tsc::read_tsc();
342+
let delay_ticks = config.tsc_freq / 10; // 100ms
343+
while crate::asm::core::tsc::read_tsc().wrapping_sub(delay_start) < delay_ticks {
344+
core::hint::spin_loop();
345+
}
309346

310347
serial_println(" [e1000e] Init complete!");
311348

network/src/mainloop/bare_metal.rs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,30 @@ unsafe fn serial_write_byte(byte: u8) {
8989
#[cfg(not(target_arch = "x86_64"))]
9090
unsafe fn serial_write_byte(_byte: u8) {}
9191

92+
// Static counters for packet debugging
93+
static mut TX_PACKET_COUNT: u32 = 0;
94+
static mut RX_PACKET_COUNT: u32 = 0;
95+
96+
/// Increment TX counter
97+
pub fn inc_tx_count() {
98+
unsafe { TX_PACKET_COUNT += 1; }
99+
}
100+
101+
/// Increment RX counter
102+
pub fn inc_rx_count() {
103+
unsafe { RX_PACKET_COUNT += 1; }
104+
}
105+
106+
/// Get TX count
107+
pub fn get_tx_count() -> u32 {
108+
unsafe { TX_PACKET_COUNT }
109+
}
110+
111+
/// Get RX count
112+
pub fn get_rx_count() -> u32 {
113+
unsafe { RX_PACKET_COUNT }
114+
}
115+
92116
/// Write string to serial port.
93117
pub fn serial_print(s: &str) {
94118
for byte in s.bytes() {
@@ -1109,6 +1133,10 @@ pub struct SmoltcpAdapter<'a, D: NetworkDriver> {
11091133
rx_buffer: [u8; 2048],
11101134
/// Length of data in rx_buffer (0 if no pending packet)
11111135
rx_len: usize,
1136+
/// TX packet count (for debug)
1137+
tx_count: u32,
1138+
/// RX packet count (for debug)
1139+
rx_count: u32,
11121140
}
11131141

11141142
impl<'a, D: NetworkDriver> SmoltcpAdapter<'a, D> {
@@ -1117,6 +1145,8 @@ impl<'a, D: NetworkDriver> SmoltcpAdapter<'a, D> {
11171145
driver,
11181146
rx_buffer: [0u8; 2048],
11191147
rx_len: 0,
1148+
tx_count: 0,
1149+
rx_count: 0,
11201150
}
11211151
}
11221152

@@ -1128,11 +1158,23 @@ impl<'a, D: NetworkDriver> SmoltcpAdapter<'a, D> {
11281158
match self.driver.receive(&mut self.rx_buffer) {
11291159
Ok(Some(len)) => {
11301160
self.rx_len = len;
1161+
self.rx_count += 1;
1162+
inc_rx_count(); // Global counter
11311163
}
11321164
_ => {}
11331165
}
11341166
}
11351167
}
1168+
1169+
/// Get TX count for debug.
1170+
pub fn tx_count(&self) -> u32 {
1171+
self.tx_count
1172+
}
1173+
1174+
/// Get RX count for debug.
1175+
pub fn rx_count(&self) -> u32 {
1176+
self.rx_count
1177+
}
11361178

11371179
/// Refill RX queue. Called in main loop Phase 1.
11381180
pub fn refill_rx(&mut self) {
@@ -1186,7 +1228,9 @@ impl<'a, D: NetworkDriver> smoltcp::phy::TxToken for TxToken<'a, D> {
11861228
let result = f(&mut buffer[..actual_len]);
11871229

11881230
// Fire-and-forget transmit - don't wait for completion
1189-
let _ = self.driver.transmit(&buffer[..actual_len]);
1231+
if self.driver.transmit(&buffer[..actual_len]).is_ok() {
1232+
inc_tx_count();
1233+
}
11901234

11911235
result
11921236
}
@@ -1732,16 +1776,27 @@ pub unsafe fn bare_metal_main(handoff: &'static BootHandoff, config: BareMetalCo
17321776
serial_println("[NET] Waiting for PHY link...");
17331777

17341778
let link_start = get_tsc();
1735-
let link_timeout_ticks = handoff.tsc_freq * 10; // 10 second timeout for link
1779+
let link_timeout_ticks = handoff.tsc_freq * 15; // 15 second timeout for link (auto-neg can be slow)
1780+
let mut last_dot_tsc = link_start;
1781+
let dot_interval = handoff.tsc_freq; // 1 second
17361782

17371783
loop {
17381784
if driver.link_up() {
1785+
serial_println("");
17391786
serial_println("[OK] PHY link established");
17401787
break;
17411788
}
17421789

17431790
let now_tsc = get_tsc();
1791+
1792+
// Print a dot every second to show progress
1793+
if now_tsc.wrapping_sub(last_dot_tsc) > dot_interval {
1794+
serial_print(".");
1795+
last_dot_tsc = now_tsc;
1796+
}
1797+
17441798
if now_tsc.wrapping_sub(link_start) > link_timeout_ticks {
1799+
serial_println("");
17451800
serial_println("[WARN] PHY link timeout - continuing anyway...");
17461801
break;
17471802
}
@@ -1817,14 +1872,33 @@ pub unsafe fn bare_metal_main(handoff: &'static BootHandoff, config: BareMetalCo
18171872
// DHCP polling loop
18181873
serial_println("[NET] Sending DHCP DISCOVER...");
18191874

1875+
let mut last_status_tsc = dhcp_start;
1876+
let status_interval = handoff.tsc_freq * 2; // Print status every 2 seconds
1877+
18201878
loop {
18211879
let now_tsc = get_tsc();
18221880

18231881
// Check timeout
18241882
if now_tsc.wrapping_sub(dhcp_start) > dhcp_timeout_ticks {
1825-
serial_println("[FAIL] DHCP timeout");
1883+
serial_println("");
1884+
serial_print("[FAIL] DHCP timeout - TX:");
1885+
serial_print_decimal(get_tx_count());
1886+
serial_print(" RX:");
1887+
serial_print_decimal(get_rx_count());
1888+
serial_println("");
18261889
return RunResult::DhcpTimeout;
18271890
}
1891+
1892+
// Print status every 2 seconds
1893+
if now_tsc.wrapping_sub(last_status_tsc) > status_interval {
1894+
serial_print(".");
1895+
serial_print(" TX:");
1896+
serial_print_decimal(get_tx_count());
1897+
serial_print(" RX:");
1898+
serial_print_decimal(get_rx_count());
1899+
serial_println("");
1900+
last_status_tsc = now_tsc;
1901+
}
18281902

18291903
// Convert TSC to smoltcp Instant
18301904
let timestamp = tsc_to_instant(now_tsc, handoff.tsc_freq);

0 commit comments

Comments
 (0)