Skip to contentSkip to conversation

arm64: Make EFI calls preemptible

PATCH RESEND Applied Ard Biesheuvel 4 participants 15 messages Oct 15, 2025 – Nov 11, 2025

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v4 cover arm64: Make EFI calls preemptible

From: Ard Biesheuvel <[email protected]>

The arm64 port permits the use of the baseline FP/SIMD register file in kernel mode, and no longer requires preemption to be disabled. Now that the EFI spec is being clarified to state that EFI runtime services may only use baseline FP/SIMD, the fact that EFI may code may use FP/SIMD registers (while executing at the same privilege level as the kernel) is no longer a reason to disable preemption when invoking them.

This means that the only remaining reason for disabling preemption is the fact that the active mm is swapped out and replaced with efi_mm in a way that is hidden from the scheduler, and so scheduling is not supported currently. However, given that virtually all (*) EFI runtime calls are made from the efi_rts_wq workqueue, the efi_mm can simply be loaded into the workqueue worker kthread while the call is in progress, and this does not require preemption to be disabled.

Note that this is only a partial solution in terms of RT guarantees, given that the runtime services execute at the same privilege level as the kernel, and can therefore disable interrupts (and therefore preemption) directly. But it should prevent scheduling latency spikes for EFI calls that simply take a long time to run to completion.

No changes since v4, rebase to v6.18-rc1 only.

Changes since v3:
- Abandon changes that disallow EFI runtime calls in hardirq or NMI context, as these might occur when EFI pstore records an OOPS into the EFI variable store. Note that this suggests that preserving/restoring userland SVE state in such cases is rather futile, but this can be revisited at a later time.
- Drop the lock in the arch wrapper, which has become redundant now that all EFI calls are serialized under the efi_runtime_lock semaphore
- Add code comment to patch #4 to explain the kludge
- Add some acks from Will

Changes since v2:
- Permit ordinary kernel mode FP/SIMD with IRQs disabled, so that the special EFI case only deals with invocations in hardirq or NMI context
- Disallow EFI runtime calls in hardirq or NMI context, so that the special FP/SIMD handling for EFI can be dropped entirely
- Use a mutex rather than a semaphore for the arm64 EFI runtime lock, now that it is never trylock()ed in IRQ or NMI context.

Changes since v1/RFC:
- Disable uaccess for SWPAN before updating the preserved TTBR0 value
- Document why disabling migration is needed
- Rebase onto v6.17-rc1

(*) only efi_reset_system() and EFI pstore invoke EFI runtime services
without going through the workqueue, and the latter only when saving a kernel oops log to the EFI varstore

Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
Cc Sebastian Andrzej Siewior <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Mark Brown <[email protected]>
Cc Pierre Gondois <[email protected]>
Cc Sami Mujawar <[email protected]>

Ard Biesheuvel (7):
efi: Add missing static initializer for efi_mm::cpus_allowed_lock efi/runtime-wrappers: Keep track of the efi_runtime_lock owner arm64/fpsimd: Don't warn when EFI execution context is preemptible arm64/fpsimd: Permit kernel mode NEON with IRQs off arm64/efi: Drop efi_rt_lock spinlock from EFI arch wrapper arm64/efi: Move uaccess en/disable out of efi_set_pgd() arm64/efi: Call EFI runtime services without disabling preemption

 arch/arm64/include/asm/efi.h            | 13 ++----
 arch/arm64/include/asm/simd.h           |  2 +-
 arch/arm64/kernel/efi.c                 | 46 +++++++++++++++++---
 arch/arm64/kernel/fpsimd.c              | 29 ++++++++----
 drivers/firmware/efi/efi.c              |  3 ++
 drivers/firmware/efi/runtime-wrappers.c | 17 +++++++-
 include/linux/efi.h                     |  2 +
 7 files changed, 86 insertions(+), 26 deletions(-)

base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787

Signature · 1 line
2.51.0.869.ge66316f041-goog
#1 lore raw

PATCH RESEND v4 1/7 efi: Add missing static initializer for efi_mm::cpus_allowed_lock

From: Ard Biesheuvel <[email protected]>

Initialize the cpus_allowed_lock struct member of efi_mm.

---
 drivers/firmware/efi/efi.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/firmware/efi/efi.c +3 -0
