/* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #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; 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) { struct k_thread *dyn_thread; 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. */ static void test_dyn_thread_perms(void) { permission_test(); TC_PRINT("===== must have access denied on k_sem %p\n", &end_sem); } static struct k_thread *dynamic_threads[CONFIG_MAX_THREAD_BYTES * 8]; static void test_thread_index_management(void) { 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 /** @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. */ static void test_kernel_create_dyn_user_thread(void) { 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. */ static void test_user_create_dyn_user_thread(void) { create_dynamic_thread(); } /* test case main entry */ void test_main(void) { k_thread_system_pool_assign(k_current_get()); prep(); ztest_test_suite(thread_dynamic, ztest_unit_test(test_kernel_create_dyn_user_thread), ztest_user_unit_test(test_user_create_dyn_user_thread), ztest_unit_test(test_dyn_thread_perms), ztest_unit_test(test_thread_index_management) ); ztest_run_test_suite(thread_dynamic); }