docs/ was ten files where a plan, a description, a guide and a bug report all
sat next to each other. It is now architecture/ (what SchOS is), plan/ (what it
will be), guides/ (how to work on it) and history/ (why it is like this), with
an index that states the one rule holding it together: when something is
finished it moves from plan/ to architecture/ and is rewritten as a description
of the thing rather than a record of building it.
A ticked checkbox says the work happened. It does not say what the work
*produced*, and six months later the checkbox is all that is left. The network
stack proved the point: finished, verified end to end, and for a month the only
description of it anywhere was a checkbox four levels of indentation deep.
So roadmap.md went from 534 lines to 181 -- nothing deleted, the done work
graduated. Seven subsystems that were finished and undescribed now have
descriptions: boot, kernel, syscalls, filesystem, networking, graphics, usb,
init. Every one ends with "What it does not do", which is the section that pays;
a document listing only strengths is marketing, and the reader who needs it most
is the one about to trip over a limitation nobody wrote down.
schfs.md still opened with "phases 1 through 3 1/2 are done, phase 4 is next"
long after all five had landed. It is a description now, with the phase record
and the hard-won bugs moved to history/.
Three corrections found on the way, all load-bearing:
* projects/schcom/README.md claimed SCM_RIGHTS was done. There is no
sendmsg/recvmsg in the kernel at all, and an entire GUI plan rested on it.
* The root README said "No TCP/IP" months after the stack landed.
* history/kernel.md sec.7 was marked "in progress" on a problem that process
groups solved.
Also adds plan/1.0.md, which draws a finish line so the open list stops being
infinite, and marks schwm/ and schde/ superseded: in Wayland the compositor is
the window manager, so that split dissolves rather than being abandoned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
165 lines
6.6 KiB
C
165 lines
6.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.
|
|
//
|
|
// 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.
|
|
#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
|
|
#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
|
|
#define E1000_EERD_START (1 << 0)
|
|
#define E1000_EERD_DONE (1 << 4)
|
|
#define E1000_EERD_ADDR_SHIFT 8
|
|
#define E1000_EERD_DATA_SHIFT 16
|
|
|
|
// 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.
|
|
#define E1000_NTX 16
|
|
#define E1000_NRX 16
|
|
#define E1000_BUFSZ 2048
|