index 1ce428e2ac8a..fc407d891348 100644
@@ -74,6 +74,9 @@ struct mm_struct efi_mm = {
 	.page_table_lock	= __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
 	.mmlist			= LIST_HEAD_INIT(efi_mm.mmlist),
 	.cpu_bitmap		= { [BITS_TO_LONGS(NR_CPUS)] = 0},
+#ifdef CONFIG_SCHED_MM_CID
+	.cpus_allowed_lock	= __RAW_SPIN_LOCK_UNLOCKED(efi_mm.cpus_allowed_lock),
+#endif
 };
 
 struct workqueue_struct *efi_rts_wq;
Signature · 1 line
2.51.0.869.ge66316f041-goog
#2 lore raw

PATCH RESEND v4 2/7 efi/runtime-wrappers: Keep track of the efi_runtime_lock owner

In reply to Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

The EFI runtime wrappers use a file local semaphore to serialize access to the EFI runtime services. This means that any calls to the arch wrappers around the runtime services will also be serialized, removing the need for redundant locking.

For robustness, add a facility that allows those arch wrappers to assert that the semaphore was taken by the current task.

---
 drivers/firmware/efi/runtime-wrappers.c | 17 ++++++++++++++++-
 include/linux/efi.h                     |  2 ++
 2 files changed, 18 insertions(+), 1 deletion(-)
drivers/firmware/efi/runtime-wrappers.c +16 -1
index 708b777857d3..da8d29621644 100644
@@ -202,6 +202,8 @@ void efi_call_virt_check_flags(unsigned long flags, const void *caller)
  */
 static DEFINE_SEMAPHORE(efi_runtime_lock, 1);
 
+static struct task_struct *efi_runtime_lock_owner;
+
 /*
  * Expose the EFI runtime lock to the UV platform
  */
@@ -219,6 +221,8 @@ static void __nocfi efi_call_rts(struct work_struct *work)
 	efi_status_t status = EFI_NOT_FOUND;
 	unsigned long flags;
 
+	efi_runtime_lock_owner = current;
+
 	arch_efi_call_virt_setup();
 	flags = efi_call_virt_save_flags();
 
@@ -310,6 +314,7 @@ static void __nocfi efi_call_rts(struct work_struct *work)
 
 	efi_rts_work.status = status;
 	complete(&efi_rts_work.efi_rts_comp);
+	efi_runtime_lock_owner = NULL;
 }
 
 static efi_status_t __efi_queue_work(enum efi_rts_ids id,
@@ -444,8 +449,10 @@ virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr,
 	if (down_trylock(&efi_runtime_lock))
 		return EFI_NOT_READY;
 
+	efi_runtime_lock_owner = current;
 	status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor,
 				       attr, data_size, data);
+	efi_runtime_lock_owner = NULL;
 	up(&efi_runtime_lock);
 	return status;
 }
@@ -481,9 +488,11 @@ virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space,
 	if (down_trylock(&efi_runtime_lock))
 		return EFI_NOT_READY;
 
+	efi_runtime_lock_owner = current;
 	status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr,
 				       storage_space, remaining_space,
 				       max_variable_size);
+	efi_runtime_lock_owner = NULL;
 	up(&efi_runtime_lock);
 	return status;
 }
@@ -509,12 +518,13 @@ virt_efi_reset_system(int reset_type, efi_status_t status,
 		return;
 	}
 
+	efi_runtime_lock_owner = current;
 	arch_efi_call_virt_setup();
 	efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM;
 	arch_efi_call_virt(efi.runtime, reset_system, reset_type, status,
 			   data_size, data);
 	arch_efi_call_virt_teardown();
-
+	efi_runtime_lock_owner = NULL;
 	up(&efi_runtime_lock);
 }
 
@@ -587,3 +597,8 @@ efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
 }
 
 #endif
+
+void efi_runtime_assert_lock_held(void)
+{
+	WARN_ON(efi_runtime_lock_owner != current);
+}
include/linux/efi.h +2 -0
index a98cc39e7aaa..b23ff8b83219 100644
@@ -1126,6 +1126,8 @@ static inline bool efi_runtime_disabled(void) { return true; }
 extern void efi_call_virt_check_flags(unsigned long flags, const void *caller);
 extern unsigned long efi_call_virt_save_flags(void);
 
