2.8. PSCI Library Integration guide for Armv8-A AArch32 systems
This document describes the PSCI library interface with a focus on how to integrate with a suitable Trusted OS for an Armv8-A AArch32 system. The PSCI Library implements the PSCI Standard as described in PSCI and is meant to be integrated with EL3 Runtime Software which invokes the PSCI Library interface appropriately. EL3 Runtime Software refers to software executing at the highest secure privileged mode, which is EL3 in AArch64 or Secure SVC/ Monitor mode in AArch32, and provides runtime services to the non-secure world. The runtime service request is made via SMC (Secure Monitor Call) and the call must adhere to SMCCC. In AArch32, EL3 Runtime Software may additionally include Trusted OS functionality. A minimal AArch32 Secure Payload, SP-MIN, is provided in Trusted Firmware-A (TF-A) to illustrate the usage and integration of the PSCI library. The description of PSCI library interface and its integration with EL3 Runtime Software in this document is targeted towards AArch32 systems.
2.8.1. Generic call sequence for PSCI Library interface (AArch32)
The generic call sequence of PSCI Library interfaces (see PSCI Library Interface) during cold boot in AArch32 system is described below:
After cold reset, the EL3 Runtime Software performs its cold boot initialization including the PSCI library pre-requisites mentioned in PSCI Library Interface, and also the necessary platform setup.
Call
psci_setup()
in Monitor mode.Optionally call
psci_register_spd_pm_hook()
to register callbacks to do bookkeeping for the EL3 Runtime Software during power management.Call
psci_prepare_next_non_secure_ctx()
to initialize the non-secure CPU context.Get the non-secure
cpu_context_t
for the current CPU by callingcm_get_context()
, then programming the registers in the non-secure context and exiting to non-secure world. If the EL3 Runtime Software needs additional configuration to be set for non-secure context, like routing FIQs to the secure world, the values of the registers can be modified prior to programming. See PSCI CPU context management for more details on CPU context management.
The generic call sequence of PSCI library interfaces during warm boot in AArch32 systems is described below:
After warm reset, the EL3 Runtime Software performs the necessary warm boot initialization including the PSCI library pre-requisites mentioned in PSCI Library Interface (Note that the Data cache must not be enabled).
Call
psci_warmboot_entrypoint()
in Monitor mode. This interface initializes/restores the non-secure CPU context as well.Do step 5 of the cold boot call sequence described above.
The generic call sequence of PSCI library interfaces on receipt of a PSCI SMC on an AArch32 system is described below:
On receipt of an SMC, save the register context as per SMCCC.
If the SMC function identifier corresponds to a SMC32 PSCI API, construct the appropriate arguments and call the
psci_smc_handler()
interface. The invocation may or may not return back to the caller depending on whether the PSCI API resulted in power down of the CPU.If
psci_smc_handler()
returns, populate the return value in R0 (AArch32)/ X0 (AArch64) and restore other registers as per SMCCC.
2.8.2. PSCI CPU context management
PSCI library is in charge of initializing/restoring the non-secure CPU system
registers according to PSCI during cold/warm boot.
This is referred to as PSCI CPU Context Management
. Registers that need to
be preserved across CPU power down/power up cycles are maintained in
cpu_context_t
data structure. The initialization of other non-secure CPU
system registers which do not require coordination with the EL3 Runtime
Software is done directly by the PSCI library (see cm_prepare_el3_exit()
).
The EL3 Runtime Software is responsible for managing register context
during switch between Normal and Secure worlds. The register context to be
saved and restored depends on the mechanism used to trigger the world switch.
For example, if the world switch was triggered by an SMC call, then the
registers need to be saved and restored according to SMCCC. In AArch64,
due to the tight integration with BL31, both BL31 and PSCI library
use the same cpu_context_t
data structure for PSCI CPU context management
and register context management during world switch. This cannot be assumed
for AArch32 EL3 Runtime Software since most AArch32 Trusted OSes already implement
a mechanism for register context management during world switch. Hence, when
the PSCI library is integrated with a AArch32 EL3 Runtime Software, the
cpu_context_t
is stripped down for just PSCI CPU context management.
During cold/warm boot, after invoking appropriate PSCI library interfaces, it
is expected that the EL3 Runtime Software will query the cpu_context_t
and
write appropriate values to the corresponding system registers. This mechanism
resolves 2 additional problems for AArch32 EL3 Runtime Software:
Values for certain system registers like SCR and SCTLR cannot be unilaterally determined by PSCI library and need inputs from the EL3 Runtime Software. Using
cpu_context_t
as an intermediary data store allows EL3 Runtime Software to modify the register values appropriately before programming them.The PSCI library provides appropriate LR and SPSR values (entrypoint information) for exit into non-secure world. Using
cpu_context_t
as an intermediary data store allows the EL3 Runtime Software to store these values safely until it is ready for exit to non-secure world.
Currently the cpu_context_t
data structure for AArch32 stores the following
registers: R0 - R3, LR (R14), SCR, SPSR, SCTLR.
The EL3 Runtime Software must implement accessors to get/set pointers
to CPU context cpu_context_t
data and these are described in
CPU Context management API.
2.8.3. PSCI Library Interface
The PSCI library implements the PSCI. The interfaces to this library are
declared in psci_lib.h
and are as listed below:
u_register_t psci_smc_handler(uint32_t smc_fid, u_register_t x1,
u_register_t x2, u_register_t x3,
u_register_t x4, void *cookie,
void *handle, u_register_t flags);
int psci_setup(const psci_lib_args_t *lib_args);
void psci_warmboot_entrypoint(void);
void psci_register_spd_pm_hook(const spd_pm_ops_t *pm);
void psci_prepare_next_non_secure_ctx(entry_point_info_t *next_image_info);
The CPU context data ‘cpu_context_t’ is programmed to the registers differently
when PSCI is integrated with an AArch32 EL3 Runtime Software compared to
when the PSCI is integrated with an AArch64 EL3 Runtime Software (BL31). For
example, in the case of AArch64, there is no need to retrieve cpu_context_t
data and program the registers as it will done implicitly as part of
el3_exit
. The description below of the PSCI interfaces is targeted at
integration with an AArch32 EL3 Runtime Software.
The PSCI library is responsible for initializing/restoring the non-secure world
to an appropriate state after boot and may choose to directly program the
non-secure system registers. The PSCI generic code takes care not to directly
modify any of the system registers affecting the secure world and instead
returns the values to be programmed to these registers via cpu_context_t
.
The EL3 Runtime Software is responsible for programming those registers and
can use the proposed values provided in the cpu_context_t
, modifying the
values if required.
PSCI library needs the flexibility to access both secure and non-secure copies of banked registers. Hence it needs to be invoked in Monitor mode for AArch32 and in EL3 for AArch64. The NS bit in SCR (in AArch32) or SCR_EL3 (in AArch64) must be set to 0. Additional requirements for the PSCI library interfaces are:
Instruction cache must be enabled
Both IRQ and FIQ must be masked for the current CPU
The page tables must be setup and the MMU enabled
The C runtime environment must be setup and stack initialized
The Data cache must be enabled prior to invoking any of the PSCI library interfaces except for
psci_warmboot_entrypoint()
. Forpsci_warmboot_entrypoint()
, if the build optionHW_ASSISTED_COHERENCY
is enabled however, data caches are expected to be enabled.
Further requirements for each interface can be found in the interface description.
2.8.3.1. Interface : psci_setup()
Argument : const psci_lib_args_t *lib_args
Return : void
This function is to be called by the primary CPU during cold boot before
any other interface to the PSCI library. It takes lib_args
, a const pointer
to psci_lib_args_t
, as the argument. The psci_lib_args_t
is a versioned
structure and is declared in psci_lib.h
header as follows:
typedef struct psci_lib_args {
/* The version information of PSCI Library Interface */
param_header_t h;
/* The warm boot entrypoint function */
mailbox_entrypoint_t mailbox_ep;
} psci_lib_args_t;
The first field h
, of param_header_t
type, provides the version
information. The second field mailbox_ep
is the warm boot entrypoint address
and is used to configure the platform mailbox. Helper macros are provided in
psci_lib.h
to construct the lib_args
argument statically or during
runtime. Prior to calling the psci_setup()
interface, the platform setup for
cold boot must have completed. Major actions performed by this interface are:
Initializes architecture.
Initializes PSCI power domain and state coordination data structures.
Calls
plat_setup_psci_ops()
with warm boot entrypointmailbox_ep
as argument.Calls
cm_set_context_by_index()
(see CPU Context management API) for all the CPUs in the platform
2.8.3.2. Interface : psci_prepare_next_non_secure_ctx()
Argument : entry_point_info_t *next_image_info
Return : void
After psci_setup()
and prior to exit to the non-secure world, this function
must be called by the EL3 Runtime Software to initialize the non-secure world
context. The non-secure world entrypoint information next_image_info
(first
argument) will be used to determine the non-secure context. After this function
returns, the EL3 Runtime Software must retrieve the cpu_context_t
(using
cm_get_context()) for the current CPU and program the registers prior to exit
to the non-secure world.
2.8.3.3. Interface : psci_register_spd_pm_hook()
Argument : const spd_pm_ops_t *
Return : void
As explained in Secure payload power management callback,
the EL3 Runtime Software may want to perform some bookkeeping during power
management operations. This function is used to register the spd_pm_ops_t
(first argument) callbacks with the PSCI library which will be called
appropriately during power management. Calling this function is optional and
need to be called by the primary CPU during the cold boot sequence after
psci_setup()
has completed.
2.8.3.4. Interface : psci_smc_handler()
Argument : uint32_t smc_fid, u_register_t x1,
u_register_t x2, u_register_t x3,
u_register_t x4, void *cookie,
void *handle, u_register_t flags
Return : u_register_t
This function is the top level handler for SMCs which fall within the
PSCI service range specified in SMCCC. The function ID smc_fid
(first
argument) determines the PSCI API to be called. The x1
to x4
(2nd to 5th
arguments), are the values of the registers r1 - r4 (in AArch32) or x1 - x4
(in AArch64) when the SMC is received. These are the arguments to PSCI API as
described in PSCI. The ‘flags’ (8th argument) is a bit field parameter
and is detailed in ‘smccc.h’ header. It includes whether the call is from the
secure or non-secure world. The cookie
(6th argument) and the handle
(7th argument) are not used and are reserved for future use.
The return value from this interface is the return value from the underlying
PSCI API corresponding to smc_fid
. This function may not return back to the
caller if PSCI API causes power down of the CPU. In this case, when the CPU
wakes up, it will start execution from the warm reset address.
2.8.3.5. Interface : psci_warmboot_entrypoint()
Argument : void
Return : void
This function performs the warm boot initialization/restoration as mandated by
PSCI. For AArch32, on wakeup from power down the CPU resets to secure SVC
mode and the EL3 Runtime Software must perform the prerequisite initializations
mentioned at top of this section. This function must be called with Data cache
disabled (unless build option HW_ASSISTED_COHERENCY
is enabled) but with MMU
initialized and enabled. The major actions performed by this function are:
Invalidates the stack and enables the data cache.
Initializes architecture and PSCI state coordination.
Restores/Initializes the peripheral drivers to the required state via appropriate
plat_psci_ops_t
hooksRestores the EL3 Runtime Software context via appropriate
spd_pm_ops_t
callbacks.Restores/Initializes the non-secure context and populates the
cpu_context_t
for the current CPU.
Upon the return of this function, the EL3 Runtime Software must retrieve the
non-secure cpu_context_t
using cm_get_context()
and program the registers
prior to exit to the non-secure world.
2.8.4. EL3 Runtime Software dependencies
The PSCI Library includes supporting frameworks like context management, cpu operations (cpu_ops) and per-cpu data framework. Other helper library functions like bakery locks and spin locks are also included in the library. The dependencies which must be fulfilled by the EL3 Runtime Software for integration with PSCI library are described below.
2.8.4.1. General dependencies
The PSCI library being a Multiprocessor (MP) implementation, EL3 Runtime Software must provide an SMC handling framework capable of MP adhering to SMCCC specification.
The EL3 Runtime Software must also export cache maintenance primitives and some helper utilities for assert, print and memory operations as listed below. The TF-A source tree provides implementations for all these functions but the EL3 Runtime Software may use its own implementation.
Functions : assert(), memcpy(), memset(), printf()
These must be implemented as described in ISO C Standard.
Function : flush_dcache_range()
Argument : uintptr_t addr, size_t size
Return : void
This function cleans and invalidates (flushes) the data cache for memory
at address addr
(first argument) address and of size size
(second argument).
Function : inv_dcache_range()
Argument : uintptr_t addr, size_t size
Return : void
This function invalidates (flushes) the data cache for memory at address
addr
(first argument) address and of size size
(second argument).
2.8.4.2. CPU Context management API
The CPU context management data memory is statically allocated by PSCI library in BSS section. The PSCI library requires the EL3 Runtime Software to implement APIs to store and retrieve pointers to this CPU context data. SP-MIN demonstrates how these APIs can be implemented but the EL3 Runtime Software can choose a more optimal implementation (like dedicating the secure TPIDRPRW system register (in AArch32) for storing these pointers).
Function : cm_set_context_by_index()
Argument : unsigned int cpu_idx, void *context, unsigned int security_state
Return : void
This function is called during cold boot when the psci_setup()
PSCI library
interface is called.
This function must store the pointer to the CPU context data, context
(2nd
argument), for the specified security_state
(3rd argument) and CPU identified
by cpu_idx
(first argument). The security_state
will always be non-secure
when called by PSCI library and this argument is retained for compatibility
with BL31. The cpu_idx
will correspond to the index returned by the
plat_core_pos_by_mpidr()
for mpidr
of the CPU.
The actual method of storing the context
pointers is implementation specific.
For example, SP-MIN stores the pointers in the array sp_min_cpu_ctx_ptr
declared in sp_min_main.c
.
Function : cm_get_context()
Argument : uint32_t security_state
Return : void *
This function must return the pointer to the cpu_context_t
structure for
the specified security_state
(first argument) for the current CPU. The caller
must ensure that cm_set_context_by_index
is called first and the appropriate
context pointers are stored prior to invoking this API. The security_state
will always be non-secure when called by PSCI library and this argument
is retained for compatibility with BL31.
Function : cm_get_context_by_index()
Argument : unsigned int cpu_idx, unsigned int security_state
Return : void *
This function must return the pointer to the cpu_context_t
structure for
the specified security_state
(second argument) for the CPU identified by
cpu_idx
(first argument). The caller must ensure that
cm_set_context_by_index
is called first and the appropriate context
pointers are stored prior to invoking this API. The security_state
will
always be non-secure when called by PSCI library and this argument is
retained for compatibility with BL31. The cpu_idx
will correspond to the
index returned by the plat_core_pos_by_mpidr()
for mpidr
of the CPU.
2.8.4.3. Platform API
The platform layer abstracts the platform-specific details from the generic PSCI library. The following platform APIs/macros must be defined by the EL3 Runtime Software for integration with the PSCI library.
The mandatory platform APIs are:
plat_my_core_pos
plat_core_pos_by_mpidr
plat_get_syscnt_freq2
plat_get_power_domain_tree_desc
plat_setup_psci_ops
plat_reset_handler
plat_panic_handler
plat_get_my_stack
The mandatory platform macros are:
PLATFORM_CORE_COUNT
PLAT_MAX_PWR_LVL
PLAT_NUM_PWR_DOMAINS
CACHE_WRITEBACK_GRANULE
PLAT_MAX_OFF_STATE
PLAT_MAX_RET_STATE
PLAT_MAX_PWR_LVL_STATES (optional)
PLAT_PCPU_DATA_SIZE (optional)
The details of these APIs/macros can be found in the Porting Guide.
All platform specific operations for power management are done via
plat_psci_ops_t
callbacks registered by the platform when
plat_setup_psci_ops()
API is called. The description of each of
the callbacks in plat_psci_ops_t
can be found in PSCI section of the
Porting Guide. If any these callbacks are not registered, then the
PSCI API associated with that callback will not be supported by PSCI
library.
2.8.4.4. Secure payload power management callback
During PSCI power management operations, the EL3 Runtime Software may
need to perform some bookkeeping, and PSCI library provides
spd_pm_ops_t
callbacks for this purpose. These hooks must be
populated and registered by using psci_register_spd_pm_hook()
PSCI
library interface.
Typical bookkeeping during PSCI power management calls include save/restore of the EL3 Runtime Software context. Also if the EL3 Runtime Software makes use of secure interrupts, then these interrupts must also be managed appropriately during CPU power down/power up. Any secure interrupt targeted to the current CPU must be disabled or re-targeted to other running CPU prior to power down of the current CPU. During power up, these interrupt can be enabled/re-targeted back to the current CPU.
typedef struct spd_pm_ops {
void (*svc_on)(u_register_t target_cpu);
int32_t (*svc_off)(u_register_t __unused);
void (*svc_suspend)(u_register_t max_off_pwrlvl);
void (*svc_on_finish)(u_register_t __unused);
void (*svc_suspend_finish)(u_register_t max_off_pwrlvl);
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);
void (*svc_system_reset)(void);
} spd_pm_ops_t;
A brief description of each callback is given below:
svc_on, svc_off, svc_on_finish
The
svc_on
,svc_off
callbacks are called during PSCI_CPU_ON, PSCI_CPU_OFF APIs respectively. Thesvc_on_finish
is called when the target CPU of PSCI_CPU_ON API powers up and executes thepsci_warmboot_entrypoint()
PSCI library interface.svc_suspend, svc_suspend_finish
The
svc_suspend
callback is called during power down bu either PSCI_SUSPEND or PSCI_SYSTEM_SUSPEND APIs. Thesvc_suspend_finish
is called when the CPU wakes up from suspend and executes thepsci_warmboot_entrypoint()
PSCI library interface. Themax_off_pwrlvl
(first parameter) denotes the highest power domain level being powered down to or woken up from suspend.svc_system_off, svc_system_reset
These callbacks are called during PSCI_SYSTEM_OFF and PSCI_SYSTEM_RESET PSCI APIs respectively.
svc_migrate_info
This callback is called in response to PSCI_MIGRATE_INFO_TYPE or PSCI_MIGRATE_INFO_UP_CPU APIs. The return value of this callback must correspond to the return value of PSCI_MIGRATE_INFO_TYPE API as described in PSCI. If the secure payload is a Uniprocessor (UP) implementation, then it must update the mpidr of the CPU it is resident in via
resident_cpu
(first argument). The updates toresident_cpu
is ignored if the secure payload is a multiprocessor (MP) implementation.svc_migrate
This callback is only relevant if the secure payload in EL3 Runtime Software is a Uniprocessor (UP) implementation and supports migration from the current CPU
from_cpu
(first argument) to another CPUto_cpu
(second argument). This callback is called in response to PSCI_MIGRATE API. This callback is never called if the secure payload is a Multiprocessor (MP) implementation.
2.8.4.5. CPU operations
The CPU operations (cpu_ops) framework implement power down sequence specific
to the CPU and the details of which can be found at
CPU specific operations framework. The TF-A tree implements the cpu_ops
for various supported CPUs and the EL3 Runtime Software needs to include the
required cpu_ops
in its build. The start and end of the cpu_ops
descriptors must be exported by the EL3 Runtime Software via the
__CPU_OPS_START__
and __CPU_OPS_END__
linker symbols.
The cpu_ops
descriptors also include reset sequences and may include errata
workarounds for the CPU. The EL3 Runtime Software can choose to call this
during cold/warm reset if it does not implement its own reset sequence/errata
workarounds.
Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved.