spm_main.c 9.33 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
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

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

#include "spm_private.h"
26

27
28
29
/*******************************************************************************
 * Secure Partition context information.
 ******************************************************************************/
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
sp_context_t sp_ctx_array[PLAT_SPM_MAX_PARTITIONS];

/* Last Secure Partition last used by the CPU */
sp_context_t *cpu_sp_ctx[PLATFORM_CORE_COUNT];

void spm_cpu_set_sp_ctx(unsigned int linear_id, sp_context_t *sp_ctx)
{
	assert(linear_id < PLATFORM_CORE_COUNT);

	cpu_sp_ctx[linear_id] = sp_ctx;
}

sp_context_t *spm_cpu_get_sp_ctx(unsigned int linear_id)
{
	assert(linear_id < PLATFORM_CORE_COUNT);

	return cpu_sp_ctx[linear_id];
}
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
/*******************************************************************************
 * Functions to keep track of how many requests a Secure Partition has received
 * and hasn't finished.
 ******************************************************************************/
void spm_sp_request_increase(sp_context_t *sp_ctx)
{
	spin_lock(&(sp_ctx->request_count_lock));
	sp_ctx->request_count++;
	spin_unlock(&(sp_ctx->request_count_lock));
}

void spm_sp_request_decrease(sp_context_t *sp_ctx)
{
	spin_lock(&(sp_ctx->request_count_lock));
	sp_ctx->request_count--;
	spin_unlock(&(sp_ctx->request_count_lock));
}

/* Returns 0 if it was originally 0, -1 otherwise. */
int spm_sp_request_increase_if_zero(sp_context_t *sp_ctx)
{
	int ret = -1;

	spin_lock(&(sp_ctx->request_count_lock));
	if (sp_ctx->request_count == 0U) {
		sp_ctx->request_count++;
		ret = 0U;
	}
	spin_unlock(&(sp_ctx->request_count_lock));

	return ret;
}

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
/*******************************************************************************
 * This function returns a pointer to the context of the Secure Partition that
 * handles the service specified by an UUID. It returns NULL if the UUID wasn't
 * found.
 ******************************************************************************/
sp_context_t *spm_sp_get_by_uuid(const uint32_t (*svc_uuid)[4])
{
	unsigned int i;

	for (i = 0U; i < PLAT_SPM_MAX_PARTITIONS; i++) {

		sp_context_t *sp_ctx = &sp_ctx_array[i];

		if (sp_ctx->is_present == 0) {
			continue;
		}

		struct sp_rd_sect_service *rdsvc;

		for (rdsvc = sp_ctx->rd.service; rdsvc != NULL;
		     rdsvc = rdsvc->next) {
			uint32_t *rd_uuid = (uint32_t *)(rdsvc->uuid);

			if (memcmp(rd_uuid, svc_uuid, sizeof(rd_uuid)) == 0) {
				return sp_ctx;
			}
		}
	}

	return NULL;
}

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*******************************************************************************
 * 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;
}

166
/*******************************************************************************
167
168
 * This function takes an SP context pointer and performs a synchronous entry
 * into it.
169
 ******************************************************************************/
170
uint64_t spm_sp_synchronous_entry(sp_context_t *sp_ctx, int can_preempt)
171
{
172
	uint64_t rc;
173
	unsigned int linear_id = plat_my_core_pos();
174

175
	assert(sp_ctx != NULL);
176

177
	/* Assign the context of the SP to this CPU */
178
	spm_cpu_set_sp_ctx(linear_id, sp_ctx);
179
	cm_set_context(&(sp_ctx->cpu_ctx), SECURE);
180

181
	/* Restore the context assigned above */
182
183
184
	cm_el1_sysregs_context_restore(SECURE);
	cm_set_next_eret_context(SECURE);

185
186
187
	/* Invalidate TLBs at EL1. */
	tlbivmalle1();
	dsbish();
188

189
190
191
192
193
194
	if (can_preempt == 1) {
		enable_intr_rm_local(INTR_TYPE_NS, SECURE);
	} else {
		disable_intr_rm_local(INTR_TYPE_NS, SECURE);
	}

195
196
197
198
199
200
201
	/* Enter Secure Partition */
	rc = spm_secure_partition_enter(&sp_ctx->c_rt_ctx);

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

	return rc;
202
203
204
}

/*******************************************************************************
205
206
 * This function returns to the place where spm_sp_synchronous_entry() was
 * called originally.
207
 ******************************************************************************/
