spm_main.c 8.36 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
 * This function takes an SP context pointer and prepares the CPU to enter.
85
 ******************************************************************************/
86
static void spm_sp_prepare_enter(sp_context_t *sp_ctx)
87
{
88
	assert(sp_ctx != NULL);
89

90
91
	/* Assign the context of the SP to this CPU */
	cm_set_context(&(sp_ctx->cpu_ctx), SECURE);
92

93
	/* Restore the context assigned above */
94
95
96
	cm_el1_sysregs_context_restore(SECURE);
	cm_set_next_eret_context(SECURE);

97
98
99
	/* Invalidate TLBs at EL1. */
	tlbivmalle1();
	dsbish();
100
101
102
}

/*******************************************************************************
103
 * Enter SP after preparing it with spm_sp_prepare_enter().
104
 ******************************************************************************/
105
static uint64_t spm_sp_enter(sp_context_t *sp_ctx)
106
{
107
108
	/* Enter Secure Partition */
	return spm_secure_partition_enter(&sp_ctx->c_rt_ctx);
109
110
111
}

/*******************************************************************************
112
 * Jump to each Secure Partition for the first time.
113
 ******************************************************************************/
114
static int32_t spm_init(void)
115
{
116
	uint64_t rc = 0;
117
	sp_context_t *ctx;
118

119
	INFO("Secure Partition init...\n");
120

121
	ctx = &sp_ctx;
122

123
	ctx->state = SP_STATE_RESET;
124

125
126
127
	spm_sp_prepare_enter(ctx);
	rc |= spm_sp_enter(ctx);
	assert(rc == 0);
128

129
	ctx->state = SP_STATE_IDLE;
130

131
	INFO("Secure Partition initialized.\n");
132

133
	return rc;
134
135
136
}

/*******************************************************************************
137
 * Initialize contexts of all Secure Partitions.
138
139
140
 ******************************************************************************/
int32_t spm_setup(void)
{
141
	sp_context_t *ctx;
142

143
144
145
146
147
148
149
	/* 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;
150

151
152
	/* Assign translation tables context. */
	ctx->xlat_ctx_handle = spm_get_sp_xlat_context();
153

154
	spm_sp_setup(ctx);
155
156

	/* Register init function for deferred init.  */
157
158
	bl31_register_bl32_init(&spm_init);

159
	INFO("Secure Partition setup done.\n");
160
161
162
163

	return 0;
}

164
165
166
/*******************************************************************************
 * Secure Partition Manager SMC handler.
 ******************************************************************************/
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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)
{
	cpu_context_t *ns_cpu_context;
	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. */

186
187
188
189
190
		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());

191
192
		switch (smc_fid) {

193
		case SPM_VERSION_AARCH32:
194
195
196
			SMC_RET1(handle, SPM_VERSION_COMPILED);

		case SP_EVENT_COMPLETE_AARCH64:
197
			/* Save secure state */
198
199
			cm_el1_sysregs_context_save(SECURE);

200
			if (sp_ctx.state == SP_STATE_RESET) {
201
202
203
204
205
206
207
				/*
				 * SPM reports completion. The SPM must have
				 * initiated the original request through a
				 * synchronous entry into the secure
				 * partition. Jump back to the original C
				 * runtime context.
				 */
208
209
210
				spm_secure_partition_exit(sp_ctx.c_rt_ctx, x1);

				/* spm_secure_partition_exit doesn't return */
211
212
			}

213
214
215
216
			/* Mark Secure Partition as idle */
			assert(sp_ctx.state == SP_STATE_BUSY);

			sp_state_set(&sp_ctx, SP_STATE_IDLE);
217

218
219
220
			/*
			 * This is the result from the Secure partition of an
			 * earlier request. Copy the result into the non-secure
221
			 * context and return to the non-secure state.
222
223
224
225
			 */

			/* Get a reference to the non-secure context */
			ns_cpu_context = cm_get_context(NON_SECURE);
226
			assert(ns_cpu_context != NULL);
227
228
229
230
231
232
233
234

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

			/* Return to normal world */
			SMC_RET1(ns_cpu_context, x1);

235
236
		case SP_MEMORY_ATTRIBUTES_GET_AARCH64:
			INFO("Received SP_MEMORY_ATTRIBUTES_GET_AARCH64 SMC\n");
237

238
			if (sp_ctx.state != SP_STATE_RESET) {
239
				WARN("SP_MEMORY_ATTRIBUTES_GET_AARCH64 is available at boot time only\n");
240
241
				SMC_RET1(handle, SPM_NOT_SUPPORTED);
			}
242
243
244
			SMC_RET1(handle,
				 spm_memory_attributes_get_smc_handler(
					 &sp_ctx, x1));
245

246
247
		case SP_MEMORY_ATTRIBUTES_SET_AARCH64:
			INFO("Received SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC\n");
248

249
			if (sp_ctx.state != SP_STATE_RESET) {
250
				WARN("SP_MEMORY_ATTRIBUTES_SET_AARCH64 is available at boot time only\n");
251
252
				SMC_RET1(handle, SPM_NOT_SUPPORTED);
			}
253
254
255
			SMC_RET1(handle,
				 spm_memory_attributes_set_smc_handler(
					&sp_ctx, x1, x2, x3));
256
257
258
259
260
261
262
263
264
		default:
			break;
		}
	} else {

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

		switch (smc_fid) {

265
266
		case MM_VERSION_AARCH32:
			SMC_RET1(handle, MM_VERSION_COMPILED);
267

268
269
		case MM_COMMUNICATE_AARCH32:
		case MM_COMMUNICATE_AARCH64:
270
271
272
273
274
275
		{
			uint64_t mm_cookie = x1;
			uint64_t comm_buffer_address = x2;
			uint64_t comm_size_address = x3;

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

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

286
			if (comm_size_address != 0U) {
287
288
				VERBOSE("MM_COMMUNICATE: comm_size_address is not 0 as recommended.\n");
			}
289
290
291
292

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

293
294
295
296
297
298
			/*
			 * Wait until the state of the Secure Partition is IDLE
			 * and set it to BUSY
			 */
			sp_state_wait_switch(&sp_ctx,
					     SP_STATE_IDLE, SP_STATE_BUSY);
299

300
301
302
			/* Jump to the Secure Partition. */

			spm_sp_prepare_enter(&sp_ctx);
303

304
305
306
			SMC_RET4(&(sp_ctx.cpu_ctx), smc_fid,
				 comm_buffer_address, comm_size_address,
				 plat_my_core_pos());
307
		}
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);
}