- Home
- Kernel Internals
- Networking
- Hardware
Hardware Layer¶
PCI Enumeration (pci.rs)¶
Before a NIC can be used, its register base must be found by scanning the PCI configuration space. The kernel uses the standard x86 mechanism: write a configuration address to port 0xCF8, then read data from 0xCFC.
address = 0x8000_0000 | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC)
find_io_base(vendor_id, device_id) iterates all 256 buses × 32 devices × 8 functions and returns the I/O BAR0 base (BAR0 bit 0 = 1 means I/O space). For RTL8139: vendor 0x10EC, device 0x8139. The RTL8139 is only initialised when PCI discovery finds it; the initial 0xC000 global value is not evidence of a card.
enable_bus_mastering sets bit 2 (Bus Master Enable) in PCI command register (offset 0x04) so the NIC can DMA without CPU involvement.
RTL8139 Driver (rtl8139.rs)¶
Initialization (rtl8139_init)¶
Called once by netdrv::register_driver when the global Ethernet driver process registers via syscall 0x37.
1. find_io_base() discover I/O base from PCI BAR0
2. enable_bus_mastering() allow NIC DMA
3. CONFIG1 ← 0x00 power on
4. CMD ← 0x10 software reset; poll until bit 4 clears
5. RBSTART ← &RX_BUFFER set RX ring base (physical == virtual, identity map)
6. CMD ← 0x0C enable RX + TX
7. RCR ← 0xF | (1<<7) | (2<<11) accept broadcast + multicast + runt frames; wrap mode, 32 KiB ring
8. IMR ← 0x0005 enable ROK (bit 0) and TOK (bit 2) interrupts
The RX buffer is a 32 KiB ring plus a 1 500-byte tail overrun guard (RX_BUFFER: [u8; 32768 + 16 + 1500]). The extra 16 bytes are the RTL8139 header prefix; the extra 1 500 are for frames that wrap across the ring boundary.
The driver does not register an IRQ handler. Instead, nic::peek_frame/consume_frame are polled every PIT tick and acknowledges ROK in the ISR register (+0x3E) manually.
RX Ring (receive_frame)¶
The RTL8139 writes incoming frames in order into RX_BUFFER starting at offset 0, wrapping around at 32 KiB. Each frame is prefixed with a 4-byte RTL8139 header:
offset +0: u16 rx_status (bit 0 = ROK)
offset +2: u16 frame_len (includes 4-byte CRC)
offset +4: frame data (frame_len bytes)
peek_frame checks CAPR+16 != CBR, reads the header at
RX_OFFSET & 0x7FFF, and returns a borrowed slice including the attached FCS.
It leaves the ring position unchanged so a receiver with no queue space can
retry. consume_frame(len) advances RX_OFFSET, aligns it to four bytes, and
writes RX_OFFSET - 16 to CAPR (+0x38). Invalid lengths below 14 or above
2048 advance past the bad header. The copying receive_frame helper leaves a
frame queued if its destination buffer is too small.
nic.rs counts RX once on consumption, subtracting the RTL8139 FCS from the
byte total. See Network Monitoring.
TX Descriptors (send_frame)¶
The RTL8139 has 4 TX descriptors (TSD0–TSD3 at +0x10–+0x1C, TxAddr0–TxAddr3 at +0x20–+0x2C). The driver cycles through them round-robin via TX_INDEX.
1. copy data into TX_BUFFERS[TX_INDEX] (zero-pad to 60 bytes minimum)
2. write physical buffer address to TxAddr[TX_INDEX] (+0x20 + idx*4)
3. write frame length to TxStatus[TX_INDEX] (+0x10 + idx*4)
→ writing TxStatus with length < 2048 starts the DMA
4. TX_INDEX = (TX_INDEX + 1) % 4
No completion poll: the driver reuses the four descriptors round robin without checking ownership. A burst of sends can therefore reuse a descriptor before transmission finishes.
MAC Address (read_mac_addr)¶
The MAC is read from IDR0–IDR5 (registers +0x00–+0x05) after rtl8139_init. It is cached in SYSTEM_CONFIG so userland can query it without re-probing.
NIC Selection and Intel Driver (nic.rs, e1000.rs)¶
netdrv::register_driver initialises the selected backend through nic::init. RTL8139 is tried first, then Intel, then Broadcom BCM57765; no initialized card means registration fails. Routing and userland use the same interface for all backends: peek_frame, consume_frame, send_frame, MAC lookup and missed-packet counters.
The Intel driver recognises these PCI devices (vendor 0x8086):
| Models | Device IDs |
|---|---|
| 82540EM, 82545EM | 0x100e, 0x100f |
| 82574L | 0x10d3 |
| 82577LM/LC, 82578DM/DC | 0x10ea, 0x10eb, 0x10ef, 0x10f0 |
| 82579LM/V | 0x1502, 0x1503 |
It uses BAR0 memory-mapped registers below 4 GiB and identity-mapped static DMA buffers. Legacy descriptor rings contain 32 RX and 64 TX descriptors, each with a 2 KiB buffer. No NIC interrupt handler is installed: timer polling handles receive delivery and link housekeeping. A full TX ring can refuse a send.
PCH controllers have additional reset, PHY, power-management and flash NVM setup. The kernel shell's nic command exposes link state, PHY and ring diagnostics, reset, test transmission, register access and PCH NVM reads. Recognition of a model does not establish that every board's firmware configuration works.
For QEMU, replace -device rtl8139,netdev=net0 with -device e1000,netdev=net0 (82540EM) or -device e1000e,netdev=net0 (82574L) to exercise the Intel backend.
Broadcom BCM57765 (bcm57765/)¶
The wired BCM57765 function has primary PCI ID 14e4:16b4, class 02:00.
Matching uses that ID rather than the subsystem ID. Wireless functions are
excluded. BAR0 must be mapped below 4 GiB; the MacBook report's 0xa0400000
meets this requirement.
The backend wakes the PCI function into D0, masks interrupts, resets the controller, waits for firmware, retrieves the station MAC and resets the copper PHY. It advertises 10/100 Mb/s and gigabit full duplex. Standard Ethernet frames use 512-entry RX producer, RX return and TX descriptor rings, with 64 RX and 32 TX buffers of 2 KiB each. DMA storage is reserved from usable RAM before the RAM disk is placed, separately from USB DMA and user memory.
Receive buffers stay owned by the CPU until consumption. Transmit buffers are reused only after hardware completion. Frame checksums and segmentation remain in userland; VLAN headers are retained and jumbo frames are disabled. Timer polling handles status updates and starts an asynchronous PHY link check once per second. Reset and MDIO waits during registration are bounded; failed initialization disables bus mastering and reports the failure stage.
Run nic after ETH starts to see firmware status, the PHY ID, negotiated link
speed, software drops and ring indices. It also refreshes
/mnt/tmp/NETINFO.TXT. nic reset, raw register/PHY access and nic tx
remain Intel-specific commands. Host register-model tests cover the Broadcom
core, but physical BCM57765 packet traffic is not yet verified.
Initialization and register definitions are adapted from FreeBSD's
bge driver
and register header.
Their BSD license notice ships at /mnt/iso/opt/licenses/bcm57765.txt.
This product includes software developed by Bill Paul.
Serial / UART (serial.rs)¶
COM1 base address: 0x3F8.
Initialization¶
+1 ← 0x00 disable interrupts
+3 ← 0x80 enable DLAB (divisor latch access)
+0 ← 0x03 divisor low byte → 38 400 baud (115 200 / 3)
+1 ← 0x00 divisor high byte
+3 ← 0x03 8N1 (8 data bits, no parity, 1 stop bit)
+2 ← 0xC7 enable FIFO, clear TX+RX FIFOs, 14-byte threshold
+4 ← 0x0B enable IRQs, assert RTS + DTR
API¶
| Function | Description |
|---|---|
init() |
Configure COM1 as above |
ready() → bool |
Check bit 0 of Line Status Register (+5); true if RX byte available |
read() → u8 |
Read one byte from data register (+0) |
write(b: u8) |
Spin on LSR bit 5 (TX FIFO empty), then write to +0 |
The UART is used as the transport for the SLIP path (see protocols.md).
Related documentation: Networking Overview, Kernel Internals, NIC Diagnostics, Build and Run.