spm_main.c 8.55 KB
Newer Older
1
/*
2
 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
3
4
5
6
7
8
9
10
11
12
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch_helpers.h>
#include <assert.h>
#include <bl31.h>
#include <context_mgmt.h>
#include <debug.h>
#include <errno.h>
13
#include <mm_svc.h>
14
15
16
#include <platform.h>
#include <runtime_svc.h>
#include <secure_partition.h>
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
17
18
#include <smccc.h>
#include <smccc_helpers.h>
19
20
21
22
23
24
#include <spinlock.h>
#include <spm_svc.h>
#include <utils.h>
#include <xlat_tables_v2.h>

#include "spm_private.h"
25

26
27
28
/*******************************************************************************
 * Secure Partition context information.
 ******************************************************************************/
29
static sp_context_t sp_ctx;
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
/*******************************************************************************
 * Set state of a Secure Partition context.
 ******************************************************************************/
void sp_state_set(sp_context_t *sp_ptr, sp_state_t state)
{
	spin_lock(&(sp_ptr->state_lock));
	sp_ptr->state = state;
	spin_unlock(&(sp_ptr->state_lock));
}

/*******************************************************************************
 * Wait until the state of a Secure Partition is the specified one and change it
 * to the desired state.
 ******************************************************************************/
void sp_state_wait_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to)
{
	int success = 0;

	while (success == 0) {
		spin_lock(&(sp_ptr->state_lock));

		if (sp_ptr->state == from) {
			sp_ptr->state = to;

			success = 1;
		}

		spin_unlock(&(sp_ptr->state_lock));
	}
}

/*******************************************************************************
 * Check if the state of a Secure Partition is the specified one and, if so,
 * change it to the desired state. Returns 0 on success, -1 on error.
 ******************************************************************************/
int sp_state_try_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to)
{
	int ret = -1;

	spin_lock(&(sp_ptr->state_lock));

	if (sp_ptr->state == from) {
		sp_ptr->state = to;

		ret = 0;
	}

	spin_unlock(&(sp_ptr->state_lock));

	return ret;
}

83
/*******************************************************************************
84
85
 * This function takes an SP context pointer and performs a synchronous entry
 * into it.
86
 ******************************************************************************/
87
static uint64_t spm_sp_synchronous_entry(sp_context_t *sp_ctx)
88
{
89
90
	uint64_t rc;

91
	assert(sp_ctx != NULL);
92

93
94
	/* Assign the context of the SP to this CPU */
	cm_set_context(&(sp_ctx->cpu_ctx), SECURE);
95

96
	/* Restore the context assigned above */
97
98
99
	cm_el1_sysregs_context_restore(SECURE);
	cm_set_next_eret_context(SECURE);

100
101
102
	/* Invalidate TLBs at EL1. */
	tlbivmalle1();
	dsbish();
103
104
105
106
107
108
109
110

	/* Enter Secure Partition */
	rc = spm_secure_partition_enter(&sp_ctx->c_rt_ctx);

	/* Save secure state */
	cm_el1_sysregs_context_save(SECURE);

	return rc;
111
112
113
}

/*******************************************************************************
114
115
 * This function returns to the place where spm_sp_synchronous_entry() was
 * called originally.
116
 ******************************************************************************/
117
__dead2 static void spm_sp_synchronous_exit(uint64_t rc)
118
{
119
120
121
122
123
124
125
126
127
128
	sp_context_t *ctx = &sp_ctx;

	/*
	 * The SPM must have initiated the original request through a
	 * synchronous entry into the secure partition. Jump back to the
	 * original C runtime context with the value of rc in x0;
	 */
	spm_secure_partition_exit(ctx->c_rt_ctx, rc);

	panic();
129
130
131
}

/*******************************************************************************
132
 * Jump to each Secure Partition for the first time.
133
 ******************************************************************************/
134
static int32_t spm_init(void)
135
{
136
	uint64_t rc;
137
	sp_context_t *ctx;
138

139
	INFO("Secure Partition init...\n");
140

141
	ctx = &sp_ctx;
142

143
	ctx->state = SP_STATE_RESET;
144

145
	rc = spm_sp_synchronous_entry(ctx);
146
	assert(rc == 0);
147

148
	ctx->state = SP_STATE_IDLE;
149

150
	INFO("Secure Partition initialized.\n");
151

152
	return rc;
153
154
155
}

/*******************************************************************************
156
 * Initialize contexts of all Secure Partitions.
157
158
159
 ******************************************************************************/
int32_t spm_setup(void)
{
160
	sp_context_t *ctx;
161

162
163
164
165
166
167
168
	/* Disable MMU at EL1 (initialized by BL2) */
	disable_mmu_icache_el1();

	/* Initialize context of the SP */
	INFO("Secure Partition context setup start...\n");

	ctx = &sp_ctx;
169

170
171
	/* Assign translation tables context. */
	ctx->xlat_ctx_handle = spm_get_sp_xlat_context();
172

173
	spm_sp_setup(ctx);
174
175

	/* Register init function for deferred init.  */
176
177
	bl31_register_bl32_init(&spm_init);

178
	INFO("Secure Partition setup done.\n");
179
180
181
182

	return 0;
}

