1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
5 */
6
7 #include <app.h>
8 #include <app_common.h>
9 #include <app_common_arch.h>
10 #include <app_header.h>
11 #include <app_header_private.h>
12 #include <app_services.h>
13 #include <assert.h>
14 #include <debug.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <time.h>
18 #include <unistd.h>
19
20 /*
21 * TODO: The host application uses a single shared page that is the same for all
22 * the application instances. The best location for this buffer would be
23 * in struct app_data_cfg, but it doesn't fit there as a size limit on
24 * the `struct rec` size which contains attestation app's data.
25 * Remove this TODO once the aarch64 implementation uses a single shared
26 * page as well.
27 */
28 static char shared_page[GRANULE_SIZE];
29
30 /* In case APP_COUNT is 0 or 1 then set a meaningful array size to prevent array
31 * subscript <unknown> is outside array bounds warning
32 */
33 /* NOLINTNEXTLINE(misc-redundant-expression) */
34 static struct app_process_data app_process_datas[(APP_COUNT < 2U)?(2U):(APP_COUNT)];
35 static size_t initialised_app_process_data_count;
36
int_to_str(int value,char * buf,size_t buf_size)37 void int_to_str(int value, char *buf, size_t buf_size)
38 {
39 char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
40 char reverse_buf[sizeof(int) * 3 + 2];
41 char *c = reverse_buf;
42 size_t buf_idx = 0;
43
44 if (buf_size < sizeof(reverse_buf)) {
45 if (buf_size > 0U) {
46 buf[0] = '\0';
47 }
48 return;
49 }
50
51 if (value < 0) {
52 buf[buf_idx++] = '-';
53 value = -value;
54 }
55 do {
56 *c = digits[value%10];
57 value = value / 10;
58 ++c;
59 } while (value != 0);
60 do {
61 --c;
62 buf[buf_idx] = *c;
63 ++buf_idx;
64 } while (c != reverse_buf);
65 buf[buf_idx] = '\0';
66 }
67
start_app_process(unsigned long app_id,int read_fd,int write_fd)68 static void start_app_process(unsigned long app_id, int read_fd, int write_fd)
69 {
70 int ret;
71 struct app_header *app_header;
72
73 ret = app_get_header_ptr(app_id, &app_header);
74 if (ret != 0) {
75 ERROR("-----------------------------------------------------------------------\n");
76 ERROR("This error usually happens when no app elf files are provided to the\n");
77 ERROR("RMM core elf, or there is a mismatch between the app ID in the RMM core\n");
78 ERROR("and the app ID provided in the command line.\n");
79 ERROR("See the output with '--help'.\n");
80 ERROR("-----------------------------------------------------------------------\n");
81 exit(1);
82 }
83
84 char s_read_fd[sizeof(int) * 3 + 2];
85 char s_write_fd[sizeof(int) * 3 + 2];
86
87 int_to_str(read_fd, s_read_fd, sizeof(s_read_fd));
88 int_to_str(write_fd, s_write_fd, sizeof(s_write_fd));
89 char *args[] = {app_header->app_elf_name, s_read_fd, s_write_fd, NULL};
90
91 ret = execv(app_header->app_elf_name, args);
92 if (ret == -1) {
93 perror("Failed to create child process");
94 exit(1);
95 }
96 }
97
create_app_process(unsigned long app_id)98 static struct app_process_data *create_app_process(unsigned long app_id)
99 {
100 /* To prevent array subscript <unknown> is outside array bounds warning */
101 /* cppcheck-suppress unsignedPositive
102 * As initialised_app_process_data_count is unsigned,
103 * initialised_app_process_data_count >= APP_COUNT is always true if
104 * APP_COUNT is zero.
105 */
106 if ((initialised_app_process_data_count == 0UL) &&
107 (initialised_app_process_data_count >= APP_COUNT)) {
108 return NULL;
109 }
110
111 struct app_process_data *ret = &app_process_datas[initialised_app_process_data_count];
112
113 int fds_rmm_to_app_process[2];
114 int fds_app_process_to_rmm[2];
115
116 if (pipe(fds_rmm_to_app_process) == -1) {
117 return NULL;
118 }
119 if (pipe(fds_app_process_to_rmm) == -1) {
120 return NULL;
121 }
122
123 ret->pid = fork();
124
125 if (ret->pid == 0) {
126 /* We are in the child process, close the unnecessary fds and
127 * execv into the app.
128 */
129 close(fds_rmm_to_app_process[1]);
130 close(fds_app_process_to_rmm[0]);
131 start_app_process(app_id, fds_rmm_to_app_process[0], fds_app_process_to_rmm[1]);
132 /* The function above should never return */
133 assert(false);
134 }
135
136 close(fds_rmm_to_app_process[0]);
137 close(fds_app_process_to_rmm[1]);
138 ret->fd_rmm_to_app_process = fds_rmm_to_app_process[1];
139 ret->fd_app_process_to_rmm = fds_app_process_to_rmm[0];
140 ret->app_id = app_id;
141
142 ++initialised_app_process_data_count;
143 return ret;
144 }
145
get_app_process_data(unsigned long app_id)146 static struct app_process_data *get_app_process_data(unsigned long app_id)
147 {
148 size_t i;
149
150 /* To prevent array subscript <unknown> is outside array bounds warning */
151 /* cppcheck-suppress unsignedPositive
152 * As initialised_app_process_data_count is unsigned,
153 * initialised_app_process_data_count >= APP_COUNT is always true if
154 * APP_COUNT is zero.
155 */
156 if ((initialised_app_process_data_count == 0UL) &&
157 (initialised_app_process_data_count >= APP_COUNT)) {
158 return NULL;
159 }
160
161 for (i = 0; i < initialised_app_process_data_count; ++i) {
162 if (app_process_datas[i].app_id == app_id) {
163 return &app_process_datas[i];
164 }
165 }
166
167 return NULL;
168 }
169
app_framework_setup(void)170 void app_framework_setup(void)
171 {
172 /* Not related to app initialisation, but this is a good location for
173 * one time initialisation
174 */
175 srand(time(NULL));
176 }
177
app_init_data(struct app_data_cfg * app_data,unsigned long app_id,uintptr_t granule_pas[],size_t granule_count,void * granule_va_start)178 int app_init_data(struct app_data_cfg *app_data,
179 unsigned long app_id,
180 uintptr_t granule_pas[],
181 size_t granule_count,
182 void *granule_va_start)
183 {
184 struct app_process_data *app_process_data;
185 unsigned long command;
186
187 (void)granule_pas;
188 (void)granule_count;
189 (void)granule_va_start;
190
191 app_process_data = get_app_process_data(app_id);
192 if (app_process_data == NULL) {
193 app_process_data = create_app_process(app_id);
194 if (app_process_data == NULL) {
195 return -EINVAL;
196 }
197 }
198
199 /* Create the thread for this app instance */
200 command = CREATE_NEW_APP_INSTANCE;
201
202 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &command, sizeof(command));
203 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &app_data->thread_id,
204 sizeof(app_data->thread_id));
205 app_data->el2_shared_page = NULL;
206 app_data->app_id = app_id;
207 app_data->app_heap = NULL;
208 app_data->heap_size = 0;
209 return 0;
210 }
211
app_map_shared_page(struct app_data_cfg * app_data)212 void app_map_shared_page(struct app_data_cfg *app_data)
213 {
214 assert(app_data->el2_shared_page == NULL);
215 app_data->el2_shared_page = shared_page;
216 }
217
app_unmap_shared_page(struct app_data_cfg * app_data)218 void app_unmap_shared_page(struct app_data_cfg *app_data)
219 {
220 assert(app_data->el2_shared_page != NULL);
221 app_data->el2_shared_page = NULL;
222 }
223
app_run_internal(struct app_data_cfg * app_data,struct app_process_data * app_process_data)224 static unsigned long app_run_internal(struct app_data_cfg *app_data,
225 struct app_process_data *app_process_data)
226 {
227 unsigned long retval = 0x0F0F0F0FU;
228
229 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
230 shared_page, GRANULE_SIZE);
231 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
232 &(app_data->heap_size), sizeof(app_data->heap_size));
233 if (app_data->heap_size > 0) {
234 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
235 app_data->app_heap, app_data->heap_size);
236 }
237
238 unsigned long reason;
239 size_t app_heap_size;
240
241 while (true) {
242 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &reason, sizeof(reason));
243 if (reason == APP_EXIT_CALL) {
244 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm,
245 &retval, sizeof(retval));
246 app_data->exit_flag = (uint32_t)APP_EXIT_SVC_EXIT_FLAG;
247 break;
248 } else if (reason == APP_YIELD_CALL) {
249 app_data->exit_flag = (uint32_t)APP_EXIT_SVC_YIELD_FLAG;
250 break;
251 } else if (reason == APP_SERVICE_CALL) {
252
253 unsigned long service_index;
254 unsigned long arg0;
255 unsigned long arg1;
256 unsigned long arg2;
257 unsigned long arg3;
258
259 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &service_index,
260 sizeof(service_index));
261 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg0, sizeof(arg0));
262 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg1, sizeof(arg1));
263 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg2, sizeof(arg2));
264 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg3, sizeof(arg3));
265 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm,
266 shared_page, GRANULE_SIZE);
267 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm,
268 &app_heap_size, sizeof(app_heap_size));
269 if (app_data->heap_size == 0) {
270 app_data->app_heap = malloc(app_heap_size);
271 app_data->heap_size = app_heap_size;
272 } else {
273 assert(app_data->heap_size == app_heap_size);
274 }
275 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm,
276 app_data->app_heap, app_heap_size);
277
278 retval = call_app_service(service_index, app_data, arg0, arg1, arg2, arg3);
279
280 unsigned long bytes_to_forward =
281 sizeof(unsigned long) +
282 sizeof(shared_page) +
283 sizeof(unsigned long) +
284 app_data->heap_size;
285
286 const unsigned long command = RUN_APP_INSTANCE;
287
288 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
289 &command, sizeof(command));
290 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_data->thread_id,
291 sizeof(app_data->thread_id));
292 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &bytes_to_forward,
293 sizeof(bytes_to_forward));
294
295 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
296 &retval, sizeof(retval));
297 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
298 shared_page, GRANULE_SIZE);
299 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
300 &(app_data->heap_size), sizeof(app_data->heap_size));
301 assert(app_data->heap_size > 0);
302 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process,
303 app_data->app_heap, app_data->heap_size);
304
305 continue;
306 }
307
308 ERROR("ERROR: Invalid exit reason %lu\n", reason);
309 exit(1);
310
311 }
312 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, shared_page, GRANULE_SIZE);
313 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm,
314 &app_heap_size, sizeof(app_heap_size));
315 if (app_data->heap_size == 0) {
316 app_data->app_heap = malloc(app_heap_size);
317 app_data->heap_size = app_heap_size;
318 } else {
319 assert(app_data->heap_size == app_heap_size);
320 }
321 READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, app_data->app_heap, app_heap_size);
322
323 return retval;
324 }
325
app_run(struct app_data_cfg * app_data,unsigned long app_func_id,unsigned long arg0,unsigned long arg1,unsigned long arg2,unsigned long arg3)326 unsigned long app_run(struct app_data_cfg *app_data,
327 unsigned long app_func_id,
328 unsigned long arg0,
329 unsigned long arg1,
330 unsigned long arg2,
331 unsigned long arg3)
332 {
333 struct app_process_data *app_process_data;
334
335 app_process_data = get_app_process_data(app_data->app_id);
336 if (app_process_data == NULL) {
337 exit(1);
338 }
339
340 /* This function should not be called if the EL0 app was yielded */
341 assert(app_data->exit_flag != APP_EXIT_SVC_YIELD_FLAG);
342
343 const unsigned long command = RUN_APP_INSTANCE;
344
345 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &command, sizeof(command));
346 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_data->thread_id,
347 sizeof(app_data->thread_id));
348
349 unsigned long bytes_to_forward =
350 5 * sizeof(unsigned long) +
351 sizeof(shared_page) +
352 sizeof(unsigned long) +
353 app_data->heap_size;
354
355 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &bytes_to_forward,
356 sizeof(bytes_to_forward));
357 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_func_id, sizeof(app_func_id));
358 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg0, sizeof(arg0));
359 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg1, sizeof(arg1));
360 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg2, sizeof(arg2));
361 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg3, sizeof(arg3));
362
363 return app_run_internal(app_data, app_process_data);
364 }
365
app_resume(struct app_data_cfg * app_data)366 unsigned long app_resume(struct app_data_cfg *app_data)
367 {
368 struct app_process_data *app_process_data;
369
370 app_process_data = get_app_process_data(app_data->app_id);
371 if (app_process_data == NULL) {
372 exit(1);
373 }
374
375 /* This function should only be called if the EL0 app was yielded */
376 assert(app_data->exit_flag == APP_EXIT_SVC_YIELD_FLAG);
377
378 const unsigned long command = RUN_APP_INSTANCE;
379
380 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &command, sizeof(command));
381 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_data->thread_id,
382 sizeof(app_data->thread_id));
383
384 unsigned long bytes_to_forward =
385 sizeof(shared_page) +
386 sizeof(unsigned long) +
387 app_data->heap_size;
388
389 WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &bytes_to_forward,
390 sizeof(bytes_to_forward));
391
392 return app_run_internal(app_data, app_process_data);
393 }
394
app_abort(struct app_data_cfg * app_data)395 void app_abort(struct app_data_cfg *app_data)
396 {
397 (void)app_data;
398
399 /* TODO: Add implementation */
400 assert(false);
401 }
402
app_get_heap_ptr(struct app_data_cfg * app_data)403 void *app_get_heap_ptr(struct app_data_cfg *app_data)
404 {
405 return app_data->app_heap;
406 }
407
app_get_required_granule_count(unsigned long app_id)408 size_t app_get_required_granule_count(unsigned long app_id)
409 {
410 (void)app_id;
411 return 0U;
412 }
413
414 /* Used by the Mbed TLS library in case EL3 token signing is active when
415 * emulating EL3 signing.
416 */
mbedtls_psa_external_get_random(void * context,uint8_t * output,size_t output_size,size_t * output_length)417 int32_t mbedtls_psa_external_get_random(
418 void *context,
419 uint8_t *output, size_t output_size, size_t *output_length)
420 {
421 size_t i;
422
423 (void)context;
424
425 for (i = 0; i < output_size; ++i) {
426 /* Using pseudo-random generation as mbedtls library might
427 * return with error if not enough entropy.
428 */
429 output[i] = (uint8_t)(unsigned int)rand();
430 }
431
432 *output_length = output_size;
433
434 return 0;
435 }
436