fix(spmd): check pwr mgmt status for SPMC framework response

The direct message response received by the SPMD upon a CPU_OFF power
management operation must be a framework message. If message indicates
SPMC denied the CPU_OFF operation, SPMD shall panic.

However, if SPMC does not support receiving power management
related framework messages from SPMD, it will return FFA_ERROR.
In such case, SPMD takes an implementation defined choice to ignore the
the FFA_ERROR and proceed with power management operation.

Change-Id: I18b9ee3fb8fd605bcd4aaa6802c969e9d36ccbe1
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h
index f5a9a2d..0010d2b 100644
--- a/include/services/ffa_svc.h
+++ b/include/services/ffa_svc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020-2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2020-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -57,8 +57,8 @@
 	<< FFA_MSG_SEND_ATTRS_BLK_SHIFT)
 
 /* Defines for FF-A framework messages exchanged using direct messages. */
-#define FFA_FWK_MSG_BIT		BIT(31)
-#define FFA_FWK_MSG_MASK	0xFF
+#define FFA_FWK_MSG_BIT		BIT_32(31)
+#define FFA_FWK_MSG_MASK	U(0xFF)
 #define FFA_FWK_MSG_PSCI	U(0x0)
 
 /* Defines for FF-A power management messages framework messages. */
diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c
index 94634f2..310610d 100644
--- a/services/std_svc/spmd/spmd_main.c
+++ b/services/std_svc/spmd/spmd_main.c
@@ -888,11 +888,15 @@
 		}
 
 		/*
-		 * If there was an SPMD logical partition direct request on-going,
+		 * Perform a synchronous exit:
+		 * 1. If there was an SPMD logical partition direct request on-going,
 		 * return back to the SPMD logical partition so the error can be
 		 * consumed.
+		 * 2. SPMC sent FFA_ERROR in response to a power management
+		 * operation sent through direct request.
 		 */
-		if (is_spmd_logical_sp_dir_req_in_progress(ctx)) {
+		if (is_spmd_logical_sp_dir_req_in_progress(ctx) ||
+		    ctx->psci_operation_ongoing) {
 			assert(secure_origin);
 			spmd_spm_core_sync_exit(0ULL);
 		}
diff --git a/services/std_svc/spmd/spmd_pm.c b/services/std_svc/spmd/spmd_pm.c
index fd89c81..4ed3e2f 100644
--- a/services/std_svc/spmd/spmd_pm.c
+++ b/services/std_svc/spmd/spmd_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -117,6 +117,8 @@
 	spmd_spm_core_context_t *ctx = spmd_get_context();
 	unsigned int linear_id = plat_my_core_pos();
 	int64_t rc;
+	uint32_t ffa_resp_func_id, msg_flags;
+	int status;
 
 	assert(ctx != NULL);
 	assert(ctx->state != SPMC_STATE_OFF);
@@ -137,24 +139,60 @@
 	write_ctx_reg(gpregs, CTX_GPREG_X16, 0);
 	write_ctx_reg(gpregs, CTX_GPREG_X17, 0);
 
+	/* Mark current core as processing a PSCI operation. */
+	ctx->psci_operation_ongoing = true;
+
 	rc = spmd_spm_core_sync_entry(ctx);
+
 	if (rc != 0ULL) {
 		ERROR("%s failed (%" PRIu64 ") on CPU%u\n", __func__, rc, linear_id);
 	}
 
+	ctx->psci_operation_ongoing = false;
+
 	/* Expect a direct message response from the SPMC. */
-	u_register_t ffa_resp_func = read_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx),
+	ffa_resp_func_id = (uint32_t)read_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx),
 						  CTX_GPREG_X0);
-	if (ffa_resp_func != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		ERROR("%s invalid SPMC response (%lx).\n",
-			__func__, ffa_resp_func);
-		return -EINVAL;
+
+	/*
+	 * Retrieve flags indicating framework message and power management
+	 * response.
+	 */
+	msg_flags = (uint32_t)read_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx),
+						  CTX_GPREG_X2);
+
+	/* Retrieve error code indicating status of power management operation. */
+	status = (int)read_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx),
+						  CTX_GPREG_X3);
+
+	if (ffa_resp_func_id == FFA_ERROR) {
+		/*
+		 * It is likely that SPMC does not support receiving PSCI
+		 * operation through framework message. SPMD takes an
+		 * implementation defined choice to not treat it as a fatal
+		 * error. Consequently, SPMD ignores the error and continues
+		 * with power management operation.
+		 */
+		VERBOSE("SPMC ignored PSCI CPU_OFF framework message\n");
+	} else if (ffa_resp_func_id != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
+		ERROR("%s invalid SPMC response (%x).\n",
+			__func__, ffa_resp_func_id);
+		panic();
+	} else if (((msg_flags & FFA_FWK_MSG_BIT) == 0U) ||
+			 ((msg_flags & FFA_FWK_MSG_MASK) != FFA_PM_MSG_PM_RESP)) {
+		ERROR("SPMC failed to send framework message response for power"
+			" management operation, message flags = (%x)\n",
+			 msg_flags);
+		panic();
+	} else if (status != PSCI_E_SUCCESS) {
+		ERROR("SPMC denied CPU_OFF power management request\n");
+		panic();
+	} else {
+		VERBOSE("CPU %u off!\n", linear_id);
 	}
 
 	ctx->state = SPMC_STATE_OFF;
 
-	VERBOSE("CPU %u off!\n", linear_id);
-
 	return 0;
 }
 
diff --git a/services/std_svc/spmd/spmd_private.h b/services/std_svc/spmd/spmd_private.h
index 2cd18a2..0dfad02 100644
--- a/services/std_svc/spmd/spmd_private.h
+++ b/services/std_svc/spmd/spmd_private.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2019-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -52,6 +52,7 @@
 	cpu_context_t cpu_ctx;
 	spmc_state_t state;
 	bool secure_interrupt_ongoing;
+	bool psci_operation_ongoing;
 #if ENABLE_SPMD_LP
 	uint8_t spmd_lp_sync_req_ongoing;
 #endif