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