* Re: [PATCH v2 6/6] efi/runtime-wrappers: retire the worker if a wedged call ever returns
From: Ard Biesheuvel @ 2026-06-12 11:11 UTC (permalink / raw)
To: Breno Leitao, Ilias Apalodimas, Borislav Petkov, Andy Lutomirski,
Kees Cook, Tony Luck, Guilherme G. Piccoli, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H . Peter Anvin
Cc: linux-efi, linux-kernel, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-6-f714bb016df6@debian.org>
Hi,
On Fri, 12 Jun 2026, at 13:01, Breno Leitao wrote:
> When __efi_queue_work() times out it disables runtime services and
> returns, but the kworker is still blocked inside firmware. If the
> firmware eventually unblocks, efi_call_rts() would run its tail on an
> efi_rts_work that the timed-out caller has long abandoned: signalling a
> stale completion and clearing efi_runtime_lock_owner that may by then
> belong to another caller.
>
> If runtime services have been disabled by the time the call returns,
> park the worker in a schedule() loop instead, so it never touches
> efi_rts_work again or returns to the workqueue.
>
> x86's efi_crash_gracefully_on_page_fault() already ends in the same loop
> for the page-fault case. Factor it into efi_rts_park_worker() and call it
> from both paths.
>
> Suggested-by: Ard Biesheuvel <ardb@kernel.org>
> Signed-off-by: Breno Leitao <leitao@debian.org>
> ---
> arch/x86/platform/efi/quirks.c | 9 +--------
> drivers/firmware/efi/runtime-wrappers.c | 21 +++++++++++++++++++++
> include/linux/efi.h | 2 ++
> 3 files changed, 24 insertions(+), 8 deletions(-)
>
> diff --git a/arch/x86/platform/efi/quirks.c
> b/arch/x86/platform/efi/quirks.c
> index 90a065fcb1fab..02c56a02eb9bc 100644
> --- a/arch/x86/platform/efi/quirks.c
> +++ b/arch/x86/platform/efi/quirks.c
> @@ -832,12 +832,5 @@ void efi_crash_gracefully_on_page_fault(unsigned
> long phys_addr,
> clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
> pr_info("Froze efi_rts_wq and disabled EFI Runtime Services\n");
>
> - /*
> - * Call schedule() in an infinite loop, so that any spurious wake ups
> - * will never run efi_rts_wq again.
> - */
> - for (;;) {
> - set_current_state(TASK_IDLE);
> - schedule();
> - }
> + efi_rts_park_worker();
> }
> diff --git a/drivers/firmware/efi/runtime-wrappers.c
> b/drivers/firmware/efi/runtime-wrappers.c
> index 842f72d44211f..998e5f8f1c62c 100644
> --- a/drivers/firmware/efi/runtime-wrappers.c
> +++ b/drivers/firmware/efi/runtime-wrappers.c
> @@ -219,6 +219,19 @@ static struct task_struct *efi_runtime_lock_owner;
> extern struct semaphore __efi_uv_runtime_lock
> __alias(efi_runtime_lock);
> #endif
>
> +/*
> + * Park a worker that must never run efi_rts_wq again: EFI runtime services
> + * have been disabled and its efi_rts_work is abandoned. Loop in schedule()
> + * so a spurious wakeup cannot resume it.
> + */
> +void efi_rts_park_worker(void)
> +{
> + for (;;) {
> + set_current_state(TASK_IDLE);
> + schedule();
> + }
> +}
> +
> /*
> * Calls the appropriate efi_runtime_service() with the appropriate
> * arguments.
> @@ -320,6 +333,14 @@ static void __nocfi efi_call_rts(struct work_struct *work)
> efi_call_virt_check_flags(flags, efi_rts_work.caller);
> arch_efi_call_virt_teardown();
>
> + /*
> + * If __efi_queue_work() timed out and disabled runtime services, the
> + * caller is gone and efi_rts_work is no longer ours: park the worker
> + * so it never signals the stale completion or runs again.
> + */
> + if (!efi_enabled(EFI_RUNTIME_SERVICES))
> + efi_rts_park_worker();
> +
So one thing to note here is that the arm64 version of the exception recovery
(in efi_runtime_fixup_exception()) will also clear EFI_RUNTIME_SERVICES, and
that will occur before this check. This means we will park the worker not only
on a time out, but also on an sync exception in the firmware.
I suspect this is actually what we want, but it deserves to be called out.
> efi_rts_work.status = status;
> complete(&efi_rts_work.efi_rts_comp);
> efi_runtime_lock_owner = NULL;
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 24221a8424121..015505423277e 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -1256,6 +1256,8 @@ extern struct efi_runtime_work efi_rts_work;
> /* Workqueue to queue EFI Runtime Services */
> extern struct workqueue_struct *efi_rts_wq;
>
> +void efi_rts_park_worker(void);
> +
> struct linux_efi_memreserve {
> int size; // allocated size of the array
> atomic_t count; // number of entries used
>
> --
> 2.53.0-Meta
^ permalink raw reply
* [PATCH v2 6/6] efi/runtime-wrappers: retire the worker if a wedged call ever returns
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-0-f714bb016df6@debian.org>
When __efi_queue_work() times out it disables runtime services and
returns, but the kworker is still blocked inside firmware. If the
firmware eventually unblocks, efi_call_rts() would run its tail on an
efi_rts_work that the timed-out caller has long abandoned: signalling a
stale completion and clearing efi_runtime_lock_owner that may by then
belong to another caller.
If runtime services have been disabled by the time the call returns,
park the worker in a schedule() loop instead, so it never touches
efi_rts_work again or returns to the workqueue.
x86's efi_crash_gracefully_on_page_fault() already ends in the same loop
for the page-fault case. Factor it into efi_rts_park_worker() and call it
from both paths.
Suggested-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
---
arch/x86/platform/efi/quirks.c | 9 +--------
drivers/firmware/efi/runtime-wrappers.c | 21 +++++++++++++++++++++
include/linux/efi.h | 2 ++
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
index 90a065fcb1fab..02c56a02eb9bc 100644
--- a/arch/x86/platform/efi/quirks.c
+++ b/arch/x86/platform/efi/quirks.c
@@ -832,12 +832,5 @@ void efi_crash_gracefully_on_page_fault(unsigned long phys_addr,
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
pr_info("Froze efi_rts_wq and disabled EFI Runtime Services\n");
- /*
- * Call schedule() in an infinite loop, so that any spurious wake ups
- * will never run efi_rts_wq again.
- */
- for (;;) {
- set_current_state(TASK_IDLE);
- schedule();
- }
+ efi_rts_park_worker();
}
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 842f72d44211f..998e5f8f1c62c 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -219,6 +219,19 @@ static struct task_struct *efi_runtime_lock_owner;
extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock);
#endif
+/*
+ * Park a worker that must never run efi_rts_wq again: EFI runtime services
+ * have been disabled and its efi_rts_work is abandoned. Loop in schedule()
+ * so a spurious wakeup cannot resume it.
+ */
+void efi_rts_park_worker(void)
+{
+ for (;;) {
+ set_current_state(TASK_IDLE);
+ schedule();
+ }
+}
+
/*
* Calls the appropriate efi_runtime_service() with the appropriate
* arguments.
@@ -320,6 +333,14 @@ static void __nocfi efi_call_rts(struct work_struct *work)
efi_call_virt_check_flags(flags, efi_rts_work.caller);
arch_efi_call_virt_teardown();
+ /*
+ * If __efi_queue_work() timed out and disabled runtime services, the
+ * caller is gone and efi_rts_work is no longer ours: park the worker
+ * so it never signals the stale completion or runs again.
+ */
+ if (!efi_enabled(EFI_RUNTIME_SERVICES))
+ efi_rts_park_worker();
+
efi_rts_work.status = status;
complete(&efi_rts_work.efi_rts_comp);
efi_runtime_lock_owner = NULL;
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 24221a8424121..015505423277e 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1256,6 +1256,8 @@ extern struct efi_runtime_work efi_rts_work;
/* Workqueue to queue EFI Runtime Services */
extern struct workqueue_struct *efi_rts_wq;
+void efi_rts_park_worker(void);
+
struct linux_efi_memreserve {
int size; // allocated size of the array
atomic_t count; // number of entries used
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 5/6] efi/runtime-wrappers: honour EFI_RUNTIME_SERVICES in the non-blocking paths
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-0-f714bb016df6@debian.org>
Three wrappers call firmware directly instead of going through
__efi_queue_work(), and none of them check whether runtime services are
still enabled: virt_efi_set_variable_nb(),
virt_efi_query_variable_info_nb() and virt_efi_reset_system(). Once a
hang has cleared EFI_RUNTIME_SERVICES - or efi_recover_from_page_fault()
has cleared it on a firmware page fault - these paths still enter the
(possibly wedged) firmware, e.g. an EFI pstore write through the
non-blocking SetVariable() variant, in violation of UEFI's
non-reentrancy rules. reset_system() is reachable too: efi_reboot()
only gates it on the static efi_rt_services_supported() mask, which does
not track the runtime disable.
Check efi_enabled(EFI_RUNTIME_SERVICES) at the top of each before taking
efi_runtime_lock and calling into firmware.
Suggested-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/runtime-wrappers.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 8badf0419a148..842f72d44211f 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -461,6 +461,9 @@ virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr,
{
efi_status_t status;
+ if (!efi_enabled(EFI_RUNTIME_SERVICES))
+ return EFI_DEVICE_ERROR;
+
if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY;
@@ -500,6 +503,9 @@ virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space,
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED;
+ if (!efi_enabled(EFI_RUNTIME_SERVICES))
+ return EFI_DEVICE_ERROR;
+
if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY;
@@ -527,6 +533,11 @@ static void __nocfi
virt_efi_reset_system(int reset_type, efi_status_t status,
unsigned long data_size, efi_char16_t *data)
{
+ if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+ pr_warn("EFI Runtime Services are disabled, not invoking reset_system()\n");
+ return;
+ }
+
if (down_trylock(&efi_runtime_lock)) {
pr_warn("failed to invoke the reset_system() runtime service:\n"
"could not get exclusive access to the firmware\n");
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 4/6] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-0-f714bb016df6@debian.org>
When an EFI runtime service hangs in firmware, the efi_rts_wq worker is
stuck inside the call and cannot be cancelled. __efi_queue_work() then
waits on the completion forever while holding efi_runtime_lock, so every
later EFI caller is wedged until reboot; the only symptom is a "workqueue
lockup" and tasks piling up on the semaphore.
Replace wait_for_completion() with wait_for_completion_timeout() bounded
by EFI_RTS_TIMEOUT (120 seconds). On timeout, clear EFI_RUNTIME_SERVICES
and return EFI_ABORTED so later callers fail fast at the entry check
instead of each paying another 120 seconds. The wedged worker is
intentionally leaked and keeps ownership of efi_rts_work.
Known limitation: the efi_rts_args the worker holds points into the
caller's stack frame; if firmware unblocks after the timeout and writes
the output buffers, they land in reused memory. Firmware hung this long
rarely recovers; a follow-up could bounce the buffers through kmalloc.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/runtime-wrappers.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 0cd350760446c..8badf0419a148 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -118,6 +118,14 @@ union efi_rts_args {
struct efi_runtime_work efi_rts_work;
+/*
+ * Upper bound on how long we wait for a single EFI runtime service
+ * call to finish before declaring firmware wedged. Chosen to be longer
+ * than any plausible legitimate call (including UpdateCapsule on slow
+ * SPI-NOR) while still bounding userspace wait time.
+ */
+#define EFI_RTS_TIMEOUT (120 * HZ)
+
/*
* efi_queue_work: Queue EFI runtime service call and wait for completion
* @_rts: EFI runtime service function identifier
@@ -342,7 +350,13 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
goto exit;
}
- wait_for_completion(&efi_rts_work.efi_rts_comp);
+ if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
+ EFI_RTS_TIMEOUT)) {
+ pr_err("EFI runtime service %d wedged in firmware; disabling EFI runtime services\n",
+ id);
+ clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+ return EFI_ABORTED;
+ }
WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED);
exit:
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 2/6] efi/runtime-wrappers: handle queue_work() failure with goto exit
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-0-f714bb016df6@debian.org>
Convert the queue_work() failure path in __efi_queue_work() to a
goto exit instead of falling through to the wait and the
WARN_ON_ONCE(status == EFI_ABORTED) below it. A failed queue_work()
leaves the status at its initial EFI_ABORTED, so that warning would
fire even though no call ran; it is meant for a completed call that
returned EFI_ABORTED.
No change for the common (successful enqueue) path. This also prepares
__efi_queue_work() for the timeout handling added later.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/runtime-wrappers.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index da8d296216441..ebeec87ed0b0a 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -338,10 +338,12 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
* queue_work() returns 0 if work was already on queue,
* _ideally_ this should never happen.
*/
- if (queue_work(efi_rts_wq, &efi_rts_work.work))
- wait_for_completion(&efi_rts_work.efi_rts_comp);
- else
+ if (!queue_work(efi_rts_wq, &efi_rts_work.work)) {
pr_err("Failed to queue work to efi_rts_wq.\n");
+ goto exit;
+ }
+
+ wait_for_completion(&efi_rts_work.efi_rts_comp);
WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED);
exit:
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 3/6] efi/runtime-wrappers: check EFI_RUNTIME_SERVICES before using efi_rts_work
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-0-f714bb016df6@debian.org>
Move the EFI_RUNTIME_SERVICES check to the top of __efi_queue_work() and
return directly, so a caller that finds runtime services disabled returns
without touching the shared efi_rts_work. No functional change.
This prepares for bounding the wait, where a timeout will clear
EFI_RUNTIME_SERVICES while the leaked worker still owns efi_rts_work; a
later caller must then bail out before reinitialising it.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/runtime-wrappers.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index ebeec87ed0b0a..0cd350760446c 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -320,17 +320,16 @@ static void __nocfi efi_call_rts(struct work_struct *work)
static efi_status_t __efi_queue_work(enum efi_rts_ids id,
union efi_rts_args *args)
{
+ if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+ pr_warn_once("EFI Runtime Services are disabled!\n");
+ return EFI_DEVICE_ERROR;
+ }
+
efi_rts_work.efi_rts_id = id;
efi_rts_work.args = args;
efi_rts_work.caller = __builtin_return_address(0);
efi_rts_work.status = EFI_ABORTED;
- if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
- pr_warn_once("EFI Runtime Services are disabled!\n");
- efi_rts_work.status = EFI_DEVICE_ERROR;
- goto exit;
- }
-
init_completion(&efi_rts_work.efi_rts_comp);
INIT_WORK(&efi_rts_work.work, efi_call_rts);
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 0/6] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
When an EFI runtime service call hangs in firmware, the kworker on
efi_rts_wq is stuck inside the firmware call and cannot be cancelled.
The kernel currently waits indefinitely on the completion, and the
caller holds efi_runtime_lock for the duration, so every subsequent
EFI runtime caller (efivarfs, NVRAM writes, set_wakeup_time, ACPI PRM
handlers, ...) is wedged until reboot. The only externally visible
symptom is a "workqueue lockup" message and userspace processes
piling up uninterruptibly on the semaphore.
A real example from one of our NVIDIA Grace hosts:
BUG: workqueue lockup - pool cpus=28 node=0 flags=0x0 nice=0 stuck for 127s!
...
CPU: 28 PID: 590 Comm: kworker/u288:6
Workqueue: efi_rts_wq efi_call_rts
Call trace:
0x4052f11ecc (P)
0x4052f10ed4
...
__efi_rt_asm_wrapper+0x50/0x78
efi_call_rts+0x178/0x240
process_scheduled_works+0x17c/0x420
worker_thread+0x184/0x4d8
kthread+0xcc/0x1f8
ret_from_fork+0x10/0x20
PC and LR are inside EFI runtime services firmware memory; firmware
never returned; the worker stayed stuck across the 127s / 157s / 188s
"workqueue lockup" reports until external monitoring eventually rebooted
the host.
This series doesn't fix the firmware bug - that's vendor territory -
but it stops one stuck EFI call from taking the rest of userspace
down with it, and turns a generic stalled-task mystery into an
unambiguous "EFI firmware is at fault" signal in dmesg, which is
especially valuable at fleet scale where the same symptom could
otherwise be attributed to dozens of unrelated stalls.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Changes in v2:
- Drop v1's efi_rts_dead flag; reuse the existing EFI_RUNTIME_SERVICES bit
(cleared on timeout) and return EFI_ABORTED instead of EFI_TIMEOUT (per Ard).
- Also guard the non-blocking paths (set_variable/query_variable_info/reset_system)
and park the leaked worker via a shared efi_rts_park_worker() reused by x86's
page-fault handler;
- Split into smaller prep patches.
- Link to v1: https://lore.kernel.org/r/20260609-efi_timeout-v1-0-69a896faa805@debian.org
---
Breno Leitao (6):
efi: fix stale reference to efi_recover_from_page_fault()
efi/runtime-wrappers: handle queue_work() failure with goto exit
efi/runtime-wrappers: check EFI_RUNTIME_SERVICES before using efi_rts_work
efi/runtime-wrappers: bound the wait for EFI runtime service calls
efi/runtime-wrappers: honour EFI_RUNTIME_SERVICES in the non-blocking paths
efi/runtime-wrappers: retire the worker if a wedged call ever returns
arch/x86/platform/efi/quirks.c | 9 +----
drivers/firmware/efi/runtime-wrappers.c | 65 ++++++++++++++++++++++++++++-----
include/linux/efi.h | 6 ++-
3 files changed, 61 insertions(+), 19 deletions(-)
---
base-commit: a87737435cfa134f9cdcc696ba3080759d04cf72
change-id: 20260609-efi_timeout-6f51d5bbcfb7
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply
* [PATCH v2 1/6] efi: fix stale reference to efi_recover_from_page_fault()
From: Breno Leitao @ 2026-06-12 11:01 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Borislav Petkov,
Andy Lutomirski, Kees Cook, Tony Luck, Guilherme G. Piccoli,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260612-efi_timeout-v2-0-f714bb016df6@debian.org>
efi_recover_from_page_fault() was renamed to
efi_crash_gracefully_on_page_fault(), but the comment above enum
efi_rts_ids was not updated. Use the current name.
Fixes: c46f52231e79 ("x86/{fault,efi}: Fix and rename efi_recover_from_page_fault()")
Signed-off-by: Breno Leitao <leitao@debian.org>
---
include/linux/efi.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ccbc35479684a..24221a8424121 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1212,8 +1212,8 @@ efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
/*
* efi_runtime_service() function identifiers.
- * "NONE" is used by efi_recover_from_page_fault() to check if the page
- * fault happened while executing an efi runtime service.
+ * "NONE" is used by efi_crash_gracefully_on_page_fault() to check if the
+ * page fault happened while executing an efi runtime service.
*/
enum efi_rts_ids {
EFI_NONE,
--
2.53.0-Meta
^ permalink raw reply related
* Re: [PATCH 1/2] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Breno Leitao @ 2026-06-12 10:28 UTC (permalink / raw)
To: Ard Biesheuvel; +Cc: Ilias Apalodimas, linux-efi, linux-kernel, kernel-team
In-Reply-To: <f8c9be21-f15c-4c97-8dfb-de083e07dd32@app.fastmail.com>
Hi Ard,
On Thu, Jun 11, 2026 at 12:57:50PM +0200, Ard Biesheuvel wrote:
> > Could we just clear the EFI_RUNTIME_SERVICES bit here right away? That
> > way, we probably won't need the second patch (unless I'm mistaken). It
> > /should/ also block the calls that are not routed via the workqueue,
> > e.g., any EFI pstore calls to the non-blocking SetVariable() variant,
> > but I just noticed that we never check EFI_RUNTIME_SERVICES on those
> > code paths, which is probably a bug.
> >
> > And please return EFI_ABORTED rather than EFI_TIMEOUT - probably doesn't
> > matter in practice but I'd like to avoid introducing more EFI return codes
> > in the runtime context that the spec mentions only for boot services stuff.
>
> Also, could we prevent the kthread that runs the workqueue from being scheduled
> again if we decide that the runtime services are wedged?
Sure, I can park it at for (;;) schedule()). Maybe with a helper?!
> x86 has some logic for this when a page fault occurs, and I wonder if
> there is a generic way to do something similar.
I have no clue, to be honest. If you have something specific in mind,
I'll dig in.
Thanks for the review,
--breno
^ permalink raw reply
* Re: [PATCH 1/2] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Breno Leitao @ 2026-06-12 10:05 UTC (permalink / raw)
To: Ard Biesheuvel; +Cc: Ilias Apalodimas, linux-efi, linux-kernel, kernel-team
In-Reply-To: <71de953a-43e9-4987-8b8e-00d9b99a099e@app.fastmail.com>
On Thu, Jun 11, 2026 at 12:21:42PM +0200, Ard Biesheuvel wrote:
> Hi Breno,
>
> On Tue, 9 Jun 2026, at 13:55, Breno Leitao wrote:
> > When an EFI runtime service call hangs in firmware, the kworker on
> > efi_rts_wq is stuck inside the firmware call and cannot be cancelled.
> > The kernel currently waits indefinitely on the completion, and the
> > caller holds efi_runtime_lock for the duration, so every subsequent
> > EFI runtime caller (efivarfs, NVRAM writes, set_wakeup_time, ACPI PRM
> > handlers, ...) is wedged until reboot. The only externally visible
> > symptom is a "workqueue lockup" message and userspace processes piling
> > up uninterruptibly on the semaphore.
> >
> > Replace the indefinite wait_for_completion() in __efi_queue_work()
> > with wait_for_completion_timeout() bounded by EFI_RTS_TIMEOUT
> > (120 seconds). On timeout, log the wedged runtime service id and
> > return EFI_TIMEOUT to the caller.
> >
> > The wedged worker is intentionally leaked - it is still inside
> > firmware and cannot be cancelled - and the shared efi_rts_work is
> > abandoned to it. EFI runtime services become unavailable until reboot;
> > the alternative is permanent userspace hang.
> >
> > This patch should land together with the follow-up that introduces
> > the efi_rts_dead fast-fail flag: between the two, a subsequent
> > __efi_queue_work() caller will re-INIT_WORK() and re-init_completion()
> > on the work_struct and completion that the leaked worker still owns,
> > which can corrupt workqueue state and let the next caller observe
> > the leaked call's status as if it were its own. The split exists for
> > review clarity; reviewers may prefer to squash.
> >
> > Known limitation: the union efi_rts_args that __efi_queue_work() hands
> > to the worker contains pointers into the caller's stack frame (the
> > compound literal in efi_queue_work() and the in/out buffers it points
> > to, e.g. *tm in GetTime). Once the caller returns -EIO and unwinds,
> > those slots are reusable. If firmware eventually unblocks and writes
> > to the output buffers after the timeout has fired, the writes land in
> > whatever now occupies that memory. In practice firmware that hangs for
> > more than 120 seconds tends to stay hung. A follow-up bouncing args
> > and output buffers through kmalloc would close this gap.
> >
> > At fleet scale this turns a generic "workqueue lockup" or stalled-task
> > mystery into an unambiguous "EFI firmware is at fault" signal in
> > dmesg.
> >
> > Signed-off-by: Breno Leitao <leitao@debian.org>
> > ---
> > drivers/firmware/efi/runtime-wrappers.c | 20 +++++++++++++++++---
> > 1 file changed, 17 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/firmware/efi/runtime-wrappers.c
> > b/drivers/firmware/efi/runtime-wrappers.c
> > index da8d29621644..6ce6d094066e 100644
> > --- a/drivers/firmware/efi/runtime-wrappers.c
> > +++ b/drivers/firmware/efi/runtime-wrappers.c
> > @@ -118,6 +118,14 @@ union efi_rts_args {
> >
> > struct efi_runtime_work efi_rts_work;
> >
> > +/*
> > + * Upper bound on how long we wait for a single EFI runtime service
> > + * call to finish before declaring firmware wedged. Chosen to be longer
> > + * than any plausible legitimate call (including UpdateCapsule on slow
> > + * SPI-NOR) while still bounding userspace wait time.
> > + */
> > +#define EFI_RTS_TIMEOUT (120 * HZ)
> > +
> > /*
> > * efi_queue_work: Queue EFI runtime service call and wait for completion
> > * @_rts: EFI runtime service function identifier
> > @@ -338,10 +346,16 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
> > * queue_work() returns 0 if work was already on queue,
> > * _ideally_ this should never happen.
> > */
> > - if (queue_work(efi_rts_wq, &efi_rts_work.work))
> > - wait_for_completion(&efi_rts_work.efi_rts_comp);
> > - else
> > + if (!queue_work(efi_rts_wq, &efi_rts_work.work)) {
> > pr_err("Failed to queue work to efi_rts_wq.\n");
> > + goto exit;
> > + }
> > +
> > + if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
> > + EFI_RTS_TIMEOUT)) {
> > + pr_err("EFI runtime service %d wedged in firmware\n", id);
> > + return EFI_TIMEOUT;
>
> Could we just clear the EFI_RUNTIME_SERVICES bit here right away?
Ack. This is better than createing a new variable. Thanks!
I am planing to move that entry check ahead of the efi_rts_work
assignments (its own small prep patch), Otherwise a post-timeout caller
would overwrite efi_rts_work.efi_rts_id and reset it to EFI_NONE on the
way out.
> It /should/ also block the calls that are not routed via
> the workqueue, e.g., any EFI pstore calls to the non-blocking
> SetVariable() variant, but I just noticed that we never check
> EFI_RUNTIME_SERVICES on those code paths, which is probably a bug.
Confirmed, fixed as a separate patch: virt_efi_set_variable_nb(),
virt_efi_query_variable_info_nb() and virt_efi_reset_system() now check
efi_enabled(EFI_RUNTIME_SERVICES) before calling firmware.
> And please return EFI_ABORTED rather than EFI_TIMEOUT - probably
> doesn't matter in practice but I'd like to avoid introducing more EFI
> return codes in the runtime context that the spec mentions only for
> boot services stuff.
Ack. The timeout path returns EFI_ABORTED, matching what
efi_recover_from_page_fault() already reports
Thanks for the review,
--breno
^ permalink raw reply
* Re: [PATCH 1/2] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Ard Biesheuvel @ 2026-06-11 10:57 UTC (permalink / raw)
To: Breno Leitao, Ilias Apalodimas; +Cc: linux-efi, linux-kernel, kernel-team
In-Reply-To: <71de953a-43e9-4987-8b8e-00d9b99a099e@app.fastmail.com>
On Thu, 11 Jun 2026, at 12:21, Ard Biesheuvel wrote:
> Hi Breno,
>
> On Tue, 9 Jun 2026, at 13:55, Breno Leitao wrote:
>> When an EFI runtime service call hangs in firmware, the kworker on
>> efi_rts_wq is stuck inside the firmware call and cannot be cancelled.
>> The kernel currently waits indefinitely on the completion, and the
>> caller holds efi_runtime_lock for the duration, so every subsequent
>> EFI runtime caller (efivarfs, NVRAM writes, set_wakeup_time, ACPI PRM
>> handlers, ...) is wedged until reboot. The only externally visible
>> symptom is a "workqueue lockup" message and userspace processes piling
>> up uninterruptibly on the semaphore.
>>
>> Replace the indefinite wait_for_completion() in __efi_queue_work()
>> with wait_for_completion_timeout() bounded by EFI_RTS_TIMEOUT
>> (120 seconds). On timeout, log the wedged runtime service id and
>> return EFI_TIMEOUT to the caller.
>>
>> The wedged worker is intentionally leaked - it is still inside
>> firmware and cannot be cancelled - and the shared efi_rts_work is
>> abandoned to it. EFI runtime services become unavailable until reboot;
>> the alternative is permanent userspace hang.
>>
>> This patch should land together with the follow-up that introduces
>> the efi_rts_dead fast-fail flag: between the two, a subsequent
>> __efi_queue_work() caller will re-INIT_WORK() and re-init_completion()
>> on the work_struct and completion that the leaked worker still owns,
>> which can corrupt workqueue state and let the next caller observe
>> the leaked call's status as if it were its own. The split exists for
>> review clarity; reviewers may prefer to squash.
>>
>> Known limitation: the union efi_rts_args that __efi_queue_work() hands
>> to the worker contains pointers into the caller's stack frame (the
>> compound literal in efi_queue_work() and the in/out buffers it points
>> to, e.g. *tm in GetTime). Once the caller returns -EIO and unwinds,
>> those slots are reusable. If firmware eventually unblocks and writes
>> to the output buffers after the timeout has fired, the writes land in
>> whatever now occupies that memory. In practice firmware that hangs for
>> more than 120 seconds tends to stay hung. A follow-up bouncing args
>> and output buffers through kmalloc would close this gap.
>>
>> At fleet scale this turns a generic "workqueue lockup" or stalled-task
>> mystery into an unambiguous "EFI firmware is at fault" signal in
>> dmesg.
>>
>> Signed-off-by: Breno Leitao <leitao@debian.org>
>> ---
>> drivers/firmware/efi/runtime-wrappers.c | 20 +++++++++++++++++---
>> 1 file changed, 17 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/firmware/efi/runtime-wrappers.c
>> b/drivers/firmware/efi/runtime-wrappers.c
>> index da8d29621644..6ce6d094066e 100644
>> --- a/drivers/firmware/efi/runtime-wrappers.c
>> +++ b/drivers/firmware/efi/runtime-wrappers.c
>> @@ -118,6 +118,14 @@ union efi_rts_args {
>>
>> struct efi_runtime_work efi_rts_work;
>>
>> +/*
>> + * Upper bound on how long we wait for a single EFI runtime service
>> + * call to finish before declaring firmware wedged. Chosen to be longer
>> + * than any plausible legitimate call (including UpdateCapsule on slow
>> + * SPI-NOR) while still bounding userspace wait time.
>> + */
>> +#define EFI_RTS_TIMEOUT (120 * HZ)
>> +
>> /*
>> * efi_queue_work: Queue EFI runtime service call and wait for completion
>> * @_rts: EFI runtime service function identifier
>> @@ -338,10 +346,16 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
>> * queue_work() returns 0 if work was already on queue,
>> * _ideally_ this should never happen.
>> */
>> - if (queue_work(efi_rts_wq, &efi_rts_work.work))
>> - wait_for_completion(&efi_rts_work.efi_rts_comp);
>> - else
>> + if (!queue_work(efi_rts_wq, &efi_rts_work.work)) {
>> pr_err("Failed to queue work to efi_rts_wq.\n");
>> + goto exit;
>> + }
>> +
>> + if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
>> + EFI_RTS_TIMEOUT)) {
>> + pr_err("EFI runtime service %d wedged in firmware\n", id);
>> + return EFI_TIMEOUT;
>
> Could we just clear the EFI_RUNTIME_SERVICES bit here right away? That
> way, we probably won't need the second patch (unless I'm mistaken). It
> /should/ also block the calls that are not routed via the workqueue,
> e.g., any EFI pstore calls to the non-blocking SetVariable() variant,
> but I just noticed that we never check EFI_RUNTIME_SERVICES on those
> code paths, which is probably a bug.
>
> And please return EFI_ABORTED rather than EFI_TIMEOUT - probably doesn't
> matter in practice but I'd like to avoid introducing more EFI return codes
> in the runtime context that the spec mentions only for boot services stuff.
Also, could we prevent the kthread that runs the workqueue from being scheduled
again if we decide that the runtime services are wedged? x86 has some logic for
this when a page fault occurs, and I wonder if there is a generic way to do
something similar.
^ permalink raw reply
* Re: [PATCH 1/2] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Ard Biesheuvel @ 2026-06-11 10:21 UTC (permalink / raw)
To: Breno Leitao, Ilias Apalodimas; +Cc: linux-efi, linux-kernel, kernel-team
In-Reply-To: <20260609-efi_timeout-v1-1-69a896faa805@debian.org>
Hi Breno,
On Tue, 9 Jun 2026, at 13:55, Breno Leitao wrote:
> When an EFI runtime service call hangs in firmware, the kworker on
> efi_rts_wq is stuck inside the firmware call and cannot be cancelled.
> The kernel currently waits indefinitely on the completion, and the
> caller holds efi_runtime_lock for the duration, so every subsequent
> EFI runtime caller (efivarfs, NVRAM writes, set_wakeup_time, ACPI PRM
> handlers, ...) is wedged until reboot. The only externally visible
> symptom is a "workqueue lockup" message and userspace processes piling
> up uninterruptibly on the semaphore.
>
> Replace the indefinite wait_for_completion() in __efi_queue_work()
> with wait_for_completion_timeout() bounded by EFI_RTS_TIMEOUT
> (120 seconds). On timeout, log the wedged runtime service id and
> return EFI_TIMEOUT to the caller.
>
> The wedged worker is intentionally leaked - it is still inside
> firmware and cannot be cancelled - and the shared efi_rts_work is
> abandoned to it. EFI runtime services become unavailable until reboot;
> the alternative is permanent userspace hang.
>
> This patch should land together with the follow-up that introduces
> the efi_rts_dead fast-fail flag: between the two, a subsequent
> __efi_queue_work() caller will re-INIT_WORK() and re-init_completion()
> on the work_struct and completion that the leaked worker still owns,
> which can corrupt workqueue state and let the next caller observe
> the leaked call's status as if it were its own. The split exists for
> review clarity; reviewers may prefer to squash.
>
> Known limitation: the union efi_rts_args that __efi_queue_work() hands
> to the worker contains pointers into the caller's stack frame (the
> compound literal in efi_queue_work() and the in/out buffers it points
> to, e.g. *tm in GetTime). Once the caller returns -EIO and unwinds,
> those slots are reusable. If firmware eventually unblocks and writes
> to the output buffers after the timeout has fired, the writes land in
> whatever now occupies that memory. In practice firmware that hangs for
> more than 120 seconds tends to stay hung. A follow-up bouncing args
> and output buffers through kmalloc would close this gap.
>
> At fleet scale this turns a generic "workqueue lockup" or stalled-task
> mystery into an unambiguous "EFI firmware is at fault" signal in
> dmesg.
>
> Signed-off-by: Breno Leitao <leitao@debian.org>
> ---
> drivers/firmware/efi/runtime-wrappers.c | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/firmware/efi/runtime-wrappers.c
> b/drivers/firmware/efi/runtime-wrappers.c
> index da8d29621644..6ce6d094066e 100644
> --- a/drivers/firmware/efi/runtime-wrappers.c
> +++ b/drivers/firmware/efi/runtime-wrappers.c
> @@ -118,6 +118,14 @@ union efi_rts_args {
>
> struct efi_runtime_work efi_rts_work;
>
> +/*
> + * Upper bound on how long we wait for a single EFI runtime service
> + * call to finish before declaring firmware wedged. Chosen to be longer
> + * than any plausible legitimate call (including UpdateCapsule on slow
> + * SPI-NOR) while still bounding userspace wait time.
> + */
> +#define EFI_RTS_TIMEOUT (120 * HZ)
> +
> /*
> * efi_queue_work: Queue EFI runtime service call and wait for completion
> * @_rts: EFI runtime service function identifier
> @@ -338,10 +346,16 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
> * queue_work() returns 0 if work was already on queue,
> * _ideally_ this should never happen.
> */
> - if (queue_work(efi_rts_wq, &efi_rts_work.work))
> - wait_for_completion(&efi_rts_work.efi_rts_comp);
> - else
> + if (!queue_work(efi_rts_wq, &efi_rts_work.work)) {
> pr_err("Failed to queue work to efi_rts_wq.\n");
> + goto exit;
> + }
> +
> + if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
> + EFI_RTS_TIMEOUT)) {
> + pr_err("EFI runtime service %d wedged in firmware\n", id);
> + return EFI_TIMEOUT;
Could we just clear the EFI_RUNTIME_SERVICES bit here right away? That
way, we probably won't need the second patch (unless I'm mistaken). It
/should/ also block the calls that are not routed via the workqueue,
e.g., any EFI pstore calls to the non-blocking SetVariable() variant,
but I just noticed that we never check EFI_RUNTIME_SERVICES on those
code paths, which is probably a bug.
And please return EFI_ABORTED rather than EFI_TIMEOUT - probably doesn't
matter in practice but I'd like to avoid introducing more EFI return codes
in the runtime context that the spec mentions only for boot services stuff.
^ permalink raw reply
* Re: [PATCH RESEND v2 1/2] serial: earlycon: add uart_clk_freq parameter
From: Markus Probst @ 2026-06-09 14:56 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Rafael J. Wysocki, Len Brown, Thomas Bogendoerfer, Ard Biesheuvel,
Ilias Apalodimas, Greg Kroah-Hartman, Jiri Slaby, linux-acpi,
linux-kernel, linux-m68k, linux-mips, linux-efi, linux-serial
In-Reply-To: <CAMuHMdXH0mR9Sr+ze2_m94uc46_7gubApzWOkf+vUO+w=DmNoQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2270 bytes --]
On Tue, 2026-06-09 at 08:53 +0200, Geert Uytterhoeven wrote:
> Hi Markus,
>
> On Tue, 9 Jun 2026 at 00:40, Markus Probst <markus.probst@posteo.de> wrote:
> > Add `uart_clk_freq` parameter to `setup_earlycon`. This allows the
> > options string to be reused with `add_preferred_console`, while still
> > allowing to set the uart clock frequency. This will be used in the
> > following commit ("ACPI: SPCR: Support UART clock frequency field").
> >
> > No logical change intended.
> >
> > Signed-off-by: Markus Probst <markus.probst@posteo.de>
>
> > --- a/drivers/tty/serial/earlycon.c
> > +++ b/drivers/tty/serial/earlycon.c
> > @@ -135,11 +135,14 @@ static int __init parse_options(struct earlycon_device *device, char *options)
> > return 0;
> > }
> >
> > -static int __init register_earlycon(char *buf, const struct earlycon_id *match)
> > +static int __init register_earlycon(char *buf, unsigned int uart_clk_freq,
> > + const struct earlycon_id *match)
> > {
> > int err;
> > struct uart_port *port = &early_console_dev.port;
> >
> > + port->uartclk = uart_clk_freq;
>
> Who is actually consuming this value?
I think primarily init_port in drivers/tty/serial/8250/8250_early.c.
> Earlycon typically works with the serial console, as configured before
> Linux boot by the firmware.
Thats defined in the SPCR table. If "Configured Baud Rate" is set to 0,
it will use it as is [1]. If not, it will configure it.
Thanks
- Markus Probst
[1]https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/serial-port-console-redirection-table
> The Microsoft doc referenced in patch 2 seem to agree with that:
>
> "On a system where the BIOS or system firmware uses the serial
> port for console input/output, this table should be used to convey
> information about the settings, to ensure a seamless transition
> between the firmware console output and Windows EMS output."
>
> > +
> > /* On parsing error, pass the options buf to the setup function */
> > if (buf && !parse_options(&early_console_dev, buf))
> > buf = NULL;
>
> Gr{oetje,eeting}s,
>
> Geert
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply
* Re: [PATCH v4 09/10] riscv: Select HAVE_ACPI_APEI required for RAS
From: Sunil V L @ 2026-06-09 14:30 UTC (permalink / raw)
To: Himanshu Chauhan
Cc: linux-riscv, linux-kernel, linux-acpi, linux-efi, acpica-devel,
paul.walmsley, palmer, lenb, james.morse, tony.luck, ardb, conor,
cleger, robert.moore, anup.patel
In-Reply-To: <20260513084325.2176952-10-himanshu.chauhan@oss.qualcomm.com>
On Wed, May 13, 2026 at 2:14 PM Himanshu Chauhan
<himanshu.chauhan@oss.qualcomm.com> wrote:
>
> Select the HAVE_ACPI_APEI option so that APEI GHES config options
> are visible.
>
> Signed-off-by: Himanshu Chauhan <himanshu.chauhan@oss.qualcomm.com>
> ---
> arch/riscv/Kconfig | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index d235396c4514..b94b19fb4249 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -187,6 +187,7 @@ config RISCV
> select HAVE_MOVE_PUD
> select HAVE_PAGE_SIZE_4KB
> select HAVE_PCI
> + select HAVE_ACPI_APEI if ACPI
> select HAVE_PERF_EVENTS
> select HAVE_PERF_REGS
> select HAVE_PERF_USER_STACK_DUMP
>
Reviewed-by: Sunil V L <sunilvl@oss.qualcomm.com>
^ permalink raw reply
* [PATCH 2/2] efi/runtime-wrappers: disable EFI runtime services after a hang
From: Breno Leitao @ 2026-06-09 11:55 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260609-efi_timeout-v1-0-69a896faa805@debian.org>
Once a runtime call has tripped EFI_RTS_TIMEOUT the firmware is
wedged and any subsequent call would also hang for 120 seconds
before timing out, blocking userspace each time.
Introduce a one-shot efi_rts_dead flag set on timeout, and check it
at the entry of __efi_queue_work() so further calls fail fast with
EFI_DEVICE_ERROR.
Beyond the wall-clock saving, the flag is required for correctness:
without it the next __efi_queue_work() caller after a timeout walks
into INIT_WORK() and init_completion() on the work_struct and
completion that the leaked worker still owns, which can corrupt the
workqueue's bookkeeping for the leaked work and let the next caller
observe the leaked call's status as if it were its own.
The flag is intentionally never cleared: the worker that wedged is
leaked inside firmware, so reusing efi_rts_work is unsafe.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/runtime-wrappers.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 6ce6d094066e..a76ba819855f 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -126,6 +126,16 @@ struct efi_runtime_work efi_rts_work;
*/
#define EFI_RTS_TIMEOUT (120 * HZ)
+/*
+ * Set once an EFI runtime service call has hung past EFI_RTS_TIMEOUT.
+ * Subsequent __efi_queue_work() callers fail fast: the wedged worker
+ * is still inside firmware, owns efi_rts_work, and any reuse of the
+ * shared work_struct or completion would corrupt the workqueue's
+ * bookkeeping for the leaked work. The flag is intentionally never
+ * cleared - recovery requires reboot.
+ */
+static bool efi_rts_dead;
+
/*
* efi_queue_work: Queue EFI runtime service call and wait for completion
* @_rts: EFI runtime service function identifier
@@ -328,6 +338,9 @@ static void __nocfi efi_call_rts(struct work_struct *work)
static efi_status_t __efi_queue_work(enum efi_rts_ids id,
union efi_rts_args *args)
{
+ if (READ_ONCE(efi_rts_dead))
+ return EFI_DEVICE_ERROR;
+
efi_rts_work.efi_rts_id = id;
efi_rts_work.args = args;
efi_rts_work.caller = __builtin_return_address(0);
@@ -353,7 +366,9 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
EFI_RTS_TIMEOUT)) {
- pr_err("EFI runtime service %d wedged in firmware\n", id);
+ WRITE_ONCE(efi_rts_dead, true);
+ pr_err("EFI runtime service %d wedged in firmware; disabling EFI runtime services\n",
+ id);
return EFI_TIMEOUT;
}
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH 1/2] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Breno Leitao @ 2026-06-09 11:55 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
In-Reply-To: <20260609-efi_timeout-v1-0-69a896faa805@debian.org>
When an EFI runtime service call hangs in firmware, the kworker on
efi_rts_wq is stuck inside the firmware call and cannot be cancelled.
The kernel currently waits indefinitely on the completion, and the
caller holds efi_runtime_lock for the duration, so every subsequent
EFI runtime caller (efivarfs, NVRAM writes, set_wakeup_time, ACPI PRM
handlers, ...) is wedged until reboot. The only externally visible
symptom is a "workqueue lockup" message and userspace processes piling
up uninterruptibly on the semaphore.
Replace the indefinite wait_for_completion() in __efi_queue_work()
with wait_for_completion_timeout() bounded by EFI_RTS_TIMEOUT
(120 seconds). On timeout, log the wedged runtime service id and
return EFI_TIMEOUT to the caller.
The wedged worker is intentionally leaked - it is still inside
firmware and cannot be cancelled - and the shared efi_rts_work is
abandoned to it. EFI runtime services become unavailable until reboot;
the alternative is permanent userspace hang.
This patch should land together with the follow-up that introduces
the efi_rts_dead fast-fail flag: between the two, a subsequent
__efi_queue_work() caller will re-INIT_WORK() and re-init_completion()
on the work_struct and completion that the leaked worker still owns,
which can corrupt workqueue state and let the next caller observe
the leaked call's status as if it were its own. The split exists for
review clarity; reviewers may prefer to squash.
Known limitation: the union efi_rts_args that __efi_queue_work() hands
to the worker contains pointers into the caller's stack frame (the
compound literal in efi_queue_work() and the in/out buffers it points
to, e.g. *tm in GetTime). Once the caller returns -EIO and unwinds,
those slots are reusable. If firmware eventually unblocks and writes
to the output buffers after the timeout has fired, the writes land in
whatever now occupies that memory. In practice firmware that hangs for
more than 120 seconds tends to stay hung. A follow-up bouncing args
and output buffers through kmalloc would close this gap.
At fleet scale this turns a generic "workqueue lockup" or stalled-task
mystery into an unambiguous "EFI firmware is at fault" signal in
dmesg.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/runtime-wrappers.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index da8d29621644..6ce6d094066e 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -118,6 +118,14 @@ union efi_rts_args {
struct efi_runtime_work efi_rts_work;
+/*
+ * Upper bound on how long we wait for a single EFI runtime service
+ * call to finish before declaring firmware wedged. Chosen to be longer
+ * than any plausible legitimate call (including UpdateCapsule on slow
+ * SPI-NOR) while still bounding userspace wait time.
+ */
+#define EFI_RTS_TIMEOUT (120 * HZ)
+
/*
* efi_queue_work: Queue EFI runtime service call and wait for completion
* @_rts: EFI runtime service function identifier
@@ -338,10 +346,16 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
* queue_work() returns 0 if work was already on queue,
* _ideally_ this should never happen.
*/
- if (queue_work(efi_rts_wq, &efi_rts_work.work))
- wait_for_completion(&efi_rts_work.efi_rts_comp);
- else
+ if (!queue_work(efi_rts_wq, &efi_rts_work.work)) {
pr_err("Failed to queue work to efi_rts_wq.\n");
+ goto exit;
+ }
+
+ if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
+ EFI_RTS_TIMEOUT)) {
+ pr_err("EFI runtime service %d wedged in firmware\n", id);
+ return EFI_TIMEOUT;
+ }
WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED);
exit:
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH 0/2] efi/runtime-wrappers: bound the wait for EFI runtime service calls
From: Breno Leitao @ 2026-06-09 11:55 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas
Cc: linux-efi, linux-kernel, Breno Leitao, kernel-team
When an EFI runtime service call hangs in firmware, the kworker on
efi_rts_wq is stuck inside the firmware call and cannot be cancelled.
The kernel currently waits indefinitely on the completion, and the
caller holds efi_runtime_lock for the duration, so every subsequent
EFI runtime caller (efivarfs, NVRAM writes, set_wakeup_time, ACPI PRM
handlers, ...) is wedged until reboot. The only externally visible
symptom is a "workqueue lockup" message and userspace processes
piling up uninterruptibly on the semaphore.
A real example from one of our NVIDIA Grace hosts:
BUG: workqueue lockup - pool cpus=28 node=0 flags=0x0 nice=0 stuck for 127s!
...
CPU: 28 PID: 590 Comm: kworker/u288:6
Workqueue: efi_rts_wq efi_call_rts
Call trace:
0x4052f11ecc (P)
0x4052f10ed4
...
__efi_rt_asm_wrapper+0x50/0x78
efi_call_rts+0x178/0x240
process_scheduled_works+0x17c/0x420
worker_thread+0x184/0x4d8
kthread+0xcc/0x1f8
ret_from_fork+0x10/0x20
PC and LR are inside EFI runtime services firmware memory; firmware
never returned; the worker stayed stuck across the 127s / 157s / 188s
"workqueue lockup" reports until external monitoring eventually rebooted
the host.
This series doesn't fix the firmware bug - that's vendor territory -
but it stops one stuck EFI call from taking the rest of userspace
down with it, and turns a generic stalled-task mystery into an
unambiguous "EFI firmware is at fault" signal in dmesg, which is
especially valuable at fleet scale where the same symptom could
otherwise be attributed to dozens of unrelated stalls.
Patch 1 bounds the wait at 120 seconds via wait_for_completion_timeout().
On timeout it logs the wedged runtime service id and returns
EFI_TIMEOUT to the caller instead of letting the task hang forever.
Patch 2 introduces the efi_rts_dead flag set on timeout and checked
at the entry of __efi_queue_work() so all subsequent callers fail
fast with EFI_DEVICE_ERROR rather than each paying another 120
seconds. The flag is also required for correctness - without it the
next caller after a timeout walks into INIT_WORK() and
init_completion() on the work_struct and completion the leaked
worker still owns. Patch 1 and patch 2 should land together;
reviewers may prefer to squash them.
The wedged worker is intentionally leaked - it is still inside
firmware and cannot be cancelled - and the shared efi_rts_work is
abandoned to it. EFI runtime services are unavailable until reboot,
but the rest of userspace keeps running.
Known limitation: the union efi_rts_args that the worker receives
contains pointers into the caller's stack frame (the compound literal
in efi_queue_work() and the in/out buffers it points to, e.g. *tm in
GetTime). Once the caller returns -EIO and unwinds, those slots are
reusable. If firmware eventually unblocks and writes the output
buffers after the timeout has fired, the writes land in whatever now
occupies that memory. In practice firmware that hangs for more than
120 seconds tends to stay hung, but the trade-off is real. A
follow-up bouncing args and output buffers through kmalloc would
close this gap.
Tested under virtme-ng + OVMF with a debug hook that hangs one
runtime service on demand: pr_err fires at +120s, the syscall that
triggered it (mount -t efivarfs) returns with EFI_TIMEOUT
(status=0x8000000000000012) propagated through efivars instead of
blocking indefinitely.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (2):
efi/runtime-wrappers: bound the wait for EFI runtime service calls
efi/runtime-wrappers: disable EFI runtime services after a hang
drivers/firmware/efi/runtime-wrappers.c | 35 ++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
---
base-commit: a87737435cfa134f9cdcc696ba3080759d04cf72
change-id: 20260609-efi_timeout-6f51d5bbcfb7
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply
* Re: [PATCH v4 07/10] riscv: Add RISC-V entries in processor type and ISA strings
From: Sunil V L @ 2026-06-09 8:27 UTC (permalink / raw)
To: Himanshu Chauhan
Cc: linux-riscv, linux-kernel, linux-acpi, linux-efi, acpica-devel,
paul.walmsley, palmer, lenb, james.morse, tony.luck, ardb, conor,
cleger, robert.moore, anup.patel
In-Reply-To: <20260513084325.2176952-8-himanshu.chauhan@oss.qualcomm.com>
On Wed, May 13, 2026 at 2:14 PM Himanshu Chauhan
<himanshu.chauhan@oss.qualcomm.com> wrote:
>
> Add RISCV and RISCV32/64 strings in the in processor type and ISA strings
> respectively. These are defined for cper records.
>
> Signed-off-by: Himanshu Chauhan <himanshu.chauhan@oss.qualcomm.com>
> ---
> drivers/firmware/efi/cper.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
> index 06b4fdb59917..1b1ab2f1355b 100644
> --- a/drivers/firmware/efi/cper.c
> +++ b/drivers/firmware/efi/cper.c
> @@ -170,6 +170,7 @@ static const char * const proc_type_strs[] = {
> "IA32/X64",
> "IA64",
> "ARM",
> + "RISC-V",
> };
>
> static const char * const proc_isa_strs[] = {
> @@ -178,6 +179,8 @@ static const char * const proc_isa_strs[] = {
> "X64",
> "ARM A32/T32",
> "ARM A64",
> + "RV32/RV32E",
> + "RV64",
> };
>
Reviewed-by: Sunil V L <sunilvl@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v4 05/10] riscv: conditionally compile GHES NMI spool function
From: Sunil V L @ 2026-06-09 8:17 UTC (permalink / raw)
To: Himanshu Chauhan
Cc: linux-riscv, linux-kernel, linux-acpi, linux-efi, acpica-devel,
paul.walmsley, palmer, lenb, james.morse, tony.luck, ardb, conor,
cleger, robert.moore, anup.patel
In-Reply-To: <20260513084325.2176952-6-himanshu.chauhan@oss.qualcomm.com>
On Wed, May 13, 2026 at 2:14 PM Himanshu Chauhan
<himanshu.chauhan@oss.qualcomm.com> wrote:
>
> Compile ghes_in_nmi_spool_from_list only when NMI and SEA
> is enabled. Otherwise compilation fails with "defined but
> not used" error.
>
> Signed-off-by: Himanshu Chauhan <himanshu.chauhan@oss.qualcomm.com>
> ---
> drivers/acpi/apei/ghes.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 3236a3ce79d6..8edc2c8db1bb 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -1397,6 +1397,7 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
> return rc;
> }
>
> +#if defined(CONFIG_HAVE_ACPI_APEI_NMI) || defined(CONFIG_ACPI_APEI_SEA)
>
> static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list,
> enum fixed_addresses fixmap_idx)
> {
> @@ -1415,6 +1416,7 @@ static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list,
>
> return ret;
> }
> +#endif
>
Reviewed-by: Sunil V L <sunilvl@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v4 02/10] riscv: Define arch_apei_get_mem_attribute for RISC-V
From: Sunil V L @ 2026-06-09 8:01 UTC (permalink / raw)
To: Himanshu Chauhan
Cc: linux-riscv, linux-kernel, linux-acpi, linux-efi, acpica-devel,
paul.walmsley, palmer, lenb, james.morse, tony.luck, ardb, conor,
cleger, robert.moore, anup.patel
In-Reply-To: <20260513084325.2176952-3-himanshu.chauhan@oss.qualcomm.com>
On Wed, May 13, 2026 at 2:13 PM Himanshu Chauhan
<himanshu.chauhan@oss.qualcomm.com> wrote:
>
> ghes_map function uses arch_apei_get_mem_attribute to get the
> protection bits for a given physical address. These protection
> bits are then used to map the physical address.
>
> Signed-off-by: Himanshu Chauhan <himanshu.chauhan@oss.qualcomm.com>
> ---
> arch/riscv/include/asm/acpi.h | 16 ++++++++++++++++
> arch/riscv/kernel/acpi.c | 12 ++++++++++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h
> index 26ab37c171bc..c142db9f81a7 100644
> --- a/arch/riscv/include/asm/acpi.h
> +++ b/arch/riscv/include/asm/acpi.h
> @@ -14,6 +14,7 @@
>
> /* Basic configuration for ACPI */
> #ifdef CONFIG_ACPI
> +pgprot_t __acpi_get_mem_attribute(phys_addr_t addr);
>
> typedef u64 phys_cpuid_t;
> #define PHYS_CPUID_INVALID INVALID_HARTID
> @@ -27,6 +28,21 @@ extern int acpi_disabled;
> extern int acpi_noirq;
> extern int acpi_pci_disabled;
>
> +#ifdef CONFIG_ACPI_APEI
> +/*
> + * acpi_disable_cmcff to disable IA-32 Corrected Machine Check (CMC)
> + * Firmware-First mode. It is not required in RISC-V architecture
> + * and is present for compatibility
> + */
> +#define acpi_disable_cmcff 1
> +static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
> +{
> + return __acpi_get_mem_attribute(addr);
> +}
> +#else /* CONFIG_ACPI_APEI */
> +#define acpi_disable_cmcff 0
> +#endif /* !CONFIG_ACPI_APEI */
> +
> static inline void disable_acpi(void)
> {
> acpi_disabled = 1;
> diff --git a/arch/riscv/kernel/acpi.c b/arch/riscv/kernel/acpi.c
> index 068e0b404b6f..7a6770697999 100644
> --- a/arch/riscv/kernel/acpi.c
> +++ b/arch/riscv/kernel/acpi.c
> @@ -204,6 +204,18 @@ struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu)
> return &cpu_madt_rintc[cpu];
> }
>
> +pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)
> +{
> + u64 attr;
> +
> + attr = efi_mem_attributes(addr);
> + if (attr & EFI_MEMORY_WB)
> + return PAGE_KERNEL;
> + if ((attr & EFI_MEMORY_WC) || (attr & EFI_MEMORY_WT))
> + return pgprot_writecombine(PAGE_KERNEL);
> + return PAGE_KERNEL;
> +}
> +
> /*
> * __acpi_map_table() will be called before paging_init(), so early_ioremap()
> * or early_memremap() should be called here to for ACPI table mapping.
>
Reviewed-by: Sunil V L <sunilvl@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH RESEND v2 1/2] serial: earlycon: add uart_clk_freq parameter
From: Geert Uytterhoeven @ 2026-06-09 6:53 UTC (permalink / raw)
To: Markus Probst
Cc: Rafael J. Wysocki, Len Brown, Thomas Bogendoerfer, Ard Biesheuvel,
Ilias Apalodimas, Greg Kroah-Hartman, Jiri Slaby, linux-acpi,
linux-kernel, linux-m68k, linux-mips, linux-efi, linux-serial
In-Reply-To: <20260609-acpi_spcr-v2-1-3cd9a3bda727@posteo.de>
Hi Markus,
On Tue, 9 Jun 2026 at 00:40, Markus Probst <markus.probst@posteo.de> wrote:
> Add `uart_clk_freq` parameter to `setup_earlycon`. This allows the
> options string to be reused with `add_preferred_console`, while still
> allowing to set the uart clock frequency. This will be used in the
> following commit ("ACPI: SPCR: Support UART clock frequency field").
>
> No logical change intended.
>
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> --- a/drivers/tty/serial/earlycon.c
> +++ b/drivers/tty/serial/earlycon.c
> @@ -135,11 +135,14 @@ static int __init parse_options(struct earlycon_device *device, char *options)
> return 0;
> }
>
> -static int __init register_earlycon(char *buf, const struct earlycon_id *match)
> +static int __init register_earlycon(char *buf, unsigned int uart_clk_freq,
> + const struct earlycon_id *match)
> {
> int err;
> struct uart_port *port = &early_console_dev.port;
>
> + port->uartclk = uart_clk_freq;
Who is actually consuming this value?
Earlycon typically works with the serial console, as configured before
Linux boot by the firmware.
The Microsoft doc referenced in patch 2 seem to agree with that:
"On a system where the BIOS or system firmware uses the serial
port for console input/output, this table should be used to convey
information about the settings, to ensure a seamless transition
between the firmware console output and Windows EMS output."
> +
> /* On parsing error, pass the options buf to the setup function */
> if (buf && !parse_options(&early_console_dev, buf))
> buf = NULL;
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH RESEND v2 1/2] serial: earlycon: add uart_clk_freq parameter
From: Greg Kroah-Hartman @ 2026-06-09 5:30 UTC (permalink / raw)
To: Markus Probst
Cc: Rafael J. Wysocki, Len Brown, Geert Uytterhoeven,
Thomas Bogendoerfer, Ard Biesheuvel, Ilias Apalodimas, Jiri Slaby,
linux-acpi, linux-kernel, linux-m68k, linux-mips, linux-efi,
linux-serial
In-Reply-To: <20260609-acpi_spcr-v2-1-3cd9a3bda727@posteo.de>
On Mon, Jun 08, 2026 at 10:40:21PM +0000, Markus Probst wrote:
> Add `uart_clk_freq` parameter to `setup_earlycon`. This allows the
> options string to be reused with `add_preferred_console`, while still
> allowing to set the uart clock frequency. This will be used in the
> following commit ("ACPI: SPCR: Support UART clock frequency field").
Ick, this is bad, now you need to look up what this 0 is as a parameter
every time you see this call. Please just add a new function that takes
the new paramter, don't abuse the old one for this.
thanks,
greg k-h
^ permalink raw reply
* [PATCH RESEND v2 2/2] ACPI: SPCR: Support UART clock frequency field
From: Markus Probst @ 2026-06-08 22:40 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown, Geert Uytterhoeven,
Thomas Bogendoerfer, Ard Biesheuvel, Ilias Apalodimas,
Greg Kroah-Hartman, Jiri Slaby
Cc: linux-acpi, linux-kernel, linux-m68k, linux-mips, linux-efi,
linux-serial, Markus Probst
In-Reply-To: <20260609-acpi_spcr-v2-0-3cd9a3bda727@posteo.de>
The Microsoft Serial Port Console Redirection (SPCR) specification
revision 1.08 comprises additional field: UART Clock Frequency [1].
It contains a non-zero value indicating the UART clock frequency in Hz.
Link: https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table [1]
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
drivers/acpi/spcr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
index cfacbe53f279..16f94073fde6 100644
--- a/drivers/acpi/spcr.c
+++ b/drivers/acpi/spcr.c
@@ -228,7 +228,7 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
pr_info("console: %s\n", opts);
if (enable_earlycon)
- setup_earlycon(opts, 0);
+ setup_earlycon(opts, table->header.revision >= 3 ? table->uart_clk_freq : 0);
if (enable_console)
err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
--
2.53.0
^ permalink raw reply related
* [PATCH RESEND v2 1/2] serial: earlycon: add uart_clk_freq parameter
From: Markus Probst @ 2026-06-08 22:40 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown, Geert Uytterhoeven,
Thomas Bogendoerfer, Ard Biesheuvel, Ilias Apalodimas,
Greg Kroah-Hartman, Jiri Slaby
Cc: linux-acpi, linux-kernel, linux-m68k, linux-mips, linux-efi,
linux-serial, Markus Probst
In-Reply-To: <20260609-acpi_spcr-v2-0-3cd9a3bda727@posteo.de>
Add `uart_clk_freq` parameter to `setup_earlycon`. This allows the
options string to be reused with `add_preferred_console`, while still
allowing to set the uart clock frequency. This will be used in the
following commit ("ACPI: SPCR: Support UART clock frequency field").
No logical change intended.
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
arch/m68k/virt/config.c | 2 +-
arch/mips/mti-malta/malta-init.c | 2 +-
drivers/acpi/spcr.c | 2 +-
drivers/firmware/efi/earlycon.c | 2 +-
drivers/tty/serial/earlycon.c | 17 ++++++++++++-----
include/linux/serial_core.h | 7 +++++--
6 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/arch/m68k/virt/config.c b/arch/m68k/virt/config.c
index b338e2a8da6a..2c35ec15a51b 100644
--- a/arch/m68k/virt/config.c
+++ b/arch/m68k/virt/config.c
@@ -83,7 +83,7 @@ void __init config_virt(void)
snprintf(earlycon, sizeof(earlycon), "early_gf_tty,0x%08x",
virt_bi_data.tty.mmio);
- setup_earlycon(earlycon);
+ setup_earlycon(earlycon, 0);
mach_init_IRQ = virt_init_IRQ;
mach_sched_init = virt_sched_init;
diff --git a/arch/mips/mti-malta/malta-init.c b/arch/mips/mti-malta/malta-init.c
index 82b0fd8576a2..88ef17967ced 100644
--- a/arch/mips/mti-malta/malta-init.c
+++ b/arch/mips/mti-malta/malta-init.c
@@ -75,7 +75,7 @@ static void __init console_config(void)
if ((strstr(fw_getcmdline(), "earlycon=")) == NULL) {
sprintf(console_string, "uart8250,io,0x3f8,%d%c%c", baud,
parity, bits);
- setup_earlycon(console_string);
+ setup_earlycon(console_string, 0);
}
if ((strstr(fw_getcmdline(), "console=")) == NULL) {
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
index 73cb933fdc89..cfacbe53f279 100644
--- a/drivers/acpi/spcr.c
+++ b/drivers/acpi/spcr.c
@@ -228,7 +228,7 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
pr_info("console: %s\n", opts);
if (enable_earlycon)
- setup_earlycon(opts);
+ setup_earlycon(opts, 0);
if (enable_console)
err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c
index 3d060d59968c..0e3c2cb08966 100644
--- a/drivers/firmware/efi/earlycon.c
+++ b/drivers/firmware/efi/earlycon.c
@@ -221,7 +221,7 @@ static bool __initdata fb_probed;
void __init efi_earlycon_reprobe(void)
{
if (fb_probed)
- setup_earlycon("efifb");
+ setup_earlycon("efifb", 0);
}
static int __init efi_earlycon_setup(struct earlycon_device *device,
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index ab9af37f6cda..a419943e083b 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -135,11 +135,14 @@ static int __init parse_options(struct earlycon_device *device, char *options)
return 0;
}
-static int __init register_earlycon(char *buf, const struct earlycon_id *match)
+static int __init register_earlycon(char *buf, unsigned int uart_clk_freq,
+ const struct earlycon_id *match)
{
int err;
struct uart_port *port = &early_console_dev.port;
+ port->uartclk = uart_clk_freq;
+
/* On parsing error, pass the options buf to the setup function */
if (buf && !parse_options(&early_console_dev, buf))
buf = NULL;
@@ -164,7 +167,8 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
/**
* setup_earlycon - match and register earlycon console
- * @buf: earlycon param string
+ * @buf: earlycon param string
+ * @uart_clk_freq: uart clock frequency in Hz or 0 for BASE_BAUD*16
*
* Registers the earlycon console matching the earlycon specified
* in the param string @buf. Acceptable param strings are of the form
@@ -177,10 +181,13 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
* <options> string in the 'options' parameter; all other forms set
* the parameter to NULL.
*
+ * If the uart clock frequency is specified in the 'options' parameter,
+ * the value of the param @uart_clk_freq will be ignored.
+ *
* Returns 0 if an attempt to register the earlycon was made,
* otherwise negative error code
*/
-int __init setup_earlycon(char *buf)
+int __init setup_earlycon(char *buf, unsigned int uart_clk_freq)
{
const struct earlycon_id *match;
bool empty_compatible = true;
@@ -209,7 +216,7 @@ int __init setup_earlycon(char *buf)
} else
buf = NULL;
- return register_earlycon(buf, match);
+ return register_earlycon(buf, uart_clk_freq, match);
}
if (empty_compatible) {
@@ -241,7 +248,7 @@ static int __init param_setup_earlycon(char *buf)
}
}
- err = setup_earlycon(buf);
+ err = setup_earlycon(buf, 0);
if (err == -ENOENT || err == -EALREADY)
return 0;
return err;
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 666430b47899..5c60fda9dd3a 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -1097,10 +1097,13 @@ int of_setup_earlycon(const struct earlycon_id *match, unsigned long node,
#ifdef CONFIG_SERIAL_EARLYCON
extern bool earlycon_acpi_spcr_enable __initdata;
-int setup_earlycon(char *buf);
+int setup_earlycon(char *buf, unsigned int uart_clk_freq);
#else
static const bool earlycon_acpi_spcr_enable EARLYCON_USED_OR_UNUSED;
-static inline int setup_earlycon(char *buf) { return 0; }
+static inline int setup_earlycon(char *buf, unsigned int uart_clk_freq)
+{
+ return 0;
+}
#endif
/* Variant of uart_console_registered() when the console_list_lock is held. */
--
2.53.0
^ permalink raw reply related
* [PATCH RESEND v2 0/2] ACPI: SPCR: Support UART clock frequency field
From: Markus Probst @ 2026-06-08 22:40 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown, Geert Uytterhoeven,
Thomas Bogendoerfer, Ard Biesheuvel, Ilias Apalodimas,
Greg Kroah-Hartman, Jiri Slaby
Cc: linux-acpi, linux-kernel, linux-m68k, linux-mips, linux-efi,
linux-serial, Markus Probst
Support the uart clock frequency in the SPCR table.
See the commit messages for details.
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v2:
- fix uart_clk_freq possibly being interpreted as parity/bits/flow
- Link to v1: https://patch.msgid.link/20260505-acpi_spcr-v1-1-fd4bc6f4eb53@posteo.de
---
Markus Probst (2):
serial: earlycon: add uart_clk_freq parameter
ACPI: SPCR: Support UART clock frequency field
arch/m68k/virt/config.c | 2 +-
arch/mips/mti-malta/malta-init.c | 2 +-
drivers/acpi/spcr.c | 2 +-
drivers/firmware/efi/earlycon.c | 2 +-
drivers/tty/serial/earlycon.c | 17 ++++++++++++-----
include/linux/serial_core.h | 7 +++++--
6 files changed, 21 insertions(+), 11 deletions(-)
---
base-commit: aa61612ab641d7d62b0b6889f2c7c9251489f6e3
change-id: 20260430-acpi_spcr-61902fd923f2
-----BEGIN PGP SIGNATURE-----
iQJPBAABCAA5FiEEgnQYxPSsWOdyMMRzNHYf+OetQ9IFAmoTfSQbFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMiwyAAoJEDR2H/jnrUPS9e4P/3ObHEJDC0UywwA9xj1z
kzoUrs/iZrus7ROb6ri7MzHVY8riTrzHZrvMOkdWBAxuMXrnzdLwDx96qnQuiaHm
GJBDNBAxoRzBxVkkQJi9Osa+zr8DOEC3+gsv3dCqNqI4DT1wXsBEMi4zgg5dJ5Ye
oFFjEXN/EAiFVa6DHeNMaoJ69sLbYjvWUxAvU74Zpa6zjQMc1n9oCcJFc5D6cvkx
9/WozDV7rTNjmqDy9kcmyb3geeEMd14/y3j8adIe6qB0kkIJQwQr671eIWzGA7pg
353gxRmbLaT9YCKJvHsP32N7Z2EUhrp/o3U6Od/Q0I+qDz13RBSuDLUTogi/mhAw
U7i9a2WHaD2LvwQdt5azLjuo7etx5si84E/cT5G2xJBwUeC2ftYjZulJZs8BMKZp
Oac3Ln/qWCEVw52DWOcxPPIkxlGfjEZOqWBajkRI4NdY3+d0o0/nBK+RYfOt30sf
L3+yLnMmqBjF1RkF1Lm3kTZ589K2KSxGOKMGoYKZqyvV4Ato4w4EoIwL+MQJw7SD
De/BNNpFpTDqJxqgnl4HuELZRzmiKAQCGMwDq285I0Ng1r7xlxFCDYBJnyjnm2Qd
LbD/ZH5yl0beq/S1qZla+JIRjYdbRNQlLUSh3MqBxgll0Xg5GYLk2qeeF+xJJDCq
LkXKR59axau1efToWKWn7CBZ
=EGzU
-----END PGP SIGNATURE-----
--
Markus Probst <markus.probst@posteo.de>
^ permalink raw reply
page: next (older)
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox