https://git.reactos.org/?p=reactos.git;a=commitdiff;h=fdc1261fb77879cde0afb…
commit fdc1261fb77879cde0afb14912f928a44b5b6305
Author: Timo Kreuzer <timo.kreuzer(a)reactos.org>
AuthorDate: Mon Feb 5 01:26:11 2018 +0100
Commit: Timo Kreuzer <timo.kreuzer(a)reactos.org>
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 (stefan.ginsberg(a)reactos.org)
- */
-
-/* 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 (timo.kreuzer(a)reactos.org)
+;--
+
+#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