Skip to main content

strat9_kernel/hardware/nic/
e1000_drv.rs

1//! Kernel adapter for the `e1000` crate.
2//!
3//! Implements `DmaAllocator` via the buddy allocator and wraps
4//! `e1000::E1000Nic` behind a `SpinLock` to satisfy `NetworkDevice`.
5
6use super::register_device;
7use crate::{
8    hardware::pci_client::{self as pci, Bar},
9    memory::{self},
10    serial_println,
11    sync::SpinLock,
12};
13use alloc::sync::Arc;
14use e1000::E1000Nic;
15use net_core::{NetError, NetworkDevice};
16use nic_buffers::{DmaAllocator, DmaRegion};
17use x86_64::VirtAddr;
18
19const LEGACY_E1000_IDS: &[u16] = &[pci::intel_eth::E1000_82540EM, pci::intel_eth::E1000_82545EM];
20
21struct KernelDma;
22
23impl DmaAllocator for KernelDma {
24    /// Allocates dma.
25    fn alloc_dma(&self, size: usize) -> Result<DmaRegion, nic_buffers::DmaAllocError> {
26        let pages = (size + 4095) / 4096;
27        let order = pages.next_power_of_two().trailing_zeros() as u8;
28        let frame = crate::sync::with_irqs_disabled(|token| {
29            crate::memory::allocate_phys_contiguous(token, order)
30        })
31        .map_err(|_| nic_buffers::DmaAllocError)?;
32        let phys = frame.start_address.as_u64();
33        let virt = memory::phys_to_virt(phys) as *mut u8;
34        Ok(DmaRegion {
35            phys,
36            virt,
37            size: pages * 4096,
38        })
39    }
40
41    /// Releases dma.
42    unsafe fn free_dma(&self, region: DmaRegion) {
43        let pages = (region.size + 4095) / 4096;
44        let order = pages.next_power_of_two().trailing_zeros() as u8;
45        let frame =
46            crate::memory::PhysFrame::containing_address(x86_64::PhysAddr::new(region.phys));
47        crate::sync::with_irqs_disabled(|token| {
48            crate::memory::free_phys_contiguous(token, frame, order);
49        });
50    }
51}
52
53pub struct KernelE1000 {
54    inner: SpinLock<E1000Nic>,
55    mac: [u8; 6],
56}
57
58impl NetworkDevice for KernelE1000 {
59    /// Performs the name operation.
60    fn name(&self) -> &str {
61        "e1000"
62    }
63    /// Performs the mac address operation.
64    fn mac_address(&self) -> [u8; 6] {
65        self.mac
66    }
67    /// Performs the link up operation.
68    fn link_up(&self) -> bool {
69        self.inner.lock().link_up()
70    }
71
72    /// Performs the receive operation.
73    fn receive(&self, buf: &mut [u8]) -> Result<usize, NetError> {
74        self.inner.lock().receive(buf)
75    }
76
77    /// Performs the transmit operation.
78    fn transmit(&self, buf: &[u8]) -> Result<(), NetError> {
79        self.inner.lock().transmit(buf, &KernelDma)
80    }
81
82    /// Handles interrupt.
83    fn handle_interrupt(&self) {
84        self.inner.lock().handle_interrupt();
85    }
86}
87
88/// Performs the init operation.
89pub fn init() {
90    serial_println!("[E1000] init: probing for Intel NICs...");
91    if !memory::paging::is_initialized() {
92        serial_println!("[E1000] paging not initialized, deferring probe");
93        return;
94    }
95
96    let candidates = pci::probe_all(pci::ProbeCriteria {
97        vendor_id: Some(pci::vendor::INTEL),
98        device_id: None,
99        class_code: Some(pci::class::NETWORK),
100        subclass: None,
101        prog_if: None,
102    });
103    serial_println!("[E1000] PCI probe returned {} candidates", candidates.len());
104    let mut found_intel_nic = false;
105    let mut warned_modern_intel = false;
106    for pci_dev in candidates.into_iter() {
107        serial_println!(
108            "[E1000] Checking device {:04x}:{:04x} class={:02x} subclass={:02x}",
109            pci_dev.vendor_id,
110            pci_dev.device_id,
111            pci_dev.class_code,
112            pci_dev.subclass
113        );
114        // Accept standard Ethernet class and vendor-specific network subclass.
115        if pci_dev.subclass != pci::net_subclass::ETHERNET
116            && pci_dev.subclass != pci::net_subclass::OTHER
117        {
118            continue;
119        }
120        found_intel_nic = true;
121        if !LEGACY_E1000_IDS.contains(&pci_dev.device_id) {
122            if matches!(
123                pci_dev.device_id,
124                pci::intel_eth::I219_LM
125                    | pci::intel_eth::I219_V
126                    | pci::intel_eth::I225_LM
127                    | pci::intel_eth::I225_V
128                    | pci::intel_eth::I226_LM
129                    | pci::intel_eth::I226_V
130            ) {
131                if !warned_modern_intel {
132                    log::warn!(
133                        "E1000: modern Intel NIC detected; add e1000e/igc for full laptop support"
134                    );
135                    warned_modern_intel = true;
136                }
137            }
138            continue;
139        }
140
141        log::info!(
142            "E1000: PCI {:04x}:{:04x} at {:?}",
143            pci_dev.vendor_id,
144            pci_dev.device_id,
145            pci_dev.address
146        );
147        pci_dev.enable_bus_master();
148        pci_dev.enable_memory_space();
149        // Firmware may leave PCI interrupt disabled; clear bit 10.
150        let mut cmd = pci_dev.read_config_u16(pci::config::COMMAND);
151        cmd &= !pci::command::INTERRUPT_DISABLE;
152        pci_dev.write_config_u16(pci::config::COMMAND, cmd);
153
154        let mmio_phys = match pci_dev.read_bar(0).or_else(|| pci_dev.read_bar(1)) {
155            Some(Bar::Memory32 { addr, .. }) => addr as u64,
156            Some(Bar::Memory64 { addr, .. }) => addr,
157            _ => {
158                log::error!("E1000: no MMIO BAR (BAR0/BAR1)");
159                continue;
160            }
161        };
162
163        memory::paging::ensure_identity_map_range(mmio_phys, 0x2_0000);
164        let mmio_virt = memory::phys_to_virt(mmio_phys);
165        let mmio_page_phys = mmio_phys & !0xFFF;
166        let mmio_page_virt = mmio_virt & !0xFFF;
167        let mapped = memory::paging::translate(VirtAddr::new(mmio_page_virt))
168            .map(|p| p.as_u64())
169            .unwrap_or(0);
170        if mapped != mmio_page_phys {
171            log::error!(
172                "E1000: MMIO not mapped after ensure_identity_map_range phys={:#x} virt={:#x} mapped={:#x}; skipping device",
173                mmio_phys,
174                mmio_virt,
175                mapped
176            );
177            continue;
178        }
179
180        // Some firmware leaves NIC in a stale power/reset state; retry once.
181        let mut init_ok = None;
182        for attempt in 0..2 {
183            log::info!(
184                "E1000: init attempt {} mmio_phys={:#x} mmio_virt={:#x}",
185                attempt + 1,
186                mmio_phys,
187                mmio_virt
188            );
189            log::debug!("E1000: entering e1000::E1000Nic::init (reset, MAC, rings)…");
190            match E1000Nic::init(mmio_virt, &KernelDma) {
191                Ok(nic) => {
192                    log::info!(
193                        "E1000: core init ok on attempt {} (see trace=e1000::* for reset/EEPROM detail)",
194                        attempt + 1
195                    );
196                    init_ok = Some(nic);
197                    break;
198                }
199                Err(e) => {
200                    log::warn!("E1000: core init attempt {} failed: {}", attempt + 1, e);
201                    if attempt == 0 {
202                        let mut cmd_retry = pci_dev.read_config_u16(pci::config::COMMAND);
203                        cmd_retry |= pci::command::BUS_MASTER | pci::command::MEMORY_SPACE;
204                        cmd_retry &= !pci::command::INTERRUPT_DISABLE;
205                        pci_dev.write_config_u16(pci::config::COMMAND, cmd_retry);
206                        continue;
207                    }
208                    log::error!("E1000: init failed: {}", e);
209                }
210            }
211        }
212
213        if let Some(nic) = init_ok {
214            let mac = nic.mac_address();
215            serial_println!(
216                "[E1000] Device initialized: MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
217                mac[0],
218                mac[1],
219                mac[2],
220                mac[3],
221                mac[4],
222                mac[5]
223            );
224            let dev = Arc::new(KernelE1000 {
225                mac,
226                inner: SpinLock::new(nic),
227            });
228            register_device(dev);
229            return;
230        }
231    }
232    if found_intel_nic {
233        serial_println!("[E1000] Intel NIC(s) found but no supported e1000 device initialized");
234    }
235    serial_println!("[E1000] no compatible device found");
236}