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"
29 #include "ke/interrupts.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;
49 KeThread
*keCreateThread(KeProcess
*process
, uintptr_t entry
, uint32_t paramcount
, ...)
51 uint32_t *params
= ¶mcount
+ 1;
52 KeThread
*thread
= malloc(sizeof(KeThread
));
53 memset(thread
, 0, sizeof(KeThread
));
54 thread
->process
= process
;
55 keInitSpinlock(&thread
->active
);
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
);
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
;
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
;
86 *(--stack
) = thread
->userstack
+ 0x1000 - paramcount
* 4 - 4;
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
;
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
);
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);
137 // Wait for other CPUs to schedule out of the thread
138 keLockSpinlock(&thread
->active
);
140 // Delete thread from list
141 keLockSpinlock(&threadlock
);
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
*));
150 threads
= realloc(threads
, threadcount
* sizeof(KeThread
*));
154 keUnlockSpinlock(&threadlock
);
155 // Delete thread from process
156 KeProcess
*process
= thread
->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
*));
171 keUnlockSpinlock(&process
->lock
);
173 keSetExecutionLevel(oldlevel
);
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);*/
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
;
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
);
221 KeThread
*keCreateKernelThread(uintptr_t entry
, uint32_t paramcount
, ...)
223 uint32_t *params
= ¶mcount
+ 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
;
237 for (i
= 0; i
< paramcount
; i
++)
239 *(--stack
) = params
[paramcount
- i
- 1];
241 *(--stack
) = 0x0BADC0DE;
244 *(--stack
) = (uint32_t)entry
;
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
;
266 keUnlockSpinlock(&threadlock
);
267 keSetExecutionLevel(oldlevel
);
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
;
290 *(--stack
) = (uint32_t)keIdleThread
;
305 thread
->kernelstack
= (uintptr_t)stack
;
310 void keThreadSetParam(KeThread
*thread
, uint8_t index
, uint32_t value
)
312 IntStackFrame
*isf
= (void*)thread
->kernelstack
;
337 uint32_t keThreadGetParam(KeThread
*thread
, uint8_t index
)
339 IntStackFrame
*isf
= (void*)thread
->kernelstack
;
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
;
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
))
379 keUnlockSpinlock(&threadlock
);
383 while (i
!= startthread
);
384 keUnlockSpinlock(&threadlock
);
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");
398 KeCPU
*cpu
= keGetCurrentCPU();
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");