1 /*
2 * Copyright (c) 2023 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/device.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/fs/fs.h>
11 #if defined(CONFIG_FILE_SYSTEM_LITTLEFS)
12 #include <zephyr/fs/littlefs.h>
13 #endif
14 #include <zephyr/llext/elf.h>
15 #include <zephyr/llext/llext.h>
16 #include <zephyr/llext/symbol.h>
17 #include <zephyr/llext/buf_loader.h>
18 #include <zephyr/llext/fs_loader.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/storage/flash_map.h>
21 #include <zephyr/sys/libc-hooks.h>
22 #include "syscalls_ext.h"
23 #include "threads_kernel_objects_ext.h"
24
25
26 LOG_MODULE_REGISTER(test_llext);
27
28
29 #ifdef CONFIG_LLEXT_STORAGE_WRITABLE
30 #define LLEXT_CONST
31 #else
32 #define LLEXT_CONST const
33 #endif
34
35 #ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
36 #define LLEXT_FIND_BUILTIN_SYM(symbol_name) llext_find_sym(NULL, symbol_name ## _SLID)
37
38 #ifdef CONFIG_64BIT
39 #define printk_SLID ((const char *)0x87B3105268827052ull)
40 #define z_impl_ext_syscall_fail_SLID ((const char *)0xD58BC0E7C64CD965ull)
41 #else
42 #define printk_SLID ((const char *)0x87B31052ull)
43 #define z_impl_ext_syscall_fail_SLID ((const char *)0xD58BC0E7ull)
44 #endif
45 #else
46 #define LLEXT_FIND_BUILTIN_SYM(symbol_name) llext_find_sym(NULL, # symbol_name)
47 #endif
48
49 struct llext_test {
50 const char *name;
51
52 LLEXT_CONST uint8_t *buf;
53 size_t buf_len;
54
55 bool kernel_only;
56
57 /*
58 * Optional callbacks
59 */
60
61 /* Called in kernel context before each test starts */
62 void (*test_setup)(struct llext *ext, struct k_thread *llext_thread);
63
64 /* Called in kernel context after each test completes */
65 void (*test_cleanup)(struct llext *ext);
66 };
67
68
69 K_THREAD_STACK_DEFINE(llext_stack, 1024);
70 struct k_thread llext_thread;
71
72
73 /* syscalls test */
74
z_impl_ext_syscall_ok(int a)75 int z_impl_ext_syscall_ok(int a)
76 {
77 return a + 1;
78 }
79
80 #ifdef CONFIG_USERSPACE
z_vrfy_ext_syscall_ok(int a)81 static inline int z_vrfy_ext_syscall_ok(int a)
82 {
83 return z_impl_ext_syscall_ok(a);
84 }
85 #include <zephyr/syscalls/ext_syscall_ok_mrsh.c>
86 #endif /* CONFIG_USERSPACE */
87
88
89 /* threads kernel objects test */
90
91 /* For these to be accessible from user space, they must be top-level globals
92 * in the Zephyr image. Also, macros that add objects to special linker sections,
93 * such as K_THREAD_STACK_DEFINE, do not work properly from extensions code.
94 */
95 K_SEM_DEFINE(my_sem, 1, 1);
96 EXPORT_SYMBOL(my_sem);
97 struct k_thread my_thread;
98 EXPORT_SYMBOL(my_thread);
99 K_THREAD_STACK_DEFINE(my_thread_stack, MY_THREAD_STACK_SIZE);
100 EXPORT_SYMBOL(my_thread_stack);
101
102 #ifdef CONFIG_USERSPACE
103 /* Allow the test threads to access global objects.
104 * Note: Permissions on objects used in the test by this thread are initialized
105 * even in supervisor mode, so that user mode descendant threads can inherit
106 * these permissions.
107 */
threads_objects_test_setup(struct llext *,struct k_thread * llext_thread)108 static void threads_objects_test_setup(struct llext *, struct k_thread *llext_thread)
109 {
110 k_object_access_grant(&my_sem, llext_thread);
111 k_object_access_grant(&my_thread, llext_thread);
112 k_object_access_grant(&my_thread_stack, llext_thread);
113 #if DT_HAS_CHOSEN(zephyr_console) && DT_NODE_HAS_STATUS_OKAY(DT_CHOSEN(zephyr_console))
114 k_object_access_grant(DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), llext_thread);
115 #endif
116 }
117 #else
118 /* No need to set up permissions for supervisor mode */
119 #define threads_objects_test_setup NULL
120 #endif /* CONFIG_USERSPACE */
121
load_call_unload(const struct llext_test * test_case)122 void load_call_unload(const struct llext_test *test_case)
123 {
124 struct llext_buf_loader buf_loader =
125 LLEXT_BUF_LOADER(test_case->buf, test_case->buf_len);
126 struct llext_loader *loader = &buf_loader.loader;
127 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
128 struct llext *ext = NULL;
129
130 int res = llext_load(loader, test_case->name, &ext, &ldr_parm);
131
132 zassert_ok(res, "load should succeed");
133
134 void (*test_entry_fn)() = llext_find_sym(&ext->exp_tab, "test_entry");
135
136 zassert_not_null(test_entry_fn, "test_entry should be an exported symbol");
137
138 #ifdef CONFIG_USERSPACE
139 /*
140 * Due to the number of MPU regions on some parts with MPU (USERSPACE)
141 * enabled we need to always call into the extension from a new dedicated
142 * thread to avoid running out of MPU regions on some parts.
143 *
144 * This is part dependent behavior and certainly on MMU capable parts
145 * this should not be needed! This test however is here to be generic
146 * across as many parts as possible.
147 */
148 struct k_mem_domain domain;
149
150 k_mem_domain_init(&domain, 0, NULL);
151
152 #ifdef Z_LIBC_PARTITION_EXISTS
153 k_mem_domain_add_partition(&domain, &z_libc_partition);
154 #endif
155
156 res = llext_add_domain(ext, &domain);
157 if (res == -ENOSPC) {
158 TC_PRINT("Too many memory partitions for this particular hardware\n");
159 ztest_test_skip();
160 return;
161 }
162 zassert_ok(res, "adding partitions to domain should succeed");
163
164 /* Should be runnable from newly created thread */
165 k_thread_create(&llext_thread, llext_stack,
166 K_THREAD_STACK_SIZEOF(llext_stack),
167 (k_thread_entry_t) &llext_bootstrap,
168 ext, test_entry_fn, NULL,
169 1, 0, K_FOREVER);
170
171 k_mem_domain_add_thread(&domain, &llext_thread);
172
173 if (test_case->test_setup) {
174 test_case->test_setup(ext, &llext_thread);
175 }
176
177 k_thread_start(&llext_thread);
178 k_thread_join(&llext_thread, K_FOREVER);
179
180 if (test_case->test_cleanup) {
181 test_case->test_cleanup(ext);
182 }
183
184 /* Some extensions may wish to be tried from the context
185 * of a userspace thread along with the usual supervisor context
186 * tried above.
187 */
188 if (!test_case->kernel_only) {
189 k_thread_create(&llext_thread, llext_stack,
190 K_THREAD_STACK_SIZEOF(llext_stack),
191 (k_thread_entry_t) &llext_bootstrap,
192 ext, test_entry_fn, NULL,
193 1, K_USER, K_FOREVER);
194
195 k_mem_domain_add_thread(&domain, &llext_thread);
196
197 if (test_case->test_setup) {
198 test_case->test_setup(ext, &llext_thread);
199 }
200
201 k_thread_start(&llext_thread);
202 k_thread_join(&llext_thread, K_FOREVER);
203
204 if (test_case->test_cleanup) {
205 test_case->test_cleanup(ext);
206 }
207 }
208
209 #else /* CONFIG_USERSPACE */
210 /* No userspace support: run the test only in supervisor mode, without
211 * creating a new thread.
212 */
213 if (test_case->test_setup) {
214 test_case->test_setup(ext, NULL);
215 }
216
217 #ifdef CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
218 /* The ELF specification forbids shared libraries from defining init
219 * entries, so calling llext_bootstrap here would be redundant. Use
220 * this opportunity to test llext_call_fn, even though llext_bootstrap
221 * would have behaved simlarly.
222 */
223 zassert_ok(llext_call_fn(ext, "test_entry"),
224 "test_entry call should succeed");
225 #else /* !USERSPACE && !SHAREDLIB */
226 llext_bootstrap(ext, test_entry_fn, NULL);
227 #endif
228
229 if (test_case->test_cleanup) {
230 test_case->test_cleanup(ext);
231 }
232 #endif /* CONFIG_USERSPACE */
233
234 llext_unload(&ext);
235 }
236
237 /*
238 * Attempt to load, list, list symbols, call a fn, and unload each
239 * extension in the test table. This excercises loading, calling into, and
240 * unloading each extension which may itself excercise various APIs provided by
241 * Zephyr.
242 */
243 #define LLEXT_LOAD_UNLOAD(_name, extra_args...) \
244 ZTEST(llext, test_load_unload_##_name) \
245 { \
246 const struct llext_test test_case = { \
247 .name = STRINGIFY(_name), \
248 .buf = _name ## _ext, \
249 .buf_len = sizeof(_name ## _ext), \
250 extra_args \
251 }; \
252 load_call_unload(&test_case); \
253 }
254
255 /*
256 * ELF file should be aligned to at least sizeof(elf_word) to avoid issues. A
257 * larger value eases debugging, since it reduces the differences in addresses
258 * between similar runs.
259 */
260 #define ELF_ALIGN __aligned(4096)
261
262 static LLEXT_CONST uint8_t hello_world_ext[] ELF_ALIGN = {
263 #include "hello_world.inc"
264 };
265 LLEXT_LOAD_UNLOAD(hello_world,
266 .kernel_only = true
267 )
268
269 #ifndef CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
270 static LLEXT_CONST uint8_t init_fini_ext[] ELF_ALIGN = {
271 #include "init_fini.inc"
272 };
273
init_fini_test_cleanup(struct llext * ext)274 static void init_fini_test_cleanup(struct llext *ext)
275 {
276 /* Make sure fini_fn() was called during teardown.
277 * (see init_fini_ext.c for more details).
278 */
279 const int *number = llext_find_sym(&ext->exp_tab, "number");
280 const int expected = (((((1 << 4) | 2) << 4) | 3) << 4) | 4; /* 0x1234 */
281
282 zassert_not_null(number, "number should be an exported symbol");
283 zassert_equal(*number, expected, "got 0x%x instead of 0x%x during cleanup",
284 *number, expected);
285 }
286
287 LLEXT_LOAD_UNLOAD(init_fini,
288 .test_cleanup = init_fini_test_cleanup
289 )
290 #endif
291
292 static LLEXT_CONST uint8_t logging_ext[] ELF_ALIGN = {
293 #include "logging.inc"
294 };
295 LLEXT_LOAD_UNLOAD(logging)
296
297 static LLEXT_CONST uint8_t relative_jump_ext[] ELF_ALIGN = {
298 #include "relative_jump.inc"
299 };
300 LLEXT_LOAD_UNLOAD(relative_jump)
301
302 static LLEXT_CONST uint8_t object_ext[] ELF_ALIGN = {
303 #include "object.inc"
304 };
305 LLEXT_LOAD_UNLOAD(object)
306
307 static LLEXT_CONST uint8_t syscalls_ext[] ELF_ALIGN = {
308 #include "syscalls.inc"
309 };
310 LLEXT_LOAD_UNLOAD(syscalls)
311
312 static LLEXT_CONST uint8_t threads_kernel_objects_ext[] ELF_ALIGN = {
313 #include "threads_kernel_objects.inc"
314 };
315 LLEXT_LOAD_UNLOAD(threads_kernel_objects,
316 .test_setup = threads_objects_test_setup,
317 )
318
319 #ifndef CONFIG_LLEXT_TYPE_ELF_OBJECT
320 static LLEXT_CONST uint8_t multi_file_ext[] ELF_ALIGN = {
321 #include "multi_file.inc"
322 };
323 LLEXT_LOAD_UNLOAD(multi_file)
324 #endif
325
326 #ifndef CONFIG_USERSPACE
327 static LLEXT_CONST uint8_t export_dependent_ext[] ELF_ALIGN = {
328 #include "export_dependent.inc"
329 };
330
331 static LLEXT_CONST uint8_t export_dependency_ext[] ELF_ALIGN = {
332 #include "export_dependency.inc"
333 };
334
ZTEST(llext,test_inter_ext)335 ZTEST(llext, test_inter_ext)
336 {
337 const void *dependency_buf = export_dependency_ext;
338 const void *dependent_buf = export_dependent_ext;
339 struct llext_buf_loader buf_loader_dependency =
340 LLEXT_BUF_LOADER(dependency_buf, sizeof(hello_world_ext));
341 struct llext_buf_loader buf_loader_dependent =
342 LLEXT_BUF_LOADER(dependent_buf, sizeof(export_dependent_ext));
343 struct llext_loader *loader_dependency = &buf_loader_dependency.loader;
344 struct llext_loader *loader_dependent = &buf_loader_dependent.loader;
345 const struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
346 struct llext *ext_dependency = NULL, *ext_dependent = NULL;
347 int ret = llext_load(loader_dependency, "inter_ext_dependency", &ext_dependency, &ldr_parm);
348
349 zassert_ok(ret, "dependency load should succeed");
350
351 ret = llext_load(loader_dependent, "export_dependent", &ext_dependent, &ldr_parm);
352
353 zassert_ok(ret, "dependent load should succeed");
354
355 int (*test_entry_fn)() = llext_find_sym(&ext_dependent->exp_tab, "test_entry");
356
357 zassert_not_null(test_entry_fn, "test_entry should be an exported symbol");
358 test_entry_fn();
359
360 llext_unload(&ext_dependent);
361 llext_unload(&ext_dependency);
362 }
363 #endif
364
365 #if defined(CONFIG_LLEXT_TYPE_ELF_RELOCATABLE) && defined(CONFIG_XTENSA)
366 static LLEXT_CONST uint8_t pre_located_ext[] ELF_ALIGN = {
367 #include "pre_located.inc"
368 };
369
ZTEST(llext,test_pre_located)370 ZTEST(llext, test_pre_located)
371 {
372 struct llext_buf_loader buf_loader =
373 LLEXT_BUF_LOADER(pre_located_ext, sizeof(pre_located_ext));
374 struct llext_loader *loader = &buf_loader.loader;
375 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
376 struct llext *ext = NULL;
377 const void *test_entry_fn;
378 int res;
379
380 /* load the extension trying to respect the addresses in the ELF */
381 ldr_parm.pre_located = true;
382 res = llext_load(loader, "pre_located", &ext, &ldr_parm);
383 zassert_ok(res, "load should succeed");
384
385 /* check the function address is the expected one */
386 test_entry_fn = llext_find_sym(&ext->exp_tab, "test_entry");
387 zassert_equal(test_entry_fn, (void *)0xbada110c, "test_entry should be at 0xbada110c");
388
389 llext_unload(&ext);
390 }
391 #endif
392
393 #if defined(CONFIG_LLEXT_STORAGE_WRITABLE)
394 static LLEXT_CONST uint8_t find_section_ext[] ELF_ALIGN = {
395 #include "find_section.inc"
396 };
397
ZTEST(llext,test_find_section)398 ZTEST(llext, test_find_section)
399 {
400 /* This test exploits the fact that in the STORAGE_WRITABLE cases, the
401 * symbol addresses calculated by llext will be directly inside the ELF
402 * file buffer, so the two methods can be easily compared.
403 */
404
405 int res;
406 ssize_t section_ofs;
407
408 struct llext_buf_loader buf_loader =
409 LLEXT_BUF_LOADER(find_section_ext, sizeof(find_section_ext));
410 struct llext_loader *loader = &buf_loader.loader;
411 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
412 struct llext *ext = NULL;
413 elf_shdr_t shdr;
414
415 res = llext_load(loader, "find_section", &ext, &ldr_parm);
416 zassert_ok(res, "load should succeed");
417
418 section_ofs = llext_find_section(loader, ".data");
419 zassert_true(section_ofs > 0, "find_section returned %zd", section_ofs);
420
421 res = llext_get_section_header(loader, ext, ".data", &shdr);
422 zassert_ok(res, "get_section_header() should succeed");
423 zassert_equal(shdr.sh_offset, section_ofs,
424 "different section offset %zd from get_section_header", shdr.sh_offset);
425
426 uintptr_t symbol_ptr = (uintptr_t)llext_find_sym(&ext->exp_tab, "number");
427 uintptr_t section_ptr = (uintptr_t)find_section_ext + section_ofs;
428
429 /*
430 * FIXME on RISC-V, at least for GCC, the symbols aren't always at the beginning
431 * of the section when CONFIG_LLEXT_TYPE_ELF_OBJECT is used, breaking this assertion.
432 * Currently, CONFIG_LLEXT_TYPE_ELF_OBJECT is not supported on RISC-V.
433 */
434
435 zassert_equal(symbol_ptr, section_ptr,
436 "symbol at %p != .data section at %p (%zd bytes in the ELF)",
437 symbol_ptr, section_ptr, section_ofs);
438
439 llext_unload(&ext);
440 }
441
442 static LLEXT_CONST uint8_t test_detached_ext[] ELF_ALIGN = {
443 #include "detached_fn.inc"
444 };
445
446 static struct llext_loader *detached_loader;
447 static struct llext *detached_llext;
448 static elf_shdr_t detached_shdr;
449
test_section_detached(const elf_shdr_t * shdr)450 static bool test_section_detached(const elf_shdr_t *shdr)
451 {
452 if (!detached_shdr.sh_addr) {
453 int res = llext_get_section_header(detached_loader, detached_llext,
454 ".detach", &detached_shdr);
455
456 zassert_ok(res, "get_section_header should succeed");
457 }
458
459 return shdr->sh_name == detached_shdr.sh_name;
460 }
461
ZTEST(llext,test_detached)462 ZTEST(llext, test_detached)
463 {
464 struct llext_buf_loader buf_loader =
465 LLEXT_BUF_LOADER(test_detached_ext, sizeof(test_detached_ext));
466 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
467 int res;
468
469 ldr_parm.section_detached = test_section_detached;
470 detached_loader = &buf_loader.loader;
471
472 res = llext_load(detached_loader, "test_detached", &detached_llext, &ldr_parm);
473 zassert_ok(res, "load should succeed");
474
475 /*
476 * Verify that the detached section is outside of the .text region.
477 * This only works with the shared ELF type, because with other types
478 * section addresses aren't "real," e.g. they can be 0.
479 */
480 elf_shdr_t *text_region = detached_loader->sects + LLEXT_MEM_TEXT;
481
482 zassert_true(text_region->sh_offset >= detached_shdr.sh_offset + detached_shdr.sh_size ||
483 detached_shdr.sh_offset >= text_region->sh_offset + text_region->sh_size);
484
485 void (*test_entry_fn)() = llext_find_sym(&detached_llext->exp_tab, "test_entry");
486
487 zassert_not_null(test_entry_fn, "test_entry should be an exported symbol");
488 test_entry_fn();
489
490 test_entry_fn = llext_find_sym(&detached_llext->exp_tab, "detached_entry");
491
492 zassert_not_null(test_entry_fn, "detached_entry should be an exported symbol");
493 test_entry_fn();
494
495 llext_unload(&detached_llext);
496 }
497 #endif
498
499 #if defined(CONFIG_FILE_SYSTEM)
500 #define LLEXT_FILE "hello_world.llext"
501
502 FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
503 static struct fs_mount_t mp = {
504 .type = FS_LITTLEFS,
505 .fs_data = &storage,
506 .storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
507 .mnt_point = "/lfs",
508 };
509
ZTEST(llext,test_fs_loader)510 ZTEST(llext, test_fs_loader)
511 {
512 int res;
513 char path[UINT8_MAX];
514 struct fs_file_t fd;
515
516 /* File system should be mounted before the testcase. If not mount it now. */
517 if (!(mp.flags & FS_MOUNT_FLAG_AUTOMOUNT)) {
518 zassert_ok(fs_mount(&mp), "Filesystem should be mounted");
519 }
520
521 snprintf(path, sizeof(path), "%s/%s", mp.mnt_point, LLEXT_FILE);
522 fs_file_t_init(&fd);
523
524 zassert_ok(fs_open(&fd, path, FS_O_CREATE | FS_O_TRUNC | FS_O_WRITE),
525 "Failed opening file");
526
527 zassert_equal(fs_write(&fd, hello_world_ext, ARRAY_SIZE(hello_world_ext)),
528 ARRAY_SIZE(hello_world_ext),
529 "Full content of the buffer holding ext should be written");
530
531 zassert_ok(fs_close(&fd), "Failed closing file");
532
533 struct llext_fs_loader fs_loader = LLEXT_FS_LOADER(path);
534 struct llext_loader *loader = &fs_loader.loader;
535 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
536 struct llext *ext = NULL;
537
538 res = llext_load(loader, "hello_world", &ext, &ldr_parm);
539 zassert_ok(res, "load should succeed");
540
541 void (*test_entry_fn)() = llext_find_sym(&ext->exp_tab, "test_entry");
542
543 zassert_not_null(test_entry_fn, "test_entry should be an exported symbol");
544
545 llext_unload(&ext);
546 fs_unmount(&mp);
547 }
548 #endif
549
550 /*
551 * Ensure that EXPORT_SYMBOL does indeed provide a symbol and a valid address
552 * to it.
553 */
ZTEST(llext,test_printk_exported)554 ZTEST(llext, test_printk_exported)
555 {
556 const void * const printk_fn = LLEXT_FIND_BUILTIN_SYM(printk);
557
558 zassert_equal(printk_fn, printk, "printk should be an exported symbol");
559 }
560
561 /*
562 * The syscalls test above verifies that custom syscalls defined by extensions
563 * are properly exported. Since `ext_syscalls.h` declares ext_syscall_fail, we
564 * know it is picked up by the syscall build machinery, but the implementation
565 * for it is missing. Make sure the exported symbol for it is NULL.
566 */
ZTEST(llext,test_ext_syscall_fail)567 ZTEST(llext, test_ext_syscall_fail)
568 {
569 const void * const esf_fn = LLEXT_FIND_BUILTIN_SYM(z_impl_ext_syscall_fail);
570
571 zassert_is_null(esf_fn, "est_fn should be NULL");
572 }
573
574 ZTEST_SUITE(llext, NULL, NULL, NULL, NULL, NULL);
575