Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 24 Jun 2026 02:14:49 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 296243] acpi(4): firmware-replayed wake button (Notify 0x80) causes resume->suspend loop; grace-window guard races device resume
Message-ID:  <bug-296243-227@https.bugs.freebsd.org/bugzilla/>

index | next in thread | raw e-mail

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=296243

            Bug ID: 296243
           Summary: acpi(4): firmware-replayed wake button (Notify 0x80)
                    causes resume->suspend loop; grace-window guard races
                    device resume
           Product: Base System
           Version: CURRENT
          Hardware: amd64
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: dteske@FreeBSD.org

Overview
--------
On the Framework Laptop 12 (the only hardware on which this has been observed),
the power/sleep button press that wakes the system from S3 is replayed by
firmware shortly after resume as an ordinary button press -- ACPI Notify value
0x80 -- instead of the wakeup notification (Notify 0x02) the ACPI specification
requires for a button that is also a wake source. The kernel
honors the replayed press as a fresh suspend request, so the machine suspends
again immediately after waking. It does not re-wake on its own -- each
occurrence follows a deliberate power-button wake -- but because every such
wake
reproduces the effect, the machine cannot practically be kept awake with the
power button.

While developing a guard for this (review D57712), instrumentation revealed a
second, subtler defect in the guard itself. The base kernel has no such guard;
it is introduced by D57712. The version of D57712 currently under review
records
the resume time AFTER device resume completes, but the replayed Notify is
delivered and evaluated on a different thread, concurrently with device resume
--
so the timestamp can be read before it has been written, defeating the guard.
The
correction is a revision to D57712 (the early-timestamp placement, below), not
a
change to any pre-existing base kernel code.

Affected hardware (observed)
----------------------------
- Framework Laptop 12, Intel Raptor Lake-P.
- Power button is a control-method device behind the embedded controller
  (Insyde firmware + a Framework fork of Chrome-EC).

Symptom / reproduction
----------------------
1. Boot FreeBSD-CURRENT (amd64).
2. Suspend: "acpiconf -s 3".
3. Wake with the power button.
4. The system resumes, then immediately suspends again -- the replayed wake
   press is honored as a fresh suspend request. (It does not re-wake by itself;
   pressing the power button again to wake reproduces the same result.)

Root cause
----------
The EC latches the physical key press that woke the system and flushes it
through
its normal _Qxx query path as soon as it is reinitialized on resume. It arrives
as a control-method Notify 0x80 on the power button device, indistinguishable
from a genuine press. Confirmed via instrumentation:

    acpi_button0: notify 0x80

Two threads are involved:

  - The resume thread runs acpi_EnterSleepState(), which performs the wake
    sequence: DEVICE_RESUME(root_bus) (re-initializing the EC, which drains
_Qxx
    and queues the replayed Notify), then EVENTHANDLER_INVOKE(power_resume),
etc.

  - The replayed Notify is dispatched ASYNCHRONOUSLY on the ACPICA notify
    taskqueue thread, where it converges at acpi_event_power_button_sleep() in
    sys/dev/acpica/acpi.c. Captured call path (kdb_backtrace at that point):

        acpi_button_resume_replay()
        acpi_event_power_button_sleep()
        acpi_task_execute()
        taskqueue_run_locked()
        taskqueue_thread_loop()
        fork_exit()

acpi_event_power_button_sleep() is both the registered fixed-feature power
button
handler AND the function the control-method notify path calls, so it is the
single convergence point for button-initiated sleep regardless of ACPI source
(fixed event vs. one or more namespace buttons). The existing post-wake
scrubber
in acpi.c (AcpiClearEvent(ACPI_EVENT_POWER_BUTTON), "cleared fixed power button
status", mandated for the Fixed Power Button) already neutralizes a
fixed-feature
replay; the spurious re-suspend manifests because this replay takes the
control-method Notify 0x80 path, which that scrubber does not cover.

Measurement reference points (important)
----------------------------------------
The replay's "latency after resume" depends entirely on WHERE in the resume
sequence the clock is started. Two placements for that timestamp were
evaluated.

The late timestamp (D57712 as currently under review) calls getsbinuptime() on
the resume thread AFTER EVENTHANDLER_INVOKE(power_resume) -- that is, after
device
resume has already completed. Replay latency measured from this point: ~162 ms.

This late timestamp is racy. The replay is queued earlier (during
DEVICE_RESUME)
and is evaluated on the taskqueue thread. If that thread runs before the resume
thread reaches the timestamp, the value is still unset when the guard evaluates
it. Proof (instrumented, late timestamp):

      acpi0: power button event: resume_sbt=0 now=646336359891
elapsed=150520810
             us window=500112 us -> pass        (guard saw resume_sbt=0)
      acpi0: recorded resume_sbt=651477099345   (timestamp written ~1.2 s
LATER)

Whether the original guard fired at all therefore depended on
taskqueue-vs-resume-thread scheduling.

The early timestamp (proposed revision to D57712) calls getsbinuptime() on the
resume thread BEFORE DEVICE_RESUME() -- that is, before the EC re-init that
drains
_Qxx. Replay latency measured from this point, across 7 cycles:

      cycle   elapsed
      1       620.2 ms
      2       609.1 ms
      3       593.2 ms
      4       605.2 ms
      5       600.2 ms
      6       608.2 ms
      7       608.2 ms
      min 593.2, max 620.2, mean 606.3, spread 27 ms

The early timestamp is race-free: it is always set before the replay can be
queued or evaluated. All 7 cycles were correctly recognized and suppressed; the
machine stayed awake every time.

The two numbers describe the SAME replay event from different reference points.
The early timestamp precedes the late one by the device-resume span (~0.45 s on
this machine), which is exactly why the same event measures ~0.6 s early versus
~0.16 s late. The move from D57712's submitted "~162 ms / 0.5 s window" to the
revised "~600 ms / ~1 s window" is entirely this change of reference point
within
the patch -- adopted to eliminate the race -- not a change in the hardware's
behavior nor a correction of an underestimate. Sizing the window from the
race-free (early) timestamp, ~1 s gives comfortable margin over the worst
observed
~620 ms while remaining well within the pre-usable resume period, so it ignores
no
genuine input.

Spec reference
--------------
ACPI 6.5, "Control Method Power Button": "Upon waking from a G1 sleeping state,
the AML event handler generates a notify command with the code of 0x2 to
indicate
it was responsible for waking the system." Spec-compliant firmware reports
0x02,
which takes a different path and never reaches this guard, so the change is
inert
on compliant systems.

Cross-vendor scope
------------------
Per testing reported on D57712 (seuros): of ~12 vendor platforms plus several
coreboot machines, none replay the wake as 0x80; the behavior appears specific
to
this EC today, though other non-compliant firmware could land on the same path.

Proposed fix
------------
1. Record the resume timestamp before DEVICE_RESUME() (the early,
deterministic,
   race-free placement).
2. Ignore a button-initiated suspend within a grace window (~1 s, sized from
the
   measured replay latency from the early timestamp) of that timestamp;
   self-clearing, inert on compliant firmware.
Tracked in review https://reviews.freebsd.org/D57712.

A follow-up refactor (suggested by olce) to move the
acpi_event_{power,sleep}_button_{sleep,wakeup}() functions from acpi.c into
acpi_button.c, with an in-flight guard for concurrent button sources, is
orthogonal and can be done separately.

-- 
You are receiving this mail because:
You are the assignee for the bug.

home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-296243-227>