spm_main.c 8.81 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/*******************************************************************************
 * 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)
{
	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);

	/* Wait until the Secure Partition is IDLE and set it to BUSY. */
	sp_state_wait_switch(ctx, SP_STATE_IDLE, SP_STATE_BUSY);

	/* Jump to the Secure Partition. */
	spm_sp_prepare_enter(ctx);

	SMC_RET4(&(ctx->cpu_ctx), smc_fid, comm_buffer_address,
		 comm_size_address, plat_my_core_pos());
}

/*******************************************************************************
 * SP_EVENT_COMPLETE_AARCH64 handler
 ******************************************************************************/
static uint64_t sp_event_complete(uint64_t x1)
{
	sp_context_t *ctx = &sp_ctx;

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

	if (ctx->state == SP_STATE_RESET) {
		/*
		 * 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.
		 */
		spm_secure_partition_exit(ctx->c_rt_ctx, x1);

		/* spm_secure_partition_exit doesn't return */
	}

	/*
	 * This is the result from the Secure partition of an earlier request.
	 * Copy the result into the non-secure context and return to the
	 * non-secure state.
	 */

	/* Mark Secure Partition as idle */
	assert(ctx->state == SP_STATE_BUSY);

	sp_state_set(ctx, SP_STATE_IDLE);

	/* Get a reference to the non-secure context */
	cpu_context_t *ns_cpu_context = cm_get_context(NON_SECURE);

	assert(ns_cpu_context != NULL);

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

	/* Return to non-secure world */
	SMC_RET1(ns_cpu_context, x1);
}

246
247
248
/*******************************************************************************
 * Secure Partition Manager SMC handler.
 ******************************************************************************/
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
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. */

267
268
269
270
271
		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());

272
273
		switch (smc_fid) {

274
		case SPM_VERSION_AARCH32:
275
276
277
			SMC_RET1(handle, SPM_VERSION_COMPILED);

		case SP_EVENT_COMPLETE_AARCH64:
278
			return sp_event_complete(x1);
279

280
281
		case SP_MEMORY_ATTRIBUTES_GET_AARCH64:
			INFO("Received SP_MEMORY_ATTRIBUTES_GET_AARCH64 SMC\n");
282

283
			if (sp_ctx.state != SP_STATE_RESET) {
284
				WARN("SP_MEMORY_ATTRIBUTES_GET_AARCH64 is available at boot time only\n");
285
286
				SMC_RET1(handle, SPM_NOT_SUPPORTED);
			}
287
288
289
			SMC_RET1(handle,
				 spm_memory_attributes_get_smc_handler(
					 &sp_ctx, x1));
290

291
292
		case SP_MEMORY_ATTRIBUTES_SET_AARCH64:
			INFO("Received SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC\n");
293

294
			if (sp_ctx.state != SP_STATE_RESET) {
295
				WARN("SP_MEMORY_ATTRIBUTES_SET_AARCH64 is available at boot time only\n");
296
297
				SMC_RET1(handle, SPM_NOT_SUPPORTED);
			}
298
299
300
			SMC_RET1(handle,
				 spm_memory_attributes_set_smc_handler(
					&sp_ctx, x1, x2, x3));
301
302
303
304
305
306
307
308
309
		default:
			break;
		}
	} else {

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

		switch (smc_fid) {

310
311
		case MM_VERSION_AARCH32:
			SMC_RET1(handle, MM_VERSION_COMPILED);
312

313
314
		case MM_COMMUNICATE_AARCH32:
		case MM_COMMUNICATE_AARCH64:
315
			return mm_communicate(smc_fid, x1, x2, x3, handle);
316

317
318
		case SP_MEMORY_ATTRIBUTES_GET_AARCH64:
		case SP_MEMORY_ATTRIBUTES_SET_AARCH64:
319
320
321
322
323
324
325
326
327
328
			/* SMC interfaces reserved for secure callers. */
			SMC_RET1(handle, SPM_NOT_SUPPORTED);

		default:
			break;
		}
	}

	SMC_RET1(handle, SMC_UNK);
}