Keyboard lights, TLB shootdowns, battery in sysfs, /proc/pid/maps
Caps, Num and Scroll Lock now light their LEDs on PS/2 and USB keyboards, share one state between keyboards, and stop toggling on key repeat. X sets them through evdev writes while it holds the keyboard. Unmapping memory in a process with threads on other CPUs now flushes their TLBs with an IPI. Without it a freed page stayed reachable through stale translations, which corrupted Epiphany's heap on SMP. ACPI batteries and AC adapters appear under /sys/class/power_supply. /proc/<pid>/maps lists file mappings, and a process killed by a fault names the file and offset it died in. r8169 gets a 256-entry RX ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
73dcea7496
commit
438e7af141
18 changed files with 945 additions and 9 deletions
228
kernel/proc/mapname.c
Normal file
228
kernel/proc/mapname.c
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
// Which file each part of an address space came from.
|
||||
//
|
||||
// mmap of a file copies it into anonymous memory (see sys_mem.c), so the page
|
||||
// tables and the vma records know nothing about files. This remembers the
|
||||
// name beside them -- for /proc/<pid>/maps, and so that a process killed by a
|
||||
// fault is reported with the library and offset it died in rather than a bare
|
||||
// address. There is no ptrace here and so no debugger: Epiphany crashing at
|
||||
// "rip 0x8a094bea" in a library loaded with dlopen was a dead end without it.
|
||||
//
|
||||
// A dynamic linker maps each library as one span from offset 0 and then
|
||||
// places its segments inside with MAP_FIXED; those inner maps of the same
|
||||
// file are absorbed, so a library is one entry. Only diagnostics read this,
|
||||
// and it is allowed to be approximate at the edges: a span unmapped in part
|
||||
// keeps its name.
|
||||
|
||||
#include "core/types.h"
|
||||
#include "core/defs.h"
|
||||
#include "core/param.h"
|
||||
#include "arch/memlayout.h"
|
||||
#include "arch/mmu.h"
|
||||
#include "core/spinlock.h"
|
||||
#include "core/sleeplock.h"
|
||||
#include "core/proc.h"
|
||||
#include "core/kmem.h"
|
||||
|
||||
struct mapname {
|
||||
struct mapname *next;
|
||||
uint64 start, end, off;
|
||||
char path[96];
|
||||
};
|
||||
|
||||
static struct spinlock mnlock;
|
||||
static int mnlock_ready;
|
||||
|
||||
static void
|
||||
mn_lock(void)
|
||||
{
|
||||
if (!mnlock_ready) {
|
||||
initlock(&mnlock, "mapname");
|
||||
mnlock_ready = 1;
|
||||
}
|
||||
acquire(&mnlock);
|
||||
}
|
||||
|
||||
// Forget names wholly inside [start, end), and trim those hanging over an
|
||||
// edge of it. A name the range falls inside stays whole.
|
||||
static void
|
||||
mn_cut(struct mm *m, uint64 start, uint64 end)
|
||||
{
|
||||
struct mapname **pp = &m->names;
|
||||
while (*pp) {
|
||||
struct mapname *n = *pp;
|
||||
if (n->start >= start && n->end <= end) {
|
||||
*pp = n->next;
|
||||
kmfree(n);
|
||||
continue;
|
||||
}
|
||||
if (n->start < start && n->end > start && n->end <= end)
|
||||
n->end = start;
|
||||
else if (n->start >= start && n->start < end && n->end > end) {
|
||||
n->off += end - n->start;
|
||||
n->start = end;
|
||||
}
|
||||
pp = &n->next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mapname_add(struct mm *m, uint64 start, uint64 len, uint64 off, const char *path)
|
||||
{
|
||||
struct mapname *n;
|
||||
|
||||
if (!path || !*path)
|
||||
return;
|
||||
mn_lock();
|
||||
for (n = m->names; n; n = n->next)
|
||||
if (n->start <= start && start + len <= n->end && strncmp(n->path, path, sizeof(n->path) - 1) == 0) {
|
||||
release(&mnlock);
|
||||
return; // a segment of a library already named
|
||||
}
|
||||
mn_cut(m, start, start + len);
|
||||
release(&mnlock);
|
||||
if ((n = kmalloc(sizeof(*n))) == 0)
|
||||
return;
|
||||
n->start = start;
|
||||
n->end = start + len;
|
||||
n->off = off;
|
||||
safestrcpy(n->path, path, sizeof(n->path));
|
||||
mn_lock();
|
||||
n->next = m->names;
|
||||
m->names = n;
|
||||
release(&mnlock);
|
||||
}
|
||||
|
||||
void
|
||||
mapname_unmap(struct mm *m, uint64 start, uint64 len)
|
||||
{
|
||||
mn_lock();
|
||||
mn_cut(m, start, start + len);
|
||||
release(&mnlock);
|
||||
}
|
||||
|
||||
void
|
||||
mapname_free(struct mm *m)
|
||||
{
|
||||
mn_lock();
|
||||
struct mapname *n = m->names;
|
||||
m->names = 0;
|
||||
release(&mnlock);
|
||||
while (n) {
|
||||
struct mapname *next = n->next;
|
||||
kmfree(n);
|
||||
n = next;
|
||||
}
|
||||
}
|
||||
|
||||
// fork: the child's address space is the parent's, names and all.
|
||||
void
|
||||
mapname_copy(struct mm *dst, struct mm *src)
|
||||
{
|
||||
struct mapname *n;
|
||||
|
||||
mn_lock();
|
||||
for (n = src->names; n; n = n->next) {
|
||||
struct mapname *c = kmalloc(sizeof(*c));
|
||||
if (!c)
|
||||
break;
|
||||
*c = *n;
|
||||
c->next = dst->names;
|
||||
dst->names = c;
|
||||
}
|
||||
release(&mnlock);
|
||||
}
|
||||
|
||||
// The name addr falls in and its offset in the file, copied out. 0 if found.
|
||||
int
|
||||
mapname_find(struct mm *m, uint64 addr, char *out, int cap, uint64 *off)
|
||||
{
|
||||
int r = -1;
|
||||
|
||||
mn_lock();
|
||||
for (struct mapname *n = m->names; n; n = n->next)
|
||||
if (n->start <= addr && addr < n->end) {
|
||||
safestrcpy(out, n->path, cap);
|
||||
*off = n->off + (addr - n->start);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
release(&mnlock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
put(char *buf, int n, int cap, const char *s)
|
||||
{
|
||||
while (*s && n < cap - 1)
|
||||
buf[n++] = *s++;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
puthex(char *buf, int n, int cap, uint64 v, int width)
|
||||
{
|
||||
char t[17];
|
||||
int i = 16;
|
||||
t[16] = 0;
|
||||
do { t[--i] = "0123456789abcdef"[v & 15]; v >>= 4; } while (v && i > 0);
|
||||
while (16 - i < width && i > 0)
|
||||
t[--i] = '0';
|
||||
return put(buf, n, cap, t + i);
|
||||
}
|
||||
|
||||
// /proc/<pid>/maps, in Linux's layout. Named spans and the anonymous records
|
||||
// beside them; permissions are not tracked per page here, so every line
|
||||
// says rw and private, and only the names are to be trusted.
|
||||
int
|
||||
mapname_gen(struct mm *m, char *buf, int cap)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
mn_lock();
|
||||
for (struct mapname *e = m->names; e; e = e->next) {
|
||||
n = puthex(buf, n, cap, e->start, 8);
|
||||
n = put(buf, n, cap, "-");
|
||||
n = puthex(buf, n, cap, e->end, 8);
|
||||
n = put(buf, n, cap, " r-xp ");
|
||||
n = puthex(buf, n, cap, e->off, 8);
|
||||
n = put(buf, n, cap, " 00:00 0 ");
|
||||
n = put(buf, n, cap, e->path);
|
||||
n = put(buf, n, cap, "\n");
|
||||
}
|
||||
release(&mnlock);
|
||||
for (int i = 0; i < NVMA; i++) {
|
||||
struct vma *v = &m->vma[i];
|
||||
if (!v->used)
|
||||
continue;
|
||||
n = puthex(buf, n, cap, v->addr, 8);
|
||||
n = put(buf, n, cap, "-");
|
||||
n = puthex(buf, n, cap, v->addr + v->length, 8);
|
||||
n = put(buf, n, cap, v->shmid >= 0 ? " rw-s 00000000 00:00 0 [memfd]\n"
|
||||
: " rw-p 00000000 00:00 0\n");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Said when a process dies of a fault: where its instruction pointer was,
|
||||
// and where the return address on top of its stack points, by file. The
|
||||
// second is the caller, which is what is left to go on when the first is in
|
||||
// generated code.
|
||||
void
|
||||
mapname_report(struct proc *p)
|
||||
{
|
||||
char path[96];
|
||||
uint64 off, ret = 0;
|
||||
|
||||
if (!p->mm || !p->tf)
|
||||
return;
|
||||
if (mapname_find(p->mm, p->tf->rip, path, sizeof(path), &off) == 0)
|
||||
cprintf("pid %d %s: rip %p is %s+0x%lx\n", p->pid, p->name,
|
||||
(void *)p->tf->rip, path, off);
|
||||
else
|
||||
cprintf("pid %d %s: rip %p is in no file\n", p->pid, p->name,
|
||||
(void *)p->tf->rip);
|
||||
if (fetchaddr(p->tf->rsp, &ret) == 0 &&
|
||||
mapname_find(p->mm, ret, path, sizeof(path), &off) == 0)
|
||||
cprintf("pid %d %s: [rsp] %p is %s+0x%lx\n", p->pid, p->name,
|
||||
(void *)ret, path, off);
|
||||
}
|
||||
Loading…
Reference in a new issue