Files
nt-lite/code/kernel/ke_selftest.c
T

760 lines
27 KiB
C

// ke_selftest.c - temporary kernel bring-up self-tests for execution/I/O model.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/ke_apc.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_event.h>
#include <kernel/ke_irp.h>
#include <kernel/ke_irql.h>
#include <kernel/ke_sched.h>
#include <ntlite/status.h>
#include <kernel/ke_thread.h>
#include <kernel/io_file.h>
#include <ntlite/ddi/io_driver.h>
#include <kernel/ke_trap.h>
#include <kernel/mm_virt.h>
#include <kernel/ob_object.h>
// -------------------------------------------------------------------------
// Kernel APC self-test
// -------------------------------------------------------------------------
typedef struct KI_APC_SELFTEST_STATE {
EVENT* ReadyEvent;
EVENT* GoEvent;
EVENT* DoneEvent;
EVENT* FinishedEvent;
THREAD* WorkerThread;
volatile int SelfDelivered;
volatile int DeferredDelivered;
volatile int CrossDelivered;
} KI_APC_SELFTEST_STATE;
static void KiApcSelfTestKernelRoutine(KAPC* apc,
void** normalRoutine,
void** normalContext,
void** systemArgument1,
void** systemArgument2) {
DbgPrint("APC selftest: kernel routine apc=0x%llX normal=0x%llX ctx=0x%llX arg1=0x%llX arg2=0x%llX irql=%u\n",
(uint64_t)apc,
(uint64_t)*normalRoutine,
(uint64_t)*normalContext,
(uint64_t)*systemArgument1,
(uint64_t)*systemArgument2,
KeGetCurrentIrql());
}
static void KiApcSelfTestNormalRoutine(void* normalContext,
void* systemArgument1,
void* systemArgument2) {
volatile int* counter = (volatile int*)normalContext;
DbgPrint("APC selftest: normal routine ctx=0x%llX arg1=0x%llX arg2=0x%llX irql=%u\n",
(uint64_t)normalContext,
(uint64_t)systemArgument1,
(uint64_t)systemArgument2,
KeGetCurrentIrql());
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("APC selftest: normal routine ran above PASSIVE_LEVEL\n");
(*counter)++;
}
static void KiApcSelfTestCrossThreadRoutine(void* normalContext,
void* systemArgument1,
void* systemArgument2) {
KI_APC_SELFTEST_STATE* st = (KI_APC_SELFTEST_STATE*)normalContext;
DbgPrint("APC selftest: cross-thread APC delivered arg1=0x%llX arg2=0x%llX irql=%u\n",
(uint64_t)systemArgument1,
(uint64_t)systemArgument2,
KeGetCurrentIrql());
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("APC selftest: cross-thread APC ran above PASSIVE_LEVEL\n");
st->CrossDelivered++;
KeSignalEvent(st->DoneEvent);
}
static void KiApcSelfTestWorker(void* arg) {
KI_APC_SELFTEST_STATE* st = (KI_APC_SELFTEST_STATE*)arg;
DbgPrint("APC selftest: worker started, signaling ready\n");
KeSignalEvent(st->ReadyEvent);
DbgPrint("APC selftest: worker waiting for go event\n");
KSTATUS s = KeWaitForEvent(st->GoEvent, 0);
if (!KSUCCESS(s))
KePanic("APC selftest: worker wait failed status=%d\n", s);
// KeCommitBlockThread(..., alertable) should deliver pending kernel APCs before this
// wait returns. If that hook is broken, DoneEvent will never be signaled.
DbgPrint("APC selftest: worker resumed after go event, cross=%d\n",
st->CrossDelivered);
if (st->CrossDelivered != 1)
KePanic("APC selftest: worker resumed but APC was not delivered\n");
KeSignalEvent(st->FinishedEvent);
KeExitThread(KeGetCurrentThread(), 0);
}
typedef struct KI_APC_ALERTABLE_SELFTEST_STATE {
EVENT* ReadyEvent;
EVENT* WaitEvent;
EVENT* FinishedEvent;
THREAD* WorkerThread;
volatile int ApcDelivered;
volatile KSTATUS WaitStatus;
} KI_APC_ALERTABLE_SELFTEST_STATE;
static void KiApcAlertableSelfTestNormalRoutine(void* normalContext,
void* systemArgument1,
void* systemArgument2) {
KI_APC_ALERTABLE_SELFTEST_STATE* st =
(KI_APC_ALERTABLE_SELFTEST_STATE*)normalContext;
DbgPrint("APC selftest: alertable APC delivered arg1=0x%llX arg2=0x%llX irql=%u\n",
(uint64_t)systemArgument1,
(uint64_t)systemArgument2,
KeGetCurrentIrql());
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("APC selftest: alertable APC ran above PASSIVE_LEVEL\n");
st->ApcDelivered++;
}
static void KiApcAlertableSelfTestWorker(void* arg) {
KI_APC_ALERTABLE_SELFTEST_STATE* st =
(KI_APC_ALERTABLE_SELFTEST_STATE*)arg;
DbgPrint("APC selftest: alertable worker ready, entering alertable wait\n");
KeSignalEvent(st->ReadyEvent);
KSTATUS s = KeWaitForEvent(st->WaitEvent, 1);
st->WaitStatus = s;
DbgPrint("APC selftest: alertable wait returned status=%u delivered=%d\n",
s, st->ApcDelivered);
if (s != STATUS_KERNEL_APC)
KePanic("APC selftest: alertable wait returned wrong status=%u\n", s);
if (st->ApcDelivered != 1)
KePanic("APC selftest: alertable APC delivery count wrong=%d\n",
st->ApcDelivered);
KeSignalEvent(st->FinishedEvent);
KeExitThread(KeGetCurrentThread(), 0);
}
static void KiRunKernelApcSelfTest(void) {
DbgPrint("APC selftest: begin\n");
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("APC selftest: called above PASSIVE_LEVEL\n");
THREAD* self = KeGetCurrentThread();
if (!self)
KePanic("APC selftest: no current thread\n");
// Test 1: queue APC to current thread at PASSIVE_LEVEL.
{
KAPC apc;
volatile int count = 0;
KeInitializeApc(&apc,
self,
KiApcSelfTestKernelRoutine,
KiApcSelfTestNormalRoutine,
(void*)&count);
if (!KeInsertQueueApc(&apc, (void*)0x11111111, (void*)0x22222222))
KePanic("APC selftest: self queue failed\n");
if (count != 1)
KePanic("APC selftest: self APC was not delivered immediately, count=%d\n", count);
DbgPrint("APC selftest: self APC passed\n");
}
// Test 2: disable kernel APCs, queue, then re-enable.
{
KAPC apc;
volatile int count = 0;
KeInitializeApc(&apc,
self,
KiApcSelfTestKernelRoutine,
KiApcSelfTestNormalRoutine,
(void*)&count);
KeEnterCriticalRegion();
if (!KeInsertQueueApc(&apc, (void*)0x33333333, (void*)0x44444444))
KePanic("APC selftest: deferred queue failed\n");
if (count != 0)
KePanic("APC selftest: APC delivered while critical region held, count=%d\n", count);
KeLeaveCriticalRegion();
if (count != 1)
KePanic("APC selftest: deferred APC was not delivered after leave, count=%d\n", count);
DbgPrint("APC selftest: critical-region deferral passed\n");
}
// Test 3: queue APC to another blocked kernel thread, then wake it.
{
KI_APC_SELFTEST_STATE st;
KAPC apc;
memset(&st, 0, sizeof(st));
KSTATUS s;
s = KeEventCreate(EventAutoReset, 0, &st.ReadyEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: ReadyEvent create failed %d\n", s);
s = KeEventCreate(EventAutoReset, 0, &st.GoEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: GoEvent create failed %d\n", s);
s = KeEventCreate(EventAutoReset, 0, &st.DoneEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: DoneEvent create failed %d\n", s);
s = KeEventCreate(EventAutoReset, 0, &st.FinishedEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: FinishedEvent create failed %d\n", s);
s = KeCreateKernelThread((uintptr_t)KiApcSelfTestWorker,
(uintptr_t)&st,
PriorityNormal,
&st.WorkerThread);
if (!KSUCCESS(s))
KePanic("APC selftest: worker create failed %d\n", s);
s = KeWaitForEvent(st.ReadyEvent, 0);
if (!KSUCCESS(s))
KePanic("APC selftest: ready wait failed %d\n", s);
DbgPrint("APC selftest: worker ready, queueing cross-thread APC\n");
KeInitializeApc(&apc,
st.WorkerThread,
KiApcSelfTestKernelRoutine,
KiApcSelfTestCrossThreadRoutine,
(void*)&st);
if (!KeInsertQueueApc(&apc, (void*)0x55555555, (void*)0x66666666))
KePanic("APC selftest: cross-thread queue failed\n");
if (st.CrossDelivered != 0)
KePanic("APC selftest: cross-thread APC delivered before target resumed\n");
KeSignalEvent(st.GoEvent);
s = KeWaitForEvent(st.DoneEvent, 0);
if (!KSUCCESS(s))
KePanic("APC selftest: done wait failed %d\n", s);
if (st.CrossDelivered != 1)
KePanic("APC selftest: cross-thread delivery count wrong, count=%d\n",
st.CrossDelivered);
// The APC signals DoneEvent before the worker returns from the wait.
// Wait for an explicit worker-owned event before dropping stack-backed
// state and event references. THREAD.ExitEvent is not part of this
// kernel-thread contract on all builds.
s = KeWaitForEvent(st.FinishedEvent, 0);
if (!KSUCCESS(s))
KePanic("APC selftest: worker finished wait failed %d\n", s);
DbgPrint("APC selftest: cross-thread APC passed\n");
ObDereferenceObject(&st.ReadyEvent->Header);
ObDereferenceObject(&st.GoEvent->Header);
ObDereferenceObject(&st.DoneEvent->Header);
ObDereferenceObject(&st.FinishedEvent->Header);
ObDereferenceObject(&st.WorkerThread->Header);
}
// Test 4: queue APC to a thread blocked in an alertable wait.
// The APC itself must wake the waiter; no event signal is sent.
{
KI_APC_ALERTABLE_SELFTEST_STATE st;
KAPC apc;
memset(&st, 0, sizeof(st));
st.WaitStatus = STATUS_PENDING;
KSTATUS s;
s = KeEventCreate(EventAutoReset, 0, &st.ReadyEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: alert ReadyEvent create failed %d\n", s);
s = KeEventCreate(EventAutoReset, 0, &st.WaitEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: alert WaitEvent create failed %d\n", s);
s = KeEventCreate(EventAutoReset, 0, &st.FinishedEvent);
if (!KSUCCESS(s)) KePanic("APC selftest: alert FinishedEvent create failed %d\n", s);
s = KeCreateKernelThread((uintptr_t)KiApcAlertableSelfTestWorker,
(uintptr_t)&st,
PriorityNormal,
&st.WorkerThread);
if (!KSUCCESS(s))
KePanic("APC selftest: alertable worker create failed %d\n", s);
s = KeWaitForEvent(st.ReadyEvent, 0);
if (!KSUCCESS(s))
KePanic("APC selftest: alert ready wait failed %d\n", s);
DbgPrint("APC selftest: queueing APC to alertable waiter\n");
KeInitializeApc(&apc,
st.WorkerThread,
KiApcSelfTestKernelRoutine,
KiApcAlertableSelfTestNormalRoutine,
(void*)&st);
if (!KeInsertQueueApc(&apc, (void*)(unsigned long long)0x77777777, (void*)(unsigned long long)0x88888888))
KePanic("APC selftest: alertable APC queue failed\n");
s = KeWaitForEvent(st.FinishedEvent, 0);
if (!KSUCCESS(s))
KePanic("APC selftest: alert finished wait failed %d\n", s);
if (st.WaitStatus != STATUS_KERNEL_APC || st.ApcDelivered != 1)
KePanic("APC selftest: alertable result bad status=%u delivered=%d\n",
st.WaitStatus, st.ApcDelivered);
DbgPrint("APC selftest: alertable wait passed\n");
ObDereferenceObject(&st.ReadyEvent->Header);
ObDereferenceObject(&st.WaitEvent->Header);
ObDereferenceObject(&st.FinishedEvent->Header);
ObDereferenceObject(&st.WorkerThread->Header);
}
DbgPrint("APC selftest: PASS\n");
}
// -------------------------------------------------------------------------
// IRP + kernel APC self-test
// -------------------------------------------------------------------------
typedef struct KI_IRP_APC_SELFTEST_STATE {
EVENT* CompletionEvent;
IRP Irp;
KAPC Apc;
uint8_t* Buffer;
volatile int ApcDelivered;
volatile KSTATUS ApcStatus;
volatile size_t ApcInformation;
} KI_IRP_APC_SELFTEST_STATE;
static void KiIrpApcSelfTestKernelRoutine(KAPC* apc,
void** normalRoutine,
void** normalContext,
void** systemArgument1,
void** systemArgument2) {
DbgPrint("IRP/APC selftest: kernel routine apc=0x%llX irp=0x%llX status=%u irql=%u\n",
(uint64_t)apc,
(uint64_t)*systemArgument1,
(uint32_t)(uintptr_t)*systemArgument2,
KeGetCurrentIrql());
(void)normalRoutine;
(void)normalContext;
}
static void KiIrpApcSelfTestNormalRoutine(void* normalContext,
void* systemArgument1,
void* systemArgument2) {
KI_IRP_APC_SELFTEST_STATE* st = (KI_IRP_APC_SELFTEST_STATE*)normalContext;
IRP* irp = (IRP*)systemArgument1;
KSTATUS status = (KSTATUS)(uintptr_t)systemArgument2;
DbgPrint("IRP/APC selftest: APC delivered irp=0x%llX status=%u info=%llu irql=%u\n",
(uint64_t)irp,
status,
(uint64_t)(irp ? irp->Information : 0),
KeGetCurrentIrql());
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("IRP/APC selftest: normal routine ran above PASSIVE_LEVEL\n");
if (irp != &st->Irp)
KePanic("IRP/APC selftest: APC arg1 was not the submitted IRP\n");
st->ApcDelivered++;
st->ApcStatus = status;
st->ApcInformation = irp->Information;
}
static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) {
DbgPrint("IRP/APC selftest: begin\n");
(void)hal;
if (!IoDiskDevicePresent(0)) {
DbgPrint("IRP/APC selftest: SKIP, drive 0 not present\n");
return;
}
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("IRP/APC selftest: called above PASSIVE_LEVEL\n");
KI_IRP_APC_SELFTEST_STATE st;
memset(&st, 0, sizeof(st));
KSTATUS s = KeEventCreate(EventAutoReset, 0, &st.CompletionEvent);
if (!KSUCCESS(s))
KePanic("IRP/APC selftest: completion event create failed %u\n", s);
st.Buffer = (uint8_t*)MmAllocatePool(512);
if (!st.Buffer)
KePanic("IRP/APC selftest: no buffer\n");
memset(st.Buffer, 0xCC, 512);
KeInitializeApc(&st.Apc,
KeGetCurrentThread(),
KiIrpApcSelfTestKernelRoutine,
KiIrpApcSelfTestNormalRoutine,
(void*)&st);
LIST_INIT(&st.Irp.Link);
st.Irp.DriveIndex = 0;
st.Irp.Flags = 0;
st.Irp.Lba = 0;
st.Irp.SectorCount = 1;
st.Irp.Buffer = st.Buffer;
st.Irp.Status = STATUS_FAILURE;
st.Irp.Information = 0;
st.Irp.CompletionEvent = st.CompletionEvent;
st.Irp.CompletionApc = &st.Apc;
st.Irp.CompletionFn = (IRP_COMPLETION_FN)0;
st.Irp.CompletionCtx = (void*)0;
st.Irp.HwSlot = 0;
st.Irp.DriverCtx = (void*)0;
DbgPrint("IRP/APC selftest: submitting drive=0 lba=0 sectors=1\n");
s = IoSubmitIrp(&st.Irp);
if (s != STATUS_PENDING)
KePanic("IRP/APC selftest: IoSubmitIrp returned %u\n", s);
s = KeWaitForEvent(st.CompletionEvent, 0);
if (!KSUCCESS(s))
KePanic("IRP/APC selftest: completion wait failed %u\n", s);
// The wait return path should have delivered the pending kernel APC.
if (st.ApcDelivered != 1)
KePanic("IRP/APC selftest: APC was not delivered after I/O wait, count=%d\n",
st.ApcDelivered);
if (!KSUCCESS(st.Irp.Status) || !KSUCCESS(st.ApcStatus))
KePanic("IRP/APC selftest: I/O failed irp=%u apc=%u info=%llu\n",
st.Irp.Status, st.ApcStatus, (uint64_t)st.Irp.Information);
if (st.Irp.Information != 512 || st.ApcInformation != 512)
KePanic("IRP/APC selftest: wrong byte count irp=%llu apc=%llu\n",
(uint64_t)st.Irp.Information, (uint64_t)st.ApcInformation);
DbgPrint("IRP/APC selftest: first bytes %02X %02X %02X %02X, tail %02X %02X\n",
st.Buffer[0], st.Buffer[1], st.Buffer[2], st.Buffer[3],
st.Buffer[510], st.Buffer[511]);
MmFreePool(st.Buffer);
ObDereferenceObject(&st.CompletionEvent->Header);
DbgPrint("IRP/APC selftest: PASS\n");
}
// -------------------------------------------------------------------------
// Virtual memory mapping invariants
// -------------------------------------------------------------------------
static void KiRunMemoryMappingSelfTest(void) {
PHYSICAL_ADDRESS phys = 0;
uint64_t flags = 0;
KSTATUS s = MmQueryPage(0, &phys, &flags);
if (s != STATUS_NOT_FOUND) {
KePanic("MM selftest: virtual page zero is mapped phys=%016llx flags=%016llx status=%u\n",
(unsigned long long)phys,
(unsigned long long)flags,
(unsigned)s);
}
DbgPrint("MM selftest: page zero unmapped\n");
}
// -------------------------------------------------------------------------
// String-view ABI self-test
// -------------------------------------------------------------------------
static void KiRunStringViewSelfTest(void) {
DbgPrint("STRINGVIEW selftest: begin\n");
SYS_STRING_VIEW hello = SYS_SV_LITERAL("Hello");
if (hello.Length != 5 || !SysSvEqualLit(hello, "Hello"))
KePanic("STRINGVIEW selftest: literal/equality failed\n");
static const char embedded[] = { 'a', 'b', '\0', 'c', 'd' };
SYS_STRING_VIEW emb = SysSvFromParts(embedded, sizeof(embedded));
if (emb.Length != 5 || !SysSvContainsByte(emb, '\0'))
KePanic("STRINGVIEW selftest: embedded-NUL detection failed\n");
char copy[6];
if (!SysSvCopyZ(copy, sizeof(copy), emb))
KePanic("STRINGVIEW selftest: bounded copy failed\n");
if (memcmp(copy, embedded, sizeof(embedded)) != 0 || copy[5] != '\0')
KePanic("STRINGVIEW selftest: bounded copy contents wrong\n");
if (SysSvCopyZ(copy, 4, emb))
KePanic("STRINGVIEW selftest: undersized copy unexpectedly succeeded\n");
// Paths are string views at the ABI boundary, but FAT/VFS path names do not
// accept embedded NUL bytes. This must fail before reaching any filesystem.
FILE_OBJECT* f = (FILE_OBJECT*)0;
KSTATUS s = IoOpenFile(SysSvFromParts("system/init.exe\0junk", 20), &f);
if (s != STATUS_INVALID_PARAMETER)
KePanic("STRINGVIEW selftest: embedded-NUL path status=%u\n", s);
DbgPrint("STRINGVIEW selftest: sample view='%v' len=%llu\n",
hello, (uint64_t)hello.Length);
DbgPrint("STRINGVIEW selftest: PASS\n");
}
// -------------------------------------------------------------------------
// Async file I/O self-test
// -------------------------------------------------------------------------
typedef struct KI_FILE_ASYNC_SELFTEST_STATE {
EVENT* CompletionEvent;
FILE_OBJECT* File;
IO_STATUS_BLOCK Iosb;
KAPC Apc;
uint8_t* Buffer;
volatile int ApcDelivered;
volatile KSTATUS ApcStatus;
} KI_FILE_ASYNC_SELFTEST_STATE;
static void KiFileAsyncSelfTestKernelRoutine(KAPC* apc,
void** normalRoutine,
void** normalContext,
void** systemArgument1,
void** systemArgument2) {
DbgPrint("FILE/ASYNC selftest: kernel routine apc=0x%llX iosb=0x%llX status=%u irql=%u\n",
(uint64_t)apc,
(uint64_t)*systemArgument1,
(uint32_t)(uintptr_t)*systemArgument2,
KeGetCurrentIrql());
(void)normalRoutine;
(void)normalContext;
}
static void KiFileAsyncSelfTestNormalRoutine(void* normalContext,
void* systemArgument1,
void* systemArgument2) {
KI_FILE_ASYNC_SELFTEST_STATE* st =
(KI_FILE_ASYNC_SELFTEST_STATE*)normalContext;
KSTATUS status = (KSTATUS)(uintptr_t)systemArgument2;
DbgPrint("FILE/ASYNC selftest: APC delivered iosb=0x%llX status=%u iosb=%u/%llu irql=%u\n",
(uint64_t)systemArgument1,
status,
st->Iosb.Status,
(uint64_t)st->Iosb.Information,
KeGetCurrentIrql());
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
KePanic("FILE/ASYNC selftest: APC ran above PASSIVE_LEVEL\n");
st->ApcDelivered++;
st->ApcStatus = status;
}
static void KiRunAsyncFileIoSelfTest(void) {
DbgPrint("FILE/ASYNC selftest: begin\n");
KI_FILE_ASYNC_SELFTEST_STATE st;
memset(&st, 0, sizeof(st));
st.Iosb.Status = STATUS_FAILURE;
KSTATUS s = IoOpenFile(SYS_SV_LITERAL("system/init.exe"), &st.File);
if (!KSUCCESS(s)) {
DbgPrint("FILE/ASYNC selftest: SKIP, system/init.exe open failed %u\n", s);
return;
}
s = KeEventCreate(EventAutoReset, 0, &st.CompletionEvent);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: event create failed %u\n", s);
st.Buffer = (uint8_t*)MmAllocatePool(512);
if (!st.Buffer)
KePanic("FILE/ASYNC selftest: no buffer\n");
memset(st.Buffer, 0xCC, 512);
KeInitializeApc(&st.Apc,
KeGetCurrentThread(),
KiFileAsyncSelfTestKernelRoutine,
KiFileAsyncSelfTestNormalRoutine,
(void*)&st);
DbgPrint("FILE/ASYNC selftest: submit read system/init.exe offset=0 size=512\n");
IO_ASYNC_FILE_REQUEST_OBJECT* readReq = (IO_ASYNC_FILE_REQUEST_OBJECT*)0;
s = IoReadFileAsyncKernel(st.File,
st.Buffer,
512,
0,
&st.Iosb,
st.CompletionEvent,
&st.Apc,
&readReq);
if (s != STATUS_PENDING)
KePanic("FILE/ASYNC selftest: submit returned %u\n", s);
s = KeWaitForEvent(st.CompletionEvent, 1);
if (s != STATUS_SUCCESS && s != STATUS_KERNEL_APC)
KePanic("FILE/ASYNC selftest: wait returned %u\n", s);
if (st.Iosb.Status == STATUS_PENDING) {
s = KeWaitForEvent(st.CompletionEvent, 0);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: final event wait returned %u\n", s);
}
if (st.ApcDelivered != 1)
KePanic("FILE/ASYNC selftest: APC not delivered count=%d\n", st.ApcDelivered);
if (!KSUCCESS(st.Iosb.Status) || !KSUCCESS(st.ApcStatus))
KePanic("FILE/ASYNC selftest: I/O failed iosb=%u apc=%u info=%llu\n",
st.Iosb.Status, st.ApcStatus, (uint64_t)st.Iosb.Information);
if (st.Iosb.Information < 2 || st.Buffer[0] != 'M' || st.Buffer[1] != 'Z')
KePanic("FILE/ASYNC selftest: bad file header info=%llu bytes=%02X %02X\n",
(uint64_t)st.Iosb.Information, st.Buffer[0], st.Buffer[1]);
DbgPrint("FILE/ASYNC selftest: first bytes %02X %02X %02X %02X info=%llu wait=%u\n",
st.Buffer[0], st.Buffer[1], st.Buffer[2], st.Buffer[3],
(uint64_t)st.Iosb.Information, s);
if (readReq)
ObDereferenceObject(IoAsyncFileRequestObject(readReq));
MmFreePool(st.Buffer);
ObDereferenceObject(&st.CompletionEvent->Header);
IoCloseFile(st.File);
// Exercise the async write path with an explicit-offset temporary file.
{
static const char payload[] = "async-file-write-selftest\n";
char verify[sizeof(payload)];
FILE_OBJECT* wf = (FILE_OBJECT*)0;
FILE_OBJECT* rf = (FILE_OBJECT*)0;
EVENT* wev = (EVENT*)0;
IO_STATUS_BLOCK wiosb;
size_t got = 0;
memset(verify, 0, sizeof(verify));
wiosb.Status = STATUS_FAILURE;
wiosb.Information = 0;
s = IoCreateFile(SYS_SV_LITERAL("asyncio.tmp"), &wf);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: create write temp failed %u\n", s);
s = KeEventCreate(EventAutoReset, 0, &wev);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: write event create failed %u\n", s);
DbgPrint("FILE/ASYNC selftest: submit write asyncio.tmp size=%llu\n",
(uint64_t)(sizeof(payload) - 1));
IO_ASYNC_FILE_REQUEST_OBJECT* writeReq = (IO_ASYNC_FILE_REQUEST_OBJECT*)0;
s = IoWriteFileAsyncKernel(wf,
payload,
sizeof(payload) - 1,
0,
&wiosb,
wev,
(KAPC*)0,
&writeReq);
if (s != STATUS_PENDING)
KePanic("FILE/ASYNC selftest: write submit returned %u\n", s);
s = KeWaitForEvent(wev, 1);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: write wait returned %u\n", s);
if (!KSUCCESS(wiosb.Status) || wiosb.Information != sizeof(payload) - 1)
KePanic("FILE/ASYNC selftest: write failed status=%u info=%llu\n",
wiosb.Status, (uint64_t)wiosb.Information);
if (writeReq)
ObDereferenceObject(IoAsyncFileRequestObject(writeReq));
IoCloseFile(wf);
wf = (FILE_OBJECT*)0;
s = IoOpenFile(SYS_SV_LITERAL("asyncio.tmp"), &rf);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: reopen write temp failed %u\n", s);
s = IoReadFileAt(rf, 0, verify, sizeof(payload) - 1, &got);
if (!KSUCCESS(s) || got != sizeof(payload) - 1 ||
memcmp(verify, payload, sizeof(payload) - 1) != 0) {
KePanic("FILE/ASYNC selftest: verify failed status=%u got=%llu\n",
s, (uint64_t)got);
}
IoCloseFile(rf);
ObDereferenceObject(&wev->Header);
s = IoDeleteFile(SYS_SV_LITERAL("asyncio.tmp"));
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: delete temp failed %u\n", s);
DbgPrint("FILE/ASYNC selftest: async write verified\n");
}
DbgPrint("FILE/ASYNC selftest: PASS\n");
}
static void KiRunTrapFrameSelfTest(void) {
DbgPrint("TRAP selftest: begin\n");
if (KeGetCurrentTrapFrame())
KePanic("TRAP selftest: kernel selftest unexpectedly has active trap frame\n");
DbgPrint("TRAP selftest: frame size=%llu query size=%llu\n",
(uint64_t)sizeof(KTRAP_FRAME),
(uint64_t)sizeof(SYS_TRAP_FRAME_INFO_K));
if (sizeof(KTRAP_FRAME) != 240)
KePanic("TRAP selftest: KTRAP_FRAME size mismatch %llu\n",
(uint64_t)sizeof(KTRAP_FRAME));
DbgPrint("TRAP selftest: PASS\n");
}
void KiRunKernelSelfTests(const HAL_API* hal) {
DbgPrint("Kernel selftests: begin\n");
KiRunKernelApcSelfTest();
KiRunMemoryMappingSelfTest();
KiRunStringViewSelfTest();
KiRunIrpKernelApcSelfTest(hal);
KiRunAsyncFileIoSelfTest();
KiRunTrapFrameSelfTest();
DbgPrint("Kernel selftests: PASS\n");
}