add ntfs.sys standalone driver target, change default usb vhd layout formatting to ntfs, implement Fat32CreateDir for directory creation support in the fat32 file system driver, add a dedicated HAL serial output endpoint for COM1 writes in hal_serial.c and subscribe serial to HAL text streams, introduce waitable kernel timers with KeTimerCreate, KeTimerSet, and KeTimerCancel alongside spinlock-protected global timer list initialization and scheduler tick tracking, implement SyscallWriteFileAsync system call for asynchronous non-blocking file writes, refactor NetSocketConnect and NetSocketSendTo to use generic SYS_SOCKET_ADDRESS_K structures instead of being hardcoded to IPv4, update gethostbyname and DNS name resolution to support IPv6 parsing via ResolveNameFamily, update tcpserve.exe with -4 and -6 command-line flags to accept and display IPv6 addresses, append size and version headers to all kernel telemetry and statistics structures including SYS_PMM_STATS, SYS_CPU_STATS, and SYS_SCHEDULER_STATS, update winmm timeGetTime to look up high-resolution hardware frequencies via SysGetTickFrequency, integrate 14 native smoke test applications.

This commit is contained in:
maksw committed 2026-06-26 15:18:48 +02:00
1 parent 2c4a68a75f
commit ed3d065abd
156 files changed
+10509 -2643

No files matched your search

