2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/x86/thread.c
5 * PURPOSE: HAL multitasking functions
6 * PROGRAMMER: David Welch (welch@cwcom.net)
11 /* INCLUDES ****************************************************************/
13 #include <ddk/ntddk.h>
14 #include <internal/ntoskrnl.h>
15 #include <internal/ps.h>
17 #include <internal/string.h>
18 #include <internal/hal.h>
19 #include <internal/i386/segment.h>
20 #include <internal/mmhal.h>
21 #include <internal/ke.h>
24 #include <internal/debug.h>
26 /* GLOBALS ***************************************************************/
30 #define FIRST_TSS_SELECTOR (KERNEL_DS + 0x8)
31 #define FIRST_TSS_OFFSET (FIRST_TSS_SELECTOR / 8)
33 static char KiNullLdt
[8] = {0,};
34 static unsigned int KiNullLdtSel
= 0;
35 static PETHREAD FirstThread
= NULL
;
37 extern ULONG
KeSetBaseGdtSelector(ULONG Entry
, PVOID Base
);
39 /* FUNCTIONS **************************************************************/
41 VOID
HalTaskSwitch(PKTHREAD thread
)
43 * FUNCTION: Switch tasks
45 * thread = Thread to switch to
46 * NOTE: This function will not return until the current thread is scheduled
47 * again (possibly never)
53 /* Set the base of the TEB selector to the base of the TEB for the
56 KeSetBaseGdtSelector(TEB_SELECTOR
, thread
->Teb
);
57 /* Switch to the new thread's context and stack */
63 : "m" (*(((unsigned char *)(&(thread
->Context
.nr
)))-4) )
65 /* Reload the TEB selector */
66 __asm__("movw %0, %%ax\n\t"
73 Thread
= PsGetCurrentThread();
74 if (Thread
->Cid
.UniqueThread
!= (HANDLE
)1)
76 // DbgPrint("Scheduling thread %x (id %d) teb %x\n",Thread,
77 // Thread->Cid.UniqueThread, Thread->Tcb.Teb);
80 if (Thread
->Tcb
.Teb
!= NULL
)
82 // DbgPrint("cr3 %x\n", Thread->ThreadsProcess->Pcb.PageTableDirectory);
83 __asm__("movl %%fs:0x18, %0\n\t"
87 // DbgPrint("Teb %x\n", Teb);
92 #define FLAG_NT (1<<14)
93 #define FLAG_VM (1<<17)
94 #define FLAG_IF (1<<9)
95 #define FLAG_IOPL ((1<<12)+(1<<13))
97 NTSTATUS
KeValidateUserContext(PCONTEXT Context
)
99 * FUNCTION: Validates a processor context
101 * Context = Context to validate
103 * NOTE: This only validates the context as not violating system security, it
104 * doesn't guararantee the thread won't crash at some point
105 * NOTE2: This relies on there only being two selectors which can access
109 if (Context
->Eip
>= KERNEL_BASE
)
111 return(STATUS_UNSUCCESSFUL
);
113 if (Context
->SegCs
== KERNEL_CS
)
115 return(STATUS_UNSUCCESSFUL
);
117 if (Context
->SegDs
== KERNEL_DS
)
119 return(STATUS_UNSUCCESSFUL
);
121 if (Context
->SegEs
== KERNEL_DS
)
123 return(STATUS_UNSUCCESSFUL
);
125 if (Context
->SegFs
== KERNEL_DS
)
127 return(STATUS_UNSUCCESSFUL
);
129 if (Context
->SegGs
== KERNEL_DS
)
131 return(STATUS_UNSUCCESSFUL
);
133 if ((Context
->EFlags
& FLAG_IOPL
) != 0 ||
134 (Context
->EFlags
& FLAG_NT
) ||
135 (Context
->EFlags
& FLAG_VM
) ||
136 (!(Context
->EFlags
& FLAG_IF
)))
138 return(STATUS_UNSUCCESSFUL
);
140 return(STATUS_SUCCESS
);
143 NTSTATUS
HalReleaseTask(PETHREAD Thread
)
145 * FUNCTION: Releases the resource allocated for a thread by
146 * HalInitTaskWithContext or HalInitTask
147 * NOTE: The thread had better not be running when this is called
150 KeFreeGdtSelector(Thread
->Tcb
.Context
.nr
/ 8);
151 ExFreePool(Thread
->Tcb
.Context
.KernelStackBase
);
152 if (Thread
->Tcb
.Context
.SavedKernelStackBase
!= NULL
)
154 ExFreePool(Thread
->Tcb
.Context
.SavedKernelStackBase
);
156 return(STATUS_SUCCESS
);
159 NTSTATUS
HalInitTaskWithContext(PETHREAD Thread
, PCONTEXT Context
)
161 * FUNCTION: Initialize a task with a user mode context
163 * Thread = Thread to initialize
164 * Context = Processor context to initialize it with
176 DPRINT("HalInitTaskWithContext(Thread %x, Context %x)\n",
179 assert(sizeof(hal_thread_state
)>=0x68);
181 if ((Status
=KeValidateUserContext(Context
))!=STATUS_SUCCESS
)
186 length
= sizeof(hal_thread_state
) - 1;
187 base
= (unsigned int)(&(Thread
->Tcb
.Context
));
188 // kernel_stack = ExAllocatePool(NonPagedPool,PAGESIZE);
189 kernel_stack
= ExAllocatePool(NonPagedPool
, 3*PAGESIZE
);
192 * Setup a TSS descriptor
194 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
195 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8900 | (length
& 0xf0000)
196 | (base
& 0xff000000);
197 desc
= KeAllocateGdtSelector(GdtDesc
);
200 return(STATUS_UNSUCCESSFUL
);
203 stack_start
= kernel_stack
+ 3*PAGESIZE
- sizeof(CONTEXT
);
204 Context
->SegFs
= TEB_SELECTOR
;
205 memcpy(stack_start
, Context
, sizeof(CONTEXT
));
208 * Initialize the thread context
210 memset(&Thread
->Tcb
.Context
,0,sizeof(hal_thread_state
));
211 Thread
->Tcb
.Context
.ldt
= KiNullLdtSel
;
212 Thread
->Tcb
.Context
.eflags
= (1<<1) + (1<<9);
213 Thread
->Tcb
.Context
.iomap_base
= FIELD_OFFSET(hal_thread_state
,io_bitmap
);
214 Thread
->Tcb
.Context
.esp0
= (ULONG
)stack_start
;
215 Thread
->Tcb
.Context
.ss0
= KERNEL_DS
;
216 Thread
->Tcb
.Context
.esp
= (ULONG
)stack_start
;
217 Thread
->Tcb
.Context
.ss
= KERNEL_DS
;
218 Thread
->Tcb
.Context
.cs
= KERNEL_CS
;
219 Thread
->Tcb
.Context
.eip
= (ULONG
)PsBeginThreadWithContextInternal
;
220 Thread
->Tcb
.Context
.io_bitmap
[0] = 0xff;
221 Thread
->Tcb
.Context
.cr3
= (ULONG
)
222 Thread
->ThreadsProcess
->Pcb
.PageTableDirectory
;
223 Thread
->Tcb
.Context
.ds
= KERNEL_DS
;
224 Thread
->Tcb
.Context
.es
= KERNEL_DS
;
225 Thread
->Tcb
.Context
.fs
= KERNEL_DS
;
226 Thread
->Tcb
.Context
.gs
= KERNEL_DS
;
228 Thread
->Tcb
.Context
.nr
= desc
* 8;
229 Thread
->Tcb
.Context
.KernelStackBase
= kernel_stack
;
230 Thread
->Tcb
.Context
.SavedKernelEsp
= 0;
231 Thread
->Tcb
.Context
.SavedKernelStackBase
= NULL
;
233 return(STATUS_SUCCESS
);
236 NTSTATUS
HalInitTask(PETHREAD thread
, PKSTART_ROUTINE fn
, PVOID StartContext
)
238 * FUNCTION: Initializes the HAL portion of a thread object
240 * thread = Object describes the thread
241 * fn = Entrypoint for the thread
242 * StartContext = parameter to pass to the thread entrypoint
243 * RETURNS: True if the function succeeded
247 unsigned int length
= sizeof(hal_thread_state
) - 1;
248 unsigned int base
= (unsigned int)(&(thread
->Tcb
.Context
));
249 // PULONG KernelStack = ExAllocatePool(NonPagedPool,4096);
253 DPRINT("HalInitTask(Thread %x, fn %x, StartContext %x)\n",
254 thread
,fn
,StartContext
);
255 DPRINT("thread->ThreadsProcess %x\n",thread
->ThreadsProcess
);
257 KernelStack
= ExAllocatePool(NonPagedPool
, 3*PAGESIZE
);
262 assert(sizeof(hal_thread_state
)>=0x68);
265 * Setup a TSS descriptor
267 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
268 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8900 | (length
& 0xf0000)
269 | (base
& 0xff000000);
270 desc
= KeAllocateGdtSelector(GdtDesc
);
273 return(STATUS_UNSUCCESSFUL
);
277 // DPRINT("sizeof(descriptor) %d\n",sizeof(descriptor));
278 // DPRINT("desc %d\n",desc);
281 * Initialize the stack for the thread (including the two arguments to
282 * the general start routine).
284 KernelStack
[1023] = (unsigned int)StartContext
;
285 KernelStack
[1022] = (unsigned int)fn
;
286 KernelStack
[1021] = 0;
289 * Initialize the thread context
291 memset(&thread
->Tcb
.Context
,0,sizeof(hal_thread_state
));
292 thread
->Tcb
.Context
.ldt
= KiNullLdtSel
;
293 thread
->Tcb
.Context
.eflags
= (1<<1)+(1<<9);
294 thread
->Tcb
.Context
.iomap_base
= FIELD_OFFSET(hal_thread_state
,io_bitmap
);
295 thread
->Tcb
.Context
.esp0
= (ULONG
)&KernelStack
[1021];
296 thread
->Tcb
.Context
.ss0
= KERNEL_DS
;
297 thread
->Tcb
.Context
.esp
= (ULONG
)&KernelStack
[1021];
298 thread
->Tcb
.Context
.ss
= KERNEL_DS
;
299 thread
->Tcb
.Context
.cs
= KERNEL_CS
;
300 thread
->Tcb
.Context
.eip
= (ULONG
)PsBeginThread
;
301 thread
->Tcb
.Context
.io_bitmap
[0] = 0xff;
302 thread
->Tcb
.Context
.cr3
= (ULONG
)
303 thread
->ThreadsProcess
->Pcb
.PageTableDirectory
;
304 thread
->Tcb
.Context
.ds
= KERNEL_DS
;
305 thread
->Tcb
.Context
.es
= KERNEL_DS
;
306 thread
->Tcb
.Context
.fs
= KERNEL_DS
;
307 thread
->Tcb
.Context
.gs
= KERNEL_DS
;
308 thread
->Tcb
.Context
.nr
= desc
* 8;
309 thread
->Tcb
.Context
.KernelStackBase
= KernelStack
;
310 thread
->Tcb
.Context
.SavedKernelEsp
= 0;
311 thread
->Tcb
.Context
.SavedKernelStackBase
= NULL
;
312 DPRINT("Allocated %x\n",desc
*8);
314 return(STATUS_SUCCESS
);
317 void HalInitFirstTask(PETHREAD thread
)
319 * FUNCTION: Called to setup the HAL portion of a thread object for the
328 memset(KiNullLdt
, 0, sizeof(KiNullLdt
));
329 base
= (unsigned int)&KiNullLdt
;
330 length
= sizeof(KiNullLdt
) - 1;
331 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
332 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8200 | (length
& 0xf0000)
333 | (base
& 0xff000000);
334 desc
= KeAllocateGdtSelector(GdtDesc
);
335 KiNullLdtSel
= desc
*8;
338 * Initialize the thread context
340 HalInitTask(thread
,NULL
,NULL
);
343 * Load the task register
347 : "a" (thread
->Tcb
.Context
.nr
));
348 FirstThread
= thread
;