Date: Thu, 02 Jul 2026 23:42:52 +0000 From: Devin Teske <dteske@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: d1b62492f97f - main - acpi: ignore wake button press replayed by firmware on resume Message-ID: <6a46f77c.203c6.1c8a53af@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by dteske: URL: https://cgit.FreeBSD.org/src/commit/?id=d1b62492f97f5044b498fa4624e3a007a43efa6f commit d1b62492f97f5044b498fa4624e3a007a43efa6f Author: Devin Teske <dteske@FreeBSD.org> AuthorDate: 2026-06-20 16:17:40 +0000 Commit: Devin Teske <dteske@FreeBSD.org> CommitDate: 2026-07-02 23:39:13 +0000 acpi: ignore wake button press replayed by firmware on resume Some firmware delivers the power or sleep button press that woke the system as an ordinary button press (Notify 0x80) shortly after resume, rather than as the wakeup notification (Notify 0x02) the ACPI specification requires for a button that is also a wake source. On affected machines (e.g. the Framework Laptop 12, Intel Raptor Lake-P) the power button is a control-method device behind the embedded controller. The EC latches the key press that woke the system across the sleep transition and flushes it through its normal _Qxx query path as soon as it is reinitialized on resume. The replayed press is indistinguishable from a genuine one, so the kernel honors it as a fresh suspend request and the machine suspends again immediately after waking; it cannot be kept awake with the button. The event cannot be filtered at its source: it arrives over the same EC query path that also carries legitimate events (lid, AC, thermal, battery), so suppressing the drain would lose real notifications. Instead, record the time of resume and ignore a button-initiated suspend that arrives within a short grace window of it. The timestamp is taken before DEVICE_RESUME() re-initializes the EC, so it is set before the replay can be processed on the ACPICA notify taskqueue; otherwise the replay can be evaluated before the timestamp is written and slip through. Measured from that point, the replay lands at ~600 ms across many cycles on a Framework Laptop 12, whereas a deliberate press cannot occur that quickly -- it happens well after the display is back -- so a one-second window separates the two without ignoring real presses for any perceptible time. Spec-compliant firmware reports the wake as Notify 0x02, which is handled on a different path and never reaches this check, so there is no change in behavior on such systems. The replay window is a fixed compile-time constant rather than a tunable on purpose: it tracks a hardware characteristic -- the EC's post-resume replay latency -- not a user policy, so there is no value a user would meaningfully choose. PR: 296243 Reviewed by: adrian, imp (earlier revision), olce MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D57712 --- sys/dev/acpica/acpi.c | 43 +++++++++++++++++++++++++++++++++++++++++++ sys/dev/acpica/acpivar.h | 1 + 2 files changed, 44 insertions(+) diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index 0033ba734dc1..93451667a2b2 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -3747,6 +3747,13 @@ backout: slp_state &= ~ACPI_SS_GPE_SET; } if ((slp_state & ACPI_SS_DEV_SUSPEND) != 0) { + /* + * Record the resume time so a spurious power/sleep button press can be + * ignored for a grace period afterward (see the comment before + * ACPI_BUTTON_REPLAY_WINDOW). This must be taken before + * DEVICE_RESUME(), which re-initializes the EC that replays the press. + */ + sc->acpi_resume_sbt = getsbinuptime(); EVENTHANDLER_INVOKE(acpi_pre_dev_resume, stype); DEVICE_RESUME(root_bus); slp_state &= ~ACPI_SS_DEV_SUSPEND; @@ -4137,6 +4144,37 @@ acpi_system_eventhandler_wakeup(struct acpi_softc *const sc, return_VOID; } +/* + * Grace window after wakeup during which a power/sleep button press for suspend + * is ignored. Some firmware wrongly reports the depress that caused the wakeup + * as an "S0 Power/Sleep Button Pressed" notify (value 0x80) instead of the + * spec-required "Device Wake" notify (0x02); honoring it re-enters sleep + * immediately after resume. On the Framework Laptop 12 the replayed event + * arrives within ~620 ms of the recorded resume time, so a one-second window + * was chosen. See https://bugs.freebsd.org/296243 for the traces, timing + * data, and analysis. + */ +#define ACPI_BUTTON_REPLAY_WINDOW SBT_1S + +static bool +acpi_button_resume_replay(struct acpi_softc *sc, const char *which) +{ + sbintime_t elapsed; + + if (sc->acpi_resume_sbt == 0) + return (false); + elapsed = getsbinuptime() - sc->acpi_resume_sbt; + if (elapsed < 0 || elapsed >= ACPI_BUTTON_REPLAY_WINDOW) + return (false); + if (bootverbose) { + device_printf(sc->acpi_dev, + "ignoring %s button press %jd us after resume " + "(firmware replayed the wake event)\n", + which, (intmax_t)(elapsed / SBT_1US)); + } + return (true); +} + /* * ACPICA Event Handlers (FixedEvent, also called from button notify handler) */ @@ -4158,6 +4196,8 @@ acpi_event_power_button_sleep(struct acpi_softc *sc) ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); #if defined(__amd64__) || defined(__i386__) + if (acpi_button_resume_replay(sc, "power")) + return_VALUE (ACPI_INTERRUPT_HANDLED); if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, (ACPI_OSD_EXEC_CALLBACK)acpi_invoke_sleep_eventhandler, &sc->acpi_power_button_stype))) @@ -4186,6 +4226,9 @@ acpi_event_sleep_button_sleep(struct acpi_softc *sc) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + if (acpi_button_resume_replay(sc, "sleep")) + return_VALUE (ACPI_INTERRUPT_HANDLED); + if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, (ACPI_OSD_EXEC_CALLBACK)acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button_stype))) diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h index 9facbaea595a..27300a00b8d1 100644 --- a/sys/dev/acpica/acpivar.h +++ b/sys/dev/acpica/acpivar.h @@ -56,6 +56,7 @@ struct acpi_softc { int acpi_enabled; enum power_stype acpi_stype; int acpi_sleep_disabled; + sbintime_t acpi_resume_sbt; /* Uptime at last resume. */ /* Supported sleep states and types. */ bool acpi_supported_stypes[POWER_STYPE_COUNT];home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a46f77c.203c6.1c8a53af>
