blob: 034dced7eb99c8f0fdcc1565a12f01fa11eec74c [file] [log] [blame]
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +00001/*
2 * Copyright (c) 2018, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch_helpers.h>
8#include <assert.h>
9#include <context_mgmt.h>
10#include <debug.h>
Antonio Nino Diaz124a1fc2018-11-30 10:52:09 +000011#include <errno.h>
12#include <limits.h>
13#include <platform.h>
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +000014#include <smccc.h>
15#include <smccc_helpers.h>
16#include <sprt_svc.h>
17#include <utils.h>
18
19#include "spm_private.h"
20
21/*******************************************************************************
Antonio Nino Diaz124a1fc2018-11-30 10:52:09 +000022 * Functions to manipulate memory regions
23 ******************************************************************************/
24
25/*
26 * Attributes are encoded using a different format in the SMC interface than in
27 * the Trusted Firmware, where the mmap_attr_t enum type is used. This function
28 * converts an attributes value from the SMC format to the mmap_attr_t format by
29 * setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and MT_EXECUTE/MT_EXECUTE_NEVER.
30 * The other fields are left as 0 because they are ignored by the function
31 * xlat_change_mem_attributes_ctx().
32 */
33static unsigned int smc_attr_to_mmap_attr(unsigned int attributes)
34{
35 unsigned int perm = attributes & SPRT_MEMORY_PERM_ATTR_MASK;
36
37 if (perm == SPRT_MEMORY_PERM_ATTR_RW) {
38 return MT_RW | MT_EXECUTE_NEVER | MT_USER;
39 } else if (perm == SPRT_MEMORY_PERM_ATTR_RO) {
40 return MT_RO | MT_EXECUTE_NEVER | MT_USER;
41 } else if (perm == SPRT_MEMORY_PERM_ATTR_RO_EXEC) {
42 return MT_RO | MT_USER;
43 } else {
44 return UINT_MAX;
45 }
46}
47
48/*
49 * This function converts attributes from the Trusted Firmware format into the
50 * SMC interface format.
51 */
52static unsigned int mmap_attr_to_smc_attr(unsigned int attr)
53{
54 unsigned int perm;
55
56 /* No access from EL0. */
57 if ((attr & MT_USER) == 0U)
58 return UINT_MAX;
59
60 if ((attr & MT_RW) != 0) {
61 assert(MT_TYPE(attr) != MT_DEVICE);
62 perm = SPRT_MEMORY_PERM_ATTR_RW;
63 } else {
64 if ((attr & MT_EXECUTE_NEVER) != 0U) {
65 perm = SPRT_MEMORY_PERM_ATTR_RO;
66 } else {
67 perm = SPRT_MEMORY_PERM_ATTR_RO_EXEC;
68 }
69 }
70
71 return perm << SPRT_MEMORY_PERM_ATTR_SHIFT;
72}
73
74static int32_t sprt_memory_perm_attr_get(sp_context_t *sp_ctx, uintptr_t base_va)
75{
76 uint32_t attributes;
77
78 spin_lock(&(sp_ctx->xlat_ctx_lock));
79
80 int ret = xlat_get_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
81 base_va, &attributes);
82
83 spin_unlock(&(sp_ctx->xlat_ctx_lock));
84
85 /* Convert error codes of xlat_get_mem_attributes_ctx() into SPM. */
86 assert((ret == 0) || (ret == -EINVAL));
87
88 if (ret != 0)
89 return SPRT_INVALID_PARAMETER;
90
91 unsigned int perm = mmap_attr_to_smc_attr(attributes);
92
93 if (perm == UINT_MAX)
94 return SPRT_INVALID_PARAMETER;
95
96 return SPRT_SUCCESS | perm;
97}
98
99static int32_t sprt_memory_perm_attr_set(sp_context_t *sp_ctx,
100 u_register_t page_address, u_register_t pages_count,
101 u_register_t smc_attributes)
102{
103 int ret;
104 uintptr_t base_va = (uintptr_t) page_address;
105 size_t size = pages_count * PAGE_SIZE;
106
107 VERBOSE(" Start address : 0x%lx\n", base_va);
108 VERBOSE(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size);
109 VERBOSE(" Attributes : 0x%lx\n", smc_attributes);
110
111 uint32_t mmap_attr = smc_attr_to_mmap_attr(smc_attributes);
112
113 if (mmap_attr == UINT_MAX) {
114 WARN("%s: Invalid memory attributes: 0x%lx\n", __func__,
115 smc_attributes);
116 return SPRT_INVALID_PARAMETER;
117 }
118
119 /*
120 * Perform some checks before actually trying to change the memory
121 * attributes.
122 */
123
124 spin_lock(&(sp_ctx->xlat_ctx_lock));
125
126 uint32_t attributes;
127
128 ret = xlat_get_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
129 base_va, &attributes);
130
131 if (ret != 0) {
132 spin_unlock(&(sp_ctx->xlat_ctx_lock));
133 return SPRT_INVALID_PARAMETER;
134 }
135
136 if ((attributes & MT_USER) == 0U) {
137 /* Prohibit changing attributes of S-EL1 regions */
138 spin_unlock(&(sp_ctx->xlat_ctx_lock));
139 return SPRT_INVALID_PARAMETER;
140 }
141
142 ret = xlat_change_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
143 base_va, size, mmap_attr);
144
145 spin_unlock(&(sp_ctx->xlat_ctx_lock));
146
147 /* Convert error codes of xlat_change_mem_attributes_ctx() into SPM. */
148 assert((ret == 0) || (ret == -EINVAL));
149
150 return (ret == 0) ? SPRT_SUCCESS : SPRT_INVALID_PARAMETER;
151}
152
153/*******************************************************************************
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +0000154 * This function handles all SMCs in the range reserved for SPRT.
155 ******************************************************************************/
156uint64_t sprt_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2,
157 uint64_t x3, uint64_t x4, void *cookie, void *handle,
158 uint64_t flags)
159{
160 /* SPRT only supported from the Secure world */
161 if (is_caller_non_secure(flags) == SMC_FROM_NON_SECURE) {
162 SMC_RET1(handle, SMC_UNK);
163 }
164
165 assert(handle == cm_get_context(SECURE));
166
167 /*
168 * Only S-EL0 partitions are supported for now. Make the next ERET into
169 * the partition jump directly to S-EL0 instead of S-EL1.
170 */
171 cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1());
172
173 switch (smc_fid) {
174 case SPRT_VERSION:
175 SMC_RET1(handle, SPRT_VERSION_COMPILED);
176
Antonio Nino Diaz8c83ad82018-11-08 14:21:19 +0000177 case SPRT_PUT_RESPONSE_AARCH64:
178 /*
179 * Registers x1-x3 aren't saved by default to the context,
180 * but they are needed after spm_sp_synchronous_exit() because
181 * they hold return values.
182 */
183 SMC_SET_GP(handle, CTX_GPREG_X1, x1);
184 SMC_SET_GP(handle, CTX_GPREG_X2, x2);
185 SMC_SET_GP(handle, CTX_GPREG_X3, x3);
186 spm_sp_synchronous_exit(SPRT_PUT_RESPONSE_AARCH64);
187
188 case SPRT_YIELD_AARCH64:
189 spm_sp_synchronous_exit(SPRT_YIELD_AARCH64);
190
Antonio Nino Diaz124a1fc2018-11-30 10:52:09 +0000191 case SPRT_MEMORY_PERM_ATTR_GET_AARCH64:
192 {
193 /* Get context of the SP in use by this CPU. */
194 unsigned int linear_id = plat_my_core_pos();
195 sp_context_t *sp_ctx = spm_cpu_get_sp_ctx(linear_id);
196
197 SMC_RET1(handle, sprt_memory_perm_attr_get(sp_ctx, x1));
198 }
199
200 case SPRT_MEMORY_PERM_ATTR_SET_AARCH64:
201 {
202 /* Get context of the SP in use by this CPU. */
203 unsigned int linear_id = plat_my_core_pos();
204 sp_context_t *sp_ctx = spm_cpu_get_sp_ctx(linear_id);
205
206 SMC_RET1(handle, sprt_memory_perm_attr_set(sp_ctx, x1, x2, x3));
207 }
208
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +0000209 default:
210 break;
211 }
212
213 WARN("SPRT: Unsupported call 0x%08x\n", smc_fid);
214 SMC_RET1(handle, SPRT_NOT_SUPPORTED);
215}