--- name: schunix-e1000-and-driver-layout description: "The e1000 NIC driver, the drivers/ reorganisation by device class, and virtio-gpu as the QEMU default." metadata: node_type: memory type: project originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95 modified: 2026-08-08T20:17:14.134Z --- 2026-08-08. Three changes, all requested by Nexoniarz in one go. ## drivers/// kernel/drivers/ registry.c the one list of what exists gpu/vga/ gpu/virtio_gpu/ net/e1000/ bus/xhci/ usb/core/ usb/hid/ usb/storage/ usb/hub/ One directory per *driver*, not per class (Nexoniarz asked for this specifically), so a driver that grows a register header, a second source file or a firmware blob has somewhere to put it. Matches `DEVCLASS_*` in `include/device.h`. Flat was eight files with four called `usb_something`, and each driver added made it worse. **Makefile note:** the compile rule is now `@mkdir -p $(dir $@)`, not a fixed path — a hardcoded mkdir only creates the top directory and every subdirectory build fails with "unable to open output file". `KERN_DRIVERS` is built with `+=` per class rather than one long `$(addprefix ...)`; scripted edits kept collapsing the line continuations. ## virtio-gpu is the QEMU default `QEMU_DISPLAY = -vga std -device virtio-gpu-pci` in the Makefile, used by all three qemu targets. **Keep `-vga std`** — the bootloader draws the splash through VBE in 16-bit real mode and there is no virtio path that early, so a machine with only virtio-gpu boots blind until the kernel is up. ## FreeBSD reference tree `Refrence/freebsd-src` (spelled that way on disk) is a full FreeBSD checkout, re-cloned 2026-08-08 after the NetBSD one was deleted for space. Never built, never in an image; recorded in `docs/layout.md` and `docs/licensing.md` §4. **It is 2.1 GB** — measured, and the same reason the NetBSD one was deleted. Only `sys/dev`, `sys/net` and `sys/netinet` have ever been read, so a sparse checkout (`--filter=blob:none --sparse`, then `sparse-checkout set sys/dev sys/net sys/netinet`) is ~100 MB and loses nothing. Single-file fetches from `raw.githubusercontent.com` answered every licence question so far and cost nothing at all. **`sys/dev/e1000/` is Intel BSD-3-clause with zero advertising clauses** — checked. Its *device ID table* was transcribed into `include/e1000.h`, taking the driver from binding one card (QEMU's 82540EM) to the whole 8254x family: 82540/41/44/45/46/47, ~27 IDs. That is the shape of adoption that works — **data, not logic**. The driver stays written from Intel manual 317453, and `NOTICE` carries the Intel attribution. The PCIe 8257x parts (82571-82574) were deliberately *excluded*: similar register map, different NVM access and init. Claiming a device the driver cannot initialise is worse than not claiming it — it binds, reports a nonsense MAC, and fails for reasons that look nothing like the cause. ## e1000 `include/e1000.h` + `kernel/drivers/net/e1000.c`, from Intel's public manual 317453. Worked on the first boot: e1000: 52:54:0:12:34:56, link up, irq 11 **Proven to carry real traffic**, 2026-08-08: `echo n > /proc/sysrq` runs `e1000_selftest()`, which hand-builds an ARP request for 10.0.2.2 and waits for the reply. Under QEMU user-mode networking: e1000: asking who has 10.0.2.2 e1000: 64 bytes from 52:55:a:0:2:2 type 806 e1000: ARP reply -- 10.0.2.2 is at 52:55:a:0:2:2 e1000: self-test passed, 1 tx 1 rx That one exchange validates the whole path: transmit descriptor format, the DD handshake, TDT/RDT bookkeeping, DMA addresses, the receive-ring handback, and **the MAC filter** -- an ARP reply is unicast to us, so a wrong RAL/RAH means it never arrives. SLIRP is silent until spoken to, which is why the test has to transmit before it can expect to receive. Do this kind of check *before* layering a protocol stack on a driver: otherwise the stack's first failure is indistinguishable from its own bugs. Run with `-netdev user,id=n0 -device e1000,netdev=n0`. Provides `e1000_transmit()`, `e1000_receive()` and the MAC — **no IP stack yet, deliberately**: a stack written before a driver has nothing to test against and every bug in it looks like a driver bug. Details that are easy to get wrong and are commented in the source: - **Set `E1000_TXD_STAT_DD` on every transmit descriptor at init.** The hardware only sets it after *using* one, so a driver that waits for a bit the device has never had reason to write waits forever. - **RDT starts at NRX-1, not NRX.** One descriptor must always be held back or "ring full" and "ring empty" are indistinguishable. - **Clear the multicast table.** It is undefined after reset, so left alone the device accepts multicast for addresses nobody asked for. - **Read the MAC from EEPROM *and* fall back to RAL/RAH.** Some cards leave the EEPROM path unimplemented and you end up with all zeroes. - **`irq_register(..., level_low = 1)`** — PCI interrupts are level-triggered, active low. Edge-configured, it fires once then wedges. - Rings and buffers come from `dma_alloc`; the device reads them by physical address and kalloc pages are contiguous only by accident. - Enable the receiver *after* the ring base is set, or it DMAs into physical 0, which on this kernel is the boot page table. `ops` is null on the pci_driver: there is no net class in the device model yet. Adding one is the natural first step when the IP stack arrives.