strat9_kernel/hardware/nic/
e1000_drv.rs1use 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 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 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 fn name(&self) -> &str {
61 "e1000"
62 }
63 fn mac_address(&self) -> [u8; 6] {
65 self.mac
66 }
67 fn link_up(&self) -> bool {
69 self.inner.lock().link_up()
70 }
71
72 fn receive(&self, buf: &mut [u8]) -> Result<usize, NetError> {
74 self.inner.lock().receive(buf)
75 }
76
77 fn transmit(&self, buf: &[u8]) -> Result<(), NetError> {
79 self.inner.lock().transmit(buf, &KernelDma)
80 }
81
82 fn handle_interrupt(&self) {
84 self.inner.lock().handle_interrupt();
85 }
86}
87
88pub 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 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 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 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}