5613b2fc8ce7747aaf628ce0580a9c0020c823f0
[barrelfish] / lib / barrelfish / threads.c
1 /**
2  * \file
3  * \brief Threads implementation.
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <barrelfish/barrelfish.h>
19 #include <barrelfish/dispatch.h>
20 #include <barrelfish/dispatcher_arch.h>
21 #include <barrelfish/debug.h>
22 #include <barrelfish/slab.h>
23 #include <barrelfish/caddr.h>
24 #include <barrelfish/curdispatcher_arch.h>
25 #include <barrelfish/vspace_mmu_aware.h>
26 #include <barrelfish_kpi/cpu_arch.h>
27 #include <barrelfish_kpi/domain_params.h>
28 #include <arch/registers.h>
29 #include <trace/trace.h>
30
31 #include <trace_definitions/trace_defs.h>
32
33 #include "arch/threads.h"
34 #include "threads_priv.h"
35 #include "init.h"
36
37 #if defined(__x86_64__)
38 #  include "arch/ldt.h"
39 #endif
40
41 #ifdef FPU_LAZY_CONTEXT_SWITCH
42 #  include <arch/fpu.h>
43 #endif
44
45 /// Maximum number of threads in a domain, used to size VM region for thread structures
46 // there is no point having MAX_THREADS > LDT_NENTRIES on x86 (see ldt.c)
47 #define MAX_THREADS 256
48
49 /// 16-byte alignment required for x86-64
50 // FIXME: this should be in an arch header
51 #define STACK_ALIGNMENT (sizeof(uint64_t) * 2)
52
53 /// Static stack and storage for a bootstrap/cleanup thread
54 // XXX: 16-byte aligned for x86-64
55 static uintptr_t staticstack[THREADS_DEFAULT_STACK_BYTES / sizeof(uintptr_t)]
56 __attribute__((aligned(STACK_ALIGNMENT)));
57 static struct thread staticthread = {
58     .stack = staticstack,
59     .stack_top = (char *)staticstack + sizeof(staticstack)
60 };
61 static struct thread_mutex staticthread_lock = THREAD_MUTEX_INITIALIZER;
62
63 /// Storage metadata for thread structures (and TLS data)
64 static struct slab_allocator thread_slabs;
65 static struct vspace_mmu_aware thread_slabs_vm;
66
67 // XXX: mutex and spinlock protecting thread slabs in spanned domains
68 /* This ought to be just a mutex. However, thread_create() is called on the
69  * inter-disp message handler thread, and if it blocks in a mutex, there is no
70  * way to wake it up and we will deadlock. This is a quick-fix workaround:
71  *   The spinlock protects the data structure
72  *   The mutex avoids unneccessary spinning (it is acquired first when safe)
73  */
74 static spinlock_t thread_slabs_spinlock;
75 static struct thread_mutex thread_slabs_mutex = THREAD_MUTEX_INITIALIZER;
76
77 /// Base and size of the original ("pristine") thread-local storage init data
78 static void *tls_block_init_base;
79 static size_t tls_block_init_len;
80 static size_t tls_block_total_len;
81
82 /// Warning already issued about RSP usage.  (Prevent repeated warnings
83 /// from the same domain -- e.g., when using THC whose stacks appear
84 /// invalid here).
85 __attribute__((unused)) static bool stack_warned=0;
86
87 /// Wrapper function for most threads, runs given function then deletes itself
88 static void thread_entry(thread_func_t start_func, void *start_data)
89 {
90     assert((lvaddr_t)start_func >= BASE_PAGE_SIZE);
91     int retval = start_func(start_data);
92     thread_exit(retval);
93     assert(!"thread_exit returned");
94 }
95
96 /// int counter for assigning initial thread ids
97 static uintptr_t threadid = 0;
98
99 #ifndef NDEBUG
100 /// Debugging assertions on thread queues
101 static void check_queue(struct thread *queue)
102 {
103     if (queue == NULL) {
104         return;
105     }
106     struct thread *q = queue;
107     int i = 0;
108
109     do {
110         assert_disabled(q != NULL);
111
112         // check for NULL next and prev pointers
113         assert_disabled((lvaddr_t)q->next > BASE_PAGE_SIZE);
114         assert_disabled((lvaddr_t)q->prev > BASE_PAGE_SIZE);
115
116         // check that next and prev pointers are sane
117         assert_disabled(q->next->prev == q);
118         assert_disabled(q->prev->next == q);
119
120         // advance to next elem
121         q = q->next;
122         i++;
123         assert_disabled(i < MAX_THREADS);
124     } while (q != queue);
125 }
126 #else /* NDEBUG version */
127 static inline void check_queue(struct thread *queue) {}
128 #endif
129
130 /**
131  * \brief Enqueue a thread in the given queue
132  *
133  * For safety, should only happen while disabled.
134  */
135 void thread_enqueue(struct thread *thread, struct thread **queue)
136 {
137     assert_disabled(thread != NULL);
138     assert_disabled(queue != NULL);
139     check_queue(*queue);
140     if (*queue == NULL) {
141         *queue = thread->prev = thread->next = thread;
142     } else {
143         assert_disabled((*queue)->prev != NULL);
144         thread->prev = (*queue)->prev;
145         thread->next = *queue;
146         (*queue)->prev = thread;
147         assert_disabled(thread->prev != NULL);
148         thread->prev->next = thread;
149     }
150
151     check_queue(*queue);
152 }
153
154 /**
155  * \brief Dequeue the first thread on the given queue
156  *
157  * For safety, should only happen while disabled.
158  *
159  * \returns Pointer to thread that was dequeued
160  */
161 struct thread *thread_dequeue(struct thread **queue)
162 {
163     assert_disabled(queue != NULL);
164     struct thread *thread = *queue;
165     assert_disabled(thread != NULL);
166     check_queue(thread);
167     if (thread->prev == thread) {
168         assert_disabled(thread->next == thread);
169         *queue = NULL;
170     } else {
171         thread->prev->next = thread->next;
172         thread->next->prev = thread->prev;
173         *queue = thread->next;
174     }
175     check_queue(*queue);
176 #ifndef NDEBUG
177     thread->prev = thread->next = NULL;
178 #endif
179     return thread;
180 }
181
182 /**
183  * \brief Remove a specific thread from a queue
184  *
185  * Does not check that the thread is in the given queue, which it must be.
186  * For safety, should only happen while disabled.
187  */
188 void thread_remove_from_queue(struct thread **queue, struct thread *thread)
189 {
190     assert_disabled(queue != NULL);
191     assert_disabled(thread != NULL);
192     check_queue(*queue);
193     if (thread->prev == thread) {
194         assert_disabled(thread->next == thread);
195         assert_disabled(*queue == thread);
196         *queue = NULL;
197     } else {
198         thread->prev->next = thread->next;
199         thread->next->prev = thread->prev;
200         if (*queue == thread) {
201             *queue = thread->next;
202         }
203     }
204     check_queue(*queue);
205 #ifndef NDEBUG
206     thread->prev = thread->next = NULL;
207 #endif
208 }
209
210 /// Refill backing storage for thread region
211 static errval_t refill_thread_slabs(struct slab_allocator *slabs)
212 {
213     assert(slabs == &thread_slabs);
214
215     size_t size;
216     void *buf;
217     errval_t err;
218
219     size_t blocksize = sizeof(struct thread) + tls_block_total_len;
220     err = vspace_mmu_aware_map(&thread_slabs_vm, blocksize, &buf, &size);
221     if (err_is_fail(err)) {
222         return err_push(err, LIB_ERR_VSPACE_MMU_AWARE_MAP);
223     }
224
225     slab_grow(slabs, buf, size);
226
227     return SYS_ERR_OK;
228 }
229
230 /// Initialise the state of a new thread structure
231 static void thread_init(dispatcher_handle_t disp, struct thread *newthread)
232 {
233     newthread->self = newthread;
234 #ifndef NDEBUG
235     newthread->next = newthread->prev = NULL;
236 #endif
237     newthread->tls_dtv = NULL;
238     newthread->disp = disp;
239     newthread->coreid = get_dispatcher_generic(disp)->core_id;
240     newthread->userptr = NULL;
241     memset(newthread->userptrs, 0, sizeof(newthread->userptrs));
242     newthread->yield_epoch = 0;
243     newthread->wakeup_reason = NULL;
244     newthread->return_value = 0;
245     thread_cond_init(&newthread->exit_condition);
246     thread_mutex_init(&newthread->exit_lock);
247     newthread->state = THREAD_STATE_RUNNABLE;
248     newthread->detached = false;
249     newthread->joining = false;
250     newthread->in_exception = false;
251     newthread->used_fpu = false;
252     newthread->paused = false;
253     newthread->slab = NULL;
254     newthread->token = 0;
255     newthread->token_number = 1;
256
257     newthread->rpc_in_progress = false;
258     newthread->async_error = SYS_ERR_OK;
259     newthread->local_trigger = NULL;
260 }
261
262 /**
263  * \brief Handle FPU context switching
264  */
265 static void fpu_context_switch(struct dispatcher_generic *disp_gen,
266                                struct thread * next)
267 {
268 #ifdef FPU_LAZY_CONTEXT_SWITCH
269     if(disp_gen->fpu_thread == NULL) {
270         // FPU wasn't used -- no switching necessary
271         return;
272     }
273
274     // Switch FPU trap back on if we switch away from FPU thread
275     if(disp_gen->fpu_thread == disp_gen->current &&
276        disp_gen->fpu_thread != next) {
277         fpu_trap_on();
278     }
279 #endif
280 }
281
282 /**
283  * \brief Returns false if the stack pointer is out of bounds.
284  */
285 static bool thread_check_stack_bounds(struct thread *thread,
286                                       arch_registers_state_t *archregs) {
287     lvaddr_t sp = (lvaddr_t) registers_get_sp(archregs);
288     return sp > (lvaddr_t)thread->stack ||
289            sp <= (lvaddr_t)thread->stack_top;
290 }
291
292 /**
293  * \brief Schedule and run the next active thread, or yield the dispatcher.
294  *
295  * This may only be called from the dispatcher (on its stack and while
296  * disabled!).
297  *
298  * \param disp Dispatcher pointer
299  */
300 void thread_run_disabled(dispatcher_handle_t handle)
301 {
302     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
303     struct dispatcher_shared_generic *disp =
304         get_dispatcher_shared_generic(handle);
305     arch_registers_state_t *enabled_area =
306         dispatcher_get_enabled_save_area(handle);
307
308     if (disp_gen->current != NULL) {
309         assert_disabled(disp_gen->runq != NULL);
310
311         // check stack bounds
312         warn_disabled(&stack_warned,
313                       thread_check_stack_bounds(disp_gen->current, enabled_area));
314
315         struct thread *next = disp_gen->current->next;
316         assert_disabled(next != NULL);
317         if (next != disp_gen->current) {
318             fpu_context_switch(disp_gen, next);
319
320             // save previous thread's state
321             arch_registers_state_t *cur_regs = &disp_gen->current->regs;
322             memcpy(cur_regs, enabled_area, sizeof(arch_registers_state_t));
323             disp_gen->current = next;
324             disp_resume(handle, &next->regs);
325         } else {
326             // same thread as before
327             disp_resume(handle, enabled_area);
328         }
329     } else if (disp_gen->runq != NULL) {
330         fpu_context_switch(disp_gen, disp_gen->runq);
331         disp_gen->current = disp_gen->runq;
332         disp->haswork = true;
333         disp_resume(handle, &disp_gen->runq->regs);
334     } else {
335         // kernel gave us the CPU when we have nothing to do. block!
336         disp->haswork = havework_disabled(handle);
337         disp_gen->current = NULL;
338         disp_yield_disabled(handle);
339     }
340 }
341
342 /** Free all heap/slab-allocated state associated with a thread */
343 static void free_thread(struct thread *thread)
344 {
345 #if defined(__x86_64__) // XXX: gungy segment selector stuff
346     assert(thread->thread_seg_selector != 0);
347     uint16_t fs;
348     __asm("mov %%fs, %0" : "=r" (fs));
349     if (thread->thread_seg_selector == fs) {
350         assert(thread->disp == curdispatcher());
351         struct dispatcher_x86_64 *disp_priv = get_dispatcher_x86_64(thread->disp);
352         // we're freeing the current thread; make sure we reload a valid segment
353         // selector so that curdispatcher() keeps working!
354         __asm volatile("mov %%ax, %%fs"
355                        : /* No outputs */
356                        : "a" (disp_priv->disp_seg_selector));
357     }
358     ldt_free_segment(thread->thread_seg_selector);
359 #endif
360
361     free(thread->stack);
362     if (thread->tls_dtv != NULL) {
363         free(thread->tls_dtv);
364     }
365
366     thread_mutex_lock(&thread_slabs_mutex);
367     acquire_spinlock(&thread_slabs_spinlock);
368     slab_free(&thread_slabs, thread->slab); // frees thread itself
369     release_spinlock(&thread_slabs_spinlock);
370     thread_mutex_unlock(&thread_slabs_mutex);
371 }
372
373 /**
374  * \brief Creates a new thread that will not be runnable
375  *
376  * \param start_func Function to run on the new thread
377  * \param arg Argument to pass to function
378  * \param stacksize Size of stack, in bytes
379  *
380  * \returns Thread pointer on success, NULL on failure
381  */
382 struct thread *thread_create_unrunnable(thread_func_t start_func, void *arg,
383                                         size_t stacksize)
384 {
385     // allocate stack
386     assert((stacksize % sizeof(uintptr_t)) == 0);
387     void *stack = malloc(stacksize);
388     if (stack == NULL) {
389         return NULL;
390     }
391
392     // allocate space for TCB + initial TLS data
393     // no mutex as it may deadlock: see comment for thread_slabs_spinlock
394     // thread_mutex_lock(&thread_slabs_mutex);
395     acquire_spinlock(&thread_slabs_spinlock);
396     void *space = slab_alloc(&thread_slabs);
397     release_spinlock(&thread_slabs_spinlock);
398     // thread_mutex_unlock(&thread_slabs_mutex);
399     if (space == NULL) {
400         free(stack);
401         return NULL;
402     }
403
404     // split space into TLS data followed by TCB
405     // XXX: this layout is specific to the x86 ABIs! once other (saner)
406     // architectures support TLS, we'll need to break out the logic.
407     void *tls_data = space;
408     struct thread *newthread = (void *)((uintptr_t)space + tls_block_total_len);
409
410     // init thread
411     thread_init(curdispatcher(), newthread);
412     newthread->slab = space;
413
414     if (tls_block_total_len > 0) {
415         // populate initial TLS data from pristine copy
416         assert(tls_block_init_len <= tls_block_total_len);
417         memcpy(tls_data, tls_block_init_base, tls_block_init_len);
418
419         // zero-fill remainder
420         memset((char *)tls_data + tls_block_init_len, 0,
421                tls_block_total_len - tls_block_init_len);
422
423         // create a TLS thread vector
424         struct tls_dtv *dtv = malloc(sizeof(struct tls_dtv) + 1 * sizeof(void *));
425         assert(dtv != NULL);
426
427         dtv->gen = 0;
428         dtv->dtv[0] = tls_data;
429         newthread->tls_dtv = dtv;
430     }
431
432     // FIXME: make arch-specific
433 #if defined(__x86_64__) || defined(__k1om__)
434     // create segment for TCB
435     errval_t err = ldt_alloc_segment(newthread, &newthread->thread_seg_selector);
436     if (err_is_fail(err)) {
437         DEBUG_ERR(err, "error allocating LDT segment for new thread");
438         free_thread(newthread);
439         free(stack);
440         return NULL;
441     }
442 #endif
443
444     // init stack
445     newthread->stack = stack;
446     newthread->stack_top = (char *)stack + stacksize;
447
448     // waste space for alignment, if malloc gave us an unaligned stack
449     newthread->stack_top = (char *)newthread->stack_top
450         - (lvaddr_t)newthread->stack_top % STACK_ALIGNMENT;
451
452     // set thread's ID
453     newthread->id = threadid++;
454
455     // init registers
456     registers_set_initial(&newthread->regs, newthread, (lvaddr_t)thread_entry,
457                           (lvaddr_t)newthread->stack_top,
458                           (lvaddr_t)start_func, (lvaddr_t)arg, 0, 0);
459
460     return newthread;
461 }
462
463 /**
464  * \brief Creates a new thread, and makes it runnable
465  *
466  * \param start_func Function to run on the new thread
467  * \param arg Argument to pass to function
468  * \param stacksize Size of stack, in bytes
469  *
470  * \returns Thread pointer on success, NULL on failure
471  */
472 struct thread *thread_create_varstack(thread_func_t start_func, void *arg,
473                                       size_t stacksize)
474 {
475     struct thread *newthread = thread_create_unrunnable(start_func, arg, stacksize);
476     if (newthread) {
477         // enqueue on runq
478         dispatcher_handle_t handle = disp_disable();
479         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
480         newthread->disp = handle;
481         thread_enqueue(newthread, &disp_gen->runq);
482         disp_enable(handle);
483     }
484     return newthread;
485 }
486
487 /**
488  * \brief Creates a new thread, and makes it runnable
489  *
490  * \param start_func Function to run on the new thread
491  * \param arg Argument to pass to function
492  *
493  * \returns Thread pointer on success, NULL on failure
494  */
495 struct thread *thread_create(thread_func_t start_func, void *arg)
496 {
497     return thread_create_varstack(start_func, arg, THREADS_DEFAULT_STACK_BYTES);
498 }
499
500 /**
501  * \brief Wait for termination of another thread
502  *
503  * \param thread        Pointer to thread to wait for
504  * \param retval        Pointer to variable to hold return value of thread, or NULL
505  *
506  * \returns SYS_ERR_OK on success, error code on error.
507  */
508 errval_t thread_join(struct thread *thread, int *retval)
509 {
510     assert(thread != NULL);
511     // this function should only be called for threads on same core
512     assert(thread->coreid == disp_get_core_id());
513
514     thread_mutex_lock(&thread->exit_lock);
515     if(thread->detached) {
516         // Thread is detached and thus not joinable
517         thread_mutex_unlock(&thread->exit_lock);
518         return LIB_ERR_THREAD_JOIN_DETACHED;
519     }
520
521     if(thread->joining) {
522         // Someone else already joins, that's an error
523         thread_mutex_unlock(&thread->exit_lock);
524         return LIB_ERR_THREAD_JOIN;
525     }
526
527     thread->joining = true;
528     if(thread->state != THREAD_STATE_EXITED) { // Possibly wait for thread exit
529         thread_cond_wait(&thread->exit_condition, &thread->exit_lock);
530     }
531
532     if(retval != NULL) {
533         *retval = thread->return_value;
534     }
535
536     thread_mutex_unlock(&thread->exit_lock);    // Not really needed
537     free_thread(thread);
538
539     return SYS_ERR_OK;
540 }
541
542 /**
543  * \brief Detach a thread. Free its state when it terminates.
544  *
545  * \param thread        Pointer to thread to detach
546  *
547  * \return SYS_ERR_OK on success.
548  */
549 errval_t thread_detach(struct thread *thread)
550 {
551     assert(thread != NULL);
552     thread_mutex_lock(&thread->exit_lock);
553
554     if(thread->joining) {
555         // Someone else already joins, that's an error
556         thread_mutex_unlock(&thread->exit_lock);
557         return LIB_ERR_THREAD_JOIN;
558     }
559
560     if(!thread->detached) {
561         thread->detached = true;
562     } else {
563         // Detaching more than once is an error
564         thread_mutex_unlock(&thread->exit_lock);
565         return LIB_ERR_THREAD_DETACHED;
566     }
567
568     if(thread->state == THREAD_STATE_EXITED) {
569         // Thread already exited before we detached, clean it up
570         free_thread(thread);
571         return SYS_ERR_OK;
572     }
573
574     thread_mutex_unlock(&thread->exit_lock);
575     return SYS_ERR_OK;
576 }
577
578 /**
579  * \brief Returns the thread pointer to the currently-running thread
580  */
581 struct thread *thread_self(void)
582 {
583     struct thread *me;
584 #if defined(__x86_64__) // XXX: AB's silly little arch-specific optimisation
585     __asm("movq %%fs:0, %0" : "=r" (me));
586 #else
587     // it's not necessary to disable, but might be once we do migration
588     bool was_enabled;
589     dispatcher_handle_t handle = disp_try_disable(&was_enabled);
590     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
591     me = disp_gen->current;
592     if (was_enabled)
593         disp_enable(handle);
594 #endif
595     return me;
596 }
597
598 struct thread *thread_self_disabled(void)
599 {
600     dispatcher_handle_t handle = curdispatcher();
601     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
602     return disp_gen->current;
603 }
604
605 uintptr_t thread_id(void)
606 {
607     return thread_self()->id;
608 }
609
610 uintptr_t thread_get_id(struct thread *t)
611 {
612     return t->id;
613 }
614
615 void thread_set_id(uintptr_t id)
616 {
617     struct thread *me = thread_self();
618     me->id = id;
619 }
620
621 uint32_t thread_set_token(struct waitset_chanstate *channel)
622 {
623     struct thread *me = thread_self();
624     // generate new token
625     uint32_t outgoing_token = (uint32_t)((me->id << 16) |
626          (me->coreid << 24) | ((me->token_number & 255) << 8)) | 1;
627     assert(me->token == 0);
628     me->token_number++;
629     if (!(me->token_number & 255))
630         me->token_number = 1;
631     me->token = outgoing_token & ~1;    // wait for this token
632     me->channel = channel;              // on that channel
633     return outgoing_token;
634 }
635
636 void thread_clear_token(struct waitset_chanstate *channel)
637 {
638     struct thread *me = thread_self();
639
640     me->token = 0;      // don't wait anymore
641     me->channel = NULL;
642 }
643
644 uint32_t thread_current_token(void)
645 {
646     return thread_self()->token;
647 }
648
649 void thread_set_outgoing_token(uint32_t token)
650 {
651     struct thread *me = thread_self();
652
653     assert(!me->outgoing_token);
654     me->outgoing_token = token;
655 }
656
657 void thread_get_outgoing_token(uint32_t *token)
658 {
659     struct thread *me = thread_self();
660     // if thread's outgoing token is set, get it
661     if (me->outgoing_token) {
662         *token = me->outgoing_token;
663         me->outgoing_token = 0;
664     }
665 }
666
667 void thread_set_local_trigger(struct waitset_chanstate *trigger)
668 {
669     struct thread *me = thread_self();
670     me->local_trigger = trigger;
671 }
672
673 struct waitset_chanstate * thread_get_local_trigger(void)
674 {
675     struct thread *me = thread_self();
676     return me->local_trigger;
677 }
678
679 void thread_set_rpc_in_progress(bool v)
680 {
681     thread_self()->rpc_in_progress = v;
682 }
683
684 bool thread_get_rpc_in_progress(void)
685 {
686     return thread_self()->rpc_in_progress;
687 }
688
689 void thread_set_async_error(errval_t e)
690 {
691     thread_self()->async_error = e;
692 }
693
694 errval_t thread_get_async_error(void)
695 {
696     return thread_self()->async_error;
697 }
698
699 /**
700  * \brief Store receive slot provided by rpc in thread state
701  */
702 void thread_store_recv_slot(struct capref recv_slot)
703 {
704     struct thread *me = thread_self();
705     assert(me);
706
707     assert(me->recv_slot_count < MAX_RECV_SLOTS);
708     assert(me->recv_slot_count >= 0);
709
710     me->recv_slots[me->recv_slot_count++] = recv_slot;
711 }
712
713 struct capref thread_get_next_recv_slot(void)
714 {
715     struct thread *me = thread_self();
716
717     // HERE: recv_slot_count is > 0 if we have one+ caps stored
718     if (me->recv_slot_count <= 0) {
719         return NULL_CAP;
720     }
721
722     return me->recv_slots[--me->recv_slot_count];
723 }
724
725 void thread_set_status(int status) {
726     struct thread *me = thread_self();
727     me->return_value = status;
728 }
729
730 /**
731  * \brief Yield the calling thread
732  *
733  * Switches to the next runnable thread in this dispatcher, or if none is
734  * available, yields the dispatcher.
735  */
736 void thread_yield(void)
737 {
738     dispatcher_handle_t handle = disp_disable();
739     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
740     struct dispatcher_shared_generic *disp =
741         get_dispatcher_shared_generic(handle);
742     arch_registers_state_t *enabled_area =
743         dispatcher_get_enabled_save_area(handle);
744
745     struct thread *me = disp_gen->current;
746     struct thread *next = me;
747     me->yield_epoch = disp_gen->timeslice;
748
749     do {
750         assert_disabled(next != NULL);
751         next = next->next;
752         if (next == me) {
753             break; // Everybody yielded this timeslice
754         }
755     } while(next->yield_epoch == disp_gen->timeslice);
756
757     poll_channels_disabled(handle);
758
759     if (next != me) {
760         fpu_context_switch(disp_gen, next);
761         disp_gen->current = next;
762         disp_switch(handle, &me->regs, &next->regs);
763     } else {
764         assert_disabled(disp_gen->runq != NULL);
765         assert_disabled(disp->haswork);
766         trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_THREADS_C_DISP_SAVE, 3);
767         disp_save(handle, enabled_area, true, CPTR_NULL);
768     }
769 }
770
771 /**
772  * \brief Yield both the calling thread, and the dispatcher to another domain
773  *
774  * \param endpoint Endpoint cap to which we wish to yield, or #CAP_NULL
775  *                  for an undirected yield
776  *
777  * Yields the dispatcher, optionally to another specified dispatcher.
778  */
779 void thread_yield_dispatcher(struct capref endpoint)
780 {
781     dispatcher_handle_t handle = disp_disable();
782     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
783     struct dispatcher_shared_generic *disp =
784         get_dispatcher_shared_generic(handle);
785     arch_registers_state_t *enabled_area =
786         dispatcher_get_enabled_save_area(handle);
787
788     assert_disabled(disp_gen->runq != NULL);
789     assert_disabled(disp->haswork);
790
791     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_THREADS_C_DISP_SAVE, 1);
792     disp_save(handle, enabled_area, true, get_cap_addr(endpoint));
793 }
794
795 /// Function that runs on the static thread/stack to clean up a "real" (alloced) thread
796 static int cleanup_thread(void *arg)
797 {
798     struct thread *thread = arg;
799
800     // free old thread and its stack
801     if (thread != NULL) {
802         free_thread(thread);
803     }
804
805     // disable and release static thread
806     dispatcher_handle_t handle = disp_disable();
807     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
808     struct dispatcher_shared_generic *disp =
809         get_dispatcher_shared_generic(handle);
810     struct thread *me = disp_gen->current;
811     struct thread *ft =
812         thread_mutex_unlock_disabled(handle, &disp_gen->cleanupthread_lock);
813     assert(ft == NULL);
814
815     // run the next thread, if any
816     struct thread *next = me->next;
817     thread_remove_from_queue(&disp_gen->runq, me);
818     if (next != me) {
819         disp_gen->current = next;
820         disp_resume(handle, &next->regs);
821     } else {
822         disp_gen->current = NULL;
823         disp->haswork = havework_disabled(handle);
824         disp_yield_disabled(handle);
825     }
826
827     return 0;
828 }
829
830 /**
831  * \brief Terminate the calling thread
832  */
833 void thread_exit(int status)
834 {
835     struct thread *me = thread_self();
836
837     thread_mutex_lock(&me->exit_lock);
838
839     // if this is the static thread, we don't need to do anything but cleanup
840     if (me == &staticthread) {
841         assert(me->detached);
842         // disable and release static thread
843         dispatcher_handle_t handle = disp_disable();
844         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
845         struct dispatcher_shared_generic *disp =
846             get_dispatcher_shared_generic(handle);
847         assert_disabled(me == &staticthread);
848         assert_disabled(me->stack == staticstack);
849         struct thread *ft =
850             thread_mutex_unlock_disabled(handle, &staticthread_lock);
851         assert(ft == NULL);
852
853 #ifdef FPU_LAZY_CONTEXT_SWITCH
854         // No more FPU usage from here on
855         if(disp_gen->fpu_thread == me) {
856             disp_gen->fpu_thread = NULL;
857             fpu_trap_on();
858         }
859 #endif
860
861         // run the next thread, if any
862         struct thread *next = me->next;
863         thread_remove_from_queue(&disp_gen->runq, me);
864         if (next != me) {
865             fpu_context_switch(disp_gen, next);
866             disp_gen->current = next;
867             disp_resume(handle, &next->regs);
868         } else {
869             disp_gen->current = NULL;
870             disp->haswork = havework_disabled(handle);
871             disp_yield_disabled(handle);
872         }
873     }
874
875     if(me->detached) {
876         // otherwise, we use a dispatcher-local thread to perform cleanup
877         struct dispatcher_generic *dg = get_dispatcher_generic(curdispatcher());
878         thread_mutex_lock(&dg->cleanupthread_lock);
879         if(dg->cleanupthread == NULL) {
880             dg->cleanupthread =
881                 thread_create_unrunnable(cleanup_thread, me,
882                                          THREADS_DEFAULT_STACK_BYTES);
883         }
884         thread_init(curdispatcher(), dg->cleanupthread);
885
886         registers_set_initial(&dg->cleanupthread->regs, dg->cleanupthread,
887                               (lvaddr_t)cleanup_thread,
888                               (lvaddr_t)dg->cleanupthread->stack_top, (lvaddr_t)me,
889                               0, 0, 0);
890
891         // Switch to it (on this dispatcher)
892         dispatcher_handle_t handle = disp_disable();
893         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
894
895 #ifdef FPU_LAZY_CONTEXT_SWITCH
896         // No more FPU usage from here on
897         if(disp_gen->fpu_thread == me) {
898             disp_gen->fpu_thread = NULL;
899             fpu_trap_on();
900         }
901 #endif
902
903         thread_remove_from_queue(&disp_gen->runq, me);
904         thread_enqueue(dg->cleanupthread, &disp_gen->runq);
905         disp_gen->cleanupthread->disp = handle;
906         fpu_context_switch(disp_gen, dg->cleanupthread);
907         disp_gen->current = dg->cleanupthread;
908         disp_resume(handle, &dg->cleanupthread->regs);
909     } else {
910         // We're not detached -- wakeup joiner
911         me->return_value = status;
912         me->state = THREAD_STATE_EXITED;
913         thread_cond_signal(&me->exit_condition);
914
915         // Disable and unlock exit lock
916         dispatcher_handle_t handle = disp_disable();
917         struct thread *wakeup =
918             thread_mutex_unlock_disabled(handle, &me->exit_lock);
919         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
920         struct dispatcher_shared_generic *disp =
921             get_dispatcher_shared_generic(handle);
922
923         assert_disabled(wakeup == NULL);
924
925 #ifdef FPU_LAZY_CONTEXT_SWITCH
926         // No more FPU usage from here on
927         if(disp_gen->fpu_thread == me) {
928             disp_gen->fpu_thread = NULL;
929             fpu_trap_on();
930         }
931 #endif
932
933         // run the next thread, if any
934         struct thread *next = me->next;
935         thread_remove_from_queue(&disp_gen->runq, me);
936         if (next != me) {
937             fpu_context_switch(disp_gen, next);
938             disp_gen->current = next;
939             disp_resume(handle, &next->regs);
940         } else {
941             disp_gen->current = NULL;
942             disp->haswork = havework_disabled(handle);
943             disp_yield_disabled(handle);
944         }
945     }
946
947     USER_PANIC("should never be reached");
948 }
949
950 /**
951  * \brief Block the caller, and optionally release a spinlock, while disabled
952  *
953  * The caller is unconditionally blocked, and placed into the given queue
954  * pending a call that will unblock it. After manipulating the queues, and
955  * before switching threds, the given spinlock, if specified, is unlocked.
956  * This function must only be called while disabled.
957  *
958  * This function is intended for use by multi-processor thread synchronisation
959  * functions.
960  *
961  * \param disp Dispatcher pointer
962  * \param queue (Optional) Queue of threads in which to place caller
963  * \param spinlock (Optional) pointer to spinlock
964  *
965  * \returns Argument passed to thread_unblock, when unblocked
966  */
967 void *thread_block_and_release_spinlock_disabled(dispatcher_handle_t handle,
968                                                  struct thread **queue,
969                                                  spinlock_t *spinlock)
970 {
971     struct dispatcher_shared_generic *disp =
972         get_dispatcher_shared_generic(handle);
973     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
974     struct thread *me = disp_gen->current;
975     struct thread *next = me->next;
976     assert_disabled(next != NULL);
977
978     assert_disabled(me->state == THREAD_STATE_RUNNABLE);
979     me->state = THREAD_STATE_BLOCKED;
980
981     thread_remove_from_queue(&disp_gen->runq, me);
982     if (queue != NULL) {
983         thread_enqueue(me, queue);
984     }
985
986     if (spinlock != NULL) {
987         release_spinlock(spinlock);
988     }
989
990     if (next != me) {
991         assert_disabled(disp_gen->runq != NULL);
992         fpu_context_switch(disp_gen, next);
993         disp_gen->current = next;
994         disp_switch(handle, &me->regs, &next->regs);
995     } else {
996         assert_disabled(disp_gen->runq == NULL);
997         disp_gen->current = NULL;
998         disp->haswork = havework_disabled(handle);
999         trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_THREADS_C_DISP_SAVE, 2);
1000         disp_save(handle, &me->regs, true, CPTR_NULL);
1001     }
1002
1003     assert(me->disp == handle); // didn't migrate while asleep
1004     return me->wakeup_reason;
1005 }
1006
1007 /**
1008  * \brief Block the calling thread, while disabled
1009  *
1010  * The caller is unconditionally blocked, and placed into the given queue
1011  * pending a call that will unblock it.
1012  * This function must only be called while disabled.
1013  *
1014  * \param disp Dispatcher pointer
1015  * \param queue Queue of threads in which to place caller
1016  *
1017  * \returns Argument passed to thread_unblock, when unblocked
1018  */
1019 void *thread_block_disabled(dispatcher_handle_t disp, struct thread **queue)
1020 {
1021     return thread_block_and_release_spinlock_disabled(disp, queue, NULL);
1022 }
1023
1024 /**
1025  * \brief Block the calling thread, while enabled
1026  *
1027  * The caller is unconditionally blocked, and placed into the given queue
1028  * pending a call that will unblock it.
1029  * This function must only be called while enabled.
1030  *
1031  * \param queue Queue of threads in which to place caller
1032  *
1033  * \returns Argument passed to thread_unblock, when unblocked
1034  */
1035 void *thread_block(struct thread **queue)
1036 {
1037     return thread_block_disabled(disp_disable(), queue);
1038 }
1039
1040 /**
1041  * \brief Unblock a single thread from a given queue, while disabled
1042  *
1043  * A single thread is removed from the queue of blocked threads, and awoken.
1044  * This function must only be called while disabled.
1045  *
1046  * \param disp   Dispatcher pointer
1047  * \param queue  Queue of threads from which to unblock one
1048  * \param reason Value to be returned from thread_block()
1049  *
1050  * \returns Pointer to thread to be woken on a foreign dispatcher
1051  */
1052 struct thread *thread_unblock_one_disabled(dispatcher_handle_t handle,
1053                                            struct thread **queue,
1054                                            void *reason)
1055 {
1056     assert_disabled(queue != NULL);
1057
1058     // Any threads in queue?
1059     if (*queue == NULL) {
1060         return NULL;
1061     }
1062
1063     // Wakeup one waiting thread
1064     struct thread *wakeup = thread_dequeue(queue);
1065     wakeup->wakeup_reason = reason;
1066     assert_disabled(wakeup->state == THREAD_STATE_BLOCKED);
1067     wakeup->state = THREAD_STATE_RUNNABLE;
1068
1069     /* enqueue on run queue if it's "our" thread, and not paused */
1070     if (wakeup->disp == handle) {
1071         if (!wakeup->paused) {
1072             struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1073             thread_enqueue(wakeup, &disp_gen->runq);
1074         }
1075         return NULL;
1076     } else {
1077         return wakeup;
1078     }
1079 }
1080
1081 /**
1082  * \brief Unblock a single thread from a given queue, while enabled
1083  *
1084  * A single thread is removed from the queue of blocked threads, and awoken.
1085  * This function must only be called while enabled.
1086  *
1087  * \param queue  Queue of threads from which to unblock one
1088  * \param reason Value to be returned from thread_block()
1089  *
1090  * \returns Pointer to thread to be woken on a foreign dispatcher
1091  */
1092 struct thread *thread_unblock_one(struct thread **queue, void *reason)
1093 {
1094     struct thread *thread;
1095
1096     dispatcher_handle_t handle = disp_disable();
1097     thread = thread_unblock_one_disabled(handle, queue, reason);
1098     disp_enable(handle);
1099     return thread;
1100 }
1101
1102 /**
1103  * \brief Unblock all threads on a given queue, while disabled
1104  *
1105  * All threads on the queue of blocked threads are awoken.
1106  * This function must only be called while disabled.
1107  *
1108  * \param disp   Dispatcher pointer
1109  * \param queue  Queue of threads to unblock
1110  * \param reason Value to be returned from thread_block()
1111  *
1112  * \returns Pointer to list of threads to be woken on a foreign dispatcher
1113  */
1114 struct thread *thread_unblock_all_disabled(dispatcher_handle_t handle,
1115                                            struct thread **queue, void *reason)
1116 {
1117     assert_disabled(queue != NULL);
1118     struct thread *wakeupq = NULL;
1119
1120     // Wakeup all waiting threads
1121     while (*queue != NULL) {
1122         struct thread *wakeup = thread_unblock_one_disabled(handle, queue, reason);
1123         if (wakeup != NULL) {
1124             wakeup->next = wakeupq;
1125             wakeupq = wakeup;
1126         }
1127     }
1128
1129     return wakeupq;
1130 }
1131
1132 extern int _main(int argc, const char *argv[]);
1133
1134 /// Thread created in new domain that runs main()
1135 static int main_thread(void *params)
1136 {
1137     struct spawn_domain_params *p = params;
1138     exit(_main(p->argc, p->argv));
1139     return EXIT_FAILURE;
1140 }
1141
1142 static bool init_domain_global; // XXX
1143
1144 /// Thread created on static stack in new domain that runs init code
1145 static int bootstrap_thread(struct spawn_domain_params *params)
1146 //int bootstrap_thread(struct spawn_domain_params *params);
1147 //int bootstrap_thread(struct spawn_domain_params *params)
1148 {
1149     errval_t err;
1150
1151     // Set libc function pointers
1152     barrelfish_libc_glue_init();
1153
1154     if (params == NULL) {
1155         printf("%s: error in creating a thread, NULL parameters given\n",
1156                 disp_name());
1157     }
1158     assert(params != NULL);
1159
1160     // Do we have TLS data?
1161     tls_block_init_base = params->tls_init_base;
1162     tls_block_init_len = params->tls_init_len;
1163     tls_block_total_len = params->tls_total_len;
1164
1165     // Initialize subsystems
1166     err = barrelfish_init_onthread(params);
1167     if (err_is_fail(err)) {
1168         DEBUG_ERR(err, "error during libbarrelfish init");
1169         exit(EXIT_FAILURE);
1170         assert(!"exit returned!");
1171     }
1172
1173     // Allocate storage region for real threads
1174     size_t blocksize = sizeof(struct thread) + tls_block_total_len;
1175     err = vspace_mmu_aware_init(&thread_slabs_vm, MAX_THREADS * blocksize);
1176     if (err_is_fail(err)) {
1177         USER_PANIC_ERR(err, "vspace_mmu_aware_init for thread region failed\n");
1178     }
1179     slab_init(&thread_slabs, blocksize, refill_thread_slabs);
1180
1181     if (init_domain_global) {
1182         // run main() on this thread, since we can't allocate
1183         if (tls_block_total_len > 0) {
1184             USER_PANIC("unsupported: use of TLS data in bootstrap domain\n");
1185         }
1186         main_thread(params);
1187     } else {
1188         // Start real thread to run main()
1189         struct thread *thread = thread_create(main_thread, params);
1190         assert(thread != NULL);
1191     }
1192
1193     return 0; // ignored
1194 }
1195
1196 /**
1197  * \brief Initialise thread system while still disabled
1198  *
1199  * This function initialises the thread system while the dispatcher is still
1200  * disabled, before enabling the dispatcher, running the general initialisation
1201  * code, and calling main().
1202  *
1203  * \param disp Dispatcher pointer
1204  * \param init_domain True if we are a bootstrap domain
1205  */
1206 void thread_init_disabled(dispatcher_handle_t handle, bool init_domain)
1207 {
1208     struct dispatcher_shared_generic *disp =
1209         get_dispatcher_shared_generic(handle);
1210     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1211     arch_registers_state_t *enabled_area =
1212         dispatcher_get_enabled_save_area(handle);
1213
1214     init_domain_global = init_domain;
1215
1216     // Create the first thread manually
1217     struct thread *thread = &staticthread;
1218     staticthread_lock.locked = true; // XXX: safe while disabled
1219
1220     // waste space for alignment, if unaligned
1221     thread->stack_top = (char *)thread->stack_top
1222         - (lvaddr_t)thread->stack_top % STACK_ALIGNMENT;
1223
1224     // Initialise the first (static) thread
1225     thread_init(handle, thread);
1226     thread->detached = true;
1227
1228 #if defined(__x86_64__)
1229     // create segment for TCB
1230     errval_t err = ldt_alloc_segment_disabled(handle, thread,
1231                                               &thread->thread_seg_selector);
1232     if (err_is_fail(err)) {
1233         USER_PANIC_ERR(err, "error allocating LDT segment for first thread");
1234     }
1235 #endif
1236
1237     uintptr_t param;
1238     registers_get_param(enabled_area, &param);
1239
1240     registers_set_initial(&thread->regs, thread, (lvaddr_t)thread_entry,
1241                           /* TODO: pass stack base and limit, choose in arch
1242                            * code (possibly setting up other hints on stack) */
1243                           (lvaddr_t)thread->stack_top,
1244                           (lvaddr_t)bootstrap_thread, param, 0, 0);
1245
1246     // Switch to it (always on this dispatcher)
1247     thread->disp = handle;
1248     thread_enqueue(thread, &disp_gen->runq);
1249     disp_gen->current = thread;
1250     disp->haswork = true;
1251     disp_resume(handle, &thread->regs);
1252 }
1253
1254 /**
1255  * \brief Called on the remote core when spanning a domain across cores
1256  *
1257  * Runs the provided thread after enqueuing it and enabling the dispatcher
1258  */
1259 void thread_init_remote(dispatcher_handle_t handle, struct thread *thread)
1260 {
1261     struct dispatcher_shared_generic *disp =
1262         get_dispatcher_shared_generic(handle);
1263     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1264     thread_enqueue(thread, &disp_gen->runq);
1265     disp_gen->current = thread;
1266     disp->haswork = true;
1267     disp_resume(handle, &thread->regs);
1268 }
1269
1270 /**
1271  * \brief Prepare to span the current domain
1272  *
1273  * This is a kludge. It is called from domain.c when creating a new dispatcher,
1274  * and is responsible for pre-allocating all the storage that might be needed
1275  * for thread metadata in the slab allocator. It can go away once we sanely
1276  * manage the vspace across multiple dispatchers in a domain.
1277  */
1278 void threads_prepare_to_span(dispatcher_handle_t newdh)
1279 {
1280     static bool called;
1281
1282     if (!called) {
1283         called = true;
1284
1285         thread_mutex_lock(&thread_slabs_mutex);
1286         acquire_spinlock(&thread_slabs_spinlock);
1287
1288         while (slab_freecount(&thread_slabs) < MAX_THREADS - 1) {
1289             size_t size;
1290             void *buf;
1291             errval_t err;
1292
1293             size_t blocksize = sizeof(struct thread) + tls_block_total_len;
1294             err = vspace_mmu_aware_map(&thread_slabs_vm, 64 * blocksize,
1295                                        &buf, &size);
1296             if (err_is_fail(err)) {
1297                 if (err_no(err) == LIB_ERR_VSPACE_MMU_AWARE_NO_SPACE) {
1298                     // we've wasted space with fragmentation
1299                     // cross our fingers and hope for the best...
1300                     break;
1301                 }
1302                 USER_PANIC_ERR(err, "in vspace_mmu_aware_map while prefilling "
1303                                "thread slabs\n");
1304             }
1305
1306             slab_grow(&thread_slabs, buf, size);
1307         }
1308
1309         release_spinlock(&thread_slabs_spinlock);
1310         thread_mutex_unlock(&thread_slabs_mutex);
1311     }
1312 }
1313
1314 /**
1315  * \brief Pause (suspend execution of) the given thread, and optionally capture its register state
1316  *
1317  * The thread will not be run, until a subsequent call to thread_resume()
1318  */
1319 void thread_pause_and_capture_state(struct thread *thread,
1320                                     arch_registers_state_t **ret_regs,
1321                                     arch_registers_fpu_state_t **ret_fpuregs)
1322 {
1323     assert(thread != NULL);
1324     dispatcher_handle_t dh = disp_disable();
1325     struct dispatcher_generic *disp = get_dispatcher_generic(dh);
1326     if (thread->disp == dh) {
1327         if (!thread->paused) {
1328             thread->paused = true;
1329             if (thread == disp->current) { // doesn't make much sense...
1330                 sys_print("Warning: pausing current thread!\n",100);
1331                 assert_disabled(thread->state == THREAD_STATE_RUNNABLE);
1332                 thread_block_disabled(dh, NULL);
1333             } else if (thread->state == THREAD_STATE_RUNNABLE) {
1334                 thread_remove_from_queue(&disp->runq, thread);
1335             }
1336         }
1337         if (ret_regs != NULL) {
1338             *ret_regs = &thread->regs;
1339         }
1340         if (ret_fpuregs != NULL) {
1341             if (thread->used_fpu) {
1342                 // FIXME: this may not be the right FPU state?
1343                 *ret_fpuregs = &thread->fpu_state;
1344             } else {
1345                 *ret_fpuregs = NULL;
1346             }
1347         }
1348     } else {
1349         USER_PANIC("NYI: remote dispatcher thread_pause()");
1350     }
1351     disp_enable(dh);
1352 }
1353
1354 /**
1355  * \brief Pause (suspend execution of) the given thread
1356  *
1357  * The thread will not be run, until a subsequent call to thread_resume()
1358  */
1359 void thread_pause(struct thread *thread)
1360 {
1361     thread_pause_and_capture_state(thread, NULL, NULL);
1362 }
1363
1364 /**
1365  * \brief Resume execution of a thread previously suspended by thread_pause()
1366  */
1367 void thread_resume(struct thread *thread)
1368 {
1369     assert(thread != NULL);
1370     dispatcher_handle_t dh = disp_disable();
1371     struct dispatcher_generic *disp = get_dispatcher_generic(dh);
1372     if (thread->disp == dh) {
1373         if (thread->paused) {
1374             thread->paused = false;
1375             if (thread->state == THREAD_STATE_RUNNABLE) {
1376                 thread_enqueue(thread, &disp->runq);
1377             }
1378         }
1379     } else {
1380         USER_PANIC("NYI: remote dispatcher thread_resume()");
1381     }
1382     disp_enable(dh);
1383 }
1384
1385 /**
1386  * \brief Set old-style thread-local storage pointer.
1387  * \param p   User's pointer
1388  */
1389 void thread_set_tls(void *p)
1390 {
1391     struct thread *me = thread_self();
1392     me->userptr = p;
1393 }
1394
1395 void thread_set_tls_key(int key, void *p)
1396 {
1397     struct thread *me = thread_self();
1398     me->userptrs[key] = p;
1399 }
1400
1401 /**
1402  * \brief Return old-style thread-local storage pointer.
1403  * \return User's pointer, previously passed to thread_set_tls()
1404  */
1405 void *thread_get_tls(void)
1406 {
1407     struct thread *me = thread_self();
1408     return me->userptr;
1409 }
1410
1411 void *thread_get_tls_key(int key)
1412 {
1413     struct thread *me = thread_self();
1414     return me->userptrs[key];
1415 }
1416
1417 /**
1418  * \brief Set the exception handler function for the current thread.
1419  *        Optionally also change its stack, and return the old values.
1420  *
1421  * \param newhandler New exception handler. Pass NULL to disable an existing handler.
1422  * \param oldhandler If non-NULL, returns previous exception handler
1423  * \param new_stack_base If non-NULL, sets a new exception handler stack (base)
1424  * \param new_stack_top  If non-NULL, sets a new exception handler stack (top)
1425  * \param old_stack_base If non-NULL, returns previous stack base
1426  * \param old_stack_top If non-NULL, returns previous stack top
1427  */
1428 errval_t thread_set_exception_handler(exception_handler_fn newhandler,
1429                                       exception_handler_fn *oldhandler,
1430                                       void *new_stack_base, void *new_stack_top,
1431                                       void **old_stack_base, void **old_stack_top)
1432 {
1433     struct thread *me = thread_self();
1434
1435     if (oldhandler != NULL) {
1436         *oldhandler = me->exception_handler;
1437     }
1438
1439     if (old_stack_base != NULL) {
1440         *old_stack_base = me->exception_stack;
1441     }
1442
1443     if (old_stack_top != NULL) {
1444         *old_stack_top = me->exception_stack_top;
1445     }
1446
1447     me->exception_handler = newhandler;
1448
1449     if (new_stack_base != NULL && new_stack_top != NULL) {
1450         me->exception_stack = new_stack_base;
1451         me->exception_stack_top = new_stack_top;
1452     }
1453
1454     return SYS_ERR_OK;
1455 }
1456
1457 static void exception_handler_wrapper(arch_registers_state_t *cpuframe,
1458                                       arch_registers_fpu_state_t *fpuframe,
1459                                       uintptr_t hack_arg, void *addr)
1460 {
1461     struct thread *me = thread_self();
1462
1463     assert(me->in_exception);
1464     assert(me->exception_handler != NULL);
1465
1466     // XXX: unpack hack arg
1467     enum exception_type type = hack_arg >> 16;
1468     int subtype = hack_arg & 0xffff;
1469
1470     // run handler
1471     me->exception_handler(type, subtype, addr, cpuframe, fpuframe);
1472
1473     // resume state
1474     dispatcher_handle_t dh = disp_disable();
1475     struct dispatcher_generic *disp_gen = get_dispatcher_generic(dh);
1476     //memcpy(&me->regs, cpuframe, sizeof(arch_registers_state_t));
1477
1478 #ifdef FPU_LAZY_CONTEXT_SWITCH
1479     if (fpuframe != NULL) {
1480         assert_disabled(me->used_fpu);
1481         arch_registers_fpu_state_t *dest;
1482         if (disp_gen->fpu_thread == me) {
1483             dest = dispatcher_get_enabled_fpu_save_area(dh);
1484         } else {
1485             dest = &me->fpu_state;
1486         }
1487         fpu_copy(dest, fpuframe);
1488         fpu_trap_on();
1489     }
1490 #endif
1491
1492     assert_disabled(me->in_exception);
1493     me->in_exception = false;
1494
1495     assert_disabled(disp_gen->current == me);
1496     disp_resume(dh, cpuframe);
1497 }
1498
1499 #if 0
1500 void thread_debug_regs(struct thread *t);
1501 void thread_debug_regs(struct thread *t)
1502 {
1503   printf("%d: RIP = %lx, RSP = %lx\n", disp_get_domain_id(),
1504          t->regs.rip, t->regs.rsp);
1505   uint64_t *stack = (uint64_t *)t->regs.rsp;
1506   printf("%d: ", disp_get_domain_id());
1507   for(int i = 0; i < 30; i++) {
1508     printf("%lx ", stack[i]);
1509   }
1510   printf("\n");
1511 }
1512 #endif
1513
1514 /**
1515  * \brief Deliver an exception to the current thread, and resume.
1516  *
1517  * This may only be called from the dispatcher (on its stack and while
1518  * disabled!).
1519  *
1520  * \param handle Dispatcher handle
1521  * \param type   Exception type
1522  * \param subtype Exception subtype
1523  * \param addr   Exception address
1524  * \param regs   CPU register state at time of exception
1525  */
1526 void thread_deliver_exception_disabled(dispatcher_handle_t handle,
1527                                        enum exception_type type, int subtype,
1528                                        void *addr, arch_registers_state_t *regs)
1529 {
1530     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1531     struct thread *thread = disp_gen->current;
1532     assert_disabled(thread != NULL);
1533     assert_disabled(disp_gen->runq != NULL);
1534
1535     // can we deliver the exception?
1536     if (thread->exception_handler == NULL || thread->exception_stack_top == NULL
1537         || thread->in_exception) {
1538         if (thread->in_exception) {
1539             sys_print("Can't deliver exception to thread: already in handler\n",
1540                       100);
1541         } else {
1542             sys_print("Can't deliver exception to thread: handler not set\n",
1543                       100);
1544         }
1545
1546         // warn on stack overflow.
1547         lvaddr_t sp = (lvaddr_t) registers_get_sp(regs);
1548         if (sp < (lvaddr_t)thread->stack ||
1549             sp > (lvaddr_t)thread->stack_top) {
1550             char str[256];
1551             snprintf(str, sizeof(str), "Error: stack bounds exceeded: sp = 0x%"
1552                      PRIxPTR " but [bottom, top] = [0x%" PRIxPTR ", 0x%"
1553                      PRIxPTR "]\n", (lvaddr_t) sp, (lvaddr_t) thread->stack,
1554                      (lvaddr_t) thread->stack_top);
1555             sys_print(str, sizeof(str));
1556         }
1557
1558         // TODO: actually delete the thread!
1559         disp_gen->current = NULL;
1560         thread_remove_from_queue(&disp_gen->runq, thread);
1561         return;
1562     }
1563
1564     thread->in_exception = true;
1565
1566     lvaddr_t stack_top = (lvaddr_t)thread->exception_stack_top;
1567
1568     // save thread's state at time of fault on top of exception stack
1569     stack_top -= sizeof(arch_registers_state_t);
1570     arch_registers_state_t *cpuframe = (void *)stack_top;
1571     memcpy(cpuframe, regs, sizeof(arch_registers_state_t));
1572
1573     arch_registers_fpu_state_t *fpuframe = NULL;
1574 #ifdef FPU_LAZY_CONTEXT_SWITCH
1575     if (thread->used_fpu) {
1576         stack_top -= sizeof(arch_registers_fpu_state_t);
1577         fpuframe = (void *)stack_top;
1578         fpu_copy(fpuframe, &thread->fpu_state);
1579     }
1580 #endif
1581
1582     // align stack
1583     stack_top -= stack_top % STACK_ALIGNMENT;
1584
1585     // XXX: sanity-check to ensure we have a sensible amount of exception stack left
1586     assert_disabled(stack_top > (lvaddr_t)thread->exception_stack + 8192);
1587
1588     // XXX: pack two small ints together to fit into a single register
1589     uintptr_t hack_arg = (uintptr_t)type << 16 | (subtype & 0xffff);
1590
1591     registers_set_initial(&thread->regs, thread,
1592                           (lvaddr_t)exception_handler_wrapper,
1593                           stack_top, (lvaddr_t)cpuframe, (lvaddr_t)fpuframe,
1594                           hack_arg, (lvaddr_t)addr);
1595
1596     disp_resume(handle, &thread->regs);
1597 }