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

@ -384,3 +384,145 @@ acpi_pci_route(void)
kinfo("acpi: %d more on a second I/O APIC, which is not driven; "
"those devices need MSI\n", beyond);
}
// --- batteries and adapters --------------------------------------------------
//
// A laptop's battery is an ACPI device, PNP0C0A, with methods that answer in
// packages: _BIX (or the older _BIF) for what it is -- capacities, units,
// names -- and _BST for how it is now. The adapter is ACPI0003 and _PSR says
// whether it is plugged in. Read on demand, from /sys/class/power_supply,
// which is where fastfetch and every battery widget look.
#define MAXPSU 4
static uacpi_namespace_node *psu_node[MAXPSU];
static int psu_isbat[MAXPSU];
static int npsu;
static uacpi_iteration_decision
found_psu(void *user, uacpi_namespace_node *node, uacpi_u32 depth)
{
(void)depth;
if (npsu < MAXPSU) {
psu_node[npsu] = node;
psu_isbat[npsu] = user != 0;
npsu++;
}
return UACPI_ITERATION_DECISION_CONTINUE;
}
static int psu_found;
static void
psu_find(void)
{
if (psu_found || !ready)
return;
psu_found = 1;
uacpi_find_devices("PNP0C0A", found_psu, (void *)1);
uacpi_find_devices("ACPI0003", found_psu, 0);
}
int
acpi_psu_count(void)
{
psu_find();
return npsu;
}
static uint32
pkg_int(uacpi_object_array *p, uacpi_size i)
{
uacpi_u64 v;
if (i >= p->count || uacpi_object_get_integer(p->objects[i], &v) != UACPI_STATUS_OK)
return ACPI_UNKNOWN;
return (uint32)v;
}
static void
pkg_str(uacpi_object_array *p, uacpi_size i, char *out, int cap)
{
uacpi_data_view d;
out[0] = 0;
if (i >= p->count ||
uacpi_object_get_string_or_buffer(p->objects[i], &d) != UACPI_STATUS_OK)
return;
int n = 0;
for (uacpi_size k = 0; k < d.length && n < cap - 1 && d.const_text[k]; k++)
if (d.const_text[k] >= ' ' && d.const_text[k] < 127)
out[n++] = d.const_text[k];
// Firmware pads with spaces.
while (n > 0 && out[n - 1] == ' ')
n--;
out[n] = 0;
}
int
acpi_psu_get(int i, struct acpi_psu *b)
{
uacpi_object *o;
uacpi_object_array p;
uacpi_u32 sta = 0xF;
psu_find();
if (i < 0 || i >= npsu)
return -1;
memset(b, 0, sizeof(*b));
b->battery = psu_isbat[i];
if (!b->battery) {
uacpi_u64 v = 0;
uacpi_eval_simple_integer(psu_node[i], "_PSR", &v);
b->online = v != 0;
return 0;
}
// Bit 4 of _STA: a battery is in the bay. A missing _STA means present.
uacpi_eval_sta(psu_node[i], &sta);
b->present = (sta & 0x10) != 0;
b->rate = b->remaining = b->voltage = ACPI_UNKNOWN;
b->design = b->full = b->design_voltage = b->cycles = ACPI_UNKNOWN;
if (!b->present)
return 0;
// _BIX: a revision first, then _BIF's fields shifted by one, with the
// cycle count after the design voltage's neighbours.
if (uacpi_eval_package(psu_node[i], "_BIX", 0, &o) == UACPI_STATUS_OK) {
if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) {
b->unit = (int)pkg_int(&p, 1);
b->design = pkg_int(&p, 2);
b->full = pkg_int(&p, 3);
b->technology = (int)pkg_int(&p, 4);
b->design_voltage = pkg_int(&p, 5);
b->cycles = pkg_int(&p, 8);
pkg_str(&p, 16, b->model, sizeof(b->model));
pkg_str(&p, 17, b->serial, sizeof(b->serial));
pkg_str(&p, 18, b->kind, sizeof(b->kind));
pkg_str(&p, 19, b->oem, sizeof(b->oem));
}
uacpi_object_unref(o);
} else if (uacpi_eval_package(psu_node[i], "_BIF", 0, &o) == UACPI_STATUS_OK) {
if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) {
b->unit = (int)pkg_int(&p, 0);
b->design = pkg_int(&p, 1);
b->full = pkg_int(&p, 2);
b->technology = (int)pkg_int(&p, 3);
b->design_voltage = pkg_int(&p, 4);
pkg_str(&p, 9, b->model, sizeof(b->model));
pkg_str(&p, 10, b->serial, sizeof(b->serial));
pkg_str(&p, 11, b->kind, sizeof(b->kind));
pkg_str(&p, 12, b->oem, sizeof(b->oem));
}
uacpi_object_unref(o);
}
if (uacpi_eval_package(psu_node[i], "_BST", 0, &o) == UACPI_STATUS_OK) {
if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) {
b->state = pkg_int(&p, 0);
b->rate = pkg_int(&p, 1);
b->remaining = pkg_int(&p, 2);
b->voltage = pkg_int(&p, 3);
}
uacpi_object_unref(o);
}
return 0;
}