blob: 84b9805e76bb596d6b5870cee822229e0dede098 [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/irq_offload.h>
#include <zephyr/debug/stack.h>
#define STACKSIZE (256 + CONFIG_TEST_EXTRA_STACK_SIZE)
static K_THREAD_STACK_DEFINE(dyn_thread_stack, STACKSIZE);
static K_SEM_DEFINE(start_sem, 0, 1);
static K_SEM_DEFINE(end_sem, 0, 1);
static ZTEST_BMEM struct k_thread *dyn_thread;
static struct k_thread *dynamic_threads[CONFIG_MAX_THREAD_BYTES * 8];
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf)
{
if (reason != K_ERR_KERNEL_OOPS) {
printk("wrong error reason\n");
k_fatal_halt(reason);
}
if (k_current_get() != dyn_thread) {
printk("wrong thread crashed\n");
k_fatal_halt(reason);
}
}
static void dyn_thread_entry(void *p1, void *p2, void *p3)
{
k_sem_take(&start_sem, K_FOREVER);
k_sem_give(&end_sem);
}
static void prep(void)
{
k_thread_access_grant(k_current_get(), dyn_thread_stack,
&start_sem, &end_sem);
}
static void create_dynamic_thread(void)
{
k_tid_t tid;
dyn_thread = k_object_alloc(K_OBJ_THREAD);
zassert_not_null(dyn_thread, "Cannot allocate thread k_object!");
tid = k_thread_create(dyn_thread, dyn_thread_stack, STACKSIZE,
dyn_thread_entry, NULL, NULL, NULL,
K_PRIO_PREEMPT(0), K_USER, K_FOREVER);
k_object_access_grant(&start_sem, tid);
k_object_access_grant(&end_sem, tid);
k_thread_start(tid);
k_sem_give(&start_sem);
zassert_true(k_sem_take(&end_sem, K_SECONDS(1)) == 0,
"k_sem_take(end_sem) failed");
k_thread_abort(tid);
k_object_release(dyn_thread);
}
static void permission_test(void)
{
k_tid_t tid;
dyn_thread = k_object_alloc(K_OBJ_THREAD);
zassert_not_null(dyn_thread, "Cannot allocate thread k_object!");
tid = k_thread_create(dyn_thread, dyn_thread_stack, STACKSIZE,
dyn_thread_entry, NULL, NULL, NULL,
K_PRIO_PREEMPT(0), K_USER, K_FOREVER);
k_object_access_grant(&start_sem, tid);
k_thread_start(tid);
/*
* Notice dyn_thread will not have permission to access
* end_sem, which will cause kernel oops.
*/
k_sem_give(&start_sem);
/*
* If dyn_thread has permission to access end_sem,
* k_sem_take() would be able to take the semaphore.
*/
zassert_true(k_sem_take(&end_sem, K_SECONDS(1)) != 0,
"Semaphore end_sem has incorrect permission");
k_thread_abort(tid);
k_object_release(dyn_thread);
}
/**
* @ingroup kernel_thread_tests
* @brief Test object permission on dynamic user thread when index is reused
*
* @details This creates one dynamic thread with permissions to both
* semaphores so there is no fault. Then a new thread is created and will be
* re-using the thread index in first pass. Except the second thread does
* not have permission to one of the semaphore. If permissions are cleared
* correctly when thread is destroyed, the second should raise kernel oops.
*/
ZTEST(thread_dynamic, test_dyn_thread_perms)
{
if (!(IS_ENABLED(CONFIG_USERSPACE))) {
ztest_test_skip();
}
permission_test();
TC_PRINT("===== must have access denied on k_sem %p\n", &end_sem);
}
ZTEST(thread_dynamic, test_thread_index_management)
{
int i, ctr = 0;
/* Create thread objects until we run out of ids */
while (true) {
struct k_thread *t = k_object_alloc(K_OBJ_THREAD);
if (t == NULL) {
break;
}
dynamic_threads[ctr] = t;
ctr++;
}
zassert_true(ctr != 0, "unable to create any thread objects");
TC_PRINT("created %d thread objects\n", ctr);
/* Show that the above NULL return value wasn't because we ran out of
* heap space. For that we need to duplicate how objects are allocated
* in kernel/userspace.c. We pessimize the alignment to the worst
* case to simplify things somewhat.
*/
size_t ret = 1024 * 1024; /* sure-to-fail initial value */
void *blob;
switch (K_OBJ_THREAD) {
/** @cond keep_doxygen_away */
#include <otype-to-size.h>
/** @endcond */
}
blob = z_dynamic_object_aligned_create(16, ret);
zassert_true(blob != NULL, "out of heap memory");
/* Free one of the threads... */
k_object_free(dynamic_threads[0]);
/* And show that we can now create another one, the freed thread's
* index should have been garbage collected.
*/
dynamic_threads[0] = k_object_alloc(K_OBJ_THREAD);
zassert_true(dynamic_threads[0] != NULL,
"couldn't create thread object\n");
/* TODO: Implement a test that shows that thread IDs are properly
* recycled when a thread object is garbage collected due to references
* dropping to zero. For example, we ought to be able to exit here
* without calling k_object_free() on any of the threads we created
* here; their references would drop to zero and they would be
* automatically freed. However, it is known that the thread IDs are
* not properly recycled when this happens, see #17023.
*/
for (i = 0; i < ctr; i++) {
k_object_free(dynamic_threads[i]);
}
}
/**
* @ingroup kernel_thread_tests
* @brief Test creation of dynamic user thread under kernel thread
*
* @details This is a simple test to create a user thread
* dynamically via k_object_alloc() under a kernel thread.
*/
ZTEST(thread_dynamic, test_kernel_create_dyn_user_thread)
{
if (!(IS_ENABLED(CONFIG_USERSPACE))) {
ztest_test_skip();
}
create_dynamic_thread();
}
/**
* @ingroup kernel_thread_tests
* @brief Test creation of dynamic user thread under user thread
*
* @details This is a simple test to create a user thread
* dynamically via k_object_alloc() under a user thread.
*/
ZTEST_USER(thread_dynamic, test_user_create_dyn_user_thread)
{
create_dynamic_thread();
}
/* test case main entry */
void *thread_test_setup(void)
{
k_thread_system_pool_assign(k_current_get());
prep();
return NULL;
}
ZTEST_SUITE(thread_dynamic, NULL, thread_test_setup, NULL, NULL, NULL);