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