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
|
|
@ -160,6 +160,11 @@ trap(struct trapframe *tf)
|
|||
}
|
||||
|
||||
unexpected:
|
||||
// A user page fault on anonymous memory that is reserved but not backed
|
||||
// yet: the page is made and the instruction runs again. See vm_fault().
|
||||
if(tf->trapno == T_PGFLT && myproc() && (tf->cs&3) == DPL_USER &&
|
||||
!(tf->err & 1) && vm_fault(rcr2()) == 0)
|
||||
break;
|
||||
if(myproc() == 0 || (tf->cs&3) == 0){
|
||||
// A kernel-mode trap ends in panic() a dozen lines down, so the console
|
||||
// can be put into its panic mode *now* -- and it has to be, because the
|
||||
|
|
|
|||
|
|
@ -1134,6 +1134,13 @@ copyuvm(pml4e_t *pgdir, uint64 sz)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// The PTE for va, if its page tables exist. For vm_fault().
|
||||
pte_t *
|
||||
walkpte(pml4e_t *pgdir, const void *va)
|
||||
{
|
||||
return walkpgdir(pgdir, va, 0);
|
||||
}
|
||||
|
||||
char*
|
||||
uva2ka(pml4e_t *pgdir, char *uva)
|
||||
{
|
||||
|
|
@ -1142,6 +1149,11 @@ uva2ka(pml4e_t *pgdir, char *uva)
|
|||
|
||||
pte_t *pte;
|
||||
pte = walkpgdir(pgdir, uva, 0);
|
||||
// Anonymous memory not touched yet: backed now, as the program's own
|
||||
// access would have done. Only for the running process's own table.
|
||||
if((pte == 0 || (*pte & PTE_P) == 0) && myproc() && pgdir == myproc()->pgdir &&
|
||||
vm_fault((addr_t)uva) == 0)
|
||||
pte = walkpgdir(pgdir, uva, 0);
|
||||
if(pte == 0 || (*pte & PTE_P) == 0)
|
||||
return 0;
|
||||
if((*pte & PTE_U) == 0)
|
||||
|
|
|
|||
|
|
@ -336,7 +336,7 @@ static struct spinlock input_lock;
|
|||
// 1 /dev/console the active terminal, so kernel messages and a
|
||||
// shell started on the console both follow a switch
|
||||
// 10 /dev/tty the caller's controlling terminal
|
||||
// 11..16 /dev/tty1..6 that terminal, whether or not it is on screen
|
||||
// 11..22 /dev/tty1..12 that terminal, whether or not it is on screen
|
||||
//
|
||||
// Anything else is the console, which is what an unrecognised minor meant
|
||||
// before there was more than one terminal to get wrong.
|
||||
|
|
@ -461,7 +461,7 @@ erase_one(struct vt *v)
|
|||
// Keyboard input
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Alt+F1..F6, Ctrl+Alt+F1..F6 and the settable chord (AltGr+F1..F6 by
|
||||
// Alt+F-key, Ctrl+Alt+F-key and the settable chord (Ctrl+Alt+F1..F12 by
|
||||
// default) switch terminals. kbd.c reports them as
|
||||
// KBD_VT_SWITCH+n so that the
|
||||
// decision of what a chord *means* is made here, where the terminals are,
|
||||
|
|
|
|||
|
|
@ -9,19 +9,40 @@
|
|||
|
||||
// The chord, settable at run time through VT_SETCHORD.
|
||||
//
|
||||
// AltGr+F1..F6 by default. Every other candidate is taken by the host, by
|
||||
// applications, or by the keyboard layout -- see include/dev/kbd.h -- and AltGr
|
||||
// with a function key is taken by none of the three: a Polish layout wants
|
||||
// AltGr+*letter*, which still reaches the shell untouched, and no window
|
||||
// manager binds AltGr with an F-key.
|
||||
// Ctrl+Alt+F1..F12 by default, the same keys X switches with. It used to be
|
||||
// AltGr+F-key, chosen because nothing else claimed it -- but under X the
|
||||
// server owns the keyboard and only Ctrl+Alt+F-key works, so the console and
|
||||
// the desktop answered different keys for the same thing.
|
||||
//
|
||||
// AltGr+minus and AltGr+equals step to the previous and next terminal on the
|
||||
// same modifiers, for when you have two and do not care which number they are.
|
||||
// Ctrl+Alt+minus and Ctrl+Alt+equals step to the previous and next terminal,
|
||||
// for when you have two and do not care which number they are.
|
||||
//
|
||||
// Zero modifiers disables all of it; `chvt N` still works.
|
||||
uint vt_chord_mods = RALT;
|
||||
// Zero modifiers disables the chord; `chvt N` still works.
|
||||
uint vt_chord_mods = CTL | ALT;
|
||||
uint vt_chord_base = SCAN_F1;
|
||||
|
||||
// Which terminal an F-key scancode names: F1..F10 are 0x3B..0x44 and F11 and
|
||||
// F12 sit apart, at 0x57 and 0x58. -1 for anything else.
|
||||
static int
|
||||
fkey_index(uint data)
|
||||
{
|
||||
if (data >= SCAN_F1 && data < SCAN_F1 + 10) return (int)(data - SCAN_F1);
|
||||
if (data == 0x57) return 10;
|
||||
if (data == 0x58) return 11;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The terminal a chord key names, from `base`: the F-keys by fkey_index, any
|
||||
// other base as a run of ten consecutive scancodes -- a digit row is 1..9, 0,
|
||||
// and the minus and equals after it are already the previous and next keys.
|
||||
static int
|
||||
chord_index(uint data, uint base)
|
||||
{
|
||||
int i = base == SCAN_F1 ? fkey_index(data)
|
||||
: (data >= base && data < base + 10 ? (int)(data - base) : -1);
|
||||
return i < NVT ? i : -1;
|
||||
}
|
||||
|
||||
// Which scancodes are modifiers, and which are locks. Hardware facts rather
|
||||
// than layout ones -- every layout agrees that 0x2A is a Shift key -- so they
|
||||
// stay here while the character tables live in keymap.c.
|
||||
|
|
@ -146,21 +167,20 @@ kbdgetc(void)
|
|||
// modifiers named, and nothing else held.
|
||||
if (vt_chord_mods != 0 &&
|
||||
(shift & (CTL | SHIFT | ALT | RALT)) == vt_chord_mods) {
|
||||
if (data >= vt_chord_base && data < vt_chord_base + NVT)
|
||||
return KBD_VT_SWITCH + (data - vt_chord_base);
|
||||
int i = chord_index(data, vt_chord_base);
|
||||
if (i >= 0)
|
||||
return KBD_VT_SWITCH + i;
|
||||
if (data == SCAN_MINUS) return KBD_VT_PREV;
|
||||
if (data == SCAN_EQUALS) return KBD_VT_NEXT;
|
||||
}
|
||||
|
||||
// And Linux's own: Alt+F1..F6 and Ctrl+Alt+F1..F6, whatever the chord is set
|
||||
// to. These are what anyone who has used Linux presses, on a real machine,
|
||||
// and nothing reached them: SchOS answered only AltGr+F-keys, which no one
|
||||
// thinks to try. Under X the terminal is in VT_PROCESS, console_vt_hotkey()
|
||||
// lets the keys through, and X switches with them itself -- as on Linux.
|
||||
if (data >= SCAN_F1 && data < SCAN_F1 + NVT &&
|
||||
// And Linux's own: Alt+F-key and Ctrl+Alt+F-key, whatever the chord is set
|
||||
// to. Under X the terminal is in VT_PROCESS, console_vt_hotkey() lets the
|
||||
// keys through, and X switches with them itself -- as on Linux.
|
||||
if (chord_index(data, SCAN_F1) >= 0 &&
|
||||
((shift & (CTL | SHIFT | ALT | RALT)) == ALT ||
|
||||
(shift & (CTL | SHIFT | ALT | RALT)) == (CTL | ALT)))
|
||||
return KBD_VT_SWITCH + (data - SCAN_F1);
|
||||
return KBD_VT_SWITCH + chord_index(data, SCAN_F1);
|
||||
|
||||
cp = keymap_lookup(data, shift);
|
||||
if (cp == 0)
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -164,9 +164,10 @@ usb_kbd_getc(void)
|
|||
static int
|
||||
hid_special(uchar usage, uchar mods)
|
||||
{
|
||||
// F1..F6 with Alt, AltGr or Ctrl+Alt: switch terminals, as on Linux.
|
||||
// F1..F12 with left Alt or Ctrl+Alt: switch terminals, as on Linux and as
|
||||
// the PS/2 path does. AltGr alone is a layout's third level, not this.
|
||||
if (usage >= 0x3A && usage < 0x3A + NVT && (mods & HID_MOD_ALT) &&
|
||||
!(mods & HID_MOD_SHIFT))
|
||||
!(mods & HID_MOD_SHIFT) && ((mods & 0x04) || (mods & HID_MOD_CTRL)))
|
||||
return KBD_VT_SWITCH + (usage - 0x3A);
|
||||
if (usage >= 0x3A && usage <= 0x45)
|
||||
return KBD_F1 + (usage - 0x3A);
|
||||
|
|
|
|||
|
|
@ -101,6 +101,12 @@ static const struct devnode devnodes[] = {
|
|||
{ "tty4", 1, 14, D_ROOT },
|
||||
{ "tty5", 1, 15, D_ROOT },
|
||||
{ "tty6", 1, 16, D_ROOT },
|
||||
{ "tty7", 1, 17, D_ROOT },
|
||||
{ "tty8", 1, 18, D_ROOT },
|
||||
{ "tty9", 1, 19, D_ROOT },
|
||||
{ "tty10", 1, 20, D_ROOT },
|
||||
{ "tty11", 1, 21, D_ROOT },
|
||||
{ "tty12", 1, 22, D_ROOT },
|
||||
{ "stdin", 1, 1, D_ROOT },
|
||||
{ "stdout", 1, 1, D_ROOT },
|
||||
{ "stderr", 1, 1, D_ROOT },
|
||||
|
|
|
|||
|
|
@ -259,3 +259,28 @@ net_eth_poll(void)
|
|||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
// Follow each device's link into lwIP.
|
||||
//
|
||||
// The link was read once, at attach, and never again. Under QEMU it is up
|
||||
// before the driver asks; on a real network autonegotiation takes two or
|
||||
// three seconds, so attach saw it down and the interface stayed down for
|
||||
// good -- no DHCP, no traffic, the cable plugged in and nothing to show for
|
||||
// it. Bringing the link up also restarts DHCP (lwIP does that itself).
|
||||
void
|
||||
net_eth_link_check(void)
|
||||
{
|
||||
for (int k = 0; k < nifs; k++) {
|
||||
struct eth_if *e = &ifs[k];
|
||||
if (!e->used)
|
||||
continue;
|
||||
int up = netdev_link_up(e->dev) != 0;
|
||||
if (up == (netif_is_link_up(&e->netif) != 0))
|
||||
continue;
|
||||
if (up)
|
||||
netif_set_link_up(&e->netif);
|
||||
else
|
||||
netif_set_link_down(&e->netif);
|
||||
klog("net: %s link %s\n", e->dev->name, up ? "up" : "down");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -292,12 +292,19 @@ net_thread(void)
|
|||
// One bit per interface, so a second one binding later is announced too.
|
||||
|
||||
|
||||
uint link_checked = 0;
|
||||
for (;;) {
|
||||
int moved;
|
||||
|
||||
lwip_sys_lock();
|
||||
moved = net_eth_poll();
|
||||
sys_check_timeouts();
|
||||
// A quarter of a second is quick enough to notice a cable, and a
|
||||
// register read per interface that often costs nothing.
|
||||
if (ticks - link_checked >= 25) {
|
||||
link_checked = ticks;
|
||||
net_eth_link_check();
|
||||
}
|
||||
// Loopback has no driver and no interrupt: a packet sent to 127.0.0.1
|
||||
// sits on the loopif's queue until something drains it. With NO_SYS=1
|
||||
// that something is this thread.
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ sockopt_set(struct socket *s, int level, int name, int val)
|
|||
case SO_REUSEADDR: s->o_reuseaddr = !!val; break;
|
||||
case SO_KEEPALIVE: s->o_keepalive = !!val; break;
|
||||
case SO_BROADCAST: s->o_broadcast = !!val; break;
|
||||
case 16: s->o_passcred = !!val; break; // SO_PASSCRED
|
||||
// Accepted and left at the size we have. Not the same lie as before:
|
||||
// Linux is explicitly free to clamp these, and sockopt_get below reports
|
||||
// the real figure rather than the requested one.
|
||||
|
|
@ -313,6 +314,7 @@ sockopt_get(struct socket *s, int level, int name, int *out)
|
|||
case SO_REUSEADDR: *out = s->o_reuseaddr; break;
|
||||
case SO_KEEPALIVE: *out = s->o_keepalive; break;
|
||||
case SO_BROADCAST: *out = s->o_broadcast; break;
|
||||
case 16: *out = s->o_passcred; break; // SO_PASSCRED
|
||||
// The real buffer, not a number somebody asked for.
|
||||
case SO_SNDBUF:
|
||||
case SO_RCVBUF: *out = SOCKBUF; break;
|
||||
|
|
@ -1025,6 +1027,12 @@ socksendto(struct socket *s, char *buf, int n, uint addr, ushort port)
|
|||
return sockwrite(s, buf, n);
|
||||
}
|
||||
|
||||
int
|
||||
sock_passcred(struct socket *s)
|
||||
{
|
||||
return s->domain != AF_INET && s->o_passcred;
|
||||
}
|
||||
|
||||
// SO_PEERCRED: the credentials recorded for the far end of a connected Unix
|
||||
// socket. -1 if there is no far end.
|
||||
int
|
||||
|
|
|
|||
|
|
@ -123,6 +123,81 @@ mmap_va(struct proc *p, uint64 len, int mergeable)
|
|||
return va;
|
||||
}
|
||||
|
||||
// --- demand paging ---------------------------------------------------------
|
||||
//
|
||||
// Anonymous memory may be recorded without being backed: the page appears,
|
||||
// zeroed, the first time it is touched -- by the program, through trap(), or
|
||||
// by the kernel on its behalf, through uva2ka(). Large mappings are made this
|
||||
// way, and so are the ones that say outright they will not all be used.
|
||||
// WebKit reserves 4 GB of address space at start for its JIT and heaps and
|
||||
// touches a sliver of it; backing all of it at mmap time failed with ENOMEM
|
||||
// and Epiphany aborted. Go, Java and Firefox reserve the same way.
|
||||
//
|
||||
// fork and munmap already skip the holes in anonymous memory, so a page that
|
||||
// was never touched is simply one they do not see, and a child that touches
|
||||
// it gets zeroes -- which is what it would have read in the parent.
|
||||
|
||||
#define LAZY_MIN (1024 * 1024)
|
||||
|
||||
static int
|
||||
mmap_lazy(int prot, int flags, uint64 length)
|
||||
{
|
||||
return (flags & MAP_NORESERVE) || prot == PROT_NONE || length >= LAZY_MIN;
|
||||
}
|
||||
|
||||
static struct spinlock faultlock;
|
||||
static int faultlock_ready;
|
||||
|
||||
// Back the page at va if it lies in this process's anonymous memory and is
|
||||
// not there yet. 0 if it is there now, -1 if this is a real fault. Takes a
|
||||
// spinlock and nothing that sleeps, so uva2ka() may call it from anywhere;
|
||||
// the lock is what keeps two threads of one process from both mapping it.
|
||||
int
|
||||
vm_fault(addr_t va)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
pte_t *pte;
|
||||
char *mem;
|
||||
int found = 0;
|
||||
|
||||
if (!p || !p->mm || va >= KERNBASE)
|
||||
return -1;
|
||||
va = PGROUNDDOWN(va);
|
||||
for (int i = 0; i < NVMA; i++) {
|
||||
struct vma *v = &p->mm->vma[i];
|
||||
if (vma_anon(v) && v->addr <= va && va < v->addr + v->length) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return -1;
|
||||
if ((mem = kalloc()) == 0)
|
||||
return -1;
|
||||
memset(mem, 0, PGSIZE);
|
||||
if (!faultlock_ready) {
|
||||
initlock(&faultlock, "vmfault");
|
||||
faultlock_ready = 1;
|
||||
}
|
||||
acquire(&faultlock);
|
||||
pte = walkpte(p->pgdir, (void *)va);
|
||||
if (pte && (*pte & PTE_P)) {
|
||||
// Another thread got here first, or the page was never missing and
|
||||
// this is a protection fault: either way, not ours to fix.
|
||||
int ours = (*pte & PTE_U) != 0;
|
||||
release(&faultlock);
|
||||
kfree(mem);
|
||||
return ours ? 0 : -1;
|
||||
}
|
||||
if (mappages(p->pgdir, (void *)va, PGSIZE, V2P((uint64)mem), PTE_W | PTE_U) < 0) {
|
||||
release(&faultlock);
|
||||
kfree(mem);
|
||||
return -1;
|
||||
}
|
||||
release(&faultlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Record [va, va+len) as anonymous memory wherever no mapping already covers
|
||||
// it, for a MAP_FIXED that may land anywhere: inside an earlier mapping, across
|
||||
// the end of one, or in a gap. Below sz is the program image and its heap,
|
||||
|
|
@ -205,8 +280,16 @@ mmap_locked(void)
|
|||
if (bad_user_range(va, length) || (va % PGSIZE))
|
||||
return -EINVAL;
|
||||
vm_unmap_range(myproc()->pgdir, va, length);
|
||||
if (prot == PROT_NONE)
|
||||
int fixed_file = f && f->type == FD_INODE && f->ip->type != T_DEV &&
|
||||
!(flags & MAP_ANONYMOUS);
|
||||
// Reserved, not backed. PROT_NONE is recorded too: mprotect() does not
|
||||
// change pages here, so a range reserved PROT_NONE and opened up later
|
||||
// has to be memory that can appear when it is touched.
|
||||
if (!fixed_file && mmap_lazy(prot, flags, length)) {
|
||||
if (vma_cover_anon(myproc(), va, length) < 0)
|
||||
return -ENOMEM;
|
||||
return va;
|
||||
}
|
||||
for (uint64 a = va; a < va + length; a += PGSIZE) {
|
||||
char *mem = kalloc();
|
||||
if (!mem) return -ENOMEM;
|
||||
|
|
@ -407,6 +490,11 @@ mmap_locked(void)
|
|||
// to the text console. fbclose() puts it back.
|
||||
fb_mapped();
|
||||
} else {
|
||||
if (mmap_lazy(prot, flags, length)) {
|
||||
if (vma_note_anon(myproc(), va, length) < 0)
|
||||
return -ENOMEM;
|
||||
return va;
|
||||
}
|
||||
for (uint64 a = va; a < va + length; a += PGSIZE) {
|
||||
char *mem = kalloc();
|
||||
if (!mem) {
|
||||
|
|
|
|||
|
|
@ -1030,7 +1030,7 @@ SYSCALL_DEFINE(recvmsg)
|
|||
struct file *file;
|
||||
struct msghdr m;
|
||||
struct file *fs[SCM_MAXFD];
|
||||
char cbuf[CMSG_SPACE(SCM_MAXFD * sizeof(int))];
|
||||
char cbuf[CMSG_SPACE(SCM_MAXFD * sizeof(int)) + CMSG_SPACE(12)];
|
||||
int ufd[SCM_MAXFD];
|
||||
addr_t umsg;
|
||||
uint64 i, clen = 0;
|
||||
|
|
@ -1141,6 +1141,35 @@ SYSCALL_DEFINE(recvmsg)
|
|||
}
|
||||
}
|
||||
|
||||
// SO_PASSCRED: who sent it, after any descriptors. On a connected Unix
|
||||
// socket that is always the peer, so its recorded credentials are the
|
||||
// answer. WebKit sets the option on its IPC sockets and aborts if it is
|
||||
// refused.
|
||||
if (sock_passcred(s) && got > 0) {
|
||||
struct { int pid; uint uid, gid; } cr;
|
||||
uint64 off = clen ? CMSG_SPACE(clen - CMSG_LEN(0)) : 0;
|
||||
if (sock_peercred(s, &cr.pid, &cr.uid, &cr.gid) == 0) {
|
||||
if (m.msg_control && off + CMSG_LEN(sizeof(cr)) <= m.msg_controllen) {
|
||||
struct cmsghdr c;
|
||||
char tmp[CMSG_SPACE(sizeof(cr))];
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
c.cmsg_len = CMSG_LEN(sizeof(cr));
|
||||
c.cmsg_level = SOL_SOCKET;
|
||||
c.cmsg_type = 2; // SCM_CREDENTIALS
|
||||
memmove(tmp, &c, sizeof(c));
|
||||
memmove(CMSG_DATA((struct cmsghdr *)tmp), &cr, sizeof(cr));
|
||||
uint64 n2 = CMSG_SPACE(sizeof(cr));
|
||||
if (off + n2 > m.msg_controllen)
|
||||
n2 = CMSG_LEN(sizeof(cr));
|
||||
if (copy_to_user((addr_t)m.msg_control + off, tmp, n2) < 0)
|
||||
return -EFAULT;
|
||||
clen = off + n2;
|
||||
} else {
|
||||
mflags |= MSG_CTRUNC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The address, for the family that has one. Same rule as recvfrom: a Unix
|
||||
// socket reports a length of zero rather than a sockaddr full of nothing.
|
||||
if (m.msg_name) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue