feat(psci): make pabandon support generic

Support for aborted powerdowns does not require much dedicated code.
Rather, it is largely a matter of orchestrating things to happen in the
right order.

The only exception to this are older secure world dispatchers, which
assume that a CPU_SUSPEND call will be terminal and therefore can
clobber context. This was patched over in common code and hidden behind
a flag. This patch moves this to the dispatchers themselves.

Dispatchers that don't register svc_suspend{_finish} are unaffected.
Those that do must save the NS context before clobbering it and
restoring in only in case of a pabandon. Due to this operation being
non-trivial, this patch makes the assumption that these dispatchers will
only be present on hardware that does not support pabandon and therefore
does not add any contexting for them. In case this assumption ever
changes, asserts are added that should alert us of this change.

Change-Id: I94a907515b782b4d2136c0d274246cfe1d567c0e
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
diff --git a/Makefile b/Makefile
index 2da1906..125d587 100644
--- a/Makefile
+++ b/Makefile
@@ -647,7 +647,6 @@
 	PSA_FWU_SUPPORT \
 	PSA_FWU_METADATA_FW_STORE_DESC \
 	ENABLE_MPMM \
-	FEAT_PABANDON \
 	FEATURE_DETECTION \
 	TRNG_SUPPORT \
 	ENABLE_ERRATA_ALL \
@@ -856,7 +855,6 @@
 	ENABLE_TRF_FOR_NS \
 	ENABLE_FEAT_HCX \
 	ENABLE_MPMM \
-	FEAT_PABANDON \
 	ENABLE_FEAT_FGT \
 	ENABLE_FEAT_FGT2 \
 	ENABLE_FEAT_FGWTE3 \
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 8a607ac..e2fba99 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -558,12 +558,6 @@
    power domain dynamic power budgeting and limit the triggering of whole-rail
    (i.e. clock chopping) responses to overcurrent conditions. Defaults to ``0``.
 
- - ``FEAT_PABANDON``: Boolean option to enable support for powerdown abandon on
-   Arm cores that support it (currently Gelas and Travis). Extends the PSCI
-   implementation to expect waking up after the terminal ``wfi``. Currently,
-   introduces a performance penalty. Once this is removed, this option will be
-   removed and the feature will be enabled by default. Defaults to ``0``.
-
 -  ``ENABLE_PIE``: Boolean option to enable Position Independent Executable(PIE)
    support within generic code in TF-A. This option is currently only supported
    in BL2, BL31, and BL32 (TSP) for AARCH64 binaries, and
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst
index 72d44ee..e37fe1f 100644
--- a/docs/porting-guide.rst
+++ b/docs/porting-guide.rst
@@ -3635,8 +3635,8 @@
 External Abort handling and RAS Support
 ---------------------------------------
 
-If any cores on the platform support powerdown abandon (i.e. ``FEAT_PABANDON``
-is set, check the "Core powerup and powerdown sequence" in their TRMs), then
+If any cores on the platform support powerdown abandon (check the "Core powerup
+and powerdown sequence" in their TRMs), then
 these functions should be able to handle being called with power domains off and
 after the powerdown ``wfi``. In other words it may run after a call to
 ``pwr_domain_suspend()`` and before a call to ``pwr_domain_suspend_finish()``
diff --git a/include/lib/psci/psci_lib.h b/include/lib/psci/psci_lib.h
index 1e1d5fc..12efe17 100644
--- a/include/lib/psci/psci_lib.h
+++ b/include/lib/psci/psci_lib.h
@@ -25,7 +25,7 @@
 	int32_t (*svc_off)(u_register_t __unused unused);
 	void (*svc_suspend)(u_register_t max_off_pwrlvl);
 	void (*svc_on_finish)(u_register_t __unused unused);
-	void (*svc_suspend_finish)(u_register_t max_off_pwrlvl);
+	void (*svc_suspend_finish)(u_register_t max_off_pwrlvl, bool abandon);
 	int32_t (*svc_migrate)(u_register_t from_cpu, u_register_t to_cpu);
 	int32_t (*svc_migrate_info)(u_register_t *resident_cpu);
 	void (*svc_system_off)(void);
diff --git a/lib/cpus/aarch64/cortex_gelas.S b/lib/cpus/aarch64/cortex_gelas.S
index 4cdec32..fc249ae 100644
--- a/lib/cpus/aarch64/cortex_gelas.S
+++ b/lib/cpus/aarch64/cortex_gelas.S
@@ -21,10 +21,6 @@
 #error "Gelas supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0"
 #endif
 
-#if FEAT_PABANDON == 0
-#error "Gelas must be compiled with FEAT_PABANDON enabled"
-#endif
-
 #if ERRATA_SME_POWER_DOWN == 0
 #error "Gelas needs ERRATA_SME_POWER_DOWN=1 to powerdown correctly"
 #endif
diff --git a/lib/cpus/aarch64/travis.S b/lib/cpus/aarch64/travis.S
index d53e46f..956da5f 100644
--- a/lib/cpus/aarch64/travis.S
+++ b/lib/cpus/aarch64/travis.S
@@ -21,10 +21,6 @@
 #error "Travis supports only AArch64. Compile with CTX_INCLUDE_AARCH32_REGS=0"
 #endif
 
-#if FEAT_PABANDON == 0
-#error "Travis must be compiled with FEAT_PABANDON enabled"
-#endif
-
 #if ERRATA_SME_POWER_DOWN == 0
 #error "Travis needs ERRATA_SME_POWER_DOWN=1 to powerdown correctly"
 #endif
diff --git a/lib/psci/psci_common.c b/lib/psci/psci_common.c
index 15a32f1..04917ea 100644
--- a/lib/psci/psci_common.c
+++ b/lib/psci/psci_common.c
@@ -1056,7 +1056,7 @@
 		unsigned int max_off_lvl = psci_find_max_off_lvl(&state_info);
 
 		assert(max_off_lvl != PSCI_INVALID_PWR_LVL);
-		psci_cpu_suspend_to_powerdown_finish(cpu_idx, max_off_lvl, &state_info);
+		psci_cpu_suspend_to_powerdown_finish(cpu_idx, max_off_lvl, &state_info, false);
 	}
 
 	/*
@@ -1311,15 +1311,13 @@
 
 	/*
 	 * Waking up does not require hardware-assisted coherency, but that is
-	 * the case for every core that can wake up. Untangling the cache
-	 * coherency code from powerdown is a non-trivial effort which isn't
-	 * needed for our purposes.
+	 * the case for every core that can wake up. Can either happen because
+	 * of errata or pabandon.
 	 */
-#if !FEAT_PABANDON || !defined(__aarch64__)
-	ERROR("Systems without FEAT_PABANDON shouldn't wake up.\n");
+#if !defined(__aarch64__) || !HW_ASSISTED_COHERENCY
+	ERROR("AArch32 systems shouldn't wake up.\n");
 	panic();
-#else /* FEAT_PABANDON */
-
+#endif
 	/*
 	 * Begin unwinding. Everything can be shared with CPU_ON and co later,
 	 * except the CPU specific bit. Cores that have hardware-assisted
@@ -1327,7 +1325,6 @@
 	 * the simplest way to achieve this
 	 */
 	prepare_cpu_pwr_dwn(power_level);
-#endif /* FEAT_PABANDON */
 }
 
 /*******************************************************************************
diff --git a/lib/psci/psci_private.h b/lib/psci/psci_private.h
index d4c6415..446f23d 100644
--- a/lib/psci/psci_private.h
+++ b/lib/psci/psci_private.h
@@ -341,7 +341,10 @@
 			   psci_power_state_t *state_info,
 			   unsigned int is_power_down_state);
 
-void psci_cpu_suspend_to_powerdown_finish(unsigned int cpu_idx, unsigned int max_off_lvl, const psci_power_state_t *state_info);
+void psci_cpu_suspend_to_powerdown_finish(unsigned int cpu_idx,
+					  unsigned int max_off_lvl,
+					  const psci_power_state_t *state_info,
+					  bool abandon);
 
 /* Private exported functions from psci_helpers.S */
 void psci_do_pwrdown_cache_maintenance(void);
diff --git a/lib/psci/psci_suspend.c b/lib/psci/psci_suspend.c
index 73b9a67..3b0364c 100644
--- a/lib/psci/psci_suspend.c
+++ b/lib/psci/psci_suspend.c
@@ -184,17 +184,6 @@
 #endif
 
 	if (is_power_down_state != 0U) {
-		/*
-		 * WHen CTX_INCLUDE_EL2_REGS is usnet, we're probably runnig
-		 * with some SPD that assumes the core is going off so it
-		 * doesn't bother saving NS's context. Do that here until we
-		 * figure out a way to make this coherent.
-		 */
-#if FEAT_PABANDON
-#if !CTX_INCLUDE_EL2_REGS
-		cm_el1_sysregs_context_save(NON_SECURE);
-#endif
-#endif
 		max_off_lvl = psci_find_max_off_lvl(state_info);
 		psci_suspend_to_pwrdown_start(idx, end_pwrlvl, end_pwrlvl, state_info);
 	}
