[PATCH RFC 5/6] firmware/psci: Add EFI runtime conduit
From: Sven Peter
Date: Wed Jul 08 2026 - 03:17:11 EST
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@xxxxxxxxxx>
---
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