https://git.reactos.org/?p=reactos.git;a=commitdiff;h=fdc1261fb77879cde0afb14912f928a44b5b6305

commit fdc1261fb77879cde0afb14912f928a44b5b6305
Author:     Timo Kreuzer <[email protected]>
AuthorDate: Mon Feb 5 01:26:11 2018 +0100
Commit:     Timo Kreuzer <[email protected]>
CommitDate: Sat Oct 31 14:23:16 2020 +0100

    [NTOS:KE:X64][NTDLL:X64] Implement x64 version of user callback code
---
 dll/ntdll/dispatch/amd64/dispatch.S  |  47 +++++-
 dll/ntdll/dispatch/amd64/stubs.c     |  67 --------
 ntoskrnl/include/internal/amd64/ke.h |   7 +
 ntoskrnl/ke/amd64/ctxswitch.S        |  62 +-------
 ntoskrnl/ke/amd64/stubs.c            |  39 -----
 ntoskrnl/ke/amd64/trap.S             |  70 +++++----
 ntoskrnl/ke/amd64/usercall.c         | 290 +++++++++++++++++++++++++++++++++++
 ntoskrnl/ke/amd64/usercall_asm.S     |  75 +++++++++
 ntoskrnl/ntos.cmake                  |   3 +-
 9 files changed, 466 insertions(+), 194 deletions(-)

diff --git a/dll/ntdll/dispatch/amd64/dispatch.S 
b/dll/ntdll/dispatch/amd64/dispatch.S
index f433c5fb22e..6d132df7755 100644
--- a/dll/ntdll/dispatch/amd64/dispatch.S
+++ b/dll/ntdll/dispatch/amd64/dispatch.S
@@ -14,6 +14,8 @@
 
 EXTERN NtContinue:PROC
 EXTERN LdrpInit:PROC
+EXTERN ZwCallbackReturn:PROC
+EXTERN RtlRaiseStatus:PROC
 
 .code
 
@@ -59,8 +61,49 @@ PUBLIC KiRaiseUserExceptionDispatcher
 
 PUBLIC KiUserCallbackDispatcher
 .PROC KiUserCallbackDispatcher
-    .endprolog
-    int 3
+
+    ; The stack is set up with a UCALLOUT_FRAME
+    ; The frame ends with a MACHINE_FRAME.
+    .PUSHFRAME
+
+    ; This is for the Home space, Buffer, Length and ApiNumber
+    .ALLOCSTACK 6 * 8
+    .ENDPROLOG
+
+#if DBG
+    ; We enter the function with a fully setup stack, so it must be aligned!
+    test rsp, 15
+    jz AlignmentOk
+    int HEX(2C)
+AlignmentOk:
+#endif
+
+    ; Get the parameters from the callout frame
+    mov rcx, [rsp + CkBuffer]
+    mov edx, [rsp + CkLength]
+    mov r8d, [rsp + CkApiNumber]
+
+    ; Get the callback table
+    mov rax, gs:[TePeb]
+    mov r9, [rax + PeKernelCallbackTable]
+
+    ; Call the routine
+    call qword ptr [r9 + r8 * 8]
+
+    ; Return from callback
+    xor ecx, ecx
+    xor edx, edx
+    mov r8d, eax
+    call ZwCallbackReturn
+
+    ; Save callback return value
+    mov esi, eax
+
+    ; Raise status
+StatusRaise:
+    mov ecx, esi
+    call RtlRaiseStatus
+    jmp StatusRaise
 
 .ENDP
 
