xref: /reactos/ntoskrnl/ke/amd64/stubs.c (revision 18b1aafd)
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 VOID
17 KiRetireDpcListInDpcStack(
18     PKPRCB Prcb,
19     PVOID DpcStack);
20 
21 NTSTATUS
22 KiConvertToGuiThread(
23     VOID);
24 
25 VOID
26 NTAPI
27 KiDpcInterruptHandler(VOID)
28 {
29     PKPRCB Prcb = KeGetCurrentPrcb();
30     PKTHREAD NewThread, OldThread;
31     KIRQL OldIrql;
32 
33     /* Raise to DISPATCH_LEVEL */
34     OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
35 
36     /* Send an EOI */
37     KiSendEOI();
38 
39     /* Check for pending timers, pending DPCs, or pending ready threads */
40     if ((Prcb->DpcData[0].DpcQueueDepth) ||
41         (Prcb->TimerRequest) ||
42         (Prcb->DeferredReadyListHead.Next))
43     {
44         /* Retire DPCs while under the DPC stack */
45         KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
46     }
47 
48     /* Enable interrupts */
49     _enable();
50 
51     /* Check for quantum end */
52     if (Prcb->QuantumEnd)
53     {
54         /* Handle quantum end */
55         Prcb->QuantumEnd = FALSE;
56         KiQuantumEnd();
57     }
58     else if (Prcb->NextThread)
59     {
60         /* Capture current thread data */
61         OldThread = Prcb->CurrentThread;
62         NewThread = Prcb->NextThread;
63 
64         /* Set new thread data */
65         Prcb->NextThread = NULL;
66         Prcb->CurrentThread = NewThread;
67 
68         /* The thread is now running */
69         NewThread->State = Running;
70         OldThread->WaitReason = WrDispatchInt;
71 
72         /* Make the old thread ready */
73         KxQueueReadyThread(OldThread, Prcb);
74 
75         /* Swap to the new thread */
76         KiSwapContext(APC_LEVEL, OldThread);
77     }
78 
79     /* Disable interrupts and go back to old irql */
80     _disable();
81     KeLowerIrql(OldIrql);
82 }
83 
84 
85 VOID
86 FASTCALL
87 KeZeroPages(IN PVOID Address,
88             IN ULONG Size)
89 {
90     /* Not using XMMI in this routine */
91     RtlZeroMemory(Address, Size);
92 }
93 
94 PVOID
95 KiSwitchKernelStackHelper(
96     LONG_PTR StackOffset,
97     PVOID OldStackBase);
98 
99 PVOID
100 NTAPI
101 KeSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
102 {
103     PKTHREAD CurrentThread;
104     PVOID OldStackBase;
105     LONG_PTR StackOffset;
106     SIZE_T StackSize;
107 
108     /* Get the current thread */
109     CurrentThread = KeGetCurrentThread();
110 
111     /* Save the old stack base */
112     OldStackBase = CurrentThread->StackBase;
113 
114     /* Get the size of the current stack */
115     StackSize = (ULONG_PTR)CurrentThread->StackBase - CurrentThread->StackLimit;
116     ASSERT(StackSize <= (ULONG_PTR)StackBase - (ULONG_PTR)StackLimit);
117 
118     /* Copy the current stack contents to the new stack */
119     RtlCopyMemory((PUCHAR)StackBase - StackSize,
120                   (PVOID)CurrentThread->StackLimit,
121                   StackSize);
122 
123     /* Calculate the offset between the old and the new stack */
124     StackOffset = (PUCHAR)StackBase - (PUCHAR)CurrentThread->StackBase;
125 
126     /* Disable interrupts while messing with the stack */
127     _disable();
128 
129     /* Set the new trap frame */
130     CurrentThread->TrapFrame = (PKTRAP_FRAME)Add2Ptr(CurrentThread->TrapFrame,
131                                                      StackOffset);
132 
133     /* Set the new initial stack */
134     CurrentThread->InitialStack = Add2Ptr(CurrentThread->InitialStack,
135                                           StackOffset);
136 
137     /* Set the new stack limits */
138     CurrentThread->StackBase = StackBase;
139     CurrentThread->StackLimit = (ULONG_PTR)StackLimit;
140     CurrentThread->LargeStack = TRUE;
141 
142     /* Adjust the PCR fields */
143     __addgsqword(FIELD_OFFSET(KPCR, NtTib.StackBase), StackOffset);
144     __addgsqword(FIELD_OFFSET(KIPCR, Prcb.RspBase), StackOffset);
145 
146     /* Finally switch RSP to the new stack.
147        We pass OldStackBase to make sure it is not lost. */
148     OldStackBase = KiSwitchKernelStackHelper(StackOffset, OldStackBase);
149 
150     /* Reenable interrupts */
151     _enable();
152 
153     return OldStackBase;
154 }
155 
156 NTSTATUS
157 NTAPI
158 KeUserModeCallback(IN ULONG RoutineIndex,
159                    IN PVOID Argument,
160                    IN ULONG ArgumentLength,
161                    OUT PVOID *Result,
162                    OUT PULONG ResultLength)
163 {
164     UNIMPLEMENTED;
165     __debugbreak();
166     return STATUS_UNSUCCESSFUL;
167 }
168 
169 VOID
170 FASTCALL
171 KiIdleLoop(VOID)
172 {
173     PKPRCB Prcb = KeGetCurrentPrcb();
174     PKTHREAD OldThread, NewThread;
175 
176     /* Now loop forever */
177     while (TRUE)
178     {
179         /* Start of the idle loop: disable interrupts */
180         _enable();
181         YieldProcessor();
182         YieldProcessor();
183         _disable();
184 
185         /* Check for pending timers, pending DPCs, or pending ready threads */
186         if ((Prcb->DpcData[0].DpcQueueDepth) ||
187             (Prcb->TimerRequest) ||
188             (Prcb->DeferredReadyListHead.Next))
189         {
190             /* Quiesce the DPC software interrupt */
191             HalClearSoftwareInterrupt(DISPATCH_LEVEL);
192 
193             /* Handle it */
194             KiRetireDpcList(Prcb);
195         }
196 
197         /* Check if a new thread is scheduled for execution */
198         if (Prcb->NextThread)
199         {
200             /* Enable interrupts */
201             _enable();
202 
203             /* Capture current thread data */
204             OldThread = Prcb->CurrentThread;
205             NewThread = Prcb->NextThread;
206 
207             /* Set new thread data */
208             Prcb->NextThread = NULL;
209             Prcb->CurrentThread = NewThread;
210 
211             /* The thread is now running */
212             NewThread->State = Running;
213 
214             /* Do the swap at SYNCH_LEVEL */
215             KfRaiseIrql(SYNCH_LEVEL);
216 
217             /* Switch away from the idle thread */
218             KiSwapContext(APC_LEVEL, OldThread);
219 
220             /* Go back to DISPATCH_LEVEL */
221             KeLowerIrql(DISPATCH_LEVEL);
222         }
223         else
224         {
225             /* Continue staying idle. Note the HAL returns with interrupts on */
226             Prcb->PowerState.IdleFunction(&Prcb->PowerState);
227         }
228     }
229 }
230 
231 
232 /*! \name KiInitializeUserApc
233  *
234  *  \brief
235  *      Prepares the current trap frame (which must have come from user mode)
236  *      with the ntdll.KiUserApcDispatcher entrypoint, copying a CONTEXT
237  *      record with the context from the old trap frame to the threads user
238  *      mode stack.
239  *
240  *  \param ExceptionFrame
241  *  \param TrapFrame
242  *  \param NormalRoutine
243  *  \param NormalContext
244  *  \param SystemArgument1
245  *  \param SystemArgument2
246  *
247  *  \remarks
248  *      This function is called from KiDeliverApc, when the trap frame came
249  *      from user mode. This happens before a systemcall or interrupt exits back
250  *      to usermode or when a thread is started from PspUserThreadstartup.
251  *      The trap exit code will then leave to KiUserApcDispatcher which in turn
252  *      calls the NormalRoutine, passing NormalContext, SystemArgument1 and
253  *      SystemArgument2 as parameters. When that function returns, it calls
254  *      NtContinue to return back to the kernel, where the old context that was
255  *      saved on the usermode stack is restored and execution is transferred
256  *      back to usermode, where the original trap originated from.
257  *
258  *--*/
259 VOID
260 NTAPI
261 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
262                     IN PKTRAP_FRAME TrapFrame,
263                     IN PKNORMAL_ROUTINE NormalRoutine,
264                     IN PVOID NormalContext,
265                     IN PVOID SystemArgument1,
266                     IN PVOID SystemArgument2)
267 {
268     CONTEXT Context = { 0 };
269     ULONG64 AlignedRsp, Stack;
270     EXCEPTION_RECORD SehExceptRecord;
271 
272     /* Sanity check, that the trap frame is from user mode */
273     ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
274 
275     /* Convert the current trap frame to a context */
276     Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
277     KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
278 
279     /* We jump to KiUserApcDispatcher in ntdll */
280     TrapFrame->Rip = (ULONG64)KeUserApcDispatcher;
281 
282     /* Setup Ring 3 segments */
283     TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
284     TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK;
285     TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK;
286     TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
287     TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK;
288     TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK;
289 
290     /* Sanitize EFLAGS, enable interrupts */
291     TrapFrame->EFlags = (Context.EFlags & EFLAGS_USER_SANITIZE);
292     TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
293 
294     /* Set parameters for KiUserApcDispatcher */
295     Context.P1Home = (ULONG64)NormalContext;
296     Context.P2Home = (ULONG64)SystemArgument1;
297     Context.P3Home = (ULONG64)SystemArgument2;
298     Context.P4Home = (ULONG64)NormalRoutine;
299 
300     /* Check if thread has IOPL and force it enabled if so */
301     //if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
302 
303     /* Align Stack to 16 bytes and allocate space */
304     AlignedRsp = Context.Rsp & ~15;
305     Stack = AlignedRsp - sizeof(CONTEXT);
306     TrapFrame->Rsp = Stack;
307 
308     /* The stack must be 16 byte aligned for KiUserApcDispatcher */
309     ASSERT((Stack & 15) == 0);
310 
311     /* Protect with SEH */
312     _SEH2_TRY
313     {
314          /* Probe the stack */
315         ProbeForWrite((PCONTEXT)Stack,  sizeof(CONTEXT), 8);
316 
317         /* Copy the context */
318         RtlCopyMemory((PCONTEXT)Stack, &Context, sizeof(CONTEXT));
319     }
320     _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
321     {
322         /* Dispatch the exception */
323         SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Rip;
324         KiDispatchException(&SehExceptRecord,
325                             ExceptionFrame,
326                             TrapFrame,
327                             UserMode,
328                             TRUE);
329     }
330     _SEH2_END;
331 }
332 
333 VOID
334 NTAPI
335 KiSwapProcess(IN PKPROCESS NewProcess,
336               IN PKPROCESS OldProcess)
337 {
338     PKIPCR Pcr = (PKIPCR)KeGetPcr();
339 
340 #ifdef CONFIG_SMP
341     /* Update active processor mask */
342     InterlockedXor64((PLONG64)&NewProcess->ActiveProcessors, Pcr->Prcb.SetMember);
343     InterlockedXor64((PLONG64)&OldProcess->ActiveProcessors, Pcr->Prcb.SetMember);
344 #endif
345 
346     /* Update CR3 */
347     __writecr3(NewProcess->DirectoryTableBase[0]);
348 
349     /* Update IOPM offset */
350     Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
351 }
352 
353 #define MAX_SYSCALL_PARAMS 16
354 
355 NTSTATUS
356 NtSyscallFailure(void)
357 {
358     /* This is the failure function */
359     return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax;
360 }
361 
362 PVOID
363 KiSystemCallHandler(
364     _In_ ULONG64 ReturnAddress,
365     _In_ ULONG64 P2,
366     _In_ ULONG64 P3,
367     _In_ ULONG64 P4)
368 {
369     PKTRAP_FRAME TrapFrame;
370     PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
371     PKTHREAD Thread;
372     PULONG64 KernelParams, UserParams;
373     ULONG ServiceNumber, Offset, Count;
374     ULONG64 UserRsp;
375 
376     /* Get a pointer to the trap frame */
377     TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS);
378 
379     /* Save some values in the trap frame */
380     TrapFrame->Rip = ReturnAddress;
381     TrapFrame->Rdx = P2;
382     TrapFrame->R8 = P3;
383     TrapFrame->R9 = P4;
384 
385     /* Increase system call count */
386     __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
387 
388     /* Get the current thread */
389     Thread = KeGetCurrentThread();
390 
391     /* Set previous mode */
392     Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
393 
394     /* Save the old trap frame and set the new */
395     TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
396     Thread->TrapFrame = TrapFrame;
397 
398     /* We don't have an exception frame yet */
399     TrapFrame->ExceptionFrame = 0;
400 
401     /* Before enabling interrupts get the user rsp from the KPCR */
402     UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
403     TrapFrame->Rsp = UserRsp;
404 
405     /* Enable interrupts */
406     _enable();
407 
408     /* If the usermode rsp was not a usermode address, prepare an exception */
409     if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
410 
411     /* Get the address of the usermode and kernelmode parameters */
412     UserParams = (PULONG64)UserRsp + 1;
413     KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
414 
415     /* Get the system call number from the trap frame and decode it */
416     ServiceNumber = (ULONG)TrapFrame->Rax;
417     Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
418     ServiceNumber &= SERVICE_NUMBER_MASK;
419 
420     /* Get descriptor table */
421     DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
422 
423     /* Validate the system call number */
424     if (ServiceNumber >= DescriptorTable->Limit)
425     {
426         /* Check if this is a GUI call */
427         if (!(Offset & SERVICE_TABLE_TEST))
428         {
429             /* Fail the call */
430             TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
431             return (PVOID)NtSyscallFailure;
432         }
433 
434         /* Convert us to a GUI thread
435            To be entirely correct. we return KiConvertToGuiThread,
436            which allocates a new stack, switches to it, calls
437            PsConvertToGuiThread and resumes in the middle of
438            KiSystemCallEntry64 to restart the system call handling. */
439         return (PVOID)KiConvertToGuiThread;
440     }
441 
442     /* Get stack bytes and calculate argument count */
443     Count = DescriptorTable->Number[ServiceNumber] / 8;
444 
445     __try
446     {
447         switch (Count)
448         {
449             case 16: KernelParams[15] = UserParams[15];
450             case 15: KernelParams[14] = UserParams[14];
451             case 14: KernelParams[13] = UserParams[13];
452             case 13: KernelParams[12] = UserParams[12];
453             case 12: KernelParams[11] = UserParams[11];
454             case 11: KernelParams[10] = UserParams[10];
455             case 10: KernelParams[9] = UserParams[9];
456             case 9: KernelParams[8] = UserParams[8];
457             case 8: KernelParams[7] = UserParams[7];
458             case 7: KernelParams[6] = UserParams[6];
459             case 6: KernelParams[5] = UserParams[5];
460             case 5: KernelParams[4] = UserParams[4];
461             case 4:
462             case 3:
463             case 2:
464             case 1:
465             case 0:
466                 break;
467 
468             default:
469                 __debugbreak();
470                 break;
471         }
472     }
473     __except(1)
474     {
475         TrapFrame->Rax = _SEH2_GetExceptionCode();
476         return (PVOID)NtSyscallFailure;
477     }
478 
479 
480     return (PVOID)DescriptorTable->Base[ServiceNumber];
481 }
482 
483 
484 // FIXME: we need to
485 VOID
486 KiSystemService(IN PKTHREAD Thread,
487                 IN PKTRAP_FRAME TrapFrame,
488                 IN ULONG Instruction)
489 {
490     UNIMPLEMENTED;
491     __debugbreak();
492 }
493 
494 NTSYSAPI
495 NTSTATUS
496 NTAPI
497 NtCallbackReturn
498 ( IN PVOID Result OPTIONAL, IN ULONG ResultLength, IN NTSTATUS Status )
499 {
500     UNIMPLEMENTED;
501     __debugbreak();
502     return STATUS_UNSUCCESSFUL;
503 }
504 
505 NTSTATUS
506 NTAPI
507 NtSetLdtEntries
508 (ULONG Selector1, LDT_ENTRY LdtEntry1, ULONG Selector2, LDT_ENTRY LdtEntry2)
509 {
510     UNIMPLEMENTED;
511     __debugbreak();
512     return STATUS_UNSUCCESSFUL;
513 }
514 
515 NTSTATUS
516 NTAPI
517 NtVdmControl(IN ULONG ControlCode,
518              IN PVOID ControlData)
519 {
520     /* Not supported */
521     return STATUS_NOT_IMPLEMENTED;
522 }
523 
524 NTSTATUS
525 NTAPI
526 KiCallUserMode(
527     IN PVOID *OutputBuffer,
528     IN PULONG OutputLength)
529 {
530     UNIMPLEMENTED;
531     __debugbreak();
532     return STATUS_UNSUCCESSFUL;
533 }
534 
535 ULONG ProcessCount;
536 BOOLEAN CcPfEnablePrefetcher;
537 
538 
539