+void efi_runtime_assert_lock_held(void);
+
 enum efi_secureboot_mode {
 	efi_secureboot_mode_unset,
 	efi_secureboot_mode_unknown,
Signature · 1 line
2.51.0.869.ge66316f041-goog
#3 lore raw

PATCH RESEND v4 3/7 arm64/fpsimd: Don't warn when EFI execution context is preemptible

In reply to Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

Kernel mode FP/SIMD no longer requires preemption to be disabled, so only warn on uses of FP/SIMD from preemptible context if the fallback path is taken for cases where kernel mode NEON would not be allowed otherwise.

---
 arch/arm64/kernel/fpsimd.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/fpsimd.c +2 -2
index e3f8f51748bc..3d848c89604e 100644
@@ -1934,11 +1934,11 @@ void __efi_fpsimd_begin(void)
 	if (!system_supports_fpsimd())
 		return;
 
-	WARN_ON(preemptible());
-
 	if (may_use_simd()) {
 		kernel_neon_begin();
 	} else {
+		WARN_ON(preemptible());
+
 		/*
 		 * If !efi_sve_state, SVE can't be in use yet and doesn't need
 		 * preserving:
Signature · 1 line
2.51.0.869.ge66316f041-goog
maintainer
#4 lore raw

Re: [PATCH v4 resend 3/7] arm64/fpsimd: Don't warn when EFI execution context is preemptible

Quoted 17 lines · Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

Kernel mode FP/SIMD no longer requires preemption to be disabled, so only warn on uses of FP/SIMD from preemptible context if the fallback path is taken for cases where kernel mode NEON would not be allowed otherwise.

---
 arch/arm64/kernel/fpsimd.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/fpsimd.c +0 -0
index e3f8f51748bc..3d848c89604e 100644

arch/arm64/kernel/fpsimd.c void __efi_fpsimd_begin(void) · line 1941

Quoted 10 lines
arch/arm64/kernel/fpsimd.c +2 -2
@@ -1934,11 +1934,11 @@ void __efi_fpsimd_begin(void)
 	if (!system_supports_fpsimd())
 		return;
 
-	WARN_ON(preemptible());
-
 	if (may_use_simd()) {
 		kernel_neon_begin();
 	} else {
+		WARN_ON(preemptible());
+

Given that may_use_simd() returns false on systems without support for fpsimd, I wonder whether moving this WARN_ON() actually helps with anything. That is, you probably shouldn't be calling __efi_fpsimd_begin() from preemptible code regardless, no?

Will

maintainer
#5 lore raw

Re: [PATCH v4 resend 3/7] arm64/fpsimd: Don't warn when EFI execution context is preemptible

Quoted 22 lines · Will Deacon
Quoted 20 lines · Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

Kernel mode FP/SIMD no longer requires preemption to be disabled, so only warn on uses of FP/SIMD from preemptible context if the fallback path is taken for cases where kernel mode NEON would not be allowed otherwise.

---
 arch/arm64/kernel/fpsimd.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/fpsimd.c +0 -0
index e3f8f51748bc..3d848c89604e 100644
@@ -1934,11 +1934,11 @@ void __efi_fpsimd_begin(void)
      if (!system_supports_fpsimd())
              return;
 
Quoted 11 lines

arch/arm64/kernel/fpsimd.c void __efi_fpsimd_begin(void) · line 1941

Quoted 7 lines
arch/arm64/kernel/fpsimd.c +2 -2
-     WARN_ON(preemptible());
-
      if (may_use_simd()) {
              kernel_neon_begin();
      } else {
+             WARN_ON(preemptible());
+

Given that may_use_simd() returns false on systems without support for fpsimd, I wonder whether moving this WARN_ON() actually helps with anything.

This code is only reachable if system_supports_fpsimd() returns true, so may_use_simd() will only return false here when running in hardirq/NMI context.

Quoted 3 lines

That is, you probably shouldn't be calling __efi_fpsimd_begin() from preemptible code regardless, no?

Not sure I follow you here: __efi_fpsimd_begin() is generally called from preemptible code, and it is the EFI runtime wrappers themselves that disable preemption - that is the whole point of this series.

maintainer
#6 lore raw

Re: [PATCH v4 resend 3/7] arm64/fpsimd: Don't warn when EFI execution context is preemptible

Quoted 35 lines · Ard Biesheuvel
Quoted 22 lines · Will Deacon
Quoted 20 lines · Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

Kernel mode FP/SIMD no longer requires preemption to be disabled, so only warn on uses of FP/SIMD from preemptible context if the fallback path is taken for cases where kernel mode NEON would not be allowed otherwise.

---
 arch/arm64/kernel/fpsimd.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/fpsimd.c +0 -0
index e3f8f51748bc..3d848c89604e 100644
@@ -1934,11 +1934,11 @@ void __efi_fpsimd_begin(void)
      if (!system_supports_fpsimd())
              return;
 
Quoted 11 lines

arch/arm64/kernel/fpsimd.c void __efi_fpsimd_begin(void) · line 1941

Quoted 7 lines
arch/arm64/kernel/fpsimd.c +2 -2
-     WARN_ON(preemptible());
-
      if (may_use_simd()) {
              kernel_neon_begin();
      } else {
+             WARN_ON(preemptible());
+

Given that may_use_simd() returns false on systems without support for fpsimd, I wonder whether moving this WARN_ON() actually helps with anything.

Quoted 3 lines

This code is only reachable if system_supports_fpsimd() returns true, so may_use_simd() will only return false here when running in hardirq/NMI context.

Sorry, I missed the check _right at the top of the hunk_!

Quoted 8 lines
Quoted 4 lines

That is, you probably shouldn't be calling __efi_fpsimd_begin() from preemptible code regardless, no?

Not sure I follow you here: __efi_fpsimd_begin() is generally called from preemptible code, and it is the EFI runtime wrappers themselves that disable preemption - that is the whole point of this series.

Ignore me. I was reviewing this against the upstream arch_efi_call_virt_setup/teardown() functions rather than in the context of the changes made later in this series (specifically patch 5).

Will

#7 lore raw

PATCH RESEND v4 4/7 arm64/fpsimd: Permit kernel mode NEON with IRQs off

In reply to Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

Currently, may_use_simd() will return false when called from a context where IRQs are disabled. One notable case where this happens is when calling the ResetSystem() EFI runtime service from the reboot/poweroff code path. For this case alone, there is a substantial amount of FP/SIMD support code to handle the corner case where a EFI runtime service is invoked with IRQs disabled.

The only reason kernel mode SIMD is not allowed when IRQs are disabled is that re-enabling softirqs in this case produces a noisy diagnostic when lockdep is enabled. The warning is valid, in the sense that delivering pending softirqs over the back of the call to local_bh_enable() is problematic when IRQs are disabled.

While the API lacks a facility to simply mask and unmask softirqs without triggering their delivery, disabling softirqs is not needed to begin with when IRQs are disabled, given that softirqs are only every taken asynchronously over the back of a hard IRQ.

So dis/enable softirq processing conditionally, based on whether IRQs are enabled, and relax the check in may_use_simd().

---
 arch/arm64/include/asm/simd.h |  2 +-
 arch/arm64/kernel/fpsimd.c    | 25 +++++++++++++++-----
 2 files changed, 20 insertions(+), 7 deletions(-)
arch/arm64/include/asm/simd.h +1 -1
index 8e86c9e70e48..abd642c92f86 100644
@@ -29,7 +29,7 @@ static __must_check inline bool may_use_simd(void)
 	 */
 	return !WARN_ON(!system_capabilities_finalized()) &&
 	       system_supports_fpsimd() &&
-	       !in_hardirq() && !irqs_disabled() && !in_nmi();
+	       !in_hardirq() && !in_nmi();
 }
 
 #else /* ! CONFIG_KERNEL_MODE_NEON */
arch/arm64/kernel/fpsimd.c +19 -6
index 3d848c89604e..6d956ed23bd2 100644
@@ -225,10 +225,21 @@ static void fpsimd_bind_task_to_cpu(void);
  */
 static void get_cpu_fpsimd_context(void)
 {
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_bh_disable();
-	else
+	if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+		/*
+		 * The softirq subsystem lacks a true unmask/mask API, and
+		 * re-enabling softirq processing using local_bh_enable() will
+		 * not only unmask softirqs, it will also result in immediate
+		 * delivery of any pending softirqs.
+		 * This is undesirable when running with IRQs disabled, but in
+		 * that case, there is no need to mask softirqs in the first
+		 * place, so only bother doing so when IRQs are enabled.
+		 */
+		if (!irqs_disabled())
+			local_bh_disable();
+	} else {
 		preempt_disable();
+	}
 }
 
 /*
@@ -240,10 +251,12 @@ static void get_cpu_fpsimd_context(void)
  */
 static void put_cpu_fpsimd_context(void)
 {
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_bh_enable();
-	else
+	if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+		if (!irqs_disabled())
+			local_bh_enable();
+	} else {
 		preempt_enable();
+	}
 }
 
 unsigned int task_get_vl(const struct task_struct *task, enum vec_type type)
