Files
nt-lite/code/kernel/include/kernel/ke_thread.h
T

276 lines
11 KiB
C

// Kernel ke thread declarations shared by implementation files and kernel clients.
#pragma once
#include <ntlite/ddk/thread.h>
// ke_thread.h - thread model
// A THREAD is the unit of scheduling. Every thread belongs to exactly one process.
#include <stdint.h>
#include <stdbool.h>
#include <kernel/ob_object.h>
#include <kernel/ke_list.h>
#include <kernel/ke_process.h>
#include <kernel/ke_event.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ke_trap.h>
#define KSTACK_SIZE (64 * 1024) // 64 KB kernel stack
#define USTACK_SIZE (512 * 1024) // 512 KB user stack
#define KE_USER_EXCEPTION_MAX_DEPTH 8u
typedef struct THREAD THREAD;
struct MUTEX;
// Allocates one boot-unique thread identifier.
KSTATUS KeAllocateThreadId(uint32_t* threadIdOut);
#ifndef NTL_THREAD_PRIORITY_DEFINED
typedef enum THREAD_PRIORITY {
PriorityLow = 0,
PriorityNormal = 1,
PriorityHigh = 2,
PriorityCount = 3,
} THREAD_PRIORITY;
#define NTL_THREAD_PRIORITY_DEFINED 1
#endif
typedef enum THREAD_STATE {
ThreadReady = 0,
ThreadRunning = 1,
ThreadWaiting = 2,
ThreadDead = 3,
ThreadSuspended = 4,
} THREAD_STATE;
typedef void (*KE_WAIT_RUNDOWN_ROUTINE)(struct THREAD* thread,
void* context,
KSTATUS status);
// Saved register context - matches the layout pushed by KeSwitchContext.
// The saved RSP points to this structure on the kernel stack.
typedef struct KCONTEXT {
uint64_t R15, R14, R13, R12;
uint64_t Rbp, Rbx;
uint64_t Rip; // return address back into KeSwitchContext
} KCONTEXT;
struct THREAD {
OBJECT Header; // must be first
uint32_t ThreadId;
THREAD_STATE State;
KE_SCHEDULING_CLASS BaseSchedulingClass;
uint32_t BasePriority;
KE_SCHEDULING_CLASS EffectiveSchedulingClass;
uint32_t EffectivePriority;
// CPU-burst mirror kept here for the assembly-stable THREAD layout.
// Dynamic quantum state lives later in the structure.
uint32_t QuantaConsumed;
PROCESS* Owner;
LIST_ENTRY ProcessLink; // node in Owner->ThreadList
LIST_ENTRY SchedulerLink; // node in run queue or object wait list
// Kernel stack - never PAGE_USER, pointed to by TssRsp0.
void* KernelStackBase;
uintptr_t KernelStackTop;
// Saved context pointer - set by KeSwitchContext.
KCONTEXT* SavedContext;
// Non-NULL while State == ThreadWaiting.
OBJECT* WaitObject;
// Updated on every context switch so the CPU loads the right kernel stack.
uintptr_t TssRsp0;
// Thread Environment Block. GS base is loaded from this pointer.
void* Teb;
size_t TebSize;
// Ring-3 entry point and argument for newly created threads.
uintptr_t UserEntry;
uintptr_t UserArg;
// Saved scheduler-lock cookie for this thread's suspended switch
// continuation. This is deliberately thread-owned, not CPU-owned:
// after KeSwitchContext() resumes a saved KiSchedule() continuation, the
// resumed thread must restore the IF/IRQL state captured when that same
// thread switched out.
uintptr_t SwitchLockCookie;
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
// Signaled (manual-reset) when the thread exits.
// Allocated in KeCreateThread, signaled in KeExitThread before teardown.
// Waiters block on this through waitable thread handles.
EVENT* ExitEvent;
void* FpuContext;
uint8_t FpuStorage[528];
// Mutable diagnostic description. This is thread-owned state rather than
// process metadata because independent worker threads may carry distinct roles.
SPINLOCK NameLock;
char Name[NTL_THREAD_NAME_MAX];
// Scheduler accounting. QuantaConsumed is kept in the assembly-stable
// portion of THREAD for layout compatibility; the dynamic scheduler uses
// these fields for per-dispatch budget and CPU-burst tracking.
uint32_t QuantumTicks;
uint32_t QuantumRemaining;
uint32_t CpuBurstTicks;
uint32_t LastRunTicks;
uint32_t RunningCpu;
uint32_t SuspendCount;
// Priority inheritance ownership is scheduler-owned. Mutex wait-list
// membership remains protected by the mutex spinlock.
LIST_ENTRY OwnedMutexList;
struct MUTEX* WaitingMutex;
// Scheduler hardening state. Queued means membership in a scheduler ready
// queue only; wait-object membership is tracked by WaitListHead. Idle
// threads are CPU-local and never enter a global ready queue.
bool Queued;
bool IsIdleThread;
uint16_t SchedulerFlags;
uint32_t LastCpu;
uint64_t AffinityMask;
// Set by wait primitives before blocking and completed by the object
// that wakes the thread. Keep this field after the assembly-visible
// context-switch fields: ke_switch.asm hard-codes offsets through
// FpuContext.
KSTATUS WaitStatus;
LIST_ENTRY* WaitListHead;
bool AlertableWait;
LIST_ENTRY TimeoutLink;
bool TimeoutArmed;
uint64_t TimeoutTick;
KSTATUS TimeoutStatus;
// Optional cleanup hook for waits that use auxiliary wait blocks.
KE_WAIT_RUNDOWN_ROUTINE WaitRundownRoutine;
void* WaitRundownContext;
// Kernel APC state. APCs are queued to a target thread and delivered
// when that thread reaches PASSIVE_LEVEL with APC delivery enabled.
LIST_ENTRY ApcQueue;
SPINLOCK ApcLock;
volatile int32_t KernelApcPending;
int32_t KernelApcDisable;
bool ApcDeliveryActive;
// Non-NULL while executing on a syscall/interrupt/fault entry frame.
// The pointed-to frame lives on this thread's kernel stack.
KTRAP_FRAME* TrapFrame;
// User APC state. User APCs are queued by kernel completions, but are
// delivered only on an alertable return to user mode through the registered
// user-mode dispatcher stub.
LIST_ENTRY UserApcQueue;
SPINLOCK UserApcLock;
volatile int32_t UserApcPending;
uintptr_t UserApcDispatcher;
bool UserApcDeliverable;
bool UserApcDeliveryActive;
uintptr_t UserApcFrameUserAddress;
uint64_t UserApcReturnToken;
uint64_t UserApcSavedRip;
uint64_t UserApcSavedRsp;
uint64_t UserApcSavedRflags;
uint64_t UserApcSavedRax;
// User exception dispatch state. The kernel tracks delivery nesting,
// continuability, and the process status to use if user policy declines
// the active exception. User-mode personalities own handler policy.
uint32_t UserExceptionDepth;
uint32_t UserExceptionNoncontinuableMask;
uintptr_t UserExceptionFrameUserAddress;
KSTATUS UserExceptionExitStatus[KE_USER_EXCEPTION_MAX_DEPTH];
// Synchronous kernel work can temporarily retain caller-owned stack or
// process memory. Process termination must wait until that ownership has
// returned instead of freeing the thread underneath an active driver.
volatile int32_t TerminationDeferralCount;
// Set once terminal thread teardown begins. Interrupts may continue to
// run, but interrupt-return scheduling must not abandon this kernel stack
// before KeFinalizeThreadExit and process-exit publication complete.
volatile int32_t ExitInProgress;
};
// Creates and queues a user thread. On success, *out receives a referenced
// THREAD pointer owned by the caller; release it with ObDereferenceObject()
// after copying any needed IDs/state. The live scheduler/thread lifetime is
// separate and remains valid until the thread exits and is reaped.
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
const KE_THREAD_CREATE_PARAMETERS* parameters, THREAD** out);
// Cancels a thread that has never left its initial suspended state. This drops
// the scheduler-owned lifetime reference but leaves the caller's reference
// intact.
KSTATUS KeCancelSuspendedThread(THREAD* thread);
// Creates a kernel-mode-only thread. fn is called directly in ring 0 with
// arg; when fn returns the thread exits automatically. No user stack or TEB
// is allocated. The thread is owned by the system process. On success,
// non-NULL out receives a referenced THREAD pointer owned by the caller;
// release it with ObDereferenceObject() after copying any needed IDs/state or
// after joining caller-owned completion state. The scheduler's live thread
// reference is separate.
__declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
const KE_THREAD_CREATE_PARAMETERS* parameters,
THREAD** out);
// Mark a thread dead and deschedule it permanently.
__declspec(dllexport) void KeExitThread(THREAD* thread, int32_t code);
// Publishes completion, releases per-thread user memory, and removes one dead
// thread from its process live-thread set. Retained handles remain waitable.
void KeFinalizeThreadExit(THREAD* thread);
// Block / unblock primitives used by sync objects.
// Correct wait pattern:
// object lock held
// KePrepareBlockThread(self, object, alertable);
// insert self into object's wait list
// release object lock
// KeCommitBlockThread(self, alertable);
// The split form prevents the classic lost wakeup where an ISR/DPC signals
// the object after the waiter is queued but before the waiter marks itself
// waiting. Alertable waits are explicit at every call site.
void KePrepareBlockThread(THREAD* thread, OBJECT* waitObject, uint8_t alertable);
void KeSetThreadWaitList(THREAD* thread, LIST_ENTRY* waitListHead);
void KeSetThreadWaitRundown(THREAD* thread, KE_WAIT_RUNDOWN_ROUTINE routine, void* context);
void KeClearThreadWaitRundown(THREAD* thread);
void KeRundownThreadWait(THREAD* thread, KSTATUS status);
KSTATUS KeCommitBlockThread(THREAD* thread, uint8_t alertable);
KSTATUS KeBlockThread(THREAD* thread, OBJECT* waitObject, uint8_t alertable);
void KeUnblockThreadWithStatus(THREAD* thread, KSTATUS status);
void KeUnblockThreadFromWaitListWithStatus(THREAD* thread, KSTATUS status);
bool KeAbortThreadWait(THREAD* thread, KSTATUS status);
void KeUnblockThread(THREAD* thread);
KSTATUS KeAlertThread(THREAD* thread);
void KiWakeThreadForKernelApc(THREAD* thread);
void KiWakeThreadForUserApc(THREAD* thread);
// Defers process termination while kernel work owns caller-backed storage.
void KeEnterTerminationDeferral(void);
void KeLeaveTerminationDeferral(void);
bool KeThreadTerminationDeferred(const THREAD* thread);
bool KeThreadExitInProgress(const THREAD* thread);
// Assembly stub - implemented in ke_switch.asm.
void KeSwitchContext(THREAD* from, THREAD* to);
__declspec(noreturn) void KeStartInitialThread(THREAD* thread);
void KeInitThreadFpuContext(THREAD* thread);
void KeSetThreadAffinityMask(THREAD* thread, uint64_t affinityMask);
KSTATUS KeSetThreadName(THREAD* thread, const char* name, size_t length);
size_t KeQueryThreadName(THREAD* thread, char* buffer, size_t capacity);
uint64_t KeGetDefaultThreadAffinityMask(void);