Merge changes from topic "spm-secondary-cores" into integration

* changes:
  SPMC: embed secondary core ep info into to SPMC context
  SPMC: manifest changes to support multicore boot
  SPMD: secondary cores PM on and off SPD hooks relayed to SPMC
  SPMD: handle SPMC message to register secondary core entry point
  SPMD: introduce SPMC to SPMD messages
  SPMD: register the SPD PM hooks
  SPMD: add generic SPD PM handlers
  SPMD: enhance SPMC internal boot states
  SPMD: entry point info get helper
This commit is contained in:
Olivier Deprez 2020-08-21 14:18:57 +00:00 committed by TrustedFirmware Code Review
commit a6ab1ae39c
7 changed files with 364 additions and 33 deletions

View File

@ -138,4 +138,43 @@
*/
#define FFA_PARAM_MBZ U(0x0)
/*
* Maximum FF-A endpoint id value
*/
#define FFA_ENDPOINT_ID_MAX U(1 << 16)
/*
* Mask for source and destination endpoint id in
* a direct message request/response.
*/
#define FFA_DIRECT_MSG_ENDPOINT_ID_MASK U(0xffff)
/*
* Bit shift for destination endpoint id in a direct message request/response.
*/
#define FFA_DIRECT_MSG_DESTINATION_SHIFT U(0)
/*
* Bit shift for source endpoint id in a direct message request/response.
*/
#define FFA_DIRECT_MSG_SOURCE_SHIFT U(16)
/******************************************************************************
* ffa_endpoint_destination
*****************************************************************************/
static inline uint16_t ffa_endpoint_destination(unsigned int ep)
{
return (ep >> FFA_DIRECT_MSG_DESTINATION_SHIFT) &
FFA_DIRECT_MSG_ENDPOINT_ID_MASK;
}
/******************************************************************************
* ffa_endpoint_source
*****************************************************************************/
static inline uint16_t ffa_endpoint_source(unsigned int ep)
{
return (ep >> FFA_DIRECT_MSG_SOURCE_SHIFT) &
FFA_DIRECT_MSG_ENDPOINT_ID_MASK;
}
#endif /* FFA_SVC_H */

View File

