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

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