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 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-09-24 15:30:38 +02:00
commit 0331d548c4
6 changed files with 530 additions and 8 deletions

View file

@ -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/gpu/virtio_gpu/virtio_gpu.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/net/e1000/e1000.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/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/audio/ac97.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o

View file

@ -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/` ## Realtek RTL8169/8168/8111 — `drivers/net/r8169/`
The NIC in a very large number of laptops and desktop boards. Every 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: [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. 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 ### It has not run on hardware
**Stated plainly because it cannot be tested here.** QEMU emulates `rtl8139` **Stated plainly because it cannot be tested here.** QEMU emulates `rtl8139`

View file

@ -42,6 +42,10 @@ struct e1000 {
uint8 mac[6]; uint8 mac[6];
int ready; 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; uint64 rx_packets, tx_packets, rx_dropped;
// Which PCI device this instance drives. Kept so that attached() can match // 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 static int
eeprom_read(struct e1000 *e, int addr, uint16 *out) 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++) { for (int i = 0; i < 100000; i++) {
uint32 v = rd(e, E1000_EERD); uint32 v = rd(e, E1000_EERD);
if (v & E1000_EERD_DONE) { if (v & done) {
*out = (uint16)(v >> E1000_EERD_DATA_SHIFT); *out = (uint16)(v >> E1000_EERD_DATA_SHIFT);
return 0; return 0;
} }
@ -287,6 +295,20 @@ e1000_probe(struct pci_dev *d)
return -1; return -1;
e->pdev = d; 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"); initlock(&e->lock, "e1000");
pci_enable(d, 0x0006); // memory space + bus master pci_enable(d, 0x0006); // memory space + bus master
// ioremap rather than P2V: firmware may place a 64-bit BAR above the 4 GB the // 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_COPPER), M(E1000_DEV_82546GB_FIBER),
M(E1000_DEV_82546GB_SERDES), M(E1000_DEV_82546GB_SERDES),
M(E1000_DEV_82547EI), 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 }, { 0, 0, 0, 0 },
}; };
#undef M #undef M

View file

@ -26,11 +26,23 @@
// the sort of second-hand hardware a system like this actually gets run on. // 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. // These are the cards in every desktop and server of that generation.
// //
// Deliberately *not* included: the PCIe 8257x parts (82571-82574) and anything // The PCIe 8257x parts used to be excluded here, on the argument that they
// newer. They present a similar register map but differ in NVM access and // "differ in NVM access and initialisation, and claiming a device this driver
// initialisation, and claiming a device this driver has not been shown to // has not been shown to initialise is worse than not claiming it". That
// initialise is worse than not claiming it -- the card binds, reports a MAC of // argument was right and the second half of it is now answerable: QEMU
// nonsense, and nothing works for reasons that look nothing like the cause. // 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 0x100E
#define E1000_DEV_82540EM_LOM 0x1015 #define E1000_DEV_82540EM_LOM 0x1015
#define E1000_DEV_82540EP_LOM 0x1016 #define E1000_DEV_82540EP_LOM 0x1016
@ -57,6 +69,17 @@
#define E1000_DEV_82546GB_COPPER 0x1079 #define E1000_DEV_82546GB_COPPER 0x1079
#define E1000_DEV_82546GB_FIBER 0x107A #define E1000_DEV_82546GB_FIBER 0x107A
#define E1000_DEV_82546GB_SERDES 0x107B #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 #define E1000_DEV_82547EI 0x1019
// --- registers, byte offsets into BAR 0 ------------------------------------ // --- registers, byte offsets into BAR 0 ------------------------------------
@ -96,12 +119,21 @@
// STATUS // STATUS
#define E1000_STATUS_LU (1 << 1) // link up #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_START (1 << 0)
#define E1000_EERD_DONE (1 << 4) #define E1000_EERD_DONE (1 << 4)
#define E1000_EERD_ADDR_SHIFT 8 #define E1000_EERD_ADDR_SHIFT 8
#define E1000_EERD_DATA_SHIFT 16 #define E1000_EERD_DATA_SHIFT 16
#define E1000_EERD_DONE_8257X (1 << 1)
#define E1000_EERD_ADDR_SHIFT_8257X 2
// RCTL // RCTL
#define E1000_RCTL_EN (1 << 1) // receiver enable #define E1000_RCTL_EN (1 << 1) // receiver enable
#define E1000_RCTL_SBP (1 << 2) // store bad packets #define E1000_RCTL_SBP (1 << 2) // store bad packets

View file

@ -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,
};

View file

@ -34,6 +34,7 @@ extern const struct pci_driver virtio_gpu_driver;
extern const struct pci_driver e1000_driver; extern const struct pci_driver e1000_driver;
extern const struct pci_driver ac97_driver; extern const struct pci_driver ac97_driver;
extern const struct pci_driver r8169_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 // 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 // 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, &vga_pci_driver,
&e1000_driver, &e1000_driver,
&r8169_driver, &r8169_driver,
&rtl8139_driver,
&xhci_driver, &xhci_driver,
&ac97_driver, &ac97_driver,
}; };