xref: /reactos/ntoskrnl/ke/amd64/traphandler.c (revision 2d4c0b87)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         x64 trap handlers
5  * PROGRAMMERS:     Timo Kreuzer (timo.kreuzer@reactos.org)
6  */
7 
8 /* INCLUDES ******************************************************************/
9 
10 #include <ntoskrnl.h>
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 VOID
16 KiRetireDpcListInDpcStack(
17     PKPRCB Prcb,
18     PVOID DpcStack);
19 
20 NTSTATUS
21 KiConvertToGuiThread(
22     VOID);
23 
24 _Requires_lock_not_held_(Prcb->PrcbLock)
25 VOID
26 NTAPI
27 KiDpcInterruptHandler(VOID)
28 {
29     PKPRCB Prcb = KeGetCurrentPrcb();
30     PKTHREAD NewThread, OldThread;
31     KIRQL OldIrql;
32 
33     /* Raise to DISPATCH_LEVEL */
34     OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
35 
36     /* Send an EOI */
37     KiSendEOI();
38 
39     /* Check for pending timers, pending DPCs, or pending ready threads */
40     if ((Prcb->DpcData[0].DpcQueueDepth) ||
41         (Prcb->TimerRequest) ||
42         (Prcb->DeferredReadyListHead.Next))
43     {
44         /* Retire DPCs while under the DPC stack */
45         KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
46     }
47 
48     /* Enable interrupts */
49     _enable();
50 
51     /* Check for quantum end */
52     if (Prcb->QuantumEnd)
53     {
54         /* Handle quantum end */
55         Prcb->QuantumEnd = FALSE;
56         KiQuantumEnd();
57     }
58     else if (Prcb->NextThread)
59     {
60         /* Acquire the PRCB lock */
61         KiAcquirePrcbLock(Prcb);
62 
63         /* Capture current thread data */
64         OldThread = Prcb->CurrentThread;
65         NewThread = Prcb->NextThread;
66 
67         /* Set new thread data */
68         Prcb->NextThread = NULL;
69         Prcb->CurrentThread = NewThread;
70 
71         /* The thread is now running */
72         NewThread->State = Running;
73         OldThread->WaitReason = WrDispatchInt;
74 
75         /* Make the old thread ready */
76         KxQueueReadyThread(OldThread, Prcb);
77 
78         /* Swap to the new thread */
79         KiSwapContext(APC_LEVEL, OldThread);
80     }
81 
82     /* Disable interrupts and go back to old irql */
83     _disable();
84     KeLowerIrql(OldIrql);
85 }
86 
87 VOID
88 KiNmiInterruptHandler(
89     _In_ PKTRAP_FRAME TrapFrame,
90     _In_ PKEXCEPTION_FRAME ExceptionFrame)
91 {
92     BOOLEAN ManualSwapGs = FALSE;
93 
94     /* Check if the NMI came from kernel mode */
95     if ((TrapFrame->SegCs & MODE_MASK) == 0)
96     {
97         /* Check if GS base is already kernel mode. This is needed, because
98            we might be interrupted during an interrupt/exception from user-mode
99            before the swapgs instruction. */
100         if ((LONG64)__readmsr(MSR_GS_BASE) >= 0)
101         {
102             /* Swap GS to kernel */
103             __swapgs();
104             ManualSwapGs = TRUE;
105         }
106     }
107 
108     /* Check if this is a freeze */
109     if (KiProcessorFreezeHandler(TrapFrame, ExceptionFrame))
110     {
111         /* NMI was handled */
112         goto Exit;
113     }
114 
115     /* Handle the NMI */
116     KiHandleNmi();
117 
118 Exit:
119     /* Check if we need to swap GS back */
120     if (ManualSwapGs)
121     {
122         /* Swap GS back to user */
123         __swapgs();
124     }
125 }
126 
127 #define MAX_SYSCALL_PARAMS 16
128 
129 NTSTATUS
130 NtSyscallFailure(void)
131 {
132     /* This is the failure function */
133     return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
134 }
135 
136 PVOID
137 KiSystemCallHandler(
138     VOID)
139 {
140     PKTRAP_FRAME TrapFrame;
141     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
142     PKTHREAD Thread;
143     PULONG64 KernelParams, UserParams;
144     ULONG ServiceNumber, TableIndex, Count;
145     ULONG64 UserRsp;
146 
147     /* Get a pointer to the trap frame */
148     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
149 
150     /* Increase system call count */
151     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
152 
153     /* Get the current thread */
154     Thread = KeGetCurrentThread();
155 
156     /* Set previous mode */
157     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
158 
159     /* We don't have an exception frame yet */
160     TrapFrame->ExceptionFrame = 0;
161 
162     /* Get the user Stack pointer */
163     UserRsp = TrapFrame->Rsp;
164 
165     /* Enable interrupts */
166     _enable();
167 
168     /* If the usermode rsp was not a usermode address, prepare an exception */
169     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
170 
171     /* Get the address of the usermode and kernelmode parameters */
172     UserParams = (PULONG64)UserRsp + 1;
173     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
174 
175     /* Get the system call number from the trap frame and decode it */
176     ServiceNumber = (ULONG)TrapFrame->Rax;
177     TableIndex = (ServiceNumber >> TABLE_OFFSET_BITS) & ((1 << TABLE_NUMBER_BITS) - 1);
178     ServiceNumber &= SERVICE_NUMBER_MASK;
179 
180     /* Check for win32k system calls */
181     if (TableIndex == WIN32K_SERVICE_INDEX)
182     {
183         ULONG GdiBatchCount;
184 
185         /* Read the GDI batch count from the TEB */
186         _SEH2_TRY
187         {
188             GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
189         }
190         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
191         {
192             GdiBatchCount = 0;
193         }
194         _SEH2_END;
195 
196         /* Flush batch, if there are entries */
197         if (GdiBatchCount != 0)
198         {
199             KeGdiFlushUserBatch();
200         }
201     }
202 
203     /* Get descriptor table */
204     DescriptorTable = &((PKSERVICE_TABLE_DESCRIPTOR)Thread->ServiceTable)[TableIndex];
205 
206     /* Validate the system call number */
207     if (ServiceNumber >= DescriptorTable->Limit)
208     {
209         /* Check if this is a GUI call and this is not a GUI thread yet */
210         if ((TableIndex == WIN32K_SERVICE_INDEX) &&
211             (Thread->ServiceTable == KeServiceDescriptorTable))
212         {
213             /* Convert this thread to a GUI thread.
214                It is invalid to change the stack in the middle of a C function,
215                therefore we return KiConvertToGuiThread to the system call entry
216                point, which then calls the asm function KiConvertToGuiThread,
217                which allocates a new stack, switches to it, calls
218                PsConvertToGuiThread and resumes in the middle of
219                KiSystemCallEntry64 to restart the system call handling.
220                If converting fails, the system call returns a failure code. */
221             return (PVOID)KiConvertToGuiThread;
222         }
223 
224         /* Fail the call */
225         TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
226         return (PVOID)NtSyscallFailure;
227     }
228 
229     /* Get stack bytes and calculate argument count */
230     Count = DescriptorTable->Number[ServiceNumber] / 8;
231 
232     _SEH2_TRY
233     {
234         switch (Count)
235         {
236             case 16: KernelParams[15] = UserParams[15];
237             case 15: KernelParams[14] = UserParams[14];
238             case 14: KernelParams[13] = UserParams[13];
239             case 13: KernelParams[12] = UserParams[12];
240             case 12: KernelParams[11] = UserParams[11];
241             case 11: KernelParams[10] = UserParams[10];
242             case 10: KernelParams[9] = UserParams[9];
243             case 9: KernelParams[8] = UserParams[8];
244             case 8: KernelParams[7] = UserParams[7];
245             case 7: KernelParams[6] = UserParams[6];
246             case 6: KernelParams[5] = UserParams[5];
247             case 5: KernelParams[4] = UserParams[4];
248             case 4:
249             case 3:
250             case 2:
251             case 1:
252             case 0:
253                 break;
254 
255             default:
256                 ASSERT(FALSE);
257                 break;
258         }
259     }
260     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
261     {
262         TrapFrame->Rax = _SEH2_GetExceptionCode();
263         return (PVOID)NtSyscallFailure;
264     }
265     _SEH2_END;
266 
267     return (PVOID)DescriptorTable->Base[ServiceNumber];
268 }
269 
270 
271 // FIXME: we need to
272 VOID
273 KiSystemService(IN PKTHREAD Thread,
274                 IN PKTRAP_FRAME TrapFrame,
275                 IN ULONG Instruction)
276 {
277     UNIMPLEMENTED;
278     __debugbreak();
279 }
280 
281