e0b8dc12e7166d75b5b1299c1d2ec512dbf78cee
[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 /* FUNCTIONS **************************************************************/
38
39 VOID HalTaskSwitch(PKTHREAD thread)
40 /*
41 * FUNCTION: Switch tasks
42 * ARGUMENTS:
43 * thread = Thread to switch to
44 * NOTE: This function will not return until the current thread is scheduled
45 * again (possibly never)
46 */
47 {
48 #if 0
49 PETHREAD Thread;
50 Thread = CONTAINING_RECORD(thread, ETHREAD, Tcb);
51 if (Thread->Cid.UniqueThread != (HANDLE)1)
52 {
53 DPRINT1("Scheduling thread %x (id %d)\n",Thread,
54 Thread->Cid.UniqueThread);
55 }
56 #endif
57 __asm__("pushfl\n\t"
58 "cli\n\t"
59 "ljmp %0\n\t"
60 "popfl\n\t"
61 : /* No outputs */
62 : "m" (*(((unsigned char *)(&(thread->Context.nr)))-4) )
63 : "ax","dx");
64 }
65
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))
70
71 NTSTATUS KeValidateUserContext(PCONTEXT Context)
72 /*
73 * FUNCTION: Validates a processor context
74 * ARGUMENTS:
75 * Context = Context to validate
76 * RETURNS: Status
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
80 * system space
81 */
82 {
83 if (Context->Eip >= KERNEL_BASE)
84 {
85 return(STATUS_UNSUCCESSFUL);
86 }
87 if (Context->SegCs == KERNEL_CS)
88 {
89 return(STATUS_UNSUCCESSFUL);
90 }
91 if (Context->SegDs == KERNEL_DS)
92 {
93 return(STATUS_UNSUCCESSFUL);
94 }
95 if (Context->SegEs == KERNEL_DS)
96 {
97 return(STATUS_UNSUCCESSFUL);
98 }
99 if (Context->SegFs == KERNEL_DS)
100 {
101 return(STATUS_UNSUCCESSFUL);
102 }
103 if (Context->SegGs == KERNEL_DS)
104 {
105 return(STATUS_UNSUCCESSFUL);
106 }
107 if ((Context->EFlags & FLAG_IOPL) != 0 ||
108 (Context->EFlags & FLAG_NT) ||
109 (Context->EFlags & FLAG_VM) ||
110 (!(Context->EFlags & FLAG_IF)))
111 {
112 return(STATUS_UNSUCCESSFUL);
113 }
114 return(STATUS_SUCCESS);
115 }
116
117 NTSTATUS HalReleaseTask(PETHREAD Thread)
118 /*
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
122 */
123 {
124 KeFreeGdtSelector(Thread->Tcb.Context.nr / 8);
125 ExFreePool(Thread->Tcb.Context.KernelStackBase);
126 if (Thread->Tcb.Context.SavedKernelStackBase != NULL)
127 {
128 ExFreePool(Thread->Tcb.Context.SavedKernelStackBase);
129 }
130 return(STATUS_SUCCESS);
131 }
132
133 NTSTATUS HalInitTaskWithContext(PETHREAD Thread, PCONTEXT Context)
134 /*
135 * FUNCTION: Initialize a task with a user mode context
136 * ARGUMENTS:
137 * Thread = Thread to initialize
138 * Context = Processor context to initialize it with
139 * RETURNS: Status
140 */
141 {
142 unsigned int desc;
143 unsigned int length;
144 unsigned int base;
145 PVOID kernel_stack;
146 NTSTATUS Status;
147 PVOID stack_start;
148 ULONG GdtDesc[2];
149
150 DPRINT("HalInitTaskWithContext(Thread %x, Context %x)\n",
151 Thread,Context);
152
153 assert(sizeof(hal_thread_state)>=0x68);
154
155 if ((Status=KeValidateUserContext(Context))!=STATUS_SUCCESS)
156 {
157 return(Status);
158 }
159
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);
164
165 /*
166 * Setup a TSS descriptor
167 */
168 GdtDesc[0] = (length & 0xffff) | ((base & 0xffff) << 16);
169 GdtDesc[1] = ((base & 0xff0000)>>16) | 0x8900 | (length & 0xf0000)
170 | (base & 0xff000000);
171 desc = KeAllocateGdtSelector(GdtDesc);
172 if (desc == 0)
173 {
174 return(STATUS_UNSUCCESSFUL);
175 }
176
177 stack_start = kernel_stack + 3*PAGESIZE - sizeof(CONTEXT);
178 memcpy(stack_start, Context, sizeof(CONTEXT));
179
180 /*
181 * Initialize the thread context
182 */
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;
200
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;
205
206 return(STATUS_SUCCESS);
207 }
208
209 NTSTATUS HalInitTask(PETHREAD thread, PKSTART_ROUTINE fn, PVOID StartContext)
210 /*
211 * FUNCTION: Initializes the HAL portion of a thread object
212 * ARGUMENTS:
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
217 */
218 {
219 unsigned int desc;
220 unsigned int length = sizeof(hal_thread_state) - 1;
221 unsigned int base = (unsigned int)(&(thread->Tcb.Context));
222 // PULONG KernelStack = ExAllocatePool(NonPagedPool,4096);
223 PULONG KernelStack;
224 ULONG GdtDesc[2];
225
226 DPRINT("HalInitTask(Thread %x, fn %x, StartContext %x)\n",
227 thread,fn,StartContext);
228 DPRINT("thread->ThreadsProcess %x\n",thread->ThreadsProcess);
229
230 KernelStack = ExAllocatePool(NonPagedPool, 3*PAGESIZE);
231
232 /*
233 * Make sure
234 */
235 assert(sizeof(hal_thread_state)>=0x68);
236
237 /*
238 * Setup a TSS descriptor
239 */
240 GdtDesc[0] = (length & 0xffff) | ((base & 0xffff) << 16);
241 GdtDesc[1] = ((base & 0xff0000)>>16) | 0x8900 | (length & 0xf0000)
242 | (base & 0xff000000);
243 desc = KeAllocateGdtSelector(GdtDesc);
244 if (desc == 0)
245 {
246 return(STATUS_UNSUCCESSFUL);
247 }
248
249
250 // DPRINT("sizeof(descriptor) %d\n",sizeof(descriptor));
251 // DPRINT("desc %d\n",desc);
252
253 /*
254 * Initialize the stack for the thread (including the two arguments to
255 * the general start routine).
256 */
257 KernelStack[1023] = (unsigned int)StartContext;
258 KernelStack[1022] = (unsigned int)fn;
259 KernelStack[1021] = 0;
260
261 /*
262 * Initialize the thread context
263 */
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);
286
287 return(STATUS_SUCCESS);
288 }
289
290 void HalInitFirstTask(PETHREAD thread)
291 /*
292 * FUNCTION: Called to setup the HAL portion of a thread object for the
293 * initial thread
294 */
295 {
296 ULONG base;
297 ULONG length;
298 ULONG desc;
299 ULONG GdtDesc[2];
300
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;
309
310 /*
311 * Initialize the thread context
312 */
313 HalInitTask(thread,NULL,NULL);
314
315 /*
316 * Load the task register
317 */
318 __asm__("ltr %%ax"
319 : /* no output */
320 : "a" (thread->Tcb.Context.nr));
321 FirstThread = thread;
322 }