blob: cb1b8fdea3d2bb8f4f7d5c14a1b8f28277de036e [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
Antonio Nino Diazb5b585a2018-11-08 14:20:07 +00007#include <assert.h>
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +00008#include <debug.h>
9#include <smccc.h>
10#include <smccc_helpers.h>
11#include <spci_svc.h>
Antonio Nino Diazb5b585a2018-11-08 14:20:07 +000012#include <spinlock.h>
13#include <string.h>
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +000014#include <utils.h>
15
16#include "spm_private.h"
17
18/*******************************************************************************
Antonio Nino Diazb5b585a2018-11-08 14:20:07 +000019 * Macros to print UUIDs.
20 ******************************************************************************/
21#define PRINT_UUID_FORMAT "%08x-%08x-%08x-%08x"
22#define PRINT_UUID_ARGS(x) x[0], x[1], x[2], x[3]
23
24/*******************************************************************************
25 * Array of structs that contains information about all handles of Secure
26 * Services that are currently open.
27 ******************************************************************************/
28typedef enum spci_handle_status {
29 HANDLE_STATUS_CLOSED = 0,
30 HANDLE_STATUS_OPEN,
31} spci_handle_status_t;
32
33typedef struct spci_handle {
34 /* 16-bit value used as reference in all SPCI calls */
35 uint16_t handle;
36
37 /* Client ID of the client that requested the handle */
38 uint16_t client_id;
39
40 /* Current status of the handle */
41 spci_handle_status_t status;
42
43 /*
44 * Context of the Secure Partition that provides the Secure Service
45 * referenced by this handle.
46 */
47 sp_context_t *sp_ctx;
48
49 /*
50 * The same handle might be used for multiple requests, keep a reference
51 * counter of them.
52 */
53 unsigned int num_active_requests;
54} spci_handle_t;
55
56static spci_handle_t spci_handles[PLAT_SPCI_HANDLES_MAX_NUM];
57static spinlock_t spci_handles_lock;
58
59/*
60 * Given a handle and a client ID, return the element of the spci_handles
61 * array that contains the information of the handle. It can only return open
62 * handles. It returns NULL if it couldn't find the element in the array.
63 */
64static spci_handle_t *spci_handle_info_get(uint16_t handle, uint16_t client_id)
65{
66 size_t i;
67
68 for (i = 0; i < ARRAY_SIZE(spci_handles); i++) {
69 spci_handle_t *h = &(spci_handles[i]);
70
71 /* Only check for open handles */
72 if (h->status == HANDLE_STATUS_CLOSED) {
73 continue;
74 }
75
76 /* Check if either the handle or the client ID are different */
77 if ((h->handle != handle) || (h->client_id != client_id)) {
78 continue;
79 }
80
81 return h;
82 }
83
84 return NULL;
85}
86
87/*
88 * Returns a unique value for a handle. This function must be called while
89 * spci_handles_lock is locked. It returns 0 on success, -1 on error.
90 */
91static int spci_create_handle_value(uint16_t *handle)
92{
93 /*
94 * Trivial implementation that relies on the fact that any handle will
95 * be closed before 2^16 more handles have been opened.
96 */
97 static uint16_t handle_count;
98
99 *handle = handle_count;
100
101 handle_count++;
102
103 return 0;
104}
105
106/*******************************************************************************
107 * This function looks for a Secure Partition that has a Secure Service
108 * identified by the given UUID. It returns a handle that the client can use to
109 * access the service, and an SPCI_*** error code.
110 ******************************************************************************/
111static uint64_t spci_service_handle_open_poll(void *handle, u_register_t x1,
112 u_register_t x2, u_register_t x3, u_register_t x4,
113 u_register_t x5, u_register_t x6, u_register_t x7)
114{
115 unsigned int i;
116 sp_context_t *sp_ptr;
117 uint16_t service_handle;
118
119 /* Bits 31:16 of w7 are reserved (MBZ). */
120 assert((x7 & 0xFFFF0000U) == 0);
121
122 uint16_t client_id = x7 & 0x0000FFFFU;
123 uint32_t uuid[4] = { x1, x2, x3, x4 };
124
125 /* Get pointer to the Secure Partition that handles this service */
126 sp_ptr = spm_sp_get_by_uuid(&uuid);
127 if (sp_ptr == NULL) {
128 WARN("SPCI: Service requested by client 0x%04x not found\n",
129 client_id);
130 WARN("SPCI: UUID: " PRINT_UUID_FORMAT "\n",
131 PRINT_UUID_ARGS(uuid));
132
133 SMC_RET2(handle, SPCI_NOT_PRESENT, 0);
134 }
135
136 /* Get lock of the array of handles */
137 spin_lock(&spci_handles_lock);
138
139 /*
140 * We need to record the client ID and Secure Partition that correspond
141 * to this handle. Look for the first free entry in the array.
142 */
143 for (i = 0; i < PLAT_SPCI_HANDLES_MAX_NUM; i++) {
144 if (spci_handles[i].status == HANDLE_STATUS_CLOSED) {
145 break;
146 }
147 }
148
149 if (i == PLAT_SPCI_HANDLES_MAX_NUM) {
150 spin_unlock(&spci_handles_lock);
151
152 WARN("SPCI: Can't open more handles. Client 0x%04x\n",
153 client_id);
154 WARN("SPCI: UUID: " PRINT_UUID_FORMAT "\n",
155 PRINT_UUID_ARGS(uuid));
156
157 SMC_RET2(handle, SPCI_NO_MEMORY, 0);
158 }
159
160 /* Create new handle value */
161 if (spci_create_handle_value(&service_handle) != 0) {
162 spin_unlock(&spci_handles_lock);
163
164 WARN("SPCI: Can't create a new handle value. Client 0x%04x\n",
165 client_id);
166 WARN("SPCI: UUID: " PRINT_UUID_FORMAT "\n",
167 PRINT_UUID_ARGS(uuid));
168
169 SMC_RET2(handle, SPCI_NO_MEMORY, 0);
170 }
171
172 /* Save all information about this handle */
173 spci_handles[i].status = HANDLE_STATUS_OPEN;
174 spci_handles[i].client_id = client_id;
175 spci_handles[i].handle = service_handle;
176 spci_handles[i].num_active_requests = 0U;
177 spci_handles[i].sp_ctx = sp_ptr;
178
179 /* Release lock of the array of handles */
180 spin_unlock(&spci_handles_lock);
181
182 VERBOSE("SPCI: Service handle request by client 0x%04x: 0x%04x\n",
183 client_id, service_handle);
184 VERBOSE("SPCI: UUID: " PRINT_UUID_FORMAT "\n", PRINT_UUID_ARGS(uuid));
185
186 /* The handle is returned in the top 16 bits of x1 */
187 SMC_RET2(handle, SPCI_SUCCESS, ((uint32_t)service_handle) << 16);
188}
189
190/*******************************************************************************
191 * This function closes a handle that a specific client uses to access a Secure
192 * Service. It returns a SPCI_*** error code.
193 ******************************************************************************/
194static uint64_t spci_service_handle_close(void *handle, u_register_t x1)
195{
196 spci_handle_t *handle_info;
197 uint16_t client_id = x1 & 0x0000FFFFU;
198 uint16_t service_handle = (x1 >> 16) & 0x0000FFFFU;
199
200 spin_lock(&spci_handles_lock);
201
202 handle_info = spci_handle_info_get(service_handle, client_id);
203
204 if (handle_info == NULL) {
205 spin_unlock(&spci_handles_lock);
206
207 WARN("SPCI: Tried to close invalid handle 0x%04x by client 0x%04x\n",
208 service_handle, client_id);
209
210 SMC_RET1(handle, SPCI_INVALID_PARAMETER);
211 }
212
213 if (handle_info->status != HANDLE_STATUS_OPEN) {
214 spin_unlock(&spci_handles_lock);
215
216 WARN("SPCI: Tried to close handle 0x%04x by client 0x%04x in status %d\n",
217 service_handle, client_id, handle_info->status);
218
219 SMC_RET1(handle, SPCI_INVALID_PARAMETER);
220 }
221
222 if (handle_info->num_active_requests != 0U) {
223 spin_unlock(&spci_handles_lock);
224
225 /* A handle can't be closed if there are requests left */
226 WARN("SPCI: Tried to close handle 0x%04x by client 0x%04x with %d requests left\n",
227 service_handle, client_id,
228 handle_info->num_active_requests);
229
230 SMC_RET1(handle, SPCI_BUSY);
231 }
232
233 memset(handle_info, 0, sizeof(spci_handle_t));
234
235 handle_info->status = HANDLE_STATUS_CLOSED;
236
237 spin_unlock(&spci_handles_lock);
238
239 VERBOSE("SPCI: Closed handle 0x%04x by client 0x%04x.\n",
240 service_handle, client_id);
241
242 SMC_RET1(handle, SPCI_SUCCESS);
243}
244
245/*******************************************************************************
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +0000246 * This function handles all SMCs in the range reserved for SPCI.
247 ******************************************************************************/
248uint64_t spci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2,
249 uint64_t x3, uint64_t x4, void *cookie, void *handle,
250 uint64_t flags)
251{
252 uint32_t spci_fid;
253
254 /* SPCI only supported from the Non-secure world for now */
255 if (is_caller_non_secure(flags) == SMC_FROM_SECURE) {
256 SMC_RET1(handle, SMC_UNK);
257 }
258
259 if ((smc_fid & SPCI_FID_TUN_FLAG) == 0) {
260
261 /* Miscellaneous calls */
262
263 spci_fid = (smc_fid >> SPCI_FID_MISC_SHIFT) & SPCI_FID_MISC_MASK;
264
265 switch (spci_fid) {
266
267 case SPCI_FID_VERSION:
268 SMC_RET1(handle, SPCI_VERSION_COMPILED);
269
Antonio Nino Diazb5b585a2018-11-08 14:20:07 +0000270 case SPCI_FID_SERVICE_HANDLE_OPEN:
271 {
272 if ((smc_fid & SPCI_SERVICE_HANDLE_OPEN_NOTIFY_BIT) != 0) {
273 /* Not supported for now */
274 WARN("SPCI_SERVICE_HANDLE_OPEN_NOTIFY not supported.\n");
275 SMC_RET1(handle, SPCI_INVALID_PARAMETER);
276 }
277
278 uint64_t x5 = SMC_GET_GP(handle, CTX_GPREG_X5);
279 uint64_t x6 = SMC_GET_GP(handle, CTX_GPREG_X6);
280 uint64_t x7 = SMC_GET_GP(handle, CTX_GPREG_X7);
281
282 return spci_service_handle_open_poll(handle, x1, x2, x3,
283 x4, x5, x6, x7);
284 }
285 case SPCI_FID_SERVICE_HANDLE_CLOSE:
286 return spci_service_handle_close(handle, x1);
287
Antonio Nino Diazf939a6a2018-11-08 14:12:40 +0000288 default:
289 break;
290 }
291
292 } else {
293
294 /* Tunneled calls */
295
296 }
297
298 WARN("SPCI: Unsupported call 0x%08x\n", smc_fid);
299 SMC_RET1(handle, SPCI_NOT_SUPPORTED);
300}