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

@ -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

View file

@ -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)

View file

@ -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,

View file

@ -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)

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 |

View file

@ -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);

View file

@ -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 },

View file

@ -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");
}
}

View file

@ -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.

View file

@ -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

View file

@ -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) {

View file

@ -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) {