- Don't use EXPORTED
[reactos.git] / reactos / ntoskrnl / ps / thread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Thread managment
6 *
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
8 * Phillip Susi
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS ******************************************************************/
18
19 extern LIST_ENTRY PsActiveProcessHead;
20 extern PEPROCESS PsIdleProcess;
21 extern PVOID PspSystemDllEntryPoint;
22 extern PHANDLE_TABLE PspCidTable;
23
24 POBJECT_TYPE PsThreadType = NULL;
25
26 /* FUNCTIONS ***************************************************************/
27
28 VOID
29 STDCALL
30 PspThreadSpecialApc(PKAPC Apc,
31 PKNORMAL_ROUTINE* NormalRoutine,
32 PVOID* NormalContext,
33 PVOID* SystemArgument1,
34 PVOID* SystemArgument2)
35 {
36 ExFreePool(Apc);
37 }
38
39 VOID
40 STDCALL
41 PspUserThreadStartup(PKSTART_ROUTINE StartRoutine,
42 PVOID StartContext)
43 {
44 PKAPC ThreadApc;
45 PETHREAD Thread = PsGetCurrentThread();
46
47 DPRINT("I am a new USER thread. This is my start routine: %p. This my context: %p."
48 "This is my IRQL: %d. This is my Thread Pointer: %x.\n", StartRoutine,
49 StartContext, KeGetCurrentIrql(), Thread);
50
51 if (!Thread->Terminated) {
52
53 /* Allocate the APC */
54 ThreadApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('T', 'h', 'r','d'));
55
56 /* Initialize it */
57 KeInitializeApc(ThreadApc,
58 &Thread->Tcb,
59 OriginalApcEnvironment,
60 PspThreadSpecialApc,
61 NULL,
62 PspSystemDllEntryPoint,
63 UserMode,
64 NULL);
65
66 /* Insert it into the queue */
67 KeInsertQueueApc(ThreadApc, NULL, NULL, IO_NO_INCREMENT);
68 Thread->Tcb.ApcState.UserApcPending = TRUE;
69 }
70
71 /* Go to Passive Level and notify debugger */
72 KeLowerIrql(PASSIVE_LEVEL);
73 DbgkCreateThread(StartContext);
74 }
75
76 VOID
77 STDCALL
78 PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine,
79 PVOID StartContext)
80 {
81 PETHREAD Thread = PsGetCurrentThread();
82
83 /* Unlock the dispatcher Database */
84 KeLowerIrql(PASSIVE_LEVEL);
85
86 /* Make sure it's not terminated by now */
87 if (!Thread->Terminated) {
88
89 /* Call it */
90 (StartRoutine)(StartContext);
91 }
92
93 /* Exit the thread */
94 PspExitThread(STATUS_SUCCESS);
95 }
96
97 NTSTATUS
98 STDCALL
99 PspCreateThread(OUT PHANDLE ThreadHandle,
100 IN ACCESS_MASK DesiredAccess,
101 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
102 IN HANDLE ProcessHandle,
103 IN PEPROCESS TargetProcess,
104 OUT PCLIENT_ID ClientId,
105 IN PCONTEXT ThreadContext,
106 IN PINITIAL_TEB InitialTeb,
107 IN BOOLEAN CreateSuspended,
108 IN PKSTART_ROUTINE StartRoutine OPTIONAL,
109 IN PVOID StartContext OPTIONAL)
110 {
111 HANDLE hThread;
112 PEPROCESS Process;
113 PETHREAD Thread;
114 PTEB TebBase;
115 KIRQL OldIrql;
116 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
117 NTSTATUS Status;
118 HANDLE_TABLE_ENTRY CidEntry;
119 PVOID KernelStack;
120
121 /* Reference the Process by handle or pointer, depending on what we got */
122 DPRINT("PspCreateThread: %x, %x, %x\n", ProcessHandle, TargetProcess, ThreadContext);
123 if (ProcessHandle) {
124
125 /* Normal thread or System Thread */
126 DPRINT("Referencing Parent Process\n");
127 Status = ObReferenceObjectByHandle(ProcessHandle,
128 PROCESS_CREATE_THREAD,
129 PsProcessType,
130 PreviousMode,
131 (PVOID*)&Process,
132 NULL);
133 } else {
134
135 /* System thread inside System Process, or Normal Thread with a bug */
136 if (StartRoutine) {
137
138 /* Reference the Process by Pointer */
139 DPRINT("Referencing Parent System Process\n");
140 ObReferenceObject(TargetProcess);
141 Process = TargetProcess;
142 Status = STATUS_SUCCESS;
143
144 } else {
145
146 /* Fake ObReference returning this */
147 Status = STATUS_INVALID_HANDLE;
148 }
149 }
150
151 /* Check for success */
152 if(!NT_SUCCESS(Status)) {
153
154 DPRINT1("Invalid Process Handle, or no handle given\n");
155 return(Status);
156 }
157
158 /* Create Thread Object */
159 DPRINT("Creating Thread Object\n");
160 Status = ObCreateObject(PreviousMode,
161 PsThreadType,
162 ObjectAttributes,
163 KernelMode,
164 NULL,
165 sizeof(ETHREAD),
166 0,
167 0,
168 (PVOID*)&Thread);
169
170 /* Check for success */
171 if (!NT_SUCCESS(Status)) {
172
173 /* Dereference the Process */
174 DPRINT1("Failed to Create Thread Object\n");
175 ObDereferenceObject(Process);
176 return(Status);
177 }
178
179 /* Zero the Object entirely */
180 DPRINT("Cleaning Thread Object\n");
181 RtlZeroMemory(Thread, sizeof(ETHREAD));
182
183 /* Create Cid Handle */
184 DPRINT("Creating Thread Handle (CID)\n");
185 CidEntry.u1.Object = Thread;
186 CidEntry.u2.GrantedAccess = 0;
187 Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
188 if (!Thread->Cid.UniqueThread) {
189
190 DPRINT1("Failed to create Thread Handle (CID)\n");
191 ObDereferenceObject(Process);
192 ObDereferenceObject(Thread);
193 return STATUS_INSUFFICIENT_RESOURCES;
194 }
195
196 /* Initialize Lists */
197 DPRINT("Initialliazing Thread Lists and Locks\n");
198 InitializeListHead(&Thread->LpcReplyChain);
199 InitializeListHead(&Thread->IrpList);
200 InitializeListHead(&Thread->ActiveTimerListHead);
201 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
202
203 /* Initialize LPC */
204 DPRINT("Initialliazing Thread Semaphore\n");
205 KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, MAXLONG);
206
207 /* Allocate Stack for non-GUI Thread */
208 DPRINT("Initialliazing Thread Stack\n");
209 KernelStack = MmCreateKernelStack(FALSE);
210
211 /* Set the Process CID */
212 DPRINT("Initialliazing Thread PID and Parent Process\n");
213 Thread->Cid.UniqueProcess = Process->UniqueProcessId;
214 Thread->ThreadsProcess = Process;
215
216 /* Now let the kernel initialize the context */
217 if (ThreadContext) {
218
219 /* User-mode Thread */
220
221 /* Create Teb */
222 DPRINT("Initialliazing Thread PEB\n");
223 TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb);
224
225 /* Set the Start Addresses */
226 DPRINT("Initialliazing Thread Start Addresses :%x, %x\n", ThreadContext->Eip, ThreadContext->Eax);
227 Thread->StartAddress = (PVOID)ThreadContext->Eip;
228 Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
229
230 /* Let the kernel intialize the Thread */
231 DPRINT("Initialliazing Kernel Thread\n");
232 KeInitializeThread(&Process->Pcb,
233 &Thread->Tcb,
234 PspUserThreadStartup,
235 NULL,
236 NULL,
237 ThreadContext,
238 TebBase,
239 KernelStack);
240
241 } else {
242
243 /* System Thread */
244 DPRINT("Initialliazing Thread Start Address :%x\n", StartRoutine);
245 Thread->StartAddress = StartRoutine;
246 Thread->SystemThread = TRUE;
247
248 /* Let the kernel intialize the Thread */
249 DPRINT("Initialliazing Kernel Thread\n");
250 KeInitializeThread(&Process->Pcb,
251 &Thread->Tcb,
252 PspSystemThreadStartup,
253 StartRoutine,
254 StartContext,
255 NULL,
256 NULL,
257 KernelStack);
258 }
259
260 /*
261 * Insert the Thread into the Process's Thread List
262 * Note, this is the ETHREAD Thread List. It is removed in
263 * ps/kill.c!PspExitThread.
264 */
265 DPRINT("Inserting into Process Thread List \n");
266 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
267
268 /* Notify Thread Creation */
269 DPRINT("Running Thread Notify \n");
270 PspRunCreateThreadNotifyRoutines(Thread, TRUE);
271
272 /* FIXME: Use Lock */
273 DPRINT("Apcs Queueable: %d \n", Thread->Tcb.ApcQueueable);
274 Thread->Tcb.ApcQueueable = TRUE;
275
276 /* Suspend the Thread if we have to */
277 if (CreateSuspended) {
278
279 DPRINT("Suspending Thread\n");
280 KeSuspendThread(&Thread->Tcb);
281 }
282
283 /* Reference ourselves as a keep-alive */
284 ObReferenceObject(Thread);
285
286 /* Insert the Thread into the Object Manager */
287 DPRINT("Inserting Thread\n");
288 Status = ObInsertObject((PVOID)Thread,
289 NULL,
290 DesiredAccess,
291 0,
292 NULL,
293 &hThread);
294
295 /* Return Cid and Handle */
296 DPRINT("All worked great!\n");
297 if(NT_SUCCESS(Status)) {
298
299 _SEH_TRY {
300
301 if(ClientId != NULL) {
302
303 *ClientId = Thread->Cid;
304 }
305 *ThreadHandle = hThread;
306
307 } _SEH_HANDLE {
308
309 Status = _SEH_GetExceptionCode();
310
311 } _SEH_END;
312 }
313
314 /* FIXME: SECURITY */
315
316 /* Dispatch thread */
317 DPRINT("About to dispatch the thread: %x!\n", &Thread->Tcb);
318 OldIrql = KeAcquireDispatcherDatabaseLock ();
319 KiUnblockThread(&Thread->Tcb, NULL, 0);
320 ObDereferenceObject(Thread);
321 KeReleaseDispatcherDatabaseLock(OldIrql);
322
323 /* Return */
324 DPRINT("Returning\n");
325 return Status;
326 }
327
328 /*
329 * @implemented
330 */
331 NTSTATUS
332 STDCALL
333 PsCreateSystemThread(PHANDLE ThreadHandle,
334 ACCESS_MASK DesiredAccess,
335 POBJECT_ATTRIBUTES ObjectAttributes,
336 HANDLE ProcessHandle,
337 PCLIENT_ID ClientId,
338 PKSTART_ROUTINE StartRoutine,
339 PVOID StartContext)
340 {
341 PEPROCESS TargetProcess = NULL;
342 HANDLE Handle = ProcessHandle;
343
344 /* Check if we have a handle. If not, use the System Process */
345 if (!ProcessHandle) {
346
347 Handle = NULL;
348 TargetProcess = PsInitialSystemProcess;
349 }
350
351 /* Call the shared function */
352 return PspCreateThread(ThreadHandle,
353 DesiredAccess,
354 ObjectAttributes,
355 Handle,
356 TargetProcess,
357 ClientId,
358 NULL,
359 NULL,
360 FALSE,
361 StartRoutine,
362 StartContext);
363 }
364
365 /*
366 * @implemented
367 */
368 NTSTATUS
369 STDCALL
370 PsLookupThreadByThreadId(IN HANDLE ThreadId,
371 OUT PETHREAD *Thread)
372 {
373 PHANDLE_TABLE_ENTRY CidEntry;
374 PETHREAD FoundThread;
375 NTSTATUS Status = STATUS_INVALID_PARAMETER;
376 PAGED_CODE();
377
378 KeEnterCriticalRegion();
379
380 /* Get the CID Handle Entry */
381 if ((CidEntry = ExMapHandleToPointer(PspCidTable,
382 ThreadId)))
383 {
384 /* Get the Process */
385 FoundThread = CidEntry->u1.Object;
386
387 /* Make sure it's really a process */
388 if (FoundThread->Tcb.DispatcherHeader.Type == ThreadObject)
389 {
390 /* Reference and return it */
391 ObReferenceObject(FoundThread);
392 *Thread = FoundThread;
393 Status = STATUS_SUCCESS;
394 }
395
396 /* Unlock the Entry */
397 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
398 }
399
400 KeLeaveCriticalRegion();
401
402 /* Return to caller */
403 return Status;
404 }
405
406 /*
407 * @implemented
408 */
409 HANDLE
410 STDCALL
411 PsGetCurrentThreadId(VOID)
412 {
413 return(PsGetCurrentThread()->Cid.UniqueThread);
414 }
415
416 /*
417 * @implemented
418 */
419 ULONG
420 STDCALL
421 PsGetThreadFreezeCount(PETHREAD Thread)
422 {
423 return Thread->Tcb.FreezeCount;
424 }
425
426 /*
427 * @implemented
428 */
429 BOOLEAN
430 STDCALL
431 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
432 {
433 return Thread->HardErrorsAreDisabled;
434 }
435
436 /*
437 * @implemented
438 */
439 HANDLE
440 STDCALL
441 PsGetThreadId(PETHREAD Thread)
442 {
443 return Thread->Cid.UniqueThread;
444 }
445
446 /*
447 * @implemented
448 */
449 PEPROCESS
450 STDCALL
451 PsGetThreadProcess(PETHREAD Thread)
452 {
453 return Thread->ThreadsProcess;
454 }
455
456 /*
457 * @implemented
458 */
459 HANDLE
460 STDCALL
461 PsGetThreadProcessId(PETHREAD Thread)
462 {
463 return Thread->Cid.UniqueProcess;
464 }
465
466 /*
467 * @implemented
468 */
469 HANDLE
470 STDCALL
471 PsGetThreadSessionId(PETHREAD Thread)
472 {
473 return (HANDLE)Thread->ThreadsProcess->Session;
474 }
475
476 /*
477 * @implemented
478 */
479 PTEB
480 STDCALL
481 PsGetThreadTeb(PETHREAD Thread)
482 {
483 return Thread->Tcb.Teb;
484 }
485
486 /*
487 * @implemented
488 */
489 PVOID
490 STDCALL
491 PsGetThreadWin32Thread(PETHREAD Thread)
492 {
493 return Thread->Tcb.Win32Thread;
494 }
495
496 /*
497 * @implemented
498 */
499 KPROCESSOR_MODE
500 STDCALL
501 PsGetCurrentThreadPreviousMode(VOID)
502 {
503 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
504 }
505
506 /*
507 * @implemented
508 */
509 PVOID
510 STDCALL
511 PsGetCurrentThreadStackBase(VOID)
512 {
513 return PsGetCurrentThread()->Tcb.StackBase;
514 }
515
516 /*
517 * @implemented
518 */
519 PVOID
520 STDCALL
521 PsGetCurrentThreadStackLimit(VOID)
522 {
523 return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
524 }
525
526 /*
527 * @implemented
528 */
529 BOOLEAN
530 STDCALL
531 PsIsThreadTerminating(IN PETHREAD Thread)
532 {
533 return (Thread->Terminated ? TRUE : FALSE);
534 }
535
536 /*
537 * @implemented
538 */
539 BOOLEAN
540 STDCALL
541 PsIsSystemThread(PETHREAD Thread)
542 {
543 return (Thread->SystemThread ? TRUE: FALSE);
544 }
545
546 /*
547 * @implemented
548 */
549 BOOLEAN
550 STDCALL
551 PsIsThreadImpersonating(PETHREAD Thread)
552 {
553 return Thread->ActiveImpersonationInfo;
554 }
555
556 /*
557 * @implemented
558 */
559 VOID
560 STDCALL
561 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
562 BOOLEAN HardErrorsAreDisabled)
563 {
564 Thread->HardErrorsAreDisabled = HardErrorsAreDisabled;
565 }
566
567 /*
568 * @implemented
569 */
570 VOID
571 STDCALL
572 PsSetThreadWin32Thread(PETHREAD Thread,
573 PVOID Win32Thread)
574 {
575 Thread->Tcb.Win32Thread = Win32Thread;
576 }
577
578 NTSTATUS
579 STDCALL
580 NtCreateThread(OUT PHANDLE ThreadHandle,
581 IN ACCESS_MASK DesiredAccess,
582 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
583 IN HANDLE ProcessHandle,
584 OUT PCLIENT_ID ClientId,
585 IN PCONTEXT ThreadContext,
586 IN PINITIAL_TEB InitialTeb,
587 IN BOOLEAN CreateSuspended)
588 {
589 INITIAL_TEB SafeInitialTeb;
590 CONTEXT SafeContext;
591 NTSTATUS Status = STATUS_SUCCESS;
592
593 PAGED_CODE();
594
595 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
596 ThreadHandle,ThreadContext);
597
598 if(KeGetPreviousMode() != KernelMode) {
599
600 if (ThreadContext == NULL) {
601 DPRINT1("No context for User-Mode Thread!!\n");
602 return STATUS_INVALID_PARAMETER;
603 }
604
605 _SEH_TRY {
606
607 ProbeForWriteHandle(ThreadHandle);
608
609 if(ClientId != NULL) {
610
611 ProbeForWrite(ClientId,
612 sizeof(CLIENT_ID),
613 sizeof(ULONG));
614 }
615
616 if(ThreadContext != NULL) {
617
618 ProbeForRead(ThreadContext,
619 sizeof(CONTEXT),
620 sizeof(ULONG));
621 SafeContext = *ThreadContext;
622 ThreadContext = &SafeContext;
623 }
624
625 ProbeForRead(InitialTeb,
626 sizeof(INITIAL_TEB),
627 sizeof(ULONG));
628 SafeInitialTeb = *InitialTeb;
629 InitialTeb = &SafeInitialTeb;
630
631 } _SEH_HANDLE {
632
633 Status = _SEH_GetExceptionCode();
634
635 } _SEH_END;
636
637 if (!NT_SUCCESS(Status)) return Status;
638 }
639
640 /* Call the shared function */
641 return PspCreateThread(ThreadHandle,
642 DesiredAccess,
643 ObjectAttributes,
644 ProcessHandle,
645 NULL,
646 ClientId,
647 ThreadContext,
648 InitialTeb,
649 CreateSuspended,
650 NULL,
651 NULL);
652 }
653
654 /*
655 * @implemented
656 */
657 NTSTATUS
658 STDCALL
659 NtOpenThread(OUT PHANDLE ThreadHandle,
660 IN ACCESS_MASK DesiredAccess,
661 IN POBJECT_ATTRIBUTES ObjectAttributes,
662 IN PCLIENT_ID ClientId OPTIONAL)
663 {
664 KPROCESSOR_MODE PreviousMode;
665 CLIENT_ID SafeClientId;
666 ULONG Attributes = 0;
667 HANDLE hThread = NULL;
668 NTSTATUS Status = STATUS_SUCCESS;
669 PETHREAD Thread;
670 BOOLEAN HasObjectName = FALSE;
671
672 PAGED_CODE();
673
674 PreviousMode = KeGetPreviousMode();
675
676 /* Probe the paraemeters */
677 if(PreviousMode != KernelMode)
678 {
679 _SEH_TRY
680 {
681 ProbeForWriteHandle(ThreadHandle);
682
683 if(ClientId != NULL)
684 {
685 ProbeForRead(ClientId,
686 sizeof(CLIENT_ID),
687 sizeof(ULONG));
688
689 SafeClientId = *ClientId;
690 ClientId = &SafeClientId;
691 }
692
693 /* just probe the object attributes structure, don't capture it
694 completely. This is done later if necessary */
695 ProbeForRead(ObjectAttributes,
696 sizeof(OBJECT_ATTRIBUTES),
697 sizeof(ULONG));
698 HasObjectName = (ObjectAttributes->ObjectName != NULL);
699 Attributes = ObjectAttributes->Attributes;
700 }
701 _SEH_HANDLE
702 {
703 Status = _SEH_GetExceptionCode();
704 }
705 _SEH_END;
706
707 if(!NT_SUCCESS(Status)) return Status;
708 }
709 else
710 {
711 HasObjectName = (ObjectAttributes->ObjectName != NULL);
712 Attributes = ObjectAttributes->Attributes;
713 }
714
715 if (HasObjectName && ClientId != NULL)
716 {
717 /* can't pass both, n object name and a client id */
718 return STATUS_INVALID_PARAMETER_MIX;
719 }
720
721 /* Open by name if one was given */
722 if (HasObjectName)
723 {
724 /* Open it */
725 Status = ObOpenObjectByName(ObjectAttributes,
726 PsThreadType,
727 NULL,
728 PreviousMode,
729 DesiredAccess,
730 NULL,
731 &hThread);
732
733 if (!NT_SUCCESS(Status))
734 {
735 DPRINT1("Could not open object by name\n");
736 }
737 }
738 else if (ClientId != NULL)
739 {
740 /* Open by Thread ID */
741 if (ClientId->UniqueProcess)
742 {
743 /* Get the Process */
744 DPRINT("Opening by Process ID: %x\n", ClientId->UniqueProcess);
745 Status = PsLookupProcessThreadByCid(ClientId,
746 NULL,
747 &Thread);
748 }
749 else
750 {
751 /* Get the Process */
752 DPRINT("Opening by Thread ID: %x\n", ClientId->UniqueThread);
753 Status = PsLookupThreadByThreadId(ClientId->UniqueThread,
754 &Thread);
755 }
756
757 if(!NT_SUCCESS(Status))
758 {
759 DPRINT1("Failure to find Thread\n");
760 return Status;
761 }
762
763 /* Open the Thread Object */
764 Status = ObOpenObjectByPointer(Thread,
765 Attributes,
766 NULL,
767 DesiredAccess,
768 PsThreadType,
769 PreviousMode,
770 &hThread);
771 if(!NT_SUCCESS(Status))
772 {
773 DPRINT1("Failure to open Thread\n");
774 }
775
776 /* Dereference the thread */
777 ObDereferenceObject(Thread);
778 }
779 else
780 {
781 /* neither an object name nor a client id was passed */
782 return STATUS_INVALID_PARAMETER_MIX;
783 }
784
785 /* Write back the handle */
786 if(NT_SUCCESS(Status))
787 {
788 _SEH_TRY
789 {
790 *ThreadHandle = hThread;
791 }
792 _SEH_HANDLE
793 {
794 Status = _SEH_GetExceptionCode();
795 }
796 _SEH_END;
797 }
798
799 /* Return status */
800 return Status;
801 }
802
803 NTSTATUS
804 STDCALL
805 NtYieldExecution(VOID)
806 {
807 KiDispatchThread(Ready);
808 return(STATUS_SUCCESS);
809 }
810
811 NTSTATUS
812 STDCALL
813 NtTestAlert(VOID)
814 {
815 /* Check and Alert Thread if needed */
816 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED : STATUS_SUCCESS;
817 }
818
819 /*
820 * @implemented
821 */
822 KPROCESSOR_MODE
823 STDCALL
824 ExGetPreviousMode (VOID)
825 {
826 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
827 }
828
829 /* EOF */