SuperTinyKernel™ RTOS 1.08.x
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk_arch_x86-win32.cpp
Go to the documentation of this file.
1/*
2 * SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
3 *
4 * Source: https://github.com/SuperTinyKernel-RTOS
5 *
6 * Copyright (c) 2022-2026 Neutron Code Limited <stk@neutroncode.com>. All Rights Reserved.
7 * License: MIT License, see LICENSE for a full text.
8 */
9
10// note: If missing, this header must be customized (get it in the root of the source folder) and
11// copied to the /include folder manually.
12#include "stk_config.h"
13
14#ifdef _STK_ARCH_X86_WIN32
15
16#include "stk_arch.h"
18
19using namespace stk;
20
21#define WIN32_LEAN_AND_MEAN
22#include <windows.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <assert.h>
26#include <list>
27#include <vector>
28
29using namespace stk;
30
31#ifndef WINAPI
32#define WINAPI __stdcall
33#endif
34
35typedef UINT MMRESULT;
36typedef MMRESULT (WINAPI * timeBeginPeriodF)(UINT uPeriod);
37static timeBeginPeriodF timeBeginPeriod = nullptr;
38
39#define STK_X86_WIN32_CRITICAL_SECTION CRITICAL_SECTION
40#define STK_X86_WIN32_CRITICAL_SECTION_INIT(SES) ::InitializeCriticalSection(SES)
41#define STK_X86_WIN32_CRITICAL_SECTION_START(SES) ::EnterCriticalSection(SES)
42#define STK_X86_WIN32_CRITICAL_SECTION_END(SES) ::LeaveCriticalSection(SES)
43#define STK_X86_WIN32_MIN_RESOLUTION (1000)
44#define STK_X86_WIN32_GET_SP(STACK) (STACK + 2) // +2 to overcome stack filler check inside Kernel (adjusting to +2 preserves 8-byte alignment)
45#define SLK_UNLOCKED hw::SpinLock::UNLOCKED
46#define SLK_LOCKED hw::SpinLock::LOCKED
47
50static __stk_forceinline bool HW_SpinLockTryLock(volatile LONG &lock)
51{
52 return (InterlockedCompareExchange(
53 reinterpret_cast<volatile LONG *>(&lock), SLK_LOCKED, SLK_UNLOCKED) == SLK_UNLOCKED);
54}
55
58static __stk_forceinline void HW_SpinLockLock(volatile LONG &lock)
59{
60 uint8_t sleep_time = 0;
61 uint32_t timeout = 0xFFFFFF;
62
63test:
64 while (!HW_SpinLockTryLock(lock))
65 {
66 if (--timeout == 0)
67 {
68 // invariant violated: the lock owner exited without releasing
70 }
71
72 for (volatile int32_t spin = 100; (spin != 0); spin--)
73 {
74 __stk_relax_cpu();
75
76 // check if became unlocked then try locking atomically again
77 if (lock == SLK_UNLOCKED)
78 goto test;
79 }
80
81 // avoid priority inversion
82 ::Sleep(sleep_time);
83 sleep_time ^= 1;
84 }
85}
86
89static __stk_forceinline void HW_SpinLockUnlock(volatile LONG &lock)
90{
91 InterlockedExchange(reinterpret_cast<volatile LONG *>(&lock), SLK_UNLOCKED);
92}
93
94struct Win32ScopedCriticalSection
95{
96 STK_X86_WIN32_CRITICAL_SECTION &m_sec;
97
98 explicit Win32ScopedCriticalSection(STK_X86_WIN32_CRITICAL_SECTION &sec) : m_sec(sec)
99 {
100 STK_X86_WIN32_CRITICAL_SECTION_START(&sec);
101 }
102 ~Win32ScopedCriticalSection()
103 {
104 STK_X86_WIN32_CRITICAL_SECTION_END(&m_sec);
105 }
106};
107
108class HiResClockQPC
109{
110 LARGE_INTEGER m_freq;
111 LARGE_INTEGER m_start;
112
113public:
114 explicit HiResClockQPC()
115 {
116 QueryPerformanceFrequency(&m_freq);
117 QueryPerformanceCounter(&m_start);
118 }
119
120 static HiResClockQPC *GetInstance()
121 {
122 // keep declaration function-local to allow compiler stripping it from the binary if
123 // it is unused by the user code
124 static HiResClockQPC clock;
125 return &clock;
126 }
127
128 Cycles GetCycles()
129 {
130 LARGE_INTEGER current;
131 QueryPerformanceCounter(&current);
132
133 // relative cycles since simulation start
134 return static_cast<Cycles>(current.QuadPart - m_start.QuadPart);
135 }
136
137 uint32_t GetFrequency()
138 {
139 return static_cast<uint32_t>(m_freq.QuadPart);
140 }
141};
142
144static struct Context final : public PlatformContext
145{
146 Context()
147 : m_overrider(nullptr),
148 m_sleep_trap(nullptr),
149 m_exit_trap(nullptr),
150 m_winmm_dll(nullptr),
151 m_timer_thread(nullptr),
152 m_switch_event(nullptr),
153 m_pending_switch_id(TID_NONE),
154 m_tls(TLS_OUT_OF_INDEXES),
155 m_tasks(),
156 m_task_threads(),
157 m_timer_tid(0),
158 #if STK_TICKLESS_IDLE
159 m_sleep_ticks(0),
160 #endif
161 m_cs(),
162 m_csu_nesting(0),
163 m_started(false),
164 m_stop_signal(false)
165 {}
166
167 void Initialize(IPlatform::IEventHandler *handler, IKernelService *service, Stack *exit_trap,
168 uint32_t resolution_us)
169 {
170 InitializeBase(handler, service, exit_trap, resolution_us);
171
172 m_sleep_trap = nullptr; // set by Context::InitStack
173 m_exit_trap = nullptr; // set by Context::InitStack
174 m_winmm_dll = nullptr;
175 m_timer_thread = nullptr;
176 m_started = false;
177 m_stop_signal = false;
178 m_csu_nesting = 0;
179 m_timer_tid = 0;
180 m_pending_switch_id = TID_NONE;
181 #if STK_TICKLESS_IDLE
182 m_sleep_ticks = 0;
183 #endif
184
185 // auto-reset: used by a task's own thread to hand a forced context
186 // switch off to TimerThread, since only TimerThread (or a thread other
187 // than the one being switched) may safely call SwitchContext()
188 m_switch_event = CreateEventA(nullptr, FALSE, FALSE, nullptr);
189 STK_ASSERT(m_switch_event != nullptr);
190
191 #if STK_TLS
192 if ((m_tls = TlsAlloc()) == TLS_OUT_OF_INDEXES)
193 {
194 STK_ASSERT(false);
195 return;
196 }
197 #endif
198
199 STK_X86_WIN32_CRITICAL_SECTION_INIT(&m_cs);
200
201 LoadWindowsAPI();
202 }
203
204 virtual ~Context()
205 {
206 #if STK_TLS
207 if (m_tls != TLS_OUT_OF_INDEXES)
208 TlsFree(m_tls);
209 #endif
210
211 if (m_switch_event != nullptr)
212 {
213 CloseHandle(m_switch_event);
214 m_switch_event = nullptr;
215 }
216
217 UnloadWindowsAPI();
218 }
219
220 void LoadWindowsAPI()
221 {
222 HMODULE winmm = GetModuleHandleA("Winmm");
223 if (winmm == nullptr)
224 {
225 m_winmm_dll = winmm = LoadLibraryA("Winmm.dll");
226 }
227 STK_ASSERT(winmm != nullptr);
228
229 timeBeginPeriod = (timeBeginPeriodF)GetProcAddress(winmm, "timeBeginPeriod");
230 STK_ASSERT(timeBeginPeriod != nullptr);
231
232 timeBeginPeriod(1);
233 }
234
235 void UnloadWindowsAPI()
236 {
237 if (m_winmm_dll != nullptr)
238 {
239 FreeLibrary(m_winmm_dll);
240 m_winmm_dll = nullptr;
241 }
242 }
243
244 struct TaskContext
245 {
246 TaskContext() : m_task(nullptr), m_stack(nullptr), m_thread(nullptr), m_thread_id(0), m_exiting(false)
247 {}
248
249 void Initialize(ITask *task, Stack *stack)
250 {
251 m_task = task;
252 m_stack = stack;
253 m_thread = nullptr;
254 m_thread_id = 0;
255 m_exiting = false;
256
257 InitThread();
258 }
259
260 void InitThread()
261 {
262 // simulate stack size limitation
263 const size_t stack_size = m_task->GetStackSize() * sizeof(Word);
264
265 m_thread = CreateThread(nullptr, stack_size, &OnTaskRun, this, CREATE_SUSPENDED, &m_thread_id);
266 }
267
268 static DWORD WINAPI OnTaskRun(LPVOID param); // defined below, after Context is complete
269
270 ITask *m_task;
271 Stack *m_stack;
272 HANDLE m_thread;
273 DWORD m_thread_id;
274 volatile bool m_exiting;
275 };
276
277 void InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task);
278 void ConfigureTime();
279 void StartActiveTask();
280 void CreateTimerThreadAndJoin();
281 void Cleanup();
282 void ProcessTick();
283 void SwitchContext();
284 void SwitchToNext();
285 void ForceContextSwitch(TId id);
286 void OnForceContextSwitch();
287 void ReportTaskExit(TaskContext *task);
288 void Sleep(Timeout ticks);
289 bool SleepUntil(Ticks timestamp);
290 EWaitResult Wait(ISyncObject *sync_obj, IMutex *mutex, Timeout timeout);
291 void Stop();
292 Word GetCallerSP() const;
293 TId GetTid() const;
294
295#if STK_TLS
296 __stk_forceinline Word GetTls()
297 {
298 return hw::PtrToWord(TlsGetValue(m_tls));
299 }
300
301 __stk_forceinline void SetTls(Word tp)
302 {
303 TlsSetValue(m_tls, hw::WordToPtr<void>(tp));
304 }
305#endif
306
307 __stk_forceinline void EnterCriticalSection()
308 {
309 STK_X86_WIN32_CRITICAL_SECTION_START(&m_cs);
310
311 if (m_csu_nesting == 0)
312 {
313 // avoid suspending self
314 if (GetCurrentThreadId() != m_timer_tid)
315 {
316 SuspendThread(m_timer_thread);
317 }
318 }
319
320 // increase nesting count within a limit
321 if (++m_csu_nesting > STK_CS_NESTINGS_MAX)
322 {
323 // invariant violated: exceeded max allowed number of recursions
324 STK_KERNEL_PANIC(KERNEL_PANIC_CS_NESTING_OVERFLOW);
325 }
326 }
327
328 __stk_forceinline void ExitCriticalSection()
329 {
330 STK_ASSERT(m_csu_nesting != 0);
331
332 --m_csu_nesting;
333
334 if (m_csu_nesting == 0)
335 {
336 // suspending self is not supported
337 if (GetCurrentThreadId() != m_timer_tid)
338 {
339 ResumeThread(m_timer_thread);
340 }
341 }
342
343 STK_X86_WIN32_CRITICAL_SECTION_END(&m_cs);
344 }
345
346 IPlatform::IEventOverrider *m_overrider;
347 Stack *m_sleep_trap;
348 Stack *m_exit_trap;
349 HMODULE m_winmm_dll;
350 HANDLE m_timer_thread;
351 HANDLE m_switch_event;
352 volatile TId m_pending_switch_id;
353 DWORD m_tls;
354 std::list<TaskContext *> m_tasks;
355 std::vector<HANDLE> m_task_threads;
356 DWORD m_timer_tid;
357#if STK_TICKLESS_IDLE
358 Timeout m_sleep_ticks;
359#endif
360 STK_X86_WIN32_CRITICAL_SECTION m_cs;
361 uint8_t m_csu_nesting;
362 bool m_started;
363 volatile bool m_stop_signal;
364}
365s_StkPlatformContext[1];
366
368static volatile EKernelPanicId g_LastPanicId = KERNEL_PANIC_NONE;
369
370__stk_attr_noinline // keep out of inlining to preserve stack frame
371__stk_attr_noreturn // never returns - a trap
373{
374 g_LastPanicId = id;
375
376 // spin forever: without a watchdog, a debugger can attach and inspect 'id'
377 for (;;)
378 {
379 __stk_relax_cpu();
380 }
381}
382
383static __stk_forceinline DWORD TicksToMs(uint64_t ticks)
384{
385 return static_cast<DWORD>((ticks * GetContext().m_tick_resolution) / 1000U);
386}
387
388static DWORD WINAPI TimerThread(LPVOID param)
389{
390 (void)param;
391
392 Context &ctx = GetContext();
393 DWORD wait_ms = TicksToMs(1U);
394 ctx.m_timer_tid = GetCurrentThreadId();
395
396 // wait_handles[0] is TimerThread's own handle: since it can only become
397 // signaled once this very thread exits, waiting on it can never actually
398 // be satisfied from inside itself - it's used purely so the call below
399 // behaves like a precise timed sleep (kept as in the original code).
400 // wait_handles[1] lets a task thread wake TimerThread early to request a
401 // forced context switch be serviced (see Context::ForceContextSwitch()).
402 HANDLE wait_handles[] = { ctx.m_timer_thread, ctx.m_switch_event };
403
404 for (;;)
405 {
406 DWORD result = WaitForMultipleObjects(STK_STATIC_ARRAY_SIZE(wait_handles), wait_handles, FALSE, wait_ms);
407
408 if (ctx.m_stop_signal)
409 {
410 break;
411 }
412
413 if (result == (WAIT_OBJECT_0 + 1))
414 {
415 // a task requested a forced context switch: SwitchContext() must
416 // never be invoked by the task thread that might itself need to
417 // be suspended, so it's serviced here instead.
418 ctx.OnForceContextSwitch();
419 continue;
420 }
421
422 STK_ASSERT(result == WAIT_TIMEOUT);
423
424 ctx.ProcessTick();
425
426 #if STK_TICKLESS_IDLE
427 wait_ms = TicksToMs(ctx.m_sleep_ticks);
428 #endif
429 }
430
431 return 0;
432}
433
434void Context::ConfigureTime()
435{
436 // Windows timers are jittery, so make resolution more coarse
437 if (m_tick_resolution < STK_X86_WIN32_MIN_RESOLUTION)
438 {
439 m_tick_resolution = STK_X86_WIN32_MIN_RESOLUTION;
440 }
441
442 // increase precision of ticks to at least 1 ms (although Windows timers will still be quite coarse and have jitter of +1 ms)
443 timeBeginPeriod(1);
444}
445
446void Context::StartActiveTask()
447{
448 STK_ASSERT(m_stack_active != nullptr);
449 TaskContext *active_task = hw::WordToPtr<TaskContext>(m_stack_active->SP);
450 STK_ASSERT(active_task != nullptr);
451
452 ResumeThread(active_task->m_thread);
453}
454
455void Context::CreateTimerThreadAndJoin()
456{
457 m_started = true;
458
459#if STK_TICKLESS_IDLE
460 m_sleep_ticks = 1;
461#endif
462
463 m_handler->OnStart(m_stack_active);
464
465 StartActiveTask();
466
467 // create tick thread with highest priority
468 m_timer_thread = CreateThread(nullptr, 0, &TimerThread, nullptr, 0, nullptr);
469 STK_ASSERT(m_timer_thread != nullptr);
470 SetThreadPriority(m_timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
471
472 while (!m_task_threads.empty())
473 {
474 DWORD result = WaitForMultipleObjects((DWORD)m_task_threads.size(), m_task_threads.data(), FALSE, INFINITE);
475 STK_ASSERT(result != WAIT_TIMEOUT);
476 STK_ASSERT(result != WAIT_ABANDONED);
477 STK_ASSERT(result != WAIT_FAILED);
478
479 Win32ScopedCriticalSection __cs(m_cs);
480
481 uint32_t i = 0;
482 for (std::vector<HANDLE>::iterator itr = m_task_threads.begin(); itr != m_task_threads.end(); ++itr)
483 {
484 if (result == (WAIT_OBJECT_0 + i))
485 {
486 // note: the kernel is notified about the exit by the exiting task thread itself
487 // (see Context::ReportTaskExit), here we only drop the finished thread's handle
488 m_task_threads.erase(itr);
489 break;
490 }
491
492 ++i;
493 }
494 }
495
496 // join (never returns to the caller from here unless thread is terminated, see KERNEL_DYNAMIC),
497 // a stop signal is sent by IPlatform::Stop() by the last exiting task
498 if (m_timer_thread != nullptr)
499 {
500 WaitForSingleObject(m_timer_thread, INFINITE);
501 }
502}
503
504void Context::Cleanup()
505{
506 // close thread handles of all tasks
507 for (std::list<TaskContext *>::iterator itr = m_tasks.begin(); itr != m_tasks.end(); ++itr)
508 {
509 if ((*itr)->m_thread != nullptr)
510 {
511 CloseHandle((*itr)->m_thread);
512 (*itr)->m_thread = nullptr;
513 }
514 }
515 m_tasks.clear();
516
517 // close timer thread
518 if (m_timer_thread != nullptr)
519 {
520 CloseHandle(m_timer_thread);
521 m_timer_thread = nullptr;
522 }
523
524 // reset stop signal
525 m_stop_signal = false;
526
527 // notify kernel about a full stop
528 m_handler->OnStop();
529}
530
531void Context::ProcessTick()
532{
533 Win32ScopedCriticalSection __cs(m_cs);
534
535#if STK_TICKLESS_IDLE
536 Timeout ticks = m_sleep_ticks;
537#endif
538
539 if (m_handler->OnTick(m_stack_idle, m_stack_active
540 #if STK_TICKLESS_IDLE
541 , ticks
542 #endif
543 ))
544 {
545 SwitchContext();
546 }
547
548#if STK_TICKLESS_IDLE
549 m_sleep_ticks = ticks;
550#endif
551}
552
553void Context::SwitchContext()
554{
555 // suspend Idle thread
556 if ((m_stack_idle != m_sleep_trap) && (m_stack_idle != m_exit_trap))
557 {
558 TaskContext *idle_task = hw::WordToPtr<TaskContext>(m_stack_idle->SP);
559 STK_ASSERT(idle_task != nullptr);
560
561 // a thread that already reported its exit is only returning from its entry function:
562 // let it finish, suspending it would leave it hanging forever (see ReportTaskExit)
563 if (!idle_task->m_exiting)
564 {
565 SuspendThread(idle_task->m_thread);
566 }
567 }
568
569 // resume Active thread
570 if (m_stack_active == m_sleep_trap)
571 {
572 #if STK_TICKLESS_IDLE
573 const Timeout sleep_ticks = m_sleep_ticks;
574 #else
575 const Timeout sleep_ticks = 1;
576 #endif
577
578 if ((m_overrider == nullptr) || !m_overrider->OnSleep(sleep_ticks))
579 {
580 // pass
581 }
582 }
583 else
584 if (m_stack_active == m_exit_trap)
585 {
586 // pass
587 }
588 else
589 {
590 TaskContext *active_task = hw::WordToPtr<TaskContext>(m_stack_active->SP);
591 STK_ASSERT(active_task != nullptr);
592
593 ResumeThread(active_task->m_thread);
594 }
595}
596
597Word Context::GetCallerSP() const
598{
599 Word caller_sp = 0;
600 DWORD calling_tid = GetCurrentThreadId();
601
602 Win32ScopedCriticalSection __cs(const_cast<STK_X86_WIN32_CRITICAL_SECTION &>(m_cs));
603
604 for (std::list<TaskContext *>::const_iterator itr = m_tasks.begin(), end = m_tasks.end(); itr != end; ++itr)
605 {
606 if ((*itr)->m_thread_id == calling_tid)
607 {
608 caller_sp = hw::PtrToWord(STK_X86_WIN32_GET_SP((*itr)->m_task->GetStack()));
609 break;
610 }
611 }
612
613 // expect to find the calling task inside m_tasks
614 STK_ASSERT(caller_sp != 0);
615
616 return caller_sp;
617}
618
619TId Context::GetTid() const
620{
621 TId result;
622
623 if (m_started)
624 {
625 result = m_handler->OnGetTid(GetCallerSP());
626 }
627 else
628 {
629 result = TID_NONE;
630 }
631
632 return result;
633}
634
635void Context::SwitchToNext()
636{
637 m_handler->OnTaskSwitch(GetCallerSP());
638}
639
640void Context::ForceContextSwitch(TId id)
641{
642 if (GetCurrentThreadId() == m_timer_tid)
643 {
644 // running on TimerThread: execute forced context switch
645 if (m_handler->OnForceContextSwitch(id, m_stack_idle, m_stack_active))
646 {
647 SwitchContext();
648 }
649 }
650 else
651 {
652 // notify TimerThread to cause a context switch,
653 // note: the id must be published before the event is signaled (SetEvent() is a full barrier)
654 m_pending_switch_id = id;
655 __stk_full_memfence();
656 SetEvent(m_switch_event);
657 }
658}
659
660void Context::OnForceContextSwitch()
661{
662 Win32ScopedCriticalSection __cs(m_cs);
663
664 // read the requested id once (under m_cs) so that a concurrent request from another task
665 // cannot change it between the read and its use, a stale id is ignored by the kernel
666 const TId id = m_pending_switch_id;
667
668 if (m_handler->OnForceContextSwitch(id, m_stack_idle, m_stack_active))
669 {
670 SwitchContext();
671 }
672}
673
674void Context::ReportTaskExit(TaskContext *task)
675{
676 // note: must be called by the exiting task's own thread while it is still the scheduled task
677 // (m_task_now), as on other platforms. If the exit was reported later from another thread
678 // (after the thread had died), the kernel could have already switched to another task and
679 // would receive SLEEP_PENDING on a non-current task, which breaks the kernel's invariant
680 // that only the currently scheduled task can be pending to sleep.
681 const TId tid = stk::GetTidFromUserTask(task->m_task);
682
683 // hold m_cs (recursive) so that TimerThread cannot switch this thread out between setting
684 // m_exiting and the kernel being notified: a switch before that point suspends the thread
685 // normally (it is simply resumed later), a switch after it must not suspend the thread
686 // at all (see SwitchContext) because it is never resumed and its handle would never signal
687 {
688 Win32ScopedCriticalSection __cs(m_cs);
689
690 task->m_exiting = true;
691
692 // kernel enters the user critical section by itself
693 m_handler->OnTaskExit(task->m_stack);
694 }
695
696 // let TimerThread switch this task out now instead of waiting for the next tick,
697 // note: 'task' must not be touched past this point, its slot may be released by the kernel
698 ForceContextSwitch(tid);
699}
700
701DWORD WINAPI Context::TaskContext::OnTaskRun(LPVOID param)
702{
703 TaskContext *const self = static_cast<TaskContext *>(param);
704
705 self->m_task->Run();
706
707 GetContext().ReportTaskExit(self);
708
709 return 0; // thread terminates, SuspendThread() on a finished thread is harmless
710}
711
712void Context::Sleep(Timeout ticks)
713{
714 m_handler->OnTaskSleep(GetCallerSP(), ticks);
715}
716
717bool Context::SleepUntil(Ticks timestamp)
718{
719 return m_handler->OnTaskSleepUntil(GetCallerSP(), timestamp);
720}
721
722EWaitResult Context::Wait(ISyncObject *sync_obj, IMutex *mutex, Timeout timeout)
723{
724 return m_handler->OnTaskWait(GetCallerSP(), sync_obj, mutex, timeout);
725}
726
727void Context::Stop()
728{
729 m_stop_signal = true;
730 m_started = false;
731}
732
733void Context::InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task)
734{
735 InitStackMemory(stack_memory);
736
737 Word *const stack_mem = const_cast<Word *>(stack_memory->GetStack());
738 TaskContext *const ctx = reinterpret_cast<TaskContext *>(STK_X86_WIN32_GET_SP(stack_mem));
739
740 switch (stack_type)
741 {
742 case STACK_USER_TASK: {
743 ctx->Initialize(user_task, stack);
744
745 m_tasks.push_back(ctx);
746 m_task_threads.push_back(ctx->m_thread);
747 break; }
748
749 case STACK_SLEEP_TRAP: {
750 GetContext().m_sleep_trap = stack;
751 break; }
752
753 case STACK_EXIT_TRAP: {
754 GetContext().m_exit_trap = stack;
755 break; }
756
757 default: {
758 STK_ASSERT(false);
759 break; }
760 }
761
762 stack->SP = hw::PtrToWord(ctx);
763}
764
765void PlatformX86Win32::Initialize(IEventHandler *event_handler, IKernelService *service, uint32_t resolution_us,
766 Stack *exit_trap)
767{
768 GetContext().Initialize(event_handler, service, exit_trap, resolution_us);
769}
770
772{
773 GetContext().ConfigureTime();
774 GetContext().CreateTimerThreadAndJoin();
775 GetContext().Cleanup();
776}
777
779{
780 GetContext().Stop();
781}
782
784{
785 STK_ASSERT(false); // unsupported
786 return 0;
787}
788
789void PlatformX86Win32::Resume(Timeout elapsed_ticks)
790{
791 STK_ASSERT(false); // unsupported
792}
793
794void PlatformX86Win32::SetCpuFrequency(uint8_t core_id, uint32_t frequency)
795{
796 STK_ASSERT(false); // unsupported
797}
798
799void PlatformX86Win32::InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task)
800{
801 GetContext().InitStack(stack_type, stack, stack_memory, user_task);
802}
803
805{
806 return GetContext().m_tick_resolution;
807}
808
810{
811 return HiResClockQPC::GetInstance()->GetCycles();
812}
813
815{
816 return HiResClockQPC::GetInstance()->GetFrequency();
817}
818
820{
821 GetContext().SwitchToNext();
822}
823
825{
826 GetContext().ForceContextSwitch(id);
827}
828
830{
831 GetContext().Sleep(ticks);
832}
833
835{
836 return GetContext().SleepUntil(timestamp);
837}
838
840{
841 return GetContext().Wait(sync_obj, mutex, timeout);
842}
843
845{
846 GetContext().ProcessTick();
847}
848
850{
851 if ((GetContext().m_overrider == nullptr) || !GetContext().m_overrider->OnHardFault())
852 {
854 }
855}
856
857void PlatformX86Win32::SetEventOverrider(IEventOverrider *overrider, bool non_secure)
858{
859 STK_UNUSED(non_secure);
860
861 STK_ASSERT(!GetContext().m_started);
862 GetContext().m_overrider = overrider;
863}
864
866{
867 return GetContext().GetCallerSP();
868}
869
871{
872 return GetContext().GetTid();
873}
874
875#if STK_TLS
876Word stk::hw::GetTls()
877{
878 return GetContext().GetTls();
879}
880
881void stk::hw::SetTls(Word tp)
882{
883 return GetContext().SetTls(tp);
884}
885#endif
886
888{
889 return GetContext().m_service;
890}
891
893{
894 STK_UNUSED(is_npriv);
895 GetContext().EnterCriticalSection();
896 return DEFAULT_SESSION;
897}
898
900{
901 STK_UNUSED(is_npriv);
902 GetContext().ExitCriticalSection();
903}
904
906{
907 HW_SpinLockLock(m_lock);
908}
909
911{
912 HW_SpinLockUnlock(m_lock);
913}
914
916{
917 return HW_SpinLockTryLock(m_lock);
918}
919
921{
922 return false;
923}
924
926{
927 return true;
928}
929
931{
932 return HiResClockQPC::GetInstance()->GetCycles();
933}
934
936{
937 return HiResClockQPC::GetInstance()->GetFrequency();
938}
939
940#endif // _STK_ARCH_X86_WIN32
Contains common inventory for platform implementation.
#define GetContext()
Get platform's context.
Hardware Abstraction Layer (HAL) declarations for the stk::hw namespace.
void STK_PANIC_HANDLER_DEFAULT(stk::EKernelPanicId id)
Default panic handler: disable interrupts, record the id, and spin in a tight loop - a defined,...
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:715
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
Definition stk_defs.h:277
#define STK_CS_NESTINGS_MAX
Maximum allowable recursion depth for critical section entry (default: 16).
Definition stk_defs.h:590
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:516
#define __stk_attr_noinline
Prevents compiler from inlining the decorated function (function prefix).
Definition stk_defs.h:357
#define __stk_attr_noreturn
Declares that function never returns to its caller (function prefix).
Definition stk_defs.h:324
#define STK_STATIC_ARRAY_SIZE(ARRAY)
Get size of the static array.
Definition stk_defs.h:742
unsigned int UINT
Definition tx_api.h:108
int LONG
Definition tx_api.h:113
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:160
static void Sleep(Timeout tick_count)
Put calling process into a sleep state.
Definition stk_helper.h:497
EWaitResult
Wait result (see IKernelService::Wait).
Definition stk_common.h:137
int64_t Ticks
Ticks value.
Definition stk_common.h:175
EKernelPanicId
Identifies the source of a kernel panic.
Definition stk_common.h:60
@ KERNEL_PANIC_HRT_HARD_FAULT
Kernel running in KERNEL_HRT mode reported deadline failure of the task.
Definition stk_common.h:65
@ KERNEL_PANIC_NONE
Panic is absent (no fault).
Definition stk_common.h:61
@ KERNEL_PANIC_SPINLOCK_DEADLOCK
Spin-lock timeout expired: lock owner never released.
Definition stk_common.h:62
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:170
static void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:183
static constexpr TId TID_NONE
Reserved task/thread id representing zero/none thread id.
Definition stk_common.h:222
EStackType
Stack type.
Definition stk_common.h:95
@ STACK_SLEEP_TRAP
Stack of the Sleep trap.
Definition stk_common.h:97
@ STACK_USER_TASK
Stack of the user task.
Definition stk_common.h:96
@ STACK_EXIT_TRAP
Stack of the Exit trap.
Definition stk_common.h:98
static constexpr TId GetTidFromUserTask(const ITask *task) noexcept
Get task identifier from ITask instance.
Definition stk_arch.h:716
uint64_t Cycles
Cycles value.
Definition stk_common.h:185
Word TId
Task (thread) id.
Definition stk_common.h:165
bool IsPrivilegedContext()
Check if caller context is Privileged.
static constexpr T * WordToPtr(Word value) noexcept
Cast a CPU register-width integer back to a pointer.
Definition stk_arch.h:231
static constexpr Word PtrToWord(T *const ptr) noexcept
Cast a pointer to a CPU register-width integer.
Definition stk_arch.h:214
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
Base platform context for all platform implementations.
EWaitResult Wait(ISyncObject *sync_obj, IMutex *mutex, Timeout timeout) override
Put calling process into a waiting state until synchronization object is signaled or timeout occurs.
void Sleep(Timeout ticks) override
Put calling process into a sleep state.
void Resume(Timeout elapsed_ticks) override
Resume scheduling after a prior Suspend() call.
void Stop() override
Stop scheduling.
void Initialize(IEventHandler *event_handler, IKernelService *service, uint32_t resolution_us, Stack *exit_trap) override
Initialize scheduler's context.
Word GetCallerSP() const override
Get caller's Stack Pointer (SP).
void Start() override
Start scheduling.
uint32_t GetSysTimerFrequency() const override
Get system timer frequency.
uint32_t GetTickResolution() const override
Get resolution of the system tick timer in microseconds. Resolution means a number of microseconds be...
void SetCpuFrequency(uint8_t core_id, uint32_t frequency) override
Notify the scheduler of a CPU core's operating frequency.
void InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task) override
Initialize stack memory of the user task.
void ProcessHardFault() override
Cause a hard fault of the system.
void ProcessTick() override
Process one tick.
Cycles GetSysTimerCount() const override
Get system timer count value.
void SetEventOverrider(IEventOverrider *overrider, bool non_secure) override
Set platform event overrider.
bool SleepUntil(Ticks timestamp) override
Put calling process into a sleep state until the specified timestamp.
void ForceContextSwitch(TId id) override
Force context switch.
TId GetTid() const override
Get thread Id.
void SwitchToNext() override
Switch to a next task.
Timeout Suspend() override
Suspend scheduling.
static constexpr Session DEFAULT_SESSION
Default session value passed to Enter()/Exit() when the caller does not need to force a specific hand...
Definition stk_arch.h:478
static Session Enter(const Session ses=DEFAULT_SESSION)
Enter a critical section.
uint8_t Session
Opaque session token returned by Enter() and consumed by Exit().
Definition stk_arch.h:471
static void Exit(const Session ses=DEFAULT_SESSION)
Exit a critical section.
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
void Lock()
Acquire SpinLock, blocking until it is available.
void Unlock()
Release SpinLock, allowing another thread or core to acquire it.
static uint32_t GetFrequency()
Get clock frequency.
static Cycles GetCycles()
Get number of clock cycles elapsed.
Stack descriptor.
Definition stk_common.h:427
Word SP
Offset 0: Stack Pointer (SP) register (note: must always be at offset 0).
Definition stk_common.h:428
Interface for a stack memory region.
Definition stk_common.h:448
virtual const Word * GetStack() const =0
Get pointer to the stack memory.
Synchronization object interface.
Definition stk_common.h:612
Interface for mutex synchronization primitive.
Definition stk_common.h:766
Interface for a user task.
Definition stk_common.h:824
Interface for the kernel services exposed to the user processes during run-time when Kernel started s...
static IKernelService * GetInstance()
Get CPU-local instance of the kernel service.
RISC-V specific event handler.