Measured against the host fetching the same 67 MB from Alpine's CDN at 11.5 MB/s, SchOS managed about 120 KB/s. Three causes: - The receive window reopened when data entered the socket buffer, before the reader drained it; lwIP then refused segments and the sender waited on retransmission timers. It reopens on read now. - The e1000 receive ring held 16 frames, so a full window arriving back to back lost everything past the sixteenth. 256 now. - Without window scaling the window stops at 64 KB, one window per round trip. Scaling on, 256 KB window, 512 KB socket ring (a power of two, which the free-running indices need), copied in blocks. Now 5.8 MB/s for the same file, and apk update in about two seconds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
203 lines
8.6 KiB
C
203 lines
8.6 KiB
C
#pragma once
|
|
|
|
// Intel 8254x gigabit Ethernet — the register and descriptor layout.
|
|
//
|
|
// From Intel's public "PCI/PCI-X Family of Gigabit Ethernet Controllers
|
|
// Software Developer's Manual" (317453). A public document with no licence on
|
|
// what you write from it, which is why this is written rather than ported: see
|
|
// docs/licensing.md and docs/guides/adding-a-driver.md.
|
|
//
|
|
// QEMU's `-device e1000` is an 82540EM and implements enough of the manual to
|
|
// be worth targeting; it is also what the default `-net nic` gives you.
|
|
|
|
#define E1000_VENDOR 0x8086
|
|
|
|
// The 8254x family: PCI/PCI-X gigabit controllers that share the register map,
|
|
// descriptor format and EEPROM interface this driver implements.
|
|
//
|
|
// The device IDs are transcribed from FreeBSD's `sys/dev/e1000/e1000_hw.h`
|
|
// (Copyright (c) 2001-2020, Intel Corporation, BSD 3-clause -- verified to
|
|
// carry no advertising clause; see docs/licensing.md and NOTICE). They are
|
|
// data, not logic: the driver below is still written from Intel's manual
|
|
// 317453, and nothing of FreeBSD's implementation is used.
|
|
//
|
|
// This matters more than it looks. The driver bound exactly one device -- the
|
|
// 82540EM QEMU emulates -- which is fine for a virtual machine and useless 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.
|
|
//
|
|
// 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
|
|
#define E1000_DEV_82540EP 0x1017
|
|
#define E1000_DEV_82540EP_LP 0x101E
|
|
#define E1000_DEV_82541EI 0x1013
|
|
#define E1000_DEV_82541EI_MOBILE 0x1018
|
|
#define E1000_DEV_82541ER_LOM 0x1014
|
|
#define E1000_DEV_82541ER 0x1078
|
|
#define E1000_DEV_82541GI 0x1076
|
|
#define E1000_DEV_82541GI_MOBILE 0x1077
|
|
#define E1000_DEV_82541GI_LF 0x107C
|
|
#define E1000_DEV_82544EI_FIBER 0x1009
|
|
#define E1000_DEV_82544GC_COPPER 0x100C
|
|
#define E1000_DEV_82544GC_LOM 0x100D
|
|
#define E1000_DEV_82545EM_COPPER 0x100F
|
|
#define E1000_DEV_82545EM_FIBER 0x1011
|
|
#define E1000_DEV_82545GM_COPPER 0x1026
|
|
#define E1000_DEV_82545GM_FIBER 0x1027
|
|
#define E1000_DEV_82545GM_SERDES 0x1028
|
|
#define E1000_DEV_82546EB_COPPER 0x1010
|
|
#define E1000_DEV_82546EB_FIBER 0x1012
|
|
#define E1000_DEV_82546EB_QUAD 0x101D
|
|
#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 ------------------------------------
|
|
|
|
#define E1000_CTRL 0x0000 // device control
|
|
#define E1000_STATUS 0x0008 // device status
|
|
#define E1000_EERD 0x0014 // EEPROM read
|
|
#define E1000_ICR 0x00C0 // interrupt cause read
|
|
#define E1000_IMS 0x00D0 // interrupt mask set
|
|
#define E1000_IMC 0x00D8 // interrupt mask clear
|
|
|
|
#define E1000_RCTL 0x0100 // receive control
|
|
#define E1000_TCTL 0x0400 // transmit control
|
|
#define E1000_TIPG 0x0410 // transmit inter-packet gap
|
|
|
|
#define E1000_RDBAL 0x2800 // receive descriptor base, low
|
|
#define E1000_RDBAH 0x2804 // receive descriptor base, high
|
|
#define E1000_RDLEN 0x2808 // receive descriptor length, in bytes
|
|
#define E1000_RDH 0x2810 // receive descriptor head
|
|
#define E1000_RDT 0x2818 // receive descriptor tail
|
|
|
|
#define E1000_TDBAL 0x3800
|
|
#define E1000_TDBAH 0x3804
|
|
#define E1000_TDLEN 0x3808
|
|
#define E1000_TDH 0x3810
|
|
#define E1000_TDT 0x3818
|
|
|
|
#define E1000_MTA 0x5200 // multicast table array, 128 entries
|
|
#define E1000_RAL 0x5400 // receive address low (the MAC filter)
|
|
#define E1000_RAH 0x5404 // receive address high
|
|
|
|
// CTRL
|
|
#define E1000_CTRL_RST (1 << 26)
|
|
#define E1000_CTRL_ASDE (1 << 5) // auto-speed detection
|
|
#define E1000_CTRL_SLU (1 << 6) // set link up
|
|
|
|
// STATUS
|
|
#define E1000_STATUS_LU (1 << 1) // link up
|
|
|
|
// 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
|
|
#define E1000_RCTL_UPE (1 << 3) // unicast promiscuous
|
|
#define E1000_RCTL_MPE (1 << 4) // multicast promiscuous
|
|
#define E1000_RCTL_LBM_NONE (0 << 6)
|
|
#define E1000_RCTL_BAM (1 << 15) // accept broadcast
|
|
#define E1000_RCTL_SZ_2048 (0 << 16) // buffer size, with BSEX clear
|
|
#define E1000_RCTL_SECRC (1 << 26) // strip the Ethernet CRC
|
|
|
|
// TCTL
|
|
#define E1000_TCTL_EN (1 << 1)
|
|
#define E1000_TCTL_PSP (1 << 3) // pad short packets
|
|
#define E1000_TCTL_CT_SHIFT 4 // collision threshold
|
|
#define E1000_TCTL_COLD_SHIFT 12 // collision distance
|
|
|
|
// Transmit descriptor command bits
|
|
#define E1000_TXD_CMD_EOP (1 << 0) // end of packet
|
|
#define E1000_TXD_CMD_IFCS (1 << 1) // insert FCS/CRC
|
|
#define E1000_TXD_CMD_RS (1 << 3) // report status
|
|
#define E1000_TXD_STAT_DD (1 << 0) // descriptor done
|
|
|
|
// Receive descriptor status bits
|
|
#define E1000_RXD_STAT_DD (1 << 0)
|
|
#define E1000_RXD_STAT_EOP (1 << 1)
|
|
|
|
// Interrupt causes worth unmasking.
|
|
#define E1000_ICR_TXDW (1 << 0) // transmit descriptor written back
|
|
#define E1000_ICR_LSC (1 << 2) // link status change
|
|
#define E1000_ICR_RXDMT0 (1 << 4) // receive descriptor minimum threshold
|
|
#define E1000_ICR_RXT0 (1 << 7) // receiver timer
|
|
|
|
// --- descriptors -----------------------------------------------------------
|
|
//
|
|
// Both are 16 bytes and the hardware reads them by physical address, so the
|
|
// rings must be physically contiguous and 16-byte aligned.
|
|
|
|
struct e1000_tx_desc {
|
|
uint64 addr;
|
|
uint16 length;
|
|
uint8 cso; // checksum offset
|
|
uint8 cmd;
|
|
uint8 status;
|
|
uint8 css; // checksum start
|
|
uint16 special;
|
|
} __attribute__((packed));
|
|
|
|
struct e1000_rx_desc {
|
|
uint64 addr;
|
|
uint16 length;
|
|
uint16 checksum;
|
|
uint8 status;
|
|
uint8 errors;
|
|
uint16 special;
|
|
} __attribute__((packed));
|
|
|
|
// Ring sizes. The manual requires the descriptor ring length to be a multiple
|
|
// of 128 bytes, i.e. a multiple of 8 descriptors.
|
|
//
|
|
// The receive ring has to hold a whole TCP window arriving at once, and more:
|
|
// a sender that has the window open sends it back to back, and every frame
|
|
// past the ring's end is dropped and waits for a retransmission. Sixteen was
|
|
// fine for a window of eight segments; with thirty-two it held downloads to a
|
|
// megabit a second. 256 buffers is half a megabyte of the 64 MB DMA arena.
|
|
#define E1000_NTX 64
|
|
#define E1000_NRX 256
|
|
#define E1000_BUFSZ 2048
|