mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 18:35:32 +00:00
454 lines
12 KiB
C
454 lines
12 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* PURPOSE: stubs
|
|
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
VOID
|
|
KiRetireDpcListInDpcStack(
|
|
PKPRCB Prcb,
|
|
PVOID DpcStack);
|
|
|
|
VOID
|
|
NTAPI
|
|
KiDpcInterruptHandler(VOID)
|
|
{
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
PKTHREAD NewThread, OldThread;
|
|
KIRQL OldIrql;
|
|
|
|
/* Raise to DISPATCH_LEVEL */
|
|
OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
|
|
|
|
/* Send an EOI */
|
|
KiSendEOI();
|
|
|
|
/* Check for pending timers, pending DPCs, or pending ready threads */
|
|
if ((Prcb->DpcData[0].DpcQueueDepth) ||
|
|
(Prcb->TimerRequest) ||
|
|
(Prcb->DeferredReadyListHead.Next))
|
|
{
|
|
/* Retire DPCs while under the DPC stack */
|
|
KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
|
|
}
|
|
|
|
/* Enable interrupts */
|
|
_enable();
|
|
|
|
/* Check for quantum end */
|
|
if (Prcb->QuantumEnd)
|
|
{
|
|
/* Handle quantum end */
|
|
Prcb->QuantumEnd = FALSE;
|
|
KiQuantumEnd();
|
|
}
|
|
else if (Prcb->NextThread)
|
|
{
|
|
/* Capture current thread data */
|
|
OldThread = Prcb->CurrentThread;
|
|
NewThread = Prcb->NextThread;
|
|
|
|
/* Set new thread data */
|
|
Prcb->NextThread = NULL;
|
|
Prcb->CurrentThread = NewThread;
|
|
|
|
/* The thread is now running */
|
|
NewThread->State = Running;
|
|
OldThread->WaitReason = WrDispatchInt;
|
|
|
|
/* Make the old thread ready */
|
|
KxQueueReadyThread(OldThread, Prcb);
|
|
|
|
/* Swap to the new thread */
|
|
KiSwapContext(APC_LEVEL, OldThread);
|
|
}
|
|
|
|
/* Go back to old irql and disable interrupts */
|
|
KeLowerIrql(OldIrql);
|
|
_disable();
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
KeZeroPages(IN PVOID Address,
|
|
IN ULONG Size)
|
|
{
|
|
/* Not using XMMI in this routine */
|
|
RtlZeroMemory(Address, Size);
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
KeSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
|
|
{
|
|
UNIMPLEMENTED;
|
|
__debugbreak();
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
KeUserModeCallback(IN ULONG RoutineIndex,
|
|
IN PVOID Argument,
|
|
IN ULONG ArgumentLength,
|
|
OUT PVOID *Result,
|
|
OUT PULONG ResultLength)
|
|
{
|
|
UNIMPLEMENTED;
|
|
__debugbreak();
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
KiIdleLoop(VOID)
|
|
{
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
PKTHREAD OldThread, NewThread;
|
|
|
|
/* Now loop forever */
|
|
while (TRUE)
|
|
{
|
|
/* Start of the idle loop: disable interrupts */
|
|
_enable();
|
|
YieldProcessor();
|
|
YieldProcessor();
|
|
_disable();
|
|
|
|
/* Check for pending timers, pending DPCs, or pending ready threads */
|
|
if ((Prcb->DpcData[0].DpcQueueDepth) ||
|
|
(Prcb->TimerRequest) ||
|
|
(Prcb->DeferredReadyListHead.Next))
|
|
{
|
|
/* Quiesce the DPC software interrupt */
|
|
HalClearSoftwareInterrupt(DISPATCH_LEVEL);
|
|
|
|
/* Handle it */
|
|
KiRetireDpcList(Prcb);
|
|
}
|
|
|
|
/* Check if a new thread is scheduled for execution */
|
|
if (Prcb->NextThread)
|
|
{
|
|
/* Enable interrupts */
|
|
_enable();
|
|
|
|
/* Capture current thread data */
|
|
OldThread = Prcb->CurrentThread;
|
|
NewThread = Prcb->NextThread;
|
|
|
|
/* Set new thread data */
|
|
Prcb->NextThread = NULL;
|
|
Prcb->CurrentThread = NewThread;
|
|
|
|
/* The thread is now running */
|
|
NewThread->State = Running;
|
|
|
|
/* Do the swap at SYNCH_LEVEL */
|
|
KfRaiseIrql(SYNCH_LEVEL);
|
|
|
|
/* Switch away from the idle thread */
|
|
KiSwapContext(APC_LEVEL, OldThread);
|
|
|
|
/* Go back to DISPATCH_LEVEL */
|
|
KeLowerIrql(DISPATCH_LEVEL);
|
|
}
|
|
else
|
|
{
|
|
/* Continue staying idle. Note the HAL returns with interrupts on */
|
|
Prcb->PowerState.IdleFunction(&Prcb->PowerState);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*! \name KiInitializeUserApc
|
|
*
|
|
* \brief
|
|
* Prepares the current trap frame (which must have come from user mode)
|
|
* with the ntdll.KiUserApcDispatcher entrypoint, copying a CONTEXT
|
|
* record with the context from the old trap frame to the threads user
|
|
* mode stack.
|
|
*
|
|
* \param ExceptionFrame
|
|
* \param TrapFrame
|
|
* \param NormalRoutine
|
|
* \param NormalContext
|
|
* \param SystemArgument1
|
|
* \param SystemArgument2
|
|
*
|
|
* \remarks
|
|
* This function is called from KiDeliverApc, when the trap frame came
|
|
* from user mode. This happens before a systemcall or interrupt exits back
|
|
* to usermode or when a thread is started from PspUserThreadstartup.
|
|
* The trap exit code will then leave to KiUserApcDispatcher which in turn
|
|
* calls the NormalRoutine, passing NormalContext, SystemArgument1 and
|
|
* SystemArgument2 as parameters. When that function returns, it calls
|
|
* NtContinue to return back to the kernel, where the old context that was
|
|
* saved on the usermode stack is restored and execution is transferred
|
|
* back to usermode, where the original trap originated from.
|
|
*
|
|
*--*/
|
|
VOID
|
|
NTAPI
|
|
KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
|
|
IN PKTRAP_FRAME TrapFrame,
|
|
IN PKNORMAL_ROUTINE NormalRoutine,
|
|
IN PVOID NormalContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2)
|
|
{
|
|
CONTEXT Context;
|
|
ULONG64 AlignedRsp, Stack;
|
|
EXCEPTION_RECORD SehExceptRecord;
|
|
|
|
/* Sanity check, that the trap frame is from user mode */
|
|
ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
|
|
|
|
/* Convert the current trap frame to a context */
|
|
Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
|
|
KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
|
|
|
|
/* We jump to KiUserApcDispatcher in ntdll */
|
|
TrapFrame->Rip = (ULONG64)KeUserApcDispatcher;
|
|
|
|
/* Setup Ring 3 segments */
|
|
TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
|
|
TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK;
|
|
TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK;
|
|
TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
|
|
TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK;
|
|
TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK;
|
|
|
|
/* Sanitize EFLAGS, enable interrupts */
|
|
TrapFrame->EFlags = (Context.EFlags & EFLAGS_USER_SANITIZE);
|
|
TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
|
|
|
|
/* Set parameters for KiUserApcDispatcher */
|
|
Context.P1Home = (ULONG64)NormalContext;
|
|
Context.P2Home = (ULONG64)SystemArgument1;
|
|
Context.P3Home = (ULONG64)SystemArgument2;
|
|
Context.P4Home = (ULONG64)NormalRoutine;
|
|
|
|
/* Check if thread has IOPL and force it enabled if so */
|
|
//if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
|
|
|
|
/* Align Stack to 16 bytes and allocate space */
|
|
AlignedRsp = Context.Rsp & ~15;
|
|
Stack = AlignedRsp - sizeof(CONTEXT);
|
|
TrapFrame->Rsp = Stack;
|
|
|
|
/* The stack must be 16 byte aligned for KiUserApcDispatcher */
|
|
ASSERT((Stack & 15) == 0);
|
|
|
|
/* Protect with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the stack */
|
|
ProbeForWrite((PCONTEXT)Stack, sizeof(CONTEXT), 8);
|
|
|
|
/* Copy the context */
|
|
RtlCopyMemory((PCONTEXT)Stack, &Context, sizeof(CONTEXT));
|
|
}
|
|
_SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
|
|
{
|
|
/* Dispatch the exception */
|
|
SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Rip;
|
|
KiDispatchException(&SehExceptRecord,
|
|
ExceptionFrame,
|
|
TrapFrame,
|
|
UserMode,
|
|
TRUE);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KiSwapProcess(IN PKPROCESS NewProcess,
|
|
IN PKPROCESS OldProcess)
|
|
{
|
|
PKIPCR Pcr = (PKIPCR)KeGetPcr();
|
|
#ifdef CONFIG_SMP
|
|
LONG SetMember;
|
|
|
|
/* Update active processor mask */
|
|
SetMember = (LONG)Pcr->SetMember;
|
|
InterlockedXor((PLONG)&NewProcess->ActiveProcessors, SetMember);
|
|
InterlockedXor((PLONG)&OldProcess->ActiveProcessors, SetMember);
|
|
#endif
|
|
|
|
/* Update CR3 */
|
|
__writecr3(NewProcess->DirectoryTableBase[0]);
|
|
|
|
/* Update IOPM offset */
|
|
Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
|
|
}
|
|
|
|
#define MAX_SYSCALL_PARAMS 16
|
|
|
|
NTSTATUS
|
|
NtSyscallFailure(void)
|
|
{
|
|
/* This is the failure function */
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
PVOID
|
|
KiSystemCallHandler(
|
|
IN PKTRAP_FRAME TrapFrame,
|
|
IN ULONG64 P2,
|
|
IN ULONG64 P3,
|
|
IN ULONG64 P4)
|
|
{
|
|
PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
|
|
PKTHREAD Thread;
|
|
PULONG64 KernelParams, UserParams;
|
|
ULONG ServiceNumber, Offset, Count;
|
|
ULONG64 UserRsp;
|
|
|
|
DPRINT("Syscall #%ld\n", TrapFrame->Rax);
|
|
//__debugbreak();
|
|
|
|
/* Increase system call count */
|
|
__addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
|
|
|
|
/* Get the current thread */
|
|
Thread = KeGetCurrentThread();
|
|
|
|
/* Set previous mode */
|
|
Thread->PreviousMode = TrapFrame->PreviousMode = UserMode;
|
|
|
|
/* Save the old trap frame and set the new */
|
|
TrapFrame->TrapFrame = (ULONG64)Thread->TrapFrame;
|
|
Thread->TrapFrame = TrapFrame;
|
|
|
|
/* Before enabling interrupts get the user rsp from the KPCR */
|
|
UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp));
|
|
TrapFrame->Rsp = UserRsp;
|
|
|
|
/* Enable interrupts */
|
|
_enable();
|
|
|
|
/* If the usermode rsp was not a usermode address, prepare an exception */
|
|
if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress;
|
|
|
|
/* Get the address of the usermode and kernelmode parameters */
|
|
UserParams = (PULONG64)UserRsp + 1;
|
|
KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS;
|
|
|
|
/* Get the system call number from the trap frame and decode it */
|
|
ServiceNumber = (ULONG)TrapFrame->Rax;
|
|
Offset = (ServiceNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
|
|
ServiceNumber &= SERVICE_NUMBER_MASK;
|
|
|
|
/* Get descriptor table */
|
|
DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
|
|
|
|
/* Get stack bytes and calculate argument count */
|
|
Count = DescriptorTable->Number[ServiceNumber] / 8;
|
|
|
|
__try
|
|
{
|
|
switch (Count)
|
|
{
|
|
case 16: KernelParams[15] = UserParams[15];
|
|
case 15: KernelParams[14] = UserParams[14];
|
|
case 14: KernelParams[13] = UserParams[13];
|
|
case 13: KernelParams[12] = UserParams[12];
|
|
case 12: KernelParams[11] = UserParams[11];
|
|
case 11: KernelParams[10] = UserParams[10];
|
|
case 10: KernelParams[9] = UserParams[9];
|
|
case 9: KernelParams[8] = UserParams[8];
|
|
case 8: KernelParams[7] = UserParams[7];
|
|
case 7: KernelParams[6] = UserParams[6];
|
|
case 6: KernelParams[5] = UserParams[5];
|
|
case 5: KernelParams[4] = UserParams[4];
|
|
case 4: KernelParams[3] = P4;
|
|
case 3: KernelParams[2] = P3;
|
|
case 2: KernelParams[1] = P2;
|
|
case 1: KernelParams[0] = TrapFrame->R10;
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
__debugbreak();
|
|
break;
|
|
}
|
|
}
|
|
__except(1)
|
|
{
|
|
TrapFrame->Rax = _SEH2_GetExceptionCode();
|
|
return (PVOID)NtSyscallFailure;
|
|
}
|
|
|
|
|
|
return (PVOID)DescriptorTable->Base[ServiceNumber];
|
|
}
|
|
|
|
|
|
// FIXME: we need to
|
|
VOID
|
|
KiSystemService(IN PKTHREAD Thread,
|
|
IN PKTRAP_FRAME TrapFrame,
|
|
IN ULONG Instruction)
|
|
{
|
|
UNIMPLEMENTED;
|
|
__debugbreak();
|
|
}
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCallbackReturn
|
|
( IN PVOID Result OPTIONAL, IN ULONG ResultLength, IN NTSTATUS Status )
|
|
{
|
|
UNIMPLEMENTED;
|
|
__debugbreak();
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetLdtEntries
|
|
(ULONG Selector1, LDT_ENTRY LdtEntry1, ULONG Selector2, LDT_ENTRY LdtEntry2)
|
|
{
|
|
UNIMPLEMENTED;
|
|
__debugbreak();
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtVdmControl(IN ULONG ControlCode,
|
|
IN PVOID ControlData)
|
|
{
|
|
/* Not supported */
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
KiCallUserMode(
|
|
IN PVOID *OutputBuffer,
|
|
IN PULONG OutputLength)
|
|
{
|
|
UNIMPLEMENTED;
|
|
__debugbreak();
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ULONG ProcessCount;
|
|
BOOLEAN CcPfEnablePrefetcher;
|
|
|
|
|