276 lines
11 KiB
C
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);
|