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