diff --git a/dll/ntdll/dispatch/amd64/stubs.c b/dll/ntdll/dispatch/amd64/stubs.c
deleted file mode 100644
index b5af8f7ba2e..00000000000
--- a/dll/ntdll/dispatch/amd64/stubs.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS NT Library
- * FILE:            dll/ntdll/dispatch/amd64/stubs.c
- * PURPOSE:         AMD64 stubs
- * PROGRAMMERS:      Stefan Ginsberg ([email protected])
- */
-
-/* INCLUDES *****************************************************************/
-
-#include <ntdll.h>
-#define NDEBUG
-#include <debug.h>
-
-/* PUBLIC FUNCTIONS **********************************************************/
-
-/*
- * @unimplemented
- */
-VOID
-NTAPI
-LdrInitializeThunk(ULONG Unknown1, // FIXME: Parameters!
-                   ULONG Unknown2,
-                   ULONG Unknown3,
-                   ULONG Unknown4)
-{
-    UNIMPLEMENTED;
-    return;
-}
-
-/*
- * @unimplemented
- */
-VOID
-NTAPI
-KiUserApcDispatcher(IN PVOID NormalRoutine,
-                    IN PVOID NormalContext,
-                    IN PVOID SystemArgument1,
-                    IN PVOID SystemArgument2)
-{
-    UNIMPLEMENTED;
-    return;
-}
-
-VOID
-NTAPI
-KiRaiseUserExceptionDispatcher(VOID)
-{
-    UNIMPLEMENTED;
-    return;
-}
-
-VOID
-NTAPI
-KiUserCallbackDispatcher(VOID)
-{
-    UNIMPLEMENTED;
-    return;
-}
-
-VOID
-NTAPI
-KiUserExceptionDispatcher(VOID)
-{
-    UNIMPLEMENTED;
-    return;
-}
diff --git a/ntoskrnl/include/internal/amd64/ke.h 
b/ntoskrnl/include/internal/amd64/ke.h
index 21a2c66a4f2..fa0408b921e 100644
--- a/ntoskrnl/include/internal/amd64/ke.h
+++ b/ntoskrnl/include/internal/amd64/ke.h
@@ -383,6 +383,13 @@ HalAllocateAdapterChannel(
   IN ULONG  NumberOfMapRegisters,
   IN PDRIVER_CONTROL  ExecutionRoutine);
 
+FORCEINLINE
+PULONG_PTR
+KiGetUserModeStackAddress(void)
+{
+    return &PsGetCurrentThread()->Tcb.TrapFrame->Rsp;
+}
+
 #endif /* __NTOSKRNL_INCLUDE_INTERNAL_AMD64_KE_H */
 
 /* EOF */
diff --git a/ntoskrnl/ke/amd64/ctxswitch.S b/ntoskrnl/ke/amd64/ctxswitch.S
index b91f314ced9..a8346021e89 100644
--- a/ntoskrnl/ke/amd64/ctxswitch.S
+++ b/ntoskrnl/ke/amd64/ctxswitch.S
@@ -9,7 +9,6 @@
 
 /* INCLUDES ******************************************************************/
 
-#include <asm.inc>
 #include <ksamd64.inc>
 
 EXTERN KiSwapContextResume:PROC
@@ -202,66 +201,17 @@ PUBLIC KiSwapContextInternal
 PUBLIC KiSwapContext
 .PROC KiSwapContext
 
-    /* Allocate a KEXCEPTION_FRAME on the stack (+8 for proper alignment) */
-    sub rsp, KEXCEPTION_FRAME_LENGTH + 8
-    .allocstack KEXCEPTION_FRAME_LENGTH + 8
-
-    /* save non-volatiles in KEXCEPTION_FRAME */
-    mov [rsp + KEXCEPTION_FRAME_Rbp], rbp
-    .savereg rbp, KEXCEPTION_FRAME_Rbp
-    mov [rsp + KEXCEPTION_FRAME_Rbx], rbx
-    .savereg rbx, KEXCEPTION_FRAME_Rbx
-    mov [rsp + KEXCEPTION_FRAME_Rdi], rdi
-    .savereg rdi, KEXCEPTION_FRAME_Rdi
-    mov [rsp + KEXCEPTION_FRAME_Rsi], rsi
-    .savereg rsi, KEXCEPTION_FRAME_Rsi
-    mov [rsp + KEXCEPTION_FRAME_R12], r12
-    .savereg r12, KEXCEPTION_FRAME_R12
-    mov [rsp + KEXCEPTION_FRAME_R13], r13
-    .savereg r13, KEXCEPTION_FRAME_R13
-    mov [rsp + KEXCEPTION_FRAME_R14], r14
-    .savereg r14, KEXCEPTION_FRAME_R14
-    mov [rsp + KEXCEPTION_FRAME_R15], r15
-    .savereg r15, KEXCEPTION_FRAME_R15
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm6], xmm6
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm7], xmm7
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm8], xmm8
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm9], xmm9
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm10], xmm10
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm11], xmm11
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm12], xmm12
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm13], xmm13
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm14], xmm14
-    movdqa [rsp + KEXCEPTION_FRAME_Xmm15], xmm15
-    // KEXCEPTION_FRAME_MxCsr
-    .endprolog
+    /* Generate a KEXCEPTION_FRAME on the stack */
+    GENERATE_EXCEPTION_FRAME
 
     /* Do the swap with the registers correctly setup */
     mov rcx, gs:[PcCurrentThread] /* Pointer to the new thread */
     call KiSwapContextInternal
 
