Skip to content

meridian: brightness and battery, found by reading the firmware - #2

Merged
Asmodeus14 merged 1 commit into
masterfrom
terminal-browsing
Sep 5, 2026
Merged

Asmodeus14 merged 1 commit into
masterfrom
terminal-browsing

Conversation

@Asmodeus14

Copy link
Copy Markdown
Owner

Two hardware features that had never worked, plus the tooling that finally made them findable on a machine with no serial console.

BRIGHTNESS — the PWM registers were being read with the wrong layout. backlight.rs modelled 0xC8254 the pre-Gen9 way (period in bits 31:16, duty in 15:0). SKL/KBL use three separate 32-bit registers: CTL 0xC8250, FREQ 0xC8254, DUTY 0xC8258. Fedora on this laptop reports max_brightness=120000, which does not fit in 16 bits — so max read as 1 and every set wrote the duty into the frequency register. That single wrong constant is why PWM looked dead, which is what sent this project through the SMI mailbox and ACPI _BCM. Priority inverted: PWM first, ACPI as fallback.

BATTERY — raw EC reads, bypassing ACPI entirely. _BIF/_BST need an EmbeddedControl handler, and installing one walks the namespace, which #GPs this kernel. The EC ports are reachable, so we read the same registers the firmware's own AML reads. Ports 0x930/0x934 from the evaluated _CRS; register map decoded from ECG6/ECG9 in the DSDT, including the mandatory bank select at 0x03 without which the whole window reads zeros. Verified against Fedora: last-full 2131 mAh, design 4474 mAh, 11400 mV, 47% health — exact matches. Live in the Entity's Power row, refreshed by the thermal governor.

ACPI SAFETY — AML is never evaluated from a syscall now. SYSCALL runs at IF=0 and AcpiEvaluateObject can block; doing it there wedged core 0 and took scheduling down with it. Evaluation moved to the thermal governor (IF=1); syscalls are pure cache reads; brightness writes are queued.

FIXES IN c_stubs.rs, all silent and all long-standing:

  • ctype tables were all zeros, so isdigit/toupper were always wrong. That broke vsnprintf's vararg handling AND every by-name namespace lookup (AcpiNsInternalizeName uses toupper).
  • AcpiOsPrintf/AcpiOsVprintf were empty stubs — every ACPICA diagnostic ever produced was discarded.
  • AcpiOsFree was a no-op; ACPICA leaked every allocation. Fixing it naively exposed the global heap's O(n) free list and hung boot, so ACPICA now has its own O(1) size-class pool.
  • The OS layer (semaphores, locks, thread ids) was entirely no-ops.

DIAGNOSTICS, because the screen is the only channel:

  • ACPICA's log is connected; acpi log shows the real table load.
  • acpi ls walks the namespace one node per syscall — AcpiWalkNamespace #GPs this kernel, and stepping is what localised that.
  • WHY_KERNEL_GPF records the faulting IP in CMOS.
  • Panic screen reads PLANE_CTL tiling and swizzles CPU writes; the fault page was painting in stripes because the plane is tiled and the desktop reaches it via the GPU BLT.

TERMINAL — scrollback (PageUp/Down, Home/End, scrollbar) and the Meridian interior: JetBrains Mono 13px, the design's .term palette. Step 11 of the build order.

Also: OSD text was being appended to an already-flushed label batch, so the popup showed a moving bar with no words. Full ACPI dump (19 SSDTs) added under nyx-recv/ssdt.

Claude-Session: https://claude.ai/code/session_01W8QnRasHQfWg2KrnSxigtX

Two hardware features that had never worked, plus the tooling that
finally made them findable on a machine with no serial console.

BRIGHTNESS — the PWM registers were being read with the wrong layout.
backlight.rs modelled 0xC8254 the pre-Gen9 way (period in bits 31:16,
duty in 15:0). SKL/KBL use three separate 32-bit registers: CTL 0xC8250,
FREQ 0xC8254, DUTY 0xC8258. Fedora on this laptop reports
max_brightness=120000, which does not fit in 16 bits — so `max` read as
1 and every set wrote the duty into the frequency register. That single
wrong constant is why PWM looked dead, which is what sent this project
through the SMI mailbox and ACPI _BCM. Priority inverted: PWM first,
ACPI as fallback.

BATTERY — raw EC reads, bypassing ACPI entirely. _BIF/_BST need an
EmbeddedControl handler, and installing one walks the namespace, which
#GPs this kernel. The EC ports are reachable, so we read the same
registers the firmware's own AML reads. Ports 0x930/0x934 from the
evaluated _CRS; register map decoded from ECG6/ECG9 in the DSDT,
including the mandatory bank select at 0x03 without which the whole
window reads zeros. Verified against Fedora: last-full 2131 mAh, design
4474 mAh, 11400 mV, 47% health — exact matches. Live in the Entity's
Power row, refreshed by the thermal governor.

ACPI SAFETY — AML is never evaluated from a syscall now. SYSCALL runs at
IF=0 and AcpiEvaluateObject can block; doing it there wedged core 0 and
took scheduling down with it. Evaluation moved to the thermal governor
(IF=1); syscalls are pure cache reads; brightness writes are queued.

FIXES IN c_stubs.rs, all silent and all long-standing:
  - ctype tables were all zeros, so isdigit/toupper were always wrong.
    That broke vsnprintf's vararg handling AND every by-name namespace
    lookup (AcpiNsInternalizeName uses toupper).
  - AcpiOsPrintf/AcpiOsVprintf were empty stubs — every ACPICA
    diagnostic ever produced was discarded.
  - AcpiOsFree was a no-op; ACPICA leaked every allocation. Fixing it
    naively exposed the global heap's O(n) free list and hung boot, so
    ACPICA now has its own O(1) size-class pool.
  - The OS layer (semaphores, locks, thread ids) was entirely no-ops.

DIAGNOSTICS, because the screen is the only channel:
  - ACPICA's log is connected; `acpi log` shows the real table load.
  - `acpi ls` walks the namespace one node per syscall — AcpiWalkNamespace
    #GPs this kernel, and stepping is what localised that.
  - WHY_KERNEL_GPF records the faulting IP in CMOS.
  - Panic screen reads PLANE_CTL tiling and swizzles CPU writes; the
    fault page was painting in stripes because the plane is tiled and the
    desktop reaches it via the GPU BLT.

TERMINAL — scrollback (PageUp/Down, Home/End, scrollbar) and the
Meridian interior: JetBrains Mono 13px, the design's .term palette.
Step 11 of the build order.

Also: OSD text was being appended to an already-flushed label batch, so
the popup showed a moving bar with no words. Full ACPI dump (19 SSDTs)
added under nyx-recv/ssdt.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8QnRasHQfWg2KrnSxigtX
@Asmodeus14
Asmodeus14 merged commit 1105c22 into master Sep 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant