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:
Nexoniarz 2026-10-25 13:43:27 +01:00
commit 438e7af141
18 changed files with 945 additions and 9 deletions

View file

@ -19,6 +19,7 @@
#include "fs/fs.h"
#include "fs/file.h"
#include "dev/input.h"
#include "dev/kbd.h"
#include "core/uaccess.h"
#include "gfx/gfx.h"
#include "uapi/errno.h"
@ -230,6 +231,9 @@ input_grab(int dev, int on)
// accident unless the kernel says which it was.
cprintf("input: event%d %s by pid %d\n", dev,
on ? "grabbed" : "released", myproc() ? myproc()->pid : -1);
// The holder kept its own lights; the console's come back with it.
if(!on && dev == INPUT_KBD)
kbd_set_leds(kbd_lock_state());
}
return 0;
}
@ -433,11 +437,31 @@ inputread(struct inode *ip, uint64 off, char *dst, int n)
return got;
}
// evdev accepts writes to set keyboard LEDs. There are no LEDs to set here,
// and a program that writes one should not see an error for it.
// evdev accepts writes to set keyboard LEDs, and X sets them this way: it
// keeps its own lock state while it holds the keyboard. Each write is
// struct input_event -- a 16-byte time, type, code, value -- and EV_LED's
// codes 0, 1 and 2 are Num, Caps and Scroll Lock. Anything else is accepted
// and ignored.
int
inputwrite(struct inode *ip, uint64 off, char *src, int n)
{
static uint xleds;
int changed = 0;
for (int k = 0; k + 24 <= n; k += 24) {
uint16 type, code;
int32 value;
memmove(&type, src + k + 16, 2);
memmove(&code, src + k + 18, 2);
memmove(&value, src + k + 20, 4);
if (type != 0x11 || code > 2) // EV_LED
continue;
uint bit = code == 0 ? NUMLOCK : code == 1 ? CAPSLOCK : SCROLLLOCK;
xleds = value ? (xleds | bit) : (xleds & ~bit);
changed = 1;
}
if (changed)
kbd_set_leds(xleds);
return n;
}

View file

@ -1,6 +1,7 @@
#include "core/types.h"
#include "arch/x86.h"
#include "core/defs.h"
#include "core/spinlock.h"
#include "dev/kbd.h"
#include "uapi/posix.h"
#include "dev/vt.h" /* NVT -- how many terminals the chords reach */
@ -99,6 +100,104 @@ queue_utf8(uint32 cp)
return pend[pend_i++];
}
// --- the lock keys and their lights -------------------------------------------
//
// Caps, Num and Scroll Lock are one state for the machine, whichever keyboard
// pressed them -- the laptop's own and a USB one agree -- and the lights on
// every keyboard show it. They never did: caps lock worked and its light
// stayed dark. Under X the server keeps its own lock state and sets the
// lights through /dev/input (see inputwrite), so these are left to it while
// it holds the keyboard.
static uint kbd_locks; // CAPSLOCK | NUMLOCK | SCROLLLOCK
static uint lock_held; // lock keys down now: a held key repeats
static struct spinlock ledlock;
static int ledlock_ready;
// The PS/2 side is a two-byte command, 0xED and the lights, and each byte
// must be acknowledged (0xFA) before the next is sent. That cannot be waited
// for here -- this runs in the keyboard's interrupt, and the acknowledgement
// *is* a keyboard interrupt -- so the second byte goes out when kbdgetc sees
// the first one acknowledged.
static int led_want = -1; // PS/2 light byte still to send
static int led_stage; // 0 idle, 1 0xED sent, 2 the byte sent
static uint led_since;
static void
ps2_led_kick(void)
{
if (led_stage != 0 && ticks - led_since > HZ)
led_stage = 0; // no keyboard answered: start over
if (led_stage == 0 && led_want >= 0 && !(inb(KBSTATP) & 0x02)) {
outb(KBDATAP, 0xED);
led_stage = 1;
led_since = ticks;
}
}
// Show `locks` on every keyboard.
void
kbd_set_leds(uint locks)
{
if (!ledlock_ready) {
initlock(&ledlock, "kbdled");
ledlock_ready = 1;
}
acquire(&ledlock);
led_want = ((locks & SCROLLLOCK) ? 1 : 0) | ((locks & NUMLOCK) ? 2 : 0) |
((locks & CAPSLOCK) ? 4 : 0);
ps2_led_kick();
release(&ledlock);
// USB numbers them the other way round: Num, Caps, Scroll.
hid_leds_want(((locks & NUMLOCK) ? 1 : 0) | ((locks & CAPSLOCK) ? 2 : 0) |
((locks & SCROLLLOCK) ? 4 : 0));
}
// A lock key pressed, on any keyboard. `down` 0 is its release.
void
kbd_lock_key(uint lock, int down)
{
if (!down) {
lock_held &= ~lock;
return;
}
if (lock_held & lock)
return; // the keyboard's repeat, not a new press
lock_held |= lock;
kbd_locks ^= lock;
if (!input_is_grabbed(INPUT_KBD))
kbd_set_leds(kbd_locks);
}
uint
kbd_lock_state(void)
{
return kbd_locks;
}
// 0xFA and 0xFE from the keyboard: answers to a light command, not keys.
static int
ps2_led_answer(uint data)
{
if (led_stage == 0 || (data != 0xFA && data != 0xFE))
return 0;
if (!ledlock_ready)
return 0;
acquire(&ledlock);
if (data == 0xFE) {
led_stage = 0; // "resend": try again with the next change
} else if (led_stage == 1 && led_want >= 0) {
outb(KBDATAP, (uchar)led_want);
led_want = -1;
led_stage = 2;
} else {
led_stage = 0;
ps2_led_kick(); // changed again meanwhile
}
release(&ledlock);
return 1;
}
int
kbdgetc(void)
{
@ -119,6 +218,9 @@ kbdgetc(void)
data = inb(KBDATAP);
if(ps2_led_answer(data))
return 0;
if(data == 0xE0){
shift |= E0ESC;
return 0;
@ -137,9 +239,10 @@ kbdgetc(void)
if(data & 0x80){
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC);
if(togglecode[data]) kbd_lock_key(togglecode[data], 0);
} else {
shift |= shiftcode[data];
shift ^= togglecode[data];
if(togglecode[data]) kbd_lock_key(togglecode[data], 1);
}
return 0;
}
@ -147,6 +250,7 @@ kbdgetc(void)
if(data & 0x80){
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC);
if(togglecode[data]) kbd_lock_key(togglecode[data], 0);
return 0;
} else if(shift & E0ESC){
data |= 0x80;
@ -154,7 +258,9 @@ kbdgetc(void)
}
shift |= shiftcode[data];
shift ^= togglecode[data];
if(togglecode[data]) kbd_lock_key(togglecode[data], 1);
// The locks are the machine's, not this keyboard's: see kbd_lock_key().
shift = (shift & ~(CAPSLOCK | NUMLOCK | SCROLLLOCK)) | kbd_locks;
// The switch chord, before the character tables, because there is no
// character to look up: it is a combination that means "show me a different