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

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