xref: /reactos/ntoskrnl/ke/amd64/stubs.c (revision 1734f297)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         stubs
5  * PROGRAMMERS:     Timo Kreuzer (timo.kreuzer@reactos.org)
6  */
7 
8 /* INCLUDES ******************************************************************/
9 
10 #include <ntoskrnl.h>
11 #include <fltkernel.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 ULONG ProcessCount;
17 SIZE_T KeXStateLength = sizeof(XSAVE_FORMAT);
18 
19 VOID
20 KiRetireDpcListInDpcStack(
21     PKPRCB Prcb,
22     PVOID DpcStack);
23 
24 NTSTATUS
25 KiConvertToGuiThread(
26     VOID);
27 
28 _Requires_lock_not_held_(Prcb->PrcbLock)
29 VOID
30 NTAPI
31 KiDpcInterruptHandler(VOID)
32 {
33     PKPRCB Prcb = KeGetCurrentPrcb();
34     PKTHREAD NewThread, OldThread;
35     KIRQL OldIrql;
36 
37     /* Raise to DISPATCH_LEVEL */
38     OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
39 
40     /* Send an EOI */
41     KiSendEOI();
42 
43     /* Check for pending timers, pending DPCs, or pending ready threads */
44     if ((Prcb->DpcData[0].DpcQueueDepth) ||
45         (Prcb->TimerRequest) ||
46         (Prcb->DeferredReadyListHead.Next))
47     {
48         /* Retire DPCs while under the DPC stack */
49         KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
50     }
51 
52     /* Enable interrupts */
53     _enable();
54 
55     /* Check for quantum end */
56     if (Prcb->QuantumEnd)
57     {
58         /* Handle quantum end */
59         Prcb->QuantumEnd = FALSE;
60         KiQuantumEnd();
61     }
62     else if (Prcb->NextThread)
63     {
64         /* Acquire the PRCB lock */
65         KiAcquirePrcbLock(Prcb);
66 
67         /* Capture current thread data */
68         OldThread = Prcb->CurrentThread;
69         NewThread = Prcb->NextThread;
70 
71         /* Set new thread data */
72         Prcb->NextThread = NULL;
73         Prcb->CurrentThread = NewThread;
74 
75         /* The thread is now running */
76         NewThread->State = Running;
77         OldThread->WaitReason = WrDispatchInt;
78 
79         /* Make the old thread ready */
80         KxQueueReadyThread(OldThread, Prcb);
81 
82         /* Swap to the new thread */
83         KiSwapContext(APC_LEVEL, OldThread);
84     }
85 
86     /* Disable interrupts and go back to old irql */
87     _disable();
88     KeLowerIrql(OldIrql);
89 }
90 
91 PVOID
92 KiSwitchKernelStackHelper(
93     LONG_PTR StackOffset,
94     PVOID OldStackBase);
95 
96 /*
97  * Kernel stack layout (example pointers):
98  * 0xFFFFFC0F'2D008000 KTHREAD::StackBase
99  *    [XSAVE_AREA size == KeXStateLength = 0x440]
100  * 0xFFFFFC0F'2D007BC0 KTHREAD::StateSaveArea _XSAVE_FORMAT
101  * 0xFFFFFC0F'2D007B90 KTHREAD::InitialStack
102  *    [0x190 bytes KTRAP_FRAME]
103  * 0xFFFFFC0F'2D007A00 KTHREAD::TrapFrame
104  *    [KSTART_FRAME] or ...
105  *    [KSWITCH_FRAME]
106  * 0xFFFFFC0F'2D007230 KTHREAD::KernelStack
107  */
108 
109 PVOID
110 NTAPI
111 KiSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
112 {
113     PKTHREAD CurrentThread;
114     PVOID OldStackBase;
115     LONG_PTR StackOffset;
116     SIZE_T StackSize;
117     PKIPCR Pcr;
118     ULONG Eflags;
119 
120     /* Get the current thread */
121     CurrentThread = KeGetCurrentThread();
122 
123     /* Save the old stack base */
124     OldStackBase = CurrentThread->StackBase;
125 
126     /* Get the size of the current stack */
127     StackSize = (ULONG_PTR)CurrentThread->StackBase - CurrentThread->StackLimit;
128     ASSERT(StackSize <= (ULONG_PTR)StackBase - (ULONG_PTR)StackLimit);
129 
130     /* Copy the current stack contents to the new stack */
131     RtlCopyMemory((PUCHAR)StackBase - StackSize,
132                   (PVOID)CurrentThread->StackLimit,
133                   StackSize);
134 
135     /* Calculate the offset between the old and the new stack */
136     StackOffset = (PUCHAR)StackBase - (PUCHAR)CurrentThread->StackBase;
137 
138     /* Disable interrupts while messing with the stack */
139     Eflags = __readeflags();
140     _disable();
141 
142     /* Set the new trap frame */
143     CurrentThread->TrapFrame = (PKTRAP_FRAME)Add2Ptr(CurrentThread->TrapFrame,
144                                                      StackOffset);
145 
146     /* Set the new initial stack */
147     CurrentThread->InitialStack = Add2Ptr(CurrentThread->InitialStack,
148                                           StackOffset);
149 
150     /* Set the new stack limits */
151     CurrentThread->StackBase = StackBase;
152     CurrentThread->StackLimit = (ULONG_PTR)StackLimit;
153     CurrentThread->LargeStack = TRUE;
154 
155     /* Adjust RspBase in the PCR */
156     Pcr = (PKIPCR)KeGetPcr();
157     Pcr->Prcb.RspBase += StackOffset;
158 
159     /* Adjust Rsp0 in the TSS */
160     Pcr->TssBase->Rsp0 += StackOffset;
161 
162     /* Restore interrupts */
163     __writeeflags(Eflags);
164 
165     return OldStackBase;
166 }
167 
168 DECLSPEC_NORETURN
169 VOID
170 KiIdleLoop(VOID)
171 {
172     PKPRCB Prcb = KeGetCurrentPrcb();
173     PKTHREAD OldThread, NewThread;
174 
175     /* Now loop forever */
176     while (TRUE)
177     {
178         /* Start of the idle loop: disable interrupts */
179         _enable();
180         YieldProcessor();
181         YieldProcessor();
182         _disable();
183 
184         /* Check for pending timers, pending DPCs, or pending ready threads */
185         if ((Prcb->DpcData[0].DpcQueueDepth) ||
186             (Prcb->TimerRequest) ||
187             (Prcb->DeferredReadyListHead.Next))
188         {
189             /* Quiesce the DPC software interrupt */
190             HalClearSoftwareInterrupt(DISPATCH_LEVEL);
191 
192             /* Handle it */
193             KiRetireDpcList(Prcb);
194         }
195 
196         /* Check if a new thread is scheduled for execution */
197         if (Prcb->NextThread)
198         {
199             /* Enable interrupts */
200             _enable();
201 
202             /* Capture current thread data */
203             OldThread = Prcb->CurrentThread;
204             NewThread = Prcb->NextThread;
205 
206             /* Set new thread data */
207             Prcb->NextThread = NULL;
208             Prcb->CurrentThread = NewThread;
209 
210             /* The thread is now running */
211             NewThread->State = Running;
212 
213             /* Do the swap at SYNCH_LEVEL */
214             KfRaiseIrql(SYNCH_LEVEL);
215 
216             /* Switch away from the idle thread */
217             KiSwapContext(APC_LEVEL, OldThread);
218 
219             /* Go back to DISPATCH_LEVEL */
220             KeLowerIrql(DISPATCH_LEVEL);
221         }
222         else
223         {
224             /* Continue staying idle. Note the HAL returns with interrupts on */
225             Prcb->PowerState.IdleFunction(&Prcb->PowerState);
226         }
227     }
228 }
229 
230 VOID
231 NTAPI
232 KiSwapProcess(IN PKPROCESS NewProcess,
233               IN PKPROCESS OldProcess)
234 {
235     PKIPCR Pcr = (PKIPCR)KeGetPcr();
236 
237 #ifdef CONFIG_SMP
238     /* Update active processor mask */
239     InterlockedXor64((PLONG64)&NewProcess->ActiveProcessors, Pcr->Prcb.SetMember);
240     InterlockedXor64((PLONG64)&OldProcess->ActiveProcessors, Pcr->Prcb.SetMember);
241 #endif
242 
243     /* Update CR3 */
244     __writecr3(NewProcess->DirectoryTableBase[0]);
245 
246     /* Update IOPM offset */
247     Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
248 }
249 
250 #define MAX_SYSCALL_PARAMS 16
251 
252 NTSTATUS
253 NtSyscallFailure(void)
254 {
255     /* This is the failure function */
256     return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
257 }
258 
259 PVOID
260 KiSystemCallHandler(
261     VOID)
262 {
263     PKTRAP_FRAME TrapFrame;
264     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
265     PKTHREAD Thread;
266     PULONG64 KernelParams, UserParams;
267     ULONG ServiceNumber, Offset, Count;
268     ULONG64 UserRsp;
269 
270     /* Get a pointer to the trap frame */
271     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
272 
273     /* Increase system call count */
274     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
275 
276     /* Get the current thread */
277     Thread = KeGetCurrentThread();
278 
279     /* Set previous mode */
280     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
281 
282     /* Save the old trap frame and set the new */
283     TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
284     Thread->TrapFrame = TrapFrame;
285 
286     /* We don't have an exception frame yet */
287     TrapFrame->ExceptionFrame = 0;
288 
289     /* Before enabling interrupts get the user rsp from the KPCR */
290     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
291     TrapFrame->Rsp = UserRsp;
292 
293     /* Enable interrupts */
294     _enable();
295 
296     /* If the usermode rsp was not a usermode address, prepare an exception */
297     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
298 
299     /* Get the address of the usermode and kernelmode parameters */
300     UserParams = (PULONG64)UserRsp + 1;
301     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
302 
303     /* Get the system call number from the trap frame and decode it */
304     ServiceNumber = (ULONG)TrapFrame->Rax;
305     Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
306     ServiceNumber &= SERVICE_NUMBER_MASK;
307 
308     /* Check for win32k system calls */
309     if (Offset & SERVICE_TABLE_TEST)
310     {
311         ULONG GdiBatchCount;
312 
313         /* Read the GDI batch count from the TEB */
314         _SEH2_TRY
315         {
316             GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
317         }
318         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
319         {
320             GdiBatchCount = 0;
321         }
322         _SEH2_END;
323 
324         /* Flush batch, if there are entries */
325         if (GdiBatchCount != 0)
326         {
327             KeGdiFlushUserBatch();
328         }
329     }
330 
331     /* Get descriptor table */
332     DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
333 
334     /* Validate the system call number */
335     if (ServiceNumber >= DescriptorTable->Limit)
336     {
337         /* Check if this is a GUI call */
338         if (!(Offset & SERVICE_TABLE_TEST))
339         {
340             /* Fail the call */
341             TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
342             return (PVOID)NtSyscallFailure;
343         }
344 
345         /* Convert us to a GUI thread
346            To be entirely correct. we return KiConvertToGuiThread,
347            which allocates a new stack, switches to it, calls
348            PsConvertToGuiThread and resumes in the middle of
349            KiSystemCallEntry64 to restart the system call handling. */
350         return (PVOID)KiConvertToGuiThread;
351     }
352 
353     /* Get stack bytes and calculate argument count */
354     Count = DescriptorTable->Number[ServiceNumber] / 8;
355 
356     _SEH2_TRY
357     {
358         switch (Count)
359         {
360             case 16: KernelParams[15] = UserParams[15];
361             case 15: KernelParams[14] = UserParams[14];
362             case 14: KernelParams[13] = UserParams[13];
363             case 13: KernelParams[12] = UserParams[12];
364             case 12: KernelParams[11] = UserParams[11];
365             case 11: KernelParams[10] = UserParams[10];
366             case 10: KernelParams[9] = UserParams[9];
367             case 9: KernelParams[8] = UserParams[8];
368             case 8: KernelParams[7] = UserParams[7];
369             case 7: KernelParams[6] = UserParams[6];
370             case 6: KernelParams[5] = UserParams[5];
371             case 5: KernelParams[4] = UserParams[4];
372             case 4:
373             case 3:
374             case 2:
375             case 1:
376             case 0:
377                 break;
378 
379             default:
380                 ASSERT(FALSE);
381                 break;
382         }
383     }
384     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
385     {
386         TrapFrame->Rax = _SEH2_GetExceptionCode();
387         return (PVOID)NtSyscallFailure;
388     }
389     _SEH2_END;
390 
391     return (PVOID)DescriptorTable->Base[ServiceNumber];
392 }
393 
394 
395 // FIXME: we need to
396 VOID
397 KiSystemService(IN PKTHREAD Thread,
398                 IN PKTRAP_FRAME TrapFrame,
399                 IN ULONG Instruction)
400 {
401     UNIMPLEMENTED;
402     __debugbreak();
403 }
404 
405 NTSTATUS
406 NTAPI
407 NtSetLdtEntries
408 (ULONG Selector1, LDT_ENTRY LdtEntry1, ULONG Selector2, LDT_ENTRY LdtEntry2)
409 {
410     UNIMPLEMENTED;
411     __debugbreak();
412     return STATUS_UNSUCCESSFUL;
413 }
414 
415 NTSTATUS
416 NTAPI
417 NtVdmControl(IN ULONG ControlCode,
418              IN PVOID ControlData)
419 {
420     /* Not supported */
421     return STATUS_NOT_IMPLEMENTED;
422 }
423 
424 
425