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:
parent
3113eff7fe
commit
54b25708c3
24 changed files with 4166 additions and 68 deletions
|
|
@ -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 |
|
||||
|
|
|
|||
Loading…
Reference in a new issue