- Implemented execp*.
[planlOS.git] / system / kernel / ke / thread.c
blob80d9d3323b879dd2deeb82e88cc1896cd21f0c67
1 /*
2 Copyright (C) 2008 Mathias Gottschlag
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in the
6 Software without restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
8 Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #include "ke/thread.h"
23 #include "mm/memory.h"
24 #include "ke/apic.h"
25 #include "ke/level.h"
26 #include "ke/cpu.h"
27 #include "ke/timer.h"
28 #include "ke/debug.h"
29 #include "ke/interrupts.h"
30 #include <stdlib.h>
31 #include <string.h>
33 static KeSpinlock threadlock;
34 static KeThread **threads = 0;
35 static uint32_t threadcount = 0;
36 static uint32_t lastthread = 0;
38 static void keIdleThread(void)
40 char *threadsign = (char*)0xC00B8000 + 158 - keAPICGetCPU() * 2;
41 while (1)
43 threadsign[0] = 'i';
44 threadsign[1]++;
45 asm volatile("hlt");
49 KeThread *keCreateThread(KeProcess *process, uintptr_t entry, uint32_t paramcount, ...)
51 uint32_t *params = &paramcount + 1;
52 KeThread *thread = malloc(sizeof(KeThread));
53 memset(thread, 0, sizeof(KeThread));
54 thread->process = process;
55 keInitSpinlock(&thread->active);
57 // Setup user stack
58 uintptr_t userstack_virt = mmFindFreePages(&process->memory, MM_MAX_USER_PAGE,
59 MM_MIN_USER_PAGE, 1, 0x1000);
60 uintptr_t userstack_phys = mmAllocPhysicalMemory(0, 0, 0x1000);
61 mmMapMemory(&process->memory, userstack_phys, userstack_virt, MM_MAP_READ | MM_MAP_WRITE);
63 // Push params onto user stack
64 uint32_t *stack = (uint32_t*)mmFindFreeKernelPages(MM_MAX_KERNEL_PAGE,
65 MM_MIN_KERNEL_PAGE, 1, 0x1000);
66 mmMapKernelMemory(userstack_phys, (uintptr_t)stack, MM_MAP_READ | MM_MAP_WRITE);
67 uint32_t i;
68 for (i = 0; i < paramcount; i++)
70 stack[1024 - paramcount + i] = params[i];
72 stack[1024 - paramcount - 1] = 0xDEADC0DE;
73 //halMapPage(kernel_directory, (uintptr_t)stack, 0, 0x0);
74 mmMapKernelMemory(0, (uintptr_t)stack, 0);
76 thread->userstack = userstack_virt;
77 // Setup kernel stack
78 uintptr_t kernelstack_virt = mmFindFreeKernelPages(MM_MAX_KERNEL_PAGE,
79 MM_MIN_KERNEL_PAGE, 1, 0x1000);
80 uintptr_t kernelstack_phys = mmAllocPhysicalMemory(0, 0, 0x1000);
81 mmMapKernelMemory(kernelstack_phys, kernelstack_virt, MM_MAP_READ | MM_MAP_WRITE);
82 thread->kernelstack_bottom = kernelstack_virt;
83 stack = (uint32_t*)kernelstack_virt;
84 stack += 1024;
85 *(--stack) = 0x23;
86 *(--stack) = thread->userstack + 0x1000 - paramcount * 4 - 4;
87 *(--stack) = 0x202;
88 *(--stack) = 0x1b;
89 *(--stack) = entry;
90 *(--stack) = 0x0;
91 *(--stack) = 0x0;
92 *(--stack) = 0;
93 *(--stack) = 0x0;
94 *(--stack) = 0x0;
95 *(--stack) = 0x0;
96 *(--stack) = 0x0;
97 *(--stack) = 0x0;
98 *(--stack) = 0x0;
99 *(--stack) = 0x0;
100 *(--stack) = 0x23;
101 *(--stack) = 0x23;
102 *(--stack) = 0x23;
103 *(--stack) = 0x23;
104 thread->kernelstack = (uintptr_t)stack;
105 thread->userframe = thread->kernelstack;
107 // Add thread to thread list
108 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
109 keLockSpinlock(&threadlock);
110 threads = realloc(threads, (threadcount + 1) * sizeof(KeThread*));
111 threads[threadcount] = thread;
112 threadcount++;
113 keUnlockSpinlock(&threadlock);
114 keSetExecutionLevel(oldlevel);
115 // Add thread to process
116 keLockSpinlock(&process->lock);
117 process->threads = realloc(process->threads, (process->threadcount + 1) * sizeof(KeThread*));
118 process->threads[process->threadcount] = thread;
119 process->threadcount++;
120 keUnlockSpinlock(&process->lock);
122 return thread;
124 int keDestroyThread(KeThread *thread)
126 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
127 // Stop thread on current CPU
128 thread->status = KE_THREAD_KILLED;
129 KeCPU *cpu = keGetCurrentCPU();
130 if (cpu && cpu->currentthread == thread)
132 cpu->currentthread = 0;
133 //keUnlockSpinlock(&thread->active);
135 else
137 // Wait for other CPUs to schedule out of the thread
138 keLockSpinlock(&thread->active);
140 // Delete thread from list
141 keLockSpinlock(&threadlock);
142 uint32_t i;
143 for (i = 0; i < threadcount; i++)
145 if (threads[i] == thread)
147 memmove(&threads[i], &threads[i + 1],
148 (threadcount - i - 1) * sizeof(KeThread*));
149 threadcount--;
150 threads = realloc(threads, threadcount * sizeof(KeThread*));
151 break;
154 keUnlockSpinlock(&threadlock);
155 // Delete thread from process
156 KeProcess *process = thread->process;
157 if (process)
159 keLockSpinlock(&process->lock);
160 for (i = 0; i < process->threadcount; i++)
162 if (process->threads[i] == thread)
164 memmove(&process->threads[i], &process->threads[i + 1],
165 (process->threadcount - i - 1) * sizeof(KeThread*));
166 process->threadcount--;
167 process->threads = realloc(process->threads, process->threadcount * sizeof(KeThread*));
168 break;
171 keUnlockSpinlock(&process->lock);
173 keSetExecutionLevel(oldlevel);
174 // Delete stacks
175 // TODO: Larger stack
176 // FIXME: Delete kernel stack later (it's probably currently in use)
177 /*uintptr_t kernel_phys = mmKernelGetPhysAddress(thread->kernelstack_bottom);
178 mmMapKernelMemory(0, thread->kernelstack_bottom, 0);
179 mmFreePhysicalMemory(kernel_phys, 0x1000);*/
180 // TODO: User stack
181 return 0;
184 KeThread *keCloneThread(KeProcess *process, KeThread *thread, uintptr_t stack)
186 // Create thread struct
187 KeThread *newthread = malloc(sizeof(KeThread));
188 memset(newthread, 0, sizeof(KeThread));
189 newthread->process = process;
190 keInitSpinlock(&newthread->active);
191 newthread->userstack = thread->userstack;
192 newthread->userstack_size = thread->userstack_size;
193 // Setup kernel stack
194 uintptr_t kernelstack_virt = mmFindFreeKernelPages(MM_MAX_KERNEL_PAGE,
195 MM_MIN_KERNEL_PAGE, 1, 0x1000);
196 uintptr_t kernelstack_phys = mmAllocPhysicalMemory(0, 0, 0x1000);
197 mmMapKernelMemory(kernelstack_phys, kernelstack_virt, MM_MAP_READ | MM_MAP_WRITE);
198 newthread->kernelstack_bottom = kernelstack_virt;
199 memcpy((void*)kernelstack_virt, (void*)thread->kernelstack_bottom, 0x1000);
200 newthread->kernelstack = kernelstack_virt + (stack - thread->kernelstack_bottom);
201 newthread->userframe = newthread->kernelstack_bottom + thread->userframe - thread->kernelstack_bottom;
202 // Add thread to thread list
203 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
204 keLockSpinlock(&threadlock);
205 threads = realloc(threads, (threadcount + 1) * sizeof(KeThread*));
206 threads[threadcount] = newthread;
207 threadcount++;
208 keUnlockSpinlock(&threadlock);
209 keSetExecutionLevel(oldlevel);
210 // Add thread to process
211 keLockSpinlock(&process->lock);
212 process->threads = realloc(process->threads, (process->threadcount + 1) * sizeof(KeThread*));
213 process->threads[process->threadcount] = newthread;
214 process->threadcount++;
215 keUnlockSpinlock(&process->lock);
217 return thread;
221 KeThread *keCreateKernelThread(uintptr_t entry, uint32_t paramcount, ...)
223 uint32_t *params = &paramcount + 1;
224 KeThread *thread = malloc(sizeof(KeThread));
225 memset(thread, 0, sizeof(KeThread));
226 keInitSpinlock(&thread->active);
228 // Setup kernel stack
229 uintptr_t kernelstack_virt = mmFindFreeKernelPages(MM_MAX_KERNEL_PAGE,
230 MM_MIN_KERNEL_PAGE, 1, 0x1000);
231 uintptr_t kernelstack_phys = mmAllocPhysicalMemory(0, 0, 0x1000);
232 mmMapKernelMemory(kernelstack_phys, kernelstack_virt, MM_MAP_READ | MM_MAP_WRITE);
233 thread->kernelstack_bottom = kernelstack_virt;
234 uint32_t *stack = (uint32_t*)kernelstack_virt;
235 stack += 1024;
236 uint32_t i;
237 for (i = 0; i < paramcount; i++)
239 *(--stack) = params[paramcount - i - 1];
241 *(--stack) = 0x0BADC0DE;
242 *(--stack) = 0x202;
243 *(--stack) = 0x08;
244 *(--stack) = (uint32_t)entry;
245 *(--stack) = 0x0;
246 *(--stack) = 0x0;
247 *(--stack) = 0;
248 *(--stack) = 0x0;
249 *(--stack) = 0x0;
250 *(--stack) = 0x0;
251 *(--stack) = 0x0;
252 *(--stack) = 0x0;
253 *(--stack) = 0x0;
254 *(--stack) = 0x0;
255 *(--stack) = 0x10;
256 *(--stack) = 0x10;
257 *(--stack) = 0x10;
258 *(--stack) = 0x10;
259 thread->kernelstack = (uintptr_t)stack;
261 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
262 keLockSpinlock(&threadlock);
263 threads = realloc(threads, (threadcount + 1) * sizeof(KeThread*));
264 threads[threadcount] = thread;
265 threadcount++;
266 keUnlockSpinlock(&threadlock);
267 keSetExecutionLevel(oldlevel);
269 return thread;
271 KeThread *keCreateIdleThread(void)
273 KeThread *thread = malloc(sizeof(KeThread));
274 memset(thread, 0, sizeof(KeThread));
275 keInitSpinlock(&thread->active);
277 // Setup kernel stack
278 uintptr_t kernelstack_virt = mmFindFreeKernelPages(MM_MAX_KERNEL_PAGE,
279 MM_MIN_KERNEL_PAGE, 1, 0x2000);
280 kernelstack_virt += 0x1000;
281 uintptr_t kernelstack_phys = mmAllocPhysicalMemory(0, 0, 0x1000);
282 mmMapKernelMemory(kernelstack_phys, kernelstack_virt - 0x1000, MM_MAP_READ | MM_MAP_WRITE);
283 kernelstack_phys = mmAllocPhysicalMemory(0, 0, 0x1000);
284 mmMapKernelMemory(kernelstack_phys, kernelstack_virt, MM_MAP_READ | MM_MAP_WRITE);
285 thread->kernelstack_bottom = kernelstack_virt;
286 uint32_t *stack = (uint32_t*)kernelstack_virt;
287 stack += 1024;
288 *(--stack) = 0x202;
289 *(--stack) = 0x08;
290 *(--stack) = (uint32_t)keIdleThread;
291 *(--stack) = 0x0;
292 *(--stack) = 0x0;
293 *(--stack) = 0;
294 *(--stack) = 0x0;
295 *(--stack) = 0x0;
296 *(--stack) = 0x0;
297 *(--stack) = 0x0;
298 *(--stack) = 0x0;
299 *(--stack) = 0x0;
300 *(--stack) = 0x0;
301 *(--stack) = 0x10;
302 *(--stack) = 0x10;
303 *(--stack) = 0x10;
304 *(--stack) = 0x10;
305 thread->kernelstack = (uintptr_t)stack;
307 return thread;
310 void keThreadSetParam(KeThread *thread, uint8_t index, uint32_t value)
312 IntStackFrame *isf = (void*)thread->kernelstack;
313 switch (index)
315 case 0:
316 isf->eax = value;
317 break;
318 case 1:
319 isf->ebx = value;
320 break;
321 case 2:
322 isf->ecx = value;
323 break;
324 case 3:
325 isf->edx = value;
326 break;
327 case 4:
328 isf->esi = value;
329 break;
330 case 5:
331 isf->edi = value;
332 break;
333 default:
334 break;
337 uint32_t keThreadGetParam(KeThread *thread, uint8_t index)
339 IntStackFrame *isf = (void*)thread->kernelstack;
340 switch (index)
342 case 0:
343 return isf->eax;
344 case 1:
345 return isf->ebx;
346 case 2:
347 return isf->ecx;
348 case 3:
349 return isf->edx;
350 case 4:
351 return isf->esi;
352 case 5:
353 return isf->edi;
354 default:
355 return 0;
359 KeThread *keGetSchedulableThread(int oldthread)
361 KeThread *old = keGetCurrentCPU()->currentthread;
362 keLockSpinlock(&threadlock);
363 if (threadcount == 0) return 0;
364 uint32_t startthread = lastthread;
365 startthread %= threadcount;
366 uint32_t i = startthread;
369 i++;
370 i %= threadcount;
371 if ((threads[i]->status == KE_THREAD_SLEEP) && (threads[i]->timeout <= keGetTime()))
373 threads[i]->status = KE_THREAD_RUNNING;
374 threads[i]->timeout = 0;
376 if ((threads[i]->status == KE_THREAD_RUNNING) && !keTryLockSpinlock(&threads[i]->active))
378 lastthread = i;
379 keUnlockSpinlock(&threadlock);
380 return threads[i];
383 while (i != startthread);
384 keUnlockSpinlock(&threadlock);
385 if (oldthread)
386 return old;
387 return 0;
390 void keSleep(uint32_t ms)
392 KeExecLevel level = keGetExecutionLevel();
393 if (level == KE_LEVEL_HIGH)
395 kePrint("keSleep called at KE_LEVEL_HIGH!\n");
396 return;
398 KeCPU *cpu = keGetCurrentCPU();
399 if (!cpu) return;
400 cpu->currentthread->timeout = keGetTime() + ms + keGetTimePerTick();
401 cpu->currentthread->status = KE_THREAD_SLEEP;
402 // Schedule out of the thread
403 asm volatile("int $0x32");
405 void keExitThread(void)
407 asm volatile("int $0x33");