1 /*
2  * Copyright (c) 2019 Intel Corporation.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/kernel.h>
7 #include <zephyr/device.h>
8 #include <zephyr/sys/libc-hooks.h>
9 #include <zephyr/logging/log.h>
10 
11 #include "app_shared.h"
12 #include "app_b.h"
13 
14 LOG_MODULE_REGISTER(app_b);
15 
16 /* Resource pool for allocations made by the kernel on behalf of system
17  * calls. Needed for k_queue_alloc_append()
18  */
19 K_HEAP_DEFINE(app_b_resource_pool, 256 * 4 + 128);
20 
21 /* Define app_b_partition, where all globals for this app will be routed.
22  * The partition starting address and size are populated by build system
23  * and linker magic.
24  */
25 K_APPMEM_PARTITION_DEFINE(app_b_partition);
26 
27 /* Global data used by application B. By tagging with APP_B_BSS or APP_B_DATA,
28  * we ensure all this gets linked into the continuous region denoted by
29  * app_b_partition.
30  *
31  * This is just for demonstration purposes, processor_thread could just as
32  * easily put this on its stack.
33  */
34 APP_B_BSS unsigned int process_count;
35 
processor_thread(void * p1,void * p2,void * p3)36 static void processor_thread(void *p1, void *p2, void *p3)
37 {
38 	void *payload;
39 
40 	ARG_UNUSED(p1);
41 	ARG_UNUSED(p2);
42 	ARG_UNUSED(p3);
43 
44 	LOG_DBG("processor thread entered");
45 
46 	/* Pretend that processor_thread takes some initialization time,
47 	 * meanwhile data coming in from the driver will be buffered in the
48 	 * incoming queue/
49 	 */
50 	k_sleep(K_MSEC(400));
51 
52 	/* Consume data blobs from shared_queue_incoming.
53 	 * Do some processing, and the put the processed data
54 	 * into shared_queue_outgoing.
55 	 */
56 	while (process_count < NUM_LOOPS) {
57 		payload = k_queue_get(&shared_queue_incoming, K_FOREVER);
58 
59 		/* pretend we're doing something complicated and useful
60 		 * to the data, which is untrusted and hence processed in
61 		 * a sandboxed App B
62 		 */
63 		LOG_DBG("processing payload #%d", process_count);
64 		k_busy_wait(100000);
65 		process_count++;
66 		LOG_INF("processing payload #%d complete", process_count);
67 
68 		/* Stick the now-processed data into the outgoing queue,
69 		 * to be handled by App A's writeback thread.
70 		 */
71 		k_queue_alloc_append(&shared_queue_outgoing, payload);
72 	}
73 
74 	LOG_DBG("processor thread exiting");
75 }
76 
app_b_entry(void * p1,void * p2,void * p3)77 void app_b_entry(void *p1, void *p2, void *p3)
78 {
79 	int ret;
80 
81 	/* Much like how we are reusing the main thread as this application's
82 	 * processor thread, we will re-use the default memory domain as the
83 	 * domain for application B.
84 	 */
85 	ret = k_mem_domain_add_partition(&k_mem_domain_default,
86 					 &app_b_partition);
87 	if (ret != 0) {
88 		LOG_ERR("Failed to add app_b_partition to mem domain (%d)",
89 			ret);
90 		k_oops();
91 	}
92 
93 	ret = k_mem_domain_add_partition(&k_mem_domain_default,
94 					 &shared_partition);
95 	if (ret != 0) {
96 		LOG_ERR("Failed to add shared_partition to mem domain (%d)",
97 			ret);
98 		k_oops();
99 	}
100 
101 	/* Assign a resource pool to serve for kernel-side allocations on
102 	 * behalf of application A. Needed for k_queue_alloc_append().
103 	 */
104 	k_thread_heap_assign(k_current_get(), &app_b_resource_pool);
105 
106 	/* We are about to drop to user mode and become the monitor thread.
107 	 * Grant ourselves access to the kernel objects we need for
108 	 * the monitor thread to function.
109 	 *
110 	 * In this case, we need access to both shared queue objects. We
111 	 * don't need access to the sample driver, App A handles all that
112 	 * for us.
113 	 */
114 	k_thread_access_grant(k_current_get(), &shared_queue_incoming,
115 			      &shared_queue_outgoing);
116 
117 	k_thread_user_mode_enter(processor_thread, NULL, NULL, NULL);
118 }
119