LDLS: every MDIO read was RFLAGS; ACPI: an Embedded Controller

On the TUF A15 Linux's r8169 found its PHY and read its ID as 0x02460246.
mdio-bus keeps the register it has just read in r11 across
preempt_enable(), which on x86 is `decl %gs:__preempt_count; je` to a
call that preserves every register. LDLS gave __preempt_count zeroes, so
the count reached zero and the call was made -- to a logging stub,
whose syscall left RFLAGS (0x246) in r11. __preempt_count now starts at
PREEMPT_ENABLED (x86 keeps NEED_RESCHED inverted), and preempt_schedule
and its notrace twin are a bare ret.

The same laptop reported its battery full and on AC, unplugged. The EC
owns both, and with no EmbeddedControl handler _REG never told the
firmware the EC was usable, so _BST and _PSR answered their fallbacks.
The EC is found by PNP0C09 or the ECDT and served by the ACPI 12.3
byte protocol, installed before _INI. SCI queries are not handled yet.

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 15:11:36 +01:00
commit d437e42b49
3 changed files with 179 additions and 1 deletions

View file

@ -264,7 +264,7 @@ resolve(void *ctx, const char *name, unsigned long long *out)
// bargain as the logging stubs, and the same caveat -- see the memcpy note in
// docs/architecture/ldls.md.
#define PERCPU_SLOTS 8
#define PERCPU_SLOTS 16
#define PERCPU_SIZE (16 * 1024)
static char percpu_mem[PERCPU_SLOTS][PERCPU_SIZE];
@ -294,6 +294,14 @@ percpu_for(void *ctx, const char *name)
*(unsigned long long *)percpu_mem[i] =
(unsigned long long)(void *)(percpu_mem[i] + 64);
puts_(" per-CPU "); puts_(name); puts_(" -- a task of zeroes\n");
} else if (streq(name, "__preempt_count")) {
// x86 keeps PREEMPT_NEED_RESCHED inverted in this word, so that
// `decl %gs:__preempt_count; je resched` is one instruction. Zero
// means "count zero *and* a reschedule wanted", and every
// preempt_enable() then called preempt_schedule -- see the note on
// __SCT__preempt_schedule in ldls/kpi/base.c for what that cost.
*(unsigned int *)percpu_mem[i] = 0x80000000u;
puts_(" per-CPU "); puts_(name); puts_(" -- PREEMPT_ENABLED\n");
} else {
puts_(" per-CPU "); puts_(name); puts_(" -- 16K of zeroes\n");
}