@ -44,7 +44,7 @@ typedef struct spm_core_manifest_sect_attribute {
uint32_t binary_size;
/*
* ID of the SPMD (mandatory)
* ID of the SPMC (mandatory)
*/
uint16_t spmc_id;

View File

@ -5,6 +5,14 @@
*/
/dts-v1/;
#define AFF 00
#include "fvp-defs.dtsi"
#undef POST
#define POST \
enable-method = "psci"; \
};
/ {
compatible = "arm,ffa-core-manifest-1.0";
#address-cells = <2>;
@ -17,6 +25,7 @@
exec_state = <0x0>;
load_address = <0x0 0x6000000>;
entrypoint = <0x0 0x6000000>;
binary_size = <0x80000>;
};
chosen {
@ -51,22 +60,15 @@
#address-cells = <0x2>;
#size-cells = <0x0>;
cpu-map {
cluster0 {
core0 {
cpu = <0x2>;
};
};
};
cpu@0 {
device_type = "cpu";
compatible = "arm,armv8";
reg = <0x0 0x0>;
enable-method = "psci";
next-level-cache = <0xc>;
phandle = <0x2>;
};
CPU_0
/* SPM(Hafnium) requires secondary cpu nodes are declared in descending order */
CPU_7
CPU_6
CPU_5
CPU_4
CPU_3
CPU_2
CPU_1
};
memory@60000000 {

View File

@ -10,6 +10,7 @@ endif
SPMD_SOURCES += $(addprefix services/std_svc/spmd/, \
${ARCH}/spmd_helpers.S \
spmd_pm.c \
spmd_main.c)
# Let the top-level Makefile know that we intend to include a BL32 image

View File

@ -41,14 +41,36 @@ static spmc_manifest_attribute_t spmc_attrs;
******************************************************************************/
static entry_point_info_t *spmc_ep_info;
/*******************************************************************************
* SPM Core context on CPU based on mpidr.
******************************************************************************/
spmd_spm_core_context_t *spmd_get_context_by_mpidr(uint64_t mpidr)
{
return &spm_core_context[plat_core_pos_by_mpidr(mpidr)];
}
/*******************************************************************************
* SPM Core context on current CPU get helper.
******************************************************************************/
spmd_spm_core_context_t *spmd_get_context(void)
{
unsigned int linear_id = plat_my_core_pos();
return spmd_get_context_by_mpidr(read_mpidr());
}
return &spm_core_context[linear_id];
/*******************************************************************************
* SPM Core entry point information get helper.
******************************************************************************/
entry_point_info_t *spmd_spmc_ep_info_get(void)
{
return spmc_ep_info;
}
/*******************************************************************************
* SPM Core ID getter.
******************************************************************************/
uint16_t spmd_spmc_id_get(void)
{
return spmc_attrs.spmc_id;
}
/*******************************************************************************
@ -125,9 +147,19 @@ static int32_t spmd_init(void)
{
spmd_spm_core_context_t *ctx = spmd_get_context();
uint64_t rc;
unsigned int linear_id = plat_my_core_pos();
unsigned int core_id;
VERBOSE("SPM Core init start.\n");
ctx->state = SPMC_STATE_RESET;
ctx->state = SPMC_STATE_ON_PENDING;
/* Set the SPMC context state on other CPUs to OFF */
for (core_id = 0U; core_id < PLATFORM_CORE_COUNT; core_id++) {
if (core_id != linear_id) {
spm_core_context[core_id].state = SPMC_STATE_OFF;
spm_core_context[core_id].secondary_ep.entry_point = 0UL;
}
}
rc = spmd_spm_core_sync_entry(ctx);
if (rc != 0ULL) {
@ -135,7 +167,8 @@ static int32_t spmd_init(void)
return 0;
}
ctx->state = SPMC_STATE_IDLE;
ctx->state = SPMC_STATE_ON;
VERBOSE("SPM Core init end.\n");
return 1;
@ -248,6 +281,9 @@ static int spmd_spmc_init(void *pm_addr)
INFO("SPM Core setup done.\n");
/* Register power management hooks with PSCI */
psci_register_spd_pm_hook(&spmd_pm);
/* Register init function for deferred init. */
bl31_register_bl32_init(&spmd_init);
@ -301,8 +337,8 @@ static uint64_t spmd_smc_forward(uint32_t smc_fid,
uint64_t x4,
void *handle)
{
uint32_t secure_state_in = (secure_origin) ? SECURE : NON_SECURE;
uint32_t secure_state_out = (!secure_origin) ? SECURE : NON_SECURE;
unsigned int secure_state_in = (secure_origin) ? SECURE : NON_SECURE;
unsigned int secure_state_out = (!secure_origin) ? SECURE : NON_SECURE;
/* Save incoming security state */
cm_el1_sysregs_context_save(secure_state_in);
@ -334,6 +370,46 @@ static uint64_t spmd_ffa_error_return(void *handle, int error_code)
FFA_PARAM_MBZ, FFA_PARAM_MBZ);
}
/*******************************************************************************
* spmd_check_address_in_binary_image
******************************************************************************/
bool spmd_check_address_in_binary_image(uint64_t address)
{
assert(!check_uptr_overflow(spmc_attrs.load_address, spmc_attrs.binary_size));
return ((address >= spmc_attrs.load_address) &&
(address < (spmc_attrs.load_address + spmc_attrs.binary_size)));
}
/******************************************************************************
* spmd_is_spmc_message
*****************************************************************************/
static bool spmd_is_spmc_message(unsigned int ep)
{
return ((ffa_endpoint_destination(ep) == SPMD_DIRECT_MSG_ENDPOINT_ID)
&& (ffa_endpoint_source(ep) == spmc_attrs.spmc_id));
}
/******************************************************************************
* spmd_handle_spmc_message
*****************************************************************************/
static int spmd_handle_spmc_message(unsigned long long msg,
unsigned long long parm1, unsigned long long parm2,
unsigned long long parm3, unsigned long long parm4)
{
VERBOSE("%s %llx %llx %llx %llx %llx\n", __func__,
msg, parm1, parm2, parm3, parm4);
switch (msg) {
case SPMD_DIRECT_MSG_SET_ENTRY_POINT:
return spmd_pm_secondary_core_set_ep(parm1, parm2, parm3);
default:
break;
}
return -EINVAL;
}
/*******************************************************************************
* This function handles all SMCs in the range reserved for FFA. Each call is
* either forwarded to the other security state or handled by the SPM dispatcher
@ -367,7 +443,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid,
* this CPU. If so, then indicate that the SPM Core initialised
* unsuccessfully.
*/
if (secure_origin && (ctx->state == SPMC_STATE_RESET)) {
if (secure_origin && (ctx->state == SPMC_STATE_ON_PENDING)) {
spmd_spm_core_sync_exit(x2);
}
@ -451,6 +527,35 @@ uint64_t spmd_smc_handler(uint32_t smc_fid,
break; /* not reached */
case FFA_MSG_SEND_DIRECT_REQ_SMC32:
if (secure_origin && spmd_is_spmc_message(x1)) {
ret = spmd_handle_spmc_message(x3, x4,
SMC_GET_GP(handle, CTX_GPREG_X5),
SMC_GET_GP(handle, CTX_GPREG_X6),
SMC_GET_GP(handle, CTX_GPREG_X7));
SMC_RET8(handle, FFA_SUCCESS_SMC32,
FFA_TARGET_INFO_MBZ, ret,
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
FFA_PARAM_MBZ);
} else {
/* Forward direct message to the other world */
return spmd_smc_forward(smc_fid, secure_origin,
x1, x2, x3, x4, handle);
}
break; /* Not reached */
case FFA_MSG_SEND_DIRECT_RESP_SMC32:
if (secure_origin && spmd_is_spmc_message(x1)) {
spmd_spm_core_sync_exit(0);
} else {
/* Forward direct message to the other world */
return spmd_smc_forward(smc_fid, secure_origin,
x1, x2, x3, x4, handle);
}
break; /* Not reached */
case FFA_RX_RELEASE:
case FFA_RXTX_MAP_SMC32:
case FFA_RXTX_MAP_SMC64:
@ -466,9 +571,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid,
case FFA_PARTITION_INFO_GET:
case FFA_MSG_SEND:
case FFA_MSG_SEND_DIRECT_REQ_SMC32:
case FFA_MSG_SEND_DIRECT_REQ_SMC64:
case FFA_MSG_SEND_DIRECT_RESP_SMC32:
case FFA_MSG_SEND_DIRECT_RESP_SMC64:
case FFA_MEM_DONATE_SMC32:
case FFA_MEM_DONATE_SMC64:
@ -500,7 +603,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid,
* this CPU from the Secure world. If so, then indicate that the
* SPM Core initialised successfully.
*/
if (secure_origin && (ctx->state == SPMC_STATE_RESET)) {
if (secure_origin && (ctx->state == SPMC_STATE_ON_PENDING)) {
spmd_spm_core_sync_exit(0);
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <lib/el3_runtime/context_mgmt.h>
#include "spmd_private.h"
/*******************************************************************************
* spmd_build_spmc_message
*
* Builds an SPMD to SPMC direct message request.
******************************************************************************/
static void spmd_build_spmc_message(gp_regs_t *gpregs, unsigned long long message)
{
write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32);
write_ctx_reg(gpregs, CTX_GPREG_X1,
(SPMD_DIRECT_MSG_ENDPOINT_ID << FFA_DIRECT_MSG_SOURCE_SHIFT) |
spmd_spmc_id_get());
write_ctx_reg(gpregs, CTX_GPREG_X2, FFA_PARAM_MBZ);
write_ctx_reg(gpregs, CTX_GPREG_X3, message);
}
/*******************************************************************************
* spmd_pm_secondary_core_set_ep
******************************************************************************/
int spmd_pm_secondary_core_set_ep(unsigned long long mpidr,
uintptr_t entry_point, unsigned long long context)
{
int id = plat_core_pos_by_mpidr(mpidr);
if ((id < 0) || (id >= PLATFORM_CORE_COUNT)) {
ERROR("%s inconsistent MPIDR (%llx)\n", __func__, mpidr);
return -EINVAL;
}
/*
* Check entry_point address is a PA within
* load_address <= entry_point < load_address + binary_size
*/
if (!spmd_check_address_in_binary_image(entry_point)) {
ERROR("%s entry point is not within image boundaries (%llx)\n",
__func__, mpidr);
return -EINVAL;
}
spmd_spm_core_context_t *ctx = spmd_get_context_by_mpidr(mpidr);
spmd_pm_secondary_ep_t *secondary_ep = &ctx->secondary_ep;
if (secondary_ep->locked) {
ERROR("%s entry locked (%llx)\n", __func__, mpidr);
return -EINVAL;
}
/* Fill new entry to corresponding secondary core id and lock it */
secondary_ep->entry_point = entry_point;
secondary_ep->context = context;
secondary_ep->locked = true;
VERBOSE("%s %d %llx %lx %llx\n",
__func__, id, mpidr, entry_point, context);
return 0;
}
/*******************************************************************************
* This CPU has been turned on. Enter SPMC to initialise S-EL1 or S-EL2. As part
* of the SPMC initialization path, they will initialize any SPs that they
* manage. Entry into SPMC is done after initialising minimal architectural
* state that guarantees safe execution.
******************************************************************************/
static void spmd_cpu_on_finish_handler(u_register_t unused)
{
entry_point_info_t *spmc_ep_info = spmd_spmc_ep_info_get();
spmd_spm_core_context_t *ctx = spmd_get_context();
unsigned int linear_id = plat_my_core_pos();
uint64_t rc;
assert(ctx != NULL);
assert(ctx->state != SPMC_STATE_ON);
assert(spmc_ep_info != NULL);
/*
* TODO: this might require locking the spmc_ep_info structure,
* or provisioning one structure per cpu
*/
if (ctx->secondary_ep.entry_point == 0UL) {
goto exit;
}
spmc_ep_info->pc = ctx->secondary_ep.entry_point;
cm_setup_context(&ctx->cpu_ctx, spmc_ep_info);
write_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx), CTX_GPREG_X0,
ctx->secondary_ep.context);
/* Mark CPU as initiating ON operation */
ctx->state = SPMC_STATE_ON_PENDING;
rc = spmd_spm_core_sync_entry(ctx);
if (rc != 0ULL) {
ERROR("%s failed (%llu) on CPU%u\n", __func__, rc,
linear_id);
ctx->state = SPMC_STATE_OFF;
return;
}
exit:
ctx->state = SPMC_STATE_ON;
VERBOSE("CPU %u on!\n", linear_id);
}
/*******************************************************************************
* spmd_cpu_off_handler
******************************************************************************/
static int32_t spmd_cpu_off_handler(u_register_t unused)
{
spmd_spm_core_context_t *ctx = spmd_get_context();
unsigned int linear_id = plat_my_core_pos();
int64_t rc;
assert(ctx != NULL);
assert(ctx->state != SPMC_STATE_OFF);
if (ctx->secondary_ep.entry_point == 0UL) {
goto exit;
}
/* Build an SPMD to SPMC direct message request. */
spmd_build_spmc_message(get_gpregs_ctx(&ctx->cpu_ctx), PSCI_CPU_OFF);
rc = spmd_spm_core_sync_entry(ctx);
if (rc != 0ULL) {
ERROR("%s failed (%llu) on CPU%u\n", __func__, rc, linear_id);
}
/* TODO expect FFA_DIRECT_MSG_RESP returned from SPMC */
exit:
ctx->state = SPMC_STATE_OFF;
VERBOSE("CPU %u off!\n", linear_id);
return 0;
}
/*******************************************************************************
* Structure populated by the SPM Dispatcher to perform any bookkeeping before
* PSCI executes a power mgmt. operation.
******************************************************************************/
const spd_pm_ops_t spmd_pm = {
.svc_on_finish = spmd_cpu_on_finish_handler,
.svc_off = spmd_cpu_off_handler
};

View File

@ -30,14 +30,24 @@
#define SPMD_C_RT_CTX_ENTRIES (SPMD_C_RT_CTX_SIZE >> DWORD_SHIFT)
#ifndef __ASSEMBLER__
#include <services/ffa_svc.h>
#include <stdint.h>
#include <lib/psci/psci_lib.h>
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
typedef enum spmc_state {
SPMC_STATE_RESET = 0,
SPMC_STATE_IDLE
SPMC_STATE_OFF,
SPMC_STATE_ON_PENDING,
SPMC_STATE_ON
} spmc_state_t;
typedef struct spmd_pm_secondary_ep {
uintptr_t entry_point;
uintptr_t context;
bool locked;
} spmd_pm_secondary_ep_t;
/*
* Data structure used by the SPM dispatcher (SPMD) in EL3 to track context of
* the SPM core (SPMC) at the next lower EL.
@ -46,16 +56,20 @@ typedef struct spmd_spm_core_context {
uint64_t c_rt_ctx;
cpu_context_t cpu_ctx;
spmc_state_t state;
spmd_pm_secondary_ep_t secondary_ep;
} spmd_spm_core_context_t;
/*
* Reserve ID for NS physical FFA Endpoint.
*/
#define FFA_NS_ENDPOINT_ID U(0)
#define FFA_NS_ENDPOINT_ID U(0)
/* Mask and shift to check valid secure FFA Endpoint ID. */
#define SPMC_SECURE_ID_MASK U(1)
#define SPMC_SECURE_ID_SHIFT U(15)
/* Mask and shift to check valid secure FF-A Endpoint ID. */
#define SPMC_SECURE_ID_MASK U(1)
#define SPMC_SECURE_ID_SHIFT U(15)
#define SPMD_DIRECT_MSG_ENDPOINT_ID U(FFA_ENDPOINT_ID_MAX - 1)
#define SPMD_DIRECT_MSG_SET_ENTRY_POINT U(1)
/* Functions used to enter/exit SPMC synchronously */
uint64_t spmd_spm_core_sync_entry(spmd_spm_core_context_t *ctx);
@ -65,9 +79,25 @@ __dead2 void spmd_spm_core_sync_exit(uint64_t rc);
uint64_t spmd_spm_core_enter(uint64_t *c_rt_ctx);
void __dead2 spmd_spm_core_exit(uint64_t c_rt_ctx, uint64_t ret);
/* SPMD SPD power management handlers */
extern const spd_pm_ops_t spmd_pm;
/* SPMC entry point information helper */
entry_point_info_t *spmd_spmc_ep_info_get(void);
/* SPMC ID getter */
uint16_t spmd_spmc_id_get(void);
/* SPMC context on CPU based on mpidr */
spmd_spm_core_context_t *spmd_get_context_by_mpidr(uint64_t mpidr);
/* SPMC context on current CPU get helper */
spmd_spm_core_context_t *spmd_get_context(void);
int spmd_pm_secondary_core_set_ep(unsigned long long mpidr,
uintptr_t entry_point, unsigned long long context);
bool spmd_check_address_in_binary_image(uint64_t address);
#endif /* __ASSEMBLER__ */
#endif /* SPMD_PRIVATE_H */