blob: be85bb26d6fa99a115d2f4b2ddf4b7758f898215 [file] [log] [blame]
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/kernel_structs.h>
#include <zephyr/app_memory/app_memdomain.h>
#include <zephyr/sys/libc-hooks.h>
#include <zephyr/sys/util.h>
#define NUM_THREADS 3
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
#define STATIC_DATA8 0x7FU
#define STATIC_DATA32 0xABCDEF00U
#define STATIC_DATA64 0x1122334455667788UL
#define PREFIX_8 0x30U
#define PREFIX_32 0x44668800U
#define PREFIX_64 0xFFEEDDCC00000000UL
#ifdef CONFIG_USERSPACE
K_APPMEM_PARTITION_DEFINE(part_common);
struct k_mem_domain dom_common;
#endif /* CONFIG_USERSPACE */
enum test_result {
TEST_OK,
/* When thread_data* != STATIC_DATA at thread entry */
ERR_BAD_STATIC_DATA,
/* When thread_bss* != 0 at thread entry */
ERR_BSS_NOT_ZERO,
/* If data/bss is changed by other threads */
ERR_DATA_CHANGED_BY_OTHERS,
ERR_BSS_CHANGED_BY_OTHERS,
TEST_NOT_STARTED,
};
static K_THREAD_STACK_ARRAY_DEFINE(tls_stack, NUM_THREADS, STACK_SIZE);
static struct k_thread tls_thread[NUM_THREADS];
K_APP_BMEM(part_common) static k_tid_t tls_tid[NUM_THREADS];
K_APP_BMEM(part_common) static enum test_result tls_result[NUM_THREADS];
/* Thread data with initialized values */
static uint8_t __thread thread_data8 = STATIC_DATA8;
static uint32_t __thread thread_data32 = STATIC_DATA32;
static uint64_t __thread thread_data64 = STATIC_DATA64;
/* Zeroed thread data */
static uint8_t __thread thread_bss8;
static uint32_t __thread thread_bss32;
static uint64_t __thread thread_bss64;
static void tls_thread_entry(void *p1, void *p2, void *p3)
{
uint32_t idx;
idx = (uint32_t)POINTER_TO_UINT(p1);
/* Check if TLS area in stack is initialized correctly */
if (thread_data8 != STATIC_DATA8) {
tls_result[idx] = ERR_BAD_STATIC_DATA;
goto out;
}
if (thread_data32 != STATIC_DATA32) {
tls_result[idx] = ERR_BAD_STATIC_DATA;
goto out;
}
if (thread_data64 != STATIC_DATA64) {
tls_result[idx] = ERR_BAD_STATIC_DATA;
goto out;
}
if (thread_bss8 != 0) {
tls_result[idx] = ERR_BSS_NOT_ZERO;
goto out;
}
if (thread_bss32 != 0) {
tls_result[idx] = ERR_BSS_NOT_ZERO;
goto out;
}
if (thread_bss64 != 0) {
tls_result[idx] = ERR_BSS_NOT_ZERO;
goto out;
}
/* Set thread data and see if they remain unchanged */
thread_data8 = STATIC_DATA8 + idx;
thread_bss8 = PREFIX_8 + idx;
thread_data32 = STATIC_DATA32 + idx;
thread_bss32 = PREFIX_32 + idx;
thread_data64 = STATIC_DATA64 + idx;
thread_bss64 = PREFIX_64 + idx;
/* Let other threads run */
k_sleep(K_MSEC(100));
if (thread_data8 != (STATIC_DATA8 + idx)) {
tls_result[idx] = ERR_DATA_CHANGED_BY_OTHERS;
goto out;
}
if (thread_data32 != (STATIC_DATA32 + idx)) {
tls_result[idx] = ERR_DATA_CHANGED_BY_OTHERS;
goto out;
}
if (thread_data64 != (STATIC_DATA64 + idx)) {
tls_result[idx] = ERR_DATA_CHANGED_BY_OTHERS;
goto out;
}
if (thread_bss8 != (PREFIX_8 + idx)) {
tls_result[idx] = ERR_BSS_CHANGED_BY_OTHERS;
goto out;
}
if (thread_bss32 != (PREFIX_32 + idx)) {
tls_result[idx] = ERR_BSS_CHANGED_BY_OTHERS;
goto out;
}
if (thread_bss64 != (PREFIX_64 + idx)) {
tls_result[idx] = ERR_BSS_CHANGED_BY_OTHERS;
goto out;
}
/* Values are all expected. Test passed */
tls_result[idx] = TEST_OK;
out:
return;
}
static void start_tls_test(uint32_t thread_options)
{
unsigned int i;
bool passed;
/* Create threads */
for (i = 0; i < NUM_THREADS; i++) {
tls_result[i] = TEST_NOT_STARTED;
tls_tid[i] = k_thread_create(&tls_thread[i], tls_stack[i],
STACK_SIZE, tls_thread_entry,
UINT_TO_POINTER(i), NULL, NULL,
0, thread_options, K_NO_WAIT);
}
/* Wait for all threads to run */
k_sleep(K_MSEC(500));
/* Stop all threads */
for (i = 0; i < NUM_THREADS; i++) {
k_thread_abort(tls_tid[i]);
k_thread_join(&tls_thread[i], K_FOREVER);
}
/* Check test results */
passed = true;
for (i = 0; i < NUM_THREADS; i++) {
TC_PRINT("thread %d: result %d (expecting %d)\n",
i, tls_result[i], TEST_OK);
if (tls_result[i] != TEST_OK) {
passed = false;
}
}
zassert_true(passed, "Test failed");
}
ZTEST(thread_tls, test_tls)
{
if (IS_ENABLED(CONFIG_USERSPACE)) {
ztest_test_skip();
}
/* TLS test in supervisor mode */
start_tls_test(0);
}
ZTEST_USER(thread_tls, test_tls_userspace)
{
/* TLS test in supervisor mode */
start_tls_test(K_USER | K_INHERIT_PERMS);
}
void *thread_tls_setup(void)
{
#ifdef CONFIG_USERSPACE
int ret;
unsigned int i;
struct k_mem_partition *parts[] = {
&part_common,
#if Z_LIBC_PARTITION_EXISTS
&z_libc_partition,
#endif
&ztest_mem_partition,
};
parts[0] = &part_common;
ret = k_mem_domain_init(&dom_common, ARRAY_SIZE(parts), parts);
__ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
ARG_UNUSED(ret);
k_mem_domain_add_thread(&dom_common, k_current_get());
for (i = 0; i < NUM_THREADS; i++) {
k_thread_access_grant(k_current_get(),
&tls_thread[i], &tls_stack[i]);
}
#endif /* CONFIG_USERSPACE */
return NULL;
}
ZTEST_SUITE(thread_tls, NULL, thread_tls_setup, NULL, NULL, NULL);