e6ce8fd10c705dd7890cc6276d0f202624316020
[reactos.git] / reactos / ntoskrnl / hal / x86 / thread.c
1 /*
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)
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 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);
76 __asm__("pushfl\n\t"
77 "cli\n\t"
78 "ljmp %0\n\t"
79 "popfl\n\t"
80 : /* No outputs */
81 : "m" (*(((unsigned char *)(&(thread->Context.nr)))-4) )
82 : "ax","dx");
83 }
84
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))
89
90 NTSTATUS KeValidateUserContext(PCONTEXT Context)
91 /*
92 * FUNCTION: Validates a processor context
93 * ARGUMENTS:
94 * Context = Context to validate
95 * RETURNS: Status
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
99 * system space
100 */
101 {
102 if (Context->Eip >= KERNEL_BASE)
103 {
104 return(STATUS_UNSUCCESSFUL);
105 }
106 if (Context->SegCs == KERNEL_CS)
107 {
108 return(STATUS_UNSUCCESSFUL);
109 }
110 if (Context->SegDs == KERNEL_DS)
111 {
112 return(STATUS_UNSUCCESSFUL);
113 }
114 if (Context->SegEs == KERNEL_DS)
115 {
116 return(STATUS_UNSUCCESSFUL);
117 }
118 if (Context->SegFs == KERNEL_DS)
119 {
120 return(STATUS_UNSUCCESSFUL);
121 }
122 if (Context->SegGs == KERNEL_DS)
123 {
124 return(STATUS_UNSUCCESSFUL);
125 }
126 if ((Context->EFlags & FLAG_IOPL) != 0 ||
127 (Context->EFlags & FLAG_NT) ||
128 (Context->EFlags & FLAG_VM) ||
129 (!(Context->EFlags & FLAG_IF)))
130 {
131 return(STATUS_UNSUCCESSFUL);
132 }
133 return(STATUS_SUCCESS);
134 }
135
136 NTSTATUS HalReleaseTask(PETHREAD Thread)
137 /*
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
141 */
142 {
143 KeFreeGdtSelector(Thread->Tcb.Context.nr);
144 ExFreePool(Thread->Tcb.Context.KernelStackBase);
145 if (Thread->Tcb.Context.SavedKernelStackBase != NULL)
146 {
147 ExFreePool(Thread->Tcb.Context.SavedKernelStackBase);
148 }
149 return(STATUS_SUCCESS);
150 }
151
152 NTSTATUS HalInitTaskWithContext(PETHREAD Thread, PCONTEXT Context)
153 /*
154 * FUNCTION: Initialize a task with a user mode context
155 * ARGUMENTS:
156 * Thread = Thread to initialize
157 * Context = Processor context to initialize it with
158 * RETURNS: Status
159 */
160 {
161 unsigned int desc;
162 unsigned int length;
163 unsigned int base;
164 PVOID kernel_stack;
165 NTSTATUS Status;
166 PVOID stack_start;
167 ULONG GdtDesc[2];
168
169 DPRINT("HalInitTaskWithContext(Thread %x, Context %x)\n",
170 Thread,Context);
171
172 assert(sizeof(hal_thread_state)>=0x68);
173
174 if ((Status=KeValidateUserContext(Context))!=STATUS_SUCCESS)
175 {
176 return(Status);
177 }
178
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);
183
184 /*
185 * Setup a TSS descriptor
186 */
187 GdtDesc[0] = (length & 0xffff) | ((base & 0xffff) << 16);
188 GdtDesc[1] = ((base & 0xff0000)>>16) | 0x8900 | (length & 0xf0000)
189 | (base & 0xff000000);
190 desc = KeAllocateGdtSelector(GdtDesc);
191 if (desc == 0)
192 {
193 return(STATUS_UNSUCCESSFUL);
194 }
195
196 stack_start = kernel_stack + 4096 - sizeof(CONTEXT);
197 memcpy(stack_start, Context, sizeof(CONTEXT));
198
199 /*
200 * Initialize the thread context
201 */
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;
219
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;
224
225 return(STATUS_SUCCESS);
226 }
227
228 NTSTATUS HalInitTask(PETHREAD thread, PKSTART_ROUTINE fn, PVOID StartContext)
229 /*
230 * FUNCTION: Initializes the HAL portion of a thread object
231 * ARGUMENTS:
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
236 */
237 {
238 unsigned int desc;
239 unsigned int length = sizeof(hal_thread_state) - 1;
240 unsigned int base = (unsigned int)(&(thread->Tcb.Context));
241 // PULONG KernelStack = ExAllocatePool(NonPagedPool,4096);
242 PULONG KernelStack;
243 ULONG GdtDesc[2];
244
245 DPRINT("HalInitTask(Thread %x, fn %x, StartContext %x)\n",
246 thread,fn,StartContext);
247 DPRINT("thread->ThreadsProcess %x\n",thread->ThreadsProcess);
248
249 KernelStack = ExAllocatePool(NonPagedPool, 3*PAGESIZE);
250
251 /*
252 * Make sure
253 */
254 assert(sizeof(hal_thread_state)>=0x68);
255
256 /*
257 * Setup a TSS descriptor
258 */
259 GdtDesc[0] = (length & 0xffff) | ((base & 0xffff) << 16);
260 GdtDesc[1] = ((base & 0xff0000)>>16) | 0x8900 | (length & 0xf0000)
261 | (base & 0xff000000);
262 desc = KeAllocateGdtSelector(GdtDesc);
263 if (desc == 0)
264 {
265 return(STATUS_UNSUCCESSFUL);
266 }
267
268
269 // DPRINT("sizeof(descriptor) %d\n",sizeof(descriptor));
270 // DPRINT("desc %d\n",desc);
271
272 /*
273 * Initialize the stack for the thread (including the two arguments to
274 * the general start routine).
275 */
276 KernelStack[1023] = (unsigned int)StartContext;
277 KernelStack[1022] = (unsigned int)fn;
278 KernelStack[1021] = 0;
279
280 /*
281 * Initialize the thread context
282 */
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);
305
306 return(STATUS_SUCCESS);
307 }
308
309 void HalInitFirstTask(PETHREAD thread)
310 /*
311 * FUNCTION: Called to setup the HAL portion of a thread object for the
312 * initial thread
313 */
314 {
315 ULONG base;
316 ULONG length;
317 ULONG desc;
318 ULONG GdtDesc[2];
319
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;
328
329 /*
330 * Initialize the thread context
331 */
332 HalInitTask(thread,NULL,NULL);
333
334 /*
335 * Load the task register
336 */
337 __asm__("ltr %%ax"
338 : /* no output */
339 : "a" (thread->Tcb.Context.nr));
340 FirstThread = thread;
341 }