Signature · 1 line
2.51.0.869.ge66316f041-goog
#8 lore raw

PATCH RESEND v4 5/7 arm64/efi: Drop efi_rt_lock spinlock from EFI arch wrapper

In reply to Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

Since commit

5894cf571e14 ("acpi/prmt: Use EFI runtime sandbox to invoke PRM handlers")

all EFI runtime calls on arm64 are routed via the EFI runtime wrappers, which are serialized using the efi_runtime_lock semaphore.

This means the efi_rt_lock spinlock in the arm64 arch wrapper code has become redundant, and can be dropped. For robustness, replace it with an assert that the EFI runtime lock is in fact held by 'current'.

---
 arch/arm64/kernel/efi.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
arch/arm64/kernel/efi.c +1 -4
index 6c371b158b99..0094f5938ba6 100644
@@ -165,19 +165,16 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)
 	return s;
 }
 
-static DEFINE_RAW_SPINLOCK(efi_rt_lock);
-
 void arch_efi_call_virt_setup(void)
 {
+	efi_runtime_assert_lock_held();
 	efi_virtmap_load();
-	raw_spin_lock(&efi_rt_lock);
 	__efi_fpsimd_begin();
 }
 
 void arch_efi_call_virt_teardown(void)
 {
 	__efi_fpsimd_end();
-	raw_spin_unlock(&efi_rt_lock);
 	efi_virtmap_unload();
 }
 
