157c7f060STimo Kreuzer /*
257c7f060STimo Kreuzer * PROJECT: ReactOS Kernel
357c7f060STimo Kreuzer * LICENSE: GPL - See COPYING in the top level directory
46c5b3f20STimo Kreuzer * PURPOSE: x64 trap handlers
557c7f060STimo Kreuzer * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
657c7f060STimo Kreuzer */
757c7f060STimo Kreuzer
857c7f060STimo Kreuzer /* INCLUDES ******************************************************************/
957c7f060STimo Kreuzer
1057c7f060STimo Kreuzer #include <ntoskrnl.h>
1157c7f060STimo Kreuzer
1257c7f060STimo Kreuzer #define NDEBUG
1357c7f060STimo Kreuzer #include <debug.h>
1457c7f060STimo Kreuzer
1557c7f060STimo Kreuzer VOID
1657c7f060STimo Kreuzer KiRetireDpcListInDpcStack(
1757c7f060STimo Kreuzer PKPRCB Prcb,
1857c7f060STimo Kreuzer PVOID DpcStack);
1957c7f060STimo Kreuzer
2057c7f060STimo Kreuzer NTSTATUS
2157c7f060STimo Kreuzer KiConvertToGuiThread(
2257c7f060STimo Kreuzer VOID);
2357c7f060STimo Kreuzer
2457c7f060STimo Kreuzer _Requires_lock_not_held_(Prcb->PrcbLock)
2557c7f060STimo Kreuzer VOID
2657c7f060STimo Kreuzer NTAPI
KiDpcInterruptHandler(VOID)2757c7f060STimo Kreuzer KiDpcInterruptHandler(VOID)
2857c7f060STimo Kreuzer {
2957c7f060STimo Kreuzer PKPRCB Prcb = KeGetCurrentPrcb();
3057c7f060STimo Kreuzer PKTHREAD NewThread, OldThread;
3157c7f060STimo Kreuzer KIRQL OldIrql;
3257c7f060STimo Kreuzer
3357c7f060STimo Kreuzer /* Raise to DISPATCH_LEVEL */
3457c7f060STimo Kreuzer OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
3557c7f060STimo Kreuzer
3657c7f060STimo Kreuzer /* Send an EOI */
3757c7f060STimo Kreuzer KiSendEOI();
3857c7f060STimo Kreuzer
3957c7f060STimo Kreuzer /* Check for pending timers, pending DPCs, or pending ready threads */
4057c7f060STimo Kreuzer if ((Prcb->DpcData[0].DpcQueueDepth) ||
4157c7f060STimo Kreuzer (Prcb->TimerRequest) ||
4257c7f060STimo Kreuzer (Prcb->DeferredReadyListHead.Next))
4357c7f060STimo Kreuzer {
4457c7f060STimo Kreuzer /* Retire DPCs while under the DPC stack */
4557c7f060STimo Kreuzer KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
4657c7f060STimo Kreuzer }
4757c7f060STimo Kreuzer
4857c7f060STimo Kreuzer /* Enable interrupts */
4957c7f060STimo Kreuzer _enable();
5057c7f060STimo Kreuzer
5157c7f060STimo Kreuzer /* Check for quantum end */
5257c7f060STimo Kreuzer if (Prcb->QuantumEnd)
5357c7f060STimo Kreuzer {
5457c7f060STimo Kreuzer /* Handle quantum end */
5557c7f060STimo Kreuzer Prcb->QuantumEnd = FALSE;
5657c7f060STimo Kreuzer KiQuantumEnd();
5757c7f060STimo Kreuzer }
5857c7f060STimo Kreuzer else if (Prcb->NextThread)
5957c7f060STimo Kreuzer {
6057c7f060STimo Kreuzer /* Acquire the PRCB lock */
6157c7f060STimo Kreuzer KiAcquirePrcbLock(Prcb);
6257c7f060STimo Kreuzer
6357c7f060STimo Kreuzer /* Capture current thread data */
6457c7f060STimo Kreuzer OldThread = Prcb->CurrentThread;
6557c7f060STimo Kreuzer NewThread = Prcb->NextThread;
6657c7f060STimo Kreuzer
6757c7f060STimo Kreuzer /* Set new thread data */
6857c7f060STimo Kreuzer Prcb->NextThread = NULL;
6957c7f060STimo Kreuzer Prcb->CurrentThread = NewThread;
7057c7f060STimo Kreuzer
7157c7f060STimo Kreuzer /* The thread is now running */
7257c7f060STimo Kreuzer NewThread->State = Running;
7357c7f060STimo Kreuzer OldThread->WaitReason = WrDispatchInt;
7457c7f060STimo Kreuzer
7557c7f060STimo Kreuzer /* Make the old thread ready */
7657c7f060STimo Kreuzer KxQueueReadyThread(OldThread, Prcb);
7757c7f060STimo Kreuzer
7857c7f060STimo Kreuzer /* Swap to the new thread */
7957c7f060STimo Kreuzer KiSwapContext(APC_LEVEL, OldThread);
8057c7f060STimo Kreuzer }
8157c7f060STimo Kreuzer
8257c7f060STimo Kreuzer /* Disable interrupts and go back to old irql */
8357c7f060STimo Kreuzer _disable();
8457c7f060STimo Kreuzer KeLowerIrql(OldIrql);
8557c7f060STimo Kreuzer }
8657c7f060STimo Kreuzer
87d96f01b5STimo Kreuzer VOID
KiNmiInterruptHandler(_In_ PKTRAP_FRAME TrapFrame,_In_ PKEXCEPTION_FRAME ExceptionFrame)88d96f01b5STimo Kreuzer KiNmiInterruptHandler(
89d96f01b5STimo Kreuzer _In_ PKTRAP_FRAME TrapFrame,
90d96f01b5STimo Kreuzer _In_ PKEXCEPTION_FRAME ExceptionFrame)
91d96f01b5STimo Kreuzer {
92*6ac260dcSTimo Kreuzer BOOLEAN ManualSwapGs = FALSE;
93*6ac260dcSTimo Kreuzer
94*6ac260dcSTimo Kreuzer /* Check if the NMI came from kernel mode */
95*6ac260dcSTimo Kreuzer if ((TrapFrame->SegCs & MODE_MASK) == 0)
96*6ac260dcSTimo Kreuzer {
97*6ac260dcSTimo Kreuzer /* Check if GS base is already kernel mode. This is needed, because
98*6ac260dcSTimo Kreuzer we might be interrupted during an interrupt/exception from user-mode
99*6ac260dcSTimo Kreuzer before the swapgs instruction. */
100*6ac260dcSTimo Kreuzer if ((LONG64)__readmsr(MSR_GS_BASE) >= 0)
101*6ac260dcSTimo Kreuzer {
102*6ac260dcSTimo Kreuzer /* Swap GS to kernel */
103*6ac260dcSTimo Kreuzer __swapgs();
104*6ac260dcSTimo Kreuzer ManualSwapGs = TRUE;
105*6ac260dcSTimo Kreuzer }
106*6ac260dcSTimo Kreuzer }
107*6ac260dcSTimo Kreuzer
10892297093STimo Kreuzer /* Check if this is a freeze */
10992297093STimo Kreuzer if (KiProcessorFreezeHandler(TrapFrame, ExceptionFrame))
11092297093STimo Kreuzer {
11192297093STimo Kreuzer /* NMI was handled */
112*6ac260dcSTimo Kreuzer goto Exit;
11392297093STimo Kreuzer }
11492297093STimo Kreuzer
11592297093STimo Kreuzer /* Handle the NMI */
116d96f01b5STimo Kreuzer KiHandleNmi();
117*6ac260dcSTimo Kreuzer
118*6ac260dcSTimo Kreuzer Exit:
119*6ac260dcSTimo Kreuzer /* Check if we need to swap GS back */
120*6ac260dcSTimo Kreuzer if (ManualSwapGs)
121*6ac260dcSTimo Kreuzer {
122*6ac260dcSTimo Kreuzer /* Swap GS back to user */
123*6ac260dcSTimo Kreuzer __swapgs();
124*6ac260dcSTimo Kreuzer }
125d96f01b5STimo Kreuzer }
126d96f01b5STimo Kreuzer
12757c7f060STimo Kreuzer #define MAX_SYSCALL_PARAMS 16
12857c7f060STimo Kreuzer
12957c7f060STimo Kreuzer NTSTATUS
NtSyscallFailure(void)13057c7f060STimo Kreuzer NtSyscallFailure(void)
13157c7f060STimo Kreuzer {
13257c7f060STimo Kreuzer /* This is the failure function */
13357c7f060STimo Kreuzer return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
13457c7f060STimo Kreuzer }
13557c7f060STimo Kreuzer
13657c7f060STimo Kreuzer PVOID
KiSystemCallHandler(VOID)13757c7f060STimo Kreuzer KiSystemCallHandler(
13857c7f060STimo Kreuzer VOID)
13957c7f060STimo Kreuzer {
14057c7f060STimo Kreuzer PKTRAP_FRAME TrapFrame;
14157c7f060STimo Kreuzer PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
14257c7f060STimo Kreuzer PKTHREAD Thread;
14357c7f060STimo Kreuzer PULONG64 KernelParams, UserParams;
1445442f870STimo Kreuzer ULONG ServiceNumber, TableIndex, Count;
14557c7f060STimo Kreuzer ULONG64 UserRsp;
14657c7f060STimo Kreuzer
14757c7f060STimo Kreuzer /* Get a pointer to the trap frame */
14857c7f060STimo Kreuzer TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
14957c7f060STimo Kreuzer
15057c7f060STimo Kreuzer /* Increase system call count */
15157c7f060STimo Kreuzer __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
15257c7f060STimo Kreuzer
15357c7f060STimo Kreuzer /* Get the current thread */
15457c7f060STimo Kreuzer Thread = KeGetCurrentThread();
15557c7f060STimo Kreuzer
15657c7f060STimo Kreuzer /* Set previous mode */
15757c7f060STimo Kreuzer Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
15857c7f060STimo Kreuzer
15957c7f060STimo Kreuzer /* We don't have an exception frame yet */
16057c7f060STimo Kreuzer TrapFrame->ExceptionFrame = 0;
16157c7f060STimo Kreuzer
16257c7f060STimo Kreuzer /* Before enabling interrupts get the user rsp from the KPCR */
16357c7f060STimo Kreuzer UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
16457c7f060STimo Kreuzer TrapFrame->Rsp = UserRsp;
16557c7f060STimo Kreuzer
16657c7f060STimo Kreuzer /* Enable interrupts */
16757c7f060STimo Kreuzer _enable();
16857c7f060STimo Kreuzer
16957c7f060STimo Kreuzer /* If the usermode rsp was not a usermode address, prepare an exception */
17057c7f060STimo Kreuzer if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
17157c7f060STimo Kreuzer
17257c7f060STimo Kreuzer /* Get the address of the usermode and kernelmode parameters */
17357c7f060STimo Kreuzer UserParams = (PULONG64)UserRsp + 1;
17457c7f060STimo Kreuzer KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
17557c7f060STimo Kreuzer
17657c7f060STimo Kreuzer /* Get the system call number from the trap frame and decode it */
17757c7f060STimo Kreuzer ServiceNumber = (ULONG)TrapFrame->Rax;
1785442f870STimo Kreuzer TableIndex = (ServiceNumber >> TABLE_OFFSET_BITS) & ((1 << TABLE_NUMBER_BITS) - 1);
17957c7f060STimo Kreuzer ServiceNumber &= SERVICE_NUMBER_MASK;
18057c7f060STimo Kreuzer
18157c7f060STimo Kreuzer /* Check for win32k system calls */
1825442f870STimo Kreuzer if (TableIndex == WIN32K_SERVICE_INDEX)
18357c7f060STimo Kreuzer {
18457c7f060STimo Kreuzer ULONG GdiBatchCount;
18557c7f060STimo Kreuzer
18657c7f060STimo Kreuzer /* Read the GDI batch count from the TEB */
18757c7f060STimo Kreuzer _SEH2_TRY
18857c7f060STimo Kreuzer {
18957c7f060STimo Kreuzer GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
19057c7f060STimo Kreuzer }
19157c7f060STimo Kreuzer _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
19257c7f060STimo Kreuzer {
19357c7f060STimo Kreuzer GdiBatchCount = 0;
19457c7f060STimo Kreuzer }
19557c7f060STimo Kreuzer _SEH2_END;
19657c7f060STimo Kreuzer
19757c7f060STimo Kreuzer /* Flush batch, if there are entries */
19857c7f060STimo Kreuzer if (GdiBatchCount != 0)
19957c7f060STimo Kreuzer {
20057c7f060STimo Kreuzer KeGdiFlushUserBatch();
20157c7f060STimo Kreuzer }
20257c7f060STimo Kreuzer }
20357c7f060STimo Kreuzer
20457c7f060STimo Kreuzer /* Get descriptor table */
2055442f870STimo Kreuzer DescriptorTable = &((PKSERVICE_TABLE_DESCRIPTOR)Thread->ServiceTable)[TableIndex];
20657c7f060STimo Kreuzer
20757c7f060STimo Kreuzer /* Validate the system call number */
20857c7f060STimo Kreuzer if (ServiceNumber >= DescriptorTable->Limit)
20957c7f060STimo Kreuzer {
2105442f870STimo Kreuzer /* Check if this is a GUI call and this is not a GUI thread yet */
2115442f870STimo Kreuzer if ((TableIndex == WIN32K_SERVICE_INDEX) &&
2125442f870STimo Kreuzer (Thread->ServiceTable == KeServiceDescriptorTable))
21357c7f060STimo Kreuzer {
2145442f870STimo Kreuzer /* Convert this thread to a GUI thread.
2155442f870STimo Kreuzer It is invalid to change the stack in the middle of a C function,
2165442f870STimo Kreuzer therefore we return KiConvertToGuiThread to the system call entry
2175442f870STimo Kreuzer point, which then calls the asm function KiConvertToGuiThread,
2185442f870STimo Kreuzer which allocates a new stack, switches to it, calls
2195442f870STimo Kreuzer PsConvertToGuiThread and resumes in the middle of
2205442f870STimo Kreuzer KiSystemCallEntry64 to restart the system call handling.
2215442f870STimo Kreuzer If converting fails, the system call returns a failure code. */
2225442f870STimo Kreuzer return (PVOID)KiConvertToGuiThread;
2235442f870STimo Kreuzer }
2245442f870STimo Kreuzer
22557c7f060STimo Kreuzer /* Fail the call */
22657c7f060STimo Kreuzer TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
22757c7f060STimo Kreuzer return (PVOID)NtSyscallFailure;
22857c7f060STimo Kreuzer }
22957c7f060STimo Kreuzer
23057c7f060STimo Kreuzer /* Get stack bytes and calculate argument count */
23157c7f060STimo Kreuzer Count = DescriptorTable->Number[ServiceNumber] / 8;
23257c7f060STimo Kreuzer
23357c7f060STimo Kreuzer _SEH2_TRY
23457c7f060STimo Kreuzer {
23557c7f060STimo Kreuzer switch (Count)
23657c7f060STimo Kreuzer {
23757c7f060STimo Kreuzer case 16: KernelParams[15] = UserParams[15];
23857c7f060STimo Kreuzer case 15: KernelParams[14] = UserParams[14];
23957c7f060STimo Kreuzer case 14: KernelParams[13] = UserParams[13];
24057c7f060STimo Kreuzer case 13: KernelParams[12] = UserParams[12];
24157c7f060STimo Kreuzer case 12: KernelParams[11] = UserParams[11];
24257c7f060STimo Kreuzer case 11: KernelParams[10] = UserParams[10];
24357c7f060STimo Kreuzer case 10: KernelParams[9] = UserParams[9];
24457c7f060STimo Kreuzer case 9: KernelParams[8] = UserParams[8];
24557c7f060STimo Kreuzer case 8: KernelParams[7] = UserParams[7];
24657c7f060STimo Kreuzer case 7: KernelParams[6] = UserParams[6];
24757c7f060STimo Kreuzer case 6: KernelParams[5] = UserParams[5];
24857c7f060STimo Kreuzer case 5: KernelParams[4] = UserParams[4];
24957c7f060STimo Kreuzer case 4:
25057c7f060STimo Kreuzer case 3:
25157c7f060STimo Kreuzer case 2:
25257c7f060STimo Kreuzer case 1:
25357c7f060STimo Kreuzer case 0:
25457c7f060STimo Kreuzer break;
25557c7f060STimo Kreuzer
25657c7f060STimo Kreuzer default:
25757c7f060STimo Kreuzer ASSERT(FALSE);
25857c7f060STimo Kreuzer break;
25957c7f060STimo Kreuzer }
26057c7f060STimo Kreuzer }
26157c7f060STimo Kreuzer _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
26257c7f060STimo Kreuzer {
26357c7f060STimo Kreuzer TrapFrame->Rax = _SEH2_GetExceptionCode();
26457c7f060STimo Kreuzer return (PVOID)NtSyscallFailure;
26557c7f060STimo Kreuzer }
26657c7f060STimo Kreuzer _SEH2_END;
26757c7f060STimo Kreuzer
26857c7f060STimo Kreuzer return (PVOID)DescriptorTable->Base[ServiceNumber];
26957c7f060STimo Kreuzer }
27057c7f060STimo Kreuzer
27157c7f060STimo Kreuzer
27257c7f060STimo Kreuzer // FIXME: we need to
27357c7f060STimo Kreuzer VOID
KiSystemService(IN PKTHREAD Thread,IN PKTRAP_FRAME TrapFrame,IN ULONG Instruction)27457c7f060STimo Kreuzer KiSystemService(IN PKTHREAD Thread,
27557c7f060STimo Kreuzer IN PKTRAP_FRAME TrapFrame,
27657c7f060STimo Kreuzer IN ULONG Instruction)
27757c7f060STimo Kreuzer {
27857c7f060STimo Kreuzer UNIMPLEMENTED;
27957c7f060STimo Kreuzer __debugbreak();
28057c7f060STimo Kreuzer }
28157c7f060STimo Kreuzer
282