@@ -274,13 +263,7 @@
 	 * the system back to a usable state.
 	 */
 	if (is_power_down_state != 0U) {
-#if FEAT_PABANDON
-		psci_cpu_suspend_to_powerdown_finish(idx, max_off_lvl, state_info);
-
-#if !CTX_INCLUDE_EL2_REGS
-		cm_el1_sysregs_context_restore(NON_SECURE);
-#endif
-#endif
+		psci_cpu_suspend_to_powerdown_finish(idx, max_off_lvl, state_info, true);
 	} else {
 		psci_cpu_suspend_to_standby_finish(end_pwrlvl, state_info);
 	}
@@ -307,7 +290,7 @@
  * are called by the common finisher routine in psci_common.c. The `state_info`
  * is the psci_power_state from which this CPU has woken up from.
  ******************************************************************************/
-void psci_cpu_suspend_to_powerdown_finish(unsigned int cpu_idx, unsigned int max_off_lvl, const psci_power_state_t *state_info)
+void psci_cpu_suspend_to_powerdown_finish(unsigned int cpu_idx, unsigned int max_off_lvl, const psci_power_state_t *state_info, bool abandon)
 {
 	unsigned int counter_freq;
 
@@ -345,7 +328,7 @@
 	 * error, it's expected to assert within
 	 */
 	if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_suspend_finish != NULL)) {
-		psci_spd_pm->svc_suspend_finish(max_off_lvl);
+		psci_spd_pm->svc_suspend_finish(max_off_lvl, abandon);
 	}
 
 	/* This loses its meaning when not suspending, reset so it's correct for OFF */
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 516d9b4..1ddcd6f 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -88,9 +88,6 @@
 # Enable the Maximum Power Mitigation Mechanism on supporting cores.
 ENABLE_MPMM			:= 0
 
-# Enable support for powerdown abandons
-FEAT_PABANDON			:= 0
-
 # Flag to Enable Position Independant support (PIE)
 ENABLE_PIE			:= 0
 
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index 8e16829..fd7e386 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -243,7 +243,6 @@
 #Build AArch64-only CPUs with no FVP model yet.
 ifeq (${BUILD_CPUS_WITH_NO_FVP_MODEL},1)
 	# travis/gelas need these
-	FEAT_PABANDON	:=	1
 	ERRATA_SME_POWER_DOWN := 1
 	FVP_CPU_LIBS    +=	lib/cpus/aarch64/cortex_gelas.S		\
 				lib/cpus/aarch64/nevis.S		\
diff --git a/plat/arm/board/tc/platform.mk b/plat/arm/board/tc/platform.mk
index b7edf28..4a3dfff 100644
--- a/plat/arm/board/tc/platform.mk
+++ b/plat/arm/board/tc/platform.mk
@@ -123,7 +123,6 @@
 
 # CPU libraries for TARGET_PLATFORM=4
 ifeq (${TARGET_PLATFORM}, 4)
-FEAT_PABANDON	:=	1
 # prevent CME related wakups
 ERRATA_SME_POWER_DOWN := 1
 TC_CPU_SOURCES	+=	lib/cpus/aarch64/cortex_gelas.S \
diff --git a/services/spd/opteed/opteed_pm.c b/services/spd/opteed/opteed_pm.c
index c949823..c4a79f5 100644
--- a/services/spd/opteed/opteed_pm.c
+++ b/services/spd/opteed/opteed_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -137,12 +137,15 @@
  * completed the preceding suspend call. Use that context to program an entry
  * into OPTEE to allow it to do any remaining book keeping
  ******************************************************************************/
-static void opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl)
+static void opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl, bool abandon)
 {
 	int32_t rc = 0;
 	uint32_t linear_id = plat_my_core_pos();
 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
 
+	/* opteed is not expected to be used on platforms with pabandon */
+	assert(!abandon);
+
 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
 		return;
 	}