Signature · 1 line
2.51.0.869.ge66316f041-goog
#9 lore raw

PATCH RESEND v4 6/7 arm64/efi: Move uaccess en/disable out of efi_set_pgd()

In reply to Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

efi_set_pgd() will no longer be called when invoking EFI runtime services via the efi_rts_wq work queue, but the uaccess en/disable are still needed when using PAN emulation using TTBR0 switching. So move these into the callers.

---
 arch/arm64/include/asm/efi.h | 13 +++----------
 arch/arm64/kernel/efi.c      | 18 ++++++++++++++++++
 2 files changed, 21 insertions(+), 10 deletions(-)
arch/arm64/include/asm/efi.h +3 -10
index bcd5622aa096..aa91165ca140 100644
@@ -126,21 +126,14 @@ static inline void efi_set_pgd(struct mm_struct *mm)
 		if (mm != current->active_mm) {
 			/*
 			 * Update the current thread's saved ttbr0 since it is
-			 * restored as part of a return from exception. Enable
-			 * access to the valid TTBR0_EL1 and invoke the errata
-			 * workaround directly since there is no return from
-			 * exception when invoking the EFI run-time services.
+			 * restored as part of a return from exception.
 			 */
 			update_saved_ttbr0(current, mm);
-			uaccess_ttbr0_enable();
-			post_ttbr_update_workaround();
 		} else {
 			/*
-			 * Defer the switch to the current thread's TTBR0_EL1
-			 * until uaccess_enable(). Restore the current
-			 * thread's saved ttbr0 corresponding to its active_mm
+			 * Restore the current thread's saved ttbr0
+			 * corresponding to its active_mm
 			 */
-			uaccess_ttbr0_disable();
 			update_saved_ttbr0(current, current->active_mm);
 		}
 	}
