[PATCH] lib: utils: Add PSCI implementation support

Niu Zhihong zhihong at nzhnb.com
Fri Jul 25 02:14:53 PDT 2025


This patch adds Power State Coordination Interface (PSCI) support to OpenSBI.
PSCI provides a standard interface for power management operations such as
CPU power on/off, system suspend/resume, and system reset.

The implementation includes:
- PSCI version information and feature detection
- CPU power management (on/off/suspend)
- System power management (reset/off)
- Affinity level management
- PSCI library utilities and helper functions

This follows the PSCI specification version 1.3 and provides a foundation
for platform-specific power management implementations.

Signed-off-by: Niu Zhihong <zhihong at nzhnb.com>
---
diff --git a/include/sbi_utils/psci/psci.h b/include/sbi_utils/psci/psci.h
new file mode 100644
index 0000000..f063ec2
--- /dev/null
+++ b/include/sbi_utils/psci/psci.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright The OpenSBI Contributors
+ *
+ * See PSCI specification (Version 1.3 issue F.b) for more details.
+ */
+
+#ifndef __PSCI_H__
+#define __PSCI_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+
+#define psci_assert(cond)                                                   \
+	do {                                                                \
+		if (!(cond)) {                                              \
+			sbi_printf("%s:%d, invalid psci state\n", __func__, \
+				   __LINE__);                               \
+			sbi_hart_hang();                                    \
+		}                                                           \
+	} while (0)
+
+/* Version 1.1 */
+#define PSCI_MAJOR_VER (1 << 16)
+#define PSCI_MINOR_VER 1
+
+#define PSCI_E_SUCCESS 0
+#define PSCI_E_NOT_SUPPORTED -1
+#define PSCI_E_INVALID_PARAMS -2
+#define PSCI_E_DENIED -3
+#define PSCI_E_ALREADY_ON -4
+#define PSCI_E_ON_PENDING -5
+#define PSCI_E_INTERN_FAIL -6
+#define PSCI_E_NOT_PRESENT -7
+#define PSCI_E_DISABLED -8
+#define PSCI_E_INVALID_ADDRESS -9
+
+/* PSCI function IDs */
+#define PSCI_FN_VERSION 0x84000000
+#define PSCI_FN_CPU_SUSPEND_SMC32 0x84000001
+#define PSCI_FN_CPU_SUSPEND_SMC64 0xC4000001
+#define PSCI_FN_CPU_OFF 0x84000002
+#define PSCI_FN_CPU_ON_SMC32 0x84000003
+#define PSCI_FN_CPU_ON_SMC64 0xC4000003
+#define PSCI_FN_AFFINITY_INFO_SMC32 0x84000004
+#define PSCI_FN_AFFINITY_INFO_SMC64 0xC4000004
+#define PSCI_FN_SYSTEM_OFF 0x84000008
+#define PSCI_FN_SYSTEM_RESET 0x84000009
+#define PSCI_FN_PSCI_FEATURES 0x8400000A
+#define PSCI_FN_SYSTEM_OFF2_SMC32 0x84000015
+#define PSCI_FN_SYSTEM_OFF2_SMC64 0xC4000015
+
+/* SMCCC function IDs */
+#define SMCCC_VERSION 0x80000000
+
+/* PSCI AFFINITY_INFO return values */
+#define PSCI_AFFINITY_STATE_ON 0
+#define PSCI_AFFINITY_STATE_OFF 1
+#define PSCI_AFFINITY_STATE_ON_PENDING 2
+
+/* PSCI power state TYPEs */
+#define PSCI_PSTATE_TYPE_STANDBY 0x0
+#define PSCI_PSTATE_TYPE_POWERDOWN 0x1
+
+/* PSCI power state IDs */
+#define PSCI_PSTATE_ID_RUN 0x0
+#define PSCI_PSTATE_ID_STANDBY 0x1
+#define PSCI_PSTATE_ID_RETENTION 0x2
+#define PSCI_PSTATE_ID_POWERDOWN 0x3
+
+/**
+ * Description:
+ * Return the version of PSCI implemented. This function allows a caller
+ * to ascertain the current version of the PSCI interface.
+ * 
+ * Return Value:
+ * uint32_t - 31-bit unsigned integer containing version information:
+ * - Bits[31:16]: Major Version (upper 15 bits)
+ * - Bits[15:0]:  Minor Version (lower 16 bits)
+ * 
+ * Current Implementation:
+ * - Major Version: 1 (PSCI_MAJOR_VER)
+ * - Minor Version: 1 (PSCI_MINOR_VER)
+ * - Combined Version: 0x00010001
+ */
+uint32_t psci_version();
+
+/**
+ * Description:
+ * Suspend execution on a core or higher-level topology node. This call is
+ * intended for use in idle subsystems where the core is expected to return
+ * to execution through a wakeup event.
+ * 
+ * Parameters:
+ * - power_state: Describes the target power state for suspension
+ *   - uint32 value defining the power state characteristics
+ *   - Extended StateID format:
+ *     - Bit[31]: Reserved, must be zero
+ *     - Bit[30]: StateType
+ *       - PSCI_PSTATE_TYPE_STANDBY (0): Standby or retention state (no core context lost)
+ *       - PSCI_PSTATE_TYPE_POWERDOWN (1): Powerdown state (core context lost, entry_point_address and context_id valid)
+ *     - Bits[29:28]: Reserved, must be zero
+ *     - Bits[27:0]: StateID (platform-specific state identifier)
+ *         - PSCI_PSTATE_ID_RUN (0x0): Normal running state
+ *         - PSCI_PSTATE_ID_STANDBY (0x1): Standby state (quick wake-up)
+ *         - PSCI_PSTATE_ID_RETENTION (0x2): Retention state (context preserved)
+ *         - PSCI_PSTATE_ID_POWERDOWN (0x3): Powerdown state (context lost)
+ *   - Format is determined by PSCI_FEATURES bit[1] for CPU_SUSPEND
+ *
+ * - entry_point_address: Resume execution address after wakeup
+ *   - Only valid for powerdown states; ignored for standby states
+ * 
+ * - context_id: Context value for powerdown states
+ *   - Only valid for powerdown states; ignored for standby states
+ * 
+ * Return Value:
+ * intptr_t - Result of the CPU_SUSPEND operation:
+ * - PSCI_E_SUCCESS: Operation completed successfully
+ * - PSCI_E_INVALID_PARAMS: Invalid parameters provided
+ * - PSCI_E_INVALID_ADDRESS: Invalid entry point address
+ * - PSCI_E_DENIED: Operation denied (only in OS-initiated mode)
+ */
+intptr_t psci_cpu_suspend(uint32_t power_state, uintptr_t entry_point_address,
+			  uintptr_t context_id);
+
+/**
+ * Description:
+ * Power down the calling core. This call is intended for use in hotplug.
+ * A core that is powered down by CPU_OFF can only be powered up again
+ * in response to a CPU_ON call.
+ * 
+ * Return Value:
+ * int32_t - Result of the CPU_OFF operation:
+ * - Does not return when successful (core is powered down)
+ * - PSCI_E_DENIED: Operation denied (only possible return value on failure)
+ */
+int32_t psci_cpu_off();
+
+/**
+ * Description:
+ * Power up a core. This call is used to power up cores that either:
+ * - Have not yet been booted into the calling supervisory software
+ * - Have been previously powered down with a CPU_OFF call
+ * 
+ * Parameters:
+ * - target_cpu: Target CPU to power on
+ *   - In OpenSBI implementation: this corresponds to hartid
+ * 
+ * - entry_point_address: Physical address where core should start execution
+ * 
+ * - context_id: Context value passed to the target core
+ * 
+ * Return Value:
+ * intptr_t - Result of the CPU_ON operation:
+ * - PSCI_E_SUCCESS: Operation completed successfully
+ * - PSCI_E_INVALID_PARAMS: Invalid parameters provided
+ * - PSCI_E_INVALID_ADDRESS: Invalid entry point address
+ * - PSCI_E_ALREADY_ON: Target CPU is already powered on
+ * - PSCI_E_ON_PENDING: Target CPU is already being powered on
+ * - PSCI_E_INTERN_FAIL: Internal failure occurred
+ * - PSCI_E_DENIED: Operation denied
+ */
+intptr_t psci_cpu_on(uintptr_t target_cpu, uintptr_t entry_point_address,
+		     uintptr_t context_id);
+
+/**
+ * Description:
+ * Enable the caller to request status of an affinity instance.
+ * 
+ * Parameters:
+ * - target_affinity: Target affinity instance to query
+ *   - In OpenSBI implementation: this corresponds to hartid
+ * 
+ * - lowest_affinity_level: 
+ *   - From PSCI version 1.0 onwards, AFFINITY_INFO is no longer required to
+ *     support affinity levels higher than 0.
+ *
+ * Return Value:
+ * intptr_t - Status of the affinity instance:
+ * - PSCI_AFFINITY_STATE_ON (0): At least one core in the affinity instance is ON
+ * - PSCI_AFFINITY_STATE_OFF (1): Affinity instance is OFF
+ * - PSCI_AFFINITY_STATE_ON_PENDING (2): Affinity instance is transitioning to ON state
+ * - PSCI_E_INVALID_PARAMS: Invalid parameters provided
+ */
+intptr_t psci_affinity_info(uintptr_t target_affinity,
+			    uint32_t lowest_affinity_level);
+
+/**
+ * Description:
+ * Shut down the system.
+ */
+void psci_system_off();
+
+/**
+ * Description:
+ * Reset the system.
+ */
+void psci_system_reset();
+
+/**
+ * Description:
+ * Query API that allows discovering whether SMCCC_VERSION or a specific PSCI 
+ * function is implemented and its features. This function is introduced in 
+ * PSCI 1.0 specification.
+ * 
+ * Parameters:
+ * - psci_func_id: Function ID of PSCI Function or SMCCC_VERSION
+ * 
+ * Return values:
+ * - PSCI_E_NOT_SUPPORTED: if the function identified by psci_func_id is not 
+ *   implemented or is an invalid function ID
+ * - Feature flags (int32): A set of feature flags associated with the 
+ *   implemented function indicated by psci_func_id. Feature flags are specific 
+ *   to each function. Format:
+ *   - Bit[31] is zero
+ *   - Bits[30:0] represent the feature flags
+ */
+int32_t psci_features(uint32_t psci_func_id);
+
+#endif /* __PSCI_H__ */
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 901ba56..5dada1e 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -34,4 +34,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/timer/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/mpxy/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/psci/Kconfig"
+
 endmenu
