xref: /reactos/ntoskrnl/ke/amd64/traphandler.c (revision 6ac260dc)
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