-    /* restore non-volatile registers */
-    mov rbp, [rsp + KEXCEPTION_FRAME_Rbp]
-    mov rbx, [rsp + KEXCEPTION_FRAME_Rbx]
-    mov rdi, [rsp + KEXCEPTION_FRAME_Rdi]
-    mov rsi, [rsp + KEXCEPTION_FRAME_Rsi]
-    mov r12, [rsp + KEXCEPTION_FRAME_R12]
-    mov r13, [rsp + KEXCEPTION_FRAME_R13]
-    mov r14, [rsp + KEXCEPTION_FRAME_R14]
-    mov r15, [rsp + KEXCEPTION_FRAME_R15]
-    movdqa xmm6, [rsp + KEXCEPTION_FRAME_Xmm6]
-    movdqa xmm7, [rsp + KEXCEPTION_FRAME_Xmm7]
-    movdqa xmm8, [rsp + KEXCEPTION_FRAME_Xmm8]
-    movdqa xmm9, [rsp + KEXCEPTION_FRAME_Xmm9]
-    movdqa xmm10, [rsp + KEXCEPTION_FRAME_Xmm10]
-    movdqa xmm11, [rsp + KEXCEPTION_FRAME_Xmm11]
-    movdqa xmm12, [rsp + KEXCEPTION_FRAME_Xmm12]
-    movdqa xmm13, [rsp + KEXCEPTION_FRAME_Xmm13]
-    movdqa xmm14, [rsp + KEXCEPTION_FRAME_Xmm14]
-    movdqa xmm15, [rsp + KEXCEPTION_FRAME_Xmm15]
-
-    /* Clean stack and return */
-    add rsp, KEXCEPTION_FRAME_LENGTH + 8
+    /* Restore the registers from the KEXCEPTION_FRAME */
+    RESTORE_EXCEPTION_STATE
+
+    /* Return */
     ret
 .ENDP
 
diff --git a/ntoskrnl/ke/amd64/stubs.c b/ntoskrnl/ke/amd64/stubs.c
index 185d68988de..c99a0efcfac 100644
--- a/ntoskrnl/ke/amd64/stubs.c
+++ b/ntoskrnl/ke/amd64/stubs.c
@@ -167,20 +167,6 @@ KiSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
     return OldStackBase;
 }
 
-
-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)
@@ -426,17 +412,6 @@ KiSystemService(IN PKTHREAD Thread,
     __debugbreak();
 }
 
-NTSYSAPI
-NTSTATUS
-NTAPI
-NtCallbackReturn
-( IN PVOID Result OPTIONAL, IN ULONG ResultLength, IN NTSTATUS Status )
-{
-    UNIMPLEMENTED;
-    __debugbreak();
-    return STATUS_UNSUCCESSFUL;
-}
-
 NTSTATUS
 NTAPI
 NtSetLdtEntries
@@ -456,18 +431,4 @@ NtVdmControl(IN ULONG ControlCode,
     return STATUS_NOT_IMPLEMENTED;
 }
 
-NTSTATUS
-NTAPI
-KiCallUserMode(
-    IN PVOID *OutputBuffer,
-    IN PULONG OutputLength)
-{
-    UNIMPLEMENTED;
-    __debugbreak();
-    return STATUS_UNSUCCESSFUL;
-}
-
-ULONG ProcessCount;
-BOOLEAN CcPfEnablePrefetcher;
-
 
diff --git a/ntoskrnl/ke/amd64/trap.S b/ntoskrnl/ke/amd64/trap.S
index d361acb1fb1..7dbe7a291f4 100644
--- a/ntoskrnl/ke/amd64/trap.S
+++ b/ntoskrnl/ke/amd64/trap.S
@@ -787,6 +787,15 @@ PUBLIC KiSystemCallEntry64
     mov r9, [rsp + MAX_SYSCALL_PARAM_SIZE + KTRAP_FRAME_R9]
     call rax
 
+.ENDP
+
+PUBLIC KiSystemServiceExit
+.PROC KiSystemServiceExit
+
+    /* Old stack pointer is in rcx, lie and say we saved it in rbp */
+    .setframe rbp, 0
+    .endprolog
+
 #if DBG
     /* Restore rbp */
     mov rbp, [rsp + MAX_SYSCALL_PARAM_SIZE + KTRAP_FRAME_Rbp]
