1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ke/i386/usercall.c 5 * PURPOSE: User-mode Callout Mechanisms (APC and Win32K Callbacks) 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Timo Kreuzer (timo.kreuzer@reactos.org) 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch; 17 18 /* PRIVATE FUNCTIONS *********************************************************/ 19 20 /*++ 21 * @name KiInitializeUserApc 22 * 23 * Prepares the Context for a User-Mode APC called through NTDLL.DLL 24 * 25 * @param Reserved 26 * Pointer to the Exception Frame on non-i386 builds. 27 * 28 * @param TrapFrame 29 * Pointer to the Trap Frame. 30 * 31 * @param NormalRoutine 32 * Pointer to the NormalRoutine to call. 33 * 34 * @param NormalContext 35 * Pointer to the context to send to the Normal Routine. 36 * 37 * @param SystemArgument[1-2] 38 * Pointer to a set of two parameters that contain untyped data. 39 * 40 * @return None. 41 * 42 * @remarks None. 43 * 44 *--*/ 45 VOID 46 NTAPI 47 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame, 48 IN PKTRAP_FRAME TrapFrame, 49 IN PKNORMAL_ROUTINE NormalRoutine, 50 IN PVOID NormalContext, 51 IN PVOID SystemArgument1, 52 IN PVOID SystemArgument2) 53 { 54 CONTEXT Context; 55 ULONG_PTR Stack, AlignedEsp; 56 ULONG ContextLength; 57 EXCEPTION_RECORD SehExceptRecord; 58 59 /* Don't deliver APCs in V86 mode */ 60 if (TrapFrame->EFlags & EFLAGS_V86_MASK) return; 61 62 /* Save the full context */ 63 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; 64 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context); 65 66 /* Protect with SEH */ 67 _SEH2_TRY 68 { 69 /* Sanity check */ 70 ASSERT(KiUserTrap(TrapFrame)); 71 72 /* Get the aligned size */ 73 AlignedEsp = Context.Esp & ~3; 74 ContextLength = CONTEXT_ALIGNED_SIZE + (4 * sizeof(ULONG_PTR)); 75 Stack = ((AlignedEsp - 8) & ~3) - ContextLength; 76 77 /* Probe the stack */ 78 ProbeForWrite((PVOID)Stack, AlignedEsp - Stack, 1); 79 ASSERT(!(Stack & 3)); 80 81 /* Copy data into it */ 82 RtlCopyMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))), 83 &Context, 84 sizeof(CONTEXT)); 85 86 /* Run at APC dispatcher */ 87 TrapFrame->Eip = (ULONG)KeUserApcDispatcher; 88 TrapFrame->HardwareEsp = Stack; 89 90 /* Setup Ring 3 state */ 91 TrapFrame->SegCs = Ke386SanitizeSeg(KGDT_R3_CODE, UserMode); 92 TrapFrame->HardwareSegSs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode); 93 TrapFrame->SegDs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode); 94 TrapFrame->SegEs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode); 95 TrapFrame->SegFs = Ke386SanitizeSeg(KGDT_R3_TEB, UserMode); 96 TrapFrame->SegGs = 0; 97 TrapFrame->ErrCode = 0; 98 99 /* Sanitize EFLAGS */ 100 TrapFrame->EFlags = Ke386SanitizeFlags(Context.EFlags, UserMode); 101 102 /* Check if thread has IOPL and force it enabled if so */ 103 if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL; 104 105 /* Setup the stack */ 106 *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine; 107 *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext; 108 *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1; 109 *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2; 110 } 111 _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER)) 112 { 113 /* Dispatch the exception */ 114 SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Eip; 115 KiDispatchException(&SehExceptRecord, 116 ExceptionFrame, 117 TrapFrame, 118 UserMode, 119 TRUE); 120 } 121 _SEH2_END; 122 } 123 124 /* PUBLIC FUNCTIONS **********************************************************/ 125 126 /* 127 * @implemented 128 */ 129 NTSTATUS 130 NTAPI 131 KeUserModeCallback(IN ULONG RoutineIndex, 132 IN PVOID Argument, 133 IN ULONG ArgumentLength, 134 OUT PVOID *Result, 135 OUT PULONG ResultLength) 136 { 137 ULONG_PTR NewStack, OldStack; 138 PULONG UserEsp; 139 NTSTATUS CallbackStatus; 140 PEXCEPTION_REGISTRATION_RECORD ExceptionList; 141 PTEB Teb; 142 ULONG GdiBatchCount = 0; 143 ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE); 144 ASSERT(KeGetPreviousMode() == UserMode); 145 146 /* Get the current user-mode stack */ 147 UserEsp = KiGetUserModeStackAddress(); 148 OldStack = *UserEsp; 149 150 /* Enter a SEH Block */ 151 _SEH2_TRY 152 { 153 /* Calculate and align the stack size */ 154 NewStack = (OldStack - ArgumentLength) & ~3; 155 156 /* Make sure it's writable */ 157 ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)), 158 ArgumentLength + 6 * sizeof(ULONG_PTR), 159 sizeof(CHAR)); 160 161 /* Copy the buffer into the stack */ 162 RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength); 163 164 /* Write the arguments */ 165 NewStack -= 24; 166 *(PULONG)NewStack = 0; 167 *(PULONG)(NewStack + 4) = RoutineIndex; 168 *(PULONG)(NewStack + 8) = (NewStack + 24); 169 *(PULONG)(NewStack + 12) = ArgumentLength; 170 171 /* Save the exception list */ 172 Teb = KeGetCurrentThread()->Teb; 173 ExceptionList = Teb->NtTib.ExceptionList; 174 175 /* Jump to user mode */ 176 *UserEsp = NewStack; 177 CallbackStatus = KiCallUserMode(Result, ResultLength); 178 if (CallbackStatus != STATUS_CALLBACK_POP_STACK) 179 { 180 /* Only restore the exception list if we didn't crash in ring 3 */ 181 Teb->NtTib.ExceptionList = ExceptionList; 182 } 183 else 184 { 185 /* Otherwise, pop the stack */ 186 OldStack = *UserEsp; 187 } 188 189 /* Read the GDI Batch count */ 190 GdiBatchCount = Teb->GdiBatchCount; 191 } 192 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 193 { 194 /* Get the SEH exception */ 195 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 196 } 197 _SEH2_END; 198 199 /* Check if we have GDI Batch operations */ 200 if (GdiBatchCount) 201 { 202 *UserEsp -= 256; 203 KeGdiFlushUserBatch(); 204 } 205 206 /* Restore stack and return */ 207 *UserEsp = OldStack; 208 return CallbackStatus; 209 } 210 211 212 /* 213 * Stack layout for KiUserModeCallout: 214 * ---------------------------------- 215 * KCALLOUT_FRAME.ResultLength <= 2nd Parameter to KiCallUserMode 216 * KCALLOUT_FRAME.Result <= 1st Parameter to KiCallUserMode 217 * KCALLOUT_FRAME.ReturnAddress <= Return address of KiCallUserMode 218 * KCALLOUT_FRAME.Ebp \ 219 * KCALLOUT_FRAME.Ebx | = non-volatile registers, pushed 220 * KCALLOUT_FRAME.Esi | by KiCallUserMode 221 * KCALLOUT_FRAME.Edi / 222 * KCALLOUT_FRAME.CallbackStack 223 * KCALLOUT_FRAME.TrapFrame 224 * KCALLOUT_FRAME.InitialStack <= CalloutFrame points here 225 * ---------------------------------- 226 * ~~ optional alignment ~~ 227 * ---------------------------------- 228 * FX_SAVE_AREA 229 * ---------------------------------- 230 * KTRAP_FRAME 231 * ---------------------------------- 232 * ~~ begin of stack frame for KiUserModeCallout ~~ 233 * 234 */ 235 236 NTSTATUS 237 FASTCALL 238 KiUserModeCallout(PKCALLOUT_FRAME CalloutFrame) 239 { 240 PKTHREAD CurrentThread; 241 PKTRAP_FRAME TrapFrame, CallbackTrapFrame; 242 PFX_SAVE_AREA FxSaveArea, OldFxSaveArea; 243 PKPCR Pcr; 244 PKTSS Tss; 245 ULONG_PTR InitialStack; 246 NTSTATUS Status; 247 248 /* Get the current thread */ 249 CurrentThread = KeGetCurrentThread(); 250 251 #if DBG 252 /* Check if we are at pasive level */ 253 if (KeGetCurrentIrql() != PASSIVE_LEVEL) 254 { 255 /* We're not, bugcheck */ 256 KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE, 257 0, 258 KeGetCurrentIrql(), 259 0, 260 0); 261 } 262 263 /* Check if we are attached or APCs are disabled */ 264 if ((CurrentThread->ApcStateIndex != OriginalApcEnvironment) || 265 (CurrentThread->CombinedApcDisable > 0)) 266 { 267 KeBugCheckEx(APC_INDEX_MISMATCH, 268 0, 269 CurrentThread->ApcStateIndex, 270 CurrentThread->CombinedApcDisable, 271 0); 272 } 273 #endif 274 275 /* Align stack on a 16-byte boundary */ 276 InitialStack = ALIGN_DOWN_BY(CalloutFrame, 16); 277 278 /* Check if we have enough space on the stack */ 279 if ((InitialStack - KERNEL_STACK_SIZE) < CurrentThread->StackLimit) 280 { 281 /* We don't, we'll have to grow our stack */ 282 Status = MmGrowKernelStack((PVOID)InitialStack); 283 284 /* Quit if we failed */ 285 if (!NT_SUCCESS(Status)) 286 { 287 if (Status == STATUS_STACK_OVERFLOW) 288 { 289 DPRINT1("Thread wants too much stack\n"); 290 } 291 292 return Status; 293 } 294 } 295 296 /* Save the current callback stack and initial stack */ 297 CalloutFrame->CallbackStack = (ULONG_PTR)CurrentThread->CallbackStack; 298 CalloutFrame->InitialStack = (ULONG_PTR)CurrentThread->InitialStack; 299 300 /* Get and save the trap frame */ 301 TrapFrame = CurrentThread->TrapFrame; 302 CalloutFrame->TrapFrame = (ULONG_PTR)TrapFrame; 303 304 /* Set the new callback stack */ 305 CurrentThread->CallbackStack = CalloutFrame; 306 307 /* Set destination and origin NPX Areas */ 308 OldFxSaveArea = (PVOID)(CalloutFrame->InitialStack - sizeof(FX_SAVE_AREA)); 309 FxSaveArea = (PVOID)(InitialStack - sizeof(FX_SAVE_AREA)); 310 311 /* Disable interrupts so we can fill the NPX State */ 312 _disable(); 313 314 /* Now copy the NPX State */ 315 FxSaveArea->U.FnArea.ControlWord = OldFxSaveArea->U.FnArea.ControlWord; 316 FxSaveArea->U.FnArea.StatusWord = OldFxSaveArea->U.FnArea.StatusWord; 317 FxSaveArea->U.FnArea.TagWord = OldFxSaveArea->U.FnArea.TagWord; 318 FxSaveArea->U.FnArea.DataSelector = OldFxSaveArea->U.FnArea.DataSelector; 319 FxSaveArea->Cr0NpxState = OldFxSaveArea->Cr0NpxState; 320 321 /* Set the stack address */ 322 CurrentThread->InitialStack = (PVOID)InitialStack; 323 324 /* Locate the trap frame on the callback stack */ 325 CallbackTrapFrame = (PVOID)((ULONG_PTR)FxSaveArea - sizeof(KTRAP_FRAME)); 326 327 /* Copy the trap frame to the new location */ 328 *CallbackTrapFrame = *TrapFrame; 329 330 /* Get PCR */ 331 Pcr = KeGetPcr(); 332 333 /* Update the exception list */ 334 CallbackTrapFrame->ExceptionList = Pcr->NtTib.ExceptionList; 335 336 /* Get TSS */ 337 Tss = Pcr->TSS; 338 339 /* Check for V86 mode */ 340 if (CallbackTrapFrame->EFlags & EFLAGS_V86_MASK) 341 { 342 /* Set new stack address in TSS (full trap frame) */ 343 Tss->Esp0 = (ULONG_PTR)(CallbackTrapFrame + 1); 344 } 345 else 346 { 347 /* Set new stack address in TSS (non-V86 trap frame) */ 348 Tss->Esp0 = (ULONG_PTR)&CallbackTrapFrame->V86Es; 349 } 350 351 /* Set user-mode dispatcher address as EIP */ 352 CallbackTrapFrame->Eip = (ULONG_PTR)KeUserCallbackDispatcher; 353 354 /* Bring interrupts back */ 355 _enable(); 356 357 /* Exit to user-mode */ 358 KiServiceExit(CallbackTrapFrame, 0); 359 } 360 361 /*++ 362 * @name NtCallbackReturn 363 * 364 * The NtCallbackReturn routine returns to kernel mode after a user-mode 365 * callback was done through KeUserModeCallback. It uses the callback frame 366 * which was setup in order to return the information, restores the stack, 367 * and resumes execution where it was left off. 368 * 369 * @param Result 370 * Pointer to a caller-allocated buffer where the return data 371 * from the user-mode function is located. 372 * 373 * @param ResultLength 374 * Size of the Output Buffer described above. 375 * 376 * @param CallbackStatus 377 * Status code of the callback operation. 378 * 379 * @return Status code of the callback operation. 380 * 381 * @remark This call MUST be paired with KeUserModeCallback. 382 * 383 *--*/ 384 NTSTATUS 385 NTAPI 386 NtCallbackReturn( 387 _In_ PVOID Result, 388 _In_ ULONG ResultLength, 389 _In_ NTSTATUS CallbackStatus) 390 { 391 PKTHREAD CurrentThread; 392 PKCALLOUT_FRAME CalloutFrame; 393 PKTRAP_FRAME CallbackTrapFrame, TrapFrame; 394 PFX_SAVE_AREA FxSaveArea, CbFxSaveArea; 395 ULONG Size; 396 PKPCR Pcr; 397 PKTSS Tss; 398 399 /* Get the current thread and make sure we have a callback stack */ 400 CurrentThread = KeGetCurrentThread(); 401 CalloutFrame = CurrentThread->CallbackStack; 402 if (CalloutFrame == NULL) 403 { 404 return STATUS_NO_CALLBACK_ACTIVE; 405 } 406 407 /* Get the trap frame */ 408 CallbackTrapFrame = CurrentThread->TrapFrame; 409 410 /* Restore the exception list */ 411 Pcr = KeGetPcr(); 412 Pcr->NtTib.ExceptionList = CallbackTrapFrame->ExceptionList; 413 414 /* Store the results in the callback stack */ 415 *((PVOID*)CalloutFrame->Result) = Result; 416 *((ULONG*)CalloutFrame->ResultLength) = ResultLength; 417 418 /* Disable interrupts for NPX save and stack switch */ 419 _disable(); 420 421 /* Set desination and origin NPX Frames */ 422 CbFxSaveArea = (PVOID)((ULONG)CurrentThread->InitialStack - sizeof(FX_SAVE_AREA)); 423 FxSaveArea = (PVOID)(CalloutFrame->InitialStack - sizeof(FX_SAVE_AREA)); 424 425 /* Now copy back NPX State */ 426 FxSaveArea->U.FnArea.ControlWord = CbFxSaveArea->U.FnArea.ControlWord; 427 FxSaveArea->U.FnArea.StatusWord = CbFxSaveArea->U.FnArea.StatusWord; 428 FxSaveArea->U.FnArea.TagWord = CbFxSaveArea->U.FnArea.TagWord; 429 FxSaveArea->U.FnArea.DataSelector = CbFxSaveArea->U.FnArea.DataSelector; 430 FxSaveArea->Cr0NpxState = CbFxSaveArea->Cr0NpxState; 431 432 /* Get the previous trap frame */ 433 TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame; 434 435 /* Check if we failed in user mode */ 436 if (CallbackStatus == STATUS_CALLBACK_POP_STACK) 437 { 438 /* Check if we came from v86 mode */ 439 if (CallbackTrapFrame->EFlags & EFLAGS_V86_MASK) 440 { 441 Size = sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, SegFs); 442 } 443 else 444 { 445 Size = FIELD_OFFSET(KTRAP_FRAME, V86Es) - FIELD_OFFSET(KTRAP_FRAME, SegFs); 446 } 447 448 /* Copy back part of the trap frame */ 449 RtlCopyMemory(&TrapFrame->SegFs, &CallbackTrapFrame->SegFs, Size); 450 } 451 452 /* Clear DR7 */ 453 TrapFrame->Dr7 = 0; 454 455 /* Check if debugging was active */ 456 if (CurrentThread->Header.DebugActive & 0xFF) 457 { 458 /* Copy debug registers data from it */ 459 TrapFrame->Dr0 = CallbackTrapFrame->Dr0; 460 TrapFrame->Dr1 = CallbackTrapFrame->Dr1; 461 TrapFrame->Dr2 = CallbackTrapFrame->Dr2; 462 TrapFrame->Dr3 = CallbackTrapFrame->Dr3; 463 TrapFrame->Dr6 = CallbackTrapFrame->Dr6; 464 TrapFrame->Dr7 = CallbackTrapFrame->Dr7; 465 } 466 467 /* Get TSS */ 468 Tss = Pcr->TSS; 469 470 /* Check for V86 mode */ 471 if (TrapFrame->EFlags & EFLAGS_V86_MASK) 472 { 473 /* Set new stack address in TSS (full trap frame) */ 474 Tss->Esp0 = (ULONG_PTR)(TrapFrame + 1); 475 } 476 else 477 { 478 /* Set new stack address in TSS (non-V86 trap frame) */ 479 Tss->Esp0 = (ULONG_PTR)&TrapFrame->V86Es; 480 } 481 482 /* Get the initial stack and restore it */ 483 CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack; 484 485 /* Restore the trap frame and the previous callback stack */ 486 CurrentThread->TrapFrame = TrapFrame; 487 CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack; 488 489 /* Bring interrupts back */ 490 _enable(); 491 492 /* Now switch back to the old stack */ 493 KiCallbackReturn(&CalloutFrame->Edi, CallbackStatus); 494 } 495 496 497 /* EOF */ 498