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 <noreply@anthropic.com>
403 lines
12 KiB
C
403 lines
12 KiB
C
// 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,
|
|
};
|