llext: add threads and kernel object test case
Tests threads and kernel objects with userspace. Includes cleanups suggested by Luca Burelli. Signed-off-by: Lauren Murphy <lauren.murphy@intel.com>
This commit is contained in:
parent
83d879bb1a
commit
83ccc8e29b
|
@ -17,7 +17,7 @@ target_include_directories(app PRIVATE
|
||||||
|
|
||||||
if(NOT LOADER_BUILD_ONLY)
|
if(NOT LOADER_BUILD_ONLY)
|
||||||
# generate extension targets foreach extension given by name
|
# generate extension targets foreach extension given by name
|
||||||
foreach(ext_name hello_world logging relative_jump object syscalls)
|
foreach(ext_name hello_world logging relative_jump object syscalls threads_kernel_objects)
|
||||||
set(ext_src ${PROJECT_SOURCE_DIR}/src/${ext_name}_ext.c)
|
set(ext_src ${PROJECT_SOURCE_DIR}/src/${ext_name}_ext.c)
|
||||||
set(ext_bin ${ZEPHYR_BINARY_DIR}/${ext_name}.llext)
|
set(ext_bin ${ZEPHYR_BINARY_DIR}/${ext_name}.llext)
|
||||||
set(ext_inc ${ZEPHYR_BINARY_DIR}/include/generated/${ext_name}.inc)
|
set(ext_inc ${ZEPHYR_BINARY_DIR}/include/generated/${ext_name}.inc)
|
||||||
|
|
|
@ -7,11 +7,13 @@
|
||||||
#include <zephyr/ztest.h>
|
#include <zephyr/ztest.h>
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/llext/llext.h>
|
#include <zephyr/llext/llext.h>
|
||||||
|
#include <zephyr/llext/symbol.h>
|
||||||
#include <zephyr/llext/buf_loader.h>
|
#include <zephyr/llext/buf_loader.h>
|
||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
#include <zephyr/sys/libc-hooks.h>
|
#include <zephyr/sys/libc-hooks.h>
|
||||||
|
|
||||||
#include "syscalls_ext.h"
|
#include "syscalls_ext.h"
|
||||||
|
#include "threads_kernel_objects_ext.h"
|
||||||
|
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(test_llext_simple);
|
LOG_MODULE_REGISTER(test_llext_simple);
|
||||||
|
|
||||||
|
@ -28,8 +30,9 @@ struct llext_test {
|
||||||
size_t buf_len;
|
size_t buf_len;
|
||||||
|
|
||||||
LLEXT_CONST uint8_t *buf;
|
LLEXT_CONST uint8_t *buf;
|
||||||
};
|
|
||||||
|
|
||||||
|
void (*perm_setup)(struct k_thread *llext_thread);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
K_THREAD_STACK_DEFINE(llext_stack, 1024);
|
K_THREAD_STACK_DEFINE(llext_stack, 1024);
|
||||||
|
@ -45,6 +48,9 @@ void llext_entry(void *arg0, void *arg1, void *arg2)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_USERSPACE */
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
|
||||||
|
|
||||||
|
/* syscalls test */
|
||||||
|
|
||||||
int z_impl_ext_syscall_ok(int a)
|
int z_impl_ext_syscall_ok(int a)
|
||||||
{
|
{
|
||||||
return a + 1;
|
return a + 1;
|
||||||
|
@ -58,6 +64,33 @@ static inline int z_vrfy_ext_syscall_ok(int a)
|
||||||
#include <syscalls/ext_syscall_ok_mrsh.c>
|
#include <syscalls/ext_syscall_ok_mrsh.c>
|
||||||
#endif /* CONFIG_USERSPACE */
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
|
||||||
|
|
||||||
|
/* threads kernel objects test */
|
||||||
|
|
||||||
|
/* For these to be accessible from user space, they must be top-level globals
|
||||||
|
* in the Zephyr image. Also, macros that add objects to special linker sections,
|
||||||
|
* such as K_THREAD_STACK_DEFINE, do not work properly from extensions code.
|
||||||
|
*/
|
||||||
|
K_SEM_DEFINE(my_sem, 1, 1);
|
||||||
|
EXPORT_SYMBOL(my_sem);
|
||||||
|
struct k_thread my_thread;
|
||||||
|
EXPORT_SYMBOL(my_thread);
|
||||||
|
K_THREAD_STACK_DEFINE(my_thread_stack, MY_THREAD_STACK_SIZE);
|
||||||
|
EXPORT_SYMBOL(my_thread_stack);
|
||||||
|
|
||||||
|
#ifdef CONFIG_USERSPACE
|
||||||
|
/* Allow the user space test thread to access global objects */
|
||||||
|
static void threads_objects_perm_setup(struct k_thread *llext_thread)
|
||||||
|
{
|
||||||
|
k_object_access_grant(&my_sem, llext_thread);
|
||||||
|
k_object_access_grant(&my_thread, llext_thread);
|
||||||
|
k_object_access_grant(&my_thread_stack, llext_thread);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* No need to set up permissions for supervisor mode */
|
||||||
|
#define threads_objects_perm_setup NULL
|
||||||
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
|
||||||
void load_call_unload(struct llext_test *test_case)
|
void load_call_unload(struct llext_test *test_case)
|
||||||
{
|
{
|
||||||
struct llext_buf_loader buf_loader =
|
struct llext_buf_loader buf_loader =
|
||||||
|
@ -108,6 +141,14 @@ void load_call_unload(struct llext_test *test_case)
|
||||||
|
|
||||||
k_mem_domain_add_thread(&domain, &llext_thread);
|
k_mem_domain_add_thread(&domain, &llext_thread);
|
||||||
|
|
||||||
|
/* Even in supervisor mode, initialize permissions on objects used in
|
||||||
|
* the test by this thread, so that user mode descendant threads can
|
||||||
|
* inherit these permissions.
|
||||||
|
*/
|
||||||
|
if (test_case->perm_setup) {
|
||||||
|
test_case->perm_setup(&llext_thread);
|
||||||
|
}
|
||||||
|
|
||||||
k_thread_start(&llext_thread);
|
k_thread_start(&llext_thread);
|
||||||
k_thread_join(&llext_thread, K_FOREVER);
|
k_thread_join(&llext_thread, K_FOREVER);
|
||||||
|
|
||||||
|
@ -123,11 +164,14 @@ void load_call_unload(struct llext_test *test_case)
|
||||||
|
|
||||||
k_mem_domain_add_thread(&domain, &llext_thread);
|
k_mem_domain_add_thread(&domain, &llext_thread);
|
||||||
|
|
||||||
|
if (test_case->perm_setup) {
|
||||||
|
test_case->perm_setup(&llext_thread);
|
||||||
|
}
|
||||||
|
|
||||||
k_thread_start(&llext_thread);
|
k_thread_start(&llext_thread);
|
||||||
k_thread_join(&llext_thread, K_FOREVER);
|
k_thread_join(&llext_thread, K_FOREVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#else /* CONFIG_USERSPACE */
|
#else /* CONFIG_USERSPACE */
|
||||||
zassert_ok(llext_call_fn(ext, "test_entry"),
|
zassert_ok(llext_call_fn(ext, "test_entry"),
|
||||||
"test_entry call should succeed");
|
"test_entry call should succeed");
|
||||||
|
@ -143,7 +187,7 @@ void load_call_unload(struct llext_test *test_case)
|
||||||
* unloading each extension which may itself excercise various APIs provided by
|
* unloading each extension which may itself excercise various APIs provided by
|
||||||
* Zephyr.
|
* Zephyr.
|
||||||
*/
|
*/
|
||||||
#define LLEXT_LOAD_UNLOAD(_name, _userspace) \
|
#define LLEXT_LOAD_UNLOAD(_name, _userspace, _perm_setup) \
|
||||||
ZTEST(llext, test_load_unload_##_name) \
|
ZTEST(llext, test_load_unload_##_name) \
|
||||||
{ \
|
{ \
|
||||||
struct llext_test test_case = { \
|
struct llext_test test_case = { \
|
||||||
|
@ -151,35 +195,42 @@ void load_call_unload(struct llext_test *test_case)
|
||||||
.try_userspace = _userspace, \
|
.try_userspace = _userspace, \
|
||||||
.buf_len = ARRAY_SIZE(_name ## _ext), \
|
.buf_len = ARRAY_SIZE(_name ## _ext), \
|
||||||
.buf = _name ## _ext, \
|
.buf = _name ## _ext, \
|
||||||
|
.perm_setup = _perm_setup, \
|
||||||
}; \
|
}; \
|
||||||
load_call_unload(&test_case); \
|
load_call_unload(&test_case); \
|
||||||
}
|
}
|
||||||
static LLEXT_CONST uint8_t hello_world_ext[] __aligned(4) = {
|
static LLEXT_CONST uint8_t hello_world_ext[] __aligned(4) = {
|
||||||
#include "hello_world.inc"
|
#include "hello_world.inc"
|
||||||
};
|
};
|
||||||
LLEXT_LOAD_UNLOAD(hello_world, false)
|
LLEXT_LOAD_UNLOAD(hello_world, false, NULL)
|
||||||
|
|
||||||
static LLEXT_CONST uint8_t logging_ext[] __aligned(4) = {
|
static LLEXT_CONST uint8_t logging_ext[] __aligned(4) = {
|
||||||
#include "logging.inc"
|
#include "logging.inc"
|
||||||
};
|
};
|
||||||
LLEXT_LOAD_UNLOAD(logging, true)
|
LLEXT_LOAD_UNLOAD(logging, true, NULL)
|
||||||
|
|
||||||
static LLEXT_CONST uint8_t relative_jump_ext[] __aligned(4) = {
|
static LLEXT_CONST uint8_t relative_jump_ext[] __aligned(4) = {
|
||||||
#include "relative_jump.inc"
|
#include "relative_jump.inc"
|
||||||
};
|
};
|
||||||
LLEXT_LOAD_UNLOAD(relative_jump, true)
|
LLEXT_LOAD_UNLOAD(relative_jump, true, NULL)
|
||||||
|
|
||||||
static LLEXT_CONST uint8_t object_ext[] __aligned(4) = {
|
static LLEXT_CONST uint8_t object_ext[] __aligned(4) = {
|
||||||
#include "object.inc"
|
#include "object.inc"
|
||||||
};
|
};
|
||||||
LLEXT_LOAD_UNLOAD(object, true)
|
LLEXT_LOAD_UNLOAD(object, true, NULL)
|
||||||
|
|
||||||
static LLEXT_CONST uint8_t syscalls_ext[] __aligned(4) = {
|
static LLEXT_CONST uint8_t syscalls_ext[] __aligned(4) = {
|
||||||
#include "syscalls.inc"
|
#include "syscalls.inc"
|
||||||
};
|
};
|
||||||
LLEXT_LOAD_UNLOAD(syscalls, true)
|
LLEXT_LOAD_UNLOAD(syscalls, true, NULL)
|
||||||
|
|
||||||
|
static LLEXT_CONST uint8_t threads_kernel_objects_ext[] __aligned(4) = {
|
||||||
|
#include "threads_kernel_objects.inc"
|
||||||
|
};
|
||||||
|
LLEXT_LOAD_UNLOAD(threads_kernel_objects, true, threads_objects_perm_setup)
|
||||||
#endif /* ! LOADER_BUILD_ONLY */
|
#endif /* ! LOADER_BUILD_ONLY */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure that EXPORT_SYMBOL does indeed provide a symbol and a valid address
|
* Ensure that EXPORT_SYMBOL does indeed provide a symbol and a valid address
|
||||||
* to it.
|
* to it.
|
||||||
|
|
40
tests/subsys/llext/simple/src/threads_kernel_objects_ext.c
Normal file
40
tests/subsys/llext/simple/src/threads_kernel_objects_ext.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code demonstrates the use of threads and requires object
|
||||||
|
* relocation support.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <zephyr/llext/symbol.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include "threads_kernel_objects_ext.h"
|
||||||
|
|
||||||
|
void test_thread(void *arg0, void *arg1, void *arg2)
|
||||||
|
{
|
||||||
|
printk("Take semaphore from test thread\n");
|
||||||
|
k_sem_take(&my_sem, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_entry(void)
|
||||||
|
{
|
||||||
|
printk("Give semaphore from main thread\n");
|
||||||
|
k_sem_give(&my_sem);
|
||||||
|
|
||||||
|
printk("Creating thread\n");
|
||||||
|
k_tid_t tid = k_thread_create(&my_thread, (k_thread_stack_t *) &my_thread_stack,
|
||||||
|
MY_THREAD_STACK_SIZE, &test_thread, NULL, NULL, NULL,
|
||||||
|
MY_THREAD_PRIO, MY_THREAD_OPTIONS, K_FOREVER);
|
||||||
|
|
||||||
|
printk("Starting thread\n");
|
||||||
|
k_thread_start(tid);
|
||||||
|
|
||||||
|
printk("Joining thread\n");
|
||||||
|
k_thread_join(&my_thread, K_FOREVER);
|
||||||
|
printk("Test thread joined\n");
|
||||||
|
}
|
||||||
|
LL_EXTENSION_SYMBOL(test_entry);
|
22
tests/subsys/llext/simple/src/threads_kernel_objects_ext.h
Normal file
22
tests/subsys/llext/simple/src/threads_kernel_objects_ext.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/llext/symbol.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
extern struct k_thread my_thread;
|
||||||
|
#define MY_THREAD_STACK_SIZE 1024
|
||||||
|
extern struct z_thread_stack_element my_thread_stack[];
|
||||||
|
|
||||||
|
extern struct k_sem my_sem;
|
||||||
|
|
||||||
|
#ifdef CONFIG_USERSPACE
|
||||||
|
#define MY_THREAD_PRIO 1
|
||||||
|
#define MY_THREAD_OPTIONS (K_USER | K_INHERIT_PERMS)
|
||||||
|
#else
|
||||||
|
#define MY_THREAD_PRIO 0
|
||||||
|
#define MY_THREAD_OPTIONS 0
|
||||||
|
#endif
|
Loading…
Reference in a new issue