+39 -24
View File
@@ -2,11 +2,11 @@
#include <ntlite/ddi/hal_api.h>
#include <kernel/hal/hal_apic.h>
#include <kernel/hal/hal_output.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <intrin.h>
#pragma intrinsic(_disable)
#include <common/printf.h>
void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w, int h, int stride,
@@ -92,26 +92,41 @@ static uint8_t gSlpTypB = 0;
static int gS5Found = 0;
int debugEnabled = 0;
int _fltused; // MSVC floating-point helper anchor
// Implements the hal putchar operation.
static void hal_putchar(char c) {
gApi.Screen.PrintChar(c, 0xCCCCCC, 0x012456);
}
// Implements the dbg Print operation.
void HalDbgPrint(const char* fmt, ...) {
if (!debugEnabled) return;
int _fltused; // MSVC floating-point helper anchor.
// Writes a formatted early HAL diagnostic through the output router.
void HalDbgPrint(const char* fmt, ...) {
if (!debugEnabled || !fmt || !gApi.Output.Write)
return;
char line[1024];
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
int count = vsnprintf_(line, sizeof(line), fmt, args);
va_end(args);
size_t length = 0;
if (count > 0) {
length = (size_t)count;
if (length >= sizeof(line))
length = sizeof(line) - 1u;
}
HAL_OUTPUT_RECORD record;
record.Stream = HAL_OUTPUT_STREAM_DEBUG;
record.Flags = 0;
record.Foreground = 0x00CCCCCCu;
record.Background = 0x00012456u;
record.Buffer = line;
record.Length = length;
gApi.Output.Write(&record);
}
static void *HalPhysToVirt(uint64_t phys) {
return (void *)(phys + gPhysBase);
}
// FADT (Fixed ACPI Description Table) offsets used here. ACPI 2.0+
// FADT (Fixed ACPI Description Table) offsets used here. ACPI 2.0+
// systems may put the real DSDT and PM1 control registers in the extended
// X_* fields, so do not rely only on the legacy 32-bit fields.
#define FADT_DSDT_OFFSET 40u
@@ -155,7 +170,7 @@ static uint16_t AcpiReadGasIoPort(const void* table, uint32_t tableLen, uint32_t
memcpy(&gas, (const uint8_t*)table + off, sizeof(gas));
// 0x01 == System I/O. HalShutdown currently writes PM1_CNT with outw(),
// 0x01 == System I/O. HalShutdown currently writes PM1_CNT with outw(),
// so ignore MMIO GAS entries until the HAL grows MMIO register access here.
if (gas.AddressSpaceId != 0x01 || gas.Address == 0 || gas.Address > 0xFFFF)
return 0;
@@ -257,7 +272,7 @@ static int AmlReadInteger(const uint8_t** pp, const uint8_t* end, uint8_t* out)
return 1;
}
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values. Real
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values. Real
// firmware commonly encodes the name as "\\_S5_" instead of just "_S5_", and
// package integers may be ZeroOp/OneOp/Byte/Word/DWord/QWord constants.
static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) {
@@ -343,7 +358,6 @@ static void ParseFadt(const ACPISDTHeader* header) {
}
}
// Implements the add Cpu Record operation.
static void HalAddCpuRecord(uint32_t uid, uint32_t lapicId, uint32_t flags) {
if (gCpuTopology.CpuCount >= HAL_MAX_CPUS)
@@ -470,7 +484,7 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
}
// Parse HPET.
else if (memcmp(header->Signature, "HPET", 4) == 0) {
// HPET base address block is located at offset 44
// HPET base address block is located at offset 44
// (which is 8 bytes past the 36-byte ACPISDTHeader).
gHpetPhys = *(uint64_t*)((uint8_t*)header + 44) & ~0xFFFULL;
}
@@ -479,13 +493,13 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
// HalShutdown - ACPI S5 soft power-off. Does not return.
static void HalShutdown(void) {
// Silence! Stop the world!
_disable();
// Refuse further maskable interrupts before entering firmware power state.
(void)HalDisableInterrupts();
if (!gS5Found || !gPm1aCntBlk) {
// Refuse to guess. Guessing SLP_TYP is exactly how real hardware gets
// Refuse to guess. Guessing SLP_TYP is exactly how real hardware gets
// put into S3/S4 sleep instead of S5 soft-off.
while (true) __halt();
HalStopProcessor();
}
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10).
@@ -495,8 +509,7 @@ static void HalShutdown(void) {
__outword(gPm1aCntBlk, valA);
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
// Die!
while (true) __halt();
HalStopProcessor();
}
__declspec(dllexport) KSTATUS HalGetProcessorTopology(HAL_CPU_TOPOLOGY* out) {
@@ -542,14 +555,16 @@ __declspec(dllexport) KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
gApi.PhysToVirt = HalPhysToVirt;
debugEnabled = initParams->Debug;
// Screen first so DbgPrint works immediately.
HalOutputInitialize(&gApi.Output);
HalSerialInitialize(&gApi.Output);
// Screen follows serial so diagnostics still work without a framebuffer.
HalScreenInitialize(initParams->AllocatePool, initParams->FramebufferBase + initParams->PhysicalWindowBase,
(int)initParams->FramebufferWidth,
(int)initParams->FramebufferHeight,
(int)initParams->FramebufferStride,
&gApi.Screen);
printf_set_putchar(hal_putchar);
HalScreenRegisterOutputEndpoint(&gApi.Output);
HalDbgPrint("HAL: screen %dx%d\n",
initParams->FramebufferWidth, initParams->FramebufferHeight);
+142
View File
@@ -0,0 +1,142 @@
// HAL output router. This file fans typed text streams out to registered physical and runtime endpoints.
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/hal/hal_output.h>
#define HAL_OUTPUT_ENDPOINT_CAPACITY 8u
typedef struct HAL_OUTPUT_SLOT {
HAL_OUTPUT_ENDPOINT Endpoint;
volatile long Published;
} HAL_OUTPUT_SLOT;
static HAL_OUTPUT_SLOT gOutputSlots[HAL_OUTPUT_ENDPOINT_CAPACITY];
static volatile long gOutputRegistrationLock;
static volatile long long gOutputRecordCount;
static volatile long long gOutputByteCount;
static volatile long long gOutputEndpointWriteCount;
static volatile long long gOutputEmergencyRecordCount;
static volatile long gOutputEndpointCount;
// Acquires the endpoint registration lock.
static void HalOutputAcquireRegistrationLock(void) {
while (_InterlockedCompareExchange(&gOutputRegistrationLock, 1, 0) != 0)
_mm_pause();
}
// Releases the endpoint registration lock.
static void HalOutputReleaseRegistrationLock(void) {
_InterlockedExchange(&gOutputRegistrationLock, 0);
}
// Registers one output endpoint in the fixed endpoint table.
static KSTATUS HalOutputRegisterEndpoint(const HAL_OUTPUT_ENDPOINT* endpoint, uint32_t* endpointIdOut) {
if (!endpoint || !endpointIdOut || !endpoint->Name || !endpoint->Write ||
endpoint->StreamMask == 0 || (endpoint->StreamMask & ~HAL_OUTPUT_STREAM_ALL) != 0)
return STATUS_INVALID_PARAMETER;
KSTATUS status = STATUS_NO_MEMORY;
HalOutputAcquireRegistrationLock();
for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) {
HAL_OUTPUT_SLOT* slot = &gOutputSlots[i];
if (_InterlockedCompareExchange(&slot->Published, 0, 0) != 0)
continue;
slot->Endpoint = *endpoint;
_ReadWriteBarrier();
_InterlockedExchange(&slot->Published, 1);
_InterlockedIncrement(&gOutputEndpointCount);
*endpointIdOut = i;
status = STATUS_SUCCESS;
break;
}
HalOutputReleaseRegistrationLock();
return status;
}
// Replaces the stream subscription mask for a registered endpoint.
static KSTATUS HalOutputSetEndpointStreams(uint32_t endpointId, uint32_t streamMask) {
if (endpointId >= HAL_OUTPUT_ENDPOINT_CAPACITY ||
(streamMask & ~HAL_OUTPUT_STREAM_ALL) != 0)
return STATUS_INVALID_PARAMETER;
HAL_OUTPUT_SLOT* slot = &gOutputSlots[endpointId];
if (_InterlockedCompareExchange(&slot->Published, 0, 0) == 0)
return STATUS_NOT_FOUND;
_InterlockedExchange((volatile long*)&slot->Endpoint.StreamMask, (long)streamMask);
return STATUS_SUCCESS;
}
// Fans one text record out to every subscribed endpoint.
static void HalOutputWrite(const HAL_OUTPUT_RECORD* record) {
if (!record || !record->Buffer || record->Length == 0 ||
(record->Stream & HAL_OUTPUT_STREAM_ALL) == 0 ||
(record->Stream & (record->Stream - 1u)) != 0)
return;
_InterlockedIncrement64(&gOutputRecordCount);
_InterlockedExchangeAdd64(&gOutputByteCount, (long long)record->Length);
if (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY)
_InterlockedIncrement64(&gOutputEmergencyRecordCount);
for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) {
HAL_OUTPUT_SLOT* slot = &gOutputSlots[i];
if (_InterlockedCompareExchange(&slot->Published, 0, 0) == 0)
continue;
uint32_t streams = (uint32_t)_InterlockedCompareExchange(
(volatile long*)&slot->Endpoint.StreamMask, 0, 0);
if ((streams & record->Stream) == 0)
continue;
if ((record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) &&
!(slot->Endpoint.Capabilities & HAL_OUTPUT_ENDPOINT_PANIC_SAFE))
continue;
HAL_OUTPUT_ENDPOINT_WRITE write = slot->Endpoint.Write;
void* context = slot->Endpoint.Context;
if (!write)
continue;
write(context, record);
_InterlockedIncrement64(&gOutputEndpointWriteCount);
}
}
// Returns a snapshot of output router counters.
static void HalOutputQueryStats(HAL_OUTPUT_STATS* statsOut) {
if (!statsOut)
return;
memset(statsOut, 0, sizeof(*statsOut));
statsOut->RecordCount = (uint64_t)_InterlockedCompareExchange64(&gOutputRecordCount, 0, 0);
statsOut->ByteCount = (uint64_t)_InterlockedCompareExchange64(&gOutputByteCount, 0, 0);
statsOut->EndpointWriteCount = (uint64_t)_InterlockedCompareExchange64(&gOutputEndpointWriteCount, 0, 0);
statsOut->EmergencyRecordCount = (uint64_t)_InterlockedCompareExchange64(&gOutputEmergencyRecordCount, 0, 0);
statsOut->EndpointCount = (uint32_t)_InterlockedCompareExchange(&gOutputEndpointCount, 0, 0);
}
// Initializes the fixed HAL output endpoint table.
void HalOutputInitialize(HAL_OUTPUT_ROUTER* output) {
if (!output)
return;
memset(gOutputSlots, 0, sizeof(gOutputSlots));
gOutputRegistrationLock = 0;
gOutputRecordCount = 0;
gOutputByteCount = 0;
gOutputEndpointWriteCount = 0;
gOutputEmergencyRecordCount = 0;
gOutputEndpointCount = 0;
memset(output, 0, sizeof(*output));
output->RegisterEndpoint = HalOutputRegisterEndpoint;
output->SetEndpointStreams = HalOutputSetEndpointStreams;
output->Write = HalOutputWrite;
output->QueryStats = HalOutputQueryStats;
output->SerialEndpointId = HAL_OUTPUT_ENDPOINT_INVALID;
output->BootFramebufferEndpointId = HAL_OUTPUT_ENDPOINT_INVALID;
}
+42
View File
@@ -0,0 +1,42 @@
// HAL processor control exports. This file owns interrupt masking and processor halt instructions.
#include <stdint.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#pragma intrinsic(_disable)
#pragma intrinsic(_enable)
// Masks interrupts and returns the previous RFLAGS value.
__declspec(dllexport) uintptr_t HalDisableInterrupts(void) {
uintptr_t flags = __readeflags();
_disable();
return flags;
}
// Restores a previously saved RFLAGS value.
__declspec(dllexport) void HalRestoreInterrupts(uintptr_t flags) {
__writeeflags(flags);
}
// Enables maskable interrupts.
__declspec(dllexport) void HalEnableInterrupts(void) {
_enable();
}
// Enables interrupts and halts until an interrupt arrives.
__declspec(dllexport) void HalWaitForInterrupt(void) {
_enable();
__halt();
}
// Halts once without changing interrupt state.
__declspec(dllexport) void HalHalt(void) {
__halt();
}
// Masks interrupts and permanently stops the current processor.
__declspec(dllexport) _Noreturn void HalStopProcessor(void) {
_disable();
for (;;)
__halt();
}
+30 -8
View File
@@ -5,9 +5,9 @@
// each byte is the leftmost pixel.
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/mm_virt.h>
#include <kernel/hal/hal_output.h>
static uint32_t* gFb = (uint32_t *)0;
static uint32_t* gBackbuffer = (uint32_t *)0;
@@ -142,7 +142,7 @@ static void ScreenScroll(int lines) {
memmove(shadow, shadow + lines * gStride,
(size_t)(gHeight - lines) * rowBytes);
// Fill the revealed rows with the console background. Do not memset here:
// Fill the revealed rows with the console background. Do not memset here:
// the background color is not zero.
uint32_t* fill = shadow + (gHeight - lines) * gStride;
for (int y = 0; y < lines; y++) {
@@ -170,11 +170,6 @@ static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
int cols = gWidth / FONT_W;
int rows = gHeight / FONT_H;
// Mirror to serial.
while ((__inbyte(0x3FD) & 0x20) == 0);
if (c == '\n') __outbyte(0x3F8, '\r');
__outbyte(0x3F8, c);
if (c == '\n') {
gCursorX = 0;
gCursorY++;
@@ -198,7 +193,7 @@ static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
}
// Mouse cursor – 12×19 monochrome arrow sprite
// 0 = transparent 1 = black outline 2 = white fill
// 0 = transparent 1 = black outline 2 = white fill
// The hotspot is at (0,0) (top-left pixel = tip of the arrow).
#define CURSOR_W 16
#define CURSOR_H 25
@@ -318,6 +313,33 @@ void HalScreenEraseCursor(void) {
gCursorDrawY = -1;
}
// Writes one HAL output record through the boot framebuffer text console.
static void HalScreenWriteOutputRecord(void* context, const HAL_OUTPUT_RECORD* record) {
(void)context;
if (!record || !record->Buffer)
return;
for (size_t i = 0; i < record->Length; i++)
ScreenPrintChar(record->Buffer[i], record->Foreground, record->Background);
}
// Registers the boot framebuffer text endpoint.
void HalScreenRegisterOutputEndpoint(HAL_OUTPUT_ROUTER* output) {
if (!output || !output->RegisterEndpoint || !gFb || gWidth <= 0 || gHeight <= 0)
return;
HAL_OUTPUT_ENDPOINT endpoint;
endpoint.Name = "boot-framebuffer";
endpoint.StreamMask = HAL_OUTPUT_STREAM_ALL;
endpoint.Capabilities = HAL_OUTPUT_ENDPOINT_PANIC_SAFE;
endpoint.Write = HalScreenWriteOutputRecord;
endpoint.Context = 0;
uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID;
if (KSUCCESS(output->RegisterEndpoint(&endpoint, &endpointId)))
output->BootFramebufferEndpointId = endpointId;
}
// Populate the HAL_SCREEN vtable.
void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w, int h, int stride,
HAL_SCREEN* out) {
+78
View File
@@ -0,0 +1,78 @@
// HAL serial output endpoint. This file owns polling COM1 writes and subscribes serial to HAL text streams.
#include <stdint.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/hal/hal_output.h>
#define HAL_COM1_DATA_PORT 0x03F8u
#define HAL_COM1_STATUS_PORT 0x03FDu
#define HAL_COM1_TX_READY 0x20u
static volatile long gSerialWriteLock;
// Waits until COM1 can accept one byte.
static void HalSerialWaitForTransmit(void) {
while ((__inbyte(HAL_COM1_STATUS_PORT) & HAL_COM1_TX_READY) == 0)
_mm_pause();
}
// Writes one byte to COM1.
static void HalSerialWriteByte(uint8_t value) {
HalSerialWaitForTransmit();
__outbyte(HAL_COM1_DATA_PORT, value);
}
// Acquires the normal serial writer lock.
static void HalSerialAcquireWriteLock(void) {
while (_InterlockedCompareExchange(&gSerialWriteLock, 1, 0) != 0)
_mm_pause();
}
// Releases the normal serial writer lock.
static void HalSerialReleaseWriteLock(void) {
_InterlockedExchange(&gSerialWriteLock, 0);
}
// Writes one output record through polling COM1.
static void HalSerialWriteRecord(void* context, const HAL_OUTPUT_RECORD* record) {
(void)context;
if (!record || !record->Buffer)
return;
bool emergency = (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) != 0;
uintptr_t interruptFlags = 0;
if (!emergency) {
interruptFlags = HalDisableInterrupts();
HalSerialAcquireWriteLock();
}
for (size_t i = 0; i < record->Length; i++) {
char c = record->Buffer[i];
if (c == '\n')
HalSerialWriteByte('\r');
HalSerialWriteByte((uint8_t)c);
}
if (!emergency) {
HalSerialReleaseWriteLock();
HalRestoreInterrupts(interruptFlags);
}
}
// Registers the polling serial endpoint.
void HalSerialInitialize(HAL_OUTPUT_ROUTER* output) {
if (!output || !output->RegisterEndpoint)
return;
HAL_OUTPUT_ENDPOINT endpoint;
endpoint.Name = "serial0";
endpoint.StreamMask = HAL_OUTPUT_STREAM_ALL;
endpoint.Capabilities = HAL_OUTPUT_ENDPOINT_PANIC_SAFE;
endpoint.Write = HalSerialWriteRecord;
endpoint.Context = 0;
uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID;
if (KSUCCESS(output->RegisterEndpoint(&endpoint, &endpointId)))
output->SerialEndpointId = endpointId;
}
+10 -4
View File
@@ -81,6 +81,11 @@ static uint64_t QueryTicks(void) {
return (baseTicks1 * TIMER_PERIOD_MS * 1000ull) + subQuantumUs;
}
// Returns the frequency used by QueryTicks.
static uint64_t QueryTickFrequency(void) {
return 1000000ull;
}
// Implements the stall operation.
static void Stall(uint32_t us) {
// Priority 1: Use HPET (Highest Precision).
@@ -224,9 +229,10 @@ KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *o
// after enabling their local APIC.
HalTimerConfigureCurrentCpu();
out->QueryTicks = QueryTicks;
out->Stall = Stall;
out->Isr = TimerIsr;
out->IrqNumber = 0;
out->QueryTicks = QueryTicks;
out->QueryTickFrequency = QueryTickFrequency;
out->Stall = Stall;
out->Isr = TimerIsr;
out->IrqNumber = 0;
return STATUS_SUCCESS;
}
@@ -10,7 +10,6 @@ void HalApicUnmaskIrq(uint8_t irq);
void HalApicSendEoi(void);
void HalApicInitialize(uintptr_t lapicPhys, uintptr_t ioapicPhys);
#define HAL_MAX_CPUS 32u
typedef struct HAL_CPU_INFO {
@@ -0,0 +1,14 @@
// HAL output router internals. This file connects built-in endpoint drivers to the public HAL output table.
#pragma once
#include <ntlite/ddi/hal_api.h>
// Initializes the fixed HAL output endpoint table.
void HalOutputInitialize(HAL_OUTPUT_ROUTER* output);
// Registers the polling serial endpoint.
void HalSerialInitialize(HAL_OUTPUT_ROUTER* output);
// Registers the boot framebuffer text endpoint.
void HalScreenRegisterOutputEndpoint(HAL_OUTPUT_ROUTER* output);
+16 -11
View File
@@ -12,6 +12,7 @@
#include <kernel/ke_spinlock.h>
#include <ntlite/string_view.h>
#include <ntlite/dirent.h>
#include <ntlite/file_stat.h>
// Kernel/filesystem code uses the public directory-entry ABI directly.
typedef SYS_DIR_ENTRY KDIR_ENTRY;
@@ -26,6 +27,7 @@ typedef void (*IO_FS_ASYNC_COMPLETE)(void* completeContext,
size_t information);
// Filesystem-level cache and traversal statistics.
// Drivers map private metadata work onto these generic counters.
typedef struct IO_FILESYSTEM_STATS {
uint64_t PathLookups;
uint64_t PathCacheHits;
@@ -34,14 +36,8 @@ typedef struct IO_FILESYSTEM_STATS {
uint64_t PathCacheInvalidations;
uint64_t PathCacheGeneration;
uint64_t DirectorySearches;
uint64_t DirectoryClustersRead;
uint64_t DirectoryBlocksRead;
uint64_t DirectoryEntriesScanned;
uint64_t FatChainCacheLookups;
uint64_t FatChainCacheHits;
uint64_t FatChainCacheMisses;
uint64_t FatChainCacheStores;
uint64_t FatChainCacheInvalidations;
uint64_t FatChainWalkSteps;
uint64_t DataReadRuns;
uint64_t DataReadSectors;
uint64_t DataReadRunMerges;
@@ -107,6 +103,12 @@ typedef struct IO_FILESYSTEM {
// Return the file size in bytes.
uint64_t (*GetSize)(struct IO_FILESYSTEM* fs, void* fileCtx);
// Query metadata for an open file or path.
KSTATUS (*QueryInfo)(struct IO_FILESYSTEM* fs, void* fileCtx,
SYS_FILE_STAT* out);
KSTATUS (*Stat)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path,
SYS_FILE_STAT* out);
// Return a stable file identity for caches that outlive one open handle.
KSTATUS (*QueryIdentity)(struct IO_FILESYSTEM* fs, void* fileCtx,
IO_FILE_IDENTITY* out);
@@ -120,8 +122,6 @@ typedef struct IO_FILESYSTEM {
// Optional driver-private data.
void* DriverCtx;
// Directory enumeration (optional - may be NULL if not supported).
// Open a directory iterator for 'dirPath' (relative or drive-prefixed).
// Returns an opaque per-iteration driver cookie in *dirCtx.
KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs,
@@ -137,7 +137,6 @@ typedef struct IO_FILESYSTEM {
void (*CloseDir)(struct IO_FILESYSTEM* fs, void* dirCtx);
} IO_FILESYSTEM;
// IO_STATUS_BLOCK - completion status visible to async callers.
typedef struct IO_STATUS_BLOCK {
volatile KSTATUS Status;
@@ -152,6 +151,7 @@ typedef struct IO_ASYNC_FILE_REQUEST_OBJECT IO_ASYNC_FILE_REQUEST_OBJECT;
// FILE_OBJECT - the kernel object returned to callers.
#define IO_FILE_FLAG_NO_CACHE 0x00000001u
#define IO_FILE_FLAG_SKIP_ZERO_EXTEND 0x00000002u
#define IO_FILE_FLAG_APPEND 0x00000004u
typedef struct FILE_OBJECT {
OBJECT Header; // must be first
@@ -227,7 +227,12 @@ __declspec(dllexport) KSTATUS IoRemoveDir (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
__declspec(dllexport) KSTATUS IoGetFileStat(FILE_OBJECT* file, SYS_FILE_STAT* out);
__declspec(dllexport) KSTATUS IoStatFile(SYS_STRING_VIEW path, SYS_FILE_STAT* out);
__declspec(dllexport) KSTATUS IoQueryFileIdentity(FILE_OBJECT* file, IO_FILE_IDENTITY* out);
__declspec(dllexport) KSTATUS IoLockFile(FILE_OBJECT* file, struct PROCESS* owner, uint32_t mode, uint64_t offset, uint64_t length, uint64_t timeoutMilliseconds);
__declspec(dllexport) KSTATUS IoUnlockFile(FILE_OBJECT* file, struct PROCESS* owner, uint64_t offset, uint64_t length);
__declspec(dllexport) void IoReleaseFileLocksForProcess(struct PROCESS* owner);
__declspec(dllexport) void IoCloseFile(FILE_OBJECT* file);
// Directory enumeration API.
@@ -237,4 +242,4 @@ __declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir);
// Current Working Directory.
__declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size);
__declspec(dllexport) KSTATUS IoRealPath(SYS_STRING_VIEW path, char* buffer, size_t bufferSize, size_t* requiredSizeOut);
+1 -4
View File
@@ -35,10 +35,7 @@ typedef struct KUSER_APC KUSER_APC;
typedef struct SYS_USER_APC_FRAME_K {
uint64_t Magic;
uint64_t OriginalRip;
uint64_t OriginalRsp;
uint64_t OriginalRflags;
uint64_t OriginalRax;
uint64_t ReturnToken;
uint64_t Routine;
uint64_t NormalContext;
uint64_t SystemArgument1;
+2 -2
View File
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <ntlite/status.h>
#include <ntlite/string_view.h>
#include <ntlite/ddi/hal_api.h>
#define KE_DEBUG_CONSOLE 0x00000001u
#define KE_DEBUG_SELFTEST 0x00000002u
@@ -22,8 +23,7 @@
#define KE_DEBUG_VM_ASSERTS 0x00000400u
#define KE_DEBUG_SYSCALL_ASSERTS 0x00000800u
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags);
void KeSetDebugConsoleWriter(void (*PrintPtr)(char, uint32_t, uint32_t));
void KeInitDebug(const HAL_API* hal, uint32_t flags);
uint32_t KeGetDebugFlags(void);
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
-2
View File
@@ -16,7 +16,6 @@
#endif
#include <ntlite/ddi/display.h>
typedef struct KE_CONSOLE_INFO {
uint32_t Columns;
uint32_t Rows;
@@ -55,7 +54,6 @@ DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo);
DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row);
DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut);
DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut);
void KeDisplayDebugPrintChar(char c, uint32_t fg, uint32_t bg);
DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount);
DISPLAY_API KSTATUS KeDisplaySync(void);
DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo);
+4 -3
View File
@@ -23,7 +23,8 @@ __declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE
__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons);
INPUT_KEY KeInputKbPoll(void);
KSTATUS KeInputWaitKey(uint8_t alertable);
KSTATUS KeInputWaitKey(uint8_t alertable, uint64_t timeoutMs);
void KeInputTimerTick(void);
bool KeInputIsKeyDown(INPUT_KEY key);
bool KeInputPeekEvent(INPUT_KEY_RECORD* record);
bool KeInputReadEvent(INPUT_KEY_RECORD* record);
@@ -33,8 +34,8 @@ INPUT_KEY KeInputReadKey(void);
char KeInputReadChar(void);
uint32_t KeInputGetModifiers(void);
bool KeInputMousePoll(void);
bool KeInputMouseRead(int* dx, int* dy, uint8_t* buttons);
bool KeInputMousePeek(INPUT_MOUSE_EVENT* event);
bool KeInputMouseRead(INPUT_MOUSE_EVENT* event);
void KeInputMouseGetPosition(int* x, int* y);
void KeInputMouseSetPosition(int x, int y);
void KeInputMouseSetBounds(int w, int h);
+2 -2
View File
@@ -1,7 +1,7 @@
// Kernel ke irp declarations shared by implementation files and kernel clients.
#pragma once
// ke_irp.h - I/O Request Packet and remove-lock primitives
// ke_irp.h - I/O Request Packet and remove-lock primitives
// IRPs are stack-based. A caller builds an IRP with one or more
// IO_STACK_LOCATION entries, submits it to a DEVICE_OBJECT, and the I/O
@@ -142,7 +142,7 @@ __declspec(dllexport) KSTATUS IoAcquireRemoveLock(IO_REMOVE_LOCK* lock, IRP* irp
__declspec(dllexport) void IoReleaseRemoveLock(IO_REMOVE_LOCK* lock, IRP* irp);
__declspec(dllexport) void IoReleaseRemoveLockAndWait(IO_REMOVE_LOCK* lock, IRP* removeIrp);
// IRP_QUEUE (per-device, driver-internal).
// IRP_QUEUE (per-device, driver-internal).
typedef struct IRP_QUEUE {
SPINLOCK Lock;
+12 -4
View File
@@ -17,6 +17,10 @@
#define PROCESS_MODULE_MAX 16
#define PROCESS_CWD_MAX 512
#define KE_PROCESS_FLAG_SYSTEM 0x00000001u
#define KE_PROCESS_FLAG_IDLE 0x00000002u
#define KE_PROCESS_FLAG_TERMINATION_PROTECTED 0x00000004u
#define HANDLE_RIGHT_READ 0x00000001u
#define HANDLE_RIGHT_WRITE 0x00000002u
#define HANDLE_RIGHT_WAIT 0x00000004u
@@ -24,6 +28,7 @@
#define HANDLE_RIGHT_DUPLICATE 0x00000010u
#define HANDLE_RIGHT_QUERY 0x00000020u
#define HANDLE_RIGHT_TERMINATE 0x00000040u
#define HANDLE_RIGHT_LOCK 0x00000080u
#define HANDLE_RIGHT_ALL 0xFFFFFFFFu
typedef struct HANDLE_ENTRY {
@@ -42,6 +47,7 @@ typedef struct PROCESS {
OBJECT Header; // must be first
uint32_t ProcessId;
uint32_t Flags;
char Name[PROCESS_NAME_MAX]; // UTF-8
char CurrentDirectory[PROCESS_CWD_MAX];
@@ -59,7 +65,7 @@ typedef struct PROCESS {
HANDLE_ENTRY Handles[HANDLE_TABLE_SIZE];
SPINLOCK HandleLock;
int ExitCode;
int32_t ExitCode;
volatile long Terminating;
volatile long Exited;
volatile long ResourcesReleased;
@@ -101,11 +107,12 @@ KSTATUS KeSetupUserProcessEnvironment(PROCESS* process,
SYS_STRING_VIEW imagePath,
SYS_STRING_VIEW commandLine,
SYS_STRING_VIEW currentDirectory);
void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode);
KSTATUS KeExitProcess(PROCESS* process, int32_t exitCode);
KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode);
void KeCompleteProcessExitIfLastThread(PROCESS* process);
int KeIsProcessTerminating(PROCESS* process);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
int KeIsProcessTerminationProtected(PROCESS* process);
KSTATUS KeGetProcessExitCode(PROCESS* process, int32_t* exitCodeOut);
// Handle table operations.
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
@@ -114,6 +121,7 @@ void KeCloseHandle(PROCESS* process, uint32_t handle);
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
OBJECT* KeLookupHandleWithRights(PROCESS* process, uint32_t handle, uint32_t requiredRights);
uint32_t KeGetHandleRights(PROCESS* process, uint32_t handle);
KSTATUS KeDuplicateHandleToProcess(PROCESS* sourceProcess, uint32_t sourceHandle, PROCESS* targetProcess, uint32_t desiredRights, uint32_t duplicateFlags, uint32_t* targetHandleOut);
// Return the global process list head (kernel-internal).
LIST_ENTRY* KeGetProcessList(void);
+15 -11
View File
@@ -1,34 +1,38 @@
// Kernel ke queue declarations shared by implementation files and kernel clients.
#pragma once
// ke_queue.h - fixed-depth message queue
// Processes share an address space, so a pointer in Param[] is valid
// on both sides. The queue is only a synchronisation and ordering tool.
// Fixed-depth message queue.
// Processes share an address space, so a pointer in Param[] is valid on both sides.
// The queue orders fixed control records and does not copy pointed-to payloads.
#include <stdint.h>
#include <stdbool.h>
#include <kernel/ob_object.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ke_list.h>
#include <kernel/ke_event.h>
#include <ntlite/status.h>
#define QUEUE_MESSAGE_SIZE 64 // bytes, fixed
#define QUEUE_MESSAGE_SIZE 56 // Bytes, fixed ABI copy size.
#define QUEUE_DEPTH 256
typedef struct QUEUE_MESSAGE {
uint32_t Type;
uint32_t Flags;
uint64_t Param[6]; // 48 bytes of payload
} QUEUE_MESSAGE; // total: 64 bytes
uint64_t Param[6]; // 48 bytes of payload.
} QUEUE_MESSAGE;
typedef char QUEUE_MESSAGE_SIZE_CHECK[
(sizeof(QUEUE_MESSAGE) == QUEUE_MESSAGE_SIZE) ? 1 : -1
];
typedef struct QUEUE {
OBJECT Header;
SPINLOCK Lock;
QUEUE_MESSAGE Ring[QUEUE_DEPTH];
uint32_t Head, Tail, Count;
LIST_ENTRY SendWaiters;
LIST_ENTRY RecvWaiters;
EVENT* NotEmptyEvent;
EVENT* NotFullEvent;
} QUEUE;
KSTATUS KeQueueCreate(QUEUE **out);
KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, bool blocking);
KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, bool blocking);
KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds);
KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds);
-1
View File
@@ -15,7 +15,6 @@ KSTATUS KeStartApplicationProcessors(void);
// park loop into their idle threads.
KSTATUS KeAdmitApplicationProcessorsToScheduler(void);
// Decode CPUID topology and build per-KCPU package/core/SMT sibling masks.
// KeStartApplicationProcessors calls this after MADT enumeration; it is also
// safe to call again after failed AP startup disables a CPU record.
+5 -7
View File
@@ -13,8 +13,8 @@
// Never hold a spinlock across a wait/blocking operation.
#include <stdint.h>
#include <intrin.h>
#pragma intrinsic(_disable)
#include <kernel/ke_irql.h>
#include <ntlite/ddi/hal_api.h>
typedef struct SPINLOCK {
volatile long Locked;
@@ -44,8 +44,7 @@ static __forceinline KIRQL KiSpinlockCookieIrql(uintptr_t cookie) {
// Save RFLAGS, mask interrupts, raise to DISPATCH_LEVEL if needed, acquire.
// If already running at DIRQL, the higher IRQL is preserved.
static __forceinline uintptr_t KeAcquireSpinlock(SPINLOCK* sl) {
uintptr_t flags = __readeflags();
_disable();
uintptr_t flags = HalDisableInterrupts();
KIRQL oldIrql = KeGetCurrentIrql();
if (oldIrql < DISPATCH_LEVEL)
@@ -64,13 +63,12 @@ static __forceinline void KeReleaseSpinlock(SPINLOCK* sl, uintptr_t cookie) {
_InterlockedExchange(&sl->Locked, 0);
KeLowerIrql(oldIrql);
__writeeflags(flags);
HalRestoreInterrupts(flags);
}
// Implements the try Acquire Spinlock operation.
static __forceinline int KeTryAcquireSpinlock(SPINLOCK* sl, uintptr_t* outCookie) {
uintptr_t flags = __readeflags();
_disable();
uintptr_t flags = HalDisableInterrupts();
KIRQL oldIrql = KeGetCurrentIrql();
if (oldIrql < DISPATCH_LEVEL)
@@ -82,7 +80,7 @@ static __forceinline int KeTryAcquireSpinlock(SPINLOCK* sl, uintptr_t* outCookie
}
KeLowerIrql(oldIrql);
__writeeflags(flags);
HalRestoreInterrupts(flags);
return 0;
}
+8 -2
View File
@@ -147,11 +147,17 @@ struct THREAD {
uintptr_t UserApcDispatcher;
uint8_t UserApcDeliverable;
uint8_t UserApcDeliveryActive;
uintptr_t UserApcFrameUserAddress;
uint64_t UserApcReturnToken;
uint64_t UserApcSavedRip;
uint64_t UserApcSavedRsp;
uint64_t UserApcSavedRflags;
uint64_t UserApcSavedRax;
};
// Creates and queues a user thread. On success, *out receives a referenced
// 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
// 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,
THREAD_PRIORITY priority, THREAD** out);
+35
View File
@@ -0,0 +1,35 @@
// Kernel timer object declarations shared by implementation files and kernel clients.
#pragma once
// ke_timer.h - waitable one-shot and periodic timers.
#include <stdint.h>
#include <kernel/ke_event.h>
#include <kernel/ke_list.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ob_object.h>
#include <ntlite/status.h>
typedef struct KTIMER {
OBJECT Header;
EVENT* Event;
LIST_ENTRY Link;
uint64_t DueTick;
uint64_t PeriodTicks;
uint8_t Active;
uint8_t Linked;
} KTIMER;
// Creates an unset waitable timer object.
KSTATUS KeTimerCreate(KTIMER** out);
// Arms or rearms a timer with a relative due time and optional period.
KSTATUS KeTimerSet(KTIMER* timer, uint64_t dueTimeMilliseconds, uint64_t periodMilliseconds);
// Cancels a timer and clears any retained signal.
KSTATUS KeTimerCancel(KTIMER* timer);
// Returns the event that backs timer waits.
EVENT* KeTimerWaitEvent(KTIMER* timer);
// Services expired timers from the scheduler tick path.
void KeTimerTick(void);
+2 -2
View File
@@ -5,9 +5,9 @@
#include <kernel/ke_list.h>
#include <ntlite/status.h>
// Work items are caller-owned embedded objects. They may be queued from
// Work items are caller-owned embedded objects. They may be queued from
// interrupt/DPC/spinlock context and run later on a persistent system worker
// thread at PASSIVE_LEVEL. Queueing the same item while it is already queued
// thread at PASSIVE_LEVEL. Queueing the same item while it is already queued
// is coalesced.
typedef void (*KWORK_ROUTINE)(void* context);
+2 -1
View File
@@ -43,5 +43,6 @@ KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, u
// Called by KernelMain to register all modules from BOOT_INFO so that
// LdrGetExport can find them.
struct BOOT_INFO; // forward declaration
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
void LdrRegisterModule(const char* name, uintptr_t base);
const char* LdrFindKernelModuleByAddress(uintptr_t address, uintptr_t* baseOut, size_t* sizeOut);
+6 -7
View File
@@ -19,18 +19,17 @@
// The physical direct map is a kernel-only alias used to reach page-table frames and early physical memory.
#define PHYS_MAP_BASE 0xFFFF880000000000ULL
// Page-table telemetry separates cumulative operations from the current number
// of 4 KiB leaf mappings owned by this layer. Huge boot mappings may exist but
// are not counted as leaves here.
typedef enum MM_PTE_QUERY_PURPOSE {
MmPteQueryGeneric = 0,
MmPteQueryUserProbe,
MmPteQueryHeap,
MmPteQueryVad,
MmPteQueryDebug,
MmPteQuerySelfTest,
MmPteQueryPurposeCount,
MmPteQueryUserProbe = 1,
MmPteQueryHeap = 2,
MmPteQueryVad = 3,
MmPteQueryDebug = 4,
MmPteQuerySelfTest = 5,
MmPteQueryPurposeCount = 6,
} MM_PTE_QUERY_PURPOSE;
typedef struct MM_PTE_STATS {
+6 -6
View File
@@ -26,16 +26,16 @@ typedef struct SOCKET_OBJECT {
} SOCKET_OBJECT;
KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKET_OBJECT** out);
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS NetSocketListen(SOCKET_OBJECT* socket, uint32_t backlog);
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut);
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut);
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut);
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
KSTATUS NetSocketShutdown(SOCKET_OBJECT* socket, uint32_t how);
KSTATUS NetSocketSetOption(SOCKET_OBJECT* socket, uint32_t option, const void* value, size_t valueSize);
KSTATUS NetSocketGetOption(SOCKET_OBJECT* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returnedEventsOut);
KSTATUS NetResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut);
KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
+1
View File
@@ -27,6 +27,7 @@ typedef enum OBJECT_TYPE {
ObjectTypeSurface = 16,
ObjectTypeInputQueue = 17,
ObjectTypeImageSection = 18,
ObjectTypeTimer = 19,
} OBJECT_TYPE;
typedef struct OBJECT {
+138 -117
View File
@@ -1,120 +1,141 @@
// Kernel syscall nr declarations shared by implementation files and kernel clients.
// Kernel syscall number declarations shared by implementation files and kernel clients.
#pragma once
// syscall_nr.h
// INT 0x40 calling convention, MS x64:
// RAX = syscall number
// RCX, RDX, R8, R9 = args 0-3
// R10, R11 = args 4-5 (loaded by SYSCALL_STUB6)
// RAX = return value.
#define SYS_EXIT_PROCESS 0
#define SYS_EXIT_THREAD 1
#define SYS_CREATE_PROCESS 2
#define SYS_WAIT_SINGLE 3
#define SYS_GET_PROCESS_EXIT_CODE 4
#define SYS_KILL_PROCESS 5
#define SYS_GET_PROCESS_LIST 6
#define SYS_GET_PROCESS_PID 7
#define SYS_CREATE_THREAD 8
#define SYS_RETIRED_WAIT_THREAD 9
#define SYS_GET_CURRENT_TID 10
#define SYS_GET_CURRENT_PID 11
#define SYS_SET_THREAD_PRIORITY 12
#define SYS_YIELD 13
#define SYS_SLEEP 14
#define SYS_SET_USER_APC_DISPATCHER 15
#define SYS_APC_RETURN 16
#define SYS_ALLOC_VM 17
#define SYS_FREE_VM 18
#define SYS_PROTECT_VM 19
#define SYS_QUERY_VM 20
#define SYS_CLOSE_HANDLE 21
#define SYS_CREATE_MUTEX 22
#define SYS_RETIRED_ACQUIRE_MUTEX 23
#define SYS_TRY_ACQUIRE_MUTEX 24
#define SYS_RELEASE_MUTEX 25
#define SYS_CREATE_SEM 26
#define SYS_RETIRED_WAIT_SEM 27
#define SYS_TRY_WAIT_SEM 28
#define SYS_SIGNAL_SEM 29
#define SYS_CREATE_EVENT 30
#define SYS_SIGNAL_EVENT 31
#define SYS_CLEAR_EVENT 32
#define SYS_RETIRED_WAIT_EVENT 33
#define SYS_CREATE_QUEUE 34
#define SYS_SEND_QUEUE 35
#define SYS_RECV_QUEUE 36
#define SYS_PUBLISH_QUEUE 37
#define SYS_OPEN_NAMED_QUEUE 38
#define SYS_DUPLICATE_QUEUE 39
#define SYS_GET_NAMED_QUEUE_OWNER_PID 40
#define SYS_OPEN_FILE 41
#define SYS_CREATE_FILE 42
#define SYS_READ_FILE 43
#define SYS_WRITE_FILE 44
#define SYS_READ_FILE_ASYNC 45
#define SYS_WRITE_FILE_ASYNC 46
#define SYS_CANCEL_IO 47
#define SYS_SEEK_FILE 48
#define SYS_GET_FILE_POS 49
#define SYS_GET_FILE_SIZE 50
#define SYS_TRUNCATE_FILE 51
#define SYS_DELETE_FILE 52
#define SYS_RENAME_FILE 53
#define SYS_OPEN_DIR 54
#define SYS_READ_DIR 55
#define SYS_CREATE_DIR 56
#define SYS_REMOVE_DIR 57
#define SYS_SET_CWD 58
#define SYS_GET_CWD 59
#define SYS_WRITE_CONSOLE 60
#define SYS_GET_CONSOLE_INFO 61
#define SYS_SET_CONSOLE_CURSOR 62
#define SYS_FILL_CONSOLE_CHARACTER 63
#define SYS_FILL_CONSOLE_ATTRIBUTE 64
#define SYS_PUT_SCREEN_CHAR 65
#define SYS_CLEAR_SCREEN 66
#define SYS_GET_SCREEN_SIZE 67
#define SYS_QUERY_DISPLAY_INFO 68
#define SYS_POLL_KEYBOARD 69
#define SYS_IS_KEY_DOWN 70
#define SYS_READ_KEYBOARD_CHAR 71
#define SYS_READ_KEY 72
#define SYS_GET_KEY_MODIFIERS 73
#define SYS_POLL_MOUSE 74
#define SYS_READ_MOUSE 75
#define SYS_GET_MOUSE_POS 76
#define SYS_GET_TICK 77
#define SYS_PRINT_DEBUG 78
#define SYS_QUERY_TRAP_FRAME 79
#define SYS_CREATE_SOCKET 80
#define SYS_BIND_SOCKET 81
#define SYS_LISTEN_SOCKET 82
#define SYS_ACCEPT_SOCKET 83
#define SYS_CONNECT_SOCKET 84
#define SYS_SEND_SOCKET 85
#define SYS_RECV_SOCKET 86
#define SYS_SHUTDOWN_SOCKET 87
#define SYS_SEND_TO_SOCKET 88
#define SYS_RECV_FROM_SOCKET 89
#define SYS_RESOLVE_HOST 90
#define SYS_SET_SOCKET_OPTION 91
#define SYS_GET_SOCKET_OPTION 92
#define SYS_POLL 93
#define SYS_GET_MEMORY_STATUS 94
#define SYS_QUERY_KERNEL_STATS 95
#define SYS_GET_SYSTEM_TIME 96
#define SYS_CREATE_FILE_MAPPING 97
#define SYS_MAP_FILE 98
#define SYS_CREATE_SURFACE 99
#define SYS_MAP_SURFACE 100
#define SYS_QUERY_SURFACE 101
#define SYS_PRESENT_SURFACE 102
#define SYS_GET_THREAD_LIST 103
#define SYS_READ_INPUT_EVENT 104
#define SYS_PEEK_INPUT_EVENT 105
#define SYS_WAIT_MANY 106
#define SYS_OPEN_INPUT_QUEUE 107
#define SYS_FLUSH_FILE 108
#define SYS_MAX 109
// INT 0x40 calling convention, MS x64.
// RAX selects the syscall number. RCX, RDX, R8, and R9 carry args 0-3.
// R10 and R11 carry args 4-5 and are loaded by six-argument stubs.
// RAX carries the return value.
// Process, threads, waits, and APC.
#define SYS_EXIT_PROCESS 0
#define SYS_EXIT_THREAD 1
#define SYS_CREATE_PROCESS 2
#define SYS_OPEN_PROCESS 3
#define SYS_GET_PROCESS_EXIT_CODE 4
#define SYS_KILL_PROCESS 5
#define SYS_GET_PROCESS_LIST 6
#define SYS_GET_PROCESS_PID 7
#define SYS_CREATE_THREAD 8
#define SYS_GET_THREAD_LIST 9
#define SYS_GET_CURRENT_TID 10
#define SYS_GET_CURRENT_PID 11
#define SYS_SET_THREAD_PRIORITY 12
#define SYS_YIELD 13
#define SYS_SLEEP 14
#define SYS_WAIT_SINGLE 15
#define SYS_WAIT_MANY 16
#define SYS_SET_USER_APC_DISPATCHER 17
#define SYS_APC_RETURN 18
// Virtual memory, mappings, and handles.
#define SYS_ALLOC_VM 19
#define SYS_FREE_VM 20
#define SYS_PROTECT_VM 21
#define SYS_QUERY_VM 22
#define SYS_CLOSE_HANDLE 23
#define SYS_DUPLICATE_HANDLE 24
#define SYS_CREATE_FILE_MAPPING 25
#define SYS_MAP_FILE 26
// Synchronization objects.
#define SYS_CREATE_MUTEX 27
#define SYS_TRY_ACQUIRE_MUTEX 28
#define SYS_RELEASE_MUTEX 29
#define SYS_CREATE_SEM 30
#define SYS_TRY_WAIT_SEM 31
#define SYS_SIGNAL_SEM 32
#define SYS_CREATE_EVENT 33
#define SYS_SIGNAL_EVENT 34
#define SYS_CLEAR_EVENT 35
// Queues and named queues.
#define SYS_CREATE_QUEUE 36
#define SYS_SEND_QUEUE 37
#define SYS_RECV_QUEUE 38
#define SYS_PUBLISH_QUEUE 39
#define SYS_OPEN_NAMED_QUEUE 40
#define SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS 41
// Files, directories, and current directory.
#define SYS_OPEN_FILE 42
#define SYS_READ_FILE 43
#define SYS_WRITE_FILE 44
#define SYS_READ_FILE_ASYNC 45
#define SYS_WRITE_FILE_ASYNC 46
#define SYS_CANCEL_IO 47
#define SYS_SEEK_FILE 48
#define SYS_GET_FILE_POS 49
#define SYS_GET_FILE_SIZE 50
#define SYS_GET_FILE_STAT 51
#define SYS_STAT_FILE 52
#define SYS_TRUNCATE_FILE 53
#define SYS_FLUSH_FILE 54
#define SYS_DELETE_FILE 55
#define SYS_RENAME_FILE 56
#define SYS_LOCK_FILE 57
#define SYS_UNLOCK_FILE 58
#define SYS_OPEN_DIR 59
#define SYS_READ_DIR 60
#define SYS_CREATE_DIR 61
#define SYS_REMOVE_DIR 62
#define SYS_SET_CWD 63
#define SYS_REAL_PATH 64
// Console, display, and surfaces.
#define SYS_WRITE_CONSOLE 65
#define SYS_GET_CONSOLE_INFO 66
#define SYS_SET_CONSOLE_CURSOR 67
#define SYS_FILL_CONSOLE_CHARACTER 68
#define SYS_FILL_CONSOLE_ATTRIBUTE 69
#define SYS_PUT_SCREEN_CHAR 70
#define SYS_CLEAR_SCREEN 71
#define SYS_GET_SCREEN_SIZE 72
#define SYS_QUERY_DISPLAY_INFO 73
#define SYS_CREATE_SURFACE 74
#define SYS_MAP_SURFACE 75
#define SYS_QUERY_SURFACE 76
#define SYS_PRESENT_SURFACE 77
// Input.
#define SYS_POLL_KEYBOARD 78
#define SYS_IS_KEY_DOWN 79
#define SYS_READ_KEYBOARD_CHAR 80
#define SYS_READ_KEY 81
#define SYS_GET_KEY_MODIFIERS 82
#define SYS_OPEN_INPUT_QUEUE 83
#define SYS_READ_INPUT_EVENT 84
#define SYS_PEEK_INPUT_EVENT 85
#define SYS_PEEK_MOUSE 86
#define SYS_READ_MOUSE 87
#define SYS_GET_MOUSE_POS 88
// Time, debug, and system information.
#define SYS_CREATE_TIMER 89
#define SYS_SET_TIMER 90
#define SYS_CANCEL_TIMER 91
#define SYS_GET_TICK 92
#define SYS_GET_TICK_FREQUENCY 93
#define SYS_GET_SYSTEM_TIME 94
#define SYS_PRINT_DEBUG 95
#define SYS_QUERY_TRAP_FRAME 96
#define SYS_GET_MEMORY_STATUS 97
#define SYS_QUERY_KERNEL_STATS 98
// Native sockets and polling.
#define SYS_CREATE_SOCKET 99
#define SYS_BIND_SOCKET 100
#define SYS_LISTEN_SOCKET 101
#define SYS_ACCEPT_SOCKET 102
#define SYS_CONNECT_SOCKET 103
#define SYS_SEND_SOCKET 104
#define SYS_RECV_SOCKET 105
#define SYS_SHUTDOWN_SOCKET 106
#define SYS_SEND_TO_SOCKET 107
#define SYS_RECV_FROM_SOCKET 108
#define SYS_RESOLVE_HOST 109
#define SYS_SET_SOCKET_OPTION 110
#define SYS_GET_SOCKET_OPTION 111
#define SYS_POLL 112
#define SYS_MAX 113
-1
View File
@@ -253,7 +253,6 @@ __declspec(dllexport) KSTATUS IoRegisterPhysicalDevice(DEVICE_OBJECT* pdo) {
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS IoSetDeviceName(DEVICE_OBJECT* device,
SYS_STRING_VIEW name) {
if (!device || !SysSvIsValid(name))
+384 -25
View File
@@ -67,6 +67,167 @@ static SPINLOCK gAsyncFileQueueLock = SPINLOCK_INIT;
static LIST_ENTRY gAsyncFileQueue;
static SEMAPHORE* gAsyncFileSemaphore;
typedef struct IO_FILE_LOCK_ENTRY {
LIST_ENTRY Link;
IO_FILE_IDENTITY Identity;
PROCESS* Owner;
uint64_t Offset;
uint64_t Length;
uint32_t Mode;
} IO_FILE_LOCK_ENTRY;
typedef struct IO_FILE_LOCK_WAITER {
LIST_ENTRY Link;
IO_FILE_IDENTITY Identity;
PROCESS* Owner;
uint64_t Offset;
uint64_t Length;
uint32_t Mode;
EVENT* Event;
uint8_t Armed;
} IO_FILE_LOCK_WAITER;
static SPINLOCK gFileLockLock = SPINLOCK_INIT;
static LIST_ENTRY gFileLocks;
static LIST_ENTRY gFileLockWaiters;
static uint8_t gFileLocksInitialized;
// Initializes the global advisory file-lock lists on first use.
static void KiIoEnsureFileLockState(void) {
uintptr_t flags = KeAcquireSpinlock(&gFileLockLock);
if (!gFileLocksInitialized) {
LIST_INIT(&gFileLocks);
LIST_INIT(&gFileLockWaiters);
gFileLocksInitialized = 1;
}
KeReleaseSpinlock(&gFileLockLock, flags);
}
// Compares the stable filesystem identity fields used for lock ownership.
static bool KiIoSameFileIdentity(const IO_FILE_IDENTITY* a, const IO_FILE_IDENTITY* b) {
if (!a || !b) return false;
return a->VolumeId == b->VolumeId &&
a->FileId == b->FileId &&
a->Generation == b->Generation;
}
// Returns the exclusive end offset for a public lock range.
static uint64_t KiIoFileLockEnd(uint64_t offset, uint64_t length) {
if (length == SYS_FILE_LOCK_TO_EOF)
return UINT64_MAX;
uint64_t end = offset + length;
if (end < offset)
return UINT64_MAX;
return end;
}
// Tests whether two byte ranges overlap.
static bool KiIoFileLockRangesOverlap(uint64_t aOffset, uint64_t aLength,
uint64_t bOffset, uint64_t bLength) {
uint64_t aEnd = KiIoFileLockEnd(aOffset, aLength);
uint64_t bEnd = KiIoFileLockEnd(bOffset, bLength);
return aOffset < bEnd && bOffset < aEnd;
}
// Tests whether one existing lock conflicts with a requested lock.
static bool KiIoFileLockConflicts(const IO_FILE_LOCK_ENTRY* lock,
const IO_FILE_IDENTITY* identity,
PROCESS* owner,
uint32_t mode,
uint64_t offset,
uint64_t length) {
if (!lock || !identity || !owner)
return false;
if (lock->Owner == owner)
return false;
if (!KiIoSameFileIdentity(&lock->Identity, identity))
return false;
if (!KiIoFileLockRangesOverlap(lock->Offset, lock->Length, offset, length))
return false;
if (lock->Mode == SYS_FILE_LOCK_EXCLUSIVE || mode == SYS_FILE_LOCK_EXCLUSIVE)
return true;
return false;
}
// Tests whether the requested lock is currently blocked by another process.
static bool KiIoFileLockHasConflictLocked(const IO_FILE_IDENTITY* identity,
PROCESS* owner,
uint32_t mode,
uint64_t offset,
uint64_t length) {
LIST_ENTRY* head = &gFileLocks;
for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) {
IO_FILE_LOCK_ENTRY* lock = CONTAINING_RECORD(node, IO_FILE_LOCK_ENTRY, Link);
if (KiIoFileLockConflicts(lock, identity, owner, mode, offset, length))
return true;
}
return false;
}
// Returns the scheduler tick value used by finite file-lock waits.
static uint64_t KiIoFileLockReadSchedulerTick(void) {
KE_SCHED_STATS stats;
KeGetSchedulerStats(&stats);
return stats.Ticks;
}
// Converts a millisecond timeout to at least one scheduler tick.
static uint64_t KiIoFileLockTimeoutTicks(uint64_t timeoutMilliseconds) {
uint64_t ticks = timeoutMilliseconds / SCHED_TICK_MS;
if ((timeoutMilliseconds % SCHED_TICK_MS) != 0)
ticks++;
return ticks ? ticks : 1;
}
// Computes an absolute scheduler-tick deadline for a finite timeout.
static uint64_t KiIoFileLockTimeoutDeadline(uint64_t timeoutMilliseconds) {
uint64_t now = KiIoFileLockReadSchedulerTick();
uint64_t ticks = KiIoFileLockTimeoutTicks(timeoutMilliseconds);
uint64_t deadline = now + ticks;
return deadline < now ? UINT64_MAX : deadline;
}
// Computes the remaining wait time for a finite timeout deadline.
static uint64_t KiIoFileLockRemainingMilliseconds(uint64_t deadlineTick) {
uint64_t now = KiIoFileLockReadSchedulerTick();
if (deadlineTick <= now)
return 0;
uint64_t ticks = deadlineTick - now;
if (ticks > UINT64_MAX / SCHED_TICK_MS)
return UINT64_MAX;
return ticks * SCHED_TICK_MS;
}
// Waits for a file-lock conflict to change.
static KSTATUS KiIoWaitForFileLockChange(EVENT* event, uint64_t timeoutMilliseconds, uint64_t deadlineTick) {
if (!event)
return STATUS_INVALID_PARAMETER;
uint64_t waitMilliseconds = timeoutMilliseconds;
if (timeoutMilliseconds != SYS_WAIT_TIMEOUT_INFINITE) {
waitMilliseconds = KiIoFileLockRemainingMilliseconds(deadlineTick);
if (waitMilliseconds == 0)
return STATUS_TIMEOUT;
}
EVENT* events[1] = { event };
return KeWaitForMultipleEvents(events, 1, 0, waitMilliseconds, (uint32_t*)0);
}
// Wakes waiters whose requested file identity may have become available.
static void KiIoSignalFileLockWaitersLocked(const IO_FILE_IDENTITY* identity) {
if (!identity || !gFileLocksInitialized)
return;
LIST_ENTRY* head = &gFileLockWaiters;
for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) {
IO_FILE_LOCK_WAITER* waiter = CONTAINING_RECORD(node, IO_FILE_LOCK_WAITER, Link);
if (waiter->Armed && waiter->Event && KiIoSameFileIdentity(&waiter->Identity, identity))
KeSignalEvent(waiter->Event);
}
}
// Implements the io Async File Request Destroy operation.
static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
IO_ASYNC_FILE_REQUEST_OBJECT* req =
@@ -416,7 +577,6 @@ __declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OBJ
return req ? &req->Header : (OBJECT*)0;
}
// Returns the wait event owned by an async file request.
__declspec(dllexport) EVENT* IoGetAsyncFileRequestWaitEvent(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? req->WaitEvent : (EVENT*)0;
@@ -488,25 +648,14 @@ static size_t KiFormatCwd(uint8_t drive, SYS_STRING_VIEW rel, char* buf, size_t
buf[0] = (char)('0' + drive);
buf[1] = ':';
buf[2] = '/';
if (rel.Length) memcpy(buf + 3, rel.Data, rel.Length);
for (size_t i = 0; i < rel.Length; i++) {
char c = rel.Data[i];
buf[3 + i] = (c == '\\') ? '/' : c;
}
buf[3 + rel.Length] = '\0';
return needed;
}
// Returns cwd.
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
if (!buf || size == 0) return STATUS_INVALID_PARAMETER;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
size_t needed = KiFormatCwd(proc->CurrentDrive, SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength), buf, size);
KeReleaseSpinlock(&proc->CwdLock, flags);
return needed && needed <= size ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
}
// Implements the sv Has Drive Root operation.
static int KiSvHasDriveRoot(SYS_STRING_VIEW path) {
return path.Length >= 3 &&
@@ -615,6 +764,29 @@ static KSTATUS KiResolvePathV(SYS_STRING_VIEW input, KRESOLVED_PATH* out) {
return STATUS_SUCCESS;
}
// Builds a canonical native path using the kernel-owned process CWD.
__declspec(dllexport) KSTATUS IoRealPath(SYS_STRING_VIEW path, char* buffer, size_t bufferSize, size_t* requiredSizeOut) {
if (requiredSizeOut) *requiredSizeOut = 0;
if (path.Length && !path.Data) return STATUS_INVALID_PARAMETER;
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!buffer && bufferSize != 0) return STATUS_INVALID_PARAMETER;
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
char formatted[SYS_CWD_MAX];
size_t required = KiFormatCwd(resolved.Drive, resolved.Path, formatted, sizeof(formatted));
if (!required || required > sizeof(formatted)) return STATUS_BUFFER_TOO_SMALL;
if (requiredSizeOut) *requiredSizeOut = required;
if (!buffer) return STATUS_SUCCESS;
if (bufferSize < required) return STATUS_BUFFER_TOO_SMALL;
memcpy(buffer, formatted, required);
return STATUS_SUCCESS;
}
// Updates cwd.
__declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path) {
if (path.Length && !path.Data) return STATUS_INVALID_PARAMETER;
@@ -722,14 +894,8 @@ __declspec(dllexport) void IoQueryFilesystemStats(IO_FILESYSTEM_STATS* out) {
if (one.PathCacheGeneration > out->PathCacheGeneration)
out->PathCacheGeneration = one.PathCacheGeneration;
out->DirectorySearches += one.DirectorySearches;
out->DirectoryClustersRead += one.DirectoryClustersRead;
out->DirectoryBlocksRead += one.DirectoryBlocksRead;
out->DirectoryEntriesScanned += one.DirectoryEntriesScanned;
out->FatChainCacheLookups += one.FatChainCacheLookups;
out->FatChainCacheHits += one.FatChainCacheHits;
out->FatChainCacheMisses += one.FatChainCacheMisses;
out->FatChainCacheStores += one.FatChainCacheStores;
out->FatChainCacheInvalidations += one.FatChainCacheInvalidations;
out->FatChainWalkSteps += one.FatChainWalkSteps;
out->DataReadRuns += one.DataReadRuns;
out->DataReadSectors += one.DataReadSectors;
out->DataReadRunMerges += one.DataReadRunMerges;
@@ -785,7 +951,6 @@ __declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out
return STATUS_SUCCESS;
}
static uint64_t KiAlignUpPage64(uint64_t value) {
return (value + PAGE_SIZE - 1u) & ~(uint64_t)(PAGE_SIZE - 1u);
}
@@ -976,7 +1141,7 @@ __declspec(dllexport) KSTATUS IoWriteFile(FILE_OBJECT* file, const void* buf,
if (!file || !buf || !bytesWritten) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&file->Lock);
uint64_t offset = file->Offset;
uint64_t offset = (file->Flags & IO_FILE_FLAG_APPEND) ? file->Size : file->Offset;
KeReleaseSpinlock(&file->Lock, flags);
KSTATUS s = IoWriteFileAt(file, offset, buf, size, bytesWritten);
@@ -1130,6 +1295,45 @@ __declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file) {
return size;
}
// Queries metadata for an open file object.
__declspec(dllexport) KSTATUS IoGetFileStat(FILE_OBJECT* file, SYS_FILE_STAT* out) {
if (!file || !out) return STATUS_INVALID_PARAMETER;
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
out->Type = SYS_FILE_TYPE_REGULAR;
out->LinkCount = 1;
out->FileSize = IoGetFileSize(file);
if (!file->Fs || !file->Fs->QueryInfo)
return STATUS_SUCCESS;
KSTATUS s = file->Fs->QueryInfo(file->Fs, file->FileCtx, out);
if (KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&file->Lock);
file->Size = out->FileSize;
KeReleaseSpinlock(&file->Lock, flags);
}
return s;
}
// Queries metadata for a filesystem path.
__declspec(dllexport) KSTATUS IoStatFile(SYS_STRING_VIEW path, SYS_FILE_STAT* out) {
if ((!path.Data && path.Length) || path.Length == 0 || !out) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Stat) return STATUS_NOT_SUPPORTED;
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
return fs->Stat(fs, resolved.Path, out);
}
// Queries the filesystem identity for a file object.
__declspec(dllexport) KSTATUS IoQueryFileIdentity(FILE_OBJECT* file, IO_FILE_IDENTITY* out) {
if (!file || !out)
@@ -1140,6 +1344,161 @@ __declspec(dllexport) KSTATUS IoQueryFileIdentity(FILE_OBJECT* file, IO_FILE_IDE
return file->Fs->QueryIdentity(file->Fs, file->FileCtx, out);
}
// Acquires an advisory process-owned byte-range lock for one file identity.
__declspec(dllexport) KSTATUS IoLockFile(FILE_OBJECT* file, PROCESS* owner, uint32_t mode,
uint64_t offset, uint64_t length,
uint64_t timeoutMilliseconds) {
if (!file || !owner) return STATUS_INVALID_PARAMETER;
if (mode != SYS_FILE_LOCK_SHARED && mode != SYS_FILE_LOCK_EXCLUSIVE) return STATUS_INVALID_PARAMETER;
if (length == 0) return STATUS_INVALID_PARAMETER;
if (timeoutMilliseconds != 0 && !KeCanWait()) return STATUS_INVALID_IRQL;
IO_FILE_IDENTITY identity;
KSTATUS s = IoQueryFileIdentity(file, &identity);
if (!KSUCCESS(s)) return s;
KiIoEnsureFileLockState();
uint64_t deadlineTick = 0;
if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_WAIT_TIMEOUT_INFINITE)
deadlineTick = KiIoFileLockTimeoutDeadline(timeoutMilliseconds);
EVENT* waitEvent = (EVENT*)0;
IO_FILE_LOCK_WAITER waiter;
memset(&waiter, 0, sizeof(waiter));
for (;;) {
IO_FILE_LOCK_ENTRY* entry = (IO_FILE_LOCK_ENTRY*)MmAllocatePoolTag(sizeof(*entry), MM_POOL_TAG('f', 'l', 'c', 'k'));
if (!entry) {
if (waitEvent) ObDereferenceObject(&waitEvent->Header);
return STATUS_NO_MEMORY;
}
uintptr_t flags = KeAcquireSpinlock(&gFileLockLock);
if (!KiIoFileLockHasConflictLocked(&identity, owner, mode, offset, length)) {
entry->Identity = identity;
entry->Owner = owner;
entry->Offset = offset;
entry->Length = length;
entry->Mode = mode;
LIST_INIT(&entry->Link);
ListInsertTail(&gFileLocks, &entry->Link);
KeReleaseSpinlock(&gFileLockLock, flags);
if (waitEvent) ObDereferenceObject(&waitEvent->Header);
return STATUS_SUCCESS;
}
KeReleaseSpinlock(&gFileLockLock, flags);
MmFreePool(entry);
if (timeoutMilliseconds == 0) {
if (waitEvent) ObDereferenceObject(&waitEvent->Header);
return STATUS_WOULD_BLOCK;
}
if (!waitEvent) {
s = KeEventCreate(EventAutoReset, 0, &waitEvent);
if (!KSUCCESS(s)) return s;
waiter.Identity = identity;
waiter.Owner = owner;
waiter.Offset = offset;
waiter.Length = length;
waiter.Mode = mode;
waiter.Event = waitEvent;
LIST_INIT(&waiter.Link);
} else {
KeClearEvent(waitEvent);
}
flags = KeAcquireSpinlock(&gFileLockLock);
if (!KiIoFileLockHasConflictLocked(&identity, owner, mode, offset, length)) {
KeReleaseSpinlock(&gFileLockLock, flags);
continue;
}
waiter.Armed = 1;
ListInsertTail(&gFileLockWaiters, &waiter.Link);
KeReleaseSpinlock(&gFileLockLock, flags);
s = KiIoWaitForFileLockChange(waitEvent, timeoutMilliseconds, deadlineTick);
flags = KeAcquireSpinlock(&gFileLockLock);
if (waiter.Armed) {
ListRemove(&waiter.Link);
waiter.Armed = 0;
}
KeReleaseSpinlock(&gFileLockLock, flags);
if (!KSUCCESS(s)) {
ObDereferenceObject(&waitEvent->Header);
return s;
}
}
}
// Releases this process's overlapping advisory locks for one file identity.
__declspec(dllexport) KSTATUS IoUnlockFile(FILE_OBJECT* file, PROCESS* owner, uint64_t offset, uint64_t length) {
if (!file || !owner || length == 0) return STATUS_INVALID_PARAMETER;
IO_FILE_IDENTITY identity;
KSTATUS s = IoQueryFileIdentity(file, &identity);
if (!KSUCCESS(s)) return s;
KiIoEnsureFileLockState();
bool released = false;
uintptr_t flags = KeAcquireSpinlock(&gFileLockLock);
LIST_ENTRY* head = &gFileLocks;
LIST_ENTRY* node = head->Fwd;
while (node != head) {
LIST_ENTRY* next = node->Fwd;
IO_FILE_LOCK_ENTRY* lock = CONTAINING_RECORD(node, IO_FILE_LOCK_ENTRY, Link);
if (lock->Owner == owner && KiIoSameFileIdentity(&lock->Identity, &identity) &&
KiIoFileLockRangesOverlap(lock->Offset, lock->Length, offset, length)) {
ListRemove(&lock->Link);
MmFreePool(lock);
released = true;
}
node = next;
}
if (released)
KiIoSignalFileLockWaitersLocked(&identity);
KeReleaseSpinlock(&gFileLockLock, flags);
return released ? STATUS_SUCCESS : STATUS_NOT_FOUND;
}
// Releases every advisory file lock owned by one exiting process.
__declspec(dllexport) void IoReleaseFileLocksForProcess(PROCESS* owner) {
if (!owner) return;
KiIoEnsureFileLockState();
bool released = false;
uintptr_t flags = KeAcquireSpinlock(&gFileLockLock);
LIST_ENTRY* head = &gFileLocks;
LIST_ENTRY* node = head->Fwd;
while (node != head) {
LIST_ENTRY* next = node->Fwd;
IO_FILE_LOCK_ENTRY* lock = CONTAINING_RECORD(node, IO_FILE_LOCK_ENTRY, Link);
if (lock->Owner == owner) {
ListRemove(&lock->Link);
MmFreePool(lock);
released = true;
}
node = next;
}
if (released) {
LIST_ENTRY* waiterHead = &gFileLockWaiters;
for (LIST_ENTRY* waiterNode = waiterHead->Fwd; waiterNode != waiterHead; waiterNode = waiterNode->Fwd) {
IO_FILE_LOCK_WAITER* waiter = CONTAINING_RECORD(waiterNode, IO_FILE_LOCK_WAITER, Link);
if (waiter->Armed && waiter->Event)
KeSignalEvent(waiter->Event);
}
}
KeReleaseSpinlock(&gFileLockLock, flags);
}
// Releases file.
__declspec(dllexport) void IoCloseFile(FILE_OBJECT* file) {
if (file) ObDereferenceObject(&file->Header);
+128 -17
View File
@@ -6,10 +6,55 @@
#include <kernel/ke_spinlock.h>
#include <kernel/ke_irql.h>
#include <kernel/ke_trap.h>
#include <kernel/ke_debug.h>
#include <kernel/mm_virt.h>
#include <string.h>
#include <intrin.h>
#define KI_USER_CS 0x1BULL
#define KI_USER_SS 0x23ULL
#define KI_RFLAGS_FIXED 0x2ULL
#define KI_RFLAGS_IOPL 0x3000ULL
#define KI_RFLAGS_NT 0x4000ULL
#define KI_RFLAGS_VM 0x20000ULL
static volatile long long gUserApcReturnTokenSeed;
// Returns whether an address is in the canonical user half accepted by IRET.
static bool KiUserAddressValid(uint64_t address) {
return address >= USER_BASE && address < USER_LIMIT;
}
// Returns whether a saved user RFLAGS value can be restored safely.
static bool KiUserRflagsValid(uint64_t rflags) {
if ((rflags & KI_RFLAGS_FIXED) == 0)
return false;
if ((rflags & (KI_RFLAGS_IOPL | KI_RFLAGS_NT | KI_RFLAGS_VM)) != 0)
return false;
return true;
}
// Validates the kernel-owned state that will be restored after a user APC.
static KSTATUS KiValidateUserApcResumeState(uint64_t rip, uint64_t rsp, uint64_t rflags,
uint64_t cs, uint64_t ss) {
if (!KiUserAddressValid(rip) || !KiUserAddressValid(rsp))
return STATUS_ACCESS_DENIED;
if (cs != KI_USER_CS || ss != KI_USER_SS)
return STATUS_ACCESS_DENIED;
if (!KiUserRflagsValid(rflags))
return STATUS_ACCESS_DENIED;
return STATUS_SUCCESS;
}
// Allocates a nonzero token for one active user APC return frame.
static uint64_t KiNextUserApcReturnToken(THREAD* thread, uintptr_t frameAddress) {
uint64_t seed = (uint64_t)_InterlockedIncrement64(&gUserApcReturnTokenSeed);
uint64_t token = seed ^ (uint64_t)(uintptr_t)thread ^ (uint64_t)frameAddress ^ SYS_USER_APC_FRAME_MAGIC;
if (token == 0)
token = SYS_USER_APC_FRAME_MAGIC;
return token;
}
// Initializes apc State.
void KiInitializeApcState(THREAD* thread) {
if (!thread) return;
@@ -25,6 +70,12 @@ void KiInitializeApcState(THREAD* thread) {
thread->UserApcDispatcher = 0;
thread->UserApcDeliverable = 0;
thread->UserApcDeliveryActive = 0;
thread->UserApcFrameUserAddress = 0;
thread->UserApcReturnToken = 0;
thread->UserApcSavedRip = 0;
thread->UserApcSavedRsp = 0;
thread->UserApcSavedRflags = 0;
thread->UserApcSavedRax = 0;
}
__declspec(dllexport) void KeInitializeApc(KAPC* apc,
@@ -184,12 +235,16 @@ void KiDeliverKernelApcs(void) {
thread->ApcDeliveryActive = 0;
}
// Updates user Apc Dispatcher.
__declspec(dllexport) KSTATUS KeSetUserApcDispatcher(void* dispatcher) {
THREAD* thread = KeGetCurrentThread();
if (!thread) return STATUS_FAILURE;
if (!dispatcher) return STATUS_INVALID_PARAMETER;
if (!KiUserAddressValid((uint64_t)(uintptr_t)dispatcher)) return STATUS_ACCESS_DENIED;
KSTATUS s = MmProbeUserRead(dispatcher, 1);
if (!KSUCCESS(s)) return s;
thread->UserApcDispatcher = (uintptr_t)dispatcher;
return STATUS_SUCCESS;
}
@@ -201,6 +256,8 @@ __declspec(dllexport) KSTATUS KeQueueUserApc(THREAD* thread,
void* systemArgument2) {
if (!thread || !routine)
return STATUS_INVALID_PARAMETER;
if (!KiUserAddressValid((uint64_t)(uintptr_t)routine))
return STATUS_ACCESS_DENIED;
if (thread->State == ThreadDead)
return STATUS_OBJECT_DEAD;
@@ -262,24 +319,33 @@ void KiCheckForUserApcDelivery(KTRAP_FRAME* frame) {
if (!apc)
return;
KSTATUS s = KiValidateUserApcResumeState(frame->Rip, frame->Rsp, frame->Rflags,
frame->Cs, frame->Ss);
if (!KSUCCESS(s)) {
DbgPrint("USER/APC: rejected saved return state rip=0x%016llX rsp=0x%016llX rflags=0x%016llX cs=0x%016llX ss=0x%016llX status=%u\n",
frame->Rip, frame->Rsp, frame->Rflags, frame->Cs, frame->Ss, s);
uintptr_t flags = KeAcquireSpinlock(&thread->UserApcLock);
ListInsertTail(&thread->UserApcQueue, &apc->Link);
thread->UserApcPending = 1;
KeReleaseSpinlock(&thread->UserApcLock, flags);
return;
}
uintptr_t oldRsp = (uintptr_t)frame->Rsp;
uintptr_t newRsp = oldRsp - sizeof(SYS_USER_APC_FRAME_K);
newRsp &= ~(uintptr_t)0xFULL;
newRsp -= 8; // make injected dispatcher entry look like a normal call target
newRsp -= 8; // Make injected dispatcher entry look like a normal call target.
SYS_USER_APC_FRAME_K userFrame;
memset(&userFrame, 0, sizeof(userFrame));
userFrame.Magic = SYS_USER_APC_FRAME_MAGIC;
userFrame.OriginalRip = frame->Rip;
userFrame.OriginalRsp = frame->Rsp;
userFrame.OriginalRflags = frame->Rflags;
userFrame.OriginalRax = STATUS_USER_APC;
userFrame.ReturnToken = KiNextUserApcReturnToken(thread, newRsp);
userFrame.Routine = (uint64_t)(uintptr_t)apc->Routine;
userFrame.NormalContext = (uint64_t)(uintptr_t)apc->NormalContext;
userFrame.SystemArgument1 = (uint64_t)(uintptr_t)apc->SystemArgument1;
userFrame.SystemArgument2 = (uint64_t)(uintptr_t)apc->SystemArgument2;
KSTATUS s = CopyToUser((void*)newRsp, &userFrame, sizeof(userFrame));
s = CopyToUser((void*)newRsp, &userFrame, sizeof(userFrame));
if (!KSUCCESS(s)) {
uintptr_t flags = KeAcquireSpinlock(&thread->UserApcLock);
ListInsertTail(&thread->UserApcQueue, &apc->Link);
@@ -288,6 +354,13 @@ void KiCheckForUserApcDelivery(KTRAP_FRAME* frame) {
return;
}
thread->UserApcFrameUserAddress = newRsp;
thread->UserApcReturnToken = userFrame.ReturnToken;
thread->UserApcSavedRip = frame->Rip;
thread->UserApcSavedRsp = frame->Rsp;
thread->UserApcSavedRflags = frame->Rflags;
thread->UserApcSavedRax = STATUS_USER_APC;
frame->Rip = thread->UserApcDispatcher;
frame->Rsp = newRsp;
frame->Rax = STATUS_USER_APC;
@@ -331,32 +404,70 @@ void KiRundownApcState(THREAD* thread) {
thread->UserApcDispatcher = 0;
thread->UserApcDeliverable = 0;
thread->UserApcDeliveryActive = 0;
thread->UserApcFrameUserAddress = 0;
thread->UserApcReturnToken = 0;
thread->UserApcSavedRip = 0;
thread->UserApcSavedRsp = 0;
thread->UserApcSavedRflags = 0;
thread->UserApcSavedRax = 0;
}
// Implements the user Apc Return operation.
__declspec(dllexport) uint64_t KeUserApcReturn(const SYS_USER_APC_FRAME_K* userFrame) {
THREAD* thread = KeGetCurrentThread();
if (!thread)
return STATUS_FAILURE;
if (!thread->UserApcDeliveryActive || !userFrame)
return STATUS_INVALID_PARAMETER;
SYS_USER_APC_FRAME_K frameCopy;
KSTATUS s = CopyFromUser(&frameCopy, userFrame, sizeof(frameCopy));
if (!KSUCCESS(s))
return s;
if (frameCopy.Magic != SYS_USER_APC_FRAME_MAGIC)
if (frameCopy.Magic != SYS_USER_APC_FRAME_MAGIC ||
frameCopy.ReturnToken != thread->UserApcReturnToken ||
(uintptr_t)userFrame != thread->UserApcFrameUserAddress) {
DbgPrint("USER/APC: rejected return frame user=0x%016llX active=0x%016llX token=0x%016llX expected=0x%016llX\n",
(uint64_t)(uintptr_t)userFrame,
(uint64_t)thread->UserApcFrameUserAddress,
frameCopy.ReturnToken,
thread->UserApcReturnToken);
return STATUS_INVALID_PARAMETER;
THREAD* thread = KeGetCurrentThread();
if (!thread)
return STATUS_FAILURE;
}
KTRAP_FRAME* frame = KeGetCurrentTrapFrame();
if (!frame || !KeTrapFrameFromUser(frame) || frame->Kind != KTRAP_KIND_SYSCALL)
return STATUS_FAILURE;
frame->Rip = frameCopy.OriginalRip;
frame->Rsp = frameCopy.OriginalRsp;
frame->Rflags = frameCopy.OriginalRflags;
frame->Rax = frameCopy.OriginalRax;
s = KiValidateUserApcResumeState(thread->UserApcSavedRip,
thread->UserApcSavedRsp,
thread->UserApcSavedRflags,
frame->Cs,
frame->Ss);
if (!KSUCCESS(s)) {
DbgPrint("USER/APC: rejected kernel-owned resume state rip=0x%016llX rsp=0x%016llX rflags=0x%016llX status=%u\n",
thread->UserApcSavedRip,
thread->UserApcSavedRsp,
thread->UserApcSavedRflags,
s);
return s;
}
uint64_t savedRax = thread->UserApcSavedRax;
frame->Rip = thread->UserApcSavedRip;
frame->Rsp = thread->UserApcSavedRsp;
frame->Rflags = thread->UserApcSavedRflags;
frame->Rax = savedRax;
thread->UserApcDeliveryActive = 0;
thread->UserApcDeliverable = 0;
return frameCopy.OriginalRax;
thread->UserApcFrameUserAddress = 0;
thread->UserApcReturnToken = 0;
thread->UserApcSavedRip = 0;
thread->UserApcSavedRsp = 0;
thread->UserApcSavedRflags = 0;
thread->UserApcSavedRax = 0;
return savedRax;
}
+229 -349
View File
@@ -7,46 +7,35 @@
#include <ntlite/ddi/hal_api.h>
#include <kernel/hal/hal_apic.h>
#include <intrin.h>
#pragma intrinsic(_disable)
#pragma intrinsic(_enable)
#include <kernel/ke_debug.h>
#include <kernel/ke_cpu.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ke_process.h>
#include <kernel/ke_thread.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_work.h>
#include <kernel/mm_virt.h>
#include <kernel/ldr_image.h>
#include <kernel/io_file.h>
#include <kernel/ke_system_root.h>
#include <common/printf.h>
int _fltused; // MSVC floating-point helper anchor
int _fltused; // MSVC floating-point helper anchor.
int gDebugEnabled = 0;
static uint32_t gDebugFlags = 0;
void (*PrintChar)(char, uint32_t, uint32_t);
static uint32_t gDebugFlags;
static const HAL_API* gDebugHal;
#define KE_DEBUG_LOG_BUFFER_SIZE (50u * 1024u)
#define KE_DEBUG_SCREEN_BUFFER_SIZE (16u * 1024u)
#define KE_DEBUG_LOG_LINE_SIZE 1024u
#define KE_DEBUG_SCREEN_DRAIN_SIZE 512u
#define KE_DEBUG_LOG_BUFFER_SIZE (50u * 1024u)
#define KE_DEBUG_LOG_LINE_SIZE 1024u
#define KE_FATAL_OUTPUT_LINE_SIZE 2048u
static SPINLOCK gDebugPrintLock = SPINLOCK_INIT;
static char* gDebugLogBuffer;
static size_t gDebugLogSize;
static size_t gDebugLogWrite;
static size_t gDebugLogUsed;
static char* gDebugScreenBuffer;
static size_t gDebugScreenSize;
static size_t gDebugScreenRead;
static size_t gDebugScreenWrite;
static size_t gDebugScreenUsed;
static char* gDebugLogBuffer;
static size_t gDebugLogSize;
static size_t gDebugLogWrite;
static size_t gDebugLogUsed;
static volatile long gDebugLogFlushing;
static volatile long gDebugConsoleOwnsSerial;
static volatile long gDebugConsoleWorkReady;
static KWORK_ITEM gDebugConsoleWorkItem;
// Implements the debug Log Append Locked operation.
// Appends bytes to the in-memory debug log while its lock is held.
static void KiDebugLogAppendLocked(const char* text, size_t len) {
if (!gDebugLogBuffer || gDebugLogSize == 0 || !text || len == 0)
return;
@@ -57,7 +46,8 @@ static void KiDebugLogAppendLocked(const char* text, size_t len) {
}
size_t first = gDebugLogSize - gDebugLogWrite;
if (first > len) first = len;
if (first > len)
first = len;
memcpy(gDebugLogBuffer + gDebugLogWrite, text, first);
if (len > first)
memcpy(gDebugLogBuffer, text + first, len - first);
@@ -69,137 +59,52 @@ static void KiDebugLogAppendLocked(const char* text, size_t len) {
gDebugLogUsed += len;
}
// Implements the debug Screen Append Locked operation.
static void KiDebugScreenAppendLocked(const char* text, size_t len) {
if (!gDebugScreenBuffer || gDebugScreenSize == 0 || !text || len == 0)
// Writes one record through the HAL output router.
static void KiOutputWrite(uint32_t stream, uint32_t flags,
uint32_t foreground, uint32_t background,
const char* text, size_t length) {
if (!gDebugHal || !gDebugHal->Output.Write || !text || length == 0)
return;
if (len >= gDebugScreenSize) {
text += len - (gDebugScreenSize - 1u);
len = gDebugScreenSize - 1u;
}
while (gDebugScreenUsed + len > gDebugScreenSize) {
gDebugScreenRead = (gDebugScreenRead + 1u) % gDebugScreenSize;
gDebugScreenUsed--;
}
size_t first = gDebugScreenSize - gDebugScreenWrite;
if (first > len) first = len;
memcpy(gDebugScreenBuffer + gDebugScreenWrite, text, first);
if (len > first)
memcpy(gDebugScreenBuffer, text + first, len - first);
gDebugScreenWrite = (gDebugScreenWrite + len) % gDebugScreenSize;
gDebugScreenUsed += len;
HAL_OUTPUT_RECORD record;
record.Stream = stream;
record.Flags = flags;
record.Foreground = foreground;
record.Background = background;
record.Buffer = text;
record.Length = length;
gDebugHal->Output.Write(&record);
}
// Implements the debug Screen Drain Locked operation.
static size_t KiDebugScreenDrainLocked(char* out, size_t max) {
if (!out || max == 0 || !gDebugScreenBuffer || gDebugScreenSize == 0 || gDebugScreenUsed == 0)
return 0;
size_t len = gDebugScreenUsed;
if (len > max) len = max;
size_t first = gDebugScreenSize - gDebugScreenRead;
if (first > len) first = len;
memcpy(out, gDebugScreenBuffer + gDebugScreenRead, first);
if (len > first)
memcpy(out + first, gDebugScreenBuffer, len - first);
gDebugScreenRead = (gDebugScreenRead + len) % gDebugScreenSize;
gDebugScreenUsed -= len;
return len;
}
// Ensures the asynchronous screen ring exists before console output starts using it.
static bool KiDebugEnsureScreenBuffer(void) {
if (gDebugScreenBuffer && gDebugScreenSize != 0)
return true;
char* buffer = (char*)MmAllocatePoolTag(KE_DEBUG_SCREEN_BUFFER_SIZE, MM_POOL_TAG('d', 's', 'c', 'r'));
if (!buffer)
return false;
memset(buffer, 0, KE_DEBUG_SCREEN_BUFFER_SIZE);
bool installed = false;
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
if (!gDebugScreenBuffer) {
gDebugScreenBuffer = buffer;
gDebugScreenSize = KE_DEBUG_SCREEN_BUFFER_SIZE;
gDebugScreenRead = 0;
gDebugScreenWrite = 0;
gDebugScreenUsed = 0;
installed = true;
}
KeReleaseSpinlock(&gDebugPrintLock, flags);
if (!installed)
MmFreePool(buffer);
return true;
}
// Implements the debug Serial Write Char operation.
static void KiDebugSerialWriteChar(char c) {
while ((__inbyte(0x3FD) & 0x20) == 0) { }
if (c == '\n') {
__outbyte(0x3F8, '\r');
while ((__inbyte(0x3FD) & 0x20) == 0) { }
}
__outbyte(0x3F8, (uint8_t)c);
}
// Implements the debug Serial Write Locked operation.
static void KiDebugSerialWriteLocked(const char* text, size_t len) {
if (!text)
// Formats one bounded output record and routes it through the HAL.
static void KiOutputVPrintf(uint32_t stream, uint32_t flags,
uint32_t foreground, uint32_t background,
const char* format, va_list args) {
if (!format)
return;
for (size_t i = 0; i < len; i++)
KiDebugSerialWriteChar(text[i]);
}
// Implements the debug Console Write operation.
static void KiDebugConsoleWrite(const char* text, size_t len) {
if (!PrintChar || !text)
return;
for (size_t i = 0; i < len; i++)
PrintChar(text[i], 0xCCCCCC, 0x012456);
}
// Implements the debug Console Work operation.
static void KiDebugConsoleWork(void* context) {
(void)context;
char chunk[KE_DEBUG_SCREEN_DRAIN_SIZE];
for (;;) {
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
size_t len = KiDebugScreenDrainLocked(chunk, sizeof(chunk));
KeReleaseSpinlock(&gDebugPrintLock, flags);
if (len == 0)
break;
KiDebugConsoleWrite(chunk, len);
char line[KE_FATAL_OUTPUT_LINE_SIZE];
int count = vsnprintf_(line, sizeof(line), format, args);
size_t length = 0;
if (count > 0) {
length = (size_t)count;
if (length >= sizeof(line))
length = sizeof(line) - 1u;
}
KiOutputWrite(stream, flags, foreground, background, line, length);
}
// Queues the debug Console Work operation if the passive work queue is ready.
static void KiDebugQueueConsoleWork(void) {
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleWorkReady, 1, 1) && KeIsWorkQueueStarted())
(void)KeQueueWorkItem(&gDebugConsoleWorkItem);
}
static volatile long gPanicInProgress;
static volatile uintptr_t gPanicOwnerToken;
static volatile long gPanicStoppedCpuCount;
static volatile long gPanicInProgress = 0;
static volatile uintptr_t gPanicOwnerToken = 0;
static volatile long gPanicStoppedCpuCount = 0;
// Implements the panic Secondary Halt operation.
// Permanently stops a secondary processor that entered an active panic.
static _Noreturn void KiPanicSecondaryHalt(void) {
_disable();
_InterlockedIncrement(&gPanicStoppedCpuCount);
for (;;) __halt();
HalStopProcessor();
}
// Implements the panic Current Token operation.
// Returns a stable token for the current execution context.
static uintptr_t KiPanicCurrentToken(void) {
THREAD* thread = KeGetCurrentThread();
if (thread)
@@ -210,20 +115,20 @@ static uintptr_t KiPanicCurrentToken(void) {
return 1u;
}
// Implements the panic Is Active operation.
// Returns whether a fatal report is active.
static int KiPanicIsActive(void) {
return _InterlockedCompareExchange((volatile long*)&gPanicInProgress, 0, 0) != 0;
}
// Implements the panic Current Cpu Owns Report operation.
// Returns whether the current processor owns the active fatal report.
static int KiPanicCurrentCpuOwnsReport(void) {
uintptr_t token = KiPanicCurrentToken();
return token != 0 && gPanicOwnerToken == token;
}
// Implements the begin Fatal Report operation.
// Claims fatal-report ownership and masks interrupts on this processor.
static void KiBeginFatalReport(void) {
_disable();
(void)HalDisableInterrupts();
uintptr_t token = KiPanicCurrentToken();
if (_InterlockedCompareExchange((volatile long*)&gPanicInProgress, 1, 0) == 0) {
gPanicOwnerToken = token;
@@ -233,10 +138,7 @@ static void KiBeginFatalReport(void) {
KiPanicSecondaryHalt();
}
// Returning NMI handler used for SMP control IPIs. Panic NMIs stop the
// target CPU permanently; TLB shootdown NMIs flush and return. A genuinely
// unexpected NMI is not handled here and will fall through to the normal fault
// path in the assembly stub.
// Handles NMI-based panic stops and TLB shootdowns.
__declspec(dllexport) int KeHandleNmiIpi(void) {
if (KiPanicIsActive())
KiPanicSecondaryHalt();
@@ -247,23 +149,11 @@ __declspec(dllexport) int KeHandleNmiIpi(void) {
return 0;
}
// Implements the kernel Put Char operation.
void KiKernelPutChar(char c) {
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
KiDebugSerialWriteChar(c);
if (PrintChar)
PrintChar(c, 0xCCCCCC, 0x012456);
}
// Initializes debug.
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags) {
// Initializes debug logging and initial output subscriptions.
void KeInitDebug(const HAL_API* hal, uint32_t flags) {
gDebugHal = hal;
gDebugFlags = flags;
gDebugEnabled = (flags & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) != 0;
PrintChar = PrintPtr;
printf_set_putchar(KiKernelPutChar);
if (!_InterlockedCompareExchange((volatile long*)&gDebugConsoleWorkReady, 1, 0))
KeInitializeWorkItem(&gDebugConsoleWorkItem, KiDebugConsoleWork, (void*)0);
if ((flags & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) && !gDebugLogBuffer) {
gDebugLogBuffer = (char*)MmAllocatePoolTag(KE_DEBUG_LOG_BUFFER_SIZE, MM_POOL_TAG('d', 'l', 'o', 'g'));
@@ -275,81 +165,56 @@ void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags) {
}
}
if (flags & KE_DEBUG_CONSOLE)
(void)KiDebugEnsureScreenBuffer();
if (hal && hal->Output.SetEndpointStreams &&
hal->Output.BootFramebufferEndpointId != HAL_OUTPUT_ENDPOINT_INVALID) {
uint32_t streams = HAL_OUTPUT_STREAM_CONSOLE |
HAL_OUTPUT_STREAM_CRASH |
HAL_OUTPUT_STREAM_PANIC;
if (flags & KE_DEBUG_CONSOLE)
streams |= HAL_OUTPUT_STREAM_DEBUG;
(void)hal->Output.SetEndpointStreams(hal->Output.BootFramebufferEndpointId, streams);
}
}
// Returns debug Flags.
// Returns the active debug flags.
uint32_t KeGetDebugFlags(void) {
return gDebugFlags;
}
// Updates debug Console Writer.
void KeSetDebugConsoleWriter(void (*PrintPtr)(char, uint32_t, uint32_t)) {
if (!PrintPtr)
return;
PrintChar = PrintPtr;
printf_set_putchar(KiKernelPutChar);
// After the display manager owns debug drawing, DbgPrint and user console
// writes mirror to COM1 themselves. The display writer is screen-only so
// asynchronous screen flushing does not duplicate serial bytes.
(void)KiDebugEnsureScreenBuffer();
_InterlockedExchange((volatile long*)&gDebugConsoleOwnsSerial, 1);
KiDebugQueueConsoleWork();
}
// Implements the dbg Print operation.
// Writes one formatted kernel diagnostic.
__declspec(dllexport) void DbgPrint(const char* fmt, ...) {
if (!gDebugEnabled || !fmt) return;
if (!gDebugEnabled || !fmt)
return;
char line[KE_DEBUG_LOG_LINE_SIZE];
va_list va;
va_start(va, fmt);
int n = vsnprintf_(line, sizeof(line), fmt, va);
va_end(va);
va_list args;
va_start(args, fmt);
int count = vsnprintf_(line, sizeof(line), fmt, args);
va_end(args);
size_t len = 0;
if (n > 0) {
len = (size_t)n;
if (len >= sizeof(line))
len = sizeof(line) - 1u;
size_t length = 0;
if (count > 0) {
length = (size_t)count;
if (length >= sizeof(line))
length = sizeof(line) - 1u;
}
if (KiPanicIsActive()) {
if (!KiPanicCurrentCpuOwnsReport())
KiPanicSecondaryHalt();
if (gDebugFlags & KE_DEBUG_CONSOLE) {
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
KiDebugSerialWriteLocked(line, len);
KiDebugConsoleWrite(line, len);
}
return;
bool panic = KiPanicIsActive();
if (panic && !KiPanicCurrentCpuOwnsReport())
KiPanicSecondaryHalt();
if (!panic) {
uintptr_t lockFlags = KeAcquireSpinlock(&gDebugPrintLock);
if (!_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 0, 0))
KiDebugLogAppendLocked(line, length);
KeReleaseSpinlock(&gDebugPrintLock, lockFlags);
}
bool queueConsole = false;
bool writeConsoleSync = false;
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
KiDebugSerialWriteLocked(line, len);
if (!_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 0, 0))
KiDebugLogAppendLocked(line, len);
if (gDebugFlags & KE_DEBUG_CONSOLE) {
if (KeIsWorkQueueStarted() && gDebugScreenBuffer) {
KiDebugScreenAppendLocked(line, len);
queueConsole = true;
} else {
writeConsoleSync = true;
}
}
KeReleaseSpinlock(&gDebugPrintLock, flags);
if (queueConsole)
KiDebugQueueConsoleWork();
else if (writeConsoleSync)
KiDebugConsoleWrite(line, len);
KiOutputWrite(panic ? HAL_OUTPUT_STREAM_PANIC : HAL_OUTPUT_STREAM_DEBUG,
panic ? HAL_OUTPUT_WRITE_EMERGENCY : 0,
panic ? 0x00FF2222u : 0x00CCCCCCu,
panic ? 0x00000000u : 0x00012456u,
line, length);
}
// Flushes debug Log To File.
@@ -402,7 +267,6 @@ __declspec(dllexport) KSTATUS KeFlushDebugLogToFile(SYS_STRING_VIEW path) {
return (KSUCCESS(st) && totalWritten == used) ? STATUS_SUCCESS : st;
}
// Attempts to persist the debug log during a panic when the filesystem is usable.
static void KiTryFlushPanicDebugLog(void) {
if (!(gDebugFlags & KE_DEBUG_FILE_LOG))
@@ -419,76 +283,69 @@ static void KiTryFlushPanicDebugLog(void) {
DbgPrint("debuglog: panic flush to %s failed st=0x%x\n", path, st);
}
// KePanic.
// Writes one formatted panic record through panic-safe endpoints.
static void KiPanicPrintf(const char* format, ...) {
va_list args;
va_start(args, format);
KiOutputVPrintf(HAL_OUTPUT_STREAM_PANIC, HAL_OUTPUT_WRITE_EMERGENCY,
0x00FF2222u, 0x00000000u, format, args);
va_end(args);
}
// Stops the system after reporting a fatal kernel error.
__declspec(dllexport) _Noreturn void KePanic(int code, const char* fmt, ...) {
KiBeginFatalReport();
// Stop every other CPU for the final panic banner. Kernel fault dispatch
// claims panic ownership before its detailed dump, so this does not steal
// ownership from the CPU that found the fatal error.
// Stop every other CPU before writing the final report. Panic-safe endpoints
// do not depend on scheduler locks held by processors stopped by this IPI.
HalApicSendPanicIpi();
if (PrintChar) {
// Print a bright-red banner so the framebuffer is unmistakable.
static const char banner[] = "\n*** KERNEL PANIC ***\n";
const char* p;
KCPU* cpu = KeGetCurrentCpu();
THREAD* thread = KeGetCurrentThread();
static const char banner[] = "\n*** KERNEL PANIC ***\n";
KiOutputWrite(HAL_OUTPUT_STREAM_PANIC, HAL_OUTPUT_WRITE_EMERGENCY,
0x00FF2222u, 0x00000000u, banner, sizeof(banner) - 1u);
KiPanicPrintf("Panic code: %d\n", code);
for (p = banner; *p; p++)
PrintChar(*p, 0xFF2222, 0x000000);
printf("Panic code: %d\n", code);
if (cpu) {
printf("Panic CPU: %u LAPIC=%u",
(unsigned)cpu->CpuIndex,
(unsigned)cpu->LapicId);
if (thread)
printf(" thread=0x%016llX tid=%llu",
thread,
(unsigned long long)thread->ThreadId);
printf("\n");
KCPU* cpu = KeGetCurrentCpu();
THREAD* thread = KeGetCurrentThread();
if (cpu) {
KiPanicPrintf("Panic CPU: %u LAPIC=%u",
(unsigned)cpu->CpuIndex,
(unsigned)cpu->LapicId);
if (thread) {
KiPanicPrintf(" thread=0x%016llX tid=%llu",
thread,
(unsigned long long)thread->ThreadId);
}
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
KiPanicPrintf("\n");
}
va_list args;
va_start(args, fmt);
KiOutputVPrintf(HAL_OUTPUT_STREAM_PANIC, HAL_OUTPUT_WRITE_EMERGENCY,
0x00FF2222u, 0x00000000u, fmt, args);
va_end(args);
KiTryFlushPanicDebugLog();
for (;;) __halt();
HalStopProcessor();
}
// KePrintConsole - write user-visible console text using the default console palette.
// User text mirrors to COM1 immediately, then queues expensive screen drawing
// through the passive debug-console worker when that path is available.
// Writes user-visible console text through subscribed output endpoints.
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len) {
if (!buf || len == 0) return;
KiOutputWrite(HAL_OUTPUT_STREAM_CONSOLE, 0,
0x00CCCCCCu, 0x00012456u, buf, len);
}
bool queueConsole = false;
bool writeConsoleSync = false;
bool canQueueScreen = PrintChar && KeIsWorkQueueStarted() && KiDebugEnsureScreenBuffer();
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
KiDebugSerialWriteLocked(buf, len);
if (PrintChar) {
if (!KiPanicIsActive() && canQueueScreen) {
KiDebugScreenAppendLocked(buf, len);
queueConsole = true;
} else {
writeConsoleSync = true;
}
}
KeReleaseSpinlock(&gDebugPrintLock, flags);
if (queueConsole)
KiDebugQueueConsoleWork();
else if (writeConsoleSync)
KiDebugConsoleWrite(buf, len);
// Writes one formatted process-fault or kernel-fault diagnostic.
static void KiFaultPrintf(const char* format, ...) {
bool panic = KiPanicIsActive();
va_list args;
va_start(args, format);
KiOutputVPrintf(panic ? HAL_OUTPUT_STREAM_PANIC : HAL_OUTPUT_STREAM_CRASH,
panic ? HAL_OUTPUT_WRITE_EMERGENCY : 0,
panic ? 0x00FF2222u : 0x00FFAA00u,
0x00000000u,
format, args);
va_end(args);
}
// KeBugCheckProcess - user-mode fault handler
@@ -585,6 +442,15 @@ static const char* KiResolveModuleEx(PROCESS* proc, uintptr_t addr,
}
}
uintptr_t kernelBase = 0;
size_t kernelSize = 0;
const char* kernelModule = LdrFindKernelModuleByAddress(addr, &kernelBase, &kernelSize);
if (kernelBase) {
if (baseOut) *baseOut = kernelBase;
if (sizeOut) *sizeOut = kernelSize;
return kernelModule;
}
return "unknown";
}
@@ -600,71 +466,96 @@ static void KiPrintAddress(PROCESS* proc, const char* label, uintptr_t addr) {
const char* mod = KiResolveModuleEx(proc, addr, &base, &size);
if (base) {
printf("%s%016llx <%s+0x%016llX>\n",
KiFaultPrintf("%s%016llx <%s+0x%016llX>\n",
label,
(unsigned long long)addr,
mod,
(unsigned long long)(addr - base));
} else {
printf("%s%016llx <unknown>\n",
KiFaultPrintf("%s%016llx <unknown>\n",
label,
(unsigned long long)addr);
}
}
// Implements the user Read U64 operation.
static int KiUserReadU64(uintptr_t addr, uint64_t* out) {
if (!out) return 0;
if (!addr) return 0;
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint64_t))))
return 0;
*out = *(const uint64_t*)addr;
// Checks whether a kernel virtual range is mapped before diagnostic reads.
static int KiKernelRangeReadable(uintptr_t addr, size_t size) {
if (!addr || size == 0) return 0;
if (size - 1u > (size_t)(UINTPTR_MAX - addr)) return 0;
uintptr_t start = addr & PAGE_MASK;
uintptr_t last = (addr + size - 1u) & PAGE_MASK;
for (uintptr_t page = start;; page += PAGE_SIZE) {
PHYSICAL_ADDRESS phys = 0;
uint64_t flags = 0;
if (!KSUCCESS(MmPteQueryPageFor(MmPteQueryDebug, page, &phys, &flags)))
return 0;
if ((flags & PAGE_PRESENT) == 0)
return 0;
if (page == last)
break;
if (page > UINTPTR_MAX - PAGE_SIZE)
return 0;
}
return 1;
}
// Implements the user Read U8 operation.
static int KiUserReadU8(uintptr_t addr, uint8_t* out) {
if (!out) return 0;
if (!addr) return 0;
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint8_t))))
// Reads diagnostic memory from either user space or a mapped kernel range.
static int KiReadDiagnosticMemory(uintptr_t addr, void* out, size_t size) {
if (!out || !addr || size == 0) return 0;
if (addr >= KERNEL_BASE) {
if (!KiKernelRangeReadable(addr, size))
return 0;
memcpy(out, (const void*)addr, size);
return 1;
}
if (!KSUCCESS(MmProbeUserRead((const void*)addr, size)))
return 0;
*out = *(const uint8_t*)addr;
memcpy(out, (const void*)addr, size);
return 1;
}
// Reads one diagnostic uint64_t.
static int KiUserReadU64(uintptr_t addr, uint64_t* out) {
return KiReadDiagnosticMemory(addr, out, sizeof(*out));
}
// Reads one diagnostic byte.
static int KiUserReadU8(uintptr_t addr, uint8_t* out) {
return KiReadDiagnosticMemory(addr, out, sizeof(*out));
}
// Dumps bytes.
static void KiDumpBytes(PROCESS* proc, const char* label, uintptr_t center) {
if (!center) {
printf("%s: <null>\n", label);
KiFaultPrintf("%s: <null>\n", label);
return;
}
printf("%s around ", label);
KiFaultPrintf("%s around ", label);
KiPrintAddress(proc, "", center);
uintptr_t start = center >= 16 ? center - 16 : 0;
for (int row = 0; row < 2; row++) {
uintptr_t addr = start + (uintptr_t)row * 16u;
printf(" %016llx:", (unsigned long long)addr);
KiFaultPrintf(" %016llx:", (unsigned long long)addr);
for (int i = 0; i < 16; i++) {
uint8_t b = 0;
if (KiUserReadU8(addr + (uintptr_t)i, &b))
printf(" %02x", b);
KiFaultPrintf(" %02x", b);
else
printf(" ??");
KiFaultPrintf(" ??");
}
printf("\n");
KiFaultPrintf("\n");
}
}
// Implements the user Read I32 operation.
// Reads one diagnostic int32_t.
static int KiUserReadI32(uintptr_t addr, int32_t* out) {
if (!out) return 0;
if (!addr) return 0;
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(int32_t))))
return 0;
*out = *(const int32_t*)addr;
return 1;
return KiReadDiagnosticMemory(addr, out, sizeof(*out));
}
// Dumps indirect Call Slot.
@@ -693,21 +584,21 @@ static void KiDumpIndirectCallSlot(PROCESS* proc, uintptr_t ret) {
KiUserReadI32(ret - 4, &disp)) {
insn = ret - 7;
} else {
printf("No RIP-relative indirect call pattern found before return address.\n");
KiFaultPrintf("No RIP-relative indirect call pattern found before return address.\n");
return;
}
uintptr_t slot = ret + (intptr_t)disp;
uint64_t target = 0;
printf("Decoded null indirect call:\n");
KiFaultPrintf("Decoded null indirect call:\n");
KiPrintAddress(proc, " insn=", insn);
KiPrintAddress(proc, " slot=", slot);
if (KiUserReadU64(slot, &target)) {
KiPrintAddress(proc, " *slot=", (uintptr_t)target);
} else {
printf(" *slot=<unreadable>\n");
KiFaultPrintf(" *slot=<unreadable>\n");
}
KiDumpBytes(proc, "Bytes near indirect slot", slot);
@@ -717,9 +608,9 @@ static void KiDumpIndirectCallSlot(PROCESS* proc, uintptr_t ret) {
static void KiDumpModules(PROCESS* proc) {
if (!proc) return;
printf("Images:\n");
KiFaultPrintf("Images:\n");
if (proc->ImageBase && proc->ImageSize) {
printf(" %-31s %016llx-%016llx size=%llx\n",
KiFaultPrintf(" %-31s %016llx-%016llx size=%llx\n",
proc->Name,
(unsigned long long)proc->ImageBase,
(unsigned long long)(proc->ImageBase + proc->ImageSize),
@@ -729,7 +620,7 @@ static void KiDumpModules(PROCESS* proc) {
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
uintptr_t base = proc->Modules[i].Base;
size_t size = proc->Modules[i].Size;
printf(" %-31s %016llx-%016llx size=%llx\n",
KiFaultPrintf(" %-31s %016llx-%016llx size=%llx\n",
proc->Modules[i].Name,
(unsigned long long)base,
(unsigned long long)(base + size),
@@ -739,9 +630,9 @@ static void KiDumpModules(PROCESS* proc) {
// Dumps user Stack.
static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
printf("User stack @ %016llx:\n", (unsigned long long)rsp);
KiFaultPrintf("User stack @ %016llx:\n", (unsigned long long)rsp);
if (!rsp) {
printf(" <no user RSP captured>\n");
KiFaultPrintf(" <no user RSP captured>\n");
return;
}
@@ -753,20 +644,20 @@ static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
size_t size = 0;
const char* mod = KiResolveModuleEx(proc, (uintptr_t)value, &base, &size);
if (base) {
printf(" [%02d] %016llx : %016llx <%s+0x%016llX>\n",
KiFaultPrintf(" [%02d] %016llx : %016llx <%s+0x%016llX>\n",
i,
(unsigned long long)slot,
(unsigned long long)value,
mod,
(unsigned long long)((uintptr_t)value - base));
} else {
printf(" [%02d] %016llx : %016llx\n",
KiFaultPrintf(" [%02d] %016llx : %016llx\n",
i,
(unsigned long long)slot,
(unsigned long long)value);
}
} else {
printf(" [%02d] %016llx : <unreadable>\n",
KiFaultPrintf(" [%02d] %016llx : <unreadable>\n",
i,
(unsigned long long)slot);
}
@@ -777,20 +668,20 @@ static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
if (!f) return;
printf("Regs:\n");
printf(" RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n",
KiFaultPrintf("Regs:\n");
KiFaultPrintf(" RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n",
(unsigned long long)f->Rax, (unsigned long long)f->Rbx,
(unsigned long long)f->Rcx, (unsigned long long)f->Rdx);
printf(" RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n",
KiFaultPrintf(" RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n",
(unsigned long long)f->Rsi, (unsigned long long)f->Rdi,
(unsigned long long)f->Rbp, (unsigned long long)f->Rsp);
printf(" R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n",
KiFaultPrintf(" R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n",
(unsigned long long)f->R8, (unsigned long long)f->R9,
(unsigned long long)f->R10, (unsigned long long)f->R11);
printf(" R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n",
KiFaultPrintf(" R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n",
(unsigned long long)f->R12, (unsigned long long)f->R13,
(unsigned long long)f->R14, (unsigned long long)f->R15);
printf(" CS=%04llx SS=%04llx RFLAGS=%016llx\n",
KiFaultPrintf(" CS=%04llx SS=%04llx RFLAGS=%016llx\n",
(unsigned long long)f->Cs,
(unsigned long long)f->Ss,
(unsigned long long)f->Rflags);
@@ -798,12 +689,12 @@ static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
if (f->Rip == 0) {
uint64_t ret = 0;
if (KiUserReadU64((uintptr_t)f->Rsp, &ret)) {
printf("Likely null indirect call. Return address from [RSP]:\n");
KiFaultPrintf("Likely null indirect call. Return address from [RSP]:\n");
KiPrintAddress(proc, " ret=", (uintptr_t)ret);
KiDumpBytes(proc, "Bytes near return address", (uintptr_t)ret);
KiDumpIndirectCallSlot(proc, (uintptr_t)ret);
} else {
printf("Likely null indirect call, but [RSP] is unreadable.\n");
KiFaultPrintf("Likely null indirect call, but [RSP] is unreadable.\n");
}
} else {
KiDumpBytes(proc, "Bytes near RIP", (uintptr_t)f->Rip);
@@ -812,14 +703,6 @@ static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
KiDumpUserStack(proc, (uintptr_t)f->Rsp);
}
// Prints crash Char.
static void KiPrintCrashChar(char c) {
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
KiDebugSerialWriteChar(c);
if (PrintChar)
PrintChar(c, 0xFFAA00, 0x000000);
}
static KSTATUS KiFaultExitStatus(int vector) {
switch (vector) {
case 0: return STATUS_PROCESS_DIVIDE_BY_ZERO;
@@ -840,13 +723,11 @@ static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
const char* modName = proc ? KiResolveModule(proc, rip) : "unknown";
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
for (const char* p = banner; *p; p++)
KiPrintCrashChar(*p);
printf_set_putchar(KiPrintCrashChar);
KiOutputWrite(HAL_OUTPUT_STREAM_CRASH, 0,
0x00FFAA00u, 0x00000000u, banner, sizeof(banner) - 1u);
if (vector == 14)
printf("Process: %s (PID %u) | Fault: %s\n"
KiFaultPrintf("Process: %s (PID %u) | Fault: %s\n"
"Module: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
procName, pid, faultName,
modName,
@@ -854,7 +735,7 @@ static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
(unsigned long long)errCode,
(unsigned long long)cr2);
else
printf("Process: %s (PID %u) | Fault: %s\n"
KiFaultPrintf("Process: %s (PID %u) | Fault: %s\n"
"Module: %s | RIP: %016llx | Code: %04llx\n",
procName, pid, faultName,
modName,
@@ -864,21 +745,19 @@ static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
// Implements the finish Faulted Process operation.
static _Noreturn void KiFinishFaultedProcess(PROCESS* proc, THREAD* thread, KSTATUS exitStatus) {
printf_set_putchar(KiKernelPutChar);
int exitCode = (int)exitStatus;
if (proc) (void)KeTerminateProcess(proc, exitCode);
if (thread) KeExitThread(thread, exitCode);
// KeExitThread normally switches away and never resumes this context. If
// KeExitThread normally switches away and never resumes this context. If
// a fault path re-enters here during scheduler bring-up or with no runnable
// peer yet, do not fall back to a local CLI/HLT dead stop: keep offering the
// dead thread to the scheduler with interrupts enabled so waiters such as
// the foreground shell can observe the process exit event.
while (true) {
_enable();
HalEnableInterrupts();
KeYield();
__halt();
HalWaitForInterrupt();
}
}
@@ -932,6 +811,7 @@ __declspec(dllexport) KSTATUS KeFaultDispatchFrame(const KFAULT_FRAME* frame) {
KiDumpFaultFrame(proc, frame);
if (vector == 14) {
MmDebugDescribeAddress((uintptr_t)frame->Cr2);
if (frame->Rip) MmDebugDescribeAddress((uintptr_t)frame->Rip);
if (frame->Rsp) MmDebugDescribeAddress((uintptr_t)frame->Rsp);
MmVadDebugDumpProcess(proc);
}
@@ -994,5 +874,5 @@ __declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
frame.Rip = rip;
frame.Cs = cs;
(void)KeFaultDispatchFrame(&frame);
while (1) __halt();
HalStopProcessor();
}
+144 -36
View File
@@ -30,6 +30,14 @@
#define DISPLAY_CONSOLE_MAX_CELLS (DISPLAY_CONSOLE_MAX_COLS * DISPLAY_CONSOLE_MAX_ROWS)
#define DISPLAY_CONSOLE_DEFAULT_ATTR 0x07u
#define DISPLAY_CURSOR_POS_INVALID (-2147483647 - 1)
#define DISPLAY_OUTPUT_BUFFER_CAPACITY 4096u
#define DISPLAY_OUTPUT_DRAIN_COUNT 128u
typedef struct DISPLAY_OUTPUT_GLYPH {
char Character;
uint32_t Foreground;
uint32_t Background;
} DISPLAY_OUTPUT_GLYPH;
typedef struct BOOTFB_CONTEXT {
const HAL_API* Hal;
@@ -78,7 +86,15 @@ static uint16_t gConsoleAttribute;
static char gConsoleCells[DISPLAY_CONSOLE_MAX_CELLS];
static uint8_t gConsoleAttrs[DISPLAY_CONSOLE_MAX_CELLS];
static volatile long gDisplayConsoleSuspended;
static SPINLOCK gDisplayOutputLock = SPINLOCK_INIT;
static DISPLAY_OUTPUT_GLYPH* gDisplayOutputBuffer;
static size_t gDisplayOutputRead;
static size_t gDisplayOutputWrite;
static size_t gDisplayOutputUsed;
static KWORK_ITEM gDisplayOutputWorkItem;
static volatile long gDisplayOutputWorkInitialized;
static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg);
static KSTATUS BootFbGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode);
static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels);
static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color);
@@ -105,7 +121,6 @@ static const DISPLAY_DEVICE_OPS gBootFbOps = {
.QueryStats = BootFbQueryStats,
};
static bool KiDisplayOutputSuspended(void);
static void KiDisplayCursorWorker(void* context);
static void KiDisplayDirtyAll(void);
@@ -161,14 +176,13 @@ static void KiDisplayQueueCursorWork(void) {
return;
}
// During single-threaded early boot there is no worker thread yet. The
// During single-threaded early boot there is no worker thread yet. The
// boot framebuffer console still needs synchronous cursor setup; once the
// scheduler work queue exists all cursor commits are asynchronous.
if (!KiDisplayProviderCallbacksRequirePassive())
KiDisplayCursorWorker(0);
}
// Checks whether a primary display provider is available.
static bool KiDisplayHasDevice(void) {
return gPrimaryDisplay && gPrimaryDisplay->Ops;
@@ -195,7 +209,6 @@ static bool KiDisplayHasCursorOps(void) {
gPrimaryDisplay->Ops->ShowCursor;
}
static void KiDisplayCursorWorker(void* context) {
(void)context;
@@ -263,9 +276,9 @@ static void KiDisplayCursorWorker(void* context) {
newCommittedY = DISPLAY_CURSOR_POS_INVALID;
}
} else if (hardwareCursor) {
// Hardware cursors are independent of framebuffer drawing. Commit
// Hardware cursors are independent of framebuffer drawing. Commit
// position only when the logical cursor request changed or the
// provider has not yet been synchronized. This prevents stale or
// provider has not yet been synchronized. This prevents stale or
// duplicate work items from hammering device cursor registers.
bool needPosition = !drawn || x != committedX || y != committedY;
if (needPosition && ops->SetCursorPosition) {
@@ -308,7 +321,7 @@ static void KiDisplayCursorWorker(void* context) {
gCursorCommittedX = newCommittedX;
gCursorCommittedY = newCommittedY;
}
// Do not spin forever on provider failure. A later explicit cursor
// Do not spin forever on provider failure. A later explicit cursor
// request, show/hide request, or display-generation change will retry.
gCursorAppliedGeneration = requestGeneration;
if (gCursorRequestGeneration != requestGeneration)
@@ -349,8 +362,7 @@ static bool KiDisplayClipRect(const DISPLAY_MODE* mode, DISPLAY_RECT* rect) {
return true;
}
// Snapshots the current primary under the manager spinlock. The caller must
// Snapshots the current primary under the manager spinlock. The caller must
// hold the provider mutex before invoking callbacks through the returned ops;
// KeDisplaySetPrimary takes the same mutex, so the provider object cannot be
// switched while the callback sequence is in progress.
@@ -966,12 +978,109 @@ static KSTATUS KiDisplayDrawConsoleCellLocked(int col, int row, char c, uint16_t
KiDisplayConsoleBg(attr));
}
// Implements the display Serial Write Char operation.
static void KiDisplaySerialWriteChar(char c) {
while ((__inbyte(0x3FD) & 0x20) == 0) { }
if (c == '\n')
__outbyte(0x3F8, '\r');
__outbyte(0x3F8, (uint8_t)c);
// Appends one glyph to the runtime display output ring while its lock is held.
static void KiDisplayOutputAppendLocked(char character, uint32_t foreground, uint32_t background) {
if (!gDisplayOutputBuffer)
return;
if (gDisplayOutputUsed == DISPLAY_OUTPUT_BUFFER_CAPACITY) {
gDisplayOutputRead = (gDisplayOutputRead + 1u) % DISPLAY_OUTPUT_BUFFER_CAPACITY;
gDisplayOutputUsed--;
}
DISPLAY_OUTPUT_GLYPH* glyph = &gDisplayOutputBuffer[gDisplayOutputWrite];
glyph->Character = character;
glyph->Foreground = foreground;
glyph->Background = background;
gDisplayOutputWrite = (gDisplayOutputWrite + 1u) % DISPLAY_OUTPUT_BUFFER_CAPACITY;
gDisplayOutputUsed++;
}
// Drains buffered output through the active display provider.
static void KiDisplayOutputWorker(void* context) {
(void)context;
DISPLAY_OUTPUT_GLYPH glyphs[DISPLAY_OUTPUT_DRAIN_COUNT];
for (;;) {
size_t count = 0;
uintptr_t flags = KeAcquireSpinlock(&gDisplayOutputLock);
while (count < DISPLAY_OUTPUT_DRAIN_COUNT && gDisplayOutputUsed != 0) {
glyphs[count++] = gDisplayOutputBuffer[gDisplayOutputRead];
gDisplayOutputRead = (gDisplayOutputRead + 1u) % DISPLAY_OUTPUT_BUFFER_CAPACITY;
gDisplayOutputUsed--;
}
KeReleaseSpinlock(&gDisplayOutputLock, flags);
if (count == 0)
break;
for (size_t i = 0; i < count; i++) {
(void)KiDisplayPrintCharInternal(glyphs[i].Character,
glyphs[i].Foreground,
glyphs[i].Background);
}
}
}
// Queues the runtime display output worker when passive work is available.
static void KiDisplayQueueOutputWorker(void) {
if (_InterlockedCompareExchange(&gDisplayOutputWorkInitialized, 1, 1) &&
KeIsWorkQueueStarted()) {
(void)KeQueueWorkItem(&gDisplayOutputWorkItem);
}
}
// Receives one HAL output record for the runtime display endpoint.
static void KiDisplayWriteOutputRecord(void* context, const HAL_OUTPUT_RECORD* record) {
(void)context;
if (!record || !record->Buffer || record->Length == 0)
return;
if (KiDisplayOutputSuspended())
return;
if (KeIsWorkQueueStarted() && gDisplayOutputBuffer) {
uintptr_t flags = KeAcquireSpinlock(&gDisplayOutputLock);
for (size_t i = 0; i < record->Length; i++) {
KiDisplayOutputAppendLocked(record->Buffer[i],
record->Foreground,
record->Background);
}
KeReleaseSpinlock(&gDisplayOutputLock, flags);
KiDisplayQueueOutputWorker();
return;
}
for (size_t i = 0; i < record->Length; i++) {
(void)KiDisplayPrintCharInternal(record->Buffer[i],
record->Foreground,
record->Background);
}
}
// Registers the runtime display as a HAL output endpoint.
static void KiDisplayRegisterOutputEndpoint(void) {
if (!gDisplayHal || !gDisplayHal->Output.RegisterEndpoint)
return;
HAL_OUTPUT_ENDPOINT endpoint;
endpoint.Name = "runtime-display";
endpoint.StreamMask = HAL_OUTPUT_STREAM_CONSOLE | HAL_OUTPUT_STREAM_CRASH;
if (KeGetDebugFlags() & KE_DEBUG_CONSOLE)
endpoint.StreamMask |= HAL_OUTPUT_STREAM_DEBUG;
endpoint.Capabilities = 0;
endpoint.Write = KiDisplayWriteOutputRecord;
endpoint.Context = 0;
uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID;
if (!KSUCCESS(gDisplayHal->Output.RegisterEndpoint(&endpoint, &endpointId)))
return;
(void)endpointId;
if (gDisplayHal->Output.SetEndpointStreams &&
gDisplayHal->Output.BootFramebufferEndpointId != HAL_OUTPUT_ENDPOINT_INVALID) {
(void)gDisplayHal->Output.SetEndpointStreams(
gDisplayHal->Output.BootFramebufferEndpointId,
HAL_OUTPUT_STREAM_PANIC);
}
}
// Implements the display Initialize operation.
@@ -1000,6 +1109,16 @@ void KeDisplayInitialize(const HAL_API* hal) {
(void)KeMutexCreate(&gDisplayProviderMutex);
KeInitializeWorkItem(&gDisplayCursorWorkItem, KiDisplayCursorWorker, 0);
_InterlockedExchange(&gDisplayCursorWorkInitialized, 1);
KeInitializeWorkItem(&gDisplayOutputWorkItem, KiDisplayOutputWorker, 0);
_InterlockedExchange(&gDisplayOutputWorkInitialized, 1);
gDisplayOutputRead = 0;
gDisplayOutputWrite = 0;
gDisplayOutputUsed = 0;
if (!gDisplayOutputBuffer) {
gDisplayOutputBuffer = (DISPLAY_OUTPUT_GLYPH*)MmAllocatePoolTag(
DISPLAY_OUTPUT_BUFFER_CAPACITY * sizeof(DISPLAY_OUTPUT_GLYPH),
MM_POOL_TAG('d', 'o', 'u', 't'));
}
gConsoleX = 0;
gConsoleY = 0;
KiDisplayConsoleResetCellsLocked();
@@ -1047,15 +1166,16 @@ void KeDisplayInitialize(const HAL_API* hal) {
mode.Width, mode.Height, mode.Stride);
}
// KeDisplayInitialize runs while the debug console still points at the
// HAL early console, so the registration messages above are written
// directly through HAL_SCREEN. Resync the bootfb backbuffer once before
// kmain switches DbgPrint to KeDisplayDebugPrintChar.
// The registration messages above were routed through the HAL boot
// framebuffer endpoint. Resync the display-manager backbuffer before
// the runtime display endpoint becomes the normal screen subscriber.
if (gBootFb.Backbuffer) {
size_t pixels2 = (size_t)gBootFb.Stride * (size_t)gBootFb.Height;
memcpy(gBootFb.Backbuffer, gBootFb.Framebuffer, pixels2 * sizeof(uint32_t));
}
}
KiDisplayRegisterOutputEndpoint();
}
DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device) {
@@ -1120,7 +1240,6 @@ static KSTATUS KiDisplaySeedNewPrimary(DISPLAY_DEVICE* oldDevice, DISPLAY_DEVICE
return s;
}
DISPLAY_API void KeDisplaySuspendConsole(bool suspend) {
_InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, suspend ? 1 : 0);
}
@@ -1171,7 +1290,7 @@ DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) {
(void)oldDevice->Ops->ShowCursor(oldDevice, false);
// Clean architecture policy: handoff does not read back/copy the old boot
// framebuffer. The new primary is cleared to the kernel console blue and
// framebuffer. The new primary is cleared to the kernel console blue and
// user/session code redraws state above the provider boundary.
if (device->Ops->GetMode && device->Ops->FillRect) {
DISPLAY_MODE mode;
@@ -1523,10 +1642,7 @@ DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t
return s;
}
static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg, bool mirrorSerial) {
if (mirrorSerial)
KiDisplaySerialWriteChar(c);
static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg) {
if (KiDisplayOutputSuspended()) {
if (!KeCanWait())
return KiDisplayFallbackPrintChar(c, fg, bg);
@@ -1534,11 +1650,8 @@ static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg, bool
}
// Once the scheduler/work-queue era begins, display provider callbacks are
// PASSIVE_LEVEL-only. DbgPrint queues screen output through the work queue;
// panic and explicit console paths may still use the serial mirroring entry.
// A panic has interrupts disabled and cannot take the provider mutex, so it
// falls back to the boot framebuffer writer instead of disappearing from
// the screen while still printing to COM1.
// PASSIVE_LEVEL-only. The HAL output endpoint buffers normal text so this
// fallback is reserved for explicit display calls made at elevated IRQL.
if (KiDisplayProviderCallbacksRequirePassive() && !KeCanWait())
return KiDisplayFallbackPrintChar(c, fg, bg);
@@ -1642,12 +1755,7 @@ static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg, bool
}
DISPLAY_API KSTATUS KeDisplayPrintChar(char c, uint32_t fg, uint32_t bg) {
return KiDisplayPrintCharInternal(c, fg, bg, true);
}
// Implements the display Debug Print Char operation.
void KeDisplayDebugPrintChar(char c, uint32_t fg, uint32_t bg) {
(void)KiDisplayPrintCharInternal(c, fg, bg, false);
return KiDisplayPrintCharInternal(c, fg, bg);
}
DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo) {
+2 -2
View File
@@ -5,8 +5,8 @@
KeFlushGdt PROC
; RCX = pGdtr (pointer to limit/base struct)
; RDX = codeSel
; R8 = dataSel
; R9 = tssSel
; R8 = dataSel
; R9 = tssSel
; 1. Load the GDT
lgdt fword ptr [rcx]
+1 -1
View File
@@ -5,7 +5,7 @@
// 0x08 ring-0 code (64-bit)
// 0x10 ring-0 data
// 0x18 ring-3 code (64-bit, DPL=3)
// 0x20 ring-3 data (DPL=3) - note: 0x1B/0x23 = 0x18|3 / 0x20|3
// 0x20 ring-3 data (DPL=3) - note: 0x1B/0x23 = 0x18|3 / 0x20|3
// 0x28 TSS low
// 0x30 TSS high.
#include <stdint.h>
+55 -33
View File
@@ -9,6 +9,7 @@
#include <kernel/ke_debug.h>
#include <kernel/ke_display.h>
#include <kernel/ke_event.h>
#include <kernel/ke_dpc.h>
#include <kernel/ob_object.h>
#define KEY_QUEUE_SIZE 256u
@@ -42,6 +43,7 @@ static volatile int gKeyHead;
static volatile int gKeyTail;
static volatile INPUT_KEY gRepeatKey;
static volatile uint64_t gRepeatNextTick;
static KDPC gRepeatDpc;
static INPUT_MOUSE_EVENT gMouseQueue[MOUSE_QUEUE_SIZE];
static volatile int gMouseHead;
@@ -54,9 +56,9 @@ static volatile uint8_t gMouseButtons;
static volatile uint32_t gNextInputDeviceId = 1;
static EVENT* gKeyboardEvent;
static EVENT* gInputStateEvent;
static char KiInputKeyToChar(INPUT_KEY key);
static void KiInputServiceRepeat(void);
static const KEY_CHAR_PAIR gCharMap[] = {
{ INPUT_KEY_1, '1', '!' }, { INPUT_KEY_2, '2', '@' },
@@ -82,7 +84,14 @@ static const KEY_CHAR_PAIR gCharMap[] = {
{ INPUT_KEY_KP_0, '0', '0' }, { INPUT_KEY_KP_PERIOD, '.', '.' },
};
// Queues a keyboard event and wakes one blocked reader when the event was accepted.
// Updates the waitable keyboard event to match queued keyboard input.
static void KiInputRefreshKeyboardEvent(void) {
if (!gKeyboardEvent) return;
if (gKeyHead != gKeyTail) KeSignalEvent(gKeyboardEvent);
else KeClearEvent(gKeyboardEvent);
}
// Queues a keyboard event and wakes waiters when the event was accepted.
static void KiInputQueueKey(INPUT_KEY key, char ch) {
if (key == INPUT_KEY_NONE) return;
int next = (gKeyTail + 1) & (int)KEY_QUEUE_MASK;
@@ -91,9 +100,7 @@ static void KiInputQueueKey(INPUT_KEY key, char ch) {
gKeyQueue[gKeyTail].ch = ch;
gKeyQueue[gKeyTail].modifiers = gModifiers;
gKeyTail = next;
if (gKeyboardEvent)
KeSignalEvent(gKeyboardEvent);
KiInputRefreshKeyboardEvent();
}
// Implements the input Queue Has Space operation.
@@ -167,6 +174,16 @@ static void KiInputServiceRepeat(void) {
gRepeatNextTick = now + KEY_REPEAT_PERIOD_US;
}
// Implements the input Repeat Dpc Routine operation.
static void KiInputRepeatDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
(void)dpc;
(void)context;
(void)systemArgument1;
(void)systemArgument2;
KiInputServiceRepeat();
}
// Implements the input Set Modifier operation.
static void KiInputSetModifier(INPUT_KEY key, bool pressed) {
uint32_t bit = 0;
@@ -219,17 +236,14 @@ void KeInputInitialize(const HAL_API* hal) {
gRepeatNextTick = 0;
gMouseHead = gMouseTail = 0;
gMouseButtons = 0;
KeInitializeDpc(&gRepeatDpc, KiInputRepeatDpcRoutine, (void*)0);
if (!gKeyboardEvent) {
KSTATUS s = KeEventCreate(EventAutoReset, 0, &gKeyboardEvent);
KSTATUS s = KeEventCreate(EventManualReset, 0, &gKeyboardEvent);
if (!KSUCCESS(s))
DbgPrint("Input: keyboard wait event create failed st=0x%x\n", s);
}
if (!gInputStateEvent) {
KSTATUS s = KeEventCreate(EventAutoReset, 0, &gInputStateEvent);
if (!KSUCCESS(s))
DbgPrint("Input: state wait event create failed st=0x%x\n", s);
}
KiInputRefreshKeyboardEvent();
int w = 1920, h = 1080;
DISPLAY_MODE mode;
@@ -272,7 +286,6 @@ __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event) {
gKeyDown[key] = 1u;
KiInputSetModifier(key, true);
if (gInputStateEvent) KeSignalEvent(gInputStateEvent);
KiInputQueueKey(key, KiInputKeyToChar(key));
KiInputStartRepeat(key, KiInputNowTicks());
} else {
@@ -281,7 +294,6 @@ __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event) {
gKeyDown[key] = 0u;
KiInputSetModifier(key, false);
if (gInputStateEvent) KeSignalEvent(gInputStateEvent);
KiInputStopRepeat(key);
}
}
@@ -363,16 +375,17 @@ INPUT_KEY KeInputKbPoll(void) {
return (gKeyHead == gKeyTail) ? INPUT_KEY_NONE : gKeyQueue[gKeyHead].key;
}
// Waits until at least one keyboard event is available.
KSTATUS KeInputWaitKey(uint8_t alertable) {
// Waits until at least one keyboard event is available or the timeout expires.
KSTATUS KeInputWaitKey(uint8_t alertable, uint64_t timeoutMs) {
if (KeInputKbPoll() != INPUT_KEY_NONE)
return STATUS_SUCCESS;
if (!gKeyboardEvent)
return STATUS_FAILURE;
for (;;) {
KSTATUS s = KeWaitForEvent(gKeyboardEvent, alertable);
EVENT* event = gKeyboardEvent;
uint32_t selected = 0;
KSTATUS s = KeWaitForMultipleEvents(&event, 1, alertable, timeoutMs, &selected);
if (!KSUCCESS(s))
return s;
if (KeInputKbPoll() != INPUT_KEY_NONE)
@@ -380,6 +393,17 @@ KSTATUS KeInputWaitKey(uint8_t alertable) {
}
}
// Services typematic repeat from the scheduler timer bottom half.
void KeInputTimerTick(void) {
INPUT_KEY key = gRepeatKey;
if (!KiInputKeyIsRepeatable(key) || key >= MAX_INPUT_KEYS || !gKeyDown[key])
return;
uint64_t now = KiInputNowTicks();
if (now != 0 && now >= gRepeatNextTick)
KeInsertQueueDpc(&gRepeatDpc, (void*)0, (void*)0);
}
// Implements the input Is Key Down operation.
bool KeInputIsKeyDown(INPUT_KEY key) {
if (key == INPUT_KEY_NONE || key >= MAX_INPUT_KEYS) return false;
@@ -395,7 +419,6 @@ bool KeInputPeekEvent(INPUT_KEY_RECORD* record) {
INPUT_KEY_QUEUE_ENTRY entry = gKeyQueue[gKeyHead];
record->Key = entry.key;
record->Char = entry.ch;
record->Reserved = 0;
record->Modifiers = entry.modifiers;
return true;
}
@@ -404,6 +427,7 @@ bool KeInputPeekEvent(INPUT_KEY_RECORD* record) {
bool KeInputReadEvent(INPUT_KEY_RECORD* record) {
if (!KeInputPeekEvent(record)) return false;
gKeyHead = (gKeyHead + 1) & (int)KEY_QUEUE_MASK;
KiInputRefreshKeyboardEvent();
return true;
}
@@ -424,21 +448,19 @@ uint32_t KeInputGetModifiers(void) {
return gModifiers;
}
// Implements the input Mouse Poll operation.
bool KeInputMousePoll(void) {
return gMouseHead != gMouseTail;
// Implements the input Mouse Peek operation.
bool KeInputMousePeek(INPUT_MOUSE_EVENT* event) {
if (!event) return false;
if (gMouseHead == gMouseTail) return false;
*event = gMouseQueue[gMouseHead];
return true;
}
// Implements the input Mouse Read operation.
bool KeInputMouseRead(int* dx, int* dy, uint8_t* buttons) {
if (!dx || !dy || !buttons) return false;
if (gMouseHead == gMouseTail) return false;
INPUT_MOUSE_EVENT ev = gMouseQueue[gMouseHead];
bool KeInputMouseRead(INPUT_MOUSE_EVENT* event) {
if (!KeInputMousePeek(event)) return false;
gMouseHead = (gMouseHead + 1) & (int)MOUSE_QUEUE_MASK;
*dx = ev.dx;
*dy = ev.dy;
*buttons = ev.buttons;
return true;
}
@@ -485,18 +507,18 @@ static void KiInputQueueDestroy(OBJECT* object) {
KSTATUS KeInputOpenQueue(INPUT_QUEUE** out) {
if (!out) return STATUS_INVALID_PARAMETER;
*out = (INPUT_QUEUE*)0;
if (!gInputStateEvent) return STATUS_FAILURE;
if (!gKeyboardEvent) return STATUS_FAILURE;
INPUT_QUEUE* queue = (INPUT_QUEUE*)ObAllocateObject(ObjectTypeInputQueue, sizeof(INPUT_QUEUE));
if (!queue) return STATUS_NO_MEMORY;
queue->Header.Destroy = KiInputQueueDestroy;
ObReferenceObject(&gInputStateEvent->Header);
queue->WaitEvent = gInputStateEvent;
ObReferenceObject(&gKeyboardEvent->Header);
queue->WaitEvent = gKeyboardEvent;
*out = queue;
return STATUS_SUCCESS;
}
// Returns the event that wakes when keyboard state changes.
// Returns the event that wakes when keyboard input can be read.
EVENT* KeInputQueueWaitEvent(INPUT_QUEUE* queue) {
return queue ? queue->WaitEvent : (EVENT*)0;
}
+73 -11
View File
@@ -66,6 +66,7 @@ KSTATUS KeCreateSystemProcess(PROCESS** out) {
return s;
}
proc->Flags |= KE_PROCESS_FLAG_SYSTEM | KE_PROCESS_FLAG_TERMINATION_PROTECTED;
gSystemProcess = proc;
*out = proc;
return STATUS_SUCCESS;
@@ -100,8 +101,6 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
KiProcessInit();
PROCESS* current = KeGetCurrentProcess();
PROCESS* system = KeGetSystemProcess();
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
size_t count = 0;
LIST_ENTRY* head = &gProcessList;
@@ -120,7 +119,7 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
uintptr_t handleFlags = KeAcquireSpinlock(&p->HandleLock);
uint32_t threadCount = p->ThreadCount;
uint32_t handleCount = KiCountProcessHandlesLocked(p);
int exitCode = p->ExitCode;
int32_t exitCode = p->ExitCode;
long terminating = p->Terminating;
long exited = p->Exited;
KeReleaseSpinlock(&p->HandleLock, handleFlags);
@@ -139,7 +138,10 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
info->State = SYS_PROCESS_STATE_RUNNING;
}
if (p == system) info->Flags |= SYS_PROCESS_FLAG_SYSTEM;
if (p->Flags & KE_PROCESS_FLAG_SYSTEM) info->Flags |= SYS_PROCESS_FLAG_SYSTEM;
if (p->Flags & KE_PROCESS_FLAG_IDLE) info->Flags |= SYS_PROCESS_FLAG_IDLE;
if (p->Flags & KE_PROCESS_FLAG_TERMINATION_PROTECTED)
info->Flags |= SYS_PROCESS_FLAG_TERMINATION_PROTECTED;
if (p == current) info->Flags |= SYS_PROCESS_FLAG_CURRENT;
MmVadQueryProcessMemory(p, &info->VadCount,
@@ -190,6 +192,7 @@ static uint32_t KiWaitObjectTypeToSys(OBJECT_TYPE type) {
case ObjectTypeUsbPipe: return SYS_THREAD_WAIT_USB_PIPE;
case ObjectTypeFileMapping: return SYS_THREAD_WAIT_FILE_MAPPING;
case ObjectTypeSurface: return SYS_THREAD_WAIT_SURFACE;
case ObjectTypeTimer: return SYS_THREAD_WAIT_TIMER;
default: return SYS_THREAD_WAIT_UNKNOWN;
}
}
@@ -276,6 +279,7 @@ static void KiProcessRundownResources(PROCESS* proc) {
if (_InterlockedExchange(&proc->ResourcesReleased, 1) != 0)
return;
IoReleaseFileLocksForProcess(proc);
KiProcessReleaseAllHandles(proc);
// Release every user VA region owned by this process. This includes the
@@ -334,6 +338,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->Header.Destroy = KiProcessDestroy;
proc->ProcessId = _InterlockedExchangeAdd((volatile long *)&gNextProcessId, 1);
proc->Flags = 0;
strncpy(proc->Name, name, PROCESS_NAME_MAX - 1);
proc->Name[PROCESS_NAME_MAX - 1] = '\0';
@@ -381,7 +386,6 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
return STATUS_SUCCESS;
}
// KeSetupUserProcessEnvironment.
static KSTATUS KiCopyEnvironmentStringView(PROCESS* proc,
SYS_STRING_VIEW src,
@@ -558,8 +562,10 @@ void KeCompleteProcessExitIfLastThread(PROCESS* process) {
}
// Implements the exit Process operation.
void KeExitProcess(PROCESS* process, int exitCode) {
if (!process) return;
KSTATUS KeExitProcess(PROCESS* process, int32_t exitCode) {
if (!process) return STATUS_INVALID_PARAMETER;
if (KeIsProcessTerminationProtected(process)) return STATUS_ACCESS_DENIED;
process->ExitCode = exitCode;
_InterlockedExchange(&process->Terminating, 1);
@@ -567,11 +573,13 @@ void KeExitProcess(PROCESS* process, int exitCode) {
// not drop the process object here: each thread owns one process reference,
// and KiThreadDestroy releases that reference after unlinking the thread.
KeCompleteProcessExitIfLastThread(process);
return STATUS_SUCCESS;
}
// Implements the terminate Process operation.
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode) {
KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode) {
if (!process) return STATUS_INVALID_PARAMETER;
if (KeIsProcessTerminationProtected(process)) return STATUS_ACCESS_DENIED;
process->ExitCode = exitCode;
_InterlockedExchange(&process->Terminating, 1);
@@ -588,7 +596,7 @@ KSTATUS KeTerminateProcess(PROCESS* process, int exitCode) {
continue;
// A terminating process must not be held open by worker threads that
// are blocked in non-alertable waits. User-mode DLLs such as Bink can
// are blocked in non-alertable waits. User-mode DLLs such as Bink can
// leave IO/submit workers parked on semaphores; after the foreground
// thread crashes, those waiters would otherwise keep ThreadCount > 0
// and prevent the process ExitEvent from waking the shell.
@@ -616,8 +624,13 @@ int KeIsProcessTerminating(PROCESS* process) {
return process && process->Terminating != 0;
}
// KeGetProcessExitCode.
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut) {
// Returns whether process termination is forbidden.
int KeIsProcessTerminationProtected(PROCESS* process) {
return process && (process->Flags & KE_PROCESS_FLAG_TERMINATION_PROTECTED) != 0;
}
// Copies the recorded process exit code.
KSTATUS KeGetProcessExitCode(PROCESS* process, int32_t* exitCodeOut) {
if (!process || !exitCodeOut) return STATUS_INVALID_PARAMETER;
*exitCodeOut = process->ExitCode;
return STATUS_SUCCESS;
@@ -699,6 +712,55 @@ uint32_t KeGetHandleRights(PROCESS* process, uint32_t handle) {
return rights;
}
// Duplicates a handle from one process table into another.
KSTATUS KeDuplicateHandleToProcess(PROCESS* sourceProcess, uint32_t sourceHandle, PROCESS* targetProcess, uint32_t desiredRights, uint32_t duplicateFlags, uint32_t* targetHandleOut) {
if (!sourceProcess || !targetProcess || !targetHandleOut) return STATUS_INVALID_PARAMETER;
if (sourceHandle == 0 || sourceHandle >= HANDLE_TABLE_SIZE) return STATUS_INVALID_HANDLE;
const uint32_t validDuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS;
const uint32_t publicHandleRights = SYS_HANDLE_RIGHT_ALL;
if ((duplicateFlags & ~validDuplicateFlags) != 0) return STATUS_INVALID_PARAMETER;
if ((duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) != 0 && desiredRights != 0)
return STATUS_INVALID_PARAMETER;
if ((duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) == 0) {
if (desiredRights == 0) return STATUS_INVALID_PARAMETER;
if ((desiredRights & ~publicHandleRights) != 0) return STATUS_INVALID_PARAMETER;
}
OBJECT* obj = NULL;
uint32_t sourceRights = 0;
uintptr_t savedFlags = KeAcquireSpinlock(&sourceProcess->HandleLock);
HANDLE_ENTRY* entry = &sourceProcess->Handles[sourceHandle];
if (!entry->Object) {
KeReleaseSpinlock(&sourceProcess->HandleLock, savedFlags);
return STATUS_INVALID_HANDLE;
}
if ((entry->Rights & HANDLE_RIGHT_DUPLICATE) == 0) {
KeReleaseSpinlock(&sourceProcess->HandleLock, savedFlags);
return STATUS_ACCESS_DENIED;
}
obj = entry->Object;
sourceRights = entry->Rights;
ObReferenceObject(obj);
KeReleaseSpinlock(&sourceProcess->HandleLock, savedFlags);
uint32_t targetRights = sourceRights;
if ((duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) == 0) {
if ((desiredRights & ~sourceRights) != 0) {
ObDereferenceObject(obj);
return STATUS_ACCESS_DENIED;
}
targetRights = desiredRights;
}
KSTATUS s = KeAllocateHandleWithRights(targetProcess, obj, targetRights, targetHandleOut);
ObDereferenceObject(obj);
return s;
}
// Returns process By Id.
PROCESS* KeGetProcessById(uint32_t pid) {
KiProcessInit();
+145 -53
View File
@@ -1,97 +1,189 @@
// Kernel message queue primitive. This file owns bounded queue storage and blocking send/receive behavior.
// Kernel message queue primitive. This file owns bounded queue storage and queue timeout behavior.
#include <stdint.h>
#include <string.h>
#include <kernel/ke_queue.h>
#include <kernel/ke_thread.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_irql.h>
#include <kernel/ob_object.h>
// Implements the queue Destroy operation.
static void KiQueueDestroy(OBJECT* obj) { (void)obj; }
#define QUEUE_TIMEOUT_INFINITE UINT64_MAX
// Implements the queue Create operation.
// Destroys a queue and releases its private state events.
static void KiQueueDestroy(OBJECT* obj) {
QUEUE* queue = (QUEUE*)obj;
if (!queue)
return;
if (queue->NotEmptyEvent) {
ObDereferenceObject(&queue->NotEmptyEvent->Header);
queue->NotEmptyEvent = (EVENT*)0;
}
if (queue->NotFullEvent) {
ObDereferenceObject(&queue->NotFullEvent->Header);
queue->NotFullEvent = (EVENT*)0;
}
}
// Updates waitable state after one message has been inserted.
static void KiQueueNoteInsertedLocked(QUEUE* queue, uint32_t previousCount) {
if (!queue || !queue->NotEmptyEvent || !queue->NotFullEvent)
return;
if (previousCount == 0)
KeSignalEvent(queue->NotEmptyEvent);
if (queue->Count == QUEUE_DEPTH)
KeClearEvent(queue->NotFullEvent);
}
// Updates waitable state after one message has been removed.
static void KiQueueNoteRemovedLocked(QUEUE* queue, uint32_t previousCount) {
if (!queue || !queue->NotEmptyEvent || !queue->NotFullEvent)
return;
if (previousCount == QUEUE_DEPTH)
KeSignalEvent(queue->NotFullEvent);
if (queue->Count == 0)
KeClearEvent(queue->NotEmptyEvent);
}
// Returns the scheduler tick value used by queue finite-timeout accounting.
static uint64_t KiQueueReadSchedulerTick(void) {
KE_SCHED_STATS stats;
KeGetSchedulerStats(&stats);
return stats.Ticks;
}
// Converts a millisecond timeout to at least one scheduler tick.
static uint64_t KiQueueTimeoutTicks(uint64_t timeoutMilliseconds) {
uint64_t ticks = timeoutMilliseconds / SCHED_TICK_MS;
if ((timeoutMilliseconds % SCHED_TICK_MS) != 0)
ticks++;
return ticks ? ticks : 1;
}
// Computes an absolute scheduler-tick deadline for a finite timeout.
static uint64_t KiQueueTimeoutDeadline(uint64_t timeoutMilliseconds) {
uint64_t now = KiQueueReadSchedulerTick();
uint64_t ticks = KiQueueTimeoutTicks(timeoutMilliseconds);
uint64_t deadline = now + ticks;
return deadline < now ? UINT64_MAX : deadline;
}
// Computes the remaining wait time for a finite timeout deadline.
static uint64_t KiQueueRemainingMilliseconds(uint64_t deadlineTick) {
uint64_t now = KiQueueReadSchedulerTick();
if (deadlineTick <= now)
return 0;
uint64_t ticks = deadlineTick - now;
if (ticks > UINT64_MAX / SCHED_TICK_MS)
return UINT64_MAX;
return ticks * SCHED_TICK_MS;
}
// Waits for a queue state event using either an infinite or finite timeout.
static KSTATUS KiQueueWaitForEvent(QUEUE* queue, EVENT* event, uint64_t timeoutMilliseconds, uint64_t deadlineTick) {
if (!queue || !event)
return STATUS_INVALID_PARAMETER;
uint64_t waitMilliseconds = timeoutMilliseconds;
if (timeoutMilliseconds != QUEUE_TIMEOUT_INFINITE) {
waitMilliseconds = KiQueueRemainingMilliseconds(deadlineTick);
if (waitMilliseconds == 0)
return STATUS_TIMEOUT;
}
EVENT* events[1] = { event };
return KeWaitForMultipleEvents(events, 1, 0, waitMilliseconds, (uint32_t*)0);
}
// Creates a fixed-depth queue with waitable occupancy and capacity events.
KSTATUS KeQueueCreate(QUEUE **out) {
if (!out) return STATUS_INVALID_PARAMETER;
*out = (QUEUE*)0;
QUEUE* q = (QUEUE *)ObAllocateObject(ObjectTypeQueue, sizeof(QUEUE));
if (!q) return STATUS_NO_MEMORY;
q->Header.Destroy = KiQueueDestroy;
q->Lock = (SPINLOCK)SPINLOCK_INIT;
q->Head = q->Tail = q->Count = 0;
LIST_INIT(&q->SendWaiters);
LIST_INIT(&q->RecvWaiters);
KSTATUS s = KeEventCreate(EventManualReset, 0, &q->NotEmptyEvent);
if (!KSUCCESS(s)) {
ObDereferenceObject(&q->Header);
return s;
}
s = KeEventCreate(EventManualReset, 1, &q->NotFullEvent);
if (!KSUCCESS(s)) {
ObDereferenceObject(&q->Header);
return s;
}
*out = q;
return STATUS_SUCCESS;
}
// Implements the send Message operation.
KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, bool blocking) {
// Sends one fixed-size queue message, waiting according to the timeout contract.
KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds) {
if (!queue || !msg) return STATUS_INVALID_PARAMETER;
if (blocking && !KeCanWait()) return STATUS_INVALID_IRQL;
THREAD* self = KeGetCurrentThread();
if (timeoutMilliseconds != 0 && !KeCanWait()) return STATUS_INVALID_IRQL;
while (true) {
uint64_t deadlineTick = 0;
if (timeoutMilliseconds != 0 && timeoutMilliseconds != QUEUE_TIMEOUT_INFINITE)
deadlineTick = KiQueueTimeoutDeadline(timeoutMilliseconds);
for (;;) {
uintptr_t flags = KeAcquireSpinlock(&queue->Lock);
if (queue->Count < QUEUE_DEPTH) {
uint32_t previousCount = queue->Count;
queue->Ring[queue->Tail] = *msg;
queue->Tail = (queue->Tail + 1) % QUEUE_DEPTH;
queue->Count++;
// Wake a receiver if one is waiting.
if (!LIST_IS_EMPTY(&queue->RecvWaiters)) {
LIST_ENTRY* node = ListPopHead(&queue->RecvWaiters);
THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink);
KeReleaseSpinlock(&queue->Lock, flags);
KeUnblockThread(t);
} else {
KeReleaseSpinlock(&queue->Lock, flags);
}
KiQueueNoteInsertedLocked(queue, previousCount);
KeReleaseSpinlock(&queue->Lock, flags);
return STATUS_SUCCESS;
}
if (!blocking) {
KeReleaseSpinlock(&queue->Lock, flags);
return STATUS_QUEUE_FULL;
}
KePrepareBlockThread(self, &queue->Header, 0);
ListInsertTail(&queue->SendWaiters, &self->SchedulerLink);
KeSetThreadWaitList(self, &queue->SendWaiters);
KeClearEvent(queue->NotFullEvent);
KeReleaseSpinlock(&queue->Lock, flags);
KeCommitBlockThread(self, 0);
if (timeoutMilliseconds == 0)
return STATUS_WOULD_BLOCK;
KSTATUS s = KiQueueWaitForEvent(queue, queue->NotFullEvent, timeoutMilliseconds, deadlineTick);
if (s == STATUS_TIMEOUT || KFAILED(s))
return s;
}
}
// Implements the receive Message operation.
KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, bool blocking) {
// Receives one fixed-size queue message, waiting according to the timeout contract.
KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds) {
if (!queue || !msg) return STATUS_INVALID_PARAMETER;
if (blocking && !KeCanWait()) return STATUS_INVALID_IRQL;
THREAD* self = KeGetCurrentThread();
if (timeoutMilliseconds != 0 && !KeCanWait()) return STATUS_INVALID_IRQL;
while (true) {
uint64_t deadlineTick = 0;
if (timeoutMilliseconds != 0 && timeoutMilliseconds != QUEUE_TIMEOUT_INFINITE)
deadlineTick = KiQueueTimeoutDeadline(timeoutMilliseconds);
for (;;) {
uintptr_t flags = KeAcquireSpinlock(&queue->Lock);
if (queue->Count > 0) {
uint32_t previousCount = queue->Count;
*msg = queue->Ring[queue->Head];
queue->Head = (queue->Head + 1) % QUEUE_DEPTH;
queue->Count--;
// Wake a sender if one is waiting.
if (!LIST_IS_EMPTY(&queue->SendWaiters)) {
LIST_ENTRY* node = ListPopHead(&queue->SendWaiters);
THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink);
KeReleaseSpinlock(&queue->Lock, flags);
KeUnblockThread(t);
} else {
KeReleaseSpinlock(&queue->Lock, flags);
}
KiQueueNoteRemovedLocked(queue, previousCount);
KeReleaseSpinlock(&queue->Lock, flags);
return STATUS_SUCCESS;
}
if (!blocking) {
KeReleaseSpinlock(&queue->Lock, flags);
return STATUS_QUEUE_EMPTY;
}
KePrepareBlockThread(self, &queue->Header, 0);
ListInsertTail(&queue->RecvWaiters, &self->SchedulerLink);
KeSetThreadWaitList(self, &queue->RecvWaiters);
KeClearEvent(queue->NotEmptyEvent);
KeReleaseSpinlock(&queue->Lock, flags);
KeCommitBlockThread(self, 0);
if (timeoutMilliseconds == 0)
return STATUS_WOULD_BLOCK;
KSTATUS s = KiQueueWaitForEvent(queue, queue->NotEmptyEvent, timeoutMilliseconds, deadlineTick);
if (s == STATUS_TIMEOUT || KFAILED(s))
return s;
}
}
+7 -11
View File
@@ -3,7 +3,6 @@
#include <stddef.h>
#include <stdbool.h>
#include <intrin.h>
#pragma intrinsic(_enable)
#include <kernel/ke_sched.h>
#include <kernel/ke_cpu.h>
#include <kernel/ke_process.h>
@@ -12,6 +11,8 @@
#include <kernel/ke_irql.h>
#include <kernel/ke_apc.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_input.h>
#include <kernel/ke_timer.h>
#include <kernel/ke_list.h>
#include <kernel/mm_virt.h>
#include <kernel/hal/hal_apic.h>
@@ -196,7 +197,6 @@ void KeGetSchedulerStats(KE_SCHED_STATS* out) {
KeReleaseSpinlock(&gSchedLock, flags);
}
// Implements the arm Post Switch Unlock operation.
static void KiArmPostSwitchUnlock(THREAD* thread, uintptr_t cookie) {
// The scheduler lock is intentionally held across KeSwitchContext(). The
@@ -213,7 +213,6 @@ static uintptr_t KiPostSwitchUnlockCookie(THREAD* thread) {
return thread ? thread->SwitchLockCookie : 0x202ULL;
}
// Implements the scheduler Bug operation.
static void KiSchedulerBug(const char* where, THREAD* thread, const char* why) {
KePanic(KE_PANIC_CODE_SCHEDULER,
@@ -298,8 +297,7 @@ static void KiIdleLoop(void) {
// the idle thread through an unusual switch path must not sleep with
// interrupts disabled, otherwise reschedule/TLB shootdown IPIs can
// be delayed forever.
_enable();
__halt();
HalWaitForInterrupt();
}
}
@@ -312,6 +310,7 @@ void KiInitializeSchedulerGlobal(void) {
KSTATUS s = KeCreateProcess("idle", &gIdleProcess);
if (!KSUCCESS(s))
KePanic(KE_PANIC_CODE_INIT, "scheduler: failed to create idle process, status=%u\n", s);
gIdleProcess->Flags |= KE_PROCESS_FLAG_IDLE | KE_PROCESS_FLAG_TERMINATION_PROTECTED;
PROCESS* systemProc;
s = KeCreateSystemProcess(&systemProc);
@@ -463,7 +462,6 @@ void KeUnreadyThread(THREAD* thread) {
KeReleaseSpinlock(&gSchedLock, flags);
}
// Implements the any Runnable For Cpu Locked operation.
static int KiAnyRunnableForCpuLocked(KCPU* cpu) {
if (!cpu) cpu = KeGetCurrentCpu();
@@ -507,7 +505,6 @@ static THREAD* KiPickNextForCpu(KCPU* cpu) {
return cpu->IdleThread;
}
// Implements the request Reschedule operation.
void KiRequestReschedule(void) {
KCPU* cpu = KeGetCurrentCpu();
@@ -592,7 +589,6 @@ void KiScheduleFromInterrupt(void) {
KiSchedule();
}
// Implements the scheduler Mark Thread Waiting operation.
void KiSchedulerMarkThreadWaiting(THREAD* thread) {
if (!thread) return;
@@ -624,7 +620,6 @@ void KiSchedulerNoteVoluntaryBlock(THREAD* thread) {
KeReleaseSpinlock(&gSchedLock, flags);
}
// Completes a wait while the scheduler lock is held.
static void KiWakeThreadLocked(THREAD* thread, KSTATUS status) {
if (!thread) return;
@@ -666,7 +661,6 @@ static void KiWakeThreadLocked(THREAD* thread, KSTATUS status) {
KiPokeRunnableCpusLocked(thread);
}
// Wakes a thread after first removing its scheduler-link wait membership.
void KeUnblockThreadFromWaitListWithStatus(THREAD* thread, KSTATUS status) {
if (!thread) return;
@@ -719,6 +713,7 @@ void KeSchedulerTick(void) {
gSchedStats.Ticks++;
KiWakeExpiredSleepersLocked(gSchedStats.Ticks);
KeInputTimerTick();
int requestReschedule = 0;
@@ -783,11 +778,12 @@ void KeSchedulerTick(void) {
KeReleaseSpinlock(&gSchedLock, flags);
KeTimerTick();
if (requestReschedule)
KiRequestReschedule();
}
// Arms a timeout for the current thread after a wait primitive has prepared it to block.
KSTATUS KeArmCurrentThreadTimeout(uint64_t milliseconds, KSTATUS timeoutStatus) {
if (milliseconds == 0)
+1 -11
View File
@@ -478,10 +478,6 @@ static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) {
DbgPrint("IRP/APC selftest: PASS\n");
}
// Verifies that the PFN database tracks ownership, references, pins, and contiguous runs.
static void KiRunPfnDatabaseSelfTest(void) {
MM_PFN_STATS before;
@@ -654,7 +650,6 @@ static void KiRunMemoryMappingSelfTest(void) {
DbgPrint("MM selftest: PTE map/query/protect/unmap PASS\n");
}
// Verifies sparse VAD commitment and NOACCESS protection metadata.
static void KiRunVadSparseSelfTest(void) {
PROCESS* process = KeGetCurrentProcess();
@@ -756,7 +751,6 @@ static void KiRunVadSparseSelfTest(void) {
DbgPrint("VAD selftest: sparse commit/protect/query/demand-zero PASS\n");
}
// Verifies that writable copy-on-write views split before either alias can modify shared state.
static void KiRunVadCopyOnWriteSelfTest(void) {
PROCESS* process = KeGetCurrentProcess();
@@ -872,7 +866,6 @@ static void KiRunVadCopyOnWriteSelfTest(void) {
DbgPrint("VAD COW selftest: shared views and zero-page split PASS\n");
}
// Verifies that a private resident VAD page can move to swap and fault back in.
static void KiRunVadSwapSelfTest(void) {
if (!MmSwapIsEnabled()) {
@@ -1098,6 +1091,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
wiosb.Status = STATUS_FAILURE;
wiosb.Information = 0;
(void)IoDeleteFile(SYS_SV_LITERAL("asyncio.tmp"));
s = IoCreateFile(SYS_SV_LITERAL("asyncio.tmp"), &wf);
if (!KSUCCESS(s))
KePanic(KE_PANIC_CODE_SELFTEST, "FILE/ASYNC selftest: create write temp failed %u\n", s);
@@ -1161,7 +1155,6 @@ static void KiRunAsyncFileIoSelfTest(void) {
DbgPrint("FILE/ASYNC selftest: PASS\n");
}
// Runs trap Frame Self Test.
static void KiRunTrapFrameSelfTest(void) {
DbgPrint("TRAP selftest: begin\n");
@@ -1180,8 +1173,6 @@ static void KiRunTrapFrameSelfTest(void) {
DbgPrint("TRAP selftest: PASS\n");
}
// Runs core Hardening Self Tests.
void KiRunCoreHardeningSelfTests(void) {
DbgPrint("CORE selftest: begin\n");
@@ -1253,7 +1244,6 @@ void KiRunCoreHardeningSelfTests(void) {
DbgPrint("CORE selftest: PASS\n");
}
// Runs early Kernel Self Tests.
void KiRunEarlyKernelSelfTests(void) {
KSTATUS s = KeRunApplicationProcessorSchedulerSelfTests();
+12 -18
View File
@@ -8,8 +8,6 @@
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#pragma intrinsic(_disable)
#pragma intrinsic(_enable)
#include <ntlite/status.h>
#include <kernel/ke_smp.h>
#include <kernel/ke_cpu.h>
@@ -254,11 +252,10 @@ static KSTATUS KiBuildApTrampoline(void) {
}
_Noreturn void KiApKernelEntry(KCPU* cpu) {
_disable();
(void)HalDisableInterrupts();
if (!cpu) {
while (1) __halt();
}
if (!cpu)
HalStopProcessor();
// APs start from the SIPI reset state, so do not assume the firmware/BSP
// CR4 feature bits are present. The scheduler and context-switch paths
@@ -313,27 +310,26 @@ _Noreturn void KiApKernelEntry(KCPU* cpu) {
cpu->Parked = 0;
// Do not arm the AP's periodic LAPIC timer while it is still in
// the raw park loop. Timer IRQs are scheduler-owned events;
// the raw park loop. Timer IRQs are scheduler-owned events;
// the AP may take them only after it has a current idle thread.
HalTimerConfigureCurrentCpu();
_InterlockedExchange(&cpu->SchedulerActive, 1);
_enable();
HalEnableInterrupts();
KeStartInitialThread(idle);
}
}
// The BSP admits parked APs by storing SchedulerCommand and sending a
// wake IPI. Avoid the classic lost wakeup where the command appears
// wake IPI. Avoid the classic lost wakeup where the command appears
// between the poll and HLT: check once with interrupts disabled, then
// STI;HLT as a single sleep transition.
_disable();
uintptr_t interruptFlags = HalDisableInterrupts();
if (_InterlockedCompareExchange(&cpu->SchedulerCommand, 0, 0) != 0) {
_enable();
HalRestoreInterrupts(interruptFlags);
continue;
}
_enable();
__halt();
HalWaitForInterrupt();
}
}
@@ -367,7 +363,6 @@ static void KiPrepareCpuRecord(uint32_t cpuIndex, const HAL_CPU_INFO* info) {
cpu->SchedulerActive = 0;
}
// Implements the ceil Log2 U32 operation.
static uint32_t KiCeilLog2U32(uint32_t value) {
if (value <= 1u)
@@ -591,7 +586,7 @@ void KeBuildProcessorTopology(void) {
// Creates ap Stack.
static KSTATUS KiAllocateApStack(KCPU* cpu) {
// AP kernel stacks do not need physically contiguous memory. Requiring
// AP kernel stacks do not need physically contiguous memory. Requiring
// MmPfnAllocateContiguous(8) made SMP startup depend on a DMA-style run being
// available, which is fragile on real machines and unrelated to AP startup.
size_t stackBytes = (size_t)AP_STACK_PAGES * PAGE_SIZE;
@@ -612,7 +607,6 @@ static KSTATUS KiAllocateApStack(KCPU* cpu) {
return STATUS_SUCCESS;
}
// Implements the ap Stage Name operation.
static const char* KiApStageName(uint32_t stage) {
switch (stage) {
@@ -806,9 +800,9 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void) {
_ReadWriteBarrier();
_InterlockedExchange(&cpu->SchedulerCommand, 1);
// Parked APs sleep with HLT and have no LAPIC timer armed yet. The
// Parked APs sleep with HLT and have no LAPIC timer armed yet. The
// command store alone is not sufficient; send a fixed-vector IPI only
// to wake the parked loop. ReschedulePending is intentionally left
// to wake the parked loop. ReschedulePending is intentionally left
// clear, so the interrupt exit path will not try to schedule before
// the AP has installed its idle thread.
HalApicSendRescheduleIpi(cpu->LapicId);
+1 -2
View File
@@ -20,7 +20,7 @@ EXTERN KeSetCurrentTssRsp0 : PROC
; -------------------------------------------------------------------------
; void KeSwitchContext(THREAD* from, THREAD* to)
; RCX = from, RDX = to (Microsoft x64)
; RCX = from, RDX = to (Microsoft x64)
; -------------------------------------------------------------------------
KeSwitchContext PROC
mov rax, [rcx + THREAD_FpuContext]
@@ -75,7 +75,6 @@ KeSwitchContext PROC
ret
KeSwitchContext ENDP
; -------------------------------------------------------------------------
; void KeStartInitialThread(THREAD* to)
; RCX = to
+1 -2
View File
@@ -152,7 +152,7 @@ KeSyscallEntry ENDP
; also shows which register held the null target.
;
; KeFaultDispatchFrame signature (MS x64):
; KSTATUS KeFaultDispatchFrame(const KFAULT_FRAME* frame) ; rcx
; KSTATUS KeFaultDispatchFrame(const KFAULT_FRAME* frame) ; rcx
; ---------------------------------------------------------------------------
KFAULT_MAGIC EQU 3146544C5541464Bh ; "KFAULTF1" little-ish
@@ -420,7 +420,6 @@ pf_unhandled_halt:
jmp pf_unhandled_halt
KeFaultPF ENDP
; ---------------------------------------------------------
; Generic CPU exception stubs.
; Each vector gets its own IDT entry and vector number so faults like #UD
+853 -389
View File
File diff suppressed because it is too large. Load diff
+14 -17
View File
@@ -75,7 +75,7 @@ static void KiThreadDestroy(OBJECT* obj) {
if (remaining == 0) {
// The last thread leaving a process is process exit even if user
// code called ExitThread instead of ExitProcess. KeExitProcess /
// code called ExitThread instead of ExitProcess. KeExitProcess /
// KeTerminateProcess set Terminating earlier; this covers the last
// thread voluntarily exiting without an explicit process-exit call.
_InterlockedExchange(&owner->Terminating, 1);
@@ -151,10 +151,10 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
return STATUS_NO_MEMORY;
}
thread->UserStackBase = ustack;
// Ring-3 starts at the image entry via IRET, not CALL. The MS x64 ABI
// Ring-3 starts at the image entry via IRET, not CALL. The MS x64 ABI
// nevertheless lets callees immediately use the caller-provided 32-byte
// home/shadow area above RSP, and MSVC commonly emits a first instruction
// like "mov [rsp+8], rcx" in unoptimized test code. Bias the synthetic
// like "mov [rsp+8], rcx" in unoptimized test code. Bias the synthetic
// entry stack as if a CALL with a normal home area had happened:
//
// RSP % 16 == 8 at function entry
@@ -190,12 +190,12 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
uintptr_t* ksp = (uintptr_t *)thread->KernelStackTop;
*(--ksp) = userStackTop;
*(--ksp) = 0x23ULL;
*(--ksp) = userStackTop;
*(--ksp) = 0x202ULL;
*(--ksp) = 0x1BULL;
*(--ksp) = entry;
*(--ksp) = arg;
*(--ksp) = 0x23ULL;
*(--ksp) = userStackTop;
*(--ksp) = 0x202ULL;
*(--ksp) = 0x1BULL;
*(--ksp) = entry;
*(--ksp) = arg;
ksp -= sizeof(KCONTEXT) / sizeof(uintptr_t);
KCONTEXT* ctx = (KCONTEXT *)ksp;
@@ -233,7 +233,7 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
KeInitThreadFpuContext(thread);
// Hand the creator a short-lived reference before making the thread
// runnable. On SMP a new user thread can run and exit before
// runnable. On SMP a new user thread can run and exit before
// KeCreateThread() returns; without this reference, callers that merely
// read ThreadId for logging or syscall output can race the scheduler's
// dead-thread dereference and touch freed pool.
@@ -332,8 +332,8 @@ static KSTATUS KiCreateKernelThreadWithAffinity(uintptr_t fn, uintptr_t arg,
// Build the initial stack: arg, fn, then KCONTEXT.
uintptr_t* ksp = (uintptr_t*)thread->KernelStackTop;
*(--ksp) = arg; // popped second (-> r12)
*(--ksp) = fn; // popped first (-> rbx)
*(--ksp) = arg; // popped second (-> r12)
*(--ksp) = fn; // popped first (-> rbx)
ksp -= sizeof(KCONTEXT) / sizeof(uintptr_t);
KCONTEXT* ctx = (KCONTEXT*)ksp;
@@ -351,9 +351,9 @@ static KSTATUS KiCreateKernelThreadWithAffinity(uintptr_t fn, uintptr_t arg,
KeReleaseSpinlock(&owner->HandleLock, flags);
// Hand the creator a short-lived reference before making the kernel
// thread runnable. Kernel workers can run, signal their completion event,
// thread runnable. Kernel workers can run, signal their completion event,
// exit, and be reaped on another CPU before KeCreateKernelThread()'s
// caller drops its returned pointer. The live scheduler reference remains
// caller drops its returned pointer. The live scheduler reference remains
// separate and is released by KeSchedulerPostSwitch() after the dead
// thread has switched away.
if (out) {
@@ -395,7 +395,6 @@ void KePrepareBlockThread(THREAD* thread, OBJECT* waitObject, uint8_t alertable)
thread->WaitRundownContext = (void*)0;
}
// Updates thread Wait List.
void KeSetThreadWaitList(THREAD* thread, LIST_ENTRY* waitListHead) {
KeAssertIrqlAtOrBelow(DISPATCH_LEVEL);
@@ -483,14 +482,12 @@ KSTATUS KeCommitBlockThread(THREAD* thread, uint8_t alertable) {
return thread->WaitStatus;
}
// Implements the block Thread operation.
KSTATUS KeBlockThread(THREAD* thread, OBJECT* waitObject, uint8_t alertable) {
KePrepareBlockThread(thread, waitObject, alertable);
return KeCommitBlockThread(thread, alertable);
}
// Implements the unblock Thread With Status operation.
void KeUnblockThreadWithStatus(THREAD* thread, KSTATUS status) {
if (!thread) return;
+219
View File
@@ -0,0 +1,219 @@
// Kernel waitable timer objects. This file owns timer deadlines and timer wait signaling.
#include <stdint.h>
#include <string.h>
#include <kernel/ke_timer.h>
#include <kernel/ke_sched.h>
#include <kernel/ob_object.h>
#define TIMER_SIGNAL_BATCH 32u
static LIST_ENTRY gTimerList;
static SPINLOCK gTimerLock;
static uint8_t gTimerInitialized;
// Initializes the global timer list on first use.
static void KiTimerEnsureInitialized(void) {
if (gTimerInitialized)
return;
gTimerLock = (SPINLOCK)SPINLOCK_INIT;
LIST_INIT(&gTimerList);
gTimerInitialized = 1;
}
// Reads the scheduler tick value used by timer deadlines.
static uint64_t KiTimerReadSchedulerTick(void) {
KE_SCHED_STATS stats;
KeGetSchedulerStats(&stats);
return stats.Ticks;
}
// Converts a millisecond interval to at least one scheduler tick.
static uint64_t KiTimerMillisecondsToTicks(uint64_t milliseconds) {
if (milliseconds == 0)
return 0;
uint64_t ticks = milliseconds / SCHED_TICK_MS;
if ((milliseconds % SCHED_TICK_MS) != 0)
ticks++;
return ticks ? ticks : 1;
}
// Adds a tick interval to the current scheduler tick with saturation.
static uint64_t KiTimerDeadlineFromNow(uint64_t intervalTicks) {
uint64_t now = KiTimerReadSchedulerTick();
uint64_t due = now + intervalTicks;
return due < now ? UINT64_MAX : due;
}
// Removes an active timer from the global deadline list while the timer lock is held.
static uint8_t KiTimerRemoveLocked(KTIMER* timer) {
if (!timer || !timer->Linked)
return 0;
ListRemove(&timer->Link);
timer->Linked = 0;
timer->Active = 0;
timer->DueTick = 0;
timer->PeriodTicks = 0;
return 1;
}
// Implements the timer Destroy operation.
static void KiTimerDestroy(OBJECT* obj) {
KTIMER* timer = (KTIMER*)obj;
if (!timer)
return;
KiTimerEnsureInitialized();
uintptr_t flags = KeAcquireSpinlock(&gTimerLock);
(void)KiTimerRemoveLocked(timer);
KeReleaseSpinlock(&gTimerLock, flags);
if (timer->Event) {
ObDereferenceObject(&timer->Event->Header);
timer->Event = (EVENT*)0;
}
}
// Creates an unset waitable timer object.
KSTATUS KeTimerCreate(KTIMER** out) {
if (!out)
return STATUS_INVALID_PARAMETER;
*out = (KTIMER*)0;
KiTimerEnsureInitialized();
KTIMER* timer = (KTIMER*)ObAllocateObject(ObjectTypeTimer, sizeof(KTIMER));
if (!timer)
return STATUS_NO_MEMORY;
timer->Header.Destroy = KiTimerDestroy;
LIST_INIT(&timer->Link);
KSTATUS s = KeEventCreate(EventAutoReset, 0, &timer->Event);
if (!KSUCCESS(s)) {
ObDereferenceObject(&timer->Header);
return s;
}
*out = timer;
return STATUS_SUCCESS;
}
// Arms or rearms a timer with a relative due time and optional period.
KSTATUS KeTimerSet(KTIMER* timer, uint64_t dueTimeMilliseconds, uint64_t periodMilliseconds) {
if (!timer || !timer->Event)
return STATUS_INVALID_PARAMETER;
KiTimerEnsureInitialized();
uint64_t dueTicks = KiTimerMillisecondsToTicks(dueTimeMilliseconds);
uint64_t periodTicks = KiTimerMillisecondsToTicks(periodMilliseconds);
uint8_t dropListReference = 0;
uint8_t addListReference = 0;
uint8_t signalNow = dueTicks == 0;
EVENT* event = timer->Event;
ObReferenceObject(&event->Header);
uintptr_t flags = KeAcquireSpinlock(&gTimerLock);
if (timer->Linked) {
if (signalNow && periodTicks == 0) {
dropListReference = KiTimerRemoveLocked(timer);
} else {
timer->DueTick = KiTimerDeadlineFromNow(signalNow ? periodTicks : dueTicks);
timer->PeriodTicks = periodTicks;
timer->Active = 1;
}
} else if (!signalNow || periodTicks != 0) {
timer->DueTick = KiTimerDeadlineFromNow(signalNow ? periodTicks : dueTicks);
timer->PeriodTicks = periodTicks;
timer->Active = 1;
timer->Linked = 1;
ListInsertTail(&gTimerList, &timer->Link);
addListReference = 1;
}
KeReleaseSpinlock(&gTimerLock, flags);
if (addListReference)
ObReferenceObject(&timer->Header);
if (dropListReference)
ObDereferenceObject(&timer->Header);
KeClearEvent(event);
if (signalNow)
KeSignalEvent(event);
ObDereferenceObject(&event->Header);
return STATUS_SUCCESS;
}
// Cancels a timer and clears any retained signal.
KSTATUS KeTimerCancel(KTIMER* timer) {
if (!timer || !timer->Event)
return STATUS_INVALID_PARAMETER;
KiTimerEnsureInitialized();
uintptr_t flags = KeAcquireSpinlock(&gTimerLock);
uint8_t hadListReference = KiTimerRemoveLocked(timer);
KeReleaseSpinlock(&gTimerLock, flags);
if (hadListReference)
ObDereferenceObject(&timer->Header);
KeClearEvent(timer->Event);
return STATUS_SUCCESS;
}
// Returns the event that backs timer waits.
EVENT* KeTimerWaitEvent(KTIMER* timer) {
if (!timer)
return (EVENT*)0;
return timer->Event;
}
// Services expired timers from the scheduler tick path.
void KeTimerTick(void) {
KiTimerEnsureInitialized();
EVENT* events[TIMER_SIGNAL_BATCH];
KTIMER* listReferencesToDrop[TIMER_SIGNAL_BATCH];
uint32_t count = 0;
uint32_t dropCount = 0;
uint64_t now = KiTimerReadSchedulerTick();
uintptr_t flags = KeAcquireSpinlock(&gTimerLock);
LIST_ENTRY* node = gTimerList.Fwd;
while (node != &gTimerList && count < TIMER_SIGNAL_BATCH) {
LIST_ENTRY* next = node->Fwd;
KTIMER* timer = CONTAINING_RECORD(node, KTIMER, Link);
if (timer->Active && timer->DueTick <= now) {
if (timer->Event) {
ObReferenceObject(&timer->Event->Header);
events[count++] = timer->Event;
}
if (timer->PeriodTicks != 0) {
uint64_t due = now + timer->PeriodTicks;
timer->DueTick = due < now ? UINT64_MAX : due;
} else {
if (KiTimerRemoveLocked(timer))
listReferencesToDrop[dropCount++] = timer;
}
}
node = next;
}
KeReleaseSpinlock(&gTimerLock, flags);
for (uint32_t i = 0; i < dropCount; i++)
ObDereferenceObject(&listReferencesToDrop[i]->Header);
for (uint32_t i = 0; i < count; i++) {
KeSignalEvent(events[i]);
ObDereferenceObject(&events[i]->Header);
}
}
+10 -20
View File
@@ -3,8 +3,6 @@
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#pragma intrinsic(_disable)
#pragma intrinsic(_enable)
#include <ntlite/boot_info.h>
#include <kernel/mm_phys.h>
#include <kernel/mm_pfn.h>
@@ -175,7 +173,6 @@ static void KiResolveSystemImports(BOOT_INFO* bootInfo) {
}
}
// Phase Helper: Initialize dynamically loaded .sys drivers.
static void KiDriversIniCb(const char* section, const char* key,
const char* val, void* ud) {
@@ -199,7 +196,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
for (uint32_t i = 0; i < bootInfo->ModuleCount; i++) {
BOOT_MODULE* mod = &bootInfo->Modules[i];
// Skip modules that are already mapped (Kernel/HAL)
// Skip modules that are already mapped (Kernel/HAL)
// or have no physical data (invalid).
if (mod->VirtBase != 0 || mod->PhysBase == 0) continue;
@@ -323,7 +320,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
DbgPrint("KiInitializeDrivers: Failed to resolve imports for runtime driver: %s\n", runtimeKeys[i]);
continue;
}
// Runtime modules may also export DDIs to drivers loaded later. Boot
// Runtime modules may also export DDIs to drivers loaded later. Boot
// drivers were already registered above; keep the module model uniform.
LdrRegisterModule(runtimeKeys[i], mappedBase);
@@ -490,9 +487,8 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
// Physical & Virtual Memory initialization.
if (!MmInitializePhysical(&bootInfo->MemoryMap, bootInfo->PhysicalMapBase)) {
_disable(); while (true) __halt(); // there's nothing we can do
}
if (!MmInitializePhysical(&bootInfo->MemoryMap, bootInfo->PhysicalMapBase))
HalStopProcessor();
PHYSICAL_ADDRESS pml4 = (PHYSICAL_ADDRESS)__readcr3();
// The early virtual allocator starts small because it only needs bootstrap page tables before the PMM is online.
@@ -523,17 +519,15 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
};
KSTATUS s = HalInitialize(&halParams);
if (!KSUCCESS(s)) {
_disable(); while (true) __halt(); // there's nothing we can do.
}
if (!KSUCCESS(s))
HalStopProcessor();
const HAL_API* hal = HalGetApi();
gKmainHal = hal;
KeInitDebug(hal->Screen.PrintChar, bootInfo->Debug);
KeInitDebug(hal, bootInfo->Debug);
KeTimeInitialize(bootInfo, hal);
KeDisplayInitialize(hal);
KeSetDebugConsoleWriter(KeDisplayDebugPrintChar);
// CPU startup intentionally lives after HAL initialization but before IRQs
// and scheduler startup. HAL owns ACPI/MADT discovery and LAPIC INIT/SIPI;
@@ -595,11 +589,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
DbgPrint("KernelMain: Handing over to scheduler...\n");
// Ignite the system!
_enable();
// The Idle Loop: The CPU rests here whenever all other threads are waiting on events.
while (true) {
__halt();
}
// The bootstrap CPU becomes the scheduler idle context once interrupts are enabled.
while (true)
HalWaitForInterrupt();
}
+21 -5
View File
@@ -233,11 +233,31 @@ void LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) {
// Implements the ldr Register Module operation.
void LdrRegisterModule(const char* name, uintptr_t base) {
if (gModuleCount < MAX_MODULES) {
KiRegisterModuleEntry(&gModules[gModuleCount], name, base, 0);
size_t size = base ? LdrGetImageSize(base) : 0;
KiRegisterModuleEntry(&gModules[gModuleCount], name, base, size);
gModuleCount++;
}
}
// Finds the loaded kernel module that owns an address.
const char* LdrFindKernelModuleByAddress(uintptr_t address, uintptr_t* baseOut, size_t* sizeOut) {
if (baseOut) *baseOut = 0;
if (sizeOut) *sizeOut = 0;
for (uint32_t i = 0; i < gModuleCount; i++) {
uintptr_t base = gModules[i].Base;
size_t size = gModules[i].Size;
if (!base || !size)
continue;
if (address >= base && address < base + size) {
if (baseOut) *baseOut = base;
if (sizeOut) *sizeOut = size;
return gModules[i].Name;
}
}
return "unknown";
}
// Returns global Module Base.
static uintptr_t KiFindGlobalModuleBase(const char* name) {
return KiFindModuleBaseInTable(gModules, gModuleCount, KiCanonicalModuleName(name));
@@ -327,7 +347,6 @@ size_t LdrGetMappedImageSize(PROCESS* process, uintptr_t base) {
return LdrGetImageSize(base);
}
// Releases loaded Image For Process.
static void KiFreeLoadedImageForProcess(PROCESS* process, uintptr_t imageBase, bool user) {
if (!imageBase) return;
@@ -450,7 +469,6 @@ KSTATUS LdrProtectImageForProcess(PROCESS* process, uintptr_t imageBase, bool us
return s;
}
// Entry point lookup.
uintptr_t LdrGetEntryPoint(uintptr_t base) {
uint32_t pe_offset = *(uint32_t*)(base + 0x3C);
@@ -720,7 +738,6 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
LdrDbg("LDR: module mapped pid=%u file=%s base=%016llx size=%llx\n",
process->ProcessId, filename, (unsigned long long)imageBase, (unsigned long long)LdrGetMappedImageSize(process, imageBase));
s = LdrResolveImportsForProcess(process, imageBase);
if (!KSUCCESS(s)) {
KiFreeLoadedImageForProcess(process, imageBase, true);
@@ -733,7 +750,6 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
return s;
}
s = LdrRegisterProcessModule(process, filename, imageBase);
if (!KSUCCESS(s)) {
KiFreeLoadedImageForProcess(process, imageBase, true);
-1
View File
@@ -268,7 +268,6 @@ static IMAGE_SECTION_OBJECT* KiFindImageSectionLocked(const IO_FILE_IDENTITY* id
return (IMAGE_SECTION_OBJECT*)0;
}
// Verifies that a cached image section is still backed by the same file generation.
static KSTATUS KiValidateImageSectionBackingFile(IMAGE_SECTION_OBJECT* section) {
if (!section || !section->BackingFile)
+5 -7
View File
@@ -253,7 +253,6 @@ static KSTATUS KiFileCacheReclaimOnePage(FILE_OBJECT* skipFile, uint64_t skipOff
return STATUS_NOT_FOUND;
}
static void KiFileCacheAddReadAheadStat(uint64_t* field, uint64_t value) {
uintptr_t flags = KeAcquireSpinlock(&gFileCacheLock);
*field += value;
@@ -719,7 +718,6 @@ static KSTATUS KiFileCacheCompleteLoadingPage(MM_FILE_CACHE_ENTRY* entry,
return STATUS_SUCCESS;
}
static uint32_t KiFileCacheChooseForegroundBatchPages(void) {
uintptr_t flags = KeAcquireSpinlock(&gFileCacheLock);
uint64_t resident = gFileCacheResidentPages;
@@ -936,9 +934,11 @@ static KSTATUS KiFileCacheGetWritablePage(FILE_OBJECT* file,
}
memset(page, 0, PAGE_SIZE);
uint32_t validBytes = PAGE_SIZE;
if (oldFileSize > aligned && oldFileSize - aligned < PAGE_SIZE)
validBytes = (uint32_t)(oldFileSize - aligned);
uint32_t validBytes = 0;
if (oldFileSize > aligned) {
uint64_t oldBytes = oldFileSize - aligned;
validBytes = oldBytes < PAGE_SIZE ? (uint32_t)oldBytes : PAGE_SIZE;
}
s = KiFileCacheCompleteLoadingPage(entry, frame, validBytes, outPage, (uint64_t*)0);
if (!KSUCCESS(s))
@@ -967,7 +967,6 @@ static void KiFileCacheMarkPageDirty(FILE_OBJECT* file, uint64_t aligned, uint32
}
}
KSTATUS MmFileCacheGetWritablePage(FILE_OBJECT* file, uint64_t offset,
uint64_t fileSizeBefore, int needExistingBytes,
MM_FILE_CACHE_PAGE* outPage) {
@@ -1180,7 +1179,6 @@ KSTATUS MmFileCacheDropOnePageForPressure(void) {
return KiFileCacheReclaimOnePage((FILE_OBJECT*)0, 0);
}
KSTATUS MmFileCacheScheduleReadAhead(FILE_OBJECT* file, uint64_t offset, uint32_t pages) {
if (!file || pages == 0)
return STATUS_INVALID_PARAMETER;
-6
View File
@@ -141,7 +141,6 @@ static void KiRegisterManagedRange(PHYSICAL_ADDRESS base,
}
}
// Returns whether the future page-replacement code may consider this frame a candidate.
static int KiPfnEvictableLocked(const MM_PFN* entry) {
if (!entry || entry->PinCount)
@@ -156,8 +155,6 @@ static int KiPfnEvictableLocked(const MM_PFN* entry) {
entry->Usage == MmPfnUseCow;
}
// Returns whether a standby frame can be counted as immediately reclaimable.
static int KiPfnReclaimableLocked(const MM_PFN* entry) {
if (!entry || entry->PinCount || entry->MappedCount)
@@ -395,7 +392,6 @@ __declspec(dllexport) MM_PFN* MmPfnGet(PHYSICAL_ADDRESS frame) {
return &gPfnDatabase[pfn];
}
// Returns the direct-map virtual address for a managed physical frame.
__declspec(dllexport) void* MmPfnToVirtual(PHYSICAL_ADDRESS frame) {
if (MmPfnIndex(frame) == MM_PFN_INVALID_INDEX)
@@ -669,8 +665,6 @@ __declspec(dllexport) KSTATUS MmPfnRemoveMapping(PHYSICAL_ADDRESS frame) {
return STATUS_SUCCESS;
}
// Moves a clean, unshared frame to standby so pressure can reclaim it later.
__declspec(dllexport) KSTATUS MmPfnMoveToStandby(PHYSICAL_ADDRESS frame) {
size_t pfn = MmPfnIndex(frame);
+2 -7
View File
@@ -77,7 +77,6 @@ static PHYSICAL_ADDRESS KiEnsureTable(PHYSICAL_ADDRESS parentPhys,
return childPhys;
}
// Initializes the page-table manager around the bootloader-created PML4.
void MmPteInitialize(PHYSICAL_ADDRESS bootPml4Phys) {
gPml4Phys = bootPml4Phys;
@@ -246,7 +245,6 @@ __declspec(dllexport) KSTATUS MmPteMapPage(uintptr_t virt, PHYSICAL_ADDRESS phys
return STATUS_SUCCESS;
}
// Removes one 4 KiB leaf PTE from the shared page tables.
__declspec(dllexport) KSTATUS MmPteUnmapPage(uintptr_t virt) {
KiPteStatInc(&gPteStats.UnmapCalls);
@@ -285,7 +283,6 @@ __declspec(dllexport) KSTATUS MmPteUnmapPage(uintptr_t virt) {
return STATUS_SUCCESS;
}
// Replaces leaf flags for a 4 KiB range and refuses huge pages instead of silently corrupting them.
__declspec(dllexport) KSTATUS MmPteProtectRange(uintptr_t virt, size_t size, uint64_t flags) {
KiPteStatInc(&gPteStats.ProtectCalls);
@@ -346,10 +343,9 @@ __declspec(dllexport) KSTATUS MmPteProtectRange(uintptr_t virt, size_t size, uin
return status;
}
// Tests and clears the hardware accessed bit on a mapped 4 KiB leaf. This is
// Tests and clears the hardware accessed bit on a mapped 4 KiB leaf. This is
// used by the pressure trimmer as a second-chance test for clean file-backed
// pages. If the page was accessed recently, reclaim skips it once and flushes
// pages. If the page was accessed recently, reclaim skips it once and flushes
// the TLB entry so future accesses can set PAGE_ACCESSED again.
__declspec(dllexport) int MmPteTestAndClearAccessed(uintptr_t virt) {
if (!gPml4Phys)
@@ -572,4 +568,3 @@ void MmPteQueryStats(MM_PTE_STATS* out) {
*out = gPteStats;
}
+1 -1
View File
@@ -93,7 +93,7 @@ KSTATUS MmSwapInitialize(SYS_STRING_VIEW path, uint64_t sizeBytes) {
// Swap slots are never read before MmSwapPageOut overwrites the whole
// page, so the filesystem may allocate backing clusters without zeroing
// them. Normal files still get zero-on-extend semantics.
// them. Normal files still get zero-on-extend semantics.
file->Flags |= IO_FILE_FLAG_NO_CACHE | IO_FILE_FLAG_SKIP_ZERO_EXTEND;
s = IoTruncateFile(file, sizeBytes);
+25 -20
View File
@@ -1,6 +1,17 @@
// Kernel virtual-memory manager. This file owns kernel pool allocation, vad bookkeeping, and user-copy validation.
// Page-table construction lives in mm_pte.c so the higher VMM can grow toward VADs, faults, COW, and swap without mixing those policies with raw PTE edits.
// Virtual address layout:
// 0000000000001000-00007FFFFFFFF000 is the lower-half user range.
// 0000100000000000-00007FFFFFFFF000 is the current VAD allocation arena.
// FFFF800000000000-FFFF87FFFFFFFFFF is reserved for kernel images and fixed kernel mappings.
// FFFF880000000000-FFFF8FFFFFFFFFFF is the physical direct map.
// FFFF900000000000-FFFF97FFFFFFFFFF is the PCI MMIO aperture owned by pci.sys.
// FFFFC00000000000-FFFFDFFFFFFFFFFF is the kernel pool.
//
// The current VM model uses one shared PML4, so user pointers are globally
// meaningful while protection and lifetime are still tracked per process VAD.
// Kernel pool:
// A boundary-tag allocator starts at POOL_BASE and commits pages on demand.
@@ -780,7 +791,6 @@ static MM_VAD_STATS gVadStats;
#define MM_VAD_ARENA_BASE 0x0000100000000000ULL
// Initializes the VAD list after the shared page table and kernel pool are ready.
static void KiVadInitialize(void) {
LIST_INIT(&gVadList);
@@ -820,7 +830,6 @@ void MmInitializeVirtual(PHYSICAL_ADDRESS bootPml4Phys, size_t poolSize) {
(void)MmPfnPin(gVadZeroPageFrame);
}
// Allocates zeroed kernel-pool memory from the non-user heap.
__declspec(dllexport) void* MmAllocatePool(size_t size) {
return KiHeapAllocEx(&gPoolHeap, size, POOL_TAG_DEFAULT, (uintptr_t)_ReturnAddress());
@@ -1100,11 +1109,11 @@ static void KiVadWaitForPagingComplete(MM_VAD_PAGE* page) {
KeYield();
}
// Atomically claims the transient paging bit. This is deliberately not a spinlock:
// Atomically claims the transient paging bit. This is deliberately not a spinlock:
// a page-in/page-out may allocate frames or do filesystem I/O, so waiters must yield
// instead of holding IRQL-raised locks. Without this CAS, two CPUs faulting the same
// instead of holding IRQL-raised locks. Without this CAS, two CPUs faulting the same
// swapped/demand-zero page can both allocate/read/map it; one map fails and rolls
// metadata back to SWAPPED while the other has already published a valid PTE. That
// metadata back to SWAPPED while the other has already published a valid PTE. That
// leaves stale or freed swap slots behind and corrupts user data under pressure.
static int KiVadTryBeginPaging(MM_VAD_PAGE* page) {
if (!page) return 0;
@@ -1172,7 +1181,6 @@ static KSTATUS KiVadFillFileBackedPage(MM_VAD* vad, size_t pageIndex, PHYSICAL_A
return STATUS_SUCCESS;
}
// Materializes one image-backed prototype page or zero-fill page for an image VAD.
static KSTATUS KiVadFillImageBackedPage(MM_VAD* vad, size_t pageIndex, int wantWrite) {
if (!vad || !vad->ImageSection || pageIndex >= vad->PageCount)
@@ -1277,7 +1285,7 @@ retry_after_paging:
if (!KiVadTryBeginPaging(page))
goto retry_after_paging;
// Re-check after claiming the transition bit. Another CPU may have resolved
// Re-check after claiming the transition bit. Another CPU may have resolved
// the page between our initial wait/access check and the CAS above.
s = KiVadCheckPageAccess(page, wantWrite, wantExecute);
if (!KSUCCESS(s)) {
@@ -1906,7 +1914,6 @@ static KSTATUS KiCopyToUserPaged(void* userDst, const void* kernelSrc, size_t si
return KiCopyUserPaged(userDst, kernelSrc, size, 1);
}
// Reserves a VAD in the global user VA arena without committing physical pages.
KSTATUS MmVadReserve(PROCESS* process, size_t size, size_t alignment,
uint64_t pageFlags, uint32_t vadFlags,
@@ -2071,9 +2078,9 @@ KSTATUS MmVadCreateCommitted(PROCESS* process, size_t size, size_t alignment,
return STATUS_SUCCESS;
}
// Creates a demand-paged file-backed user mapping. The VAD owns a reference to
// Creates a demand-paged file-backed user mapping. The VAD owns a reference to
// the FILE_OBJECT so the caller may close the original file handle immediately
// after a view is established. Pages are read from the file on first fault and
// after a view is established. Pages are read from the file on first fault and
// then behave like ordinary private user pages: if memory pressure trims them
// after they are resident, the swap path preserves later private modifications.
KSTATUS MmVadCreateFileMapping(PROCESS* process, FILE_OBJECT* file,
@@ -2133,7 +2140,6 @@ KSTATUS MmVadCreateFileMapping(PROCESS* process, FILE_OBJECT* file,
return STATUS_SUCCESS;
}
// Creates an image-backed VAD whose pages fault from a shared image section object.
KSTATUS MmVadCreateImageSectionMapping(PROCESS* process, IMAGE_SECTION_OBJECT* section,
uint64_t pageFlags, uint32_t vadFlags,
@@ -2425,9 +2431,9 @@ KSTATUS MmVadCreateSharedViewFromFrames(PROCESS* targetProcess,
}
// Returns whether a resident file-backed page can be discarded instead of
// swapped. This is safe only while the page is still clean by construction:
// swapped. This is safe only while the page is still clean by construction:
// read-only mappings cannot have been modified, and the backing file can
// repopulate the page on the next fault. Writable/private file mappings still
// repopulate the page on the next fault. Writable/private file mappings still
// fall back to the swap path because this VMM does not yet track dirty bits.
static int KiVadPageCanDiscardCleanFile(const MM_VAD* vad, const MM_VAD_PAGE* page) {
if (!vad || !page || vad->Type != MmVadMappedFile)
@@ -2463,7 +2469,7 @@ static int KiVadPageCanDiscardCleanFileAfterPagingClaim(const MM_VAD* vad, const
return (page->Flags & MM_VAD_PAGE_PAGING) != 0;
}
// Returns whether a resident VAD page is safe to move to swap. Clean read-only
// Returns whether a resident VAD page is safe to move to swap. Clean read-only
// file-backed pages are intentionally excluded; the pressure trimmer discards
// them and reloads them from the file instead of wasting swap slots.
static int KiVadPageCanSwap(const MM_VAD_PAGE* page) {
@@ -2498,7 +2504,7 @@ static int KiVadPageCanSwapAfterPagingClaim(const MM_VAD_PAGE* page) {
return 1;
}
// Discards one resident clean file-backed page. No swap slot is allocated; the
// Discards one resident clean file-backed page. No swap slot is allocated; the
// VAD keeps its FILE_BACKED state and refaults the page from the file later.
static KSTATUS KiVadDiscardCleanFilePage(MM_VAD* vad, size_t pageIndex, void** baseOut) {
if (baseOut) *baseOut = (void*)0;
@@ -2516,9 +2522,9 @@ static KSTATUS KiVadDiscardCleanFilePage(MM_VAD* vad, size_t pageIndex, void** b
uintptr_t va = vad->Base + pageIndex * PAGE_SIZE;
// Second chance for hot mapped-file pages. Dropping the active video/WAD
// Second chance for hot mapped-file pages. Dropping the active video/WAD
// working set is much worse than swapping cold anonymous pages: it causes
// repeated file reads, page-table churn, and global TLB shootdowns. Clear
// repeated file reads, page-table churn, and global TLB shootdowns. Clear
// the hardware accessed bit and skip the page once; if it stays cold until
// a later pressure scan, it may be discarded then.
if (MmPteTestAndClearAccessed(va))
@@ -2580,7 +2586,7 @@ static KSTATUS KiVadTrimPageToSwap(MM_VAD* vad, size_t pageIndex, void** baseOut
return STATUS_NOT_FOUND;
// Claim the transient paging bit atomically before pinning/removing the
// user PTE. This serializes page-out against another page-out and against
// user PTE. This serializes page-out against another page-out and against
// a concurrent fault on the same VAD page without holding a spinlock across
// pagefile I/O.
if (!KiVadTryBeginPaging(page))
@@ -2655,7 +2661,7 @@ KSTATUS MmVadTrimOnePageForTest(PROCESS* process, void** baseOut) {
// Reclaims one resident user page from any process during allocation pressure.
// Prefer clean file-backed pages because they can be dropped and re-read without
// consuming swap. Fall back to anonymous/private swap only when no such page is
// consuming swap. Fall back to anonymous/private swap only when no such page is
// available.
KSTATUS MmVadTrimOnePageForPressure(void) {
if (!gVadListReady) return STATUS_NOT_SUPPORTED;
@@ -2798,7 +2804,6 @@ KSTATUS MmVadResolveUserPageFault(PROCESS* process, uintptr_t faultAddress, uint
return STATUS_SUCCESS;
}
// Resolves a page fault by looking up the owning VAD in the global user arena.
// The single address space intentionally allows the kernel and other processes
// to touch any lower-half mapping, so fault ownership is derived from the VAD
+8 -8
View File
@@ -81,7 +81,7 @@ KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKE
}
// Implements the net Socket Bind operation.
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) {
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
@@ -97,7 +97,7 @@ KSTATUS NetSocketListen(SOCKET_OBJECT* socket, uint32_t backlog) {
}
// Implements the net Socket Accept operation.
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut) {
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut) {
if (!acceptedOut)
return STATUS_INVALID_PARAMETER;
*acceptedOut = NULL;
@@ -127,7 +127,7 @@ KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_
}
// Implements the net Socket Connect operation.
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) {
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
@@ -165,7 +165,7 @@ KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uin
}
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags,
const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut) {
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) {
if (sentOut)
*sentOut = 0;
if (!buffer && length)
@@ -184,7 +184,7 @@ KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length
}
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags,
SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut) {
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut) {
if (receivedOut)
*receivedOut = 0;
if (!buffer && length)
@@ -238,8 +238,8 @@ KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returned
return KSUCCESS(st) ? provider->Poll(providerSocket, events, returnedEventsOut) : st;
}
// Implements the net Resolve Host Ipv4 operation.
KSTATUS NetResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut) {
// Implements the net Resolve Host operation.
KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) {
if (!name || !addressOut)
return STATUS_INVALID_PARAMETER;
@@ -247,5 +247,5 @@ KSTATUS NetResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressO
if (!provider)
return STATUS_NOT_SUPPORTED;
return provider->ResolveHostIpv4(name, addressOut);
return provider->ResolveHost(name, family, addressOut);
}
+1 -1
View File
@@ -19,7 +19,7 @@ static KSTATUS NetValidateStackProvider(const NET_STACK_PROVIDER* p) {
!p->Bind || !p->Listen || !p->Accept || !p->Connect ||
!p->Send || !p->Receive || !p->SendTo || !p->ReceiveFrom ||
!p->Shutdown || !p->SetOption || !p->GetOption || !p->Poll ||
!p->ResolveHostIpv4) {
!p->ResolveHost) {
return STATUS_INVALID_PARAMETER;
}
+1
View File
@@ -26,6 +26,7 @@ static uint32_t KiObjectPoolTag(OBJECT_TYPE type) {
case ObjectTypeSurface: return MM_POOL_TAG('s', 'u', 'r', 'f');
case ObjectTypeInputQueue: return MM_POOL_TAG('i', 'n', 'p', 'q');
case ObjectTypeImageSection: return MM_POOL_TAG('i', 'm', 's', 'e');
case ObjectTypeTimer: return MM_POOL_TAG('t', 'm', 'r', ' ');
default: return MM_POOL_TAG('o', 'b', 'j', ' ');
}
}