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
KiDpcInterruptHandler(VOID)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
KiNmiInterruptHandler(_In_ PKTRAP_FRAME TrapFrame,_In_ PKEXCEPTION_FRAME ExceptionFrame)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
NtSyscallFailure(void)130 NtSyscallFailure(void)
131 {
132 /* This is the failure function */
133 return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
134 }
135
136 PVOID
KiSystemCallHandler(VOID)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
KiSystemService(IN PKTHREAD Thread,IN PKTRAP_FRAME TrapFrame,IN ULONG Instruction)273 KiSystemService(IN PKTHREAD Thread,
274 IN PKTRAP_FRAME TrapFrame,
275 IN ULONG Instruction)
276 {
277 UNIMPLEMENTED;
278 __debugbreak();
279 }
280
281