Implemented setting the fs register to point to the TEB
[reactos.git] / reactos / ntoskrnl / ke / i386 / thread.c
1 /*
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)
7 * REVISION HISTORY:
8 * 27/06/98: Created
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #include <ddk/ntddk.h>
14 #include <internal/ntoskrnl.h>
15 #include <internal/ps.h>
16 #include <string.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>
22
23 #define NDEBUG
24 #include <internal/debug.h>
25
26 /* GLOBALS ***************************************************************/
27
28 #define NR_TASKS 128
29
30 #define FIRST_TSS_SELECTOR (KERNEL_DS + 0x8)
31 #define FIRST_TSS_OFFSET (FIRST_TSS_SELECTOR / 8)
32
33 static char KiNullLdt[8] = {0,};
34 static unsigned int KiNullLdtSel = 0;
35 static PETHREAD FirstThread = NULL;
36
37 extern ULONG KeSetBaseGdtSelector(ULONG Entry, PVOID Base);
38
39 /* FUNCTIONS **************************************************************/
40
41 VOID HalTaskSwitch(PKTHREAD thread)
42 /*
43 * FUNCTION: Switch tasks
44 * ARGUMENTS:
45 * thread = Thread to switch to
46 * NOTE: This function will not return until the current thread is scheduled
47 * again (possibly never)
48 */
49 {
50 // PETHREAD Thread;
51 // PVOID Teb;
52
53 /* Set the base of the TEB selector to the base of the TEB for the
54 * new thread
55 */
56 KeSetBaseGdtSelector(TEB_SELECTOR, thread->Teb);
57 /* Switch to the new thread's context and stack */
58 __asm__("pushfl\n\t"
59 "cli\n\t"
60 "ljmp %0\n\t"
61 "popfl\n\t"
62 : /* No outputs */
63 : "m" (*(((unsigned char *)(&(thread->Context.nr)))-4) )
64 : "ax","dx");
65 /* Reload the TEB selector */
66 __asm__("movw %0, %%ax\n\t"
67 "movw %%ax, %%fs\n\t"
68 : /* No outputs */
69 : "i" (TEB_SELECTOR)
70 : "ax");
71
72 #if 0
73 Thread = PsGetCurrentThread();
74 if (Thread->Cid.UniqueThread != (HANDLE)1)
75 {
76 // DbgPrint("Scheduling thread %x (id %d) teb %x\n",Thread,
77 // Thread->Cid.UniqueThread, Thread->Tcb.Teb);
78 }
79
80 if (Thread->Tcb.Teb != NULL)
81 {
82 // DbgPrint("cr3 %x\n", Thread->ThreadsProcess->Pcb.PageTableDirectory);
83 __asm__("movl %%fs:0x18, %0\n\t"
84 : "=g" (Teb)
85 : /* No inputs */
86 );
87 // DbgPrint("Teb %x\n", Teb);
88 }
89 #endif
90 }
91
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))
96
97 NTSTATUS KeValidateUserContext(PCONTEXT Context)
98 /*
99 * FUNCTION: Validates a processor context
100 * ARGUMENTS:
101 * Context = Context to validate
102 * RETURNS: Status
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
106 * system space
107 */
108 {
109 if (Context->Eip >= KERNEL_BASE)
110 {
111 return(STATUS_UNSUCCESSFUL);
112 }
113 if (Context->SegCs == KERNEL_CS)
114 {
115 return(STATUS_UNSUCCESSFUL);
116 }
117 if (Context->SegDs == KERNEL_DS)
118 {
119 return(STATUS_UNSUCCESSFUL);
120 }
121 if (Context->SegEs == KERNEL_DS)
122 {
123 return(STATUS_UNSUCCESSFUL);
124 }
125 if (Context->SegFs == KERNEL_DS)
126 {
127 return(STATUS_UNSUCCESSFUL);
128 }
129 if (Context->SegGs == KERNEL_DS)
130 {
131 return(STATUS_UNSUCCESSFUL);
132 }
133 if ((Context->EFlags & FLAG_IOPL) != 0 ||
134 (Context->EFlags & FLAG_NT) ||
135 (Context->EFlags & FLAG_VM) ||
136 (!(Context->EFlags & FLAG_IF)))
137 {
138 return(STATUS_UNSUCCESSFUL);
139 }
140 return(STATUS_SUCCESS);
141 }
142
143 NTSTATUS HalReleaseTask(PETHREAD Thread)
144 /*
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
148 */
149 {
150 KeFreeGdtSelector(Thread->Tcb.Context.nr / 8);
151 ExFreePool(Thread->Tcb.Context.KernelStackBase);
152 if (Thread->Tcb.Context.SavedKernelStackBase != NULL)
153 {
154 ExFreePool(Thread->Tcb.Context.SavedKernelStackBase);
155 }
156 return(STATUS_SUCCESS);
157 }
158
159 NTSTATUS HalInitTaskWithContext(PETHREAD Thread, PCONTEXT Context)
160 /*
161 * FUNCTION: Initialize a task with a user mode context
162 * ARGUMENTS:
163 * Thread = Thread to initialize
164 * Context = Processor context to initialize it with
165 * RETURNS: Status
166 */
167 {
168 unsigned int desc;
169 unsigned int length;
170 unsigned int base;
171 PVOID kernel_stack;
172 NTSTATUS Status;
173 PVOID stack_start;
174 ULONG GdtDesc[2];
175
176 DPRINT("HalInitTaskWithContext(Thread %x, Context %x)\n",
177 Thread,Context);
178
179 assert(sizeof(hal_thread_state)>=0x68);
180
181 if ((Status=KeValidateUserContext(Context))!=STATUS_SUCCESS)
182 {
183 return(Status);
184 }
185
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);
190
191 /*
192 * Setup a TSS descriptor
193 */
194 GdtDesc[0] = (length & 0xffff) | ((base & 0xffff) << 16);
195 GdtDesc[1] = ((base & 0xff0000)>>16) | 0x8900 | (length & 0xf0000)
196 | (base & 0xff000000);
197 desc = KeAllocateGdtSelector(GdtDesc);
198 if (desc == 0)
199 {
200 return(STATUS_UNSUCCESSFUL);
201 }
202
203 stack_start = kernel_stack + 3*PAGESIZE - sizeof(CONTEXT);
204 Context->SegFs = TEB_SELECTOR;
205 memcpy(stack_start, Context, sizeof(CONTEXT));
206
207 /*
208 * Initialize the thread context
209 */
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;
227
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;
232
233 return(STATUS_SUCCESS);
234 }
235
236 NTSTATUS HalInitTask(PETHREAD thread, PKSTART_ROUTINE fn, PVOID StartContext)
237 /*
238 * FUNCTION: Initializes the HAL portion of a thread object
239 * ARGUMENTS:
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
244 */
245 {
246 unsigned int desc;
247 unsigned int length = sizeof(hal_thread_state) - 1;
248 unsigned int base = (unsigned int)(&(thread->Tcb.Context));
249 // PULONG KernelStack = ExAllocatePool(NonPagedPool,4096);
250 PULONG KernelStack;
251 ULONG GdtDesc[2];
252
253 DPRINT("HalInitTask(Thread %x, fn %x, StartContext %x)\n",
254 thread,fn,StartContext);
255 DPRINT("thread->ThreadsProcess %x\n",thread->ThreadsProcess);
256
257 KernelStack = ExAllocatePool(NonPagedPool, 3*PAGESIZE);
258
259 /*
260 * Make sure
261 */
262 assert(sizeof(hal_thread_state)>=0x68);
263
264 /*
265 * Setup a TSS descriptor
266 */
267 GdtDesc[0] = (length & 0xffff) | ((base & 0xffff) << 16);
268 GdtDesc[1] = ((base & 0xff0000)>>16) | 0x8900 | (length & 0xf0000)
269 | (base & 0xff000000);
270 desc = KeAllocateGdtSelector(GdtDesc);
271 if (desc == 0)
272 {
273 return(STATUS_UNSUCCESSFUL);
274 }
275
276
277 // DPRINT("sizeof(descriptor) %d\n",sizeof(descriptor));
278 // DPRINT("desc %d\n",desc);
279
280 /*
281 * Initialize the stack for the thread (including the two arguments to
282 * the general start routine).
283 */
284 KernelStack[1023] = (unsigned int)StartContext;
285 KernelStack[1022] = (unsigned int)fn;
286 KernelStack[1021] = 0;
287
288 /*
289 * Initialize the thread context
290 */
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);
313
314 return(STATUS_SUCCESS);
315 }
316
317 void HalInitFirstTask(PETHREAD thread)
318 /*
319 * FUNCTION: Called to setup the HAL portion of a thread object for the
320 * initial thread
321 */
322 {
323 ULONG base;
324 ULONG length;
325 ULONG desc;
326 ULONG GdtDesc[2];
327
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;
336
337 /*
338 * Initialize the thread context
339 */
340 HalInitTask(thread,NULL,NULL);
341
342 /*
343 * Load the task register
344 */
345 __asm__("ltr %%ax"
346 : /* no output */
347 : "a" (thread->Tcb.Context.nr));
348 FirstThread = thread;
349 }