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

@ -835,6 +835,17 @@ static int kpi_system_state = 3; // SYSTEM_RUNNING
// A list_head: empty means pointing at itself, not zero.
static void *kpi_leds_list[2] = { kpi_leds_list, kpi_leds_list };
// --- calls that must not touch a register ---------------------------------------------
//
// preempt_schedule is reached through a thunk that saves every register, so
// its callers keep live values in caller-saved ones across the call. A
// logging stub makes a syscall, and `syscall` writes RFLAGS into r11: mdio-
// bus kept the PHY register it had just read in r11, and every MDIO read on
// the RTL8168H came back 0x0246 -- the PHY ID read as 0x02460246 and no
// driver matched it. Nothing to schedule here anyway; a bare ret.
__asm__(".text\n.globl kpi_ret\nkpi_ret:\n\tret\n");
extern void kpi_ret(void);
// --- the table ------------------------------------------------------------------------------------
extern int kpi_queue_delayed_work_on(int cpu, void *q, void *dwork, unsigned long delay);
@ -937,6 +948,12 @@ const struct kpi_sym kpi_base_table[] = {
{ "request_firmware", (void *)kpi_request_firmware },
{ "release_firmware", (void *)kpi_nop },
// must preserve every register
{ "__SCT__preempt_schedule", (void *)kpi_ret },
{ "__SCT__preempt_schedule_notrace", (void *)kpi_ret },
{ "preempt_schedule_thunk", (void *)kpi_ret },
{ "preempt_schedule_notrace_thunk", (void *)kpi_ret },
// must not be zero
{ "__list_add_valid_or_report", (void *)kpi_true },
{ "__list_del_entry_valid_or_report", (void *)kpi_true },