diff --git a/lib/utils/psci/Kconfig b/lib/utils/psci/Kconfig
new file mode 100644
index 0000000..f818cf7
--- /dev/null
+++ b/lib/utils/psci/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-2-Clause
+# Copyright The OpenSBI Contributors
+
+menu "Arm Power State Coordination Interface Support"
+
+config ARM_PSCI
+	bool "Support PSCI protocol"
+	default n
+
+endmenu
diff --git a/lib/utils/psci/objects.mk b/lib/utils/psci/objects.mk
new file mode 100644
index 0000000..558dcae
--- /dev/null
+++ b/lib/utils/psci/objects.mk
@@ -0,0 +1,19 @@
+#
+# Copyright The OpenSBI Contributors
+#
+
+libsbiutils-objs-${CONFIG_ARM_PSCI} += psci/psci_version.o
+
+libsbiutils-objs-${CONFIG_ARM_PSCI} += psci/psci_cpu_suspend.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI) += psci/psci_cpu_off.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI) += psci/psci_cpu_on.o
+
+libsbiutils-objs-${CONFIG_ARM_PSCI} += psci/psci_affinity_info.o
+
+libsbiutils-objs-${CONFIG_ARM_PSCI} += psci/psci_system_off.o
+
+libsbiutils-objs-${CONFIG_ARM_PSCI} += psci/psci_system_reset.o
+
+libsbiutils-objs-${CONFIG_ARM_PSCI} += psci/psci_features.o
diff --git a/lib/utils/psci/psci_affinity_info.c b/lib/utils/psci/psci_affinity_info.c
new file mode 100644
index 0000000..e9b795f
--- /dev/null
+++ b/lib/utils/psci/psci_affinity_info.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_ecall_interface.h>
+
+intptr_t psci_affinity_info(uintptr_t target_affinity,
+			    uint32_t lowest_affinity_level)
+{
+	struct sbi_scratch *scratch  = sbi_scratch_thishart_ptr();
+	const struct sbi_domain *dom = sbi_domain_thishart_ptr();
+	uint32_t target_hartid	     = (uint32_t)target_affinity;
+	int hart_state;
+
+	/* Validate that we have a valid scratch area and domain */
+	if (!scratch || !dom)
+		return PSCI_E_INTERN_FAIL;
+
+	/* 
+	 * PSCI v1.0+ allows implementations to only support level 0 (individual cores)
+	 * For now, we only support querying individual hart/core state (level 0)
+	 */
+	if (lowest_affinity_level > 0)
+		return PSCI_E_INVALID_PARAMS;
+
+	/* Check if target_affinity describes a valid hartid */
+	if (!sbi_hartid_valid(target_hartid))
+		return PSCI_E_INVALID_PARAMS;
+
+	/* Check if the target hart is assigned to the current domain */
+	if (!sbi_domain_is_assigned_hart(
+		    dom, sbi_hartid_to_hartindex(target_hartid)))
+		return PSCI_E_INVALID_PARAMS;
+
+	/* Get the current state of the target hart */
+	hart_state = sbi_hsm_hart_get_state(dom, target_hartid);
+	if (hart_state < 0)
+		return PSCI_E_INVALID_PARAMS;
+
+	/* Map SBI HSM states to PSCI affinity states */
+	switch (hart_state) {
+	case SBI_HSM_STATE_STARTED:
+	case SBI_HSM_STATE_SUSPENDED:
+	case SBI_HSM_STATE_RESUME_PENDING:
+		/*
+		 * PSCI ON: At least one core in the affinity instance is ON
+		 */
+		return PSCI_AFFINITY_STATE_ON;
+
+	case SBI_HSM_STATE_STOPPED:
+		/*
+		 * PSCI OFF: All cores in the affinity instance are OFF
+		 */
+		return PSCI_AFFINITY_STATE_OFF;
+
+	case SBI_HSM_STATE_START_PENDING:
+	case SBI_HSM_STATE_STOP_PENDING:
+	case SBI_HSM_STATE_SUSPEND_PENDING:
+		/*
+		 * PSCI ON_PENDING: The affinity instance is transitioning to an ON state
+		 */
+		return PSCI_AFFINITY_STATE_ON_PENDING;
+
+	default:
+		/* Invalid or unknown state */
+		return PSCI_E_INTERN_FAIL;
+	}
+}
diff --git a/lib/utils/psci/psci_cpu_off.c b/lib/utils/psci/psci_cpu_off.c
new file mode 100644
index 0000000..3e1940a
--- /dev/null
+++ b/lib/utils/psci/psci_cpu_off.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_domain.h>
+
+int32_t psci_cpu_off()
+{
+	struct sbi_scratch *scratch  = sbi_scratch_thishart_ptr();
+	const struct sbi_domain *dom = sbi_domain_thishart_ptr();
+	int ret;
+
+	/* Validate that we have a valid scratch area */
+	psci_assert(scratch != NULL);
+	psci_assert(dom != NULL);
+
+	ret = sbi_hsm_hart_stop(scratch, true);
+
+	/* We should never reach here if the hart stop succeeds */
+	if (ret != 0) {
+		return PSCI_E_DENIED;
+	}
+
+	/* This point should never be reached */
+	psci_assert(0);
+	return PSCI_E_DENIED;
+}
diff --git a/lib/utils/psci/psci_cpu_on.c b/lib/utils/psci/psci_cpu_on.c
new file mode 100644
index 0000000..17e5e1b
--- /dev/null
+++ b/lib/utils/psci/psci_cpu_on.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/sbi_error.h>
+
+intptr_t psci_cpu_on(uintptr_t target_cpu, uintptr_t entry_point_address,
+		     uintptr_t context_id)
+{
+	struct sbi_scratch *scratch  = sbi_scratch_thishart_ptr();
+	const struct sbi_domain *dom = sbi_domain_thishart_ptr();
+	struct sbi_scratch *remote_scratch;
+	uint32_t target_hartid = (uint32_t)target_cpu;
+	int hart_state;
+	int ret;
+
+	/* Validate that we have a valid scratch area and domain */
+	psci_assert(scratch != NULL);
+	psci_assert(dom != NULL);
+
+	/* Check if target_cpu describes a valid MPIDR/hartid */
+	if (!sbi_hartid_valid(target_hartid)) {
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	/* Get scratch pointer for target hart */
+	remote_scratch = sbi_hartid_to_scratch(target_hartid);
+	if (!remote_scratch) {
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	/* Check if the entry point address is valid for the caller's domain */
+	if (!sbi_domain_check_addr(dom, entry_point_address, PRV_S,
+				   SBI_DOMAIN_EXECUTE)) {
+		return PSCI_E_INVALID_ADDRESS;
+	}
+
+	/* Get current state of the target hart */
+	hart_state = sbi_hsm_hart_get_state(dom, target_hartid);
+
+	switch (hart_state) {
+	case SBI_HSM_STATE_STARTED:
+		/* Hart is already running */
+		return PSCI_E_ALREADY_ON;
+
+	case SBI_HSM_STATE_START_PENDING:
+		/* Hart start is already pending */
+		return PSCI_E_ON_PENDING;
+
+	case SBI_HSM_STATE_STOPPED:
+		/* Hart is stopped, we can start it */
+		break;
+
+	default:
+		/* Invalid state or other error */
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	/* 
+	 * Start the target hart
+	 * - target_hartid: the hart to start
+	 * - entry_point_address: where the hart should start execution
+	 * - PRV_S: start in supervisor mode
+	 * - context_id: passed as argument to the started hart
+	 */
+	ret = sbi_hsm_hart_start(remote_scratch, dom, target_hartid,
+				 entry_point_address, PRV_S, context_id);
+
+	/* Convert SBI error codes to PSCI error codes */
+	switch (ret) {
+	case SBI_SUCCESS:
+		return PSCI_E_SUCCESS;
+	case SBI_ERR_INVALID_PARAM:
+		return PSCI_E_INVALID_PARAMS;
+	case SBI_ERR_INVALID_ADDRESS:
+		return PSCI_E_INVALID_ADDRESS;
+	case SBI_ERR_ALREADY_STARTED:
+		return PSCI_E_ALREADY_ON;
+	case SBI_ERR_DENIED:
+		return PSCI_E_DENIED;
+	default:
+		return PSCI_E_INTERN_FAIL;
+	}
+}
diff --git a/lib/utils/psci/psci_cpu_suspend.c b/lib/utils/psci/psci_cpu_suspend.c
new file mode 100644
index 0000000..16b0e5c
--- /dev/null
+++ b/lib/utils/psci/psci_cpu_suspend.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/sbi_error.h>
+
+/* PSCI power state TYPEs */
+#define PSCI_PSTATE_TYPE_SHIFT 30
+#define PSCI_PSTATE_TYPE_MASK 0x1
+
+/* PSCI power state StateID */
+#define PSCI_PSTATE_ID_SHIFT 0
+#define PSCI_PSTATE_ID_MASK 0x0FFFFFFF
+
+/* Helper macros for power state */
+#define psci_get_pstate_type(pstate) \
+	(((pstate) >> PSCI_PSTATE_TYPE_SHIFT) & PSCI_PSTATE_TYPE_MASK)
+#define psci_get_pstate_id(pstate) \
+	(((pstate) >> PSCI_PSTATE_ID_SHIFT) & PSCI_PSTATE_ID_MASK)
+
+intptr_t psci_cpu_suspend(uint32_t power_state, uintptr_t entry_point_address,
+			  uintptr_t context_id)
+{
+	struct sbi_scratch *scratch  = sbi_scratch_thishart_ptr();
+	const struct sbi_domain *dom = sbi_domain_thishart_ptr();
+	uint32_t pstate_type, pstate_id, suspend_type;
+	int ret;
+
+	/* Validate runtime environment */
+	psci_assert(scratch != NULL);
+	psci_assert(dom != NULL);
+
+	/* Validate power state format - check reserved bits */
+	if (power_state & 0x80000000) { /* Bit[31] must be zero */
+		return PSCI_E_INVALID_PARAMS;
+	}
+	if (power_state & 0x30000000) { /* Bits[29:28] must be zero */
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	/* Extract StateType and StateID from power_state */
+	pstate_type = psci_get_pstate_type(power_state);
+	pstate_id   = psci_get_pstate_id(power_state);
+
+	/* Validate StateID values */
+	if (pstate_id > PSCI_PSTATE_ID_POWERDOWN) {
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	/*
+	 * Handle different suspend types based on PSCI specification:
+	 * - STANDBY: Retention state, context preserved, quick wake-up
+	 * - POWERDOWN: Context lost, requires entry_point_address and context_id
+	 * 
+	 * StateID provides platform-specific power state information:
+	 * - PSCI_PSTATE_ID_RUN (0x0): Normal running state (invalid for suspend)
+	 * - PSCI_PSTATE_ID_STANDBY (0x1): Standby state (quick wake-up)
+	 * - PSCI_PSTATE_ID_RETENTION (0x2): Retention state (context preserved)
+	 * - PSCI_PSTATE_ID_POWERDOWN (0x3): Powerdown state (context lost)
+	 */
+	if (pstate_type == PSCI_PSTATE_TYPE_STANDBY) {
+		/*
+		 * Standby/Retention suspend:
+		 * - Core context is preserved
+		 * - entry_point_address and context_id are ignored
+		 * - Use retentive suspend mode
+		 * - Valid StateIDs: STANDBY, RETENTION
+		 */
+		if (pstate_id != PSCI_PSTATE_ID_STANDBY &&
+		    pstate_id != PSCI_PSTATE_ID_RETENTION) {
+			return PSCI_E_INVALID_PARAMS;
+		}
+
+		suspend_type = SBI_HSM_SUSPEND_RET_DEFAULT;
+
+		/* For standby, entry_point_address is ignored but we use current context */
+		entry_point_address = 0; /* Will be ignored by SBI HSM */
+		context_id	    = 0; /* Will be ignored by SBI HSM */
+
+	} else if (pstate_type == PSCI_PSTATE_TYPE_POWERDOWN) {
+		/*
+		 * Powerdown suspend:
+		 * - Core context will be lost
+		 * - entry_point_address must be valid and accessible
+		 * - context_id will be passed to resumed execution
+		 * - Use non-retentive suspend mode
+		 * - Valid StateID: POWERDOWN
+		 */
+		if (pstate_id != PSCI_PSTATE_ID_POWERDOWN) {
+			return PSCI_E_INVALID_PARAMS;
+		}
+
+		suspend_type = SBI_HSM_SUSPEND_NON_RET_DEFAULT;
+
+		/* Validate entry point address for powerdown states */
+		if (!entry_point_address) {
+			return PSCI_E_INVALID_ADDRESS;
+		}
+
+		/* Verify entry point is within caller's domain and executable */
+		if (!sbi_domain_check_addr(dom, entry_point_address, PRV_S,
+					   SBI_DOMAIN_EXECUTE)) {
+			return PSCI_E_INVALID_ADDRESS;
+		}
+
+	} else {
+		/* Invalid StateType - only STANDBY(0) and POWERDOWN(1) are valid */
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	/*
+	 * Initiate hart suspension through SBI HSM:
+	 * - suspend_type: SBI_HSM_SUSPEND_RET_DEFAULT or SBI_HSM_SUSPEND_NON_RET_DEFAULT
+	 * - entry_point_address: Resume address (powerdown) or 0 (standby)
+	 * - PRV_S: Resume in supervisor privilege mode
+	 * - context_id: Context value passed to resumed execution
+	 */
+	ret = sbi_hsm_hart_suspend(scratch, suspend_type, entry_point_address,
+				   PRV_S, context_id);
+
+	/*
+	 * Handle return values:
+	 * 
+	 * For STANDBY (retentive) suspend:
+	 * - Normal return with PSCI_E_SUCCESS indicates successful wake-up
+	 * 
+	 * For POWERDOWN (non-retentive) suspend:
+	 * - Should NOT return here on success (execution resumes at entry_point_address)
+	 * - If we return here, it indicates suspend was rejected, downgraded, or failed
+	 */
+	switch (ret) {
+	case SBI_SUCCESS:
+		/* Success - either completed standby or failed powerdown */
+		return PSCI_E_SUCCESS;
+	case SBI_ERR_INVALID_PARAM:
+		return PSCI_E_INVALID_PARAMS;
+	case SBI_ERR_INVALID_ADDRESS:
+		return PSCI_E_INVALID_ADDRESS;
+	case SBI_ERR_DENIED:
+		return PSCI_E_DENIED;
+	case SBI_ERR_NOT_SUPPORTED:
+		return PSCI_E_NOT_SUPPORTED;
+	default:
+		return PSCI_E_INTERN_FAIL;
+	}
+}
diff --git a/lib/utils/psci/psci_features.c b/lib/utils/psci/psci_features.c
new file mode 100644
index 0000000..120cbc3
--- /dev/null
+++ b/lib/utils/psci/psci_features.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+
+int32_t psci_features(uint32_t psci_func_id)
+{
+	switch (psci_func_id) {
+	case PSCI_FN_VERSION:
+	case PSCI_FN_PSCI_FEATURES:
+	case PSCI_FN_CPU_OFF:
+	case PSCI_FN_CPU_ON_SMC32:
+	case PSCI_FN_CPU_ON_SMC64:
+	case PSCI_FN_AFFINITY_INFO_SMC32:
+	case PSCI_FN_AFFINITY_INFO_SMC64:
+	case PSCI_FN_SYSTEM_OFF:
+	case PSCI_FN_SYSTEM_RESET:
+		return 0;
+
+	case PSCI_FN_CPU_SUSPEND_SMC32:
+	case PSCI_FN_CPU_SUSPEND_SMC64:
+		return 1;
+
+	default:
+		return PSCI_E_NOT_SUPPORTED;
+	}
+}
diff --git a/lib/utils/psci/psci_system_off.c b/lib/utils/psci/psci_system_off.c
new file mode 100644
index 0000000..a2c1058
--- /dev/null
+++ b/lib/utils/psci/psci_system_off.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_ecall_interface.h>
+
+void __noreturn psci_system_off()
+{
+	sbi_system_reset(SBI_SRST_RESET_TYPE_SHUTDOWN,
+			 SBI_SRST_RESET_REASON_NONE);
+
+	/* We should never reach this point */
+	psci_assert(0);
+}
diff --git a/lib/utils/psci/psci_system_reset.c b/lib/utils/psci/psci_system_reset.c
new file mode 100644
index 0000000..6800322
--- /dev/null
+++ b/lib/utils/psci/psci_system_reset.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_ecall_interface.h>
+
+void __noreturn psci_system_reset()
+{
+	sbi_system_reset(SBI_SRST_RESET_TYPE_COLD_REBOOT,
+			 SBI_SRST_RESET_REASON_NONE);
+
+	/* We should never reach this point */
+	psci_assert(0);
+}
diff --git a/lib/utils/psci/psci_version.c b/lib/utils/psci/psci_version.c
new file mode 100644
index 0000000..1dbadf6
--- /dev/null
+++ b/lib/utils/psci/psci_version.c
@@ -0,0 +1,10 @@
+/*
+ * Copyright The OpenSBI Contributors
+ */
+
+#include <sbi_utils/psci/psci.h>
+
+uint32_t psci_version(void)
+{
+	return PSCI_MAJOR_VER | PSCI_MINOR_VER;
+}
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index c7a6531..dbb4039 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -58,3 +58,4 @@ CONFIG_FDT_MPXY=y
 CONFIG_FDT_MPXY_RPMI_MBOX=y
 CONFIG_FDT_MPXY_RPMI_CLOCK=y
 CONFIG_FDT_MPXY_RPMI_SYSMSI=y
+CONFIG_ARM_PSCI=y



More information about the opensbi mailing list