208
__dead2 void spm_sp_synchronous_exit(uint64_t rc)
209
{
210
211
212
	/* Get context of the SP in use by this CPU. */
	unsigned int linear_id = plat_my_core_pos();
	sp_context_t *ctx = spm_cpu_get_sp_ctx(linear_id);
213
214
215
216
217
218
219
220
221

	/*
	 * 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();
222
223
}

224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*******************************************************************************
 * This function is the handler registered for Non secure interrupts by the SPM.
 * It validates the interrupt and upon success arranges entry into the normal
 * world for handling the interrupt.
 ******************************************************************************/
static uint64_t spm_ns_interrupt_handler(uint32_t id, uint32_t flags,
					  void *handle, void *cookie)
{
	/* Check the security state when the exception was generated */
	assert(get_interrupt_src_ss(flags) == SECURE);

	spm_sp_synchronous_exit(SPM_SECURE_PARTITION_PREEMPTED);
}

238
/*******************************************************************************
239
 * Jump to each Secure Partition for the first time.
240
 ******************************************************************************/
241
static int32_t spm_init(void)
242
{
243
	uint64_t rc = 0;
244
	sp_context_t *ctx;
245

246
247
248
249
250
251
252
	for (unsigned int i = 0U; i < PLAT_SPM_MAX_PARTITIONS; i++) {

		ctx = &sp_ctx_array[i];

		if (ctx->is_present == 0) {
			continue;
		}
253

254
		INFO("Secure Partition %u init...\n", i);
255

256
		ctx->state = SP_STATE_RESET;
257

258
		rc = spm_sp_synchronous_entry(ctx, 0);
259
260
261
262
		if (rc != SPRT_YIELD_AARCH64) {
			ERROR("Unexpected return value 0x%llx\n", rc);
			panic();
		}
263

264
		ctx->state = SP_STATE_IDLE;
265

266
267
		INFO("Secure Partition %u initialized.\n", i);
	}
268

269
	return rc;
270
271
272
}

/*******************************************************************************
273
 * Initialize contexts of all Secure Partitions.
274
275
276
 ******************************************************************************/
int32_t spm_setup(void)
{
277
	int rc;
278
	sp_context_t *ctx;
279
280
	void *sp_base, *rd_base;
	size_t sp_size, rd_size;
281
	uint64_t flags = 0U;
282

283
284
285
	/* Disable MMU at EL1 (initialized by BL2) */
	disable_mmu_icache_el1();

286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
	/*
	 * Non-blocking services can be interrupted by Non-secure interrupts.
	 * Register an interrupt handler for NS interrupts when generated while
	 * the CPU is in secure state. They are routed to EL3.
	 */
	set_interrupt_rm_flag(flags, SECURE);

	uint64_t rc_int = register_interrupt_type_handler(INTR_TYPE_NS,
				spm_ns_interrupt_handler, flags);
	if (rc_int) {
		ERROR("SPM: Failed to register NS interrupt handler with rc = %llx\n",
		      rc_int);
		panic();
	}

	/*
	 * Setup all Secure Partitions.
	 */
304
	unsigned int i = 0U;
305

306
307
308
309
310
311
312
	while (1) {
		rc = plat_spm_sp_get_next_address(&sp_base, &sp_size,
						&rd_base, &rd_size);
		if (rc < 0) {
			/* Reached the end of the package. */
			break;
		}
313

314
315
316
317
		if (i >= PLAT_SPM_MAX_PARTITIONS) {
			ERROR("Too many partitions in the package.\n");
			panic();
		}
318

319
		ctx = &sp_ctx_array[i];
320

321
		assert(ctx->is_present == 0);
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
		/* Initialize context of the SP */
		INFO("Secure Partition %u context setup start...\n", i);

		/* Assign translation tables context. */
		ctx->xlat_ctx_handle = spm_sp_xlat_context_alloc();

		/* Save location of the image in physical memory */
		ctx->image_base = (uintptr_t)sp_base;
		ctx->image_size = sp_size;

		rc = plat_spm_sp_rd_load(&ctx->rd, rd_base, rd_size);
		if (rc < 0) {
			ERROR("Error while loading RD blob.\n");
			panic();
		}

		spm_sp_setup(ctx);

		ctx->is_present = 1;

		INFO("Secure Partition %u setup done.\n", i);

		i++;
346
347
	}

348
349
350
351
	if (i == 0U) {
		ERROR("No present partitions in the package.\n");
		panic();
	}
352
353

	/* Register init function for deferred init.  */
354
355
356
357
	bl31_register_bl32_init(&spm_init);

	return 0;
}