2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/hal/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);
48 DPRINT("Scheduling thread %x\n",thread
);
49 DPRINT("Scheduling thread %x\n",thread
->Context
.nr
);
50 DPRINT("previous task %x reserved1 %x esp0 %x ss0 %x\n",
51 thread
->Context
.previous_task
,thread
->Context
.reserved1
,
52 thread
->Context
.esp0
,thread
->Context
.ss0
);
53 DPRINT("reserved2 %x esp1 %x ss1 %x reserved3 %x esp2 %x ss2 %x\n",
54 thread
->Context
.reserved2
,thread
->Context
.esp1
,thread
->Context
.ss1
,
55 thread
->Context
.reserved3
,thread
->Context
.esp2
,thread
->Context
.ss2
);
56 DPRINT("reserved4 %x cr3 %x eip %x eflags %x eax %x\n",
57 thread
->Context
.reserved4
,thread
->Context
.cr3
,thread
->Context
.eip
,
58 thread
->Context
.eflags
,thread
->Context
.eax
);
59 DPRINT("ecx %x edx %x ebx %x esp %x ebp %x esi %x\n",
60 thread
->Context
.ecx
,thread
->Context
.edx
,thread
->Context
.ebx
,
61 thread
->Context
.esp
,thread
->Context
.ebp
,thread
->Context
.esi
);
62 DPRINT("edi %x es %x reserved5 %x cs %x reserved6 %x\n",
63 thread
->Context
.edi
,thread
->Context
.es
,thread
->Context
.reserved5
,
64 thread
->Context
.cs
,thread
->Context
.reserved6
);
65 DPRINT("ss %x reserved7 %x ds %x reserved8 %x fs %x\n",
66 thread
->Context
.ss
,thread
->Context
.reserved7
,thread
->Context
.ds
,
67 thread
->Context
.reserved8
,thread
->Context
.fs
);
68 DPRINT("reserved9 %x gs %x reserved10 %x ldt %x reserved11 %x\n",
69 thread
->Context
.reserved9
,thread
->Context
.gs
,
70 thread
->Context
.reserved10
,thread
->Context
.ldt
,
71 thread
->Context
.reserved11
);
72 DPRINT("trap %x iomap_base %x nr %x io_bitmap[0] %x\n",
73 thread
->Context
.trap
,thread
->Context
.iomap_base
,
74 thread
->Context
.nr
,thread
->Context
.io_bitmap
[0]);
75 DPRINT("thread->Context.cr3 %x\n",thread
->Context
.cr3
);
81 : "m" (*(((unsigned char *)(&(thread
->Context
.nr
)))-4) )
85 #define FLAG_NT (1<<14)
86 #define FLAG_VM (1<<17)
87 #define FLAG_IF (1<<9)
88 #define FLAG_IOPL ((1<<12)+(1<<13))
90 NTSTATUS
KeValidateUserContext(PCONTEXT Context
)
92 * FUNCTION: Validates a processor context
94 * Context = Context to validate
96 * NOTE: This only validates the context as not violating system security, it
97 * doesn't guararantee the thread won't crash at some point
98 * NOTE2: This relies on there only being two selectors which can access
102 if (Context
->Eip
>= KERNEL_BASE
)
104 return(STATUS_UNSUCCESSFUL
);
106 if (Context
->SegCs
== KERNEL_CS
)
108 return(STATUS_UNSUCCESSFUL
);
110 if (Context
->SegDs
== KERNEL_DS
)
112 return(STATUS_UNSUCCESSFUL
);
114 if (Context
->SegEs
== KERNEL_DS
)
116 return(STATUS_UNSUCCESSFUL
);
118 if (Context
->SegFs
== KERNEL_DS
)
120 return(STATUS_UNSUCCESSFUL
);
122 if (Context
->SegGs
== KERNEL_DS
)
124 return(STATUS_UNSUCCESSFUL
);
126 if ((Context
->EFlags
& FLAG_IOPL
) != 0 ||
127 (Context
->EFlags
& FLAG_NT
) ||
128 (Context
->EFlags
& FLAG_VM
) ||
129 (!(Context
->EFlags
& FLAG_IF
)))
131 return(STATUS_UNSUCCESSFUL
);
133 return(STATUS_SUCCESS
);
136 NTSTATUS
HalReleaseTask(PETHREAD Thread
)
138 * FUNCTION: Releases the resource allocated for a thread by
139 * HalInitTaskWithContext or HalInitTask
140 * NOTE: The thread had better not be running when this is called
143 KeFreeGdtSelector(Thread
->Tcb
.Context
.nr
);
144 ExFreePool(Thread
->Tcb
.Context
.KernelStackBase
);
145 if (Thread
->Tcb
.Context
.SavedKernelStackBase
!= NULL
)
147 ExFreePool(Thread
->Tcb
.Context
.SavedKernelStackBase
);
149 return(STATUS_SUCCESS
);
152 NTSTATUS
HalInitTaskWithContext(PETHREAD Thread
, PCONTEXT Context
)
154 * FUNCTION: Initialize a task with a user mode context
156 * Thread = Thread to initialize
157 * Context = Processor context to initialize it with
169 DPRINT("HalInitTaskWithContext(Thread %x, Context %x)\n",
172 assert(sizeof(hal_thread_state
)>=0x68);
174 if ((Status
=KeValidateUserContext(Context
))!=STATUS_SUCCESS
)
179 length
= sizeof(hal_thread_state
) - 1;
180 base
= (unsigned int)(&(Thread
->Tcb
.Context
));
181 // kernel_stack = ExAllocatePool(NonPagedPool,PAGESIZE);
182 kernel_stack
= ExAllocatePool(NonPagedPool
, 3*PAGESIZE
);
185 * Setup a TSS descriptor
187 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
188 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8900 | (length
& 0xf0000)
189 | (base
& 0xff000000);
190 desc
= KeAllocateGdtSelector(GdtDesc
);
193 return(STATUS_UNSUCCESSFUL
);
196 stack_start
= kernel_stack
+ 4096 - sizeof(CONTEXT
);
197 memcpy(stack_start
, Context
, sizeof(CONTEXT
));
200 * Initialize the thread context
202 memset(&Thread
->Tcb
.Context
,0,sizeof(hal_thread_state
));
203 Thread
->Tcb
.Context
.ldt
= KiNullLdtSel
;
204 Thread
->Tcb
.Context
.eflags
= (1<<1) + (1<<9);
205 Thread
->Tcb
.Context
.iomap_base
= FIELD_OFFSET(hal_thread_state
,io_bitmap
);
206 Thread
->Tcb
.Context
.esp0
= (ULONG
)stack_start
;
207 Thread
->Tcb
.Context
.ss0
= KERNEL_DS
;
208 Thread
->Tcb
.Context
.esp
= (ULONG
)stack_start
;
209 Thread
->Tcb
.Context
.ss
= KERNEL_DS
;
210 Thread
->Tcb
.Context
.cs
= KERNEL_CS
;
211 Thread
->Tcb
.Context
.eip
= (ULONG
)PsBeginThreadWithContextInternal
;
212 Thread
->Tcb
.Context
.io_bitmap
[0] = 0xff;
213 Thread
->Tcb
.Context
.cr3
= (ULONG
)
214 Thread
->ThreadsProcess
->Pcb
.PageTableDirectory
;
215 Thread
->Tcb
.Context
.ds
= KERNEL_DS
;
216 Thread
->Tcb
.Context
.es
= KERNEL_DS
;
217 Thread
->Tcb
.Context
.fs
= KERNEL_DS
;
218 Thread
->Tcb
.Context
.gs
= KERNEL_DS
;
220 Thread
->Tcb
.Context
.nr
= desc
* 8;
221 Thread
->Tcb
.Context
.KernelStackBase
= kernel_stack
;
222 Thread
->Tcb
.Context
.SavedKernelEsp
= 0;
223 Thread
->Tcb
.Context
.SavedKernelStackBase
= NULL
;
225 return(STATUS_SUCCESS
);
228 NTSTATUS
HalInitTask(PETHREAD thread
, PKSTART_ROUTINE fn
, PVOID StartContext
)
230 * FUNCTION: Initializes the HAL portion of a thread object
232 * thread = Object describes the thread
233 * fn = Entrypoint for the thread
234 * StartContext = parameter to pass to the thread entrypoint
235 * RETURNS: True if the function succeeded
239 unsigned int length
= sizeof(hal_thread_state
) - 1;
240 unsigned int base
= (unsigned int)(&(thread
->Tcb
.Context
));
241 // PULONG KernelStack = ExAllocatePool(NonPagedPool,4096);
245 DPRINT("HalInitTask(Thread %x, fn %x, StartContext %x)\n",
246 thread
,fn
,StartContext
);
247 DPRINT("thread->ThreadsProcess %x\n",thread
->ThreadsProcess
);
249 KernelStack
= ExAllocatePool(NonPagedPool
, 3*PAGESIZE
);
254 assert(sizeof(hal_thread_state
)>=0x68);
257 * Setup a TSS descriptor
259 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
260 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8900 | (length
& 0xf0000)
261 | (base
& 0xff000000);
262 desc
= KeAllocateGdtSelector(GdtDesc
);
265 return(STATUS_UNSUCCESSFUL
);
269 // DPRINT("sizeof(descriptor) %d\n",sizeof(descriptor));
270 // DPRINT("desc %d\n",desc);
273 * Initialize the stack for the thread (including the two arguments to
274 * the general start routine).
276 KernelStack
[1023] = (unsigned int)StartContext
;
277 KernelStack
[1022] = (unsigned int)fn
;
278 KernelStack
[1021] = 0;
281 * Initialize the thread context
283 memset(&thread
->Tcb
.Context
,0,sizeof(hal_thread_state
));
284 thread
->Tcb
.Context
.ldt
= KiNullLdtSel
;
285 thread
->Tcb
.Context
.eflags
= (1<<1)+(1<<9);
286 thread
->Tcb
.Context
.iomap_base
= FIELD_OFFSET(hal_thread_state
,io_bitmap
);
287 thread
->Tcb
.Context
.esp0
= (ULONG
)&KernelStack
[1021];
288 thread
->Tcb
.Context
.ss0
= KERNEL_DS
;
289 thread
->Tcb
.Context
.esp
= (ULONG
)&KernelStack
[1021];
290 thread
->Tcb
.Context
.ss
= KERNEL_DS
;
291 thread
->Tcb
.Context
.cs
= KERNEL_CS
;
292 thread
->Tcb
.Context
.eip
= (ULONG
)PsBeginThread
;
293 thread
->Tcb
.Context
.io_bitmap
[0] = 0xff;
294 thread
->Tcb
.Context
.cr3
= (ULONG
)
295 thread
->ThreadsProcess
->Pcb
.PageTableDirectory
;
296 thread
->Tcb
.Context
.ds
= KERNEL_DS
;
297 thread
->Tcb
.Context
.es
= KERNEL_DS
;
298 thread
->Tcb
.Context
.fs
= KERNEL_DS
;
299 thread
->Tcb
.Context
.gs
= KERNEL_DS
;
300 thread
->Tcb
.Context
.nr
= desc
* 8;
301 thread
->Tcb
.Context
.KernelStackBase
= KernelStack
;
302 thread
->Tcb
.Context
.SavedKernelEsp
= 0;
303 thread
->Tcb
.Context
.SavedKernelStackBase
= NULL
;
304 DPRINT("Allocated %x\n",desc
*8);
306 return(STATUS_SUCCESS
);
309 void HalInitFirstTask(PETHREAD thread
)
311 * FUNCTION: Called to setup the HAL portion of a thread object for the
320 memset(KiNullLdt
, 0, sizeof(KiNullLdt
));
321 base
= (unsigned int)&KiNullLdt
;
322 length
= sizeof(KiNullLdt
) - 1;
323 GdtDesc
[0] = (length
& 0xffff) | ((base
& 0xffff) << 16);
324 GdtDesc
[1] = ((base
& 0xff0000)>>16) | 0x8200 | (length
& 0xf0000)
325 | (base
& 0xff000000);
326 desc
= KeAllocateGdtSelector(GdtDesc
);
327 KiNullLdtSel
= desc
*8;
330 * Initialize the thread context
332 HalInitTask(thread
,NULL
,NULL
);
335 * Load the task register
339 : "a" (thread
->Tcb
.Context
.nr
));
340 FirstThread
= thread
;