IBNOS
thread.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, Michael Müller
3  * Copyright (c) 2014, Sebastian Lackner
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include <process/thread.h>
29 #include <process/process.h>
30 #include <process/object.h>
31 #include <hardware/gdt.h>
32 #include <interrupt/interrupt.h>
33 #include <memory/physmem.h>
34 #include <memory/paging.h>
35 #include <memory/allocator.h>
36 #include <util/list.h>
37 #include <util/util.h>
38 
44 struct linkedList threadList = LL_INIT(threadList);
46 
47 static void __threadDestroy(struct object *obj);
48 static void __threadShutdown(struct object *obj, uint32_t mode);
49 static int32_t __threadGetStatus(struct object *obj, UNUSED uint32_t mode);
50 static struct linkedList *__threadWait(struct object *obj, UNUSED uint32_t mode, uint32_t *result);
51 static void __threadSignal(struct object *obj, uint32_t result);
52 
53 static const struct objectFunctions threadFunctions =
54 {
55  __threadDestroy,
56  NULL, /* getMinHandle */
57  __threadShutdown,
58  __threadGetStatus,
59  __threadWait,
60  __threadSignal,
61  NULL, /* write */
62  NULL, /* read */
63  NULL, /* insert */
64  NULL, /* remove */
65 };
66 
80 struct thread *threadCreate(struct process *p, struct thread *original, void *eip)
81 {
82  struct thread *t;
83  struct taskContext *task;;
84  assert(p);
85 
86  /* allocate some new memory */
87  if (!(t = heapAlloc(sizeof(*t))))
88  return NULL;
89 
90  /* initialize general object info */
91  __objectInit(&t->obj, &threadFunctions);
92  ll_add_tail(&threadList, &t->obj.entry);
93  ll_init(&t->waiters);
94  t->blocked = false;
95  t->process = p;
97  t->exitcode = -1;
98 
99  if (!original)
100  {
101  t->fpuInitialized = false;
102 
105 
108 
109  /* initialize the cpu registers */
110  task = &t->task;
111  memset(task, 0, sizeof(*task));
112  task->prevTask = 0;
115  task->esp1 = 0;
116  task->ss1 = 0;
117  task->esp2 = 0;
118  task->ss2 = 0;
119  task->cr3 = pagingGetPhysMem(NULL, p->pageDirectory) << PAGE_BITS;
120  task->eip = (uint32_t)eip;
121  task->eflags = (1 << 9); /* Enable interrupts */
122 
123  task->eax = 0;
124  task->ecx = 0;
125  task->edx = 0;
126  task->ebx = 0;
127  task->esp = (uint32_t)t->user_ring3StackBase + (t->user_ring3StackLength << PAGE_BITS);
128  task->ebp = 0;
129  task->esi = 0;
130  task->edi = 0;
137  task->ldt = 0;
138  task->iomap = sizeof(*task);
139 
140  }
141  else
142  {
143  /* forking a thread in the same process will not work! */
144  assert(original->process && p != original->process);
145 
146  t->fpuInitialized = original->fpuInitialized;
147 
150 
153 
154  /* initialize the cpu registers */
155  t->task = original->task;
156  t->task.cr3 = pagingGetPhysMem(NULL, p->pageDirectory) << PAGE_BITS;
157 
158  if (t->fpuInitialized)
159  {
160  /* we have to save the last fpu context to memory in order to make sure we copy the latest state */
161  if (lastFPUthread == original)
162  {
163  asm volatile("clts");
164  asm volatile("fnsave %0; fwait" : "=m" (lastFPUthread->fpu));
165  }
166 
167  t->fpu = original->fpu;
168  }
169  }
170 
171  /* addref for the corresponding process and thread */
172  objectAddRef(t);
173  objectAddRef(t->process);
174 
175  return t;
176 }
177 
183 static void __threadDestroy(struct object *obj)
184 {
185  struct thread *t = objectContainer(obj, struct thread, &threadFunctions);
186  if (t == lastFPUthread) lastFPUthread = NULL;
187 
188  /* there should be no more threads waiting on this one */
189  assert(ll_empty(&t->waiters));
190 
191  /* thread should be disassociated from thread */
192  assert(!t->process);
193 
194  /* release thread memory */
195  t->obj.functions = NULL;
196  heapFree(t);
197 }
198 
209 static void __threadShutdown(struct object *obj, uint32_t exitcode)
210 {
211  struct thread *t = objectContainer(obj, struct thread, &threadFunctions);
212  struct process *p = t->process;
213  if (t == lastFPUthread) lastFPUthread = NULL;
214 
215  if (t->process)
216  {
217  t->process = NULL;
218  t->exitcode = exitcode;
219 
222 
223  /* release stacks */
225  t->user_threadLocalBase = NULL;
226 
228  t->user_ring3StackBase = NULL;
229 
230  ll_remove(&t->obj.entry);
231  ll_remove(&t->entry_process);
232 
233  /* wake up waiting threads */
234  queueWakeup(&t->waiters, true, t->exitcode);
235 
236  /* special case: all threads termined, but nothing set the exit code */
237  if (ll_empty(&p->threads))
238  objectShutdown(p, t->exitcode);
239 
240  /* release objects */
241  objectRelease(t);
242  objectRelease(p);
243  }
244 }
245 
258 static int32_t __threadGetStatus(struct object *obj, UNUSED uint32_t mode)
259 {
260  struct thread *t = objectContainer(obj, struct thread, &threadFunctions);
261  return (t->process != NULL) ? (-1) : (signed)t->exitcode;
262 }
263 
275 static struct linkedList *__threadWait(struct object *obj, UNUSED uint32_t mode, uint32_t *result)
276 {
277  struct thread *t = objectContainer(obj, struct thread, &threadFunctions);
278  if (t->process != NULL) return &t->waiters;
279 
280  *result = t->exitcode;
281  return NULL;
282 }
283 
295 static void __threadSignal(struct object *obj, uint32_t result)
296 {
297  struct thread *t = objectContainer(obj, struct thread, &threadFunctions);
298  if (!t->process || !t->blocked) return;
299 
300  t->blocked = false;
301  t->task.eax = result;
302  ll_remove(&t->obj.entry);
303  ll_add_tail(&threadList, &t->obj.entry);
304 }
305 
312 static struct thread *__threadRun(struct thread *t)
313 {
314  struct thread *next_t = NULL;
315  struct process *p = t->process;
316  uint32_t status = INTERRUPT_CONTINUE_EXECUTION;
317 
318  /* get next thread in the linked list */
319  next_t = LL_ENTRY(t->obj.entry.next, struct thread, obj.entry);
320 
321  if (p)
322  {
323  /* run task and dispatch the interrupt */
324  while (status == INTERRUPT_CONTINUE_EXECUTION)
325  {
326  assert(t->process == p && !t->blocked);
327  status = tssRunUsermodeThread(t);
328  }
329  }
330 
331  /* handle special cases */
332  if (status == INTERRUPT_EXIT_THREAD)
333  {
334  /* shutdown the current thread (using the borrowed reference) */
335  objectShutdown(t, t->task.ebx);
336  }
337  else if (status == INTERRUPT_EXIT_PROCESS)
338  {
339  /* get pointer to the next process */
340  while (&next_t->obj.entry != &threadList && next_t->process == p)
341  next_t = LL_ENTRY(next_t->obj.entry.next, struct thread, obj.entry);
342 
343  /* terminate the current process (using the borrowed reference) */
344  objectShutdown(p, t->task.ebx);
345  }
346 
347  return next_t;
348 }
349 
356 {
357  struct thread *t;
358 
359  /* if the last process is terminated there is nothing we can do */
360  while (!ll_empty(&processList))
361  {
362 
363  /* as long as threads are available schedule them one after each other */
364  while (!ll_empty(&threadList))
365  {
366  /* we automatically get a pointer to the next thread which should be executed */
367  for (t = LL_ENTRY(threadList.next, struct thread, obj.entry); &t->obj.entry != &threadList;)
368  t = __threadRun(t);
369  }
370 
371  /* enable interrupts and wait */
372  tssKernelIdle();
373  }
374 }
375 
388 uint32_t threadWait(struct thread *t, struct object *obj, uint32_t mode)
389 {
390  struct linkedList *queue;
391  uint32_t status = INTERRUPT_CONTINUE_EXECUTION;
392 
393  t->task.eax = 0;
394  queue = __objectWait(obj, mode, &t->task.eax);
395  if (queue)
396  {
397  assert(!t->blocked);
398  t->blocked = true;
399  ll_remove(&t->obj.entry);
400  ll_add_after(queue, &t->obj.entry);
401  status = INTERRUPT_YIELD;
402  }
403 
404  return status;
405 }
406 
uint32_t ebx
Definition: context.h:55
struct thread * threadCreate(struct process *p, struct thread *original, void *eip)
Creates a new kernel thread object.
Definition: thread.c:80
#define USERMODE_KERNELSTACK_LIMIT
Definition: gdt.h:99
uint16_t fs
Definition: context.h:69
uint16_t cs
Definition: context.h:63
const struct objectFunctions * functions
Definition: object.h:72
void * heapAlloc(uint32_t length)
Allocates a block of kernel memory.
Definition: allocator.c:363
uint16_t ss1
Definition: context.h:41
#define assert(ex)
Definition: util.h:61
uint32_t eflags
Definition: context.h:50
uint16_t ss2
Definition: context.h:45
#define UNUSED
Definition: util.h:39
#define GDT_CPL_RING3
Definition: gdt.h:47
void * memset(void *ptr, int value, size_t num)
Fills a memory region with some specific byte value.
Definition: util.c:123
uint32_t ecx
Definition: context.h:53
uint32_t edx
Definition: context.h:54
#define objectShutdown(p, a)
Definition: object.h:111
uint16_t iomap
Definition: context.h:77
bool fpuInitialized
Definition: thread.h:59
struct linkedList threadList
Definition: thread.c:44
void heapFree(void *addr)
Deallocates a block of kernel memory.
Definition: allocator.c:385
struct pagingEntry * pageDirectory
Definition: process.h:75
void * pagingAllocatePhysMem(struct process *p, uint32_t length, bool rw, bool user)
Allocates several pages of physical memory in a process.
Definition: paging.c:659
void threadSchedule()
Schedules threads until all process have been terminated.
Definition: thread.c:355
#define DEFAULT_STACK_SIZE
Definition: thread.h:73
void * user_ring3StackBase
Definition: thread.h:62
struct thread * lastFPUthread
Definition: thread.c:45
#define PAGE_BITS
Definition: physmem.h:37
#define LL_INIT(list)
Initializes a linkedList.
Definition: list.h:124
#define INTERRUPT_YIELD
Definition: interrupt.h:50
struct object obj
Definition: thread.h:49
#define LL_ENTRY(element, type, field)
Definition: list.h:127
uint32_t eax
Definition: context.h:52
Definition: thread.h:47
struct GDTEntry * codeRing3
Definition: gdt.c:65
void * user_threadLocalBase
Definition: thread.h:66
uint32_t esi
Definition: context.h:58
struct GDTEntry * dataRing0
Definition: gdt.c:64
uint32_t user_threadLocalLength
Definition: thread.h:67
#define INTERRUPT_EXIT_PROCESS
Definition: interrupt.h:54
#define DEFAULT_TLB_SIZE
Definition: thread.h:74
uint16_t prevTask
Definition: context.h:33
void pagingReleasePhysMem(struct process *p, void *addr, uint32_t length)
Releases several pages of physical memory in a process.
Definition: paging.c:1034
struct linkedList * next
Definition: list.h:36
struct linkedList threads
Definition: process.h:72
#define objectAddRef(p)
Definition: object.h:89
uint32_t esp2
Definition: context.h:44
#define objectContainer(p, type, functions)
Definition: object.h:66
uint32_t eip
Definition: context.h:186
uint32_t gdtGetEntryOffset(struct GDTEntry *entry, uint32_t ring)
Determines the offset of a GDT entry.
Definition: gdt.c:537
uint32_t tssRunUsermodeThread(struct thread *t)
Run a thread.
Definition: gdt.c:628
uint32_t cr3
Definition: context.h:48
struct linkedList waiters
Definition: thread.h:50
uint32_t exitcode
Definition: thread.h:56
uint32_t eip
Definition: context.h:49
struct linkedList entry
Definition: object.h:73
#define ll_add_tail
Definition: list.h:100
struct GDTEntry * dataRing3
Definition: gdt.c:66
uint16_t gs
Definition: context.h:71
struct linkedList entry_process
Definition: thread.h:55
uint32_t esp0
Definition: context.h:36
struct fpuContext fpu
Definition: thread.h:70
struct process * process
Definition: thread.h:54
uint32_t pagingGetPhysMem(struct process *p, void *addr)
Returns the physical page index for a virtual address.
Definition: paging.c:1111
uint16_t ss0
Definition: context.h:37
uint16_t ldt
Definition: context.h:74
#define GDT_CPL_RING0
Definition: gdt.h:46
#define INTERRUPT_CONTINUE_EXECUTION
Definition: interrupt.h:48
#define objectRelease(p)
Definition: object.h:97
uint16_t ds
Definition: context.h:67
uint32_t esp1
Definition: context.h:40
struct linkedList processList
Definition: process.c:43
#define INTERRUPT_EXIT_THREAD
Definition: interrupt.h:52
uint16_t es
Definition: context.h:61
uint32_t edi
Definition: context.h:59
uint16_t ss
Definition: context.h:65
struct taskContext task
Definition: thread.h:69
uint32_t ebp
Definition: context.h:57
uint32_t threadWait(struct thread *t, struct object *obj, uint32_t mode)
Makes a kernel thread object wait for some waitable object.
Definition: thread.c:388
char mode[8]
Definition: filesystem.h:65
uint32_t user_ring3StackLength
Definition: thread.h:63
uint32_t esp
Definition: context.h:56
bool blocked
Definition: thread.h:51
uint32_t exitcode
Definition: process.h:69