1 /*
2  * Copyright (c) 2024 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/app_memory/mem_domain.h>
9 #include <zephyr/llext/llext.h>
10 #include <zephyr/llext/buf_loader.h>
11 
12 #include <app_api.h>
13 
14 #include <string.h>
15 
16 /**
17  * Assume that if the extension 1 is not built, we are building the
18  * EDK. If others are not built, this will just fail.
19  */
20 #if defined __has_include
21 #  if __has_include("../../ext1/build/ext1.inc")
22 #    undef EDK_BUILD
23 #  else
24 #    pragma message "Extension 1 not built, assuming EDK build."
25 #    define EDK_BUILD
26 #  endif
27 #endif
28 
29 #ifndef EDK_BUILD
30 #include "../../ext1/build/ext1.inc"
31 #define ext1_inc ext1_llext
32 #define ext1_len ext1_llext_len
33 #include "../../ext2/build/ext2.inc"
34 #define ext2_inc ext2_llext
35 #define ext2_len ext2_llext_len
36 #include "../../ext3/build/ext3.inc"
37 #define ext3_inc ext3_llext
38 #define ext3_len ext3_llext_len
39 #ifndef CONFIG_LLEXT_EDK_USERSPACE_ONLY
40 #include "../../k-ext1/build/kext1.inc"
41 #define kext1_inc kext1_llext
42 #define kext1_len kext1_llext_len
43 #endif
44 #endif
45 
46 #define USER_STACKSIZE  2048
47 #define USER_HEAPSIZE  8192
48 #define MAX_EXTENSIONS 4
49 
50 extern k_tid_t start_subscriber_thread(void);
51 
52 /* Maybe make all this depend on MAX_EXTENSIONS? */
53 struct k_thread user_thread1, user_thread2, user_thread3, kernel_thread1;
54 K_THREAD_STACK_DEFINE(user_stack1, USER_STACKSIZE);
55 K_THREAD_STACK_DEFINE(user_stack2, USER_STACKSIZE);
56 K_THREAD_STACK_DEFINE(user_stack3, USER_STACKSIZE);
57 K_THREAD_STACK_DEFINE(kernel_stack1, USER_STACKSIZE);
58 
59 K_HEAP_DEFINE(user_heap1, USER_HEAPSIZE);
60 K_HEAP_DEFINE(user_heap2, USER_HEAPSIZE);
61 K_HEAP_DEFINE(user_heap3, USER_HEAPSIZE);
62 K_HEAP_DEFINE(kernel_heap1, USER_HEAPSIZE);
63 
64 struct {
65 	k_tid_t thread;
66 	struct llext *ext;
67 } extension_threads[MAX_EXTENSIONS];
68 int max_extension_thread_idx;
69 
load(const char * name,struct llext ** ext,void * buf,size_t len)70 static const void * const load(const char *name, struct llext **ext, void *buf,
71 			       size_t len)
72 {
73 #ifndef EDK_BUILD
74 	struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(buf, len);
75 	struct llext_loader *loader = &buf_loader.loader;
76 	struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
77 
78 	llext_load(loader, name, ext, &ldr_parm);
79 
80 	return llext_find_sym(&(*ext)->exp_tab, "start");
81 #else
82 	return NULL;
83 #endif
84 }
85 
unload(struct llext ** ext)86 static void unload(struct llext **ext)
87 {
88 	llext_unload(ext);
89 }
90 
user_function(void * p1,void * p2,void * p3)91 static void user_function(void *p1, void *p2, void *p3)
92 {
93 	int (*start_fn)(void) = p1;
94 
95 	printk("[app]Thread %p created to run extension [%s], at %s\n",
96 	       k_current_get(), (char *)p2,
97 	       k_is_user_context() ? "userspace." : "privileged mode.");
98 
99 	start_fn();
100 	printk("[app]Thread %p done\n", k_current_get());
101 }
102 
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * esf)103 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *esf)
104 {
105 	int i;
106 
107 	printk("[app]Fatal handler! Thread: %p\n", k_current_get());
108 
109 	for (i = 0; i < max_extension_thread_idx; i++) {
110 		if (extension_threads[i].thread == k_current_get()) {
111 			unload(&extension_threads[i].ext);
112 		}
113 	}
114 }
115 
run_extension_on_thread(void * ext_inc,size_t ext_len,struct k_mem_domain * domain,struct k_thread * thread,k_thread_stack_t * stack,struct k_heap * heap,const char * name,k_tid_t subscriber_thread_id,int flag)116 void run_extension_on_thread(void *ext_inc, size_t ext_len,
117 			     struct k_mem_domain *domain,
118 			     struct k_thread *thread,
119 			     k_thread_stack_t *stack,
120 			     struct k_heap *heap,
121 			     const char *name,
122 			     k_tid_t subscriber_thread_id,
123 			     int flag)
124 {
125 	int (*start_fn)(void);
126 	struct llext **ext = &extension_threads[max_extension_thread_idx].ext;
127 
128 	printk("[app]Loading extension [%s].\n", name);
129 	start_fn = load(name, ext, ext_inc, ext_len);
130 
131 	llext_add_domain(*ext, domain);
132 
133 	k_thread_create(thread, stack, USER_STACKSIZE,
134 			user_function, start_fn, (void *)name, NULL,
135 			-1, flag | K_INHERIT_PERMS, K_FOREVER);
136 	k_mem_domain_add_thread(domain, thread);
137 	k_mem_domain_add_thread(domain, subscriber_thread_id);
138 
139 	k_thread_heap_assign(thread, heap);
140 
141 	extension_threads[max_extension_thread_idx].thread = thread;
142 	max_extension_thread_idx++;
143 
144 	k_thread_start(thread);
145 }
146 
main(void)147 int main(void)
148 {
149 	struct k_mem_domain domain1, domain2, domain3, kdomain1;
150 
151 #ifndef EDK_BUILD
152 	k_tid_t subscriber_thread_id = start_subscriber_thread();
153 #endif
154 	/* This and all other similar sleeps are here to provide a chance for
155 	 * the newly created thread to run.
156 	 */
157 	k_sleep(K_MSEC(1));
158 
159 	k_mem_domain_init(&domain1, 0, NULL);
160 	k_mem_domain_init(&domain2, 0, NULL);
161 	k_mem_domain_init(&domain3, 0, NULL);
162 	k_mem_domain_init(&kdomain1, 0, NULL);
163 
164 #ifndef EDK_BUILD
165 #ifndef CONFIG_LLEXT_EDK_USERSPACE_ONLY
166 	run_extension_on_thread(kext1_inc, kext1_len, &kdomain1,
167 				&kernel_thread1, kernel_stack1, &kernel_heap1,
168 				"kext1", subscriber_thread_id, 0);
169 	k_sleep(K_MSEC(1));
170 #endif
171 	run_extension_on_thread(ext1_inc, ext1_len, &domain1, &user_thread1,
172 				user_stack1, &user_heap1, "ext1",
173 				subscriber_thread_id, K_USER);
174 	k_sleep(K_MSEC(1));
175 	run_extension_on_thread(ext2_inc, ext2_len, &domain2, &user_thread2,
176 				user_stack2, &user_heap2, "ext2",
177 				subscriber_thread_id, K_USER);
178 	k_sleep(K_MSEC(1));
179 	run_extension_on_thread(ext3_inc, ext3_len, &domain3, &user_thread3,
180 				user_stack3, &user_heap3, "ext3",
181 				subscriber_thread_id, K_USER);
182 #endif
183 
184 	k_sleep(K_FOREVER);
185 
186 	return 0;
187 }
188