diff --git a/services/spd/tlkd/tlkd_pm.c b/services/spd/tlkd/tlkd_pm.c
index ed5bf77..ed66245 100644
--- a/services/spd/tlkd/tlkd_pm.c
+++ b/services/spd/tlkd/tlkd_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2025, Arm Limited and Contributors. All rights reserved.
  * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -69,12 +69,15 @@
  * This cpu is being resumed. Inform TLK of the SYSTEM_SUSPEND exit, so
  * that it can pass this information to its Trusted Apps.
  ******************************************************************************/
-static void cpu_resume_handler(u_register_t suspend_level)
+static void cpu_resume_handler(u_register_t suspend_level, bool abandon)
 {
 	gp_regs_t *gp_regs;
 	int cpu = read_mpidr() & MPIDR_CPU_MASK;
 	int32_t rc = 0;
 
+	/* tlkd is not expected to be used on platforms with pabandon */
+	assert(!abandon);
+
 	/*
 	 * TLK runs only on CPU0 and resumes its Trusted Apps during
 	 * SYSTEM_SUSPEND exit. It has no role to play during CPU_SUSPEND
diff --git a/services/spd/trusty/trusty.c b/services/spd/trusty/trusty.c
index aae2d9a..4b44798 100644
--- a/services/spd/trusty/trusty.c
+++ b/services/spd/trusty/trusty.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2025, Arm Limited and Contributors. All rights reserved.
  * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -397,12 +397,21 @@
 
 static void trusty_cpu_suspend_handler(u_register_t max_off_lvl)
 {
+	/* Save NS context in case we need to return to it */
+	cm_el1_sysregs_context_save(NON_SECURE);
+
 	trusty_cpu_suspend(max_off_lvl);
 }
 
-static void trusty_cpu_suspend_finish_handler(u_register_t max_off_lvl)
+static void trusty_cpu_suspend_finish_handler(u_register_t max_off_lvl, bool abandon)
 {
 	trusty_cpu_resume(max_off_lvl);
+
+	/* We're returning back to NS so we need to put back its context */
+	if (abandon) {
+		cm_el1_sysregs_context_restore(NON_SECURE);
+	}
+
 }
 
 static const spd_pm_ops_t trusty_pm = {
diff --git a/services/spd/tspd/tspd_pm.c b/services/spd/tspd/tspd_pm.c
index b95ee8f..d44a807 100644
--- a/services/spd/tspd/tspd_pm.c
+++ b/services/spd/tspd/tspd_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -83,6 +83,10 @@
 
 	/* Program the entry point and enter the TSP */
 	cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->cpu_suspend_entry);
+
+	/* Save NS context in case we need to return to it */
+	cm_el1_sysregs_context_save(NON_SECURE);
+
 	rc = tspd_synchronous_sp_entry(tsp_ctx);
 
 	/*
@@ -147,7 +151,7 @@
  * completed the preceding suspend call. Use that context to program an entry
  * into the TSP to allow it to do any remaining book keeping
  ******************************************************************************/
-static void tspd_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl)
+static void tspd_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl, bool abandon)
 {
 	int32_t rc = 0;
 	uint32_t linear_id = plat_my_core_pos();
@@ -170,6 +174,11 @@
 	if (rc != 0)
 		panic();
 
+	/* We're returning back to NS so we need to put back its context */
+	if (abandon) {
+		cm_el1_sysregs_context_restore(NON_SECURE);
+	}
+
 	/* Update its context to reflect the state the SP is in */
 	set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON);
 }
diff --git a/services/std_svc/spm/el3_spmc/spmc_pm.c b/services/std_svc/spm/el3_spmc/spmc_pm.c
index 0a6215c..b267212 100644
--- a/services/std_svc/spm/el3_spmc/spmc_pm.c
+++ b/services/std_svc/spm/el3_spmc/spmc_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022-2023, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -201,7 +201,7 @@
 /*******************************************************************************
  * spmc_cpu_suspend_finish_handler
  ******************************************************************************/
-static void spmc_cpu_suspend_finish_handler(u_register_t unused)
+static void spmc_cpu_suspend_finish_handler(u_register_t unused, bool abandon)
 {
 	struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
 	unsigned int linear_id = plat_my_core_pos();
@@ -210,6 +210,9 @@
 	/* Sanity check for a NULL pointer dereference. */
 	assert(sp != NULL);
 
+	/* EL3 SPMC is not expected to be used on platforms with pabandon */
+	assert(!abandon);
+
 	/*
 	 * Check if the SP has subscribed for this power management message.
 	 * If not then we don't have anything else to do here.