xref: /reactos/ntoskrnl/ke/amd64/traphandler.c (revision 32d615fc)
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     /* Before enabling interrupts get the user rsp from the KPCR */
163     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
164     TrapFrame->Rsp = UserRsp;
165 
166     /* Enable interrupts */
167     _enable();
168 
169     /* If the usermode rsp was not a usermode address, prepare an exception */
170     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
171 
172     /* Get the address of the usermode and kernelmode parameters */
173     UserParams = (PULONG64)UserRsp + 1;
174     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
175 
176     /* Get the system call number from the trap frame and decode it */
177     ServiceNumber = (ULONG)TrapFrame->Rax;
178     TableIndex = (ServiceNumber >> TABLE_OFFSET_BITS) & ((1 << TABLE_NUMBER_BITS) - 1);
179     ServiceNumber &= SERVICE_NUMBER_MASK;
180 
181     /* Check for win32k system calls */
182     if (TableIndex == WIN32K_SERVICE_INDEX)
183     {
184         ULONG GdiBatchCount;
185 
186         /* Read the GDI batch count from the TEB */
187         _SEH2_TRY
188         {
189             GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
190         }
191         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
192         {
193             GdiBatchCount = 0;
194         }
195         _SEH2_END;
196 
197         /* Flush batch, if there are entries */
198         if (GdiBatchCount != 0)
199         {
200             KeGdiFlushUserBatch();
201         }
202     }
203 
204     /* Get descriptor table */
205     DescriptorTable = &((PKSERVICE_TABLE_DESCRIPTOR)Thread->ServiceTable)[TableIndex];
206 
207     /* Validate the system call number */
208     if (ServiceNumber >= DescriptorTable->Limit)
209     {
210         /* Check if this is a GUI call and this is not a GUI thread yet */
211         if ((TableIndex == WIN32K_SERVICE_INDEX) &&
212             (Thread->ServiceTable == KeServiceDescriptorTable))
213         {
214             /* Convert this thread to a GUI thread.
215                It is invalid to change the stack in the middle of a C function,
216                therefore we return KiConvertToGuiThread to the system call entry
217                point, which then calls the asm function KiConvertToGuiThread,
218                which allocates a new stack, switches to it, calls
219                PsConvertToGuiThread and resumes in the middle of
220                KiSystemCallEntry64 to restart the system call handling.
221                If converting fails, the system call returns a failure code. */
222             return (PVOID)KiConvertToGuiThread;
223         }
224 
225         /* Fail the call */
226         TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
227         return (PVOID)NtSyscallFailure;
228     }
229 
230     /* Get stack bytes and calculate argument count */
231     Count = DescriptorTable->Number[ServiceNumber] / 8;
232 
233     _SEH2_TRY
234     {
235         switch (Count)
236         {
237             case 16: KernelParams[15] = UserParams[15];
238             case 15: KernelParams[14] = UserParams[14];
239             case 14: KernelParams[13] = UserParams[13];
240             case 13: KernelParams[12] = UserParams[12];
241             case 12: KernelParams[11] = UserParams[11];
242             case 11: KernelParams[10] = UserParams[10];
243             case 10: KernelParams[9] = UserParams[9];
244             case 9: KernelParams[8] = UserParams[8];
245             case 8: KernelParams[7] = UserParams[7];
246             case 7: KernelParams[6] = UserParams[6];
247             case 6: KernelParams[5] = UserParams[5];
248             case 5: KernelParams[4] = UserParams[4];
249             case 4:
250             case 3:
251             case 2:
252             case 1:
253             case 0:
254                 break;
255 
256             default:
257                 ASSERT(FALSE);
258                 break;
259         }
260     }
261     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
262     {
263         TrapFrame->Rax = _SEH2_GetExceptionCode();
264         return (PVOID)NtSyscallFailure;
265     }
266     _SEH2_END;
267 
268     return (PVOID)DescriptorTable->Base[ServiceNumber];
269 }
270 
271 
272 // FIXME: we need to
273 VOID
274 KiSystemService(IN PKTHREAD Thread,
275                 IN PKTRAP_FRAME TrapFrame,
276                 IN ULONG Instruction)
277 {
278     UNIMPLEMENTED;
279     __debugbreak();
280 }
281 
282