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