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