1 /*
2 * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include "ts_rpc_caller_linux.h"
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <linux/tee.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/ioctl.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <util.h>
23
24 #define INVALID_SESS_ID 0
25 #define MAX_TEE_DEV_NUM 16
26 #define TS_TEE_DRV_INVALID_SHM_ID (0)
27
28 /*
29 * This define is part of linux/tee.h starting from Linux v6.10
30 * Let's keep a copy here in case the kernel headers come from an older version
31 */
32 #ifndef TEE_IMPL_ID_TSTEE
33 #define TEE_IMPL_ID_TSTEE 3
34 #endif
35
36 struct ts_tee_dev {
37 uint16_t endpoint_id;
38 char path[16];
39 };
40
41 struct ts_rpc_caller_linux_context {
42 struct ts_tee_dev ts_tee_devs[MAX_TEE_DEV_NUM];
43 uint32_t session_id;
44 int fd;
45 };
46
47 #define TEE_IOC_OPEN_SESSION_NUM_PARAMS 0
open_session(void * context,const struct rpc_uuid * service_uuid,uint16_t endpoint_id)48 static rpc_status_t open_session(void *context, const struct rpc_uuid *service_uuid,
49 uint16_t endpoint_id)
50 {
51 struct ts_rpc_caller_linux_context *caller = (struct ts_rpc_caller_linux_context *)context;
52 const size_t arg_size = sizeof(struct tee_ioctl_open_session_arg) +
53 TEE_IOC_OPEN_SESSION_NUM_PARAMS * sizeof(struct tee_ioctl_param);
54 union {
55 struct tee_ioctl_open_session_arg arg;
56 uint8_t data[arg_size];
57 } buf;
58 struct tee_ioctl_open_session_arg *arg = NULL;
59 struct tee_ioctl_buf_data buf_data = { 0 };
60 struct ts_tee_dev *dev = NULL;
61 int rc = -1;
62
63 if (caller->fd >= 0 || caller->session_id != INVALID_SESS_ID) {
64 printf("%s():%d session is already opened\n", __func__, __LINE__);
65 return RPC_ERROR_INVALID_STATE;
66 }
67
68 for (int i = 0; i < ARRAY_SIZE(caller->ts_tee_devs); i++) {
69 if (caller->ts_tee_devs[i].endpoint_id == endpoint_id) {
70 dev = &caller->ts_tee_devs[i];
71 break;
72 }
73 }
74
75 if (!dev) {
76 printf("%s():%d cannot find device for 0x%04x\n", __func__, __LINE__, endpoint_id);
77 return RPC_ERROR_NOT_FOUND;
78 }
79
80 caller->fd = open(dev->path, O_RDWR);
81 if (caller->fd < 0) {
82 printf("%s():%d cannot open %s: %d\n", __func__, __LINE__, dev->path, errno);
83 return RPC_ERROR_INTERNAL;
84 }
85
86 memset(&buf, 0, sizeof(buf));
87
88 buf_data.buf_ptr = (uintptr_t)&buf;
89 buf_data.buf_len = sizeof(buf);
90
91 arg = &buf.arg;
92 arg->num_params = TEE_IOC_OPEN_SESSION_NUM_PARAMS;
93
94 memcpy(arg->uuid, service_uuid->uuid, sizeof(service_uuid->uuid));
95
96 rc = ioctl(caller->fd, TEE_IOC_OPEN_SESSION, &buf_data);
97 if (rc) {
98 close(caller->fd);
99 caller->fd = -1;
100 return RPC_ERROR_INTERNAL;
101 }
102
103 caller->session_id = arg->session;
104
105 return RPC_SUCCESS;
106 }
107
find_and_open_session(void * context,const struct rpc_uuid * service_uuid)108 static rpc_status_t find_and_open_session(void *context, const struct rpc_uuid *service_uuid)
109 {
110 struct ts_rpc_caller_linux_context *caller = (struct ts_rpc_caller_linux_context *)context;
111
112 for (int i = 0; i < ARRAY_SIZE(caller->ts_tee_devs); i++) {
113 if (!open_session(context, service_uuid, caller->ts_tee_devs[i].endpoint_id))
114 return RPC_SUCCESS;
115 }
116
117 return RPC_ERROR_INTERNAL;
118 }
119
close_session(void * context)120 rpc_status_t close_session(void *context)
121 {
122 struct ts_rpc_caller_linux_context *caller = (struct ts_rpc_caller_linux_context *)context;
123 struct tee_ioctl_close_session_arg arg = { 0 };
124 int rc = -1;
125
126 if (caller->fd < 0) {
127 printf("%s():%d session is already closed\n", __func__, __LINE__);
128 return RPC_ERROR_INVALID_STATE;
129 }
130
131 arg.session = caller->session_id;
132
133 rc = ioctl(caller->fd, TEE_IOC_CLOSE_SESSION, &arg);
134 if (rc) {
135 printf("%s():%d failed to close session: %d\n", __func__, __LINE__, errno);
136 return RPC_ERROR_INTERNAL;
137 }
138
139 close(caller->fd);
140 caller->fd = -1;
141 caller->session_id = INVALID_SESS_ID;
142
143 return RPC_SUCCESS;
144 }
145
create_shared_memory(void * context,size_t size,struct rpc_caller_shared_memory * shared_memory)146 rpc_status_t create_shared_memory(void *context, size_t size,
147 struct rpc_caller_shared_memory *shared_memory)
148 {
149 struct ts_rpc_caller_linux_context *caller = (struct ts_rpc_caller_linux_context *)context;
150 struct tee_ioctl_shm_alloc_data data = { .size = size };
151 int shm_fd = -1;
152
153 if (!size) {
154 shared_memory->buffer = NULL;
155 shared_memory->size = 0;
156 shared_memory->id = TS_TEE_DRV_INVALID_SHM_ID;
157
158 return RPC_SUCCESS;
159 }
160
161 shm_fd = ioctl(caller->fd, TEE_IOC_SHM_ALLOC, &data);
162 if (shm_fd < 0) {
163 printf("%s():%d failed to create shared memory: %d\n", __func__, __LINE__, errno);
164 return RPC_ERROR_INTERNAL;
165 }
166
167 shared_memory->buffer =
168 mmap(NULL, data.size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
169 if (shared_memory->buffer == (void *)MAP_FAILED) {
170 printf("%s():%d failed to map shared memory: %d\n", __func__, __LINE__, errno);
171 close(shm_fd);
172 return RPC_ERROR_INTERNAL;
173 }
174 close(shm_fd);
175 shared_memory->size = data.size;
176 shared_memory->id = data.id;
177
178 return RPC_SUCCESS;
179 }
180
release_shared_memory(void * context,struct rpc_caller_shared_memory * shared_memory)181 rpc_status_t release_shared_memory(void *context, struct rpc_caller_shared_memory *shared_memory)
182 {
183 (void)context;
184
185 if (shared_memory->id == TS_TEE_DRV_INVALID_SHM_ID)
186 return RPC_SUCCESS;
187
188 if (munmap(shared_memory->buffer, shared_memory->size)) {
189 printf("%s():%d failed to unmap shared memory: %d\n", __func__, __LINE__, errno);
190 return RPC_ERROR_INTERNAL;
191 }
192
193 *shared_memory = (struct rpc_caller_shared_memory){ 0 };
194
195 return RPC_SUCCESS;
196 }
197
198 #define TEE_IOC_INVOKE_NUM_PARAMS 1
call(void * context,uint16_t opcode,struct rpc_caller_shared_memory * shared_memory,size_t request_length,size_t * response_length,service_status_t * service_status)199 static rpc_status_t call(void *context, uint16_t opcode,
200 struct rpc_caller_shared_memory *shared_memory, size_t request_length,
201 size_t *response_length, service_status_t *service_status)
202 {
203 struct ts_rpc_caller_linux_context *caller = (struct ts_rpc_caller_linux_context *)context;
204 const size_t arg_size = sizeof(struct tee_ioctl_invoke_arg) +
205 TEE_IOC_INVOKE_NUM_PARAMS * sizeof(struct tee_ioctl_param);
206 union {
207 struct tee_ioctl_invoke_arg arg;
208 uint8_t data[arg_size];
209 } buf;
210 struct tee_ioctl_buf_data buf_data = { 0 };
211 struct tee_ioctl_invoke_arg *arg = NULL;
212 struct tee_ioctl_param *params = NULL;
213 int rc = -1;
214
215 memset(&buf, 0, sizeof(buf));
216
217 buf_data.buf_ptr = (uintptr_t)&buf;
218 buf_data.buf_len = sizeof(buf);
219
220 arg = &buf.arg;
221 arg->func = opcode;
222 arg->session = caller->session_id;
223 arg->num_params = TEE_IOC_INVOKE_NUM_PARAMS;
224 params = (struct tee_ioctl_param *)(arg + 1);
225
226 params[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
227 params[0].a = shared_memory->id;
228 params[0].b = request_length;
229 params[0].c = 0;
230
231 rc = ioctl(caller->fd, TEE_IOC_INVOKE, &buf_data);
232 if (rc) {
233 printf("%s():%d failed to invoke command: %d\n", __func__, __LINE__, errno);
234 return RPC_ERROR_INTERNAL;
235 }
236
237 *response_length = params[0].a;
238 *service_status = (int)arg->ret;
239
240 return RPC_SUCCESS;
241 }
242
ts_tee_drv_discover(struct ts_tee_dev * ts_tee_devs,size_t count)243 static void ts_tee_drv_discover(struct ts_tee_dev *ts_tee_devs, size_t count)
244 {
245 struct tee_ioctl_version_data vers = { 0 };
246 unsigned int tee_file_index = 0;
247 unsigned int ts_tee_dev_index = 0;
248 char path[16];
249 int rc = -1;
250 int fd = -1;
251
252 for (tee_file_index = 0; tee_file_index < MAX_TEE_DEV_NUM && ts_tee_dev_index < count;
253 tee_file_index++) {
254 snprintf(path, sizeof(path), "/dev/tee%u", tee_file_index);
255
256 fd = open(path, O_RDWR);
257 if (fd < 0)
258 continue;
259
260 memset(&vers, 0, sizeof(vers));
261
262 rc = ioctl(fd, TEE_IOC_VERSION, &vers);
263 close(fd);
264
265 if (!rc && vers.impl_id == TEE_IMPL_ID_TSTEE) {
266 ts_tee_devs[ts_tee_dev_index].endpoint_id = vers.impl_caps;
267 memcpy(ts_tee_devs[ts_tee_dev_index].path, path, sizeof(path));
268 ts_tee_dev_index++;
269 }
270 }
271 }
272
ts_rpc_caller_linux_init(struct rpc_caller_interface * rpc_caller)273 rpc_status_t ts_rpc_caller_linux_init(struct rpc_caller_interface *rpc_caller)
274 {
275 struct ts_rpc_caller_linux_context *context = NULL;
276
277 if (!rpc_caller || rpc_caller->context)
278 return RPC_ERROR_INVALID_VALUE;
279
280 context = (struct ts_rpc_caller_linux_context *)calloc(
281 1, sizeof(struct ts_rpc_caller_linux_context));
282 if (!context)
283 return RPC_ERROR_INTERNAL;
284
285 context->fd = -1;
286 context->session_id = INVALID_SESS_ID;
287
288 rpc_caller->context = context;
289 rpc_caller->open_session = open_session;
290 rpc_caller->find_and_open_session = find_and_open_session;
291 rpc_caller->close_session = close_session;
292 rpc_caller->create_shared_memory = create_shared_memory;
293 rpc_caller->release_shared_memory = release_shared_memory;
294 rpc_caller->call = call;
295
296 ts_tee_drv_discover(context->ts_tee_devs, ARRAY_SIZE(context->ts_tee_devs));
297
298 return RPC_SUCCESS;
299 }
300
ts_rpc_caller_linux_deinit(struct rpc_caller_interface * rpc_caller)301 rpc_status_t ts_rpc_caller_linux_deinit(struct rpc_caller_interface *rpc_caller)
302 {
303 struct ts_rpc_caller_linux_context *caller = NULL;
304
305 if (!rpc_caller || !rpc_caller->context)
306 return RPC_ERROR_INVALID_VALUE;
307
308 caller = (struct ts_rpc_caller_linux_context *)rpc_caller->context;
309
310 if (caller->session_id != INVALID_SESS_ID) {
311 close_session(rpc_caller);
312 caller->session_id = INVALID_SESS_ID;
313 }
314
315 if (caller->fd >= 0) {
316 close(caller->fd);
317 caller->fd = -1;
318 }
319
320 free(rpc_caller->context);
321
322 return RPC_SUCCESS;
323 }
324