summaryrefslogtreecommitdiff
path: root/test/tx/thread_stack_alignment/threadx_thread_stack_alignment_test.c
blob: f3ecbac707577659e15d30725b108ae94064d681 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/***************************************************************************
 * Copyright (c) 2026 Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * AI Disclosure: This file was largely AI-generated by Codex (gpt-6.1-sol).
 * The AI-generated portions may be considered public domain (CC0-1.0)
 * and not subject to the project's licence. The human contributor has
 * reviewed and verified that the code is correct.
 *
 * SPDX-License-Identifier: MIT AND CC0-1.0
 **************************************************************************/

/* Execute the real create service and MISRA shim with Linux port types.
   Only interrupt, stack-builder and scheduler boundaries are recorders. */
#define TX_SOURCE_CODE
#include "tx_api.h"
#include "tx_thread.h"
#include <stdio.h>

#define STACK_BYTES     8192U
#define PADDING_BYTES   32U
#define SENTINEL        0xA5U
#define CASE_COUNT      (sizeof(ULONG) * sizeof(ULONG))

/* Kernel state owned by this harness; the real shim declares it extern. */
TX_THREAD *_tx_thread_created_ptr;
TX_THREAD *_tx_thread_execute_ptr;
ULONG _tx_thread_created_count;
volatile ULONG _tx_thread_system_state;
volatile UINT _tx_thread_preempt_disable;

static TX_THREAD threads[CASE_COUNT];
static ALIGN_TYPE storage[(STACK_BYTES + 2U * PADDING_BYTES + 16U) / sizeof(ALIGN_TYPE)];
static VOID *recorded_start;
static VOID *recorded_end;
static ULONG recorded_size;
static VOID *safe_stack_ptr;
static UINT interrupt_depth;
static UINT interrupt_errors;
static UINT build_calls;
static UINT preempt_calls;
static UINT unexpected_calls;
static UINT failures;

/* Report all cases, including on the old tree, without using corrupt metadata
   as an address to read or write. */
static VOID check(UINT condition, const CHAR *message)
{
    if (condition == 0U)
    {
        printf("FAIL: %s\n", message);
        failures++;
    }
}

/* Record the stack handed to the port. The supplied pointer belongs to the
   original allocation even when the create service truncates its metadata. */
VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*entry)(VOID))
{
    recorded_start =  thread_ptr -> tx_thread_stack_start;
    recorded_end =  thread_ptr -> tx_thread_stack_end;
    recorded_size =  thread_ptr -> tx_thread_stack_size;
    thread_ptr -> tx_thread_stack_ptr =  safe_stack_ptr;
    build_calls++;
    check(entry == _tx_thread_shell_entry, "stack builder shell entry");
}

/* Preserve and restore the port's interrupt posture contract. */
UINT _tx_thread_interrupt_disable(VOID)
{
UINT previous =  interrupt_depth;

    interrupt_depth++;
    return(previous);
}

VOID _tx_thread_interrupt_restore(UINT previous)
{
    if ((interrupt_depth == 0U) || (previous + 1U != interrupt_depth))
    {
        interrupt_errors++;
    }
    interrupt_depth =  previous;
}

/* DONT_START reaches preemption checking, but must never resume or execute. */
VOID _tx_thread_system_preempt_check(VOID)
{
    preempt_calls++;
    check(_tx_thread_preempt_disable == 0U, "balanced preemption posture");
}

VOID _tx_thread_system_resume(TX_THREAD *thread_ptr)
{
    (void) thread_ptr;
    unexpected_calls++;
}

VOID _tx_thread_shell_entry(VOID)
{
    unexpected_calls++;
}

VOID _tx_thread_timeout(ULONG input)
{
    (void) input;
    unexpected_calls++;
}

static VOID thread_entry(ULONG input)
{
    (void) input;
    unexpected_calls++;
}

/* Check the complete fill region, caller padding and both reserved stack
   boundaries. Expectations use remainder arithmetic, independently of the
   create service's alignment mask. */