183
184
185
186
187
188
189
/*******************************************************************************
 * MM_COMMUNICATE handler
 ******************************************************************************/
static uint64_t mm_communicate(uint32_t smc_fid, uint64_t mm_cookie,
			       uint64_t comm_buffer_address,
			       uint64_t comm_size_address, void *handle)
{
190
	uint64_t rc;
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
	sp_context_t *ctx = &sp_ctx;

	/* Cookie. Reserved for future use. It must be zero. */
	if (mm_cookie != 0U) {
		ERROR("MM_COMMUNICATE: cookie is not zero\n");
		SMC_RET1(handle, SPM_INVALID_PARAMETER);
	}

	if (comm_buffer_address == 0U) {
		ERROR("MM_COMMUNICATE: comm_buffer_address is zero\n");
		SMC_RET1(handle, SPM_INVALID_PARAMETER);
	}

	if (comm_size_address != 0U) {
		VERBOSE("MM_COMMUNICATE: comm_size_address is not 0 as recommended.\n");
	}

	/* Save the Normal world context */
	cm_el1_sysregs_context_save(NON_SECURE);

211
	/* Wait until the Secure Partition is idle and set it to busy. */
212
213
	sp_state_wait_switch(ctx, SP_STATE_IDLE, SP_STATE_BUSY);

214
215
	/* Set values for registers on SP entry */
	cpu_context_t *cpu_ctx = &(ctx->cpu_ctx);
216

217
218
219
220
	write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X0, smc_fid);
	write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X1, comm_buffer_address);
	write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X2, comm_size_address);
	write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X3, plat_my_core_pos());
221

222
223
	/* Jump to the Secure Partition. */
	rc = spm_sp_synchronous_entry(ctx);
224

225
	/* Flag Secure Partition as idle. */
226
227
228
229
230
231
232
	assert(ctx->state == SP_STATE_BUSY);
	sp_state_set(ctx, SP_STATE_IDLE);

	/* Restore non-secure state */
	cm_el1_sysregs_context_restore(NON_SECURE);
	cm_set_next_eret_context(NON_SECURE);

233
	SMC_RET1(handle, rc);
234
235
}

236
237
238
/*******************************************************************************
 * Secure Partition Manager SMC handler.
 ******************************************************************************/
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
uint64_t spm_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)
{
	unsigned int ns;

	/* Determine which security state this SMC originated from */
	ns = is_caller_non_secure(flags);

	if (ns == SMC_FROM_SECURE) {

		/* Handle SMCs from Secure world. */

257
258
259
260
261
		assert(handle == cm_get_context(SECURE));

		/* Make next ERET jump to S-EL0 instead of S-EL1. */
		cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1());

262
263
		switch (smc_fid) {

264
		case SPM_VERSION_AARCH32:
265
266
267
			SMC_RET1(handle, SPM_VERSION_COMPILED);

		case SP_EVENT_COMPLETE_AARCH64:
268
			spm_sp_synchronous_exit(x1);
269

270
271
		case SP_MEMORY_ATTRIBUTES_GET_AARCH64:
			INFO("Received SP_MEMORY_ATTRIBUTES_GET_AARCH64 SMC\n");
272

273
			if (sp_ctx.state != SP_STATE_RESET) {
274
				WARN("SP_MEMORY_ATTRIBUTES_GET_AARCH64 is available at boot time only\n");
275
276
				SMC_RET1(handle, SPM_NOT_SUPPORTED);
			}
277
278
279
			SMC_RET1(handle,
				 spm_memory_attributes_get_smc_handler(
					 &sp_ctx, x1));
280

281
282
		case SP_MEMORY_ATTRIBUTES_SET_AARCH64:
			INFO("Received SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC\n");
283

284
			if (sp_ctx.state != SP_STATE_RESET) {
285
				WARN("SP_MEMORY_ATTRIBUTES_SET_AARCH64 is available at boot time only\n");
286
287
				SMC_RET1(handle, SPM_NOT_SUPPORTED);
			}
288
289
290
			SMC_RET1(handle,
				 spm_memory_attributes_set_smc_handler(
					&sp_ctx, x1, x2, x3));
291
292
293
294
295
296
297
		default:
			break;
		}
	} else {

		/* Handle SMCs from Non-secure world. */

298
299
		assert(handle == cm_get_context(NON_SECURE));

300
301
		switch (smc_fid) {

302
303
		case MM_VERSION_AARCH32:
			SMC_RET1(handle, MM_VERSION_COMPILED);
304

305
306
		case MM_COMMUNICATE_AARCH32:
		case MM_COMMUNICATE_AARCH64:
307
			return mm_communicate(smc_fid, x1, x2, x3, handle);
308

309
310
		case SP_MEMORY_ATTRIBUTES_GET_AARCH64:
		case SP_MEMORY_ATTRIBUTES_SET_AARCH64:
311
312
313
314
315
316
317
318
319
320
			/* SMC interfaces reserved for secure callers. */
			SMC_RET1(handle, SPM_NOT_SUPPORTED);

		default:
			break;
		}
	}

	SMC_RET1(handle, SMC_UNK);
}