arm-trusted-firmware/services/std_svc/spmd/spmd_pm.c

157 lines
4.3 KiB
C
Raw Normal View History

/*
* Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/spinlock.h>
#include "spmd_private.h"
static struct {
bool secondary_ep_locked;
uintptr_t secondary_ep;
spinlock_t lock;
} g_spmd_pm;
/*******************************************************************************
* spmd_pm_secondary_ep_register
******************************************************************************/
int spmd_pm_secondary_ep_register(uintptr_t entry_point)
{
int ret = FFA_ERROR_INVALID_PARAMETER;
spin_lock(&g_spmd_pm.lock);
if (g_spmd_pm.secondary_ep_locked == true) {
goto out;
}
/*
* 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\n",
__func__);
goto out;
}
g_spmd_pm.secondary_ep = entry_point;
g_spmd_pm.secondary_ep_locked = true;
VERBOSE("%s %lx\n", __func__, entry_point);
ret = 0;
out:
spin_unlock(&g_spmd_pm.lock);
return ret;
}
/*******************************************************************************
* 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)
{
spmd_spm_core_context_t *ctx = spmd_get_context();
unsigned int linear_id = plat_my_core_pos();
refactor(spmd): boot interface and pass core id This change refactors the SPMD to setup SPMC CPU contexts once and early from spmd_spmc_init (single call to cm_setup_context rather than on each and every warm boot). Pass the core linear ID through a GP register as an implementation defined behavior helping FF-A adoption to legacy TOSes (essentially when secure virtualization is not used). A first version of this change was originally submitted by Lukas [1]. Pasting below the original justification: Our TEE, Kinibi, is used to receive the core linear ID in the x3 register of booting secondary cores. This patch is necessary to bring up secondary cores with Kinibi as an SPMC in SEL1. In Kinibi, the TEE is mostly platform-independent and all platform- specifics like topology is concentrated in TF-A of our customers. That is why we don't have the MPIDR - linear ID mapping in Kinibi. We need the correct linear ID to program the GICv2 target register, for example in power management case. It is not needed on GICv3/v4, because of using a fixed mapping from MPIDR to ICDIPTR/GICD_ITARGETSRn register. For debug and power management purpose, we also want a unified view to linear id between Linux and the TEE. E.g. to disable a core, to see what cores are printing a trace / an event. In the past, Kinibi had several other designs, but the complexity was getting out of control: * Platform-specific assembler macros in the kernel. * A per-core SMC from Linux to tell the linear ID after the boot. * With DynamiQ, it seems SIPs were playing with MPIDR register values, reusing them between cores and changing them during boot. [1] https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/10235 Signed-off-by: Olivier Deprez <olivier.deprez@arm.com> Signed-off-by: Lukas Hanel <lukas.hanel@trustonic.com> Change-Id: Ifa8fa208e9b8eb1642c80b5f7b54152dadafa75e
2021-06-21 08:47:13 +01:00
el3_state_t *el3_state;
uintptr_t entry_point;
uint64_t rc;
assert(ctx != NULL);
assert(ctx->state != SPMC_STATE_ON);
spin_lock(&g_spmd_pm.lock);
/*
* Leave the possibility that the SPMC does not call
* FFA_SECONDARY_EP_REGISTER in which case re-use the
* primary core address for booting secondary cores.
*/
if (g_spmd_pm.secondary_ep_locked == true) {
refactor(spmd): boot interface and pass core id This change refactors the SPMD to setup SPMC CPU contexts once and early from spmd_spmc_init (single call to cm_setup_context rather than on each and every warm boot). Pass the core linear ID through a GP register as an implementation defined behavior helping FF-A adoption to legacy TOSes (essentially when secure virtualization is not used). A first version of this change was originally submitted by Lukas [1]. Pasting below the original justification: Our TEE, Kinibi, is used to receive the core linear ID in the x3 register of booting secondary cores. This patch is necessary to bring up secondary cores with Kinibi as an SPMC in SEL1. In Kinibi, the TEE is mostly platform-independent and all platform- specifics like topology is concentrated in TF-A of our customers. That is why we don't have the MPIDR - linear ID mapping in Kinibi. We need the correct linear ID to program the GICv2 target register, for example in power management case. It is not needed on GICv3/v4, because of using a fixed mapping from MPIDR to ICDIPTR/GICD_ITARGETSRn register. For debug and power management purpose, we also want a unified view to linear id between Linux and the TEE. E.g. to disable a core, to see what cores are printing a trace / an event. In the past, Kinibi had several other designs, but the complexity was getting out of control: * Platform-specific assembler macros in the kernel. * A per-core SMC from Linux to tell the linear ID after the boot. * With DynamiQ, it seems SIPs were playing with MPIDR register values, reusing them between cores and changing them during boot. [1] https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/10235 Signed-off-by: Olivier Deprez <olivier.deprez@arm.com> Signed-off-by: Lukas Hanel <lukas.hanel@trustonic.com> Change-Id: Ifa8fa208e9b8eb1642c80b5f7b54152dadafa75e
2021-06-21 08:47:13 +01:00
/*
* The CPU context has already been initialized at boot time
* (in spmd_spmc_init by a call to cm_setup_context). Adjust
* below the target core entry point based on the address
* passed to by FFA_SECONDARY_EP_REGISTER.
*/
entry_point = g_spmd_pm.secondary_ep;
el3_state = get_el3state_ctx(&ctx->cpu_ctx);
write_ctx_reg(el3_state, CTX_ELR_EL3, entry_point);
}
spin_unlock(&g_spmd_pm.lock);
refactor(spmd): boot interface and pass core id This change refactors the SPMD to setup SPMC CPU contexts once and early from spmd_spmc_init (single call to cm_setup_context rather than on each and every warm boot). Pass the core linear ID through a GP register as an implementation defined behavior helping FF-A adoption to legacy TOSes (essentially when secure virtualization is not used). A first version of this change was originally submitted by Lukas [1]. Pasting below the original justification: Our TEE, Kinibi, is used to receive the core linear ID in the x3 register of booting secondary cores. This patch is necessary to bring up secondary cores with Kinibi as an SPMC in SEL1. In Kinibi, the TEE is mostly platform-independent and all platform- specifics like topology is concentrated in TF-A of our customers. That is why we don't have the MPIDR - linear ID mapping in Kinibi. We need the correct linear ID to program the GICv2 target register, for example in power management case. It is not needed on GICv3/v4, because of using a fixed mapping from MPIDR to ICDIPTR/GICD_ITARGETSRn register. For debug and power management purpose, we also want a unified view to linear id between Linux and the TEE. E.g. to disable a core, to see what cores are printing a trace / an event. In the past, Kinibi had several other designs, but the complexity was getting out of control: * Platform-specific assembler macros in the kernel. * A per-core SMC from Linux to tell the linear ID after the boot. * With DynamiQ, it seems SIPs were playing with MPIDR register values, reusing them between cores and changing them during boot. [1] https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/10235 Signed-off-by: Olivier Deprez <olivier.deprez@arm.com> Signed-off-by: Lukas Hanel <lukas.hanel@trustonic.com> Change-Id: Ifa8fa208e9b8eb1642c80b5f7b54152dadafa75e
2021-06-21 08:47:13 +01:00
/* 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 (%" PRIu64 ") on CPU%u\n", __func__, rc,
linear_id);
ctx->state = SPMC_STATE_OFF;
return;
}
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);
/* Build an SPMD to SPMC direct message request. */
spmd_build_spmc_message(get_gpregs_ctx(&ctx->cpu_ctx),
FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
rc = spmd_spm_core_sync_entry(ctx);
if (rc != 0ULL) {
ERROR("%s failed (%" PRIu64 ") on CPU%u\n", __func__, rc, linear_id);
}
/* Expect a direct message response from the SPMC. */
u_register_t ffa_resp_func = 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;
}
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
};