Demand paging, the RTL8168H, links that come up late, and Ctrl+Alt+F1..F12

- Anonymous memory can be reserved without being backed: large mappings,
  MAP_NORESERVE and PROT_NONE are recorded and each page appears, zeroed,
  on first touch -- from trap() for the program, from uva2ka() for the
  kernel on its behalf. WebKit reserves 4 GB at start and touches a sliver;
  backing it all failed with ENOMEM and Epiphany aborted.
- r8169: the 8168G generation and later (the laptop's 8168H, XID 541) are
  taken from the firmware's out-of-band mode, get their PLL powered, the
  receive gate opened, and their PHY reached through the OCP window. The
  PHY's power-down bit is cleared; the old code kept it while restarting
  autonegotiation. Untested on the hardware so far.
- The network thread follows each device's link into lwIP. The link was
  read once at attach, 0.02 s into boot; real autonegotiation takes
  seconds, so on hardware the interface stayed down for good.
- Twelve terminals, switched with Ctrl+Alt+F1..F12 by default: the keys X
  uses, so the console and the desktop agree. AltGr+F-key is gone from
  the defaults (chvt -c altgr+F brings it back).
- SO_PASSCRED on Unix sockets, with SCM_CREDENTIALS delivered by recvmsg.
  WebKit sets it on its IPC sockets and aborted when refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-25 11:27:48 +01:00
commit 54b25708c3
24 changed files with 4166 additions and 68 deletions

View file

@ -51,6 +51,13 @@
#define R_CONFIG1 0x52
#define R_PHYAR 0x60 // the MII window
#define R_PHYSTATUS 0x6C
#define R_PMCH 0x6F // PLL power
#define R_ERIDR 0x70 // extended register window: data
#define R_ERIAR 0x74 // and address
#define R_GPHY_OCP 0xB8 // the PHY's registers, 8168G and later
#define R_MAC_OCP 0xB0 // the MAC's own OCP registers
#define R_MCU 0xD3
#define R_MISC 0xF0
#define R_RMS 0xDA // largest frame the chip will accept
#define R_CPCR 0xE0 // "C+" command
#define R_RDSAR 0xE4 // Rx descriptors, 64-bit
@ -77,6 +84,15 @@
#define PHYSTATUS_LINK 0x02
#define MCU_NOW_IS_OOB 0x80 // the firmware, not a driver, owns the MAC
#define MCU_LL_READY 0x02 // link-list FIFO ready
#define MISC_RXDV_GATE 0x00080000
#define OCP_FLAG 0x80000000
#define OCP_STD_PHY_BASE 0xA400
#define BMCR_PDOWN 0x0800
#define BMCR_ANENABLE 0x1000
#define BMCR_ANRESTART 0x0200
// Rx config: accept frames addressed to us, broadcast and multicast, with the
// largest DMA burst and Rx FIFO threshold the chip offers. Promiscuous is
// deliberately not set -- a NIC that hands the stack every frame on the wire
@ -128,6 +144,8 @@ struct r8169 {
int tx_next;
uint8 mac[6];
int ready;
int gen_g; // 8168G or later: see r8169_gen_g()
uint16 ocp_base; // the PHY page selected through register 0x1F
uint64 rx_packets, tx_packets;
struct spinlock lock;
@ -168,7 +186,7 @@ wr64(struct r8169 *r, int o, uint64 v)
#define PHYAR_FLAG 0x80000000
static void
mii_write(struct r8169 *r, int reg, uint16 val)
phyar_write(struct r8169 *r, int reg, uint16 val)
{
wr32(r, R_PHYAR, PHYAR_FLAG | ((reg & 0x1F) << 16) | val);
for (int i = 0; i < 1000; i++) {
@ -179,7 +197,7 @@ mii_write(struct r8169 *r, int reg, uint16 val)
}
static int
mii_read(struct r8169 *r, int reg)
phyar_read(struct r8169 *r, int reg)
{
wr32(r, R_PHYAR, (reg & 0x1F) << 16);
for (int i = 0; i < 1000; i++) {
@ -191,6 +209,164 @@ mii_read(struct r8169 *r, int reg)
return -1;
}
// From the 8168G on, the PHY sits behind an OCP window instead: its standard
// registers are at 0xA400 + 2*reg, and "select page N" through register 0x1F
// becomes a base address. PHYAR still answers reads on some of these chips,
// which is how this driver got away without it -- until a write mattered.
static void
ocp_phy_write(struct r8169 *r, uint16 addr, uint16 val)
{
wr32(r, R_GPHY_OCP, OCP_FLAG | ((uint32)(addr >> 1) << 16) | val);
for (int i = 0; i < 1000; i++) {
microdelay(20);
if (!(rd32(r, R_GPHY_OCP) & OCP_FLAG))
return;
}
}
static int
ocp_phy_read(struct r8169 *r, uint16 addr)
{
wr32(r, R_GPHY_OCP, (uint32)(addr >> 1) << 16);
for (int i = 0; i < 1000; i++) {
microdelay(20);
uint32 v = rd32(r, R_GPHY_OCP);
if (v & OCP_FLAG)
return (int)(v & 0xFFFF);
}
return -1;
}
static void
mii_write(struct r8169 *r, int reg, uint16 val)
{
if (!r->gen_g) {
phyar_write(r, reg, val);
return;
}
if (reg == 0x1F) {
r->ocp_base = val ? (uint16)(val << 4) : OCP_STD_PHY_BASE;
return;
}
if (r->ocp_base != OCP_STD_PHY_BASE)
reg -= 0x10;
ocp_phy_write(r, (uint16)(r->ocp_base + reg * 2), val);
}
static int
mii_read(struct r8169 *r, int reg)
{
if (!r->gen_g)
return phyar_read(r, reg);
if (r->ocp_base != OCP_STD_PHY_BASE)
reg -= 0x10;
return ocp_phy_read(r, (uint16)(r->ocp_base + reg * 2));
}
// The MAC's OCP registers. No completion flag to wait on.
static uint16
mac_ocp_read(struct r8169 *r, uint16 reg)
{
wr32(r, R_MAC_OCP, (uint32)(reg >> 1) << 16);
return (uint16)rd32(r, R_MAC_OCP);
}
static void
mac_ocp_write(struct r8169 *r, uint16 reg, uint16 val)
{
wr32(r, R_MAC_OCP, OCP_FLAG | ((uint32)(reg >> 1) << 16) | val);
}
// The extended (ERI) registers, all four bytes at once.
static uint32
eri_read(struct r8169 *r, int addr)
{
wr32(r, R_ERIAR, (0xFu << 12) | addr);
for (int i = 0; i < 100; i++) {
microdelay(10);
if (rd32(r, R_ERIAR) & OCP_FLAG)
return rd32(r, R_ERIDR);
}
return 0;
}
static void
eri_write(struct r8169 *r, int addr, uint32 val)
{
wr32(r, R_ERIDR, val);
wr32(r, R_ERIAR, OCP_FLAG | (0xFu << 12) | addr);
for (int i = 0; i < 100; i++) {
microdelay(10);
if (!(rd32(r, R_ERIAR) & OCP_FLAG))
return;
}
}
// Which XIDs are the 8168G generation and later: 8168G, GU, 8411B, EP, H.
// They share the OCP windows and the firmware handover below.
static int
r8169_gen_g(uint32 xid)
{
switch (xid) {
case 0x4C0: case 0x4C1: case 0x5C8: case 0x509:
case 0x500: case 0x501: case 0x502:
case 0x540: case 0x541:
return 1;
}
return 0;
}
static void
wait_ll_ready(struct r8169 *r)
{
for (int i = 0; i < 42; i++) {
if (rd8(r, R_MCU) & MCU_LL_READY)
return;
microdelay(100);
}
}
// Taking the chip from the firmware. A laptop that was last running Windows,
// or that keeps Wake-on-LAN armed, hands it over in out-of-band mode with the
// receive path gated off: the MAC answers its registers and moves no frames.
static void
r8169_takeover_g(struct r8169 *r)
{
wr32(r, R_MISC, rd32(r, R_MISC) | MISC_RXDV_GATE);
wr8(r, R_CR, rd8(r, R_CR) & ~(CR_TE | CR_RE));
microdelay(1000);
wr8(r, R_MCU, rd8(r, R_MCU) & ~MCU_NOW_IS_OOB);
mac_ocp_write(r, 0xE8DE, mac_ocp_read(r, 0xE8DE) & ~(1u << 14));
wait_ll_ready(r);
mac_ocp_write(r, 0xE8DE, mac_ocp_read(r, 0xE8DE) | (1u << 15));
wait_ll_ready(r);
}
// The PHY up and negotiating: page 0, out of power-down, autoneg on and
// restarted. Power-down is what firmware leaves set after a shutdown, and
// the old code ORed autoneg into BMCR and kept it.
static void
r8169_phy_up(struct r8169 *r)
{
mii_write(r, 0x1F, 0);
int bmcr = mii_read(r, 0);
if (bmcr < 0)
return;
bmcr &= ~BMCR_PDOWN;
mii_write(r, 0, (uint16)bmcr);
if (r->gen_g) {
// Wait for the PHY to report it is on: its state, low three bits of
// OCP 0xA420, reads 3 once it is.
for (int i = 0; i < 100; i++) {
int st = ocp_phy_read(r, 0xA420);
if (st >= 0 && (st & 7) == 3)
break;
microdelay(1000);
}
}
mii_write(r, 0, (uint16)(bmcr | BMCR_ANENABLE | BMCR_ANRESTART));
}
// --- the class ops ----------------------------------------------------------
static const unsigned char *
@ -392,6 +568,7 @@ r8169_probe(struct pci_dev *d)
}
r->pdev = d;
r->ocp_base = OCP_STD_PHY_BASE;
initlock(&r->lock, "r8169");
pci_enable(d, PCI_CMD_MEMORY | PCI_CMD_BUS_MASTER);
@ -404,6 +581,10 @@ r8169_probe(struct pci_dev *d)
return -1;
}
r->gen_g = r8169_gen_g((rd32(r, R_TCR) >> 20) & 0xFCF);
if (r->gen_g)
r8169_takeover_g(r);
// Soft reset, and wait for the chip to say it finished.
wr8(r, R_CR, CR_RST);
for (int i = 0; i < 1000; i++) {
@ -426,6 +607,16 @@ r8169_probe(struct pci_dev *d)
wr8(r, R_9346CR, C9346_UNLOCK); // config registers are writable now
if (r->gen_g) {
// The PLL powered, the extra ERI bits the 8168H wants with it, and the
// receive gate opened again.
wr8(r, R_PMCH, rd8(r, R_PMCH) | 0xC0);
uint32 xid = (rd32(r, R_TCR) >> 20) & 0xFCF;
if (xid == 0x540 || xid == 0x541)
eri_write(r, 0x1A8, eri_read(r, 0x1A8) | 0xFC000000);
wr32(r, R_MISC, rd32(r, R_MISC) & ~MISC_RXDV_GATE);
}
// CPCR is deliberately left alone.
//
// An earlier version set bit 3 here with a comment calling it "PCI multiple
@ -455,14 +646,7 @@ r8169_probe(struct pci_dev *d)
wr8(r, R_CR, CR_RE | CR_TE);
wr8(r, R_9346CR, C9346_LOCK);
// Restart autonegotiation. The PHY normally does this out of reset, and
// asking again costs nothing and covers the case where firmware left it
// parked -- BMCR bit 12 is autoneg enable, bit 9 restarts it.
{
int bmcr = mii_read(r, 0);
if (bmcr >= 0)
mii_write(r, 0, (uint16)(bmcr | 0x1200));
}
r8169_phy_up(r);
wr16(r, R_ISR, 0xFFFF); // clear anything left over
wr16(r, R_IMR, ISR_ROK | ISR_RER | ISR_TOK | ISR_TER |