aboutsummaryrefslogtreecommitdiff
path: root/services/std_svc/spmd/spmd_pm.c
blob: 569c579cae31d88d8798f686ddcd0ab3102862f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>

#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <plat/common/platform.h>
#include <smccc_helpers.h>
#include "spmd_private.h"

/*******************************************************************************
 * This cpu has been turned on. Enter SPMC to initialise S-EL1 or S-EL2 before
 * initialising 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)
{
	int32_t rc = 0;
	uint32_t linear_id = plat_my_core_pos();
	spmd_spm_core_context_t *ctx = &spm_core_context[linear_id];

	assert(ctx->state != AFF_STATE_ON);

	/* Enter SPMC on this cpu if an entry point has been setup*/
	if (ctx->state == AFF_STATE_OFF)
		return;

	rc = spmd_spm_core_sync_entry(ctx);
	if (rc) {
		ERROR("SPMC initialisation failed (%d) on cpu%d\n", rc,
		      linear_id);
		panic();
	}

	ctx->state = AFF_STATE_ON;
}

/*******************************************************************************
 * This function handles all PSCI SMCs that originate from the Secure world.
 ******************************************************************************/
uint64_t spmd_psci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2,
			       uint64_t x3, uint64_t x4, void *cookie,
			       void *handle, uint64_t flags)
{
	int32_t ret;
	entry_point_info_t ep_info;
	int32_t target_ctx;
	spmd_spm_core_context_t *ctx;

	/* Assert that the call originated from the Secure world */
	assert(!is_caller_non_secure(flags));

	switch (smc_fid) {
	case PSCI_VERSION:
		return (u_register_t) psci_version();
	case PSCI_CPU_ON_AARCH32:
	case PSCI_CPU_ON_AARCH64:
		/* Determine if the target SPMC context exists of not */
		ret = psci_validate_mpidr(x1);
		if (ret != PSCI_E_SUCCESS) {
			WARN("%s: %d \n", __func__, ret);
			return PSCI_E_INVALID_PARAMS;
		}

		/* Obtain linear index of target ctx */
		target_ctx = plat_core_pos_by_mpidr(x1);

		/* Obtain reference to SPMC context information on target ctx */
		ctx = &spm_core_context[target_ctx];

		/* Check if target context is already on */
		if (ctx->state == AFF_STATE_ON) {
			WARN("%s: %d \n", __func__, PSCI_E_ALREADY_ON);
			return PSCI_E_ALREADY_ON;
		}

		/* Check if target context is being turned on */
		if (ctx->state == AFF_STATE_ON_PENDING) {
			WARN("%s: %d \n", __func__, ret);
			return PSCI_E_ON_PENDING;
		}

		/*
		 * Initialise an entry_point_info structure for target
		 * context.
		 */
		ep_info = *spmc_ep_info;
		ep_info.pc = x2;
		zeromem(&ep_info.args, sizeof(ep_info.args));
		ep_info.args.arg0 = x3;

		spin_lock(&ctx->lock);

		/* Check if a parallel call got ahead of us */
		ret = PSCI_E_ON_PENDING;
		if (ctx->state == AFF_STATE_OFF) {
			/*
			 * Setup a CPU context for entry into SPMC on the target
			 * ctx
			 */
			cm_setup_context(&ctx->cpu_ctx, &ep_info);
			ctx->state = AFF_STATE_ON_PENDING;
			ret = PSCI_E_SUCCESS;
		}

		spin_unlock(&ctx->lock);
		return ret;
	default:
		WARN("SPMD: Unsupported PSCI call: 0x%08x\n", smc_fid);
		return SMC_UNK;
	}
}

/*******************************************************************************
 * Structure populated by the SPM Dispatcher to perform any bookkeeping before
 * PSCI executes a power mgmt. operation.
 ******************************************************************************/
const spd_pm_ops_t spmd_pm = {
	.psci_sec_smc_handler = spmd_psci_smc_handler,
	.svc_on_finish = spmd_cpu_on_finish_handler,
};