@@ -822,6 +831,38 @@ IntsEnabled:
 .ENDP
 
 
+/*!
+ * VOID
+ * DECLSPEC_NORETURN
+ * KiServiceExit(IN PKTRAP_FRAME TrapFrame, IN NTSTATUS Status));
+ */
+PUBLIC KiServiceExit
+.PROC KiServiceExit
+    .endprolog
+
+    lea rsp, [rcx - MAX_SYSCALL_PARAM_SIZE]
+    jmp KiSystemServiceExit
+
+.ENDP
+
+
+/*!
+ * VOID
+ * DECLSPEC_NORETURN
+ * KiServiceExit2(IN PKTRAP_FRAME TrapFrame);
+ */
+PUBLIC KiServiceExit2
+.PROC KiServiceExit2
+    .ENDPROLOG
+
+    mov rbp, rcx
+    mov rsp, rcx
+
+    /* Return */
+    ExitTrap TF_SAVE_ALL
+.ENDP
+
+
 PUBLIC KiSystemCallEntry32
 KiSystemCallEntry32:
     swapgs
@@ -962,35 +1003,6 @@ ENDFUNC
 KiExitToUserApc:
     int 3
 
-/*!
- * VOID
- * DECLSPEC_NORETURN
- * KiServiceExit(IN PKTRAP_FRAME TrapFrame, IN NTSTATUS Status));
- */
-PUBLIC KiServiceExit
-KiServiceExit:
-    mov [rcx + KTRAP_FRAME_Rax], rdx
-    mov rbp, rcx
-    mov rsp, rcx
-
-    /* Return */
-    //ExitTrap TF_SAVE_ALL
-
-/*!
- * VOID
- * DECLSPEC_NORETURN
- * KiServiceExit2(IN PKTRAP_FRAME TrapFrame);
- */
-PUBLIC KiServiceExit2
-.PROC KiServiceExit2
-    .ENDPROLOG
-
-    mov rbp, rcx
-    mov rsp, rcx
-
-    /* Return */
-    ExitTrap TF_SAVE_ALL
-.ENDP
 
 PUBLIC KiInitializeSegments
 KiInitializeSegments:
diff --git a/ntoskrnl/ke/amd64/usercall.c b/ntoskrnl/ke/amd64/usercall.c
index 38517cb58fc..42976d340ef 100644
--- a/ntoskrnl/ke/amd64/usercall.c
+++ b/ntoskrnl/ke/amd64/usercall.c
@@ -106,3 +106,293 @@ KiInitializeUserApc(
     TrapFrame->EFlags &= EFLAGS_USER_SANITIZE;
     TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
 }
