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