1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ps/debug.c 5 * PURPOSE: Process Manager: Debugging Support (Set/Get Context) 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Thomas Weidenmueller (w3seek@reactos.org) 8 */ 9 10 /* INCLUDES ****************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* PRIVATE FUNCTIONS *********************************************************/ 17 18 #if DBG 19 VOID 20 NTAPI 21 PspDumpThreads(BOOLEAN IncludeSystem) 22 { 23 PLIST_ENTRY CurrentThread, CurrentProcess; 24 PEPROCESS Process; 25 PETHREAD Thread; 26 ULONG nThreads = 0; 27 28 /* Loop all Active Processes */ 29 CurrentProcess = PsActiveProcessHead.Flink; 30 while(CurrentProcess != &PsActiveProcessHead) 31 { 32 /* Get the process */ 33 Process = CONTAINING_RECORD(CurrentProcess, EPROCESS, ActiveProcessLinks); 34 35 /* Skip the Initial Process if requested */ 36 if((Process != PsInitialSystemProcess) || 37 (Process == PsInitialSystemProcess && IncludeSystem)) 38 { 39 /* Loop all its threads */ 40 CurrentThread = Process->ThreadListHead.Flink; 41 while(CurrentThread != &Process->ThreadListHead) 42 { 43 44 /* Get teh Thread */ 45 Thread = CONTAINING_RECORD(CurrentThread, ETHREAD, ThreadListEntry); 46 nThreads++; 47 48 /* Print the Info */ 49 DbgPrint("State %u Affinity %08x Priority %d PID.TID %d.%d Name %.8s Stack:\n", 50 Thread->Tcb.State, 51 Thread->Tcb.Affinity, 52 Thread->Tcb.Priority, 53 Thread->Cid.UniqueProcess, 54 Thread->Cid.UniqueThread, 55 Thread->ThreadsProcess->ImageFileName); 56 57 /* Make sure it's not running */ 58 if(Thread->Tcb.State == Ready || 59 Thread->Tcb.State == Standby || 60 Thread->Tcb.State == Waiting) 61 { 62 #ifdef _M_IX86 63 ULONG i = 0; 64 PULONG Esp = (PULONG)Thread->Tcb.KernelStack; 65 PULONG Ebp = (PULONG)Esp[4]; 66 67 /* Print EBP */ 68 DbgPrint("Ebp %p\n", Ebp); 69 70 /* Walk it */ 71 while(Ebp != 0 && Ebp >= (PULONG)Thread->Tcb.StackLimit) 72 { 73 ULONG EbpContent[2]; 74 ULONG MemoryCopied; 75 NTSTATUS Status; 76 77 /* Get stack frame content */ 78 Status = KdpCopyMemoryChunks((ULONG64)(ULONG_PTR)Ebp, 79 EbpContent, 80 sizeof(EbpContent), 81 sizeof(EbpContent), 82 MMDBG_COPY_UNSAFE, 83 &MemoryCopied); 84 if (!NT_SUCCESS(Status) || (MemoryCopied < sizeof(EbpContent))) 85 { 86 break; 87 } 88 89 DbgPrint("%.8X %.8X%s", EbpContent[0], EbpContent[1], (i % 8) == 7 ? "\n" : " "); 90 Ebp = (PULONG)EbpContent[0]; 91 i++; 92 } 93 94 /* Print a new line if there's nothing */ 95 if((i % 8) != 0) DbgPrint("\n"); 96 #else 97 DbgPrint("FIXME: Backtrace skipped on non-x86\n"); 98 #endif 99 } 100 101 /* Move to the next Thread */ 102 CurrentThread = CurrentThread->Flink; 103 } 104 } 105 106 /* Move to the next Process */ 107 CurrentProcess = CurrentProcess->Flink; 108 } 109 } 110 #endif 111 112 /* PUBLIC FUNCTIONS **********************************************************/ 113 114 /* 115 * @implemented 116 */ 117 NTSTATUS 118 NTAPI 119 PsGetContextThread(IN PETHREAD Thread, 120 IN OUT PCONTEXT ThreadContext, 121 IN KPROCESSOR_MODE PreviousMode) 122 { 123 GET_SET_CTX_CONTEXT GetSetContext; 124 ULONG Size = 0, Flags = 0; 125 NTSTATUS Status; 126 127 /* Enter SEH */ 128 _SEH2_TRY 129 { 130 /* Set default length */ 131 Size = sizeof(CONTEXT); 132 133 /* Read the flags */ 134 Flags = ProbeForReadUlong(&ThreadContext->ContextFlags); 135 136 #ifdef _M_IX86 137 /* Check if the caller wanted extended registers */ 138 if ((Flags & CONTEXT_EXTENDED_REGISTERS) != 139 CONTEXT_EXTENDED_REGISTERS) 140 { 141 /* Cut them out of the size */ 142 Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters); 143 } 144 #endif 145 146 /* Check if we came from user mode */ 147 if (PreviousMode != KernelMode) 148 { 149 /* Probe the context */ 150 ProbeForWrite(ThreadContext, Size, sizeof(ULONG)); 151 } 152 } 153 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 154 { 155 /* Return the exception code */ 156 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 157 } 158 _SEH2_END; 159 160 /* Initialize the wait event */ 161 KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE); 162 163 /* Set the flags and previous mode */ 164 RtlZeroMemory(&GetSetContext.Context, Size); 165 GetSetContext.Context.ContextFlags = Flags; 166 GetSetContext.Mode = PreviousMode; 167 168 /* Check if we're running in the same thread */ 169 if (Thread == PsGetCurrentThread()) 170 { 171 /* Setup APC parameters manually */ 172 GetSetContext.Apc.SystemArgument1 = NULL; 173 GetSetContext.Apc.SystemArgument2 = Thread; 174 175 /* Enter a guarded region to simulate APC_LEVEL */ 176 KeEnterGuardedRegion(); 177 178 /* Manually call the APC */ 179 PspGetOrSetContextKernelRoutine(&GetSetContext.Apc, 180 NULL, 181 NULL, 182 &GetSetContext.Apc.SystemArgument1, 183 &GetSetContext.Apc.SystemArgument2); 184 185 /* Leave the guarded region */ 186 KeLeaveGuardedRegion(); 187 188 /* We are done */ 189 Status = STATUS_SUCCESS; 190 } 191 else 192 { 193 /* Initialize the APC */ 194 KeInitializeApc(&GetSetContext.Apc, 195 &Thread->Tcb, 196 OriginalApcEnvironment, 197 PspGetOrSetContextKernelRoutine, 198 NULL, 199 NULL, 200 KernelMode, 201 NULL); 202 203 /* Queue it as a Get APC */ 204 if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2)) 205 { 206 /* It was already queued, so fail */ 207 Status = STATUS_UNSUCCESSFUL; 208 } 209 else 210 { 211 /* Wait for the APC to complete */ 212 Status = KeWaitForSingleObject(&GetSetContext.Event, 213 0, 214 KernelMode, 215 FALSE, 216 NULL); 217 } 218 } 219 220 _SEH2_TRY 221 { 222 /* Copy the context */ 223 RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size); 224 } 225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 226 { 227 /* Get the exception code */ 228 Status = _SEH2_GetExceptionCode(); 229 } 230 _SEH2_END; 231 232 /* Return status */ 233 return Status; 234 } 235 236 /* 237 * @implemented 238 */ 239 NTSTATUS 240 NTAPI 241 PsSetContextThread(IN PETHREAD Thread, 242 IN OUT PCONTEXT ThreadContext, 243 IN KPROCESSOR_MODE PreviousMode) 244 { 245 GET_SET_CTX_CONTEXT GetSetContext; 246 ULONG Size = 0, Flags = 0; 247 NTSTATUS Status; 248 249 /* Enter SEH */ 250 _SEH2_TRY 251 { 252 /* Set default length */ 253 Size = sizeof(CONTEXT); 254 255 /* Read the flags */ 256 Flags = ProbeForReadUlong(&ThreadContext->ContextFlags); 257 258 #ifdef _M_IX86 259 /* Check if the caller wanted extended registers */ 260 if ((Flags & CONTEXT_EXTENDED_REGISTERS) != 261 CONTEXT_EXTENDED_REGISTERS) 262 { 263 /* Cut them out of the size */ 264 Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters); 265 } 266 #endif 267 268 /* Check if we came from user mode */ 269 if (PreviousMode != KernelMode) 270 { 271 /* Probe the context */ 272 ProbeForRead(ThreadContext, Size, sizeof(ULONG)); 273 } 274 275 /* Copy the context */ 276 RtlCopyMemory(&GetSetContext.Context, ThreadContext, Size); 277 } 278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 279 { 280 /* Return the exception code */ 281 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 282 } 283 _SEH2_END; 284 285 /* Initialize the wait event */ 286 KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE); 287 288 /* Set the flags and previous mode */ 289 GetSetContext.Context.ContextFlags = Flags; 290 GetSetContext.Mode = PreviousMode; 291 292 /* Check if we're running in the same thread */ 293 if (Thread == PsGetCurrentThread()) 294 { 295 /* Setup APC parameters manually */ 296 GetSetContext.Apc.SystemArgument1 = UlongToPtr(1); 297 GetSetContext.Apc.SystemArgument2 = Thread; 298 299 /* Enter a guarded region to simulate APC_LEVEL */ 300 KeEnterGuardedRegion(); 301 302 /* Manually call the APC */ 303 PspGetOrSetContextKernelRoutine(&GetSetContext.Apc, 304 NULL, 305 NULL, 306 &GetSetContext.Apc.SystemArgument1, 307 &GetSetContext.Apc.SystemArgument2); 308 309 /* Leave the guarded region */ 310 KeLeaveGuardedRegion(); 311 312 /* We are done */ 313 Status = STATUS_SUCCESS; 314 } 315 else 316 { 317 /* Initialize the APC */ 318 KeInitializeApc(&GetSetContext.Apc, 319 &Thread->Tcb, 320 OriginalApcEnvironment, 321 PspGetOrSetContextKernelRoutine, 322 NULL, 323 NULL, 324 KernelMode, 325 NULL); 326 327 /* Queue it as a Get APC */ 328 if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2)) 329 { 330 /* It was already queued, so fail */ 331 Status = STATUS_UNSUCCESSFUL; 332 } 333 else 334 { 335 /* Wait for the APC to complete */ 336 Status = KeWaitForSingleObject(&GetSetContext.Event, 337 0, 338 KernelMode, 339 FALSE, 340 NULL); 341 } 342 } 343 344 /* Return status */ 345 return Status; 346 } 347 348 NTSTATUS 349 NTAPI 350 NtGetContextThread(IN HANDLE ThreadHandle, 351 IN OUT PCONTEXT ThreadContext) 352 { 353 PETHREAD Thread; 354 NTSTATUS Status; 355 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 356 PAGED_CODE(); 357 358 /* Get the Thread Object */ 359 Status = ObReferenceObjectByHandle(ThreadHandle, 360 THREAD_GET_CONTEXT, 361 PsThreadType, 362 PreviousMode, 363 (PVOID*)&Thread, 364 NULL); 365 366 if (!NT_SUCCESS(Status)) return Status; 367 368 /* Make sure it's not a system thread */ 369 if (Thread->SystemThread) 370 { 371 /* Fail */ 372 Status = STATUS_INVALID_HANDLE; 373 } 374 else 375 { 376 /* Call the kernel API */ 377 Status = PsGetContextThread(Thread, ThreadContext, PreviousMode); 378 } 379 380 /* Dereference it and return */ 381 ObDereferenceObject(Thread); 382 return Status; 383 } 384 385 NTSTATUS 386 NTAPI 387 NtSetContextThread(IN HANDLE ThreadHandle, 388 IN PCONTEXT ThreadContext) 389 { 390 PETHREAD Thread; 391 NTSTATUS Status; 392 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 393 PAGED_CODE(); 394 395 /* Get the Thread Object */ 396 Status = ObReferenceObjectByHandle(ThreadHandle, 397 THREAD_SET_CONTEXT, 398 PsThreadType, 399 PreviousMode, 400 (PVOID*)&Thread, 401 NULL); 402 403 if (!NT_SUCCESS(Status)) return Status; 404 405 /* Make sure it's not a system thread */ 406 if (Thread->SystemThread) 407 { 408 /* Fail */ 409 Status = STATUS_INVALID_HANDLE; 410 } 411 else 412 { 413 /* Call the kernel API */ 414 Status = PsSetContextThread(Thread, ThreadContext, PreviousMode); 415 } 416 417 /* Dereference it and return */ 418 ObDereferenceObject(Thread); 419 return Status; 420 } 421 422 /* EOF */ 423