xref: /reactos/ntoskrnl/ke/amd64/stubs.c (revision c86c55ac)
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 
247 /*! \name KiInitializeUserApc
248  *
249  *  \brief
250  *      Prepares the current trap frame (which must have come from user mode)
251  *      with the ntdll.KiUserApcDispatcher entrypoint, copying a CONTEXT
252  *      record with the context from the old trap frame to the threads user
253  *      mode stack.
254  *
255  *  \param ExceptionFrame
256  *  \param TrapFrame
257  *  \param NormalRoutine
258  *  \param NormalContext
259  *  \param SystemArgument1
260  *  \param SystemArgument2
261  *
262  *  \remarks
263  *      This function is called from KiDeliverApc, when the trap frame came
264  *      from user mode. This happens before a systemcall or interrupt exits back
265  *      to usermode or when a thread is started from PspUserThreadstartup.
266  *      The trap exit code will then leave to KiUserApcDispatcher which in turn
267  *      calls the NormalRoutine, passing NormalContext, SystemArgument1 and
268  *      SystemArgument2 as parameters. When that function returns, it calls
269  *      NtContinue to return back to the kernel, where the old context that was
270  *      saved on the usermode stack is restored and execution is transferred
271  *      back to usermode, where the original trap originated from.
272  *
273  *--*/
274 VOID
275 NTAPI
276 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
277                     IN PKTRAP_FRAME TrapFrame,
278                     IN PKNORMAL_ROUTINE NormalRoutine,
279                     IN PVOID NormalContext,
280                     IN PVOID SystemArgument1,
281                     IN PVOID SystemArgument2)
282 {
283     CONTEXT Context = { 0 };
284     ULONG64 AlignedRsp, Stack;
285     EXCEPTION_RECORD SehExceptRecord;
286 
287     /* Sanity check, that the trap frame is from user mode */
288     ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
289 
290     /* Convert the current trap frame to a context */
291     Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
292     KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
293 
294     /* We jump to KiUserApcDispatcher in ntdll */
295     TrapFrame->Rip = (ULONG64)KeUserApcDispatcher;
296 
297     /* Setup Ring 3 segments */
298     TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
299     TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK;
300     TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK;
301     TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
302     TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK;
303     TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK;
304 
305     /* Sanitize EFLAGS, enable interrupts */
306     TrapFrame->EFlags = (Context.EFlags & EFLAGS_USER_SANITIZE);
307     TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
308 
309     /* Set parameters for KiUserApcDispatcher */
310     Context.P1Home = (ULONG64)NormalContext;
311     Context.P2Home = (ULONG64)SystemArgument1;
312     Context.P3Home = (ULONG64)SystemArgument2;
313     Context.P4Home = (ULONG64)NormalRoutine;
314 
315     /* Check if thread has IOPL and force it enabled if so */
316     //if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
317 
318     /* Align Stack to 16 bytes and allocate space */
319     AlignedRsp = Context.Rsp & ~15;
320     Stack = AlignedRsp - sizeof(CONTEXT);
321     TrapFrame->Rsp = Stack;
322 
323     /* The stack must be 16 byte aligned for KiUserApcDispatcher */
324     ASSERT((Stack & 15) == 0);
325 
326     /* Protect with SEH */
327     _SEH2_TRY
328     {
329          /* Probe the stack */
330         ProbeForWrite((PCONTEXT)Stack,  sizeof(CONTEXT), 8);
331 
332         /* Copy the context */
333         RtlCopyMemory((PCONTEXT)Stack, &Context, sizeof(CONTEXT));
334     }
335     _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
336     {
337         /* Dispatch the exception */
338         SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Rip;
339         KiDispatchException(&SehExceptRecord,
340                             ExceptionFrame,
341                             TrapFrame,
342                             UserMode,
343                             TRUE);
344     }
345     _SEH2_END;
346 }
347 
348 VOID
349 NTAPI
350 KiSwapProcess(IN PKPROCESS NewProcess,
351               IN PKPROCESS OldProcess)
352 {
353     PKIPCR Pcr = (PKIPCR)KeGetPcr();
354 
355 #ifdef CONFIG_SMP
356     /* Update active processor mask */
357     InterlockedXor64((PLONG64)&NewProcess->ActiveProcessors, Pcr->Prcb.SetMember);
358     InterlockedXor64((PLONG64)&OldProcess->ActiveProcessors, Pcr->Prcb.SetMember);
359 #endif
360 
361     /* Update CR3 */
362     __writecr3(NewProcess->DirectoryTableBase[0]);
363 
364     /* Update IOPM offset */
365     Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
366 }
367 
368 #define MAX_SYSCALL_PARAMS 16
369 
370 NTSTATUS
371 NtSyscallFailure(void)
372 {
373     /* This is the failure function */
374     return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
375 }
376 
377 PVOID
378 KiSystemCallHandler(
379     _In_ ULONG64 ReturnAddress,
380     _In_ ULONG64 P2,
381     _In_ ULONG64 P3,
382     _In_ ULONG64 P4)
383 {
384     PKTRAP_FRAME TrapFrame;
385     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
386     PKTHREAD Thread;
387     PULONG64 KernelParams, UserParams;
388     ULONG ServiceNumber, Offset, Count;
389     ULONG64 UserRsp;
390 
391     /* Get a pointer to the trap frame */
392     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
393 
394     /* Save some values in the trap frame */
395     TrapFrame->Rip = ReturnAddress;
396     TrapFrame->Rdx = P2;
397     TrapFrame->R8 = P3;
398     TrapFrame->R9 = P4;
399 
400     /* Increase system call count */
401     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
402 
403     /* Get the current thread */
404     Thread = KeGetCurrentThread();
405 
406     /* Set previous mode */
407     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
408 
409     /* Save the old trap frame and set the new */
410     TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
411     Thread->TrapFrame = TrapFrame;
412 
413     /* We don't have an exception frame yet */
414     TrapFrame->ExceptionFrame = 0;
415 
416     /* Before enabling interrupts get the user rsp from the KPCR */
417     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
418     TrapFrame->Rsp = UserRsp;
419 
420     /* Enable interrupts */
421     _enable();
422 
423     /* If the usermode rsp was not a usermode address, prepare an exception */
424     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
425 
426     /* Get the address of the usermode and kernelmode parameters */
427     UserParams = (PULONG64)UserRsp + 1;
428     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
429 
430     /* Get the system call number from the trap frame and decode it */
431     ServiceNumber = (ULONG)TrapFrame->Rax;
432     Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
433     ServiceNumber &= SERVICE_NUMBER_MASK;
434 
435     /* Get descriptor table */
436     DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
437 
438     /* Validate the system call number */
439     if (ServiceNumber >= DescriptorTable->Limit)
440     {
441         /* Check if this is a GUI call */
442         if (!(Offset & SERVICE_TABLE_TEST))
443         {
444             /* Fail the call */
445             TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
446             return (PVOID)NtSyscallFailure;
447         }
448 
449         /* Convert us to a GUI thread
450            To be entirely correct. we return KiConvertToGuiThread,
451            which allocates a new stack, switches to it, calls
452            PsConvertToGuiThread and resumes in the middle of
453            KiSystemCallEntry64 to restart the system call handling. */
454         return (PVOID)KiConvertToGuiThread;
455     }
456 
457     /* Get stack bytes and calculate argument count */
458     Count = DescriptorTable->Number[ServiceNumber] / 8;
459 
460     __try
461     {
462         switch (Count)
463         {
464             case 16: KernelParams[15] = UserParams[15];
465             case 15: KernelParams[14] = UserParams[14];
466             case 14: KernelParams[13] = UserParams[13];
467             case 13: KernelParams[12] = UserParams[12];
468             case 12: KernelParams[11] = UserParams[11];
469             case 11: KernelParams[10] = UserParams[10];
470             case 10: KernelParams[9] = UserParams[9];
471             case 9: KernelParams[8] = UserParams[8];
472             case 8: KernelParams[7] = UserParams[7];
473             case 7: KernelParams[6] = UserParams[6];
474             case 6: KernelParams[5] = UserParams[5];
475             case 5: KernelParams[4] = UserParams[4];
476             case 4:
477             case 3:
478             case 2:
479             case 1:
480             case 0:
481                 break;
482 
483             default:
484                 __debugbreak();
485                 break;
486         }
487     }
488     __except(1)
489     {
490         TrapFrame->Rax = _SEH2_GetExceptionCode();
491         return (PVOID)NtSyscallFailure;
492     }
493 
494 
495     return (PVOID)DescriptorTable->Base[ServiceNumber];
496 }
497 
498 
499 // FIXME: we need to
500 VOID
501 KiSystemService(IN PKTHREAD Thread,
502                 IN PKTRAP_FRAME TrapFrame,
503                 IN ULONG Instruction)
504 {
505     UNIMPLEMENTED;
506     __debugbreak();
507 }
508 
509 NTSYSAPI
510 NTSTATUS
511 NTAPI
512 NtCallbackReturn
513 ( IN PVOID Result OPTIONAL, IN ULONG ResultLength, IN NTSTATUS Status )
514 {
515     UNIMPLEMENTED;
516     __debugbreak();
517     return STATUS_UNSUCCESSFUL;
518 }
519 
520 NTSTATUS
521 NTAPI
522 NtSetLdtEntries
523 (ULONG Selector1, LDT_ENTRY LdtEntry1, ULONG Selector2, LDT_ENTRY LdtEntry2)
524 {
525     UNIMPLEMENTED;
526     __debugbreak();
527     return STATUS_UNSUCCESSFUL;
528 }
529 
530 NTSTATUS
531 NTAPI
532 NtVdmControl(IN ULONG ControlCode,
533              IN PVOID ControlData)
534 {
535     /* Not supported */
536     return STATUS_NOT_IMPLEMENTED;
537 }
538 
539 NTSTATUS
540 NTAPI
541 KiCallUserMode(
542     IN PVOID *OutputBuffer,
543     IN PULONG OutputLength)
544 {
545     UNIMPLEMENTED;
546     __debugbreak();
547     return STATUS_UNSUCCESSFUL;
548 }
549 
550 ULONG ProcessCount;
551 BOOLEAN CcPfEnablePrefetcher;
552 
553 
554