1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - 2.0 + (https ://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: AMD64 User-mode Callout Mechanisms (APC and Win32K Callbacks) 5 * COPYRIGHT: Timo Kreuzer(timo.kreuzer@reactos.org) 6 */ 7 8 /* INCLUDES ******************************************************************/ 9 10 #include <ntoskrnl.h> 11 #define NDEBUG 12 #include <debug.h> 13 14 /*! 15 * \name KiInitializeUserApc 16 * 17 * \brief 18 * Prepares the current trap frame (which must have come from user mode) 19 * with the ntdll.KiUserApcDispatcher entrypoint, copying a CONTEXT 20 * record with the context from the old trap frame to the threads user 21 * mode stack. 22 * 23 * \param ExceptionFrame - Pointer to the Exception Frame 24 * 25 * \param TrapFrame Pointer to the Trap Frame. 26 * 27 * \param NormalRoutine - Pointer to the NormalRoutine to call. 28 * 29 * \param NormalContext - Pointer to the context to send to the Normal Routine. 30 * 31 * \param SystemArgument[1-2] 32 * Pointer to a set of two parameters that contain untyped data. 33 * 34 * \remarks 35 * This function is called from KiDeliverApc, when the trap frame came 36 * from user mode. This happens before a systemcall or interrupt exits back 37 * to usermode or when a thread is started from PspUserThreadstartup. 38 * The trap exit code will then leave to KiUserApcDispatcher which in turn 39 * calls the NormalRoutine, passing NormalContext, SystemArgument1 and 40 * SystemArgument2 as parameters. When that function returns, it calls 41 * NtContinue to return back to the kernel, where the old context that was 42 * saved on the usermode stack is restored and execution is transferred 43 * back to usermode, where the original trap originated from. 44 * 45 *--*/ 46 VOID 47 NTAPI 48 KiInitializeUserApc( 49 _In_ PKEXCEPTION_FRAME ExceptionFrame, 50 _Inout_ PKTRAP_FRAME TrapFrame, 51 _In_ PKNORMAL_ROUTINE NormalRoutine, 52 _In_ PVOID NormalContext, 53 _In_ PVOID SystemArgument1, 54 _In_ PVOID SystemArgument2) 55 { 56 PCONTEXT Context; 57 EXCEPTION_RECORD ExceptionRecord; 58 59 /* Sanity check, that the trap frame is from user mode */ 60 ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode); 61 62 /* Align the user tack to 16 bytes and allocate space for a CONTEXT structure */ 63 Context = (PCONTEXT)ALIGN_DOWN_POINTER_BY(TrapFrame->Rsp, 16) - 1; 64 65 /* Protect with SEH */ 66 _SEH2_TRY 67 { 68 /* Probe the context */ 69 ProbeForWrite(Context, sizeof(CONTEXT), 16); 70 71 /* Convert the current trap frame to a context */ 72 Context->ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; 73 KeTrapFrameToContext(TrapFrame, ExceptionFrame, Context); 74 75 /* Set parameters for KiUserApcDispatcher */ 76 Context->P1Home = (ULONG64)NormalContext; 77 Context->P2Home = (ULONG64)SystemArgument1; 78 Context->P3Home = (ULONG64)SystemArgument2; 79 Context->P4Home = (ULONG64)NormalRoutine; 80 } 81 _SEH2_EXCEPT(ExceptionRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord, EXCEPTION_EXECUTE_HANDLER) 82 { 83 /* Dispatch the exception */ 84 ExceptionRecord.ExceptionAddress = (PVOID)TrapFrame->Rip; 85 KiDispatchException(&ExceptionRecord, 86 ExceptionFrame, 87 TrapFrame, 88 UserMode, 89 TRUE); 90 } 91 _SEH2_END; 92 93 /* Set the stack pointer to the context record */ 94 TrapFrame->Rsp = (ULONG64)Context; 95 96 /* We jump to KiUserApcDispatcher in ntdll */ 97 TrapFrame->Rip = (ULONG64)KeUserApcDispatcher; 98 99 /* Setup Ring 3 segments */ 100 TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK; 101 TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK; 102 TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK; 103 TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK; 104 105 /* Sanitize EFLAGS, enable interrupts */ 106 TrapFrame->EFlags &= EFLAGS_USER_SANITIZE; 107 TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK; 108 } 109 110 /* 111 * Stack layout for KiUserModeCallout: 112 * ---------------------------------- 113 * KCALLOUT_FRAME.ResultLength <= 2nd Parameter to KiCallUserMode 114 * KCALLOUT_FRAME.Result <= 1st Parameter to KiCallUserMode 115 * KCALLOUT_FRAME.ReturnAddress <= Return address of KiCallUserMode 116 * KCALLOUT_FRAME.Ebp \ 117 * KCALLOUT_FRAME.Ebx | = non-volatile registers, pushed 118 * KCALLOUT_FRAME.Esi | by KiCallUserMode 119 * KCALLOUT_FRAME.Edi / 120 * KCALLOUT_FRAME.CallbackStack 121 * KCALLOUT_FRAME.TrapFrame 122 * KCALLOUT_FRAME.InitialStack <= CalloutFrame points here 123 * ---------------------------------- 124 * ~~ optional alignment ~~ 125 * ---------------------------------- 126 * FX_SAVE_AREA 127 * ---------------------------------- 128 * KTRAP_FRAME 129 * ---------------------------------- 130 * ~~ begin of stack frame for KiUserModeCallout ~~ 131 * 132 */ 133 NTSTATUS 134 FASTCALL 135 KiUserModeCallout( 136 _Out_ PKCALLOUT_FRAME CalloutFrame) 137 { 138 PKTHREAD CurrentThread; 139 PKTRAP_FRAME TrapFrame; 140 KTRAP_FRAME CallbackTrapFrame; 141 PKIPCR Pcr; 142 ULONG_PTR InitialStack; 143 NTSTATUS Status; 144 145 /* Get the current thread */ 146 CurrentThread = KeGetCurrentThread(); 147 148 /* Check if we are at pasive level */ 149 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 150 151 /* Check if we are attached or APCs are disabled */ 152 ASSERT((CurrentThread->ApcStateIndex == OriginalApcEnvironment) && 153 (CurrentThread->CombinedApcDisable == 0)); 154 155 /* Align stack on a 16-byte boundary */ 156 InitialStack = (ULONG_PTR)ALIGN_DOWN_POINTER_BY(CalloutFrame, 16); 157 158 /* Check if we have enough space on the stack */ 159 if ((InitialStack - KERNEL_STACK_SIZE) < CurrentThread->StackLimit) 160 { 161 /* We don't, we'll have to grow our stack */ 162 Status = MmGrowKernelStack((PVOID)InitialStack); 163 164 /* Quit if we failed */ 165 if (!NT_SUCCESS(Status)) return Status; 166 } 167 168 /* Save the current callback stack and initial stack */ 169 CalloutFrame->CallbackStack = (ULONG_PTR)CurrentThread->CallbackStack; 170 CalloutFrame->InitialStack = (ULONG_PTR)CurrentThread->InitialStack; 171 172 /* Get and save the trap frame */ 173 TrapFrame = CurrentThread->TrapFrame; 174 CalloutFrame->TrapFrame = (ULONG_PTR)TrapFrame; 175 176 /* Set the new callback stack */ 177 CurrentThread->CallbackStack = CalloutFrame; 178 179 /* Disable interrupts so we can fill the NPX State */ 180 _disable(); 181 182 /* Set the stack address */ 183 CurrentThread->InitialStack = (PVOID)InitialStack; 184 185 /* Copy the trap frame to the new location */ 186 CallbackTrapFrame = *TrapFrame; 187 188 /* Get PCR */ 189 Pcr = (PKIPCR)KeGetPcr(); 190 191 /* Set user-mode dispatcher address as EIP */ 192 Pcr->TssBase->Rsp0 = InitialStack; 193 Pcr->Prcb.RspBase = InitialStack; 194 CallbackTrapFrame.Rip = (ULONG_PTR)KeUserCallbackDispatcher; 195 196 /* Bring interrupts back */ 197 _enable(); 198 199 /* Exit to user-mode */ 200 KiServiceExit(&CallbackTrapFrame, 0); 201 } 202 203 VOID 204 KiSetupUserCalloutFrame( 205 _Out_ PUCALLOUT_FRAME UserCalloutFrame, 206 _In_ PKTRAP_FRAME TrapFrame, 207 _In_ ULONG ApiNumber, 208 _In_ PVOID Buffer, 209 _In_ ULONG BufferLength) 210 { 211 #ifdef _M_IX86 212 CalloutFrame->Reserved = 0; 213 CalloutFrame->ApiNumber = ApiNumber; 214 CalloutFrame->Buffer = (ULONG_PTR)NewStack; 215 CalloutFrame->Length = ArgumentLength; 216 #elif defined(_M_AMD64) 217 UserCalloutFrame->Buffer = (PVOID)(UserCalloutFrame + 1); 218 UserCalloutFrame->Length = BufferLength; 219 UserCalloutFrame->ApiNumber = ApiNumber; 220 UserCalloutFrame->MachineFrame.Rip = TrapFrame->Rip; 221 UserCalloutFrame->MachineFrame.Rsp = TrapFrame->Rsp; 222 #else 223 #error "KiSetupUserCalloutFrame not implemented!" 224 #endif 225 } 226 227 NTSTATUS 228 NTAPI 229 KeUserModeCallback( 230 IN ULONG RoutineIndex, 231 IN PVOID Argument, 232 IN ULONG ArgumentLength, 233 OUT PVOID *Result, 234 OUT PULONG ResultLength) 235 { 236 ULONG_PTR OldStack; 237 PUCHAR UserArguments; 238 PUCALLOUT_FRAME CalloutFrame; 239 PULONG_PTR UserStackPointer; 240 NTSTATUS CallbackStatus; 241 #ifdef _M_IX86 242 PEXCEPTION_REGISTRATION_RECORD ExceptionList; 243 #endif // _M_IX86 244 PTEB Teb; 245 ULONG GdiBatchCount = 0; 246 ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE); 247 ASSERT(KeGetPreviousMode() == UserMode); 248 249 /* Get the current user-mode stack */ 250 UserStackPointer = KiGetUserModeStackAddress(); 251 OldStack = *UserStackPointer; 252 253 /* Enter a SEH Block */ 254 _SEH2_TRY 255 { 256 /* Calculate and align the stack size */ 257 UserArguments = (PUCHAR)ALIGN_DOWN_POINTER_BY(OldStack - ArgumentLength, sizeof(PVOID)); 258 259 /* The callout frame is below the arguments */ 260 CalloutFrame = ((PUCALLOUT_FRAME)UserArguments) - 1; 261 262 /* Make sure it's all writable */ 263 ProbeForWrite(CalloutFrame, 264 sizeof(PUCALLOUT_FRAME) + ArgumentLength, 265 sizeof(PVOID)); 266 267 /* Copy the buffer into the stack */ 268 RtlCopyMemory(UserArguments, Argument, ArgumentLength); 269 270 /* Write the arguments */ 271 KiSetupUserCalloutFrame(CalloutFrame, 272 KeGetCurrentThread()->TrapFrame, 273 RoutineIndex, 274 UserArguments, 275 ArgumentLength); 276 277 /* Save the exception list */ 278 Teb = KeGetCurrentThread()->Teb; 279 #ifdef _M_IX86 280 ExceptionList = Teb->NtTib.ExceptionList; 281 #endif // _M_IX86 282 283 /* Jump to user mode */ 284 *UserStackPointer = (ULONG_PTR)CalloutFrame; 285 CallbackStatus = KiCallUserMode(Result, ResultLength); 286 if (CallbackStatus != STATUS_CALLBACK_POP_STACK) 287 { 288 #ifdef _M_IX86 289 /* Only restore the exception list if we didn't crash in ring 3 */ 290 Teb->NtTib.ExceptionList = ExceptionList; 291 #endif // _M_IX86 292 } 293 else 294 { 295 /* Otherwise, pop the stack */ 296 OldStack = *UserStackPointer; 297 } 298 299 /* Read the GDI Batch count */ 300 GdiBatchCount = Teb->GdiBatchCount; 301 } 302 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 303 { 304 /* Get the SEH exception */ 305 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 306 } 307 _SEH2_END; 308 309 /* Check if we have GDI Batch operations */ 310 if (GdiBatchCount) 311 { 312 *UserStackPointer -= 256; 313 KeGdiFlushUserBatch(); 314 } 315 316 /* Restore stack and return */ 317 *UserStackPointer = OldStack; 318 #ifdef _M_AMD64 // could probably move the update to TrapFrame->Rsp from the C handler to the asm code 319 __writegsqword(FIELD_OFFSET(KIPCR, UserRsp), OldStack); 320 #endif 321 return CallbackStatus; 322 } 323 324 NTSTATUS 325 NTAPI 326 NtCallbackReturn( 327 _In_ PVOID Result, 328 _In_ ULONG ResultLength, 329 _In_ NTSTATUS CallbackStatus) 330 { 331 PKTHREAD CurrentThread; 332 PKCALLOUT_FRAME CalloutFrame; 333 PKTRAP_FRAME CallbackTrapFrame, TrapFrame; 334 PKIPCR Pcr; 335 336 /* Get the current thread and make sure we have a callback stack */ 337 CurrentThread = KeGetCurrentThread(); 338 CalloutFrame = CurrentThread->CallbackStack; 339 if (CalloutFrame == NULL) 340 { 341 return STATUS_NO_CALLBACK_ACTIVE; 342 } 343 344 /* Store the results in the callback stack */ 345 *((PVOID*)CalloutFrame->OutputBuffer) = Result; 346 *((ULONG*)CalloutFrame->OutputLength) = ResultLength; 347 348 /* Get the trap frame */ 349 CallbackTrapFrame = CurrentThread->TrapFrame; 350 351 /* Disable interrupts for NPX save and stack switch */ 352 _disable(); 353 354 /* Restore the exception list */ 355 Pcr = (PKIPCR)KeGetPcr(); 356 357 /* Get the previous trap frame */ 358 TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame; 359 360 /* Check if we failed in user mode */ 361 if (CallbackStatus == STATUS_CALLBACK_POP_STACK) 362 { 363 *TrapFrame = *CallbackTrapFrame; 364 } 365 366 /* Clear DR7 */ 367 TrapFrame->Dr7 = 0; 368 369 /* Check if debugging was active */ 370 if (CurrentThread->Header.DebugActive & 0xFF) 371 { 372 /* Copy debug registers data from it */ 373 TrapFrame->Dr0 = CallbackTrapFrame->Dr0; 374 TrapFrame->Dr1 = CallbackTrapFrame->Dr1; 375 TrapFrame->Dr2 = CallbackTrapFrame->Dr2; 376 TrapFrame->Dr3 = CallbackTrapFrame->Dr3; 377 TrapFrame->Dr6 = CallbackTrapFrame->Dr6; 378 TrapFrame->Dr7 = CallbackTrapFrame->Dr7; 379 } 380 381 /* Switch the stack back to the previous value */ 382 Pcr->TssBase->Rsp0 = CalloutFrame->InitialStack; 383 Pcr->Prcb.RspBase = CalloutFrame->InitialStack; 384 385 /* Get the initial stack and restore it */ 386 CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack; 387 388 /* Restore the trap frame and the previous callback stack */ 389 CurrentThread->TrapFrame = TrapFrame; 390 CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack; 391 392 /* Bring interrupts back */ 393 _enable(); 394 395 /* Now switch back to the old stack */ 396 KiCallbackReturn(CalloutFrame, CallbackStatus); 397 } 398 399