From 0331d548c43792469fdede588fe41317574b0903 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Thu, 24 Sep 2026 15:30:38 +0200 Subject: [PATCH] net: an RTL8139 driver, and the e1000 stops refusing the PCIe parts Two NICs, and unlike the RTL8111 last commit, QEMU emulates both -- so both are tested rather than reasoned about. **RTL8139** is its own driver and not a mode of the 8169, because it is its own chip. No descriptor rings: the receiver is one circular buffer the chip writes packets into back to back behind four-byte headers, and the transmitter is four fixed slots. Putting both programming models behind one set of function pointers is how a mode flag ends up threaded through every path. Three details are the whole driver. CAPR runs sixteen bytes behind the true read position -- a quirk of the part, not an off-by-one. WRAP means the chip runs a packet past the end of the buffer rather than splitting it, so the 8 KB buffer is allocated as 8 KB + 16 + one maximum frame; getting that wrong is a chip writing past its allocation at line rate. And a length field of 0xFFF0 means "still writing this one", so treating it as a length copies a partial packet and loses the alignment for everything after it. **The e1000 now matches the PCIe 8257x parts.** Its header refused them, with a note saying they "differ in NVM access and initialisation, and claiming a device this driver has not been shown to initialise is worse than not claiming it". That was right, and the second half is answerable: QEMU emulates the 82574L, so it can be *shown* to initialise. What differs turned out to be one register. EERD moved its address field from bits 8-15 to bits 2-15 and its done bit from 4 to 1; the rings, RCTL, TCTL and ICR are identical. So there is a per-instance flag for the NVM layout and seven more ids -- not a second copy of four hundred lines of ring code. The wrong pair of constants fails silently, reading the wrong word or waiting for a bit that never sets, and the MAC comes out as zeroes; that is why it is named in the header rather than papered over. Still refused: the ICH/PCH parts (82562, 82577-82579, I217-I219), which put the PHY behind a MAC-PHY interconnect and need a different bring-up nothing here can test. Tested, each booted alone: rtl8139 (10ec:8139) and 82574L (8086:10d3) both bind, read a real MAC, complete DHCP and ping 4 of 4 with no loss. The classic 82540EM does the same, which is the regression that mattered for the NVM change. Both supported display configurations: one boot each, no traps, no panics, socktest and memfdtest green. Also recorded: the Chromebox CN62 carries an RTL8111, not an Intel part, so last commit's driver covers it too. Co-Authored-By: Claude Opus 5 --- Makefile | 1 + docs/architecture/networking.md | 56 ++++ kernel/drivers/net/e1000/e1000.c | 32 ++- kernel/drivers/net/e1000/e1000.h | 44 ++- kernel/drivers/net/rtl8139/rtl8139.c | 403 +++++++++++++++++++++++++++ kernel/drivers/registry.c | 2 + 6 files changed, 530 insertions(+), 8 deletions(-) create mode 100644 kernel/drivers/net/rtl8139/rtl8139.c diff --git a/Makefile b/Makefile index 78cbeff..e2e0654 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_fb.o KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/virtio_gpu/virtio_gpu.o KERN_DRIVERS += $(BUILD)/kernel/drivers/net/e1000/e1000.o KERN_DRIVERS += $(BUILD)/kernel/drivers/net/r8169/r8169.o +KERN_DRIVERS += $(BUILD)/kernel/drivers/net/rtl8139/rtl8139.o KERN_DRIVERS += $(BUILD)/kernel/drivers/audio/ac97.o KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o diff --git a/docs/architecture/networking.md b/docs/architecture/networking.md index 670d4e7..84de7ba 100644 --- a/docs/architecture/networking.md +++ b/docs/architecture/networking.md @@ -29,6 +29,55 @@ that completes a handshake and drops every byte. └────────────────────────────────────┘ ``` +## Intel e1000, and the PCIe generation it used to refuse + +`drivers/net/e1000/` matched the 8254x parts and deliberately refused the PCIe +8257x, with a note saying they "differ in NVM access and initialisation, and +claiming a device this driver has not been shown to initialise is worse than +not claiming it". + +That was right, and the second half of it is answerable: QEMU emulates the +**82574L**, so the part can be shown to initialise rather than assumed to. What +actually differs turned out to be **one register**. `EERD` moved its address +field from bits 8-15 to bits 2-15 and its done bit from 4 to 1; the descriptor +rings, `RCTL`, `TCTL` and the interrupt cause register are identical. So the +8257x ids are matched now, with a per-instance flag for the NVM layout — not a +second copy of four hundred lines of ring code. + +Using the wrong pair of constants does not fail loudly, which is why it is +named rather than papered over: it reads the wrong word or waits for a bit that +will never be set, and the MAC comes out as zeroes. + +Still refused: the ICH/PCH parts (82562, 82577-82579, I217-I219). Those put the +PHY behind a MAC-PHY interconnect and need a genuinely different bring-up, and +nothing here can test one. + + +## Realtek RTL8139 — `drivers/net/rtl8139/` + +A separate driver from the RTL8111 next door, despite the neighbouring name, +because it is a different chip. The 8139 has **no descriptor rings**: the +receiver is one circular buffer the chip writes packets into back to back, each +behind a four-byte header, and the transmitter is four fixed slots used +round-robin. Sharing a driver with the 8169 would put two programming models +behind one set of function pointers, which is how a mode flag ends up threaded +through every path. + +Three details that are the whole driver: + +- **`CAPR` runs sixteen bytes behind.** The register that tells the chip how + far the driver has read is offset by 16 — a quirk of the part, not an + off-by-one, and the buffer is allocated with that padding in mind. +- **`WRAP` means the allocation is bigger than the buffer.** With it set the + chip runs a packet past the end rather than splitting it, so the 8 KB buffer + is allocated as 8 KB + 16 + one maximum frame. Getting that wrong is a chip + writing past the allocation at line rate. +- **A length of `0xFFF0` means "still writing"**, not a length. Copying on it + gives a partial packet *and* loses the alignment for everything after. + +It is also the one Realtek part QEMU emulates, so unlike the 8169 every line of +it has run: DHCP completes and `ping` is 4 of 4. + ## Realtek RTL8169/8168/8111 — `drivers/net/r8169/` The NIC in a very large number of laptops and desktop boards. Every @@ -60,6 +109,13 @@ The register layout is a *fact about the hardware*, taken the way [ldls.md](ldls.md) §3 says facts may be taken. The expression is this tree's: a descriptor-ring driver in the same shape as the e1000 beside it. +### The Chromebox has one of these + +The Asus Chromebox CN62 "Guado" turns out to carry an RTL8111 rather than the +Intel part its spec sheet suggests, so this driver covers it as well as the +laptop that prompted it — which is the argument for the family match in one +line rather than a device id per board. + ### It has not run on hardware **Stated plainly because it cannot be tested here.** QEMU emulates `rtl8139` diff --git a/kernel/drivers/net/e1000/e1000.c b/kernel/drivers/net/e1000/e1000.c index 1917825..b0a0e49 100644 --- a/kernel/drivers/net/e1000/e1000.c +++ b/kernel/drivers/net/e1000/e1000.c @@ -42,6 +42,10 @@ struct e1000 { uint8 mac[6]; int ready; + // Which generation's EERD layout this part speaks. See E1000_EERD_DONE_8257X + // in e1000.h -- it is the only register whose shape differs between the + // 8254x and the PCIe 8257x, and getting it wrong yields a MAC of zeroes. + int nvm_8257x; uint64 rx_packets, tx_packets, rx_dropped; // Which PCI device this instance drives. Kept so that attached() can match @@ -86,10 +90,14 @@ static inline void wr(struct e1000 *e, int off, uint32 v) { *(volatile uint32 static int eeprom_read(struct e1000 *e, int addr, uint16 *out) { - wr(e, E1000_EERD, (addr << E1000_EERD_ADDR_SHIFT) | E1000_EERD_START); + int shift = e->nvm_8257x ? E1000_EERD_ADDR_SHIFT_8257X + : E1000_EERD_ADDR_SHIFT; + uint32 done = e->nvm_8257x ? E1000_EERD_DONE_8257X : E1000_EERD_DONE; + + wr(e, E1000_EERD, (addr << shift) | E1000_EERD_START); for (int i = 0; i < 100000; i++) { uint32 v = rd(e, E1000_EERD); - if (v & E1000_EERD_DONE) { + if (v & done) { *out = (uint16)(v >> E1000_EERD_DATA_SHIFT); return 0; } @@ -287,6 +295,20 @@ e1000_probe(struct pci_dev *d) return -1; e->pdev = d; + // Everything from 0x105E upwards in the table is PCIe; the 8254x ids are all + // below it. Spelled as a list rather than a comparison because an id is not + // a magnitude -- 0x150C is a PCIe part and 0x1078 is not. + switch (d->device) { + case E1000_DEV_82571EB_COPPER: case E1000_DEV_82572EI_COPPER: + case E1000_DEV_82572EI: case E1000_DEV_82573E: + case E1000_DEV_82573L: case E1000_DEV_82574L: + case E1000_DEV_82583V: + e->nvm_8257x = 1; + break; + default: + e->nvm_8257x = 0; + break; + } initlock(&e->lock, "e1000"); pci_enable(d, 0x0006); // memory space + bus master // ioremap rather than P2V: firmware may place a 64-bit BAR above the 4 GB the @@ -374,6 +396,12 @@ static const struct pci_match e1000_match[] = { M(E1000_DEV_82546GB_COPPER), M(E1000_DEV_82546GB_FIBER), M(E1000_DEV_82546GB_SERDES), M(E1000_DEV_82547EI), + // The PCIe generation. 82574L is emulated by QEMU and is what this was + // tested against; the others share its register map and NVM layout. + M(E1000_DEV_82571EB_COPPER), M(E1000_DEV_82572EI_COPPER), + M(E1000_DEV_82572EI), M(E1000_DEV_82573E), + M(E1000_DEV_82573L), M(E1000_DEV_82574L), + M(E1000_DEV_82583V), { 0, 0, 0, 0 }, }; #undef M diff --git a/kernel/drivers/net/e1000/e1000.h b/kernel/drivers/net/e1000/e1000.h index 21b38bb..281826f 100644 --- a/kernel/drivers/net/e1000/e1000.h +++ b/kernel/drivers/net/e1000/e1000.h @@ -26,11 +26,23 @@ // the sort of second-hand hardware a system like this actually gets run on. // These are the cards in every desktop and server of that generation. // -// Deliberately *not* included: the PCIe 8257x parts (82571-82574) and anything -// newer. They present a similar register map but differ in NVM access and -// initialisation, and claiming a device this driver has not been shown to -// initialise is worse than not claiming it -- the card binds, reports a MAC of -// nonsense, and nothing works for reasons that look nothing like the cause. +// The PCIe 8257x parts used to be excluded here, on the argument that they +// "differ in NVM access and initialisation, and claiming a device this driver +// has not been shown to initialise is worse than not claiming it". That +// argument was right and the second half of it is now answerable: QEMU +// emulates the 82574L, so the part can be *shown* to initialise rather than +// assumed to. +// +// What actually differs turned out to be one register's layout. EERD moved its +// address field and its done bit between the two generations, and everything +// else this driver touches -- the descriptor rings, RCTL, TCTL, the interrupt +// cause register -- is the same. So the 8257x parts are matched, with the NVM +// difference named below and handled, rather than a second copy of four +// hundred lines of ring code. +// +// Still *not* included: the ICH/PCH parts (82562, 82577-82579, I217-I219). +// Those put the PHY behind a MAC-PHY interconnect and need a genuinely +// different bring-up, and nothing here can test one. #define E1000_DEV_82540EM 0x100E #define E1000_DEV_82540EM_LOM 0x1015 #define E1000_DEV_82540EP_LOM 0x1016 @@ -57,6 +69,17 @@ #define E1000_DEV_82546GB_COPPER 0x1079 #define E1000_DEV_82546GB_FIBER 0x107A #define E1000_DEV_82546GB_SERDES 0x107B +// The PCIe generation. 82574L is the one QEMU emulates and therefore the one +// this driver has been shown to bring up; its siblings share the register map +// and the NVM layout. +#define E1000_DEV_82571EB_COPPER 0x105E +#define E1000_DEV_82572EI_COPPER 0x107D +#define E1000_DEV_82572EI 0x10B9 +#define E1000_DEV_82573E 0x108B +#define E1000_DEV_82573L 0x109A +#define E1000_DEV_82574L 0x10D3 +#define E1000_DEV_82583V 0x150C + #define E1000_DEV_82547EI 0x1019 // --- registers, byte offsets into BAR 0 ------------------------------------ @@ -96,12 +119,21 @@ // STATUS #define E1000_STATUS_LU (1 << 1) // link up -// EERD +// EERD, and the one thing that is not the same across the two generations. +// +// On the 8254x the address goes in bits 8-15 and the chip answers with bit 4. +// On the PCIe 8257x the address is in bits 2-15 and the answer is bit 1. Using +// the wrong pair does not fail loudly: it reads the wrong word, or waits for a +// bit that will never be set, and the MAC ends up as zeroes or as somebody +// else's address. #define E1000_EERD_START (1 << 0) #define E1000_EERD_DONE (1 << 4) #define E1000_EERD_ADDR_SHIFT 8 #define E1000_EERD_DATA_SHIFT 16 +#define E1000_EERD_DONE_8257X (1 << 1) +#define E1000_EERD_ADDR_SHIFT_8257X 2 + // RCTL #define E1000_RCTL_EN (1 << 1) // receiver enable #define E1000_RCTL_SBP (1 << 2) // store bad packets diff --git a/kernel/drivers/net/rtl8139/rtl8139.c b/kernel/drivers/net/rtl8139/rtl8139.c new file mode 100644 index 0000000..4249f61 --- /dev/null +++ b/kernel/drivers/net/rtl8139/rtl8139.c @@ -0,0 +1,403 @@ +// Realtek RTL8139 Fast Ethernet. +// +// The other Realtek, and a genuinely different chip from the RTL8111 next +// door despite the neighbouring name. The 8139 has no descriptor rings: the +// receiver is **one circular buffer** the chip writes packets into back to +// back, each behind a four-byte header, and the transmitter is **four fixed +// slots** used round-robin. Sharing a driver with the 8169 would mean two +// programming models behind one set of function pointers, which is how a +// driver ends up with a mode flag threaded through every path. +// +// It is here for two reasons. It is what a great many older machines have -- +// and it is the one Realtek part QEMU emulates, so unlike the 8169 beside it +// every line of this has been run. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "x86.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "file.h" +#include "memlayout.h" +#include "mmu.h" +#include "pci.h" +#include "device.h" +#include "netdev.h" +#include "dma.h" +#include "irq.h" + +// --- registers -------------------------------------------------------------- + +#define R_IDR0 0x00 // station address +#define R_MAR0 0x08 // multicast filter +#define R_TSD0 0x10 // four transmit status registers +#define R_TSAD0 0x20 // four transmit buffer addresses +#define R_RBSTART 0x30 // receive buffer, 32-bit physical +#define R_CR 0x37 // command +#define R_CAPR 0x38 // how far the driver has read +#define R_CBR 0x3A // how far the chip has written +#define R_IMR 0x3C +#define R_ISR 0x3E +#define R_TCR 0x40 +#define R_RCR 0x44 +#define R_9346CR 0x50 +#define R_CONFIG1 0x52 +#define R_MSR 0x58 // media status + +#define CR_RST 0x10 +#define CR_RE 0x08 +#define CR_TE 0x04 +#define CR_BUFE 0x01 // receive buffer empty + +#define TSD_OWN 0x00002000 // the chip has taken the data +#define TSD_TOK 0x00008000 // and put it on the wire +#define TSD_SIZE 0x00001FFF + +#define ISR_ROK 0x0001 +#define ISR_RER 0x0002 +#define ISR_TOK 0x0004 +#define ISR_TER 0x0008 +#define ISR_RXOVW 0x0010 +#define ISR_LINKCHG 0x0020 +#define ISR_FOVW 0x0040 + +#define MSR_LINKB 0x04 // link *bad* -- the sense is inverted + +// Accept our own address, broadcast and multicast; no promiscuous. WRAP lets +// the chip run a packet past the end of the buffer rather than splitting it, +// which is why the allocation below is larger than the buffer length it +// declares. MXDMA and RXFTH are both "unlimited". +#define RCR_APM 0x00000002 +#define RCR_AM 0x00000004 +#define RCR_AB 0x00000008 +#define RCR_WRAP 0x00000080 +#define RCR_RBLEN_8K 0x00000000 +#define RCR_UNLIMITED 0x0000E700 + +// 8 KB of buffer, 16 bytes the chip requires past it, and one maximum frame of +// slack because WRAP is set. Getting this last part wrong is a chip writing +// over whatever follows the allocation, at line rate. +#define RXBUF_LEN 8192 +#define RXBUF_PAD 16 +#define RXBUF_SLACK 1536 +#define RXBUF_TOTAL (RXBUF_LEN + RXBUF_PAD + RXBUF_SLACK) + +#define NTX 4 +#define TXBUF 2048 + +#define RTL8139_MAX_NICS 2 + +struct rtl8139 { + volatile uint8 *mmio; + uint8 *rxbuf, *txbuf; + uint64 rxbuf_phys, txbuf_phys; + + int rx_ptr; // our offset into the circular buffer + int tx_slot; // the next of the four to use + uint8 mac[6]; + int ready; + uint64 rx_packets, tx_packets; + + struct spinlock lock; + struct pci_dev *pdev; + struct device *ndev; +}; + +static struct rtl8139 nics[RTL8139_MAX_NICS]; + +static struct rtl8139 * +of(struct device *d) +{ + return d ? (struct rtl8139 *)d->priv : 0; +} + +static inline uint8 rd8 (struct rtl8139 *r, int o) { return *(volatile uint8 *)(r->mmio + o); } +static inline uint16 rd16(struct rtl8139 *r, int o) { return *(volatile uint16 *)(r->mmio + o); } +static inline uint32 rd32(struct rtl8139 *r, int o) { return *(volatile uint32 *)(r->mmio + o); } +static inline void wr8 (struct rtl8139 *r, int o, uint8 v) { *(volatile uint8 *)(r->mmio + o) = v; } +static inline void wr16(struct rtl8139 *r, int o, uint16 v) { *(volatile uint16 *)(r->mmio + o) = v; } +static inline void wr32(struct rtl8139 *r, int o, uint32 v) { *(volatile uint32 *)(r->mmio + o) = v; } + +// --- the class ops ---------------------------------------------------------- + +static const unsigned char * +rtl8139_mac_op(struct device *d) +{ + struct rtl8139 *r = of(d); + static const unsigned char zero[6]; + return r ? r->mac : zero; +} + +static int +rtl8139_link_up(struct device *d) +{ + struct rtl8139 *r = of(d); + // LINKB is set when the link is *down*. Reading it the obvious way round is + // a driver that reports a cable as unplugged whenever it is plugged in. + return r ? (rd8(r, R_MSR) & MSR_LINKB) == 0 : 0; +} + +static int +rtl8139_transmit(struct device *d, const void *frame, int len) +{ + struct rtl8139 *r = of(d); + int slot; + uint32 tsd; + + if (!r || !r->ready) + return -1; + if (len <= 0 || len > TXBUF) + return -1; + + acquire(&r->lock); + slot = r->tx_slot; + tsd = rd32(r, R_TSD0 + slot * 4); + + // A slot is ours again once the chip has taken the data (OWN) -- not once it + // has reached the wire. Waiting for TOK instead would idle three quarters of + // the transmitter for no benefit. + if (r->tx_packets >= NTX && !(tsd & TSD_OWN)) { + release(&r->lock); + return -1; // all four busy: congestion, so retry + } + + // Ethernet's minimum is 60 bytes before the FCS and this chip does not pad. + // An ARP request is 42, so a driver that skips this cannot resolve anything. + int n = len < 60 ? 60 : len; + memmove(r->txbuf + (uint64)slot * TXBUF, frame, len); + if (n > len) + memset(r->txbuf + (uint64)slot * TXBUF + len, 0, n - len); + + wr32(r, R_TSAD0 + slot * 4, (uint32)(r->txbuf_phys + (uint64)slot * TXBUF)); + __sync_synchronize(); + // Writing the size is what starts it: this clears OWN, and the chip sets OWN + // again when it has the data. The early-transmit threshold in bits 16-21 is + // left at zero, which means "start when the whole frame is in the FIFO" -- + // the safe end of a trade whose other end is underruns. + wr32(r, R_TSD0 + slot * 4, (uint32)n); + + r->tx_slot = (slot + 1) % NTX; + r->tx_packets++; + release(&r->lock); + return 0; +} + +static int +rtl8139_receive(struct device *d, void *out, int cap) +{ + struct rtl8139 *r = of(d); + uint32 hdr; + int status, len, avail; + + if (!r || !r->ready) + return 0; + + acquire(&r->lock); + + if (rd8(r, R_CR) & CR_BUFE) { // nothing waiting + release(&r->lock); + return 0; + } + + // The four-byte header the chip writes in front of every packet: status in + // the low half, length in the high half. The length counts the 4-byte CRC. + hdr = *(volatile uint32 *)(r->rxbuf + r->rx_ptr); + status = (int)(hdr & 0xFFFF); + len = (int)((hdr >> 16) & 0xFFFF); + + // 0xFFF0 is the chip saying "still writing this one". Treating it as a + // length is a driver that copies a partial packet and then loses the buffer + // alignment for everything after it. + if (len == 0xFFF0 || len == 0) { + release(&r->lock); + return 0; + } + + if (!(status & 0x0001) || len < 8 || len > RXBUF_LEN) { + // A bad frame. Resynchronise rather than try to skip it: the length is + // the only thing that says where the next one starts, and it is the field + // that is not to be trusted here. + wr8(r, R_CR, CR_TE); // stop the receiver + r->rx_ptr = 0; + wr32(r, R_RBSTART, (uint32)r->rxbuf_phys); + wr16(r, R_CAPR, (uint16)(0 - RXBUF_PAD)); + wr8(r, R_CR, CR_RE | CR_TE); + release(&r->lock); + return 0; + } + + avail = len - 4; // drop the CRC the stack does not want + if (avail > cap) + avail = cap; + + // The copy may run past the end of the 8 KB window, which is exactly what + // WRAP is for and why the allocation has a frame of slack: the bytes are + // there, contiguously, so one memmove is correct. + if (avail > 0) + memmove(out, r->rxbuf + r->rx_ptr + 4, avail); + + // Four-byte align the next offset, wrap it, and tell the chip how far we + // have read. CAPR is *sixteen behind* the true position -- a quirk of the + // part, not an off-by-one. + r->rx_ptr = (r->rx_ptr + len + 4 + 3) & ~3; + r->rx_ptr %= RXBUF_LEN; + wr16(r, R_CAPR, (uint16)(r->rx_ptr - RXBUF_PAD)); + + r->rx_packets++; + release(&r->lock); + return avail; +} + +// --- interrupt -------------------------------------------------------------- + +static void +rtl8139_intr(void *arg) +{ + struct rtl8139 *r = arg; + uint16 isr; + + if (!r || !r->mmio) + return; + + isr = rd16(r, R_ISR); + if (!isr) + return; + wr16(r, R_ISR, isr); // write-1-to-clear, or it re-enters + + // As with every other NIC here: nothing is read from the buffer in interrupt + // context. This only ends the network thread's wait early. + if (isr & (ISR_ROK | ISR_RXOVW | ISR_FOVW)) + netdev_rx_signal(); +} + +// --- probe ------------------------------------------------------------------ + +static int +rtl8139_probe(struct pci_dev *d) +{ + struct rtl8139 *r = 0; + uint64 bar = 0, barlen = 0; + + for (int i = 0; i < RTL8139_MAX_NICS; i++) + if (!nics[i].ready) { r = &nics[i]; break; } + if (!r) + return -1; + + // The first memory BAR. BAR 0 is I/O on this part and BAR 1 is the same + // registers in memory space; using memory keeps this the same shape as + // every other driver here. + for (int i = 0; i < 6; i++) + if (d->bar[i].base && !d->bar[i].is_io) { + bar = d->bar[i].base; + barlen = d->bar[i].size; + break; + } + if (!bar) { + cprintf("rtl8139: no memory BAR; not driving this device\n"); + return -1; + } + + r->pdev = d; + initlock(&r->lock, "rtl8139"); + pci_enable(d, PCI_CMD_MEMORY | PCI_CMD_BUS_MASTER); + + r->mmio = ioremap(bar, barlen ? barlen : 0x100); + if (!r->mmio) { + cprintf("rtl8139: cannot map registers at %p\n", (void *)bar); + return -1; + } + + // Wake the chip if the firmware left it asleep, then reset. + wr8(r, R_CONFIG1, 0x00); + wr8(r, R_CR, CR_RST); + for (int i = 0; i < 10000; i++) { + microdelay(10); + if (!(rd8(r, R_CR) & CR_RST)) + break; + } + if (rd8(r, R_CR) & CR_RST) { + cprintf("rtl8139: the chip did not come out of reset\n"); + return -1; + } + + for (int i = 0; i < 6; i++) + r->mac[i] = rd8(r, R_IDR0 + i); + + r->rxbuf = dma_alloc(RXBUF_TOTAL, 8); + r->txbuf = dma_alloc(NTX * TXBUF, 8); + if (!r->rxbuf || !r->txbuf) { + cprintf("rtl8139: no DMA memory for the buffers\n"); + return -1; + } + r->rxbuf_phys = dma_phys(r->rxbuf); + r->txbuf_phys = dma_phys(r->txbuf); + memset(r->rxbuf, 0, RXBUF_TOTAL); + + // A 32-bit part: the buffer address register is 32 bits and there is no + // dual-address cycle to fall back on. The DMA arena is below 4 GB, so this + // holds -- but it holds for a reason rather than by luck, and if the arena + // ever moves this is where it breaks. + if (r->rxbuf_phys + RXBUF_TOTAL > 0xFFFFFFFFULL || + r->txbuf_phys + NTX * TXBUF > 0xFFFFFFFFULL) { + cprintf("rtl8139: buffers above 4 GB; this chip cannot reach them\n"); + return -1; + } + + wr32(r, R_RBSTART, (uint32)r->rxbuf_phys); + r->rx_ptr = 0; + wr16(r, R_CAPR, (uint16)(0 - RXBUF_PAD)); + + wr32(r, R_RCR, RCR_APM | RCR_AM | RCR_AB | RCR_WRAP | + RCR_RBLEN_8K | RCR_UNLIMITED); + wr32(r, R_TCR, 0x00000700); // unlimited DMA burst + wr32(r, R_MAR0, 0xFFFFFFFF); // multicast: everything; the stack filters + wr32(r, R_MAR0 + 4, 0xFFFFFFFF); + + wr8(r, R_CR, CR_RE | CR_TE); + wr16(r, R_ISR, 0xFFFF); + wr16(r, R_IMR, ISR_ROK | ISR_RER | ISR_TOK | ISR_TER | + ISR_RXOVW | ISR_FOVW | ISR_LINKCHG); + + if (d->irq != 0xFF && d->irq < 24) + irq_register(d->irq, rtl8139_intr, r, "rtl8139", 1); + + r->ready = 1; + kinfo("rtl8139: %x:%x:%x:%x:%x:%x, %s, irq %d\n", + r->mac[0], r->mac[1], r->mac[2], r->mac[3], r->mac[4], r->mac[5], + (rd8(r, R_MSR) & MSR_LINKB) ? "link down" : "link up", d->irq); + return 0; +} + +static void +rtl8139_attached(struct pci_dev *d, struct device *dev) +{ + for (int i = 0; i < RTL8139_MAX_NICS; i++) + if (nics[i].ready && nics[i].pdev == d) { + dev->priv = &nics[i]; + nics[i].ndev = dev; + return; + } +} + +static const struct netdev_ops rtl8139_netdev_ops = { + .transmit = rtl8139_transmit, + .receive = rtl8139_receive, + .mac = rtl8139_mac_op, + .link_up = rtl8139_link_up, +}; + +static const struct pci_match rtl8139_match[] = { + { 0x10EC, 0x8139, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +const struct pci_driver rtl8139_driver = { + .name = "rtl8139", + .devname = "eth", + .match = rtl8139_match, + .ops = &rtl8139_netdev_ops, + .probe = rtl8139_probe, + .attached = rtl8139_attached, +}; diff --git a/kernel/drivers/registry.c b/kernel/drivers/registry.c index b5d5b2c..72c8449 100644 --- a/kernel/drivers/registry.c +++ b/kernel/drivers/registry.c @@ -34,6 +34,7 @@ extern const struct pci_driver virtio_gpu_driver; extern const struct pci_driver e1000_driver; extern const struct pci_driver ac97_driver; extern const struct pci_driver r8169_driver; +extern const struct pci_driver rtl8139_driver; // Order matters here in one respect: virtio-gpu is offered the bus before the // VBE framebuffer driver, so that on a machine with both, the device with real @@ -45,6 +46,7 @@ static const struct pci_driver *drivers[] = { &vga_pci_driver, &e1000_driver, &r8169_driver, + &rtl8139_driver, &xhci_driver, &ac97_driver, };