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 KiUserCallbackExit(&CallbackTrapFrame); 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. This is unaligned by 8 bytes, since the following 257 UCALLOUT_FRAME compensates for that and on entry we already have a full stack 258 frame with home space for the next call, i.e. we are already inside the function 259 body and the stack needs to be 16 byte aligned. */ 260 UserArguments = (PUCHAR)ALIGN_DOWN_POINTER_BY(OldStack - ArgumentLength, 16) - 8; 261 262 /* The callout frame is below the arguments */ 263 CalloutFrame = ((PUCALLOUT_FRAME)UserArguments) - 1; 264 265 /* Make sure it's all writable */ 266 ProbeForWrite(CalloutFrame, 267 sizeof(PUCALLOUT_FRAME) + ArgumentLength, 268 sizeof(PVOID)); 269 270 /* Copy the buffer into the stack */ 271 RtlCopyMemory(UserArguments, Argument, ArgumentLength); 272 273 /* Write the arguments */ 274 KiSetupUserCalloutFrame(CalloutFrame, 275 KeGetCurrentThread()->TrapFrame, 276 RoutineIndex, 277 UserArguments, 278 ArgumentLength); 279 280 /* Save the exception list */ 281 Teb = KeGetCurrentThread()->Teb; 282 #ifdef _M_IX86 283 ExceptionList = Teb->NtTib.ExceptionList; 284 #endif // _M_IX86 285 286 /* Jump to user mode */ 287 *UserStackPointer = (ULONG_PTR)CalloutFrame; 288 CallbackStatus = KiCallUserMode(Result, ResultLength); 289 if (CallbackStatus != STATUS_CALLBACK_POP_STACK) 290 { 291 #ifdef _M_IX86 292 /* Only restore the exception list if we didn't crash in ring 3 */ 293 Teb->NtTib.ExceptionList = ExceptionList; 294 #endif // _M_IX86 295 } 296 else 297 { 298 /* Otherwise, pop the stack */ 299 OldStack = *UserStackPointer; 300 } 301 302 /* Read the GDI Batch count */ 303 GdiBatchCount = Teb->GdiBatchCount; 304 } 305 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 306 { 307 /* Get the SEH exception */ 308 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 309 } 310 _SEH2_END; 311 312 /* Check if we have GDI Batch operations */ 313 if (GdiBatchCount) 314 { 315 *UserStackPointer -= 256; 316 KeGdiFlushUserBatch(); 317 } 318 319 /* Restore stack and return */ 320 *UserStackPointer = OldStack; 321 #ifdef _M_AMD64 // could probably move the update to TrapFrame->Rsp from the C handler to the asm code 322 __writegsqword(FIELD_OFFSET(KIPCR, UserRsp), OldStack); 323 #endif 324 return CallbackStatus; 325 } 326 327 NTSTATUS 328 NTAPI 329 NtCallbackReturn( 330 _In_ PVOID Result, 331 _In_ ULONG ResultLength, 332 _In_ NTSTATUS CallbackStatus) 333 { 334 PKTHREAD CurrentThread; 335 PKCALLOUT_FRAME CalloutFrame; 336 PKTRAP_FRAME CallbackTrapFrame, TrapFrame; 337 PKIPCR Pcr; 338 339 /* Get the current thread and make sure we have a callback stack */ 340 CurrentThread = KeGetCurrentThread(); 341 CalloutFrame = CurrentThread->CallbackStack; 342 if (CalloutFrame == NULL) 343 { 344 return STATUS_NO_CALLBACK_ACTIVE; 345 } 346 347 /* Store the results in the callback stack */ 348 *((PVOID*)CalloutFrame->OutputBuffer) = Result; 349 *((ULONG*)CalloutFrame->OutputLength) = ResultLength; 350 351 /* Get the trap frame */ 352 CallbackTrapFrame = CurrentThread->TrapFrame; 353 354 /* Disable interrupts for NPX save and stack switch */ 355 _disable(); 356 357 /* Restore the exception list */ 358 Pcr = (PKIPCR)KeGetPcr(); 359 360 /* Get the previous trap frame */ 361 TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame; 362 363 /* Check if we failed in user mode */ 364 if (CallbackStatus == STATUS_CALLBACK_POP_STACK) 365 { 366 *TrapFrame = *CallbackTrapFrame; 367 } 368 369 /* Clear DR7 */ 370 TrapFrame->Dr7 = 0; 371 372 /* Check if debugging was active */ 373 if (CurrentThread->Header.DebugActive & 0xFF) 374 { 375 /* Copy debug registers data from it */ 376 TrapFrame->Dr0 = CallbackTrapFrame->Dr0; 377 TrapFrame->Dr1 = CallbackTrapFrame->Dr1; 378 TrapFrame->Dr2 = CallbackTrapFrame->Dr2; 379 TrapFrame->Dr3 = CallbackTrapFrame->Dr3; 380 TrapFrame->Dr6 = CallbackTrapFrame->Dr6; 381 TrapFrame->Dr7 = CallbackTrapFrame->Dr7; 382 } 383 384 /* Switch the stack back to the previous value */ 385 Pcr->TssBase->Rsp0 = CalloutFrame->InitialStack; 386 Pcr->Prcb.RspBase = CalloutFrame->InitialStack; 387 388 /* Get the initial stack and restore it */ 389 CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack; 390 391 /* Restore the trap frame and the previous callback stack */ 392 CurrentThread->TrapFrame = TrapFrame; 393 CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack; 394 395 /* Bring interrupts back */ 396 _enable(); 397 398 /* Now switch back to the old stack */ 399 KiCallbackReturn(CalloutFrame, CallbackStatus); 400 } 401 402