add a realtime scheduler class and priority-inheritance mutexes with timeouts and abandonment, add an HDA audio driver and a SYS_AUDIO_* syscall family, flesh out kernel32 with Win32 threads, events, mutexes, critical sections, and condition variables, add SYS_RESUME_THREAD for suspended thread creation, add KeInitializeMemoryTypes to reprogram MTRRs and install a canonical PAT for WB/WC/UC control, rework the display pipeline with a pitched present path and console output flush, bring back disk.vhd with mtools since vvfat fucking sucks, harden AHCI with serialized cache flush, USB mass storage with SCSI sense-data decoding, and xHCI with cleaner DMA teardown, fix the last stray for(;;) in HalStopProcessor to while(true), pile on smoke tests for audio, win32 sync primitives, and thread suspend, implement AHCI timeout, kill readahead since it was desperetaly trying to murder me, fix doom sound x2.
This commit is contained in:
1 parent
8b254f84f4
commit
4add6789ba
177 files changed
+22565
-3070
No files matched your search
+596
-122
@@ -71,7 +71,9 @@
|
||||
#include <kernel/ke_trap.h>
|
||||
#include <kernel/ke_input.h>
|
||||
#include <kernel/ke_display.h>
|
||||
#include <kernel/ke_audio.h>
|
||||
#include <kernel/ke_time.h>
|
||||
#include <kernel/mm_pool.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
// HAL pointer - set once at boot by KeSyscallSetHal().
|
||||
@@ -182,7 +184,8 @@ typedef struct SURFACE_OBJECT {
|
||||
uint64_t PresentBytesCopied;
|
||||
PHYSICAL_ADDRESS* Frames;
|
||||
void* UserBase;
|
||||
uint32_t* RowScratch;
|
||||
MM_KERNEL_VIEW* KernelView;
|
||||
void* KernelBase;
|
||||
MUTEX* Lock;
|
||||
} SURFACE_OBJECT;
|
||||
|
||||
@@ -274,6 +277,12 @@ static void KiSurfaceDestroy(OBJECT* obj) {
|
||||
surface->UserBase = (void*)0;
|
||||
}
|
||||
|
||||
if (surface->KernelView) {
|
||||
MmDestroyKernelView(surface->KernelView);
|
||||
surface->KernelView = (MM_KERNEL_VIEW*)0;
|
||||
surface->KernelBase = (void*)0;
|
||||
}
|
||||
|
||||
if (surface->Lock) {
|
||||
ObDereferenceObject(&surface->Lock->Header);
|
||||
surface->Lock = (MUTEX*)0;
|
||||
@@ -288,52 +297,19 @@ static void KiSurfaceDestroy(OBJECT* obj) {
|
||||
surface->Frames = (PHYSICAL_ADDRESS*)0;
|
||||
}
|
||||
|
||||
if (surface->RowScratch) {
|
||||
MmFreePool(surface->RowScratch);
|
||||
surface->RowScratch = (uint32_t*)0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct SURFACE_ROW_CONTEXT {
|
||||
SURFACE_OBJECT* Surface;
|
||||
uint32_t SourceX;
|
||||
uint32_t SourceY;
|
||||
} SURFACE_ROW_CONTEXT;
|
||||
|
||||
static KSTATUS KiSurfaceCopyRow(void* context, int rowIndex, uint32_t* scratch, size_t pixelCount) {
|
||||
SURFACE_ROW_CONTEXT* ctx = (SURFACE_ROW_CONTEXT*)context;
|
||||
if (!ctx || !ctx->Surface || !scratch || rowIndex < 0)
|
||||
// Creates the process mapping used for user drawing.
|
||||
static KSTATUS KiSurfaceEnsureViewLocked(SURFACE_OBJECT* surface) {
|
||||
if (!surface || !surface->Owner || !surface->Frames || !surface->PageCount)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (surface->UserBase)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
SURFACE_OBJECT* surface = ctx->Surface;
|
||||
uint64_t row = (uint64_t)ctx->SourceY + (uint64_t)(uint32_t)rowIndex;
|
||||
uint64_t offset = row * (uint64_t)surface->PitchBytes + (uint64_t)ctx->SourceX * sizeof(uint32_t);
|
||||
uint64_t bytes = (uint64_t)pixelCount * sizeof(uint32_t);
|
||||
if (offset > (uint64_t)surface->PayloadSize || bytes > (uint64_t)surface->PayloadSize - offset)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint8_t* dst = (uint8_t*)scratch;
|
||||
while (bytes) {
|
||||
size_t frameIndex = (size_t)(offset / PAGE_SIZE);
|
||||
size_t pageOffset = (size_t)(offset & (PAGE_SIZE - 1));
|
||||
if (frameIndex >= surface->PageCount || !surface->Frames[frameIndex])
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
size_t chunk = PAGE_SIZE - pageOffset;
|
||||
if (chunk > bytes)
|
||||
chunk = (size_t)bytes;
|
||||
|
||||
const uint8_t* src = (const uint8_t*)MmPfnToVirtual(surface->Frames[frameIndex]);
|
||||
if (!src)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
memcpy(dst, src + pageOffset, chunk);
|
||||
|
||||
dst += chunk;
|
||||
offset += chunk;
|
||||
bytes -= chunk;
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
return MmVadCreateSharedViewFromFrames(surface->Owner, surface->Frames, surface->PageCount,
|
||||
surface->PayloadSize,
|
||||
PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX,
|
||||
MM_VAD_ZERO, MmVadShared, &surface->UserBase);
|
||||
}
|
||||
|
||||
// Implements the syscall Init operation.
|
||||
@@ -513,8 +489,9 @@ static uint64_t SyscallExitThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
}
|
||||
|
||||
// SYS_CREATE_THREAD a0=*SYS_CREATE_THREAD_REQUEST.
|
||||
// Creates a runnable user thread and optionally returns both its waitable
|
||||
// handle and diagnostic TID. The handle is the synchronization identity.
|
||||
// Creates a user thread and optionally returns both its waitable handle and
|
||||
// diagnostic TID. Suspended creation leaves the thread off every ready queue
|
||||
// until SYS_RESUME_THREAD decrements its initial suspend count.
|
||||
static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
@@ -527,8 +504,11 @@ static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
if (!proc) return NTLSTATUS_FAILURE;
|
||||
if (!req.Entry) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (req.Priority >= PriorityCount) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (req.Flags) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (req.Flags & ~SYS_CREATE_THREAD_FLAG_SUSPENDED)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!req.ThreadHandleOut && !req.ThreadIdOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if ((req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED) && !req.ThreadHandleOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
s = MmProbeUserRead((const void*)req.Entry, 1);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
@@ -542,16 +522,22 @@ static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
}
|
||||
|
||||
THREAD* newt = NULL;
|
||||
KE_THREAD_CREATE_PARAMETERS threadParameters = KE_THREAD_CREATE_NORMAL(req.Priority);
|
||||
if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED)
|
||||
threadParameters.Flags |= KE_THREAD_CREATE_FLAG_SUSPENDED;
|
||||
s = KeCreateThread(proc, req.Entry, req.Argument,
|
||||
(THREAD_PRIORITY)req.Priority, &newt);
|
||||
&threadParameters, &newt);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
uint32_t threadHandle = 0;
|
||||
if (req.ThreadHandleOut) {
|
||||
s = KeAllocateHandleWithRights(proc, &newt->Header,
|
||||
HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY,
|
||||
HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY |
|
||||
HANDLE_RIGHT_RESUME,
|
||||
&threadHandle);
|
||||
if (!KSUCCESS(s)) {
|
||||
if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED)
|
||||
(void)KeCancelSuspendedThread(newt);
|
||||
ObDereferenceObject(&newt->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -561,6 +547,8 @@ static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ThreadHandleOut, threadHandle);
|
||||
if (!KSUCCESS(s)) {
|
||||
KeCloseHandle(proc, threadHandle);
|
||||
if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED)
|
||||
(void)KeCancelSuspendedThread(newt);
|
||||
ObDereferenceObject(&newt->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -570,6 +558,8 @@ static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ThreadIdOut, newt->ThreadId);
|
||||
if (!KSUCCESS(s)) {
|
||||
if (threadHandle) KeCloseHandle(proc, threadHandle);
|
||||
if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED)
|
||||
(void)KeCancelSuspendedThread(newt);
|
||||
ObDereferenceObject(&newt->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -579,6 +569,35 @@ static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// SYS_RESUME_THREAD a0=threadHandle, a1=previousSuspendCountOut.
|
||||
static uint64_t SyscallResumeThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a2; (void)a3; (void)a4; (void)a5;
|
||||
if (!a0 || !a1) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KSTATUS status = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(uint32_t));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
PROCESS* process = KeGetCurrentProcess();
|
||||
if (!process) return NTLSTATUS_FAILURE;
|
||||
|
||||
OBJECT* object = KeLookupHandleWithRights(process, (uint32_t)a0,
|
||||
HANDLE_RIGHT_RESUME);
|
||||
if (!object) return NTLSTATUS_INVALID_HANDLE;
|
||||
if (object->Type != ObjectTypeThread) {
|
||||
ObDereferenceObject(object);
|
||||
return NTLSTATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
THREAD* thread = CONTAINING_RECORD(object, THREAD, Header);
|
||||
uint32_t previousSuspendCount = 0;
|
||||
status = KeResumeThread(thread, &previousSuspendCount);
|
||||
ObDereferenceObject(object);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
return KiCopyU32ToUser(a1, previousSuspendCount);
|
||||
}
|
||||
|
||||
static void KiFinishAlertableUserWait(uint8_t alertable) {
|
||||
if (!alertable) return;
|
||||
|
||||
@@ -619,6 +638,9 @@ static KSTATUS KiHandleToWaitEvent(PROCESS* proc, uint32_t handle, EVENT** event
|
||||
case ObjectTypeDisplayClock:
|
||||
event = KeDisplayClockWaitEvent((DISPLAY_CLOCK_OBJECT*)obj);
|
||||
break;
|
||||
case ObjectTypeAudioStream:
|
||||
event = KeAudioStreamWaitEvent((KE_AUDIO_STREAM*)obj);
|
||||
break;
|
||||
default:
|
||||
ObDereferenceObject(obj);
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
@@ -656,28 +678,14 @@ static uint64_t SyscallWaitSingle(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
|
||||
if (obj->Type == ObjectTypeMutex) {
|
||||
if (alertable) s = NTLSTATUS_NOT_SUPPORTED;
|
||||
else if (timeoutMs == 0) {
|
||||
s = KeTryAcquireMutex(CONTAINING_RECORD(obj, MUTEX, Header));
|
||||
if (s == NTLSTATUS_FAILURE) s = NTLSTATUS_TIMEOUT;
|
||||
} else if (timeoutMs == SYS_WAIT_TIMEOUT_INFINITE) {
|
||||
s = KeAcquireMutex(CONTAINING_RECORD(obj, MUTEX, Header));
|
||||
} else {
|
||||
s = NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
else s = KeAcquireMutex(CONTAINING_RECORD(obj, MUTEX, Header), timeoutMs);
|
||||
ObDereferenceObject(obj);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (obj->Type == ObjectTypeSemaphore) {
|
||||
if (alertable) s = NTLSTATUS_NOT_SUPPORTED;
|
||||
else if (timeoutMs == 0) {
|
||||
s = KeTrySemaphore(CONTAINING_RECORD(obj, SEMAPHORE, Header));
|
||||
if (s == NTLSTATUS_FAILURE) s = NTLSTATUS_TIMEOUT;
|
||||
} else if (timeoutMs == SYS_WAIT_TIMEOUT_INFINITE) {
|
||||
s = KeWaitSemaphore(CONTAINING_RECORD(obj, SEMAPHORE, Header));
|
||||
} else {
|
||||
s = NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
else s = KeWaitSemaphore(CONTAINING_RECORD(obj, SEMAPHORE, Header), timeoutMs);
|
||||
ObDereferenceObject(obj);
|
||||
return s;
|
||||
}
|
||||
@@ -992,8 +1000,7 @@ static uint64_t SyscallSetThreadPriority(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
if (a0 >= PriorityCount) return NTLSTATUS_INVALID_PARAMETER;
|
||||
THREAD* t = KeGetCurrentThread();
|
||||
if (!t) return NTLSTATUS_FAILURE;
|
||||
t->Priority = (THREAD_PRIORITY)a0;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
return KiSchedulerSetNormalPriority(t, (THREAD_PRIORITY)a0);
|
||||
}
|
||||
|
||||
// SYS_VM_* page-granular user virtual memory.
|
||||
@@ -1163,7 +1170,8 @@ static uint64_t SyscallMutexTryAcquire(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
MUTEX* mx = (MUTEX*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_WAIT);
|
||||
if (!mx) return NTLSTATUS_NOT_FOUND;
|
||||
KSTATUS s = KeTryAcquireMutex(mx);
|
||||
KSTATUS s = KeAcquireMutex(mx, 0);
|
||||
if (s == NTLSTATUS_TIMEOUT) s = NTLSTATUS_FAILURE;
|
||||
ObDereferenceObject(&mx->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -1204,7 +1212,8 @@ static uint64_t SyscallSemTryWait(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_WAIT);
|
||||
if (!sem) return NTLSTATUS_NOT_FOUND;
|
||||
KSTATUS s = KeTrySemaphore(sem);
|
||||
KSTATUS s = KeWaitSemaphore(sem, 0);
|
||||
if (s == NTLSTATUS_TIMEOUT) s = NTLSTATUS_FAILURE;
|
||||
ObDereferenceObject(&sem->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -1672,7 +1681,7 @@ static void* KiAllocateSyscallIoBuffer(size_t requested, size_t* actualOut) {
|
||||
if (!requested) return NULL;
|
||||
|
||||
size_t chunk = requested < SYSCALL_FILE_IO_CHUNK_BYTES ? requested : SYSCALL_FILE_IO_CHUNK_BYTES;
|
||||
for (;;) {
|
||||
while (true) {
|
||||
void* buf = MmAllocatePoolTag(chunk, MM_POOL_TAG('s', 'i', 'o', ' '));
|
||||
if (buf) {
|
||||
if (actualOut) *actualOut = chunk;
|
||||
@@ -2275,9 +2284,8 @@ static uint64_t SyscallCreateSurface(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
surface->PageCount = pageCount;
|
||||
|
||||
surface->Frames = (PHYSICAL_ADDRESS*)MmAllocatePoolTag(pageCount * sizeof(PHYSICAL_ADDRESS), MM_POOL_TAG('s', 'f', 'r', 'm'));
|
||||
surface->RowScratch = (uint32_t*)MmAllocatePoolTag(rowBytes, MM_POOL_TAG('s', 'r', 'o', 'w'));
|
||||
s = KeMutexCreate(&surface->Lock);
|
||||
if (!surface->Frames || !surface->RowScratch || !KSUCCESS(s)) {
|
||||
if (!surface->Frames || !KSUCCESS(s)) {
|
||||
ObDereferenceObject(&surface->Header);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
@@ -2295,6 +2303,14 @@ static uint64_t SyscallCreateSurface(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
memset(kva, 0, PAGE_SIZE);
|
||||
}
|
||||
|
||||
s = MmCreateKernelViewFromFrames(surface->Frames, surface->PageCount,
|
||||
&surface->KernelView);
|
||||
if (!KSUCCESS(s)) {
|
||||
ObDereferenceObject(&surface->Header);
|
||||
return s;
|
||||
}
|
||||
surface->KernelBase = MmKernelViewBase(surface->KernelView);
|
||||
|
||||
uint32_t handle = 0;
|
||||
s = KiAllocHandleForCurrentWithRights(&surface->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &handle);
|
||||
if (!KSUCCESS(s))
|
||||
@@ -2327,18 +2343,13 @@ static uint64_t SyscallMapSurface(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
KSTATUS s = KeAcquireMutex(surface->Lock);
|
||||
if (!KSUCCESS(s)) {
|
||||
KSTATUS s = KeAcquireMutex(surface->Lock, KE_WAIT_INFINITE);
|
||||
if (!KE_MUTEX_ACQUIRE_SUCCEEDED(s)) {
|
||||
ObDereferenceObject(&surface->Header);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (!surface->UserBase) {
|
||||
s = MmVadCreateSharedViewFromFrames(current, surface->Frames, surface->PageCount,
|
||||
surface->PayloadSize,
|
||||
PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX,
|
||||
MM_VAD_ZERO, MmVadShared, &surface->UserBase);
|
||||
}
|
||||
s = KiSurfaceEnsureViewLocked(surface);
|
||||
|
||||
void* userBase = surface->UserBase;
|
||||
uint32_t pitchBytes = surface->PitchBytes;
|
||||
@@ -2374,8 +2385,8 @@ static uint64_t SyscallQuerySurface(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
SYS_SURFACE_DESC desc;
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
|
||||
KSTATUS s = KeAcquireMutex(surface->Lock);
|
||||
if (KSUCCESS(s)) {
|
||||
KSTATUS s = KeAcquireMutex(surface->Lock, KE_WAIT_INFINITE);
|
||||
if (KE_MUTEX_ACQUIRE_SUCCEEDED(s)) {
|
||||
desc.Width = surface->Width;
|
||||
desc.Height = surface->Height;
|
||||
desc.PitchBytes = surface->PitchBytes;
|
||||
@@ -2475,20 +2486,21 @@ static uint64_t SyscallPresentSurface(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t rowsCopied = 0;
|
||||
uint64_t bytesCopied = 0;
|
||||
|
||||
KSTATUS s = NTLSTATUS_SUCCESS;
|
||||
for (uint32_t i = 0; i < rectCount; ++i) {
|
||||
KSTATUS s = surface->KernelBase ? NTLSTATUS_SUCCESS : NTLSTATUS_DEVICE_ERROR;
|
||||
const uint8_t* sourceBase = (const uint8_t*)surface->KernelBase;
|
||||
|
||||
for (uint32_t i = 0; i < rectCount && KSUCCESS(s); ++i) {
|
||||
DISPLAY_RECT clipped;
|
||||
if (!KiClipSurfaceRect(surface, &rects[i], &clipped)) {
|
||||
emptyClips++;
|
||||
continue;
|
||||
}
|
||||
|
||||
SURFACE_ROW_CONTEXT ctx;
|
||||
ctx.Surface = surface;
|
||||
ctx.SourceX = (uint32_t)clipped.X;
|
||||
ctx.SourceY = (uint32_t)clipped.Y;
|
||||
s = KeDisplayBlitRows(clipped.X, clipped.Y, clipped.W, clipped.H,
|
||||
KiSurfaceCopyRow, &ctx, surface->RowScratch);
|
||||
const uint32_t* source = (const uint32_t*)(sourceBase +
|
||||
(size_t)(uint32_t)clipped.Y * surface->PitchBytes +
|
||||
(size_t)(uint32_t)clipped.X * sizeof(uint32_t));
|
||||
s = KeDisplayPresentPitched(clipped.X, clipped.Y, clipped.W, clipped.H,
|
||||
source, surface->PitchBytes);
|
||||
if (!KSUCCESS(s))
|
||||
break;
|
||||
|
||||
@@ -2657,7 +2669,7 @@ static uint64_t SyscallKbReadChar(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
if (!a0) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
for (;;) {
|
||||
while (true) {
|
||||
char ch = KeInputReadChar();
|
||||
if (ch)
|
||||
return CopyToUser((void*)(uintptr_t)a0, &ch, sizeof(ch));
|
||||
@@ -2674,7 +2686,7 @@ static uint64_t SyscallKbReadKey(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
if (!a0) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
for (;;) {
|
||||
while (true) {
|
||||
INPUT_KEY key = KeInputReadKey();
|
||||
if (key != INPUT_KEY_NONE)
|
||||
return CopyToUser((void*)(uintptr_t)a0, &key, sizeof(key));
|
||||
@@ -2722,7 +2734,7 @@ static uint64_t SyscallKbReadInputEvent(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
INPUT_KEY_RECORD record;
|
||||
for (;;) {
|
||||
while (true) {
|
||||
bool available = queue ? KeInputQueueReadEvent(queue, &record)
|
||||
: KeInputReadEvent(&record);
|
||||
if (available) {
|
||||
@@ -2759,7 +2771,7 @@ static uint64_t SyscallKbPeekInputEvent(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
INPUT_KEY_RECORD record;
|
||||
for (;;) {
|
||||
while (true) {
|
||||
bool available = queue ? KeInputQueuePeekEvent(queue, &record)
|
||||
: KeInputPeekEvent(&record);
|
||||
if (available) {
|
||||
@@ -2995,7 +3007,8 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
|
||||
THREAD* thread;
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
|
||||
const KE_THREAD_CREATE_PARAMETERS threadParameters = KE_THREAD_CREATE_NORMAL(PriorityNormal);
|
||||
s = KeCreateThread(proc, entryPoint, 0, &threadParameters, &thread);
|
||||
if (!KSUCCESS(s)) {
|
||||
ProcDbg("CreateProcess: KeCreateThread failed st=%d pid=%u entry=%016llx\n", s, proc->ProcessId, (unsigned long long)entryPoint);
|
||||
MmVadDebugDumpProcess(proc);
|
||||
@@ -3070,7 +3083,7 @@ static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
}
|
||||
|
||||
// SYS_WRITE_CONSOLE (60) a0=*buf a1=len
|
||||
// Writes user console text in white; distinct from green SYS_PRINT_DEBUG.
|
||||
// Writes user console text and completes display delivery before returning.
|
||||
static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
@@ -3092,7 +3105,7 @@ static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
KePrintConsole(chunk, todo);
|
||||
offset += todo;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
return KeDisplayFlushConsoleOutput();
|
||||
}
|
||||
|
||||
// SYS_OPEN_DIR (54) a0=*dirPath a1=*handleOut.
|
||||
@@ -3309,7 +3322,7 @@ void KeNamedQueueUnpublishHandle(PROCESS* process, uint32_t handle, OBJECT* obje
|
||||
if (!process || !object || object->Type != ObjectTypeQueue)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
while (true) {
|
||||
QUEUE* queue = (QUEUE*)0;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock);
|
||||
for (int i = 0; i < NAMED_QUEUE_MAX; i++) {
|
||||
@@ -4473,7 +4486,7 @@ static uint64_t SyscallPoll(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t start = gHal ? gHal->Timer.QueryTicks() : 0;
|
||||
size_t ready = 0;
|
||||
|
||||
for (;;) {
|
||||
while (true) {
|
||||
ready = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
fds[i].ReturnedEvents = 0;
|
||||
@@ -4982,6 +4995,7 @@ static uint64_t SyscallQueryDisplayInfo(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
out.Mode = ki.Mode;
|
||||
out.Caps = ki.Caps;
|
||||
out.DeviceCount = ki.DeviceCount;
|
||||
out.Flags = ki.Flags;
|
||||
out.ManagerStats = ki.ManagerStats;
|
||||
out.DeviceStats = ki.DeviceStats;
|
||||
|
||||
@@ -5012,6 +5026,12 @@ static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
us.CpuBurstDemotions = ks.CpuBurstDemotions;
|
||||
us.SmtSiblingDeferrals = ks.SmtSiblingDeferrals;
|
||||
us.SmtPrimaryPokes = ks.SmtPrimaryPokes;
|
||||
us.RealtimeDispatches = ks.RealtimeDispatches;
|
||||
us.RealtimePreemptions = ks.RealtimePreemptions;
|
||||
us.PriorityInheritanceBoosts = ks.PriorityInheritanceBoosts;
|
||||
us.PriorityInheritanceDrops = ks.PriorityInheritanceDrops;
|
||||
us.PriorityInheritancePropagations = ks.PriorityInheritancePropagations;
|
||||
us.PriorityInheritanceMaxDepth = ks.PriorityInheritanceMaxDepth;
|
||||
return KiReturnKernelStats(a1, (size_t)a2, a3, &us, sizeof(us));
|
||||
}
|
||||
|
||||
@@ -5199,19 +5219,6 @@ static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
um.DropFailures = km.DropFailures;
|
||||
um.StandbyTransitions = km.StandbyTransitions;
|
||||
um.StandbyHits = km.StandbyHits;
|
||||
um.ReadAheadRequests = km.ReadAheadRequests;
|
||||
um.ReadAheadPagesRequested = km.ReadAheadPagesRequested;
|
||||
um.ReadAheadPagesQueued = km.ReadAheadPagesQueued;
|
||||
um.ReadAheadPagesLoaded = km.ReadAheadPagesLoaded;
|
||||
um.ReadAheadPagesSkipped = km.ReadAheadPagesSkipped;
|
||||
um.ReadAheadFailures = km.ReadAheadFailures;
|
||||
um.ReadAheadPagesThrottled = km.ReadAheadPagesThrottled;
|
||||
um.MaxReadAheadPagesPerRequest = km.MaxReadAheadPagesPerRequest;
|
||||
um.BatchReads = km.BatchReads;
|
||||
um.BatchPagesRequested = km.BatchPagesRequested;
|
||||
um.BatchPagesLoaded = km.BatchPagesLoaded;
|
||||
um.MaxBatchPagesRequested = km.MaxBatchPagesRequested;
|
||||
um.MaxBatchPagesLoaded = km.MaxBatchPagesLoaded;
|
||||
um.ResidentPages = km.ResidentPages;
|
||||
um.ActivePages = km.ActivePages;
|
||||
um.StandbyPages = km.StandbyPages;
|
||||
@@ -5285,17 +5292,6 @@ static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
um.WriteSectors = km.WriteSectors;
|
||||
um.MaxReadSectors = km.MaxReadSectors;
|
||||
um.MaxWriteSectors = km.MaxWriteSectors;
|
||||
um.ReadaheadHits = km.ReadaheadHits;
|
||||
um.ReadaheadMisses = km.ReadaheadMisses;
|
||||
um.ReadaheadStores = km.ReadaheadStores;
|
||||
um.ReadaheadInvalidations = km.ReadaheadInvalidations;
|
||||
um.ReadaheadBytesServed = km.ReadaheadBytesServed;
|
||||
um.ReadaheadSectorsLoaded = km.ReadaheadSectorsLoaded;
|
||||
um.ReadaheadSequentialDetections = km.ReadaheadSequentialDetections;
|
||||
um.ReadaheadSuppressed = km.ReadaheadSuppressed;
|
||||
um.ReadaheadAllocFailures = km.ReadaheadAllocFailures;
|
||||
um.ReadaheadReadFailures = km.ReadaheadReadFailures;
|
||||
um.MaxReadaheadSectors = km.MaxReadaheadSectors;
|
||||
return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um));
|
||||
}
|
||||
|
||||
@@ -5374,6 +5370,466 @@ static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
}
|
||||
}
|
||||
|
||||
// Converts one public audio format into the kernel mixer format.
|
||||
static KSTATUS KiImportAudioFormat(const SYS_AUDIO_FORMAT* source, IO_AUDIO_FORMAT* destination) {
|
||||
if (!source || !destination)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (source->SampleFormat != SysAudioSampleS16 ||
|
||||
(source->ChannelCount != 1 && source->ChannelCount != 2) ||
|
||||
(source->SampleRate != 11025u && source->SampleRate != 22050u &&
|
||||
source->SampleRate != 44100u && source->SampleRate != 48000u))
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
destination->SampleRate = source->SampleRate;
|
||||
destination->ChannelCount = source->ChannelCount;
|
||||
destination->SampleFormat = IoAudioSampleS16;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Converts one kernel audio format into the public syscall representation.
|
||||
static SYS_AUDIO_FORMAT KiExportAudioFormat(const IO_AUDIO_FORMAT* source) {
|
||||
SYS_AUDIO_FORMAT destination;
|
||||
destination.SampleRate = source->SampleRate;
|
||||
destination.ChannelCount = source->ChannelCount;
|
||||
destination.SampleFormat = source->SampleFormat == IoAudioSampleS16
|
||||
? SysAudioSampleS16
|
||||
: 0;
|
||||
return destination;
|
||||
}
|
||||
|
||||
// Converts one normalized kernel gain into unsigned Q16.
|
||||
static uint32_t KiExportAudioGainQ16(float gain) {
|
||||
if (gain <= 0.0f) return 0;
|
||||
if (gain >= 1.0f) return SYS_AUDIO_GAIN_Q16_UNITY;
|
||||
return (uint32_t)(gain * (float)SYS_AUDIO_GAIN_Q16_UNITY + 0.5f);
|
||||
}
|
||||
|
||||
// Converts one normalized kernel pan into signed Q16.
|
||||
static int32_t KiExportAudioPanQ16(float pan) {
|
||||
if (pan <= -1.0f) return SYS_AUDIO_PAN_Q16_LEFT;
|
||||
if (pan >= 1.0f) return SYS_AUDIO_PAN_Q16_RIGHT;
|
||||
float scaled = pan * (float)SYS_AUDIO_GAIN_Q16_UNITY;
|
||||
return (int32_t)(scaled + (scaled >= 0.0f ? 0.5f : -0.5f));
|
||||
}
|
||||
|
||||
// Converts one public unsigned Q16 gain into the kernel representation.
|
||||
static float KiImportAudioGainQ16(uint32_t gainQ16) {
|
||||
return (float)gainQ16 / (float)SYS_AUDIO_GAIN_Q16_UNITY;
|
||||
}
|
||||
|
||||
// Converts one public signed Q16 pan into the kernel representation.
|
||||
static float KiImportAudioPanQ16(int32_t panQ16) {
|
||||
return (float)panQ16 / (float)SYS_AUDIO_GAIN_Q16_UNITY;
|
||||
}
|
||||
|
||||
// Converts one kernel endpoint kind into its public ABI value.
|
||||
static uint32_t KiExportAudioEndpointKind(IO_AUDIO_ENDPOINT_KIND kind) {
|
||||
switch (kind) {
|
||||
case IoAudioEndpointLineOut: return SysAudioEndpointLineOut;
|
||||
case IoAudioEndpointSpeakerHeadphone: return SysAudioEndpointSpeakerHeadphone;
|
||||
case IoAudioEndpointOtherAnalog: return SysAudioEndpointOtherAnalog;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts provider endpoint capabilities into public capability bits.
|
||||
static uint32_t KiExportAudioEndpointCapabilities(uint32_t capabilities) {
|
||||
uint32_t result = 0;
|
||||
if (capabilities & IO_AUDIO_ENDPOINT_CAP_JACK_SENSE)
|
||||
result |= SYS_AUDIO_ENDPOINT_CAP_JACK_SENSE;
|
||||
if (capabilities & IO_AUDIO_ENDPOINT_CAP_HARDWARE_GAIN)
|
||||
result |= SYS_AUDIO_ENDPOINT_CAP_HARDWARE_GAIN;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Converts one endpoint lifecycle value into its public ABI value.
|
||||
static uint32_t KiExportAudioEndpointState(KE_AUDIO_ENDPOINT_STATE state) {
|
||||
switch (state) {
|
||||
case KeAudioEndpointStarting: return SysAudioEndpointStarting;
|
||||
case KeAudioEndpointReady: return SysAudioEndpointReady;
|
||||
case KeAudioEndpointStopping: return SysAudioEndpointStopping;
|
||||
case KeAudioEndpointFailed: return SysAudioEndpointFailed;
|
||||
case KeAudioEndpointRemoved: return SysAudioEndpointRemoved;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts one stream lifecycle value into its public ABI value.
|
||||
static uint32_t KiExportAudioStreamState(KE_AUDIO_STREAM_STATE state) {
|
||||
switch (state) {
|
||||
case KeAudioStreamCreated: return SysAudioStreamCreated;
|
||||
case KeAudioStreamRunning: return SysAudioStreamRunning;
|
||||
case KeAudioStreamPaused: return SysAudioStreamPaused;
|
||||
case KeAudioStreamDraining: return SysAudioStreamDraining;
|
||||
case KeAudioStreamStopped: return SysAudioStreamStopped;
|
||||
case KeAudioStreamFailed: return SysAudioStreamFailed;
|
||||
case KeAudioStreamClosed:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts public wait flags into the kernel stream condition mask.
|
||||
static uint32_t KiImportAudioWaitMask(uint32_t mask) {
|
||||
uint32_t result = 0;
|
||||
if (mask & SYS_AUDIO_WAIT_WRITABLE) result |= KE_AUDIO_WAIT_WRITABLE;
|
||||
if (mask & SYS_AUDIO_WAIT_CONSUMED) result |= KE_AUDIO_WAIT_CONSUMED;
|
||||
if (mask & SYS_AUDIO_WAIT_PRESENTED) result |= KE_AUDIO_WAIT_PRESENTED;
|
||||
if (mask & SYS_AUDIO_WAIT_STOPPED) result |= KE_AUDIO_WAIT_STOPPED;
|
||||
if (mask & SYS_AUDIO_WAIT_DRAINED) result |= KE_AUDIO_WAIT_DRAINED;
|
||||
if (mask & SYS_AUDIO_WAIT_FAILED) result |= KE_AUDIO_WAIT_FAILED;
|
||||
if (mask & SYS_AUDIO_WAIT_DISCONTINUITY) result |= KE_AUDIO_WAIT_DISCONTINUITY;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Looks up one audio stream handle with the requested operation rights.
|
||||
static KE_AUDIO_STREAM* KiLookupAudioStream(uint32_t handle, uint32_t rights) {
|
||||
OBJECT* object = KiHandleToObject(handle, ObjectTypeAudioStream, rights);
|
||||
return object ? (KE_AUDIO_STREAM*)object : (KE_AUDIO_STREAM*)0;
|
||||
}
|
||||
|
||||
// SYS_AUDIO_ENUMERATE_ENDPOINTS a0=*SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST.
|
||||
static uint64_t SyscallAudioEnumerateEndpoints(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST request;
|
||||
KSTATUS status = KiCopyUserRequest(a0, &request, sizeof(request));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (request.Size != sizeof(request) || request.Flags != 0 || request.Reserved != 0 ||
|
||||
!request.CountOut || (request.Capacity && !request.Endpoints))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
status = MmProbeUserWrite(request.CountOut, sizeof(*request.CountOut));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
uint32_t copyCapacity = request.Capacity < SYS_AUDIO_MAX_ENDPOINTS
|
||||
? request.Capacity
|
||||
: SYS_AUDIO_MAX_ENDPOINTS;
|
||||
if (copyCapacity) {
|
||||
status = MmProbeUserWrite(request.Endpoints,
|
||||
(size_t)copyCapacity * sizeof(*request.Endpoints));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
|
||||
KE_AUDIO_ENDPOINT_INFO kernelInfo[KE_AUDIO_MAX_ENDPOINTS];
|
||||
uint32_t count = 0;
|
||||
status = KeAudioQueryEndpoints(kernelInfo, KE_AUDIO_MAX_ENDPOINTS, &count);
|
||||
if (KFAILED(status)) return status;
|
||||
|
||||
SYS_AUDIO_ENDPOINT_INFO publicInfo[SYS_AUDIO_MAX_ENDPOINTS];
|
||||
uint32_t copyCount = copyCapacity < count ? copyCapacity : count;
|
||||
for (uint32_t index = 0; index < copyCount; index++) {
|
||||
const KE_AUDIO_ENDPOINT_INFO* source = &kernelInfo[index];
|
||||
SYS_AUDIO_ENDPOINT_INFO* destination = &publicInfo[index];
|
||||
memset(destination, 0, sizeof(*destination));
|
||||
destination->Size = sizeof(*destination);
|
||||
destination->Version = SYS_AUDIO_ABI_VERSION;
|
||||
destination->EndpointId = source->EndpointId;
|
||||
destination->Kind = KiExportAudioEndpointKind(source->Kind);
|
||||
if (source->Available) destination->Flags |= SYS_AUDIO_ENDPOINT_FLAG_AVAILABLE;
|
||||
if (source->IsDefault) destination->Flags |= SYS_AUDIO_ENDPOINT_FLAG_DEFAULT;
|
||||
destination->Capabilities = KiExportAudioEndpointCapabilities(source->Capabilities);
|
||||
destination->State = KiExportAudioEndpointState(source->State);
|
||||
destination->HardwareFormat = KiExportAudioFormat(&source->HardwareFormat);
|
||||
destination->PeriodCount = source->PeriodCount;
|
||||
destination->FramesPerPeriod = source->FramesPerPeriod;
|
||||
destination->MasterVolumeQ16 = KiExportAudioGainQ16(source->MasterRequestedVolume);
|
||||
destination->MasterMuted = source->MasterMuted ? 1u : 0u;
|
||||
destination->FailureStatus = source->FailureStatus;
|
||||
}
|
||||
|
||||
if (copyCount) {
|
||||
status = CopyToUser(request.Endpoints,
|
||||
publicInfo,
|
||||
(size_t)copyCount * sizeof(publicInfo[0]));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
status = CopyToUser(request.CountOut, &count, sizeof(count));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
return request.Capacity < count ? NTLSTATUS_BUFFER_TOO_SMALL : NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// SYS_AUDIO_OPEN_STREAM a0=*SYS_AUDIO_OPEN_STREAM_REQUEST.
|
||||
static uint64_t SyscallAudioOpenStream(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
|
||||
SYS_AUDIO_OPEN_STREAM_REQUEST request;
|
||||
KSTATUS status = KiCopyUserRequest(a0, &request, sizeof(request));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (request.Size != sizeof(request) || request.Flags != 0 || request.Reserved != 0 ||
|
||||
!request.EndpointId || !request.StreamHandleOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
status = MmProbeUserWrite(request.StreamHandleOut, sizeof(*request.StreamHandleOut));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
IO_AUDIO_FORMAT format;
|
||||
status = KiImportAudioFormat(&request.Format, &format);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
KE_AUDIO_STREAM* stream = (KE_AUDIO_STREAM*)0;
|
||||
status = KeAudioCreateStream(request.EndpointId, &format, &stream);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
uint32_t handle = 0;
|
||||
status = KiAllocHandleForCurrentWithRights((OBJECT*)stream,
|
||||
HANDLE_RIGHT_WRITE |
|
||||
HANDLE_RIGHT_WAIT |
|
||||
HANDLE_RIGHT_QUERY,
|
||||
&handle);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
status = CopyToUser(request.StreamHandleOut, &handle, sizeof(handle));
|
||||
if (!KSUCCESS(status)) {
|
||||
PROCESS* process = KeGetCurrentProcess();
|
||||
if (process) KeCloseHandle(process, handle);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// SYS_AUDIO_WRITE a0=*SYS_AUDIO_WRITE_REQUEST.
|
||||
static uint64_t SyscallAudioWrite(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
|
||||
SYS_AUDIO_WRITE_REQUEST request;
|
||||
KSTATUS status = KiCopyUserRequest(a0, &request, sizeof(request));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (request.Size != sizeof(request) || request.Flags != 0 || !request.StreamHandle ||
|
||||
!request.WrittenFramesOut || (request.FrameCount && !request.Frames))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
status = MmProbeUserWrite(request.WrittenFramesOut, sizeof(*request.WrittenFramesOut));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
uint32_t written = 0;
|
||||
|
||||
KE_AUDIO_STREAM* stream = KiLookupAudioStream(request.StreamHandle, HANDLE_RIGHT_WRITE);
|
||||
if (!stream) return NTLSTATUS_INVALID_HANDLE;
|
||||
if (!request.FrameCount) {
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return CopyToUser(request.WrittenFramesOut, &written, sizeof(written));
|
||||
}
|
||||
|
||||
KE_AUDIO_STREAM_INFO info;
|
||||
status = KeAudioQueryStream(stream, &info);
|
||||
if (!KSUCCESS(status)) {
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return status;
|
||||
}
|
||||
if (info.State == KeAudioStreamFailed) {
|
||||
status = KFAILED(info.FailureStatus) ? info.FailureStatus : NTLSTATUS_DEVICE_ERROR;
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
(void)CopyToUser(request.WrittenFramesOut, &written, sizeof(written));
|
||||
return status;
|
||||
}
|
||||
if (info.State == KeAudioStreamDraining) {
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
(void)CopyToUser(request.WrittenFramesOut, &written, sizeof(written));
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (!info.WritableFrames) {
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
status = CopyToUser(request.WrittenFramesOut, &written, sizeof(written));
|
||||
return KSUCCESS(status) ? NTLSTATUS_WOULD_BLOCK : status;
|
||||
}
|
||||
|
||||
uint32_t frameCount = request.FrameCount < info.WritableFrames
|
||||
? request.FrameCount
|
||||
: info.WritableFrames;
|
||||
uint32_t frameBytes = (uint32_t)info.Format.ChannelCount * sizeof(int16_t);
|
||||
size_t byteCount = (size_t)frameCount * frameBytes;
|
||||
status = MmProbeUserRead(request.Frames, byteCount);
|
||||
if (!KSUCCESS(status)) {
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return status;
|
||||
}
|
||||
|
||||
void* buffer = MmAllocatePoolTag(byteCount, MM_POOL_TAG('a', 'u', 'w', 'r'));
|
||||
if (!buffer) {
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
status = CopyFromUser(buffer, request.Frames, byteCount);
|
||||
if (KSUCCESS(status))
|
||||
status = CopyToUser(request.WrittenFramesOut, &written, sizeof(written));
|
||||
if (KSUCCESS(status))
|
||||
status = KeAudioWriteStream(stream, buffer, frameCount, &written);
|
||||
MmFreePool(buffer);
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
return CopyToUser(request.WrittenFramesOut, &written, sizeof(written));
|
||||
}
|
||||
|
||||
// SYS_AUDIO_ARM_WAIT a0=*SYS_AUDIO_ARM_WAIT_REQUEST.
|
||||
static uint64_t SyscallAudioArmWait(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
|
||||
SYS_AUDIO_ARM_WAIT_REQUEST request;
|
||||
KSTATUS status = KiCopyUserRequest(a0, &request, sizeof(request));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (request.Size != sizeof(request) || request.Flags != 0 || request.Reserved != 0 ||
|
||||
!request.StreamHandle || !request.Mask ||
|
||||
(request.Mask & ~SYS_AUDIO_WAIT_VALID_MASK) != 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KE_AUDIO_STREAM_WAIT_CONDITION condition;
|
||||
condition.Mask = KiImportAudioWaitMask(request.Mask);
|
||||
condition.WritableFrames = request.WritableFrames;
|
||||
condition.ConsumedFrames = request.ConsumedFrames;
|
||||
condition.PresentedFrames = request.PresentedFrames;
|
||||
condition.DrainedGeneration = request.DrainedGeneration;
|
||||
condition.DiscontinuityGeneration = request.DiscontinuityGeneration;
|
||||
|
||||
KE_AUDIO_STREAM* stream = KiLookupAudioStream(request.StreamHandle, HANDLE_RIGHT_WAIT);
|
||||
if (!stream) return NTLSTATUS_INVALID_HANDLE;
|
||||
status = KeAudioArmStreamWait(stream, &condition);
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return status;
|
||||
}
|
||||
|
||||
// SYS_AUDIO_QUERY_STREAM a0=streamHandle a1=*SYS_AUDIO_STREAM_INFO.
|
||||
static uint64_t SyscallAudioQueryStream(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a2; (void)a3; (void)a4; (void)a5;
|
||||
if (!a0 || !a1) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t userSize = 0;
|
||||
KSTATUS status = CopyFromUser(&userSize, (const void*)(uintptr_t)a1, sizeof(userSize));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (userSize != sizeof(SYS_AUDIO_STREAM_INFO))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
status = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_AUDIO_STREAM_INFO));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
KE_AUDIO_STREAM* stream = KiLookupAudioStream((uint32_t)a0, HANDLE_RIGHT_QUERY);
|
||||
if (!stream) return NTLSTATUS_INVALID_HANDLE;
|
||||
KE_AUDIO_STREAM_INFO kernelInfo;
|
||||
status = KeAudioQueryStream(stream, &kernelInfo);
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_AUDIO_STREAM_INFO publicInfo;
|
||||
memset(&publicInfo, 0, sizeof(publicInfo));
|
||||
publicInfo.Size = sizeof(publicInfo);
|
||||
publicInfo.Version = SYS_AUDIO_ABI_VERSION;
|
||||
publicInfo.State = KiExportAudioStreamState(kernelInfo.State);
|
||||
publicInfo.Format = KiExportAudioFormat(&kernelInfo.Format);
|
||||
publicInfo.CapacityFrames = kernelInfo.CapacityFrames;
|
||||
publicInfo.BufferedFrames = kernelInfo.BufferedFrames;
|
||||
publicInfo.WritableFrames = kernelInfo.WritableFrames;
|
||||
publicInfo.FailureStatus = kernelInfo.FailureStatus;
|
||||
publicInfo.FramesSubmitted = kernelInfo.FramesSubmitted;
|
||||
publicInfo.FramesConsumed = kernelInfo.FramesConsumed;
|
||||
publicInfo.FramesPresented = kernelInfo.FramesPresented;
|
||||
publicInfo.FramesDiscarded = kernelInfo.FramesDiscarded;
|
||||
publicInfo.OutputFramesMixed = kernelInfo.OutputFramesMixed;
|
||||
publicInfo.SilenceFramesInserted = kernelInfo.SilenceFramesInserted;
|
||||
publicInfo.Underruns = kernelInfo.Underruns;
|
||||
publicInfo.DrainedGeneration = kernelInfo.DrainedGeneration;
|
||||
publicInfo.DiscontinuityGeneration = kernelInfo.DiscontinuityGeneration;
|
||||
publicInfo.VolumeQ16 = KiExportAudioGainQ16(kernelInfo.Volume);
|
||||
publicInfo.PanQ16 = KiExportAudioPanQ16(kernelInfo.Pan);
|
||||
publicInfo.Muted = kernelInfo.Muted;
|
||||
return CopyToUser((void*)(uintptr_t)a1, &publicInfo, sizeof(publicInfo));
|
||||
}
|
||||
|
||||
typedef KSTATUS (*KI_AUDIO_STREAM_OPERATION)(KE_AUDIO_STREAM* stream);
|
||||
|
||||
// Invokes one existing kernel stream operation through a write-capable handle.
|
||||
static KSTATUS KiCallAudioStreamOperation(uint32_t handle, KI_AUDIO_STREAM_OPERATION operation) {
|
||||
if (!handle || !operation) return NTLSTATUS_INVALID_PARAMETER;
|
||||
KE_AUDIO_STREAM* stream = KiLookupAudioStream(handle, HANDLE_RIGHT_WRITE);
|
||||
if (!stream) return NTLSTATUS_INVALID_HANDLE;
|
||||
KSTATUS status = operation(stream);
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return status;
|
||||
}
|
||||
|
||||
// SYS_AUDIO_START a0=streamHandle.
|
||||
static uint64_t SyscallAudioStart(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
return KiCallAudioStreamOperation((uint32_t)a0, KeAudioStartStream);
|
||||
}
|
||||
|
||||
// SYS_AUDIO_PAUSE a0=streamHandle.
|
||||
static uint64_t SyscallAudioPause(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
return KiCallAudioStreamOperation((uint32_t)a0, KeAudioPauseStream);
|
||||
}
|
||||
|
||||
// SYS_AUDIO_RESUME a0=streamHandle.
|
||||
static uint64_t SyscallAudioResume(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
return KiCallAudioStreamOperation((uint32_t)a0, KeAudioResumeStream);
|
||||
}
|
||||
|
||||
// SYS_AUDIO_DRAIN a0=streamHandle.
|
||||
static uint64_t SyscallAudioDrain(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
return KiCallAudioStreamOperation((uint32_t)a0, KeAudioDrainStream);
|
||||
}
|
||||
|
||||
// SYS_AUDIO_STOP a0=streamHandle.
|
||||
static uint64_t SyscallAudioStop(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
return KiCallAudioStreamOperation((uint32_t)a0, KeAudioStopStream);
|
||||
}
|
||||
|
||||
// SYS_AUDIO_RESET a0=streamHandle.
|
||||
static uint64_t SyscallAudioReset(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
return KiCallAudioStreamOperation((uint32_t)a0, KeAudioResetStream);
|
||||
}
|
||||
|
||||
// SYS_AUDIO_SET_VOLUME a0=*SYS_AUDIO_SET_VOLUME_REQUEST.
|
||||
static uint64_t SyscallAudioSetVolume(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
|
||||
SYS_AUDIO_SET_VOLUME_REQUEST request;
|
||||
KSTATUS status = KiCopyUserRequest(a0, &request, sizeof(request));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (request.Size != sizeof(request) || request.Flags != 0 || !request.StreamHandle ||
|
||||
request.Muted > 1u || request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY ||
|
||||
request.PanQ16 < SYS_AUDIO_PAN_Q16_LEFT ||
|
||||
request.PanQ16 > SYS_AUDIO_PAN_Q16_RIGHT)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KE_AUDIO_STREAM* stream = KiLookupAudioStream(request.StreamHandle, HANDLE_RIGHT_WRITE);
|
||||
if (!stream) return NTLSTATUS_INVALID_HANDLE;
|
||||
status = KeAudioSetStreamVolume(stream,
|
||||
KiImportAudioGainQ16(request.VolumeQ16),
|
||||
KiImportAudioPanQ16(request.PanQ16),
|
||||
request.Muted != 0);
|
||||
ObDereferenceObject((OBJECT*)stream);
|
||||
return status;
|
||||
}
|
||||
|
||||
// SYS_AUDIO_SET_MASTER_VOLUME a0=*SYS_AUDIO_SET_MASTER_VOLUME_REQUEST.
|
||||
static uint64_t SyscallAudioSetMasterVolume(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
||||
|
||||
SYS_AUDIO_SET_MASTER_VOLUME_REQUEST request;
|
||||
KSTATUS status = KiCopyUserRequest(a0, &request, sizeof(request));
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (request.Size != sizeof(request) || request.Flags != 0 || !request.EndpointId ||
|
||||
request.Muted > 1u || request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
return KeAudioSetMasterVolume(request.EndpointId,
|
||||
KiImportAudioGainQ16(request.VolumeQ16),
|
||||
request.Muted != 0);
|
||||
}
|
||||
|
||||
// Dispatch table keyed by SYS_* constants.
|
||||
typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t,
|
||||
uint64_t, uint64_t, uint64_t);
|
||||
@@ -5389,6 +5845,7 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
|
||||
[SYS_GET_PROCESS_LIST] = SyscallGetProcessList,
|
||||
[SYS_GET_PROCESS_PID] = SyscallGetProcessPid,
|
||||
[SYS_CREATE_THREAD] = SyscallCreateThread,
|
||||
[SYS_RESUME_THREAD] = SyscallResumeThread,
|
||||
[SYS_GET_THREAD_LIST] = SyscallGetThreadList,
|
||||
[SYS_LOAD_MODULE] = SyscallLoadModule,
|
||||
[SYS_UNLOAD_MODULE] = SyscallUnloadModule,
|
||||
@@ -5480,6 +5937,21 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
|
||||
[SYS_QUERY_SURFACE] = SyscallQuerySurface,
|
||||
[SYS_PRESENT_SURFACE] = SyscallPresentSurface,
|
||||
|
||||
// Native render audio.
|
||||
[SYS_AUDIO_ENUMERATE_ENDPOINTS] = SyscallAudioEnumerateEndpoints,
|
||||
[SYS_AUDIO_OPEN_STREAM] = SyscallAudioOpenStream,
|
||||
[SYS_AUDIO_WRITE] = SyscallAudioWrite,
|
||||
[SYS_AUDIO_ARM_WAIT] = SyscallAudioArmWait,
|
||||
[SYS_AUDIO_QUERY_STREAM] = SyscallAudioQueryStream,
|
||||
[SYS_AUDIO_START] = SyscallAudioStart,
|
||||
[SYS_AUDIO_PAUSE] = SyscallAudioPause,
|
||||
[SYS_AUDIO_RESUME] = SyscallAudioResume,
|
||||
[SYS_AUDIO_DRAIN] = SyscallAudioDrain,
|
||||
[SYS_AUDIO_STOP] = SyscallAudioStop,
|
||||
[SYS_AUDIO_RESET] = SyscallAudioReset,
|
||||
[SYS_AUDIO_SET_VOLUME] = SyscallAudioSetVolume,
|
||||
[SYS_AUDIO_SET_MASTER_VOLUME] = SyscallAudioSetMasterVolume,
|
||||
|
||||
// Input.
|
||||
[SYS_POLL_KEYBOARD] = SyscallKbPoll,
|
||||
[SYS_IS_KEY_DOWN] = SyscallKbIsKeyDown,
|
||||
@@ -5532,6 +6004,7 @@ uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1,
|
||||
THREAD* self = KeGetCurrentThread();
|
||||
PROCESS* owner = self ? self->Owner : (PROCESS*)0;
|
||||
if (owner && KeIsProcessTerminating(owner) &&
|
||||
!KeThreadTerminationDeferred(self) &&
|
||||
nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) {
|
||||
int32_t exitCode = owner->ExitCode;
|
||||
KeExitThread(self, exitCode);
|
||||
@@ -5547,6 +6020,7 @@ uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1,
|
||||
// has been marked terminating.
|
||||
owner = self ? self->Owner : (PROCESS*)0;
|
||||
if (owner && KeIsProcessTerminating(owner) &&
|
||||
!KeThreadTerminationDeferred(self) &&
|
||||
nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) {
|
||||
int32_t exitCode = owner->ExitCode;
|
||||
KeExitThread(self, exitCode);
|
||||
|
||||
Reference in new issue
Block a user