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 /* FUNCTIONS **************************************************************/
39 VOID
HalTaskSwitch(PKTHREAD thread
)
41 * FUNCTION: Switch tasks
43 * thread = Thread to switch to
44 * NOTE: This function will not return until the current thread is scheduled
45 * again (possibly never)
50 Thread
= CONTAINING_RECORD(thread
, ETHREAD
, Tcb
);
51 if (Thread
->Cid
.UniqueThread
!= (HANDLE
)1)
53 DPRINT1("Scheduling thread %x (id %d)\n",Thread
,
54 Thread
->Cid
.UniqueThread
);
62 : "m" (*(((unsigned char *)(&(thread
->Context
.nr
)))-4) )
66 #define FLAG_NT (1<<14)
67 #define FLAG_VM (1<<17)
68 #define FLAG_IF (1<<9)
69 #define FLAG_IOPL ((1<<12)+(1<<13))
71 NTSTATUS
KeValidateUserContext(PCONTEXT Context
)
73 * FUNCTION: Validates a processor context
75 * Context = Context to validate
77 * NOTE: This only validates the context as not violating system security, it
78 * doesn't guararantee the thread won't crash at some point
79 * NOTE2: This relies on there only being two selectors which can access
83 if (Context
->Eip
>= KERNEL_BASE
)
85 return(STATUS_UNSUCCESSFUL
);
87 if (Context
->SegCs
== KERNEL_CS
)
89 return(STATUS_UNSUCCESSFUL
);
91 if (Context
->SegDs
== KERNEL_DS
)
93 return(STATUS_UNSUCCESSFUL
);
95 if (Context
->SegEs
== KERNEL_DS
)
97 return(STATUS_UNSUCCESSFUL
);
99 if (Context
->SegFs
== KERNEL_DS
)
101 return(STATUS_UNSUCCESSFUL
);
103 if (Context
->SegGs
== KERNEL_DS
)
105 return(STATUS_UNSUCCESSFUL
);
107 if ((Context
->EFlags
& FLAG_IOPL
) != 0 ||
108 (Context
->EFlags
& FLAG_NT
) ||
109 (Context
->EFlags
& FLAG_VM
) ||
110 (!(Context
->EFlags
& FLAG_IF
)))
112 return(STATUS_UNSUCCESSFUL
);
114 return(STATUS_SUCCESS
);
117 NTSTATUS
HalReleaseTask(PETHREAD Thread
)
119 * FUNCTION: Releases the resource allocated for a thread by
120 * HalInitTaskWithContext or HalInitTask
121 * NOTE: The thread had better not be running when this is called
124 KeFreeGdtSelector(Thread
->Tcb
.Context
.nr
/ 8);
125 ExFreePool(Thread
->Tcb
.Context
.KernelStackBase
);
126 if (Thread
->Tcb
.Context
.SavedKernelStackBase
!= NULL
)
128 ExFreePool(Thread
->Tcb
.Context
.SavedKernelStackBase
);
130 return(STATUS_SUCCESS
);
133 NTSTATUS
HalInitTaskWithContext(PETHREAD Thread
, PCONTEXT Context
)
135 * FUNCTION: Initialize a task with a user mode context
137 * Thread = Thread to initialize
138 * Context = Processor context to initialize it with
150 DPRINT("HalInitTaskWithContext(Thread %x, Context %x)\n",
153 assert(sizeof(hal_thread_state
)>=0x68);
155 if ((Status
=KeValidateUserContext(Context
))!=STATUS_SUCCESS
)
160 length
= sizeof(hal_thread_state
) - 1;
161 base
= (unsigned int)(&(Thread
->Tcb
.Context
));
162 // kernel_stack = ExAllocatePool(NonPagedPool,PAGESIZE);
163 kernel_stack
= ExAllocatePool(NonPagedPool
, 3*PAGESIZE
);
166 * Setup a TSS descriptor
168 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
169 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8900 | (length
& 0xf0000)
170 | (base
& 0xff000000);
171 desc
= KeAllocateGdtSelector(GdtDesc
);
174 return(STATUS_UNSUCCESSFUL
);
177 stack_start
= kernel_stack
+ 3*PAGESIZE
- sizeof(CONTEXT
);
178 memcpy(stack_start
, Context
, sizeof(CONTEXT
));
181 * Initialize the thread context
183 memset(&Thread
->Tcb
.Context
,0,sizeof(hal_thread_state
));
184 Thread
->Tcb
.Context
.ldt
= KiNullLdtSel
;
185 Thread
->Tcb
.Context
.eflags
= (1<<1) + (1<<9);
186 Thread
->Tcb
.Context
.iomap_base
= FIELD_OFFSET(hal_thread_state
,io_bitmap
);
187 Thread
->Tcb
.Context
.esp0
= (ULONG
)stack_start
;
188 Thread
->Tcb
.Context
.ss0
= KERNEL_DS
;
189 Thread
->Tcb
.Context
.esp
= (ULONG
)stack_start
;
190 Thread
->Tcb
.Context
.ss
= KERNEL_DS
;
191 Thread
->Tcb
.Context
.cs
= KERNEL_CS
;
192 Thread
->Tcb
.Context
.eip
= (ULONG
)PsBeginThreadWithContextInternal
;
193 Thread
->Tcb
.Context
.io_bitmap
[0] = 0xff;
194 Thread
->Tcb
.Context
.cr3
= (ULONG
)
195 Thread
->ThreadsProcess
->Pcb
.PageTableDirectory
;
196 Thread
->Tcb
.Context
.ds
= KERNEL_DS
;
197 Thread
->Tcb
.Context
.es
= KERNEL_DS
;
198 Thread
->Tcb
.Context
.fs
= KERNEL_DS
;
199 Thread
->Tcb
.Context
.gs
= KERNEL_DS
;
201 Thread
->Tcb
.Context
.nr
= desc
* 8;
202 Thread
->Tcb
.Context
.KernelStackBase
= kernel_stack
;
203 Thread
->Tcb
.Context
.SavedKernelEsp
= 0;
204 Thread
->Tcb
.Context
.SavedKernelStackBase
= NULL
;
206 return(STATUS_SUCCESS
);
209 NTSTATUS
HalInitTask(PETHREAD thread
, PKSTART_ROUTINE fn
, PVOID StartContext
)
211 * FUNCTION: Initializes the HAL portion of a thread object
213 * thread = Object describes the thread
214 * fn = Entrypoint for the thread
215 * StartContext = parameter to pass to the thread entrypoint
216 * RETURNS: True if the function succeeded
220 unsigned int length
= sizeof(hal_thread_state
) - 1;
221 unsigned int base
= (unsigned int)(&(thread
->Tcb
.Context
));
222 // PULONG KernelStack = ExAllocatePool(NonPagedPool,4096);
226 DPRINT("HalInitTask(Thread %x, fn %x, StartContext %x)\n",
227 thread
,fn
,StartContext
);
228 DPRINT("thread->ThreadsProcess %x\n",thread
->ThreadsProcess
);
230 KernelStack
= ExAllocatePool(NonPagedPool
, 3*PAGESIZE
);
235 assert(sizeof(hal_thread_state
)>=0x68);
238 * Setup a TSS descriptor
240 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
241 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8900 | (length
& 0xf0000)
242 | (base
& 0xff000000);
243 desc
= KeAllocateGdtSelector(GdtDesc
);
246 return(STATUS_UNSUCCESSFUL
);
250 // DPRINT("sizeof(descriptor) %d\n",sizeof(descriptor));
251 // DPRINT("desc %d\n",desc);
254 * Initialize the stack for the thread (including the two arguments to
255 * the general start routine).
257 KernelStack
[1023] = (unsigned int)StartContext
;
258 KernelStack
[1022] = (unsigned int)fn
;
259 KernelStack
[1021] = 0;
262 * Initialize the thread context
264 memset(&thread
->Tcb
.Context
,0,sizeof(hal_thread_state
));
265 thread
->Tcb
.Context
.ldt
= KiNullLdtSel
;
266 thread
->Tcb
.Context
.eflags
= (1<<1)+(1<<9);
267 thread
->Tcb
.Context
.iomap_base
= FIELD_OFFSET(hal_thread_state
,io_bitmap
);
268 thread
->Tcb
.Context
.esp0
= (ULONG
)&KernelStack
[1021];
269 thread
->Tcb
.Context
.ss0
= KERNEL_DS
;
270 thread
->Tcb
.Context
.esp
= (ULONG
)&KernelStack
[1021];
271 thread
->Tcb
.Context
.ss
= KERNEL_DS
;
272 thread
->Tcb
.Context
.cs
= KERNEL_CS
;
273 thread
->Tcb
.Context
.eip
= (ULONG
)PsBeginThread
;
274 thread
->Tcb
.Context
.io_bitmap
[0] = 0xff;
275 thread
->Tcb
.Context
.cr3
= (ULONG
)
276 thread
->ThreadsProcess
->Pcb
.PageTableDirectory
;
277 thread
->Tcb
.Context
.ds
= KERNEL_DS
;
278 thread
->Tcb
.Context
.es
= KERNEL_DS
;
279 thread
->Tcb
.Context
.fs
= KERNEL_DS
;
280 thread
->Tcb
.Context
.gs
= KERNEL_DS
;
281 thread
->Tcb
.Context
.nr
= desc
* 8;
282 thread
->Tcb
.Context
.KernelStackBase
= KernelStack
;
283 thread
->Tcb
.Context
.SavedKernelEsp
= 0;
284 thread
->Tcb
.Context
.SavedKernelStackBase
= NULL
;
285 DPRINT("Allocated %x\n",desc
*8);
287 return(STATUS_SUCCESS
);
290 void HalInitFirstTask(PETHREAD thread
)
292 * FUNCTION: Called to setup the HAL portion of a thread object for the
301 memset(KiNullLdt
, 0, sizeof(KiNullLdt
));
302 base
= (unsigned int)&KiNullLdt
;
303 length
= sizeof(KiNullLdt
) - 1;
304 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
305 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8200 | (length
& 0xf0000)
306 | (base
& 0xff000000);
307 desc
= KeAllocateGdtSelector(GdtDesc
);
308 KiNullLdtSel
= desc
*8;
311 * Initialize the thread context
313 HalInitTask(thread
,NULL
,NULL
);
316 * Load the task register
320 : "a" (thread
->Tcb
.Context
.nr
));
321 FirstThread
= thread
;