+
+/*
+ * Stack layout for KiUserModeCallout:
+ * ----------------------------------
+ * KCALLOUT_FRAME.ResultLength    <= 2nd Parameter to KiCallUserMode
+ * KCALLOUT_FRAME.Result          <= 1st Parameter to KiCallUserMode
+ * KCALLOUT_FRAME.ReturnAddress   <= Return address of KiCallUserMode
+ * KCALLOUT_FRAME.Ebp             \
+ * KCALLOUT_FRAME.Ebx              | = non-volatile registers, pushed
+ * KCALLOUT_FRAME.Esi              |   by KiCallUserMode
+ * KCALLOUT_FRAME.Edi             /
+ * KCALLOUT_FRAME.CallbackStack
+ * KCALLOUT_FRAME.TrapFrame
+ * KCALLOUT_FRAME.InitialStack    <= CalloutFrame points here
+ * ----------------------------------
+ * ~~ optional alignment ~~
+ * ----------------------------------
+ * FX_SAVE_AREA
+ * ----------------------------------
+ * KTRAP_FRAME
+ * ----------------------------------
+ * ~~ begin of stack frame for KiUserModeCallout ~~
+ *
+ */
+NTSTATUS
+FASTCALL
+KiUserModeCallout(
+    _Out_ PKCALLOUT_FRAME CalloutFrame)
+{
+    PKTHREAD CurrentThread;
+    PKTRAP_FRAME TrapFrame;
+    KTRAP_FRAME CallbackTrapFrame;
+    PKIPCR Pcr;
+    ULONG_PTR InitialStack;
+    NTSTATUS Status;
+
+    /* Get the current thread */
+    CurrentThread = KeGetCurrentThread();
+
+    /* Check if we are at pasive level */
+    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+    /* Check if we are attached or APCs are disabled */
+    ASSERT((CurrentThread->ApcStateIndex == OriginalApcEnvironment) &&
+        (CurrentThread->CombinedApcDisable == 0));
+
+    /* Align stack on a 16-byte boundary */
+    InitialStack = (ULONG_PTR)ALIGN_DOWN_POINTER_BY(CalloutFrame, 16);
+
+    /* Check if we have enough space on the stack */
+    if ((InitialStack - KERNEL_STACK_SIZE) < CurrentThread->StackLimit)
+    {
+        /* We don't, we'll have to grow our stack */
+        Status = MmGrowKernelStack((PVOID)InitialStack);
+
+        /* Quit if we failed */
+        if (!NT_SUCCESS(Status)) return Status;
+    }
+
+    /* Save the current callback stack and initial stack */
+    CalloutFrame->CallbackStack = (ULONG_PTR)CurrentThread->CallbackStack;
+    CalloutFrame->InitialStack = (ULONG_PTR)CurrentThread->InitialStack;
+
+    /* Get and save the trap frame */
+    TrapFrame = CurrentThread->TrapFrame;
+    CalloutFrame->TrapFrame = (ULONG_PTR)TrapFrame;
+
+    /* Set the new callback stack */
+    CurrentThread->CallbackStack = CalloutFrame;
+
+    /* Disable interrupts so we can fill the NPX State */
+    _disable();
+
+    /* Set the stack address */
+    CurrentThread->InitialStack = (PVOID)InitialStack;
+
+    /* Copy the trap frame to the new location */
+    CallbackTrapFrame = *TrapFrame;
+
+    /* Get PCR */
+    Pcr = (PKIPCR)KeGetPcr();
+
+    /* Set user-mode dispatcher address as EIP */
+    Pcr->TssBase->Rsp0 = InitialStack;
+    Pcr->Prcb.RspBase = InitialStack;
+    CallbackTrapFrame.Rip = (ULONG_PTR)KeUserCallbackDispatcher;
+
+    /* Bring interrupts back */
+    _enable();
+
+    /* Exit to user-mode */
+    KiServiceExit(&CallbackTrapFrame, 0);
+}
+
+VOID
+KiSetupUserCalloutFrame(
+    _Out_ PUCALLOUT_FRAME UserCalloutFrame,
+    _In_ PKTRAP_FRAME TrapFrame,
+    _In_ ULONG ApiNumber,
+    _In_ PVOID Buffer,
+    _In_ ULONG BufferLength)
+{
+#ifdef _M_IX86
+    CalloutFrame->Reserved = 0;
+    CalloutFrame->ApiNumber = ApiNumber;
+    CalloutFrame->Buffer = (ULONG_PTR)NewStack;
+    CalloutFrame->Length = ArgumentLength;
+#elif defined(_M_AMD64)
+    UserCalloutFrame->Buffer = (PVOID)(UserCalloutFrame + 1);
+    UserCalloutFrame->Length = BufferLength;
+    UserCalloutFrame->ApiNumber = ApiNumber;
+    UserCalloutFrame->MachineFrame.Rip = TrapFrame->Rip;
+    UserCalloutFrame->MachineFrame.Rsp = TrapFrame->Rsp;
+#else
+#error "KiSetupUserCalloutFrame not implemented!"
+#endif
+}
+
+NTSTATUS
+NTAPI
+KeUserModeCallback(
+    IN ULONG RoutineIndex,
+    IN PVOID Argument,
+    IN ULONG ArgumentLength,
+    OUT PVOID *Result,
+    OUT PULONG ResultLength)
+{
+    ULONG_PTR OldStack;
+    PUCHAR UserArguments;
+    PUCALLOUT_FRAME CalloutFrame;
+    PULONG_PTR UserStackPointer;
+    NTSTATUS CallbackStatus;
+#ifdef _M_IX86
+    PEXCEPTION_REGISTRATION_RECORD ExceptionList;
+#endif // _M_IX86
+    PTEB Teb;
+    ULONG GdiBatchCount = 0;
+    ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
+    ASSERT(KeGetPreviousMode() == UserMode);
+
+    /* Get the current user-mode stack */
+    UserStackPointer = KiGetUserModeStackAddress();
+    OldStack = *UserStackPointer;
+
+    /* Enter a SEH Block */
+    _SEH2_TRY
+    {
+        /* Calculate and align the stack size */
+        UserArguments = (PUCHAR)ALIGN_DOWN_POINTER_BY(OldStack - 
ArgumentLength, sizeof(PVOID));
+
+        /* The callout frame is below the arguments */
+        CalloutFrame = ((PUCALLOUT_FRAME)UserArguments) - 1;
+
+        /* Make sure it's all writable */
+        ProbeForWrite(CalloutFrame,
+                      sizeof(PUCALLOUT_FRAME) + ArgumentLength,
+                      sizeof(PVOID));
+
+        /* Copy the buffer into the stack */
+        RtlCopyMemory(UserArguments, Argument, ArgumentLength);
+
+        /* Write the arguments */
+        KiSetupUserCalloutFrame(CalloutFrame,
+                                KeGetCurrentThread()->TrapFrame,
+                                RoutineIndex,
+                                UserArguments,
+                                ArgumentLength);
+
+        /* Save the exception list */
+        Teb = KeGetCurrentThread()->Teb;
+#ifdef _M_IX86
+        ExceptionList = Teb->NtTib.ExceptionList;
+#endif // _M_IX86
+
+        /* Jump to user mode */
+        *UserStackPointer = (ULONG_PTR)CalloutFrame;
+        CallbackStatus = KiCallUserMode(Result, ResultLength);
+        if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
+        {
+#ifdef _M_IX86
+            /* Only restore the exception list if we didn't crash in ring 3 */
+            Teb->NtTib.ExceptionList = ExceptionList;
+#endif // _M_IX86
+        }
+        else
+        {
+            /* Otherwise, pop the stack */
+            OldStack = *UserStackPointer;
+        }
+
+        /* Read the GDI Batch count */
+        GdiBatchCount = Teb->GdiBatchCount;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Get the SEH exception */
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    /* Check if we have GDI Batch operations */
+    if (GdiBatchCount)
+    {
+        *UserStackPointer -= 256;
+        KeGdiFlushUserBatch();
+    }
+
+    /* Restore stack and return */
+    *UserStackPointer = OldStack;
+#ifdef _M_AMD64 // could probably  move the update to TrapFrame->Rsp from the 
C handler to the asm code
+    __writegsqword(FIELD_OFFSET(KIPCR, UserRsp), OldStack);
+#endif
+    return CallbackStatus;
+}
+
+NTSTATUS
+NTAPI
+NtCallbackReturn(
+    _In_ PVOID Result,
+    _In_ ULONG ResultLength,
+    _In_ NTSTATUS CallbackStatus)
+{
+    PKTHREAD CurrentThread;
+    PKCALLOUT_FRAME CalloutFrame;
+    PKTRAP_FRAME CallbackTrapFrame, TrapFrame;
+    PKIPCR Pcr;
+
+    /* Get the current thread and make sure we have a callback stack */
+    CurrentThread = KeGetCurrentThread();
+    CalloutFrame = CurrentThread->CallbackStack;
+    if (CalloutFrame == NULL)
+    {
+        return STATUS_NO_CALLBACK_ACTIVE;
+    }
+
+    /* Store the results in the callback stack */
+    *((PVOID*)CalloutFrame->OutputBuffer) = Result;
+    *((ULONG*)CalloutFrame->OutputLength) = ResultLength;
+
+    /* Get the trap frame */
+    CallbackTrapFrame = CurrentThread->TrapFrame;
+
+    /* Disable interrupts for NPX save and stack switch */
+    _disable();
+
+    /* Restore the exception list */
+    Pcr = (PKIPCR)KeGetPcr();
+
+    /* Get the previous trap frame */
+    TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame;
+
+    /* Check if we failed in user mode */
+    if (CallbackStatus == STATUS_CALLBACK_POP_STACK)
+    {
+        *TrapFrame = *CallbackTrapFrame;
+    }
+
+    /* Clear DR7 */
+    TrapFrame->Dr7 = 0;
+
+    /* Check if debugging was active */
+    if (CurrentThread->Header.DebugActive & 0xFF)
+    {
+        /* Copy debug registers data from it */
+        TrapFrame->Dr0 = CallbackTrapFrame->Dr0;
+        TrapFrame->Dr1 = CallbackTrapFrame->Dr1;
+        TrapFrame->Dr2 = CallbackTrapFrame->Dr2;
+        TrapFrame->Dr3 = CallbackTrapFrame->Dr3;
+        TrapFrame->Dr6 = CallbackTrapFrame->Dr6;
+        TrapFrame->Dr7 = CallbackTrapFrame->Dr7;
+    }
+
+    /* Switch the stack back to the previous value */
+    Pcr->TssBase->Rsp0 = CalloutFrame->InitialStack;
+    Pcr->Prcb.RspBase = CalloutFrame->InitialStack;
+
+    /* Get the initial stack and restore it */
+    CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack;
+
+    /* Restore the trap frame and the previous callback stack */
+    CurrentThread->TrapFrame = TrapFrame;
+    CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack;
+
+    /* Bring interrupts back */
+    _enable();
+
+    /* Now switch back to the old stack */
+    KiCallbackReturn(CalloutFrame, CallbackStatus);
+}
+
diff --git a/ntoskrnl/ke/amd64/usercall_asm.S b/ntoskrnl/ke/amd64/usercall_asm.S
new file mode 100644
index 00000000000..2d894161fed
--- /dev/null
+++ b/ntoskrnl/ke/amd64/usercall_asm.S
@@ -0,0 +1,75 @@
+;++
+; PROJECT:     ReactOS Kernel
+; LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+; PURPOSE:     ReactOS AMD64 user mode callback helper
+; COPYRIGHT:   Timo Kreuzer ([email protected])
+;--
+
+#include <ksamd64.inc>
+
+;
+; NTSTATUS
+; KiUserModeCallout (
+;     _Inout_ PKCALLOUT_FRAME CalloutFrame);
+;
+EXTERN KiUserModeCallout:PROC
+
+.code64
+
+;
+; NTSTATUS
+; KiCallUserMode (
+;     _In_ PVOID *OutputBuffer@<rcx>,
+;     _In_ PULONG OutputLength@<rdx>);
+;
+PUBLIC KiCallUserMode
+.PROC KiCallUserMode
+
+    ; Generate a KEXCEPTION_FRAME on the stack
+    ; This is identical to a KCALLOUT_FRAME
+    GENERATE_EXCEPTION_FRAME
+
+    ; Save OutputBuffer and OutputLength
+    mov [rsp + ExOutputBuffer], rcx
+    mov [rsp + ExOutputLength], rdx
+
+    ; Call the C function
+    mov rcx, rsp
+    call KiUserModeCallout
+
+    ; Restore the registers from the KEXCEPTION_FRAME
+    RESTORE_EXCEPTION_STATE
+
+    ; Return
+    ret
+
+.ENDP
+
+;
+; DECLSPEC_NORETURN
+; VOID
+; KiCallbackReturn (
+;     _In_ PVOID Stack,
+;     _In_ NTSTATUS Status);
+;
+PUBLIC KiCallbackReturn
+.PROC KiCallbackReturn
+
+    .ENDPROLOG
+
+    ; Restore the stack
+    mov rsp, rcx
+
+    ; Set return status
+    mov eax, edx
+
+    ; Restore the registers from the KEXCEPTION_FRAME
+    RESTORE_EXCEPTION_STATE
+
+    ; Return
+    ret
+
+.ENDP
+
+
+END
diff --git a/ntoskrnl/ntos.cmake b/ntoskrnl/ntos.cmake
index c69c284cdf2..6b2a127c2fd 100644
--- a/ntoskrnl/ntos.cmake
+++ b/ntoskrnl/ntos.cmake
@@ -314,7 +314,8 @@ elseif(ARCH STREQUAL "amd64")
     list(APPEND ASM_SOURCE
         ${REACTOS_SOURCE_DIR}/ntoskrnl/ke/amd64/boot.S
         ${REACTOS_SOURCE_DIR}/ntoskrnl/ke/amd64/ctxswitch.S
-        ${REACTOS_SOURCE_DIR}/ntoskrnl/ke/amd64/trap.S)
+        ${REACTOS_SOURCE_DIR}/ntoskrnl/ke/amd64/trap.S
+        ${REACTOS_SOURCE_DIR}/ntoskrnl/ke/amd64/usercall_asm.S)
     list(APPEND SOURCE
         ${REACTOS_SOURCE_DIR}/ntoskrnl/config/i386/cmhardwr.c
         ${REACTOS_SOURCE_DIR}/ntoskrnl/ke/amd64/context.c

Reply via email to