arch/arm64/kernel/efi.c +18 -0
index 0094f5938ba6..85f65d5c863c 100644
@@ -169,12 +169,30 @@ void arch_efi_call_virt_setup(void)
 {
 	efi_runtime_assert_lock_held();
 	efi_virtmap_load();
+
+	/*
+	 * Enable access to the valid TTBR0_EL1 and invoke the errata
+	 * workaround directly since there is no return from exception when
+	 * invoking the EFI run-time services.
+	 */
+	uaccess_ttbr0_enable();
+	post_ttbr_update_workaround();
+
 	__efi_fpsimd_begin();
 }
 
 void arch_efi_call_virt_teardown(void)
 {
 	__efi_fpsimd_end();
+
+	/*
+	 * Defer the switch to the current thread's TTBR0_EL1 until
+	 * uaccess_enable(). Do so before efi_virtmap_unload() updates the
+	 * saved TTBR0 value, so the userland page tables are not activated
+	 * inadvertently over the back of an exception.
+	 */
+	uaccess_ttbr0_disable();
+
 	efi_virtmap_unload();
 }
 
Signature · 1 line
2.51.0.869.ge66316f041-goog
#10 lore raw

PATCH RESEND v4 7/7 arm64/efi: Call EFI runtime services without disabling preemption

In reply to Ard Biesheuvel

From: Ard Biesheuvel <[email protected]>

The only remaining reason why EFI runtime services are invoked with preemption disabled is the fact that the mm is swapped out behind the back of the context switching code.

The kernel no longer disables preemption in kernel_neon_begin(). Furthermore, the EFI spec is being clarified to explicitly state that only baseline FP/SIMD is permitted in EFI runtime service implementations, and so the existing kernel mode NEON context switching code is sufficient to preserve and restore the execution context of an in-progress EFI runtime service call.

Most EFI calls are made from the efi_rts_wq, which is serviced by a kthread. As kthreads never return to user space, they usually don't have an mm, and so we can use the existing infrastructure to swap in the efi_mm while the EFI call is in progress. This is visible to the scheduler, which will therefore reactivate the selected mm when switching out the kthread and back in again.

Given that the EFI spec explicitly permits runtime services to be called with interrupts enabled, firmware code is already required to tolerate interruptions. So rather than disable preemption, disable only migration so that EFI runtime services are less likely to cause scheduling delays. To avoid potential issues where runtime services are interrupted while polling the secure firmware for async completions, keep migration disabled so that a runtime service invocation does not resume on a different CPU from the one it was started on.

Note, though, that the firmware executes at the same privilege level as the kernel, and is therefore able to disable interrupts altogether.

---
 arch/arm64/kernel/efi.c | 23 ++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)
arch/arm64/kernel/efi.c +21 -2
index 85f65d5c863c..a81cb4aa4738 100644
@@ -10,6 +10,7 @@
 #include <linux/efi.h>
 #include <linux/init.h>
 #include <linux/kmemleak.h>
+#include <linux/kthread.h>
 #include <linux/screen_info.h>
 #include <linux/vmalloc.h>
 
@@ -168,7 +169,20 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)
 void arch_efi_call_virt_setup(void)
 {
 	efi_runtime_assert_lock_held();
-	efi_virtmap_load();
+
+	if (preemptible() && (current->flags & PF_KTHREAD)) {
+		/*
+		 * Disable migration to ensure that a preempted EFI runtime
+		 * service call will be resumed on the same CPU. This avoids
+		 * potential issues with EFI runtime calls that are preempted
+		 * while polling for an asynchronous completion of a secure
+		 * firmware call, which may not permit the CPU to change.
+		 */
+		migrate_disable();
+		kthread_use_mm(&efi_mm);
+	} else {
+		efi_virtmap_load();
+	}
 
 	/*
 	 * Enable access to the valid TTBR0_EL1 and invoke the errata
@@ -193,7 +207,12 @@ void arch_efi_call_virt_teardown(void)
 	 */
 	uaccess_ttbr0_disable();
 
-	efi_virtmap_unload();
+	if (preemptible() && (current->flags & PF_KTHREAD)) {
+		kthread_unuse_mm(&efi_mm);
+		migrate_enable();
+	} else {
+		efi_virtmap_unload();
+	}
 }
 
 asmlinkage u64 *efi_rt_stack_top __ro_after_init;
Signature · 1 line
2.51.0.869.ge66316f041-goog
maintainer Acked-by on the v4 series
#11 lore raw
In reply to Ard Biesheuvel
Quoted 8 lines · Ard Biesheuvel

