reactos/ntoskrnl/ke/amd64/traphandler.c
Timo Kreuzer 1538712c0b [NTOS:KE/x64] Move setting the thread's trap frame to KiSystemCallEntry64
This is needed, because KiSystemCallHandler can be called multiple times for the same syscall entry, which would mess up the linkage. This replaces a previous hack and makes things cleaner.
2024-03-31 12:12:48 +02:00

241 lines
6.7 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* PURPOSE: x64 trap handlers
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
VOID
KiRetireDpcListInDpcStack(
PKPRCB Prcb,
PVOID DpcStack);
NTSTATUS
KiConvertToGuiThread(
VOID);
_Requires_lock_not_held_(Prcb->PrcbLock)
VOID
NTAPI
KiDpcInterruptHandler(VOID)
{
PKPRCB Prcb = KeGetCurrentPrcb();
PKTHREAD NewThread, OldThread;
KIRQL OldIrql;
/* Raise to DISPATCH_LEVEL */
OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
/* Send an EOI */
KiSendEOI();
/* Check for pending timers, pending DPCs, or pending ready threads */
if ((Prcb->DpcData[0].DpcQueueDepth) ||
(Prcb->TimerRequest) ||
(Prcb->DeferredReadyListHead.Next))
{
/* Retire DPCs while under the DPC stack */
KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
}
/* Enable interrupts */
_enable();
/* Check for quantum end */
if (Prcb->QuantumEnd)
{
/* Handle quantum end */
Prcb->QuantumEnd = FALSE;
KiQuantumEnd();
}
else if (Prcb->NextThread)
{
/* Acquire the PRCB lock */
KiAcquirePrcbLock(Prcb);
/* Capture current thread data */
OldThread = Prcb->CurrentThread;
NewThread = Prcb->NextThread;
/* Set new thread data */
Prcb->NextThread = NULL;
Prcb->CurrentThread = NewThread;
/* The thread is now running */
NewThread->State = Running;
OldThread->WaitReason = WrDispatchInt;
/* Make the old thread ready */
KxQueueReadyThread(OldThread, Prcb);
/* Swap to the new thread */
KiSwapContext(APC_LEVEL, OldThread);
}
/* Disable interrupts and go back to old irql */
_disable();
KeLowerIrql(OldIrql);
}
#define MAX_SYSCALL_PARAMS 16
NTSTATUS
NtSyscallFailure(void)
{
/* This is the failure function */
return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
}
PVOID
KiSystemCallHandler(
VOID)
{
PKTRAP_FRAME TrapFrame;
PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
PKTHREAD Thread;
PULONG64 KernelParams, UserParams;
ULONG ServiceNumber, TableIndex, Count;
ULONG64 UserRsp;
/* Get a pointer to the trap frame */
TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
/* Increase system call count */
__addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
/* Get the current thread */
Thread = KeGetCurrentThread();
/* Set previous mode */
Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
/* We don't have an exception frame yet */
TrapFrame->ExceptionFrame = 0;
/* Before enabling interrupts get the user rsp from the KPCR */
UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
TrapFrame->Rsp = UserRsp;
/* Enable interrupts */
_enable();
/* If the usermode rsp was not a usermode address, prepare an exception */
if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
/* Get the address of the usermode and kernelmode parameters */
UserParams = (PULONG64)UserRsp + 1;
KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
/* Get the system call number from the trap frame and decode it */
ServiceNumber = (ULONG)TrapFrame->Rax;
TableIndex = (ServiceNumber >> TABLE_OFFSET_BITS) & ((1 << TABLE_NUMBER_BITS) - 1);
ServiceNumber &= SERVICE_NUMBER_MASK;
/* Check for win32k system calls */
if (TableIndex == WIN32K_SERVICE_INDEX)
{
ULONG GdiBatchCount;
/* Read the GDI batch count from the TEB */
_SEH2_TRY
{
GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
GdiBatchCount = 0;
}
_SEH2_END;
/* Flush batch, if there are entries */
if (GdiBatchCount != 0)
{
KeGdiFlushUserBatch();
}
}
/* Get descriptor table */
DescriptorTable = &((PKSERVICE_TABLE_DESCRIPTOR)Thread->ServiceTable)[TableIndex];
/* Validate the system call number */
if (ServiceNumber >= DescriptorTable->Limit)
{
/* Check if this is a GUI call and this is not a GUI thread yet */
if ((TableIndex == WIN32K_SERVICE_INDEX) &&
(Thread->ServiceTable == KeServiceDescriptorTable))
{
/* Convert this thread to a GUI thread.
It is invalid to change the stack in the middle of a C function,
therefore we return KiConvertToGuiThread to the system call entry
point, which then calls the asm function KiConvertToGuiThread,
which allocates a new stack, switches to it, calls
PsConvertToGuiThread and resumes in the middle of
KiSystemCallEntry64 to restart the system call handling.
If converting fails, the system call returns a failure code. */
return (PVOID)KiConvertToGuiThread;
}
/* Fail the call */
TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
return (PVOID)NtSyscallFailure;
}
/* Get stack bytes and calculate argument count */
Count = DescriptorTable->Number[ServiceNumber] / 8;
_SEH2_TRY
{
switch (Count)
{
case 16: KernelParams[15] = UserParams[15];
case 15: KernelParams[14] = UserParams[14];
case 14: KernelParams[13] = UserParams[13];
case 13: KernelParams[12] = UserParams[12];
case 12: KernelParams[11] = UserParams[11];
case 11: KernelParams[10] = UserParams[10];
case 10: KernelParams[9] = UserParams[9];
case 9: KernelParams[8] = UserParams[8];
case 8: KernelParams[7] = UserParams[7];
case 7: KernelParams[6] = UserParams[6];
case 6: KernelParams[5] = UserParams[5];
case 5: KernelParams[4] = UserParams[4];
case 4:
case 3:
case 2:
case 1:
case 0:
break;
default:
ASSERT(FALSE);
break;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
TrapFrame->Rax = _SEH2_GetExceptionCode();
return (PVOID)NtSyscallFailure;
}
_SEH2_END;
return (PVOID)DescriptorTable->Base[ServiceNumber];
}
// FIXME: we need to
VOID
KiSystemService(IN PKTHREAD Thread,
IN PKTRAP_FRAME TrapFrame,
IN ULONG Instruction)
{
UNIMPLEMENTED;
__debugbreak();
}