xref: /reactos/ntoskrnl/ke/amd64/stubs.c (revision 34593d93)
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     VOID)
264 {
265     PKTRAP_FRAME TrapFrame;
266     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
267     PKTHREAD Thread;
268     PULONG64 KernelParams, UserParams;
269     ULONG ServiceNumber, Offset, Count;
270     ULONG64 UserRsp;
271 
272     /* Get a pointer to the trap frame */
273     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
274 
275     /* Increase system call count */
276     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
277 
278     /* Get the current thread */
279     Thread = KeGetCurrentThread();
280 
281     /* Set previous mode */
282     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
283 
284     /* Save the old trap frame and set the new */
285     TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
286     Thread->TrapFrame = TrapFrame;
287 
288     /* We don't have an exception frame yet */
289     TrapFrame->ExceptionFrame = 0;
290 
291     /* Before enabling interrupts get the user rsp from the KPCR */
292     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
293     TrapFrame->Rsp = UserRsp;
294 
295     /* Enable interrupts */
296     _enable();
297 
298     /* If the usermode rsp was not a usermode address, prepare an exception */
299     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
300 
301     /* Get the address of the usermode and kernelmode parameters */
302     UserParams = (PULONG64)UserRsp + 1;
303     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
304 
305     /* Get the system call number from the trap frame and decode it */
306     ServiceNumber = (ULONG)TrapFrame->Rax;
307     Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
308     ServiceNumber &= SERVICE_NUMBER_MASK;
309 
310     /* Check for win32k system calls */
311     if (Offset & SERVICE_TABLE_TEST)
312     {
313         ULONG GdiBatchCount;
314 
315         /* Read the GDI batch count from the TEB */
316         _SEH2_TRY
317         {
318             GdiBatchCount = NtCurrentTeb()->GdiBatchCount;
319         }
320         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
321         {
322             GdiBatchCount = 0;
323         }
324         _SEH2_END;
325 
326         /* Flush batch, if there are entries */
327         if (GdiBatchCount != 0)
328         {
329             KeGdiFlushUserBatch();
330         }
331     }
332 
333     /* Get descriptor table */
334     DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
335 
336     /* Validate the system call number */
337     if (ServiceNumber >= DescriptorTable->Limit)
338     {
339         /* Check if this is a GUI call */
340         if (!(Offset & SERVICE_TABLE_TEST))
341         {
342             /* Fail the call */
343             TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
344             return (PVOID)NtSyscallFailure;
345         }
346 
347         /* Convert us to a GUI thread
348            To be entirely correct. we return KiConvertToGuiThread,
349            which allocates a new stack, switches to it, calls
350            PsConvertToGuiThread and resumes in the middle of
351            KiSystemCallEntry64 to restart the system call handling. */
352         return (PVOID)KiConvertToGuiThread;
353     }
354 
355     /* Get stack bytes and calculate argument count */
356     Count = DescriptorTable->Number[ServiceNumber] / 8;
357 
358     _SEH2_TRY
359     {
360         switch (Count)
361         {
362             case 16: KernelParams[15] = UserParams[15];
363             case 15: KernelParams[14] = UserParams[14];
364             case 14: KernelParams[13] = UserParams[13];
365             case 13: KernelParams[12] = UserParams[12];
366             case 12: KernelParams[11] = UserParams[11];
367             case 11: KernelParams[10] = UserParams[10];
368             case 10: KernelParams[9] = UserParams[9];
369             case 9: KernelParams[8] = UserParams[8];
370             case 8: KernelParams[7] = UserParams[7];
371             case 7: KernelParams[6] = UserParams[6];
372             case 6: KernelParams[5] = UserParams[5];
373             case 5: KernelParams[4] = UserParams[4];
374             case 4:
375             case 3:
376             case 2:
377             case 1:
378             case 0:
379                 break;
380 
381             default:
382                 ASSERT(FALSE);
383                 break;
384         }
385     }
386     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
387     {
388         TrapFrame->Rax = _SEH2_GetExceptionCode();
389         return (PVOID)NtSyscallFailure;
390     }
391     _SEH2_END;
392 
393     return (PVOID)DescriptorTable->Base[ServiceNumber];
394 }
395 
396 
397 // FIXME: we need to
398 VOID
399 KiSystemService(IN PKTHREAD Thread,
400                 IN PKTRAP_FRAME TrapFrame,
401                 IN ULONG Instruction)
402 {
403     UNIMPLEMENTED;
404     __debugbreak();
405 }
406 
407 NTSTATUS
408 NTAPI
409 NtSetLdtEntries
410 (ULONG Selector1, LDT_ENTRY LdtEntry1, ULONG Selector2, LDT_ENTRY LdtEntry2)
411 {
412     UNIMPLEMENTED;
413     __debugbreak();
414     return STATUS_UNSUCCESSFUL;
415 }
416 
417 NTSTATUS
418 NTAPI
419 NtVdmControl(IN ULONG ControlCode,
420              IN PVOID ControlData)
421 {
422     /* Not supported */
423     return STATUS_NOT_IMPLEMENTED;
424 }
425 
426 
427