xref: /reactos/ntoskrnl/ke/amd64/stubs.c (revision aaa86d07)
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 
171 NTSTATUS
172 NTAPI
173 KeUserModeCallback(IN ULONG RoutineIndex,
174                    IN PVOID Argument,
175                    IN ULONG ArgumentLength,
176                    OUT PVOID *Result,
177                    OUT PULONG ResultLength)
178 {
179     UNIMPLEMENTED;
180     __debugbreak();
181     return STATUS_UNSUCCESSFUL;
182 }
183 
184 VOID
185 FASTCALL
186 KiIdleLoop(VOID)
187 {
188     PKPRCB Prcb = KeGetCurrentPrcb();
189     PKTHREAD OldThread, NewThread;
190 
191     /* Now loop forever */
192     while (TRUE)
193     {
194         /* Start of the idle loop: disable interrupts */
195         _enable();
196         YieldProcessor();
197         YieldProcessor();
198         _disable();
199 
200         /* Check for pending timers, pending DPCs, or pending ready threads */
201         if ((Prcb->DpcData[0].DpcQueueDepth) ||
202             (Prcb->TimerRequest) ||
203             (Prcb->DeferredReadyListHead.Next))
204         {
205             /* Quiesce the DPC software interrupt */
206             HalClearSoftwareInterrupt(DISPATCH_LEVEL);
207 
208             /* Handle it */
209             KiRetireDpcList(Prcb);
210         }
211 
212         /* Check if a new thread is scheduled for execution */
213         if (Prcb->NextThread)
214         {
215             /* Enable interrupts */
216             _enable();
217 
218             /* Capture current thread data */
219             OldThread = Prcb->CurrentThread;
220             NewThread = Prcb->NextThread;
221 
222             /* Set new thread data */
223             Prcb->NextThread = NULL;
224             Prcb->CurrentThread = NewThread;
225 
226             /* The thread is now running */
227             NewThread->State = Running;
228 
229             /* Do the swap at SYNCH_LEVEL */
230             KfRaiseIrql(SYNCH_LEVEL);
231 
232             /* Switch away from the idle thread */
233             KiSwapContext(APC_LEVEL, OldThread);
234 
235             /* Go back to DISPATCH_LEVEL */
236             KeLowerIrql(DISPATCH_LEVEL);
237         }
238         else
239         {
240             /* Continue staying idle. Note the HAL returns with interrupts on */
241             Prcb->PowerState.IdleFunction(&Prcb->PowerState);
242         }
243     }
244 }
245 
246 VOID
247 NTAPI
248 KiSwapProcess(IN PKPROCESS NewProcess,
249               IN PKPROCESS OldProcess)
250 {
251     PKIPCR Pcr = (PKIPCR)KeGetPcr();
252 
253 #ifdef CONFIG_SMP
254     /* Update active processor mask */
255     InterlockedXor64((PLONG64)&NewProcess->ActiveProcessors, Pcr->Prcb.SetMember);
256     InterlockedXor64((PLONG64)&OldProcess->ActiveProcessors, Pcr->Prcb.SetMember);
257 #endif
258 
259     /* Update CR3 */
260     __writecr3(NewProcess->DirectoryTableBase[0]);
261 
262     /* Update IOPM offset */
263     Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
264 }
265 
266 #define MAX_SYSCALL_PARAMS 16
267 
268 NTSTATUS
269 NtSyscallFailure(void)
270 {
271     /* This is the failure function */
272     return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
273 }
274 
275 PVOID
276 KiSystemCallHandler(
277     _In_ ULONG64 ReturnAddress,
278     _In_ ULONG64 P2,
279     _In_ ULONG64 P3,
280     _In_ ULONG64 P4)
281 {
282     PKTRAP_FRAME TrapFrame;
283     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
284     PKTHREAD Thread;
285     PULONG64 KernelParams, UserParams;
286     ULONG ServiceNumber, Offset, Count;
287     ULONG64 UserRsp;
288 
289     /* Get a pointer to the trap frame */
290     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
291 
292     /* Save some values in the trap frame */
293     TrapFrame->Rip = ReturnAddress;
294     TrapFrame->Rdx = P2;
295     TrapFrame->R8 = P3;
296     TrapFrame->R9 = P4;
297 
298     /* Increase system call count */
299     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
300 
301     /* Get the current thread */
302     Thread = KeGetCurrentThread();
303 
304     /* Set previous mode */
305     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
306 
307     /* Save the old trap frame and set the new */
308     TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
309     Thread->TrapFrame = TrapFrame;
310 
311     /* We don't have an exception frame yet */
312     TrapFrame->ExceptionFrame = 0;
313 
314     /* Before enabling interrupts get the user rsp from the KPCR */
315     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
316     TrapFrame->Rsp = UserRsp;
317 
318     /* Enable interrupts */
319     _enable();
320 
321     /* If the usermode rsp was not a usermode address, prepare an exception */
322     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
323 
324     /* Get the address of the usermode and kernelmode parameters */
325     UserParams = (PULONG64)UserRsp + 1;
326     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
327 
328     /* Get the system call number from the trap frame and decode it */
329     ServiceNumber = (ULONG)TrapFrame->Rax;
330     Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
331     ServiceNumber &= SERVICE_NUMBER_MASK;
332 
333     /* Check for win32k system calls */
334     if (Offset & SERVICE_TABLE_TEST)
335     {
336         ULONG GdiBatchCount;
337 
338         /* Read the GDI batch count from the TEB */
339         _SEH2_TRY
340         {
341             GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
342         }
343         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
344         {
345             GdiBatchCount = 0;
346         }
347 
348         /* Flush batch, if there are entries */
349         if (GdiBatchCount != 0)
350         {
351             KeGdiFlushUserBatch();
352         }
353     }
354 
355     /* Get descriptor table */
356     DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
357 
358     /* Validate the system call number */
359     if (ServiceNumber >= DescriptorTable->Limit)
360     {
361         /* Check if this is a GUI call */
362         if (!(Offset & SERVICE_TABLE_TEST))
363         {
364             /* Fail the call */
365             TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
366             return (PVOID)NtSyscallFailure;
367         }
368 
369         /* Convert us to a GUI thread
370            To be entirely correct. we return KiConvertToGuiThread,
371            which allocates a new stack, switches to it, calls
372            PsConvertToGuiThread and resumes in the middle of
373            KiSystemCallEntry64 to restart the system call handling. */
374         return (PVOID)KiConvertToGuiThread;
375     }
376 
377     /* Get stack bytes and calculate argument count */
378     Count = DescriptorTable->Number[ServiceNumber] / 8;
379 
380     __try
381     {
382         switch (Count)
383         {
384             case 16: KernelParams[15] = UserParams[15];
385             case 15: KernelParams[14] = UserParams[14];
386             case 14: KernelParams[13] = UserParams[13];
387             case 13: KernelParams[12] = UserParams[12];
388             case 12: KernelParams[11] = UserParams[11];
389             case 11: KernelParams[10] = UserParams[10];
390             case 10: KernelParams[9] = UserParams[9];
391             case 9: KernelParams[8] = UserParams[8];
392             case 8: KernelParams[7] = UserParams[7];
393             case 7: KernelParams[6] = UserParams[6];
394             case 6: KernelParams[5] = UserParams[5];
395             case 5: KernelParams[4] = UserParams[4];
396             case 4:
397             case 3:
398             case 2:
399             case 1:
400             case 0:
401                 break;
402 
403             default:
404                 __debugbreak();
405                 break;
406         }
407     }
408     __except(1)
409     {
410         TrapFrame->Rax = _SEH2_GetExceptionCode();
411         return (PVOID)NtSyscallFailure;
412     }
413 
414 
415     return (PVOID)DescriptorTable->Base[ServiceNumber];
416 }
417 
418 
419 // FIXME: we need to
420 VOID
421 KiSystemService(IN PKTHREAD Thread,
422                 IN PKTRAP_FRAME TrapFrame,
423                 IN ULONG Instruction)
424 {
425     UNIMPLEMENTED;
426     __debugbreak();
427 }
428 
429 NTSYSAPI
430 NTSTATUS
431 NTAPI
432 NtCallbackReturn
433 ( IN PVOID Result OPTIONAL, IN ULONG ResultLength, IN NTSTATUS Status )
434 {
435     UNIMPLEMENTED;
436     __debugbreak();
437     return STATUS_UNSUCCESSFUL;
438 }
439 
440 NTSTATUS
441 NTAPI
442 NtSetLdtEntries
443 (ULONG Selector1, LDT_ENTRY LdtEntry1, ULONG Selector2, LDT_ENTRY LdtEntry2)
444 {
445     UNIMPLEMENTED;
446     __debugbreak();
447     return STATUS_UNSUCCESSFUL;
448 }
449 
450 NTSTATUS
451 NTAPI
452 NtVdmControl(IN ULONG ControlCode,
453              IN PVOID ControlData)
454 {
455     /* Not supported */
456     return STATUS_NOT_IMPLEMENTED;
457 }
458 
459 NTSTATUS
460 NTAPI
461 KiCallUserMode(
462     IN PVOID *OutputBuffer,
463     IN PULONG OutputLength)
464 {
465     UNIMPLEMENTED;
466     __debugbreak();
467     return STATUS_UNSUCCESSFUL;
468 }
469 
470 ULONG ProcessCount;
471 BOOLEAN CcPfEnablePrefetcher;
472 
473 
474