Ard Biesheuvel (7):
efi: Add missing static initializer for efi_mm::cpus_allowed_lock efi/runtime-wrappers: Keep track of the efi_runtime_lock owner arm64/fpsimd: Don't warn when EFI execution context is preemptible arm64/fpsimd: Permit kernel mode NEON with IRQs off arm64/efi: Drop efi_rt_lock spinlock from EFI arch wrapper arm64/efi: Move uaccess en/disable out of efi_set_pgd() arm64/efi: Call EFI runtime services without disabling preemption

The series looks fine to me:

What do you plan to do with this, merge via the EFI tree? Are there any dependencies?

Signature · 1 line
Catalin
maintainer
#12 lore raw
Quoted 11 lines · Catalin Marinas
Quoted 8 lines · Ard Biesheuvel

Ard Biesheuvel (7):
efi: Add missing static initializer for efi_mm::cpus_allowed_lock efi/runtime-wrappers: Keep track of the efi_runtime_lock owner arm64/fpsimd: Don't warn when EFI execution context is preemptible arm64/fpsimd: Permit kernel mode NEON with IRQs off arm64/efi: Drop efi_rt_lock spinlock from EFI arch wrapper arm64/efi: Move uaccess en/disable out of efi_set_pgd() arm64/efi: Call EFI runtime services without disabling preemption

Quoted 7 lines

The series looks fine to me:

What do you plan to do with this, merge via the EFI tree? Are there any dependencies?

As you prefer - I can take it via the EFI tree, or you can take it. There are no conflicts with the other kernel mode FP/SIMD related changes that we are taking via libcrypto, and there are no other dependencies that I am aware of.

maintainer
#13 lore raw
Quoted 16 lines · Ard Biesheuvel
Quoted 15 lines · Catalin Marinas
Quoted 8 lines · Ard Biesheuvel

Ard Biesheuvel (7):
efi: Add missing static initializer for efi_mm::cpus_allowed_lock efi/runtime-wrappers: Keep track of the efi_runtime_lock owner arm64/fpsimd: Don't warn when EFI execution context is preemptible arm64/fpsimd: Permit kernel mode NEON with IRQs off arm64/efi: Drop efi_rt_lock spinlock from EFI arch wrapper arm64/efi: Move uaccess en/disable out of efi_set_pgd() arm64/efi: Call EFI runtime services without disabling preemption

The series looks fine to me:

Quoted 7 lines
Quoted 2 lines

What do you plan to do with this, merge via the EFI tree? Are there any dependencies?

As you prefer - I can take it via the EFI tree, or you can take it. There are no conflicts with the other kernel mode FP/SIMD related changes that we are taking via libcrypto, and there are no other dependencies that I am aware of.

OK, I'll queue them via arm64 shortly.

Signature · 1 line
Catalin
maintainer Applied
#14 lore raw
In reply to Ard Biesheuvel
Quoted 8 lines · Ard Biesheuvel

The arm64 port permits the use of the baseline FP/SIMD register file in kernel mode, and no longer requires preemption to be disabled. Now that the EFI spec is being clarified to state that EFI runtime services may only use baseline FP/SIMD, the fact that EFI may code may use FP/SIMD registers (while executing at the same privilege level as the kernel) is no longer a reason to disable preemption when invoking them.

[...]

Applied to arm64 (for-next/efi-preempt), thanks!

[1/7] efi: Add missing static initializer for efi_mm::cpus_allowed_lock

      https://git.kernel.org/arm64/c/40374d308e4e
[2/7] efi/runtime-wrappers: Keep track of the efi_runtime_lock owner
      https://git.kernel.org/arm64/c/a2860501203c
[3/7] arm64/fpsimd: Don't warn when EFI execution context is preemptible
      https://git.kernel.org/arm64/c/1d038e801833
[4/7] arm64/fpsimd: Permit kernel mode NEON with IRQs off
      https://git.kernel.org/arm64/c/7137a203b251
[5/7] arm64/efi: Drop efi_rt_lock spinlock from EFI arch wrapper
      https://git.kernel.org/arm64/c/1068cb52e8ef
[6/7] arm64/efi: Move uaccess en/disable out of efi_set_pgd()
      https://git.kernel.org/arm64/c/6b9c98e65755
[7/7] arm64/efi: Call EFI runtime services without disabling preemption
      https://git.kernel.org/arm64/c/a5baf582f4c0
Signature · 1 line
Catalin