* [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon
@ 2026-07-08 7:15 Sven Peter
2026-07-08 7:15 ` [PATCH RFC 1/6] dt-bindings: arm: psci: Add EFI conduit Sven Peter
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
Hi,
Usually, idle and sleep state are implemented in firmware running in
e.g. EL3 with the kernel trapping into that from EL2. Unfortunately,
there's no EL3 on Apple Silicon machines and we'd rather not run the
kernel in EL1 since this would result in losing KVM support.
While the shallower states could be implemented inside a custom cpuidle
driver (like we do downstream, see [1]) the deeper states result in a
complete loss of state and require bootstraping the cores again which is
quite involved. So instead we need some way to call back into our
open-source firmware to be able to handle that. This is even more
important for M4+ which don't even support the architectural wfi anymore
and always lose state when that instruction is executed.
Luckily, EFI runtime services provide much of scaffolding we need,
namely a way to keep some code and data mapped and the ability to jump
into there from inside the kernel.
This series adds a custom EFI table that points to a PSCI entry point
(plus some other stuff that has to be available before EFI runtime
services are set up) and adds support for this new conduit to the psci
code. We can't directly use the normal EFI runtime path because that one
takes a sleeping lock and we need to be able to call into PSCI from
atomic context during e.g. cpu bringup or during idle.
It also adds support for specifying the specific MAIR attributes for EFI
runtime mappings as defined in the latest UEFI spec since Apple Silicon
is rather allergic to using Device-nGnRnE vs. Device-nGnRE for its MMIO.
This all results in a surprisingly small diffstat. I believe this
approach was originally suggested in some IRC discussion years ago,
possibly by Ard, but I can't find the old logs anymore.
Happy to add a Suggested-by tag though if anyone remembers.
The firmware implementation I used for testing can be found at [2] and
the full kernel tree with this series applied at [3].
Best,
Sven
[1] https://github.com/AsahiLinux/linux/blob/asahi/drivers/cpuidle/cpuidle-apple.c
[2] https://github.com/AsahiLinux/m1n1/tree/psci-via-efi
[3] https://git.kernel.org/pub/scm/linux/kernel/git/sven/linux.git/log/?h=efi-psci
Signed-off-by: Sven Peter <sven@kernel.org>
---
Sven Peter (6):
dt-bindings: arm: psci: Add EFI conduit
arm64/efi: Add and parse custom PSCI EFI configuration table
efi: Add EFI_MEMORY_ISA_{MASK,VALID}
arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE
firmware/psci: Add EFI runtime conduit
arm64: dts: apple: t8103: Add PSCI and CPU idle states
Documentation/devicetree/bindings/arm/psci.yaml | 12 ++++
arch/arm64/boot/dts/apple/t8103.dtsi | 51 ++++++++++++---
arch/arm64/include/asm/efi.h | 56 +++++++++++++++++
arch/arm64/kernel/efi.c | 82 ++++++++++++++++++++++++-
arch/arm64/kernel/setup.c | 2 +-
drivers/firmware/psci/psci.c | 64 +++++++++++++++++++
include/linux/arm-smccc.h | 1 +
include/linux/efi.h | 3 +
8 files changed, 261 insertions(+), 10 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260625-efi-psci-a9b67bf6f145
Best regards,
--
Sven Peter <sven@kernel.org>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH RFC 1/6] dt-bindings: arm: psci: Add EFI conduit
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
@ 2026-07-08 7:15 ` Sven Peter
2026-07-08 7:15 ` [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table Sven Peter
` (4 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
Apple Silicon machines run the kernel in the highest available exception
level and can't trap to anything higher for PSCI calls. Instead, we will
add support for a conduit based on EFI runtime services to be able to
share the same level with the kernel itself.
Add the conduit to the bindings.
Signed-off-by: Sven Peter <sven@kernel.org>
---
Documentation/devicetree/bindings/arm/psci.yaml | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/psci.yaml b/Documentation/devicetree/bindings/arm/psci.yaml
index 6e2e0c551841..f9d607e1a213 100644
--- a/Documentation/devicetree/bindings/arm/psci.yaml
+++ b/Documentation/devicetree/bindings/arm/psci.yaml
@@ -28,6 +28,14 @@ description: |+
Note that the immediate field of the trapping instruction must be set
to #0.
+ Alternatively, when the "efi" method is used, the PSCI functions are not
+ invoked by trapping to a higher privilege level. Instead, they are backed
+ by EFI runtime services: the firmware exposes a PSCI handler that the OS
+ invokes through an EFI runtime call. This method is intended for platforms
+ where the kernel runs at the highest privilege level, leaving no higher level
+ to trap into; the firmware shares that same level and is instead reached
+ through EFI runtime services.
+
[2] Power State Coordination Interface (PSCI) specification
http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf
@@ -69,6 +77,10 @@ properties:
- smc
# HVC #0, with the register assignments specified in this binding.
- hvc
+ # The PSCI functions are implemented by EFI runtime services, and are
+ # invoked through a runtime call into the firmware rather than via an
+ # SMC or HVC trap.
+ - efi
cpu_suspend:
$ref: /schemas/types.yaml#/definitions/uint32
--
2.55.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
2026-07-08 7:15 ` [PATCH RFC 1/6] dt-bindings: arm: psci: Add EFI conduit Sven Peter
@ 2026-07-08 7:15 ` Sven Peter
2026-07-08 7:39 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID} Sven Peter
` (3 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
On Apple Silicon, the kernel runs in the highest privilege level (EL2)
leaving no higher level to trap into for PSCI firmware calls. Instead,
we keep the firmware in the same level and expose it as an EFI runtime
service. Add a new custom EFI configuraiton table and extract the
pointer to the PSCI handler from it such that it can later be used by
the PSCI code.
The handler is invoked as a plain call under the EFI virtual mapping rather
than through the EFI runtime services dispatch path, so the usual runtime
call environment is not set up around it. This relies on two guarantees
from the firmware: the handler must not use FP/SIMD or any other
lazily-saved CPU state, so the caller can skip saving and restoring it,
and it must be reentrant and independent of the other EFI runtime
services, so it can be invoked from the cpuidle and secondary CPU
bring-up paths which cannot take the sleeping efi_runtime_lock.
Signed-off-by: Sven Peter <sven@kernel.org>
---
arch/arm64/include/asm/efi.h | 56 ++++++++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/efi.c | 58 ++++++++++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/setup.c | 2 +-
include/linux/efi.h | 1 +
4 files changed, 116 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index e8a9783235cb..bc7cf4dd00ae 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -14,10 +14,12 @@
#ifdef CONFIG_EFI
extern void efi_init(void);
+extern void arm64_efi_init(void);
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg);
#else
#define efi_init()
+#define arm64_efi_init()
static inline
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
@@ -151,4 +153,58 @@ efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f);
void efi_icache_sync(unsigned long start, unsigned long end);
+/*
+ * PSCI handler exposed by the firmware through
+ * LINUX_EFI_ARM_PSCI_HANDLER_TABLE_GUID.
+ *
+ * Unlike the regular EFI runtime services, this handler is invoked as a plain
+ * call under the EFI virtual mapping and not through the runtime services
+ * dispatch path. The OS therefore does not set up the usual runtime call
+ * environment around it, which imposes two requirements on the firmware:
+ *
+ * - The handler must not corrupt FP/SIMD/SVE or any other lazily-saved CPU
+ * state.
+ *
+ * - The handler must be reentrant and independent of the other EFI runtime
+ * services. It is only guaranteed to run with interrupts disabled on the
+ * core it is called.
+ */
+typedef unsigned long efi_psci_handler_t(unsigned long function_id,
+ unsigned long arg0,
+ unsigned long arg1,
+ unsigned long arg2);
+
+#define EFI_PSCI_MAX_FN 0x20
+
+/**
+ * struct efi_psci_table - firmware-provided PSCI conduit table
+ * @version: PSCI version implemented by the firmware, in the format
+ * returned by PSCI_VERSION (major in bits 16-30, minor in
+ * bits 0-15).
+ * @num_features: Number of valid entries in @features. Function numbers at
+ * or above this value are treated as not supported. Guaranteed
+ * to be at least 0x20.
+ * @psci_handler: Firmware entry point invoked for PSCI calls that are not
+ * served from the cached fields above. Called via
+ * arm64_efi_psci_call() with the EFI runtime mapping active.
+ * @features: PSCI_FEATURES results indexed by PSCI function number (the
+ * low byte of the function ID, which is identical for the
+ * SMC32 and SMC64 variants).
+ *
+ * Populated at boot from the firmware's PSCI handler configuration table
+ * (LINUX_EFI_ARM_PSCI_HANDLER_TABLE_GUID). @version, @num_features and
+ * @features cache the answers to the PSCI calls that must be serviced before
+ * EFI runtime services can be invoked. All other calls are forwarded to
+ * @psci_handler using arm64_efi_psci_call().
+ */
+extern struct efi_psci_table {
+ u32 version;
+ u32 num_features;
+ efi_psci_handler_t *psci_handler;
+ s32 features[EFI_PSCI_MAX_FN];
+} efi_psci;
+
+unsigned long arm64_efi_psci_call(unsigned long function_id, unsigned long arg0,
+ unsigned long arg1, unsigned long arg2);
+
#endif /* _ASM_EFI_H */
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 30cd7f804398..e40885567f9e 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -18,6 +18,7 @@
#include <asm/stacktrace.h>
#include <asm/vmap_stack.h>
+
static bool region_is_misaligned(const efi_memory_desc_t *md)
{
if (PAGE_SIZE == EFI_PAGE_SIZE)
@@ -242,6 +243,63 @@ bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
/* EFI requires 8 KiB of stack space for runtime services */
static_assert(THREAD_SIZE >= SZ_8K);
+struct efi_psci_table efi_psci __ro_after_init;
+static unsigned long efi_psci_handler_table __initdata = EFI_INVALID_TABLE_ADDR;
+const efi_config_table_type_t efi_arch_tables[] __initconst = {
+ {LINUX_EFI_ARM_PSCI_HANDLER_TABLE_GUID, &efi_psci_handler_table},
+ {}
+};
+
+static void __init arm64_efi_init_psci(void)
+{
+ struct efi_psci_table *psci;
+
+ if (efi_psci_handler_table == EFI_INVALID_TABLE_ADDR)
+ return;
+
+ psci = early_memremap_ro(efi_psci_handler_table, sizeof(*psci));
+ if (psci == NULL) {
+ pr_warn("Unable to map PSCI table.\n");
+ return;
+ }
+
+ memcpy(&efi_psci, psci, sizeof(*psci));
+ early_memunmap(psci, sizeof(*psci));
+}
+
+void __init arm64_efi_init(void)
+{
+ efi_init();
+ arm64_efi_init_psci();
+}
+
+unsigned long arm64_efi_psci_call(unsigned long function_id, unsigned long arg0,
+ unsigned long arg1, unsigned long arg2)
+{
+ unsigned long ret, flags;
+
+ /*
+ * Note that unlike for regular EFI runtime calls we don't have to save
+ * FP/SIMD state here because the handler ABI forbids using those.
+ * Likewise, we do not take any lock here because the handler has to be
+ * re-entrant. We couldn't take the sleeping efi_runtime_lock here
+ * anyway because we may be called from atomic context for cpuidle
+ * and CPU bring-up.
+ */
+ local_irq_save(flags);
+ efi_virtmap_load();
+ uaccess_ttbr0_enable();
+ post_ttbr_update_workaround();
+
+ ret = efi_psci.psci_handler(function_id, arg0, arg1, arg2);
+
+ uaccess_ttbr0_disable();
+ efi_virtmap_unload();
+ local_irq_restore(flags);
+
+ return ret;
+}
+
static int __init arm64_efi_rt_init(void)
{
void *p;
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 23c05dc7a8f2..01735fcacf2a 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -320,7 +320,7 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
cpu_uninstall_idmap();
xen_early_init();
- efi_init();
+ arm64_efi_init();
if (!efi_enabled(EFI_BOOT)) {
if ((u64)_text % MIN_KIMG_ALIGN)
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ccbc35479684..f3db3a18b540 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -422,6 +422,7 @@ void efi_native_runtime_setup(void);
#define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x484c, 0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47)
#define LINUX_EFI_BOOT_MEMMAP_GUID EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4)
#define LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID EFI_GUID(0xd5d1de3c, 0x105c, 0x44f9, 0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31)
+#define LINUX_EFI_ARM_PSCI_HANDLER_TABLE_GUID EFI_GUID(0xf9b47651, 0x4674, 0x4ed9, 0x94, 0x3b, 0x5c, 0xea, 0xd2, 0x10, 0x9a, 0x8b)
#define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec, 0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
--
2.55.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID}
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
2026-07-08 7:15 ` [PATCH RFC 1/6] dt-bindings: arm: psci: Add EFI conduit Sven Peter
2026-07-08 7:15 ` [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table Sven Peter
@ 2026-07-08 7:15 ` Sven Peter
2026-07-08 7:25 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE Sven Peter
` (2 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
As defined in UEFI 2.10, Section 7.2.3 [1], if EFI_MEMORY_ISA_VALID is
set the memory region is to be mapped using ISA-specific cacheability
attributes found in EFI_MEMORY_ISA_MASK. On AArch64, these bits are e.g.
defined as the attributes stored in MAIR [2].
[1] https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#efi-boot-services-getmemorymap
[2] https://uefi.org/specs/UEFI/2.10/02_Overview.html#memory-types
Signed-off-by: Sven Peter <sven@kernel.org>
---
include/linux/efi.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/efi.h b/include/linux/efi.h
index f3db3a18b540..882148114068 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -128,6 +128,8 @@ typedef struct {
#define EFI_MEMORY_SP BIT_ULL(18) /* soft reserved */
#define EFI_MEMORY_CPU_CRYPTO BIT_ULL(19) /* supports encryption */
#define EFI_MEMORY_HOT_PLUGGABLE BIT_ULL(20) /* supports unplugging at runtime */
+#define EFI_MEMORY_ISA_MASK GENMASK_ULL(59, 44) /* ISA-specific attributes */
+#define EFI_MEMORY_ISA_VALID BIT_ULL(62) /* EFI_MEMORY_ISA_MASK bits are valid */
#define EFI_MEMORY_RUNTIME BIT_ULL(63) /* range requires runtime mapping */
#define EFI_MEMORY_DESCRIPTOR_VERSION 1
--
2.55.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
` (2 preceding siblings ...)
2026-07-08 7:15 ` [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID} Sven Peter
@ 2026-07-08 7:15 ` Sven Peter
2026-07-08 7:33 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit Sven Peter
2026-07-08 7:15 ` [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states Sven Peter
5 siblings, 1 reply; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
On Apple Silicon, access to MMIO requires Device-nGnRnE while Device-nGnRE
results in SErrors. UEFI defines the EFI_MEMORY_ISA_MASK for cases like
that which just contains the MAIR attribute bits. We cannot support any
other Device- types without changing MAIR so just warn and fall back to
the previous default, Device-nGnRE, if we encounter anything else.
Signed-off-by: Sven Peter <sven@kernel.org>
---
arch/arm64/kernel/efi.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index e40885567f9e..50ba8e9112fc 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -7,6 +7,7 @@
* Copyright (C) 2013, 2014 Linaro Ltd.
*/
+#include <linux/bitfield.h>
#include <linux/efi.h>
#include <linux/init.h>
#include <linux/kmemleak.h>
@@ -16,6 +17,7 @@
#include <asm/efi.h>
#include <asm/stacktrace.h>
+#include <asm/sysreg.h>
#include <asm/vmap_stack.h>
@@ -38,7 +40,27 @@ static __init ptval_t create_mapping_protection(efi_memory_desc_t *md)
u32 type = md->type;
if (type == EFI_MEMORY_MAPPED_IO) {
- pgprot_t prot = __pgprot(PROT_DEVICE_nGnRE);
+ pgprot_t prot;
+
+ if (attr & EFI_MEMORY_ISA_VALID) {
+ u8 mair = FIELD_GET(EFI_MEMORY_ISA_MASK, attr);
+
+ switch (mair) {
+ case MAIR_ATTR_DEVICE_nGnRnE:
+ prot = __pgprot(PROT_DEVICE_nGnRnE);
+ break;
+ case MAIR_ATTR_DEVICE_nGnRE:
+ prot = __pgprot(PROT_DEVICE_nGnRE);
+ break;
+ default:
+ prot = __pgprot(PROT_DEVICE_nGnRE);
+ pr_warn("unsupported MAIR attribute %#x for EFI MMIO region at 0x%llx, using Device-nGnRE\n",
+ mair, md->phys_addr);
+ break;
+ }
+ } else {
+ prot = __pgprot(PROT_DEVICE_nGnRE);
+ }
if (arm64_is_protected_mmio(md->phys_addr,
md->num_pages << EFI_PAGE_SHIFT))
--
2.55.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
` (3 preceding siblings ...)
2026-07-08 7:15 ` [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE Sven Peter
@ 2026-07-08 7:15 ` Sven Peter
2026-07-08 7:46 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states Sven Peter
5 siblings, 1 reply; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
Apple Silicon machines run the kernel at the highest available exception
level, leaving no higher level to trap into for PSCI firmware calls. The
firmware implementing PSCI therefore shares that level with the kernel and
is exposed as an EFI runtime service instead of being reached through an
SMC or HVC trap.
Add a new "efi" conduit that routes PSCI calls to the handler extracted
from the custom EFI configuration table.
PSCI_VERSION, MIGRATE_INFO_TYPE and PSCI_FEATURES are queried during early
CPU bring-up before EFI runtime services are available and have to be
answered directly from the firmware-provided table.
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/firmware/psci/psci.c | 64 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/arm-smccc.h | 1 +
2 files changed, 65 insertions(+)
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index e73bae6cb23a..af022bf2cb9f 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -10,6 +10,7 @@
#include <linux/arm-smccc.h>
#include <linux/cpuidle.h>
#include <linux/debugfs.h>
+#include <linux/efi.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
@@ -24,6 +25,7 @@
#include <asm/cpuidle.h>
#include <asm/cputype.h>
+#include <asm/efi.h>
#include <asm/hypervisor.h>
#include <asm/system_misc.h>
#include <asm/smp_plat.h>
@@ -131,6 +133,63 @@ __invoke_psci_fn_smc(unsigned long function_id,
return res.a0;
}
+#if IS_ENABLED(CONFIG_EFI) && IS_ENABLED(CONFIG_ARM64)
+static bool efi_psci_fn_valid(unsigned long function_id)
+{
+ if (function_id >= PSCI_0_2_FN_BASE &&
+ function_id <= PSCI_0_2_FN(EFI_PSCI_MAX_FN))
+ return true;
+
+ if (function_id >= PSCI_0_2_FN64_BASE &&
+ function_id <= PSCI_0_2_FN64(EFI_PSCI_MAX_FN))
+ return true;
+
+ return false;
+}
+
+static unsigned long __invoke_psci_fn_efi(unsigned long function_id,
+ unsigned long arg0,
+ unsigned long arg1,
+ unsigned long arg2)
+{
+ u32 fn;
+
+ /* These are called before EFI runtime services are available */
+ switch (function_id) {
+ case PSCI_0_2_FN_PSCI_VERSION:
+ return efi_psci.version;
+ case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+ return PSCI_0_2_TOS_MP;
+ case PSCI_1_0_FN_PSCI_FEATURES:
+ if (!efi_psci_fn_valid(arg0))
+ return PSCI_RET_NOT_SUPPORTED;
+ fn = arg0 & 0xff;
+ if (fn >= efi_psci.num_features || fn >= EFI_PSCI_MAX_FN)
+ return PSCI_RET_NOT_SUPPORTED;
+ return efi_psci.features[fn];
+ }
+
+ if (!efi_psci_fn_valid(function_id))
+ return PSCI_RET_NOT_SUPPORTED;
+
+ if (WARN_ON_ONCE(!efi_psci.psci_handler))
+ return PSCI_RET_NOT_SUPPORTED;
+ if (WARN_ON_ONCE(!efi_enabled(EFI_RUNTIME_SERVICES)))
+ return PSCI_RET_NOT_SUPPORTED;
+
+ return arm64_efi_psci_call(function_id, arg0, arg1, arg2);
+}
+#else
+static unsigned long __invoke_psci_fn_efi(unsigned long function_id,
+ unsigned long arg0,
+ unsigned long arg1,
+ unsigned long arg2)
+{
+ WARN(1, "EFI PSCI conduit invoked but kernel has not EFI support");
+ return PSCI_RET_NOT_SUPPORTED;
+}
+#endif
+
static __always_inline int psci_to_linux_errno(int errno)
{
switch (errno) {
@@ -277,6 +336,9 @@ static void set_conduit(enum arm_smccc_conduit conduit)
case SMCCC_CONDUIT_SMC:
invoke_psci_fn = __invoke_psci_fn_smc;
break;
+ case SMCCC_CONDUIT_EFI:
+ invoke_psci_fn = __invoke_psci_fn_efi;
+ break;
default:
WARN(1, "Unexpected PSCI conduit %d\n", conduit);
}
@@ -299,6 +361,8 @@ static int get_set_conduit_method(const struct device_node *np)
set_conduit(SMCCC_CONDUIT_HVC);
} else if (!strcmp("smc", method)) {
set_conduit(SMCCC_CONDUIT_SMC);
+ } else if (!strcmp("efi", method)) {
+ set_conduit(SMCCC_CONDUIT_EFI);
} else {
pr_warn("invalid \"method\" property: %s\n", method);
return -EINVAL;
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 4de81848fe2e..7328e1e222ca 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -322,6 +322,7 @@ enum arm_smccc_conduit {
SMCCC_CONDUIT_NONE,
SMCCC_CONDUIT_SMC,
SMCCC_CONDUIT_HVC,
+ SMCCC_CONDUIT_EFI,
};
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
` (4 preceding siblings ...)
2026-07-08 7:15 ` [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit Sven Peter
@ 2026-07-08 7:15 ` Sven Peter
2026-07-08 7:48 ` sashiko-bot
5 siblings, 1 reply; 12+ messages in thread
From: Sven Peter @ 2026-07-08 7:15 UTC (permalink / raw)
To: Mark Rutland, Lorenzo Pieralisi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Ard Biesheuvel, Ilias Apalodimas, Catalin Marinas,
Will Deacon, Sudeep Holla, Janne Grunau, Neal Gompa
Cc: linux-arm-kernel, devicetree, linux-kernel, linux-efi, asahi,
Sven Peter
Switch the base M1 from spin-table to PSCI and also add two basic idle
states that we support for now.
Signed-off-by: Sven Peter <sven@kernel.org>
---
arch/arm64/boot/dts/apple/t8103.dtsi | 51 ++++++++++++++++++++++++++++++------
1 file changed, 43 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
index da774096b667..3dbcaa698a90 100644
--- a/arch/arm64/boot/dts/apple/t8103.dtsi
+++ b/arch/arm64/boot/dts/apple/t8103.dtsi
@@ -24,6 +24,11 @@ aliases {
gpu = &gpu;
};
+ psci {
+ compatible = "arm,psci-1.0";
+ method = "efi";
+ };
+
cpus {
#address-cells = <2>;
#size-cells = <0>;
@@ -64,7 +69,8 @@ cpu_e0: cpu@0 {
compatible = "apple,icestorm";
device_type = "cpu";
reg = <0x0 0x0>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&ecluster_opp>;
capacity-dmips-mhz = <714>;
@@ -78,7 +84,8 @@ cpu_e1: cpu@1 {
compatible = "apple,icestorm";
device_type = "cpu";
reg = <0x0 0x1>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&ecluster_opp>;
capacity-dmips-mhz = <714>;
@@ -92,7 +99,8 @@ cpu_e2: cpu@2 {
compatible = "apple,icestorm";
device_type = "cpu";
reg = <0x0 0x2>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&ecluster_opp>;
capacity-dmips-mhz = <714>;
@@ -106,7 +114,8 @@ cpu_e3: cpu@3 {
compatible = "apple,icestorm";
device_type = "cpu";
reg = <0x0 0x3>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&ecluster_opp>;
capacity-dmips-mhz = <714>;
@@ -120,7 +129,8 @@ cpu_p0: cpu@10100 {
compatible = "apple,firestorm";
device_type = "cpu";
reg = <0x0 0x10100>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&pcluster_opp>;
capacity-dmips-mhz = <1024>;
@@ -134,7 +144,8 @@ cpu_p1: cpu@10101 {
compatible = "apple,firestorm";
device_type = "cpu";
reg = <0x0 0x10101>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&pcluster_opp>;
capacity-dmips-mhz = <1024>;
@@ -148,7 +159,8 @@ cpu_p2: cpu@10102 {
compatible = "apple,firestorm";
device_type = "cpu";
reg = <0x0 0x10102>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&pcluster_opp>;
capacity-dmips-mhz = <1024>;
@@ -162,7 +174,8 @@ cpu_p3: cpu@10103 {
compatible = "apple,firestorm";
device_type = "cpu";
reg = <0x0 0x10103>;
- enable-method = "spin-table";
+ enable-method = "psci";
+ cpu-idle-states = <&cpu_retention_wfi &cpu_deep_wfi>;
cpu-release-addr = <0 0>; /* To be filled by loader */
operating-points-v2 = <&pcluster_opp>;
capacity-dmips-mhz = <1024>;
@@ -185,6 +198,28 @@ l2_cache_1: l2-cache-1 {
cache-unified;
cache-size = <0xc00000>;
};
+
+ idle-states {
+ entry-method = "psci";
+
+ cpu_retention_wfi: cpu-retention-wfi {
+ compatible = "arm,idle-state";
+ idle-state-name = "wfi";
+ arm,psci-suspend-param = <0x00000000>;
+ entry-latency-us = <1>;
+ exit-latency-us = <1>;
+ min-residency-us = <1>;
+ };
+
+ cpu_deep_wfi: cpu-deep-wfi {
+ compatible = "arm,idle-state";
+ idle-state-name = "deep-wfi";
+ arm,psci-suspend-param = <0x00000001>;
+ entry-latency-us = <5>;
+ exit-latency-us = <5>;
+ min-residency-us = <10000>;
+ };
+ };
};
ecluster_opp: opp-table-0 {
--
2.55.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID}
2026-07-08 7:15 ` [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID} Sven Peter
@ 2026-07-08 7:25 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-08 7:25 UTC (permalink / raw)
To: Sven Peter; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly introduced EFI_MEMORY_ISA_MASK and EFI_MEMORY_ISA_VALID attributes are omitted from the known attributes mask in efi_md_typeattr_format(), breaking human-readable formatting for memory regions using these attributes.
--
--- Patch [3]: [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID} ---
commit c6357eb92aeb854ab69dbec9ea5006a518bddfd0
Author: Sven Peter <sven@kernel.org>
efi: Add EFI_MEMORY_ISA_{MASK,VALID}
This commit introduces new memory region attributes defined in UEFI 2.10
that allow mapping with ISA-specific cacheability attributes.
Link: https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#efi-boot-services-getmemorymap
Link: https://uefi.org/specs/UEFI/2.10/02_Overview.html#memory-types
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index f3db3a18b5404..882148114068f 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -128,6 +128,8 @@ typedef struct {
> #define EFI_MEMORY_SP BIT_ULL(18) /* soft reserved */
> #define EFI_MEMORY_CPU_CRYPTO BIT_ULL(19) /* supports encryption */
> #define EFI_MEMORY_HOT_PLUGGABLE BIT_ULL(20) /* supports unplugging at runtime */
> +#define EFI_MEMORY_ISA_MASK GENMASK_ULL(59, 44) /* ISA-specific attributes */
> +#define EFI_MEMORY_ISA_VALID BIT_ULL(62) /* EFI_MEMORY_ISA_MASK bits are valid */
[Severity: Medium]
Should these new attributes be added to the known attributes whitelist in
efi_md_typeattr_format() over in drivers/firmware/efi/efi.c?
Because they are omitted from the inverted mask check there, if system
firmware provides a memory descriptor containing these new UEFI 2.10
attributes, won't the formatter fall back to printing a raw hexadecimal
string instead of human-readable attributes?
drivers/firmware/efi/efi.c:efi_md_typeattr_format() {
...
if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT |
EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO |
EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP |
EFI_MEMORY_NV | EFI_MEMORY_SP | EFI_MEMORY_CPU_CRYPTO |
EFI_MEMORY_MORE_RELIABLE | EFI_MEMORY_HOT_PLUGGABLE |
EFI_MEMORY_RUNTIME))
snprintf(pos, size, "|attr=0x%016llx]",
(unsigned long long)attr);
...
}
> #define EFI_MEMORY_RUNTIME BIT_ULL(63) /* range requires runtime mapping */
>
> #define EFI_MEMORY_DESCRIPTOR_VERSION 1
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260708-efi-psci-v1-0-9efb3abf0e4c@kernel.org?part=3
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE
2026-07-08 7:15 ` [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE Sven Peter
@ 2026-07-08 7:33 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-08 7:33 UTC (permalink / raw)
To: Sven Peter; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Silent truncation of the 16-bit EFI_MEMORY_ISA_MASK field into a u8 variable, bypassing validation and printing misleading warnings.
--
--- Patch [4]: [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE ---
commit 6b01988498665d7f9d353c79435cf09a3768a624
Author: Sven Peter <sven@kernel.org>
arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE
This patch updates the mapping protection for EFI MMIO regions to honor the
MAIR attributes provided in EFI_MEMORY_ISA_MASK if EFI_MEMORY_ISA_VALID is
set. It defaults to Device-nGnRE for unsupported values or when the valid bit
is absent.
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index e40885567f9e9..50ba8e9112fc8 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
[ ... ]
> @@ -38,7 +40,27 @@ static __init ptval_t create_mapping_protection(efi_memory_desc_t *md)
> u32 type = md->type;
>
> if (type == EFI_MEMORY_MAPPED_IO) {
> - pgprot_t prot = __pgprot(PROT_DEVICE_nGnRE);
> + pgprot_t prot;
> +
> + if (attr & EFI_MEMORY_ISA_VALID) {
> + u8 mair = FIELD_GET(EFI_MEMORY_ISA_MASK, attr);
[Severity: Medium]
Will this silently truncate the extracted field?
EFI_MEMORY_ISA_MASK covers 16 bits (bits 44-59). If the firmware provides an
attribute with non-zero bits in the upper 8 bits of this mask (like 0x0100),
assigning the result of FIELD_GET to a u8 will truncate it to 0x00.
This would cause the switch statement below to incorrectly match
MAIR_ATTR_DEVICE_nGnRnE (0x00) instead of falling through to the default
case. Should mair be a u16 or u32 instead?
> +
> + switch (mair) {
> + case MAIR_ATTR_DEVICE_nGnRnE:
> + prot = __pgprot(PROT_DEVICE_nGnRnE);
> + break;
> + case MAIR_ATTR_DEVICE_nGnRE:
> + prot = __pgprot(PROT_DEVICE_nGnRE);
> + break;
> + default:
> + prot = __pgprot(PROT_DEVICE_nGnRE);
> + pr_warn("unsupported MAIR attribute %#x for EFI MMIO region at 0x%llx, using Device-nGnRE\n",
> + mair, md->phys_addr);
[Severity: Medium]
If truncation occurs above, will this warning print a misleading value?
Because mair is a u8, any unsupported attributes in the upper 8 bits of the
ISA mask will be stripped out before being printed here, obscuring the actual
unsupported bits provided by the firmware.
> + break;
> + }
> + } else {
> + prot = __pgprot(PROT_DEVICE_nGnRE);
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260708-efi-psci-v1-0-9efb3abf0e4c@kernel.org?part=4
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table
2026-07-08 7:15 ` [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table Sven Peter
@ 2026-07-08 7:39 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-08 7:39 UTC (permalink / raw)
To: Sven Peter; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Unclamped `num_features` leads to out-of-bounds read of `efi_psci.features` array.
- [High] PSCI power-down calls (CPU_SUSPEND/CPU_OFF) bypass the `arm64_efi_psci_call` epilogue, causing a permanent `preempt_count` and `TTBR0` leak.
- [High] `arm64_efi_psci_call()` lacks validation of EFI runtime services state, leading to a translation fault and panic if booted with `efi=noruntime`.
--
--- Patch [2]: [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table ---
commit fe3c6fc95ebeb940962943f65e4f442c468d4718
Author: Sven Peter <sven@kernel.org>
arm64/efi: Add and parse custom PSCI EFI configuration table
This commit adds a new custom EFI configuration table and extracts a
pointer to a PSCI handler. It invokes the handler under the EFI virtual
mapping as a plain call rather than through the runtime services dispatch.
This approach relies on the firmware to not use lazy state and to be
reentrant.
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index 30cd7f8043986..e40885567f9e9 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
[ ... ]
> @@ -242,6 +243,63 @@ bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
> /* EFI requires 8 KiB of stack space for runtime services */
> static_assert(THREAD_SIZE >= SZ_8K);
>
> +struct efi_psci_table efi_psci __ro_after_init;
> +static unsigned long efi_psci_handler_table __initdata = EFI_INVALID_TABLE_ADDR;
> +const efi_config_table_type_t efi_arch_tables[] __initconst = {
> + {LINUX_EFI_ARM_PSCI_HANDLER_TABLE_GUID, &efi_psci_handler_table},
> + {}
> +};
> +
> +static void __init arm64_efi_init_psci(void)
> +{
> + struct efi_psci_table *psci;
> +
> + if (efi_psci_handler_table == EFI_INVALID_TABLE_ADDR)
> + return;
> +
> + psci = early_memremap_ro(efi_psci_handler_table, sizeof(*psci));
> + if (psci == NULL) {
> + pr_warn("Unable to map PSCI table.\n");
> + return;
> + }
> +
> + memcpy(&efi_psci, psci, sizeof(*psci));
[Severity: High]
Is it safe to copy the num_features field from untrusted firmware directly
into efi_psci without clamping it to EFI_PSCI_MAX_FN?
The destination structure defines features as a fixed-size array of size
EFI_PSCI_MAX_FN. If firmware provides a num_features value larger than
this, subsequent driver code relying on num_features to indicate the number
of valid entries could read past the end of the efi_psci object in .rodata.
Could we validate and clamp num_features here?
> + early_memunmap(psci, sizeof(*psci));
> +}
> +
> +void __init arm64_efi_init(void)
> +{
> + efi_init();
> + arm64_efi_init_psci();
> +}
> +
> +unsigned long arm64_efi_psci_call(unsigned long function_id, unsigned long arg0,
> + unsigned long arg1, unsigned long arg2)
> +{
> + unsigned long ret, flags;
> +
> + /*
> + * Note that unlike for regular EFI runtime calls we don't have to save
> + * FP/SIMD state here because the handler ABI forbids using those.
> + * Likewise, we do not take any lock here because the handler has to be
> + * re-entrant. We couldn't take the sleeping efi_runtime_lock here
> + * anyway because we may be called from atomic context for cpuidle
> + * and CPU bring-up.
> + */
> + local_irq_save(flags);
> + efi_virtmap_load();
> + uaccess_ttbr0_enable();
> + post_ttbr_update_workaround();
[Severity: High]
What happens here if the system is booted with efi=noruntime?
If EFI runtime services are disabled, arm_enable_runtime_services() returns
early, and efi_mm.pgd is left as NULL. However, arm64_efi_init_psci()
parses the configuration table and registers the handler regardless of whether
efi_enabled(EFI_RUNTIME_SERVICES) is true.
When arm64_efi_psci_call() unconditionally invokes efi_virtmap_load(), it
would pass the NULL efi_mm.pgd to cpu_switch_mm(), programming a garbage
physical address into TTBR0_EL1. This could crash the system with a
translation fault when attempting to execute the unmapped EFI virtual address.
Should we check efi_enabled(EFI_RUNTIME_SERVICES) before registering the
handler or attempting to load the map?
> +
> + ret = efi_psci.psci_handler(function_id, arg0, arg1, arg2);
> +
> + uaccess_ttbr0_disable();
> + efi_virtmap_unload();
> + local_irq_restore(flags);
[Severity: High]
Can PSCI power-down calls (like CPU_SUSPEND or CPU_OFF) bypass this
epilogue?
efi_virtmap_load() calls preempt_disable(), and efi_virtmap_unload()
calls preempt_enable().
Successful CPU power-down calls do not return to the caller. For example, for
CPU_SUSPEND, execution resumes via the kernel's hardware reset vector
(cpu_resume), which bypasses this return path and jumps straight to the
caller of __cpu_suspend_enter.
As a result, the epilogue of arm64_efi_psci_call() is skipped entirely,
permanently leaking the +1 preempt_count and leaving TTBR0_EL1 incorrectly
mapped to efi_mm on the idle thread.
Is there a way to handle this bypass for suspend/off calls?
> +
> + return ret;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260708-efi-psci-v1-0-9efb3abf0e4c@kernel.org?part=2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit
2026-07-08 7:15 ` [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit Sven Peter
@ 2026-07-08 7:46 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-08 7:46 UTC (permalink / raw)
To: Sven Peter; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] State leak and memory corruption during CPU suspend in the EFI PSCI conduit
- [High] Early PSCI call to set OSI mode fails and triggers WARN_ON_ONCE
--
--- Patch [5]: [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit ---
commit faf63cfc78f9ee926dc0d221e33d99624e0fb74d
Author: Sven Peter <sven@kernel.org>
firmware/psci: Add EFI runtime conduit
This patch adds a new "efi" conduit for PSCI firmware calls, routing them
through an EFI runtime service. This is needed for Apple Silicon where the
kernel shares the highest exception level with the firmware. Early CPU
bring-up queries are answered directly from a firmware-provided table before
EFI runtime services are available.
> diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
> index e73bae6cb23a3..af022bf2cb9f2 100644
> --- a/drivers/firmware/psci/psci.c
> +++ b/drivers/firmware/psci/psci.c
> @@ -131,6 +133,63 @@ __invoke_psci_fn_smc(unsigned long function_id,
[ ... ]
> +static unsigned long __invoke_psci_fn_efi(unsigned long function_id,
> + unsigned long arg0,
> + unsigned long arg1,
> + unsigned long arg2)
> +{
> + u32 fn;
> +
> + /* These are called before EFI runtime services are available */
> + switch (function_id) {
> + case PSCI_0_2_FN_PSCI_VERSION:
> + return efi_psci.version;
> + case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
> + return PSCI_0_2_TOS_MP;
> + case PSCI_1_0_FN_PSCI_FEATURES:
> + if (!efi_psci_fn_valid(arg0))
> + return PSCI_RET_NOT_SUPPORTED;
> + fn = arg0 & 0xff;
> + if (fn >= efi_psci.num_features || fn >= EFI_PSCI_MAX_FN)
> + return PSCI_RET_NOT_SUPPORTED;
> + return efi_psci.features[fn];
> + }
> +
> + if (!efi_psci_fn_valid(function_id))
> + return PSCI_RET_NOT_SUPPORTED;
> +
> + if (WARN_ON_ONCE(!efi_psci.psci_handler))
> + return PSCI_RET_NOT_SUPPORTED;
> + if (WARN_ON_ONCE(!efi_enabled(EFI_RUNTIME_SERVICES)))
> + return PSCI_RET_NOT_SUPPORTED;
[Severity: High]
Does this early call switch statement need to handle
PSCI_1_0_FN_SET_SUSPEND_MODE?
During early boot, psci_1_0_init() can invoke psci_set_osi_mode(false), which
calls PSCI_1_0_FN_SET_SUSPEND_MODE:
setup_arch() -> psci_dt_init() -> psci_1_0_init()
psci_set_osi_mode(false);
Since this happens before efi_enabled(EFI_RUNTIME_SERVICES) is true, will this
fall through and trigger the WARN_ON_ONCE above, failing to set the suspend
mode?
> +
> + return arm64_efi_psci_call(function_id, arg0, arg1, arg2);
> +}
[Severity: Critical]
Is it safe to call arm64_efi_psci_call() during CPU suspend?
When cpu_suspend() is called to enter a deep sleep state, it eventually invokes
arm64_efi_psci_call(), which modifies thread_info state:
arch/arm64/kernel/efi.c:arm64_efi_psci_call() {
...
efi_virtmap_load();
uaccess_ttbr0_enable();
post_ttbr_update_workaround();
ret = efi_psci.psci_handler(function_id, arg0, arg1, arg2);
...
}
If the CPU powers down, execution doesn't return directly from the EFI handler.
Instead, it resumes at the hardware reset vector, calls cpu_resume(), restores
context, and returns to the caller of cpu_suspend().
Because this bypasses the cleanup in arm64_efi_psci_call() (like
efi_virtmap_unload()), could this leak the preempt_count increment and leave
thread_info->ttbr0 pointing to the EFI runtime page tables when returning to
userspace?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260708-efi-psci-v1-0-9efb3abf0e4c@kernel.org?part=5
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states
2026-07-08 7:15 ` [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states Sven Peter
@ 2026-07-08 7:48 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-08 7:48 UTC (permalink / raw)
To: Sven Peter; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] State leak (preempt_count) and corruption (saved_ttbr0) in `arm64_efi_psci_call` during CPU/System suspend.
Pre-existing issues:
- [High] Missing Context Synchronization Events (ISB) after writes to control-plane system registers in user access helpers.
--
--- Patch [6]: [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states ---
commit 4618cadb25b8f3f6d74cc463901a493df3b5128c
Author: Sven Peter <sven@kernel.org>
arm64: dts: apple: t8103: Add PSCI and CPU idle states
Switch the base M1 from spin-table to PSCI and also add two basic idle
states that we support for now.
> --- a/arch/arm64/boot/dts/apple/t8103.dtsi
> +++ b/arch/arm64/boot/dts/apple/t8103.dtsi
[ ... ]
> @@ -185,6 +198,28 @@ l2_cache_1: l2-cache-1 {
> cache-unified;
> cache-size = <0xc00000>;
> };
> +
> + idle-states {
> + entry-method = "psci";
> +
> + cpu_retention_wfi: cpu-retention-wfi {
> + compatible = "arm,idle-state";
> + idle-state-name = "wfi";
> + arm,psci-suspend-param = <0x00000000>;
> + entry-latency-us = <1>;
> + exit-latency-us = <1>;
> + min-residency-us = <1>;
> + };
> +
> + cpu_deep_wfi: cpu-deep-wfi {
> + compatible = "arm,idle-state";
> + idle-state-name = "deep-wfi";
> + arm,psci-suspend-param = <0x00000001>;
> + entry-latency-us = <5>;
> + exit-latency-us = <5>;
> + min-residency-us = <10000>;
> + };
[Severity: High]
Does adding this deep suspend state expose a state leak in the EFI PSCI
wrapper?
When the CPU enters deep sleep via CPU_SUSPEND or SYSTEM_SUSPEND, it uses
psci_suspend_finisher which invokes arm64_efi_psci_call() in
arch/arm64/kernel/efi.c. This wrapper calls efi_virtmap_load() (which
disables preemption and swaps ttbr0 to efi_mm) and uaccess_ttbr0_enable().
On success, the firmware powers down the CPU and never returns to the caller
of arm64_efi_psci_call(). Upon waking, the hardware resumes execution at
pa_cpu_resume, restoring the pre-suspend CPU registers and returning directly
to cpu_suspend().
Since the return path of arm64_efi_psci_call() is bypassed,
uaccess_ttbr0_disable() and efi_virtmap_unload() are never executed. Can this
leak the preempt_count increment and leave the saved_ttbr0 page table
reference corrupted, leading to scheduling-while-atomic panics or deadlocks
upon resume?
[Severity: High]
This is a pre-existing issue, but the EFI PSCI path used by this idle state
relies on __uaccess_ttbr0_enable() and __uaccess_ttbr0_disable() in
arch/arm64/include/asm/uaccess.h, which appear to be missing required
synchronization.
In __uaccess_ttbr0_disable(), the code performs sequential writes to control
plane registers without an intervening isb():
write_sysreg(ttbr - RESERVED_SWAPPER_OFFSET, ttbr0_el1);
/* Set reserved ASID */
write_sysreg(ttbr, ttbr1_el1);
isb();
Similarly, __uaccess_ttbr0_enable() writes to ttbr1_el1 and ttbr0_el1
sequentially before the isb().
Architectural rules require an isb() immediately after each write to a
control-plane system register before any subsequent write or read-back. Does
the lack of an intervening isb() here force the pipeline to observe
architecturally undefined state?
> + };
> };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260708-efi-psci-v1-0-9efb3abf0e4c@kernel.org?part=6
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-07-08 7:48 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 7:15 [PATCH RFC 0/6] PSCI-via-EFI to support firmware and kernel sharing EL2 for Apple Silicon Sven Peter
2026-07-08 7:15 ` [PATCH RFC 1/6] dt-bindings: arm: psci: Add EFI conduit Sven Peter
2026-07-08 7:15 ` [PATCH RFC 2/6] arm64/efi: Add and parse custom PSCI EFI configuration table Sven Peter
2026-07-08 7:39 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 3/6] efi: Add EFI_MEMORY_ISA_{MASK,VALID} Sven Peter
2026-07-08 7:25 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 4/6] arm64/efi: Honor EFI_MEMORY_ISA_MASK for Device-nGnRnE vs -nGnRE Sven Peter
2026-07-08 7:33 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit Sven Peter
2026-07-08 7:46 ` sashiko-bot
2026-07-08 7:15 ` [PATCH RFC 6/6] arm64: dts: apple: t8103: Add PSCI and CPU idle states Sven Peter
2026-07-08 7:48 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox