Re: [PATCH] arm64/efi: Do not call EFI runtime services preemptibly under SW PAN

Gus Bourg <[email protected]>
Newsgroups org.kernel.vger.linux-efi,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <CAKL=SEpvBqMjw2Ts4ODYHCJz0K1axwmtrUGKQEJeb_rvuAQk0A@mail.gmail.com>
On Mon, Aug 10, 2026 at 4:04 AM Will Deacon <[email protected]> wrote:
>
> On Fri, Aug 07, 2026 at 04:56:00PM +0300, Ard Biesheuvel wrote:
> >
> >
> > On Fri, 7 Aug 2026, at 15:16, Will Deacon wrote:
> > > On Wed, Aug 05, 2026 at 05:01:44PM -0700, gus bourg wrote:
> > >> From: Gus Bourg <[email protected]>
> > >>
> > >> When PAN is emulated by switching TTBR0_EL1, arch_efi_call_virt_setup()
> > >> installs the EFI mm into TTBR0_EL1 via uaccess_ttbr0_enable(), and only a
> > >> return from exception ever puts it back: check_and_switch_context() skips
> > >> the register write by design ("Defer TTBR0_EL1 setting for user threads to
> > >> uaccess_enable() when emulating PAN"), and __switch_to() does not touch it
> > >> either. switch_mm() updates only thread_info->ttbr0.
> > >>
> > >> Since commit a5baf582f4c0 ("arm64/efi: Call EFI runtime services without
> > >> disabling preemption") the runtime call is preemptible, so the EFI worker
> > >> can now be scheduled out inside that window. An involuntary preemption is
> > >> harmless, because __swpan_entry_el1()/__swpan_exit_el1() save and restore
> > >> the state around the exception. A voluntary reschedule is not: it performs
> > >> no return from exception, so the worker resumes with another task's
> > >> TTBR0_EL1 - or reserved_pg_dir - and the next efi_mm access takes a level 0
> > >> translation fault.
> > >>
> > >> There is such a preemption point in arch_efi_call_virt_setup() itself,
> > >> immediately after the TTBR0 install: __efi_fpsimd_begin() ->
> > >> kernel_neon_begin() -> put_cpu_fpsimd_context() -> local_bh_enable() ->
> > >> preempt_check_resched().
> > >
> > > Hmm, can we move the ttbr0 toggling until after __efi_fpsimd_begin() so
> > > that this preemption point doesn't interfere?
> > >
> >
> > Either that, or tweak the logic so that the switch is done eagerly in
> > this particular case (untested):
> >
> > --- a/arch/arm64/mm/context.c
> > +++ b/arch/arm64/mm/context.c
> > @@ -266,7 +266,7 @@
> >          * Defer TTBR0_EL1 setting for user threads to uaccess_enable() when
> >          * emulating PAN.
> >          */
> > -       if (!system_uses_ttbr0_pan())
> > +       if (!system_uses_ttbr0_pan() || mm_is_efi(mm))
> >                 cpu_switch_mm(mm->pgd, mm);
> >  }
>
> I think I'd prefer to avoid special-casing this, if we can. I was just
> thinking of the diff below.
>
> Will

Tested the v2 approach (__efi_fpsimd_begin() before
uaccess_ttbr0_enable()) on the board that originally reproduced the
oops: Khadas VIM3, Amlogic A311D (4x Cortex-A73 + 2x Cortex-A53, no
FEAT_PAN, so CONFIG_ARM64_SW_TTBR0_PAN is in use), EDK2-based UEFI
firmware, v7.0.12 plus this change alone (my prior workaround removed).

Both firmware description modes were exercised (DeviceTree and ACPI
boots of the same system):

  - 3,000,000 GetVariable calls (three concurrent readers) plus
    200,000 GetTime calls per mode, under sustained CPU contention
    (busy-loop load on half the cores to force scheduling in the call
    window) - i.e. 6.4M calls total across the ACPI and DeviceTree
    boots of the same system
  - SetVariable create/write/delete round-trips in both modes
  - ~15 reboots/power cycles (ResetSystem) and clean shutdowns across
    the session

Zero efi_call_rts faults and zero "Unable to handle kernel access to
user memory" events; efivars and the EFI RTC remained functional
throughout, and poweroff/reboot stayed clean. Under the unpatched
preemptible path the same board faulted within thousands of calls with
comparable contention, so this comfortably clears the reproduction
threshold.

Tested-by: gus bourg <[email protected]>


>
> --->8
>
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index 30cd7f804398..0ec90fd1754e 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
> @@ -184,6 +184,8 @@ void arch_efi_call_virt_setup(void)
>                 efi_virtmap_load();
>         }
>
> +       __efi_fpsimd_begin();
> +
>         /*
>          * Enable access to the valid TTBR0_EL1 and invoke the errata
>          * workaround directly since there is no return from exception when
> @@ -191,8 +193,6 @@ void arch_efi_call_virt_setup(void)
>          */
>         uaccess_ttbr0_enable();
>         post_ttbr_update_workaround();
> -
> -       __efi_fpsimd_begin();
>  }
>
>  void arch_efi_call_virt_teardown(void)
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.