static VOID run_case(UINT offset, ULONG size, UINT index)
{
TX_THREAD *thread_ptr =  &threads[index];
UCHAR *bytes =  (UCHAR *) storage;
UCHAR *input =  bytes + PADDING_BYTES + offset;
ULONG remainder =  (ULONG) ((ALIGN_TYPE) input % sizeof(ULONG));
ULONG adjustment =  (remainder == 0U) ? 0U : (ULONG) sizeof(ULONG) - remainder;
UCHAR *expected_start =  input + adjustment;
ULONG expected_size =  size - size % (ULONG) sizeof(ULONG) - (ULONG) sizeof(ULONG);
UCHAR *expected_end;
UINT status;
UINT i;
UINT calls_before =  build_calls;
TX_THREAD *next;
TX_THREAD *previous;

    if (adjustment != 0U)
    {
        expected_size -=  (ULONG) sizeof(ULONG);
    }
    expected_end =  expected_start + expected_size - 1U;
    safe_stack_ptr =  expected_start + expected_size - 2U * sizeof(ULONG);
    memset(storage, SENTINEL, sizeof(storage));
    status =  _tx_thread_create(thread_ptr, "alignment", thread_entry, 17U,
                               input, size, 15U, 15U, TX_NO_TIME_SLICE, TX_DONT_START);
    printf("offset=%u size=%u input=%p start=%p end=%p usable=%u\n",
           offset, (UINT) size, (VOID *) input, recorded_start, recorded_end, (UINT) recorded_size);
    check(status == TX_SUCCESS, "create status");
    check(build_calls == calls_before + 1U, "one stack build per create");
    check(recorded_start == expected_start, "port start retains high bits and alignment");
    check(thread_ptr -> tx_thread_stack_start == expected_start, "stored stack start");
    check(recorded_size == expected_size, "port stack size and guards");
    check(thread_ptr -> tx_thread_stack_size == expected_size, "stored stack size");
    check(recorded_end == expected_end, "port stack end within caller storage");
    check(thread_ptr -> tx_thread_stack_end == expected_end, "stored stack end");
    check(thread_ptr -> tx_thread_stack_highest_ptr == safe_stack_ptr, "highest stack pointer");
    check(thread_ptr -> tx_thread_stack_ptr == safe_stack_ptr, "initial stack pointer");
    check(thread_ptr -> tx_thread_state == TX_SUSPENDED, "DONT_START suspended state");
    check(thread_ptr -> tx_thread_id == TX_THREAD_ID, "created thread ID");
    check(thread_ptr -> tx_thread_entry == thread_entry, "entry function");
    check(thread_ptr -> tx_thread_entry_parameter == 17U, "scalar entry parameter");
    check(thread_ptr -> tx_thread_priority == 15U, "priority");
    check(thread_ptr -> tx_thread_preempt_threshold == 15U, "preemption threshold");
    check(thread_ptr -> tx_thread_time_slice == TX_NO_TIME_SLICE, "time slice");
    check(thread_ptr -> tx_thread_timer.tx_timer_internal_timeout_function == _tx_thread_timeout,
          "timeout callback");
    check(thread_ptr -> tx_thread_timer.tx_timer_internal_extension_ptr == thread_ptr,
          "timeout thread pointer retains high bits");

    for (i = 0U; i < sizeof(storage); i++)
    {
        UCHAR expected =  ((i >= PADDING_BYTES + offset) &&
                          (i < PADDING_BYTES + offset + size)) ? (UCHAR) TX_STACK_FILL : SENTINEL;
        if (bytes[i] != expected)
        {
            check(0U, "fill region or caller sentinel corrupted");
            break;
        }
    }
    for (i = 0U; i < sizeof(ULONG); i++)
    {
        check(expected_start[i] == (UCHAR) TX_STACK_FILL, "bottom stack guard fill");
        check(expected_end[1U + i] == (UCHAR) TX_STACK_FILL, "top reserved guard fill");
    }
    check(_tx_thread_created_count == index + 1U, "created list count");
    check(_tx_thread_created_ptr == &threads[0], "created list head");
    next =  _tx_thread_created_ptr;
    previous =  thread_ptr;
    for (i = 0U; i <= index; i++)
    {
        check(next == &threads[i], "created list order");
        check(next -> tx_thread_created_previous == previous, "created list back link");
        previous =  next;
        next =  next -> tx_thread_created_next;
    }
    check(next == _tx_thread_created_ptr, "created list closes");
    check((interrupt_depth == 0U) && (interrupt_errors == 0U), "balanced interrupt posture");
    check(preempt_calls == index + 1U, "preemption check count");
    check(unexpected_calls == 0U, "no scheduling at recorder boundary");
}

/* Native64 is a required precondition, never a successful no-op test. */
INT main(VOID)
{
UINT offset;
UINT tail;
UINT index =  0U;

    printf("sizeof(ULONG)=%zu sizeof(ALIGN_TYPE)=%zu sizeof(void*)=%zu\n",
           sizeof(ULONG), sizeof(ALIGN_TYPE), sizeof(VOID *));
    if ((sizeof(ULONG) != 4U) || (sizeof(ALIGN_TYPE) != 8U) || (sizeof(VOID *) != 8U) ||
        ((ALIGN_TYPE) storage <= (ALIGN_TYPE) ((ULONG) ~0U)))
    {
        puts("FAIL: native64 high-address preconditions absent");
        return(1);
    }
    for (offset = 0U; offset < sizeof(ULONG); offset++)
    {
        for (tail = 0U; tail < sizeof(ULONG); tail++)
        {
            run_case(offset, STACK_BYTES + tail, index++);
        }
    }
    printf("%s: %u alignment/size cases, %u failures\n", (failures == 0U) ? "PASS" : "FAIL",
           index, failures);
    return((failures == 0U) ? 0 : 1);
}