xref: /reactos/ntoskrnl/ke/amd64/traphandler.c (revision 864aed6b)
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 #define MAX_SYSCALL_PARAMS 16
88 
89 NTSTATUS
90 NtSyscallFailure(void)
91 {
92     /* This is the failure function */
93     return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
94 }
95 
96 PVOID
97 KiSystemCallHandler(
98     VOID)
99 {
100     PKTRAP_FRAME TrapFrame;
101     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
102     PKTHREAD Thread;
103     PULONG64 KernelParams, UserParams;
104     ULONG ServiceNumber, Offset, Count;
105     ULONG64 UserRsp;
106 
107     /* Get a pointer to the trap frame */
108     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
109 
110     /* Increase system call count */
111     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
112 
113     /* Get the current thread */
114     Thread = KeGetCurrentThread();
115 
116     /* Set previous mode */
117     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
118 
119     /* Save the old trap frame and set the new */
120     TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
121     Thread->TrapFrame = TrapFrame;
122 
123     /* We don't have an exception frame yet */
124     TrapFrame->ExceptionFrame = 0;
125 
126     /* Before enabling interrupts get the user rsp from the KPCR */
127     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
128     TrapFrame->Rsp = UserRsp;
129 
130     /* Enable interrupts */
131     _enable();
132 
133     /* If the usermode rsp was not a usermode address, prepare an exception */
134     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
135 
136     /* Get the address of the usermode and kernelmode parameters */
137     UserParams = (PULONG64)UserRsp + 1;
138     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
139 
140     /* Get the system call number from the trap frame and decode it */
141     ServiceNumber = (ULONG)TrapFrame->Rax;
142     Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
143     ServiceNumber &= SERVICE_NUMBER_MASK;
144 
145     /* Check for win32k system calls */
146     if (Offset & SERVICE_TABLE_TEST)
147     {
148         ULONG GdiBatchCount;
149 
150         /* Read the GDI batch count from the TEB */
151         _SEH2_TRY
152         {
153             GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
154         }
155         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
156         {
157             GdiBatchCount = 0;
158         }
159         _SEH2_END;
160 
161         /* Flush batch, if there are entries */
162         if (GdiBatchCount != 0)
163         {
164             KeGdiFlushUserBatch();
165         }
166     }
167 
168     /* Get descriptor table */
169     DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
170 
171     /* Validate the system call number */
172     if (ServiceNumber >= DescriptorTable->Limit)
173     {
174         /* Check if this is a GUI call */
175         if (!(Offset & SERVICE_TABLE_TEST))
176         {
177             /* Fail the call */
178             TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
179             return (PVOID)NtSyscallFailure;
180         }
181 
182         /* Convert us to a GUI thread
183            To be entirely correct. we return KiConvertToGuiThread,
184            which allocates a new stack, switches to it, calls
185            PsConvertToGuiThread and resumes in the middle of
186            KiSystemCallEntry64 to restart the system call handling. */
187         return (PVOID)KiConvertToGuiThread;
188     }
189 
190     /* Get stack bytes and calculate argument count */
191     Count = DescriptorTable->Number[ServiceNumber] / 8;
192 
193     _SEH2_TRY
194     {
195         switch (Count)
196         {
197             case 16: KernelParams[15] = UserParams[15];
198             case 15: KernelParams[14] = UserParams[14];
199             case 14: KernelParams[13] = UserParams[13];
200             case 13: KernelParams[12] = UserParams[12];
201             case 12: KernelParams[11] = UserParams[11];
202             case 11: KernelParams[10] = UserParams[10];
203             case 10: KernelParams[9] = UserParams[9];
204             case 9: KernelParams[8] = UserParams[8];
205             case 8: KernelParams[7] = UserParams[7];
206             case 7: KernelParams[6] = UserParams[6];
207             case 6: KernelParams[5] = UserParams[5];
208             case 5: KernelParams[4] = UserParams[4];
209             case 4:
210             case 3:
211             case 2:
212             case 1:
213             case 0:
214                 break;
215 
216             default:
217                 ASSERT(FALSE);
218                 break;
219         }
220     }
221     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
222     {
223         TrapFrame->Rax = _SEH2_GetExceptionCode();
224         return (PVOID)NtSyscallFailure;
225     }
226     _SEH2_END;
227 
228     return (PVOID)DescriptorTable->Base[ServiceNumber];
229 }
230 
231 
232 // FIXME: we need to
233 VOID
234 KiSystemService(IN PKTHREAD Thread,
235                 IN PKTRAP_FRAME TrapFrame,
236                 IN ULONG Instruction)
237 {
238     UNIMPLEMENTED;
239     __debugbreak();
240 }
241 
242