posixcompat: properly handle PTHREAD_MUTEX_RECURSIVE and BF thread id assignment.
[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     start_func(start_data);
92     thread_exit();
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     struct capref frame;
216     size_t size;
217     void *buf;
218     errval_t err;
219
220     err = slot_alloc(&frame);
221     if (err_is_fail(err)) {
222         return err_push(err, LIB_ERR_SLOT_ALLOC);
223     }
224
225     size_t blocksize = sizeof(struct thread) + tls_block_total_len;
226     err = vspace_mmu_aware_map(&thread_slabs_vm, frame, blocksize, &buf, &size);
227     if (err_is_fail(err)) {
228         return err_push(err, LIB_ERR_VSPACE_MMU_AWARE_MAP);
229     }
230
231     slab_grow(slabs, buf, size);
232
233     return SYS_ERR_OK;
234 }
235
236 /// Initialise the state of a new thread structure
237 static void thread_init(dispatcher_handle_t disp, struct thread *newthread)
238 {
239     newthread->self = newthread;
240 #ifndef NDEBUG
241     newthread->next = newthread->prev = NULL;
242 #endif
243     newthread->tls_dtv = NULL;
244     newthread->disp = disp;
245     newthread->coreid = get_dispatcher_generic(disp)->core_id;
246     newthread->userptr = NULL;
247     memset(newthread->userptrs, 0, sizeof(newthread->userptrs));
248     newthread->yield_epoch = 0;
249     newthread->wakeup_reason = NULL;
250     newthread->return_value = 0;
251     thread_cond_init(&newthread->exit_condition);
252     thread_mutex_init(&newthread->exit_lock);
253     newthread->state = THREAD_STATE_RUNNABLE;
254     newthread->detached = false;
255     newthread->joining = false;
256     newthread->in_exception = false;
257     newthread->used_fpu = false;
258     newthread->paused = false;
259     newthread->slab = 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         return NULL;
440     }
441 #endif
442
443     // init stack
444     newthread->stack = stack;
445     newthread->stack_top = (char *)stack + stacksize;
446
447     // waste space for alignment, if malloc gave us an unaligned stack
448     newthread->stack_top = (char *)newthread->stack_top
449         - (lvaddr_t)newthread->stack_top % STACK_ALIGNMENT;
450
451     // set thread's ID
452     newthread->id = threadid++;
453
454     // init registers
455     registers_set_initial(&newthread->regs, newthread, (lvaddr_t)thread_entry,
456                           (lvaddr_t)newthread->stack_top,
457                           (lvaddr_t)start_func, (lvaddr_t)arg, 0, 0);
458
459     return newthread;
460 }
461
462 /**
463  * \brief Creates a new thread, and makes it runnable
464  *
465  * \param start_func Function to run on the new thread
466  * \param arg Argument to pass to function
467  * \param stacksize Size of stack, in bytes
468  *
469  * \returns Thread pointer on success, NULL on failure
470  */
471 struct thread *thread_create_varstack(thread_func_t start_func, void *arg,
472                                       size_t stacksize)
473 {
474     struct thread *newthread = thread_create_unrunnable(start_func, arg, stacksize);
475     if (newthread) {
476         // enqueue on runq
477         dispatcher_handle_t handle = disp_disable();
478         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
479         newthread->disp = handle;
480         thread_enqueue(newthread, &disp_gen->runq);
481         disp_enable(handle);
482     }
483     return newthread;
484 }
485
486 /**
487  * \brief Creates a new thread, and makes it runnable
488  *
489  * \param start_func Function to run on the new thread
490  * \param arg Argument to pass to function
491  *
492  * \returns Thread pointer on success, NULL on failure
493  */
494 struct thread *thread_create(thread_func_t start_func, void *arg)
495 {
496     return thread_create_varstack(start_func, arg, THREADS_DEFAULT_STACK_BYTES);
497 }
498
499 /**
500  * \brief Wait for termination of another thread
501  *
502  * \param thread        Pointer to thread to wait for
503  * \param retval        Pointer to variable to hold return value of thread, or NULL
504  *
505  * \returns SYS_ERR_OK on success, error code on error.
506  */
507 errval_t thread_join(struct thread *thread, int *retval)
508 {
509     assert(thread != NULL);
510
511     thread_mutex_lock(&thread->exit_lock);
512     if(thread->detached) {
513         // Thread is detached and thus not joinable
514         thread_mutex_unlock(&thread->exit_lock);
515         return LIB_ERR_THREAD_JOIN_DETACHED;
516     }
517
518     if(thread->joining) {
519         // Someone else already joins, that's an error
520         thread_mutex_unlock(&thread->exit_lock);
521         return LIB_ERR_THREAD_JOIN;
522     }
523
524     thread->joining = true;
525     if(thread->state != THREAD_STATE_EXITED) { // Possibly wait for thread exit
526         thread_cond_wait(&thread->exit_condition, &thread->exit_lock);
527     }
528
529     if(retval != NULL) {
530         *retval = thread->return_value;
531     }
532
533     thread_mutex_unlock(&thread->exit_lock);    // Not really needed
534     free_thread(thread);
535
536     return SYS_ERR_OK;
537 }
538
539 /**
540  * \brief Detach a thread. Free its state when it terminates.
541  *
542  * \param thread        Pointer to thread to detach
543  *
544  * \return SYS_ERR_OK on success.
545  */
546 errval_t thread_detach(struct thread *thread)
547 {
548     assert(thread != NULL);
549     thread_mutex_lock(&thread->exit_lock);
550
551     if(thread->joining) {
552         // Someone else already joins, that's an error
553         thread_mutex_unlock(&thread->exit_lock);
554         return LIB_ERR_THREAD_JOIN;
555     }
556
557     if(!thread->detached) {
558         thread->detached = true;
559     } else {
560         // Detaching more than once is an error
561         thread_mutex_unlock(&thread->exit_lock);
562         return LIB_ERR_THREAD_DETACHED;
563     }
564
565     if(thread->state == THREAD_STATE_EXITED) {
566         // Thread already exited before we detached, clean it up
567         free_thread(thread);
568         return SYS_ERR_OK;
569     }
570
571     thread_mutex_unlock(&thread->exit_lock);
572     return SYS_ERR_OK;
573 }
574
575 /**
576  * \brief Returns the thread pointer to the currently-running thread
577  */
578 struct thread *thread_self(void)
579 {
580     struct thread *me;
581 #if defined(__x86_64__) // XXX: AB's silly little arch-specific optimisation
582     __asm("movq %%fs:0, %0" : "=r" (me));
583 #else
584     // it's not necessary to disable, but might be once we do migration
585     dispatcher_handle_t handle = disp_disable();
586     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
587     me = disp_gen->current;
588     disp_enable(handle);
589 #endif
590     return me;
591 }
592
593 uintptr_t thread_id(void)
594 {
595     return thread_self()->id;
596 }
597
598 uintptr_t thread_get_id(struct thread *t)
599 {
600     return t->id;
601 }
602
603 void thread_set_id(uintptr_t id)
604 {
605     struct thread *me = thread_self();
606     me->id = id;
607 }
608
609 /**
610  * \brief Yield the calling thread
611  *
612  * Switches to the next runnable thread in this dispatcher, or if none is
613  * available, yields the dispatcher.
614  */
615 void thread_yield(void)
616 {
617     dispatcher_handle_t handle = disp_disable();
618     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
619     struct dispatcher_shared_generic *disp =
620         get_dispatcher_shared_generic(handle);
621     arch_registers_state_t *enabled_area =
622         dispatcher_get_enabled_save_area(handle);
623
624     struct thread *me = disp_gen->current;
625     struct thread *next = me;
626     me->yield_epoch = disp_gen->timeslice;
627
628     do {
629         assert_disabled(next != NULL);
630         next = next->next;
631         if (next == me) {
632             break; // Everybody yielded this timeslice
633         }
634     } while(next->yield_epoch == disp_gen->timeslice);
635
636     if (next != me) {
637         fpu_context_switch(disp_gen, next);
638         disp_gen->current = next;
639         disp_switch(handle, &me->regs, &next->regs);
640     } else {
641         assert_disabled(disp_gen->runq != NULL);
642         assert_disabled(disp->haswork);
643         trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_THREADS_C_DISP_SAVE, 3);
644         disp_save(handle, enabled_area, true, CPTR_NULL);
645     }
646 }
647
648 /**
649  * \brief Yield both the calling thread, and the dispatcher to another domain
650  *
651  * \param endpoint Endpoint cap to which we wish to yield, or #CAP_NULL
652  *                  for an undirected yield
653  *
654  * Yields the dispatcher, optionally to another specified dispatcher.
655  */
656 void thread_yield_dispatcher(struct capref endpoint)
657 {
658     dispatcher_handle_t handle = disp_disable();
659     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
660     struct dispatcher_shared_generic *disp =
661         get_dispatcher_shared_generic(handle);
662     arch_registers_state_t *enabled_area =
663         dispatcher_get_enabled_save_area(handle);
664
665     assert_disabled(disp_gen->runq != NULL);
666     assert_disabled(disp->haswork);
667
668     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_THREADS_C_DISP_SAVE, 1);
669     disp_save(handle, enabled_area, true, get_cap_addr(endpoint));
670 }
671
672 /// Function that runs on the static thread/stack to clean up a "real" (alloced) thread
673 static int cleanup_thread(void *arg)
674 {
675     struct thread *thread = arg;
676
677     // free old thread and its stack
678     if (thread != NULL) {
679         free_thread(thread);
680     }
681
682     // disable and release static thread
683     dispatcher_handle_t handle = disp_disable();
684     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
685     struct dispatcher_shared_generic *disp =
686         get_dispatcher_shared_generic(handle);
687     struct thread *me = disp_gen->current;
688     struct thread *ft =
689         thread_mutex_unlock_disabled(handle, &disp_gen->cleanupthread_lock);
690     assert(ft == NULL);
691
692     // run the next thread, if any
693     struct thread *next = me->next;
694     thread_remove_from_queue(&disp_gen->runq, me);
695     if (next != me) {
696         disp_gen->current = next;
697         disp_resume(handle, &next->regs);
698     } else {
699         disp_gen->current = NULL;
700         disp->haswork = havework_disabled(handle);
701         disp_yield_disabled(handle);
702     }
703
704     return 0;
705 }
706
707 /**
708  * \brief Terminate the calling thread
709  */
710 void thread_exit(void)
711 {
712     struct thread *me = thread_self();
713
714     thread_mutex_lock(&me->exit_lock);
715
716     // if this is the static thread, we don't need to do anything but cleanup
717     if (me == &staticthread) {
718         assert(me->detached);
719         // disable and release static thread
720         dispatcher_handle_t handle = disp_disable();
721         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
722         struct dispatcher_shared_generic *disp =
723             get_dispatcher_shared_generic(handle);
724         assert_disabled(me == &staticthread);
725         assert_disabled(me->stack == staticstack);
726         struct thread *ft =
727             thread_mutex_unlock_disabled(handle, &staticthread_lock);
728         assert(ft == NULL);
729
730 #ifdef FPU_LAZY_CONTEXT_SWITCH
731         // No more FPU usage from here on
732         if(disp_gen->fpu_thread == me) {
733             disp_gen->fpu_thread = NULL;
734             fpu_trap_on();
735         }
736 #endif
737
738         // run the next thread, if any
739         struct thread *next = me->next;
740         thread_remove_from_queue(&disp_gen->runq, me);
741         if (next != me) {
742             fpu_context_switch(disp_gen, next);
743             disp_gen->current = next;
744             disp_resume(handle, &next->regs);
745         } else {
746             disp_gen->current = NULL;
747             disp->haswork = havework_disabled(handle);
748             disp_yield_disabled(handle);
749         }
750     }
751
752     if(me->detached) {
753         // otherwise, we use a dispatcher-local thread to perform cleanup
754         struct dispatcher_generic *dg = get_dispatcher_generic(curdispatcher());
755         thread_mutex_lock(&dg->cleanupthread_lock);
756         if(dg->cleanupthread == NULL) {
757             dg->cleanupthread =
758                 thread_create_unrunnable(cleanup_thread, me,
759                                          THREADS_DEFAULT_STACK_BYTES);
760         }
761         thread_init(curdispatcher(), dg->cleanupthread);
762
763         registers_set_initial(&dg->cleanupthread->regs, dg->cleanupthread,
764                               (lvaddr_t)cleanup_thread,
765                               (lvaddr_t)dg->cleanupthread->stack_top, (lvaddr_t)me,
766                               0, 0, 0);
767
768         // Switch to it (on this dispatcher)
769         dispatcher_handle_t handle = disp_disable();
770         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
771
772 #ifdef FPU_LAZY_CONTEXT_SWITCH
773         // No more FPU usage from here on
774         if(disp_gen->fpu_thread == me) {
775             disp_gen->fpu_thread = NULL;
776             fpu_trap_on();
777         }
778 #endif
779
780         thread_remove_from_queue(&disp_gen->runq, me);
781         thread_enqueue(dg->cleanupthread, &disp_gen->runq);
782         disp_gen->cleanupthread->disp = handle;
783         fpu_context_switch(disp_gen, dg->cleanupthread);
784         disp_gen->current = dg->cleanupthread;
785         disp_resume(handle, &dg->cleanupthread->regs);
786     } else {
787         // We're not detached -- wakeup joiner
788         me->return_value = 0;   // XXX: Should be an argument to thread_exit()
789         me->state = THREAD_STATE_EXITED;
790         thread_cond_signal(&me->exit_condition);
791
792         // Disable and unlock exit lock
793         dispatcher_handle_t handle = disp_disable();
794         struct thread *wakeup =
795             thread_mutex_unlock_disabled(handle, &me->exit_lock);
796         struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
797         struct dispatcher_shared_generic *disp =
798             get_dispatcher_shared_generic(handle);
799
800         assert_disabled(wakeup == NULL);
801
802 #ifdef FPU_LAZY_CONTEXT_SWITCH
803         // No more FPU usage from here on
804         if(disp_gen->fpu_thread == me) {
805             disp_gen->fpu_thread = NULL;
806             fpu_trap_on();
807         }
808 #endif
809
810         // run the next thread, if any
811         struct thread *next = me->next;
812         thread_remove_from_queue(&disp_gen->runq, me);
813         if (next != me) {
814             fpu_context_switch(disp_gen, next);
815             disp_gen->current = next;
816             disp_resume(handle, &next->regs);
817         } else {
818             disp_gen->current = NULL;
819             disp->haswork = havework_disabled(handle);
820             disp_yield_disabled(handle);
821         }
822     }
823
824     USER_PANIC("should never be reached");
825 }
826
827 /**
828  * \brief Block the caller, and optionally release a spinlock, while disabled
829  *
830  * The caller is unconditionally blocked, and placed into the given queue
831  * pending a call that will unblock it. After manipulating the queues, and
832  * before switching threds, the given spinlock, if specified, is unlocked.
833  * This function must only be called while disabled.
834  *
835  * This function is intended for use by multi-processor thread synchronisation
836  * functions.
837  *
838  * \param disp Dispatcher pointer
839  * \param queue (Optional) Queue of threads in which to place caller
840  * \param spinlock (Optional) pointer to spinlock
841  *
842  * \returns Argument passed to thread_unblock, when unblocked
843  */
844 void *thread_block_and_release_spinlock_disabled(dispatcher_handle_t handle,
845                                                  struct thread **queue,
846                                                  spinlock_t *spinlock)
847 {
848     struct dispatcher_shared_generic *disp =
849         get_dispatcher_shared_generic(handle);
850     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
851     struct thread *me = disp_gen->current;
852     struct thread *next = me->next;
853     assert_disabled(next != NULL);
854
855     assert_disabled(me->state == THREAD_STATE_RUNNABLE);
856     me->state = THREAD_STATE_BLOCKED;
857
858     thread_remove_from_queue(&disp_gen->runq, me);
859     if (queue != NULL) {
860         thread_enqueue(me, queue);
861     }
862
863     if (spinlock != NULL) {
864         release_spinlock(spinlock);
865     }
866
867     if (next != me) {
868         assert_disabled(disp_gen->runq != NULL);
869         fpu_context_switch(disp_gen, next);
870         disp_gen->current = next;
871         disp_switch(handle, &me->regs, &next->regs);
872     } else {
873         assert_disabled(disp_gen->runq == NULL);
874         disp_gen->current = NULL;
875         disp->haswork = havework_disabled(handle);
876         trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_THREADS_C_DISP_SAVE, 2);
877         disp_save(handle, &me->regs, true, CPTR_NULL);
878     }
879
880     assert(me->disp == handle); // didn't migrate while asleep
881     return me->wakeup_reason;
882 }
883
884 /**
885  * \brief Block the calling thread, while disabled
886  *
887  * The caller is unconditionally blocked, and placed into the given queue
888  * pending a call that will unblock it.
889  * This function must only be called while disabled.
890  *
891  * \param disp Dispatcher pointer
892  * \param queue Queue of threads in which to place caller
893  *
894  * \returns Argument passed to thread_unblock, when unblocked
895  */
896 void *thread_block_disabled(dispatcher_handle_t disp, struct thread **queue)
897 {
898     return thread_block_and_release_spinlock_disabled(disp, queue, NULL);
899 }
900
901 /**
902  * \brief Block the calling thread, while enabled
903  *
904  * The caller is unconditionally blocked, and placed into the given queue
905  * pending a call that will unblock it.
906  * This function must only be called while enabled.
907  *
908  * \param queue Queue of threads in which to place caller
909  *
910  * \returns Argument passed to thread_unblock, when unblocked
911  */
912 void *thread_block(struct thread **queue)
913 {
914     return thread_block_disabled(disp_disable(), queue);
915 }
916
917 /**
918  * \brief Unblock a single thread from a given queue, while disabled
919  *
920  * A single thread is removed from the queue of blocked threads, and awoken.
921  * This function must only be called while disabled.
922  *
923  * \param disp   Dispatcher pointer
924  * \param queue  Queue of threads from which to unblock one
925  * \param reason Value to be returned from thread_block()
926  *
927  * \returns Pointer to thread to be woken on a foreign dispatcher
928  */
929 struct thread *thread_unblock_one_disabled(dispatcher_handle_t handle,
930                                            struct thread **queue,
931                                            void *reason)
932 {
933     assert_disabled(queue != NULL);
934
935     // Any threads in queue?
936     if (*queue == NULL) {
937         return NULL;
938     }
939
940     // Wakeup one waiting thread
941     struct thread *wakeup = thread_dequeue(queue);
942     wakeup->wakeup_reason = reason;
943     assert_disabled(wakeup->state == THREAD_STATE_BLOCKED);
944     wakeup->state = THREAD_STATE_RUNNABLE;
945
946     /* enqueue on run queue if it's "our" thread, and not paused */
947     if (wakeup->disp == handle) {
948         if (!wakeup->paused) {
949             struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
950             thread_enqueue(wakeup, &disp_gen->runq);
951         }
952         return NULL;
953     } else {
954         return wakeup;
955     }
956 }
957
958 /**
959  * \brief Unblock a single thread from a given queue, while enabled
960  *
961  * A single thread is removed from the queue of blocked threads, and awoken.
962  * This function must only be called while enabled.
963  *
964  * \param queue  Queue of threads from which to unblock one
965  * \param reason Value to be returned from thread_block()
966  *
967  * \returns Pointer to thread to be woken on a foreign dispatcher
968  */
969 struct thread *thread_unblock_one(struct thread **queue, void *reason)
970 {
971     return thread_unblock_one_disabled(disp_disable(), queue, reason);
972 }
973
974 /**
975  * \brief Unblock all threads on a given queue, while disabled
976  *
977  * All threads on the queue of blocked threads are awoken.
978  * This function must only be called while disabled.
979  *
980  * \param disp   Dispatcher pointer
981  * \param queue  Queue of threads to unblock
982  * \param reason Value to be returned from thread_block()
983  *
984  * \returns Pointer to list of threads to be woken on a foreign dispatcher
985  */
986 struct thread *thread_unblock_all_disabled(dispatcher_handle_t handle,
987                                            struct thread **queue, void *reason)
988 {
989     assert_disabled(queue != NULL);
990     struct thread *wakeupq = NULL;
991
992     // Wakeup all waiting threads
993     while (*queue != NULL) {
994         struct thread *wakeup = thread_unblock_one_disabled(handle, queue, reason);
995         if (wakeup != NULL) {
996             wakeup->next = wakeupq;
997             wakeupq = wakeup;
998         }
999     }
1000
1001     return wakeupq;
1002 }
1003
1004 extern int _main(int argc, const char *argv[]);
1005
1006 /// Thread created in new domain that runs main()
1007 static int main_thread(void *params)
1008 {
1009     struct spawn_domain_params *p = params;
1010     exit(_main(p->argc, p->argv));
1011     return EXIT_FAILURE;
1012 }
1013
1014 static bool init_domain_global; // XXX
1015
1016 /// Thread created on static stack in new domain that runs init code
1017 static int bootstrap_thread(struct spawn_domain_params *params)
1018 //int bootstrap_thread(struct spawn_domain_params *params);
1019 //int bootstrap_thread(struct spawn_domain_params *params)
1020 {
1021     errval_t err;
1022
1023     // Set libc function pointers
1024     barrelfish_libc_glue_init();
1025
1026     if (params == NULL) {
1027         printf("%s: error in creating a thread, NULL parameters given\n",
1028                 disp_name());
1029     }
1030     assert(params != NULL);
1031
1032     // Do we have TLS data?
1033     tls_block_init_base = params->tls_init_base;
1034     tls_block_init_len = params->tls_init_len;
1035     tls_block_total_len = params->tls_total_len;
1036
1037     // Initialize subsystems
1038     err = barrelfish_init_onthread(params);
1039     if (err_is_fail(err)) {
1040         DEBUG_ERR(err, "error during libbarrelfish init");
1041         exit(EXIT_FAILURE);
1042         assert(!"exit returned!");
1043     }
1044
1045     // Allocate storage region for real threads
1046     size_t blocksize = sizeof(struct thread) + tls_block_total_len;
1047     err = vspace_mmu_aware_init(&thread_slabs_vm, MAX_THREADS * blocksize);
1048     if (err_is_fail(err)) {
1049         USER_PANIC_ERR(err, "vspace_mmu_aware_init for thread region failed\n");
1050     }
1051     slab_init(&thread_slabs, blocksize, refill_thread_slabs);
1052
1053     if (init_domain_global) {
1054         // run main() on this thread, since we can't allocate
1055         if (tls_block_total_len > 0) {
1056             USER_PANIC("unsupported: use of TLS data in bootstrap domain\n");
1057         }
1058         main_thread(params);
1059     } else {
1060         // Start real thread to run main()
1061         struct thread *thread = thread_create(main_thread, params);
1062         assert(thread != NULL);
1063     }
1064
1065     return 0; // ignored
1066 }
1067
1068 /**
1069  * \brief Initialise thread system while still disabled
1070  *
1071  * This function initialises the thread system while the dispatcher is still
1072  * disabled, before enabling the dispatcher, running the general initialisation
1073  * code, and calling main().
1074  *
1075  * \param disp Dispatcher pointer
1076  * \param init_domain True if we are a bootstrap domain
1077  */
1078 void thread_init_disabled(dispatcher_handle_t handle, bool init_domain)
1079 {
1080     struct dispatcher_shared_generic *disp =
1081         get_dispatcher_shared_generic(handle);
1082     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1083     arch_registers_state_t *enabled_area =
1084         dispatcher_get_enabled_save_area(handle);
1085
1086     init_domain_global = init_domain;
1087
1088     // Create the first thread manually
1089     struct thread *thread = &staticthread;
1090     staticthread_lock.locked = true; // XXX: safe while disabled
1091
1092     // waste space for alignment, if unaligned
1093     thread->stack_top = (char *)thread->stack_top
1094         - (lvaddr_t)thread->stack_top % STACK_ALIGNMENT;
1095
1096     // Initialise the first (static) thread
1097     thread_init(handle, thread);
1098     thread->detached = true;
1099
1100 #if defined(__x86_64__)
1101     // create segment for TCB
1102     errval_t err = ldt_alloc_segment_disabled(handle, thread,
1103                                               &thread->thread_seg_selector);
1104     if (err_is_fail(err)) {
1105         USER_PANIC_ERR(err, "error allocating LDT segment for first thread");
1106     }
1107 #endif
1108
1109     uintptr_t param;
1110     registers_get_param(enabled_area, &param);
1111
1112     registers_set_initial(&thread->regs, thread, (lvaddr_t)thread_entry,
1113                           /* TODO: pass stack base and limit, choose in arch
1114                            * code (possibly setting up other hints on stack) */
1115                           (lvaddr_t)thread->stack_top,
1116                           (lvaddr_t)bootstrap_thread, param, 0, 0);
1117
1118     // Switch to it (always on this dispatcher)
1119     thread->disp = handle;
1120     thread_enqueue(thread, &disp_gen->runq);
1121     disp_gen->current = thread;
1122     disp->haswork = true;
1123     disp_resume(handle, &thread->regs);
1124 }
1125
1126 /**
1127  * \brief Called on the remote core when spanning a domain across cores
1128  *
1129  * Runs the provided thread after enqueuing it and enabling the dispatcher
1130  */
1131 void thread_init_remote(dispatcher_handle_t handle, struct thread *thread)
1132 {
1133     struct dispatcher_shared_generic *disp =
1134         get_dispatcher_shared_generic(handle);
1135     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1136     thread_enqueue(thread, &disp_gen->runq);
1137     disp_gen->current = thread;
1138     disp->haswork = true;
1139     disp_resume(handle, &thread->regs);
1140 }
1141
1142 /**
1143  * \brief Prepare to span the current domain
1144  *
1145  * This is a kludge. It is called from domain.c when creating a new dispatcher,
1146  * and is responsible for pre-allocating all the storage that might be needed
1147  * for thread metadata in the slab allocator. It can go away once we sanely
1148  * manage the vspace across multiple dispatchers in a domain.
1149  */
1150 void threads_prepare_to_span(dispatcher_handle_t newdh)
1151 {
1152     static bool called;
1153
1154     if (!called) {
1155         called = true;
1156
1157         thread_mutex_lock(&thread_slabs_mutex);
1158         acquire_spinlock(&thread_slabs_spinlock);
1159
1160         while (slab_freecount(&thread_slabs) < MAX_THREADS - 1) {
1161             struct capref frame;
1162             size_t size;
1163             void *buf;
1164             errval_t err;
1165
1166             err = slot_alloc(&frame);
1167             if (err_is_fail(err)) {
1168                 USER_PANIC_ERR(err, "in slot_alloc while prefilling thread slabs\n");
1169             }
1170
1171             size_t blocksize = sizeof(struct thread) + tls_block_total_len;
1172             err = vspace_mmu_aware_map(&thread_slabs_vm, frame, blocksize,
1173                                        &buf, &size);
1174             if (err_is_fail(err)) {
1175                 slot_free(frame);
1176                 if (err_no(err) == LIB_ERR_VSPACE_MMU_AWARE_NO_SPACE) {
1177                     // we've wasted space with fragmentation
1178                     // cross our fingers and hope for the best...
1179                     break;
1180                 }
1181                 USER_PANIC_ERR(err, "in vspace_mmu_aware_map while prefilling "
1182                                "thread slabs\n");
1183             }
1184
1185             slab_grow(&thread_slabs, buf, size);
1186         }
1187
1188         release_spinlock(&thread_slabs_spinlock);
1189         thread_mutex_unlock(&thread_slabs_mutex);
1190     }
1191 }
1192
1193 /**
1194  * \brief Pause (suspend execution of) the given thread, and optionally capture its register state
1195  *
1196  * The thread will not be run, until a subsequent call to thread_resume()
1197  */
1198 void thread_pause_and_capture_state(struct thread *thread,
1199                                     arch_registers_state_t **ret_regs,
1200                                     arch_registers_fpu_state_t **ret_fpuregs)
1201 {
1202     assert(thread != NULL);
1203     dispatcher_handle_t dh = disp_disable();
1204     struct dispatcher_generic *disp = get_dispatcher_generic(dh);
1205     if (thread->disp == dh) {
1206         if (!thread->paused) {
1207             thread->paused = true;
1208             if (thread == disp->current) { // doesn't make much sense...
1209                 sys_print("Warning: pausing current thread!\n",100);
1210                 assert_disabled(thread->state == THREAD_STATE_RUNNABLE);
1211                 thread_block_disabled(dh, NULL);
1212             } else if (thread->state == THREAD_STATE_RUNNABLE) {
1213                 thread_remove_from_queue(&disp->runq, thread);
1214             }
1215         }
1216         if (ret_regs != NULL) {
1217             *ret_regs = &thread->regs;
1218         }
1219         if (ret_fpuregs != NULL) {
1220             if (thread->used_fpu) {
1221                 // FIXME: this may not be the right FPU state?
1222                 *ret_fpuregs = &thread->fpu_state;
1223             } else {
1224                 *ret_fpuregs = NULL;
1225             }
1226         }
1227     } else {
1228         USER_PANIC("NYI: remote dispatcher thread_pause()");
1229     }
1230     disp_enable(dh);
1231 }
1232
1233 /**
1234  * \brief Pause (suspend execution of) the given thread
1235  *
1236  * The thread will not be run, until a subsequent call to thread_resume()
1237  */
1238 void thread_pause(struct thread *thread)
1239 {
1240     thread_pause_and_capture_state(thread, NULL, NULL);
1241 }
1242
1243 /**
1244  * \brief Resume execution of a thread previously suspended by thread_pause()
1245  */
1246 void thread_resume(struct thread *thread)
1247 {
1248     assert(thread != NULL);
1249     dispatcher_handle_t dh = disp_disable();
1250     struct dispatcher_generic *disp = get_dispatcher_generic(dh);
1251     if (thread->disp == dh) {
1252         if (thread->paused) {
1253             thread->paused = false;
1254             if (thread->state == THREAD_STATE_RUNNABLE) {
1255                 thread_enqueue(thread, &disp->runq);
1256             }
1257         }
1258     } else {
1259         USER_PANIC("NYI: remote dispatcher thread_resume()");
1260     }
1261     disp_enable(dh);
1262 }
1263
1264 /**
1265  * \brief Set old-style thread-local storage pointer.
1266  * \param p   User's pointer
1267  */
1268 void thread_set_tls(void *p)
1269 {
1270     struct thread *me = thread_self();
1271     me->userptr = p;
1272 }
1273
1274 void thread_set_tls_key(int key, void *p)
1275 {
1276     struct thread *me = thread_self();
1277     me->userptrs[key] = p;
1278 }
1279
1280 /**
1281  * \brief Return old-style thread-local storage pointer.
1282  * \return User's pointer, previously passed to thread_set_tls()
1283  */
1284 void *thread_get_tls(void)
1285 {
1286     struct thread *me = thread_self();
1287     return me->userptr;
1288 }
1289
1290 void *thread_get_tls_key(int key)
1291 {
1292     struct thread *me = thread_self();
1293     return me->userptrs[key];
1294 }
1295
1296 /**
1297  * \brief Set the exception handler function for the current thread.
1298  *        Optionally also change its stack, and return the old values.
1299  *
1300  * \param newhandler New exception handler. Pass NULL to disable an existing handler.
1301  * \param oldhandler If non-NULL, returns previous exception handler
1302  * \param new_stack_base If non-NULL, sets a new exception handler stack (base)
1303  * \param new_stack_top  If non-NULL, sets a new exception handler stack (top)
1304  * \param old_stack_base If non-NULL, returns previous stack base
1305  * \param old_stack_top If non-NULL, returns previous stack top
1306  */
1307 errval_t thread_set_exception_handler(exception_handler_fn newhandler,
1308                                       exception_handler_fn *oldhandler,
1309                                       void *new_stack_base, void *new_stack_top,
1310                                       void **old_stack_base, void **old_stack_top)
1311 {
1312     struct thread *me = thread_self();
1313
1314     if (oldhandler != NULL) {
1315         *oldhandler = me->exception_handler;
1316     }
1317
1318     if (old_stack_base != NULL) {
1319         *old_stack_base = me->exception_stack;
1320     }
1321
1322     if (old_stack_top != NULL) {
1323         *old_stack_top = me->exception_stack_top;
1324     }
1325
1326     me->exception_handler = newhandler;
1327
1328     if (new_stack_base != NULL && new_stack_top != NULL) {
1329         me->exception_stack = new_stack_base;
1330         me->exception_stack_top = new_stack_top;
1331     }
1332
1333     return SYS_ERR_OK;
1334 }
1335
1336 static void exception_handler_wrapper(arch_registers_state_t *cpuframe,
1337                                       arch_registers_fpu_state_t *fpuframe,
1338                                       uintptr_t hack_arg, void *addr)
1339 {
1340     struct thread *me = thread_self();
1341
1342     assert(me->in_exception);
1343     assert(me->exception_handler != NULL);
1344
1345     // XXX: unpack hack arg
1346     enum exception_type type = hack_arg >> 16;
1347     int subtype = hack_arg & 0xffff;
1348
1349     // run handler
1350     me->exception_handler(type, subtype, addr, cpuframe, fpuframe);
1351
1352     // resume state
1353     dispatcher_handle_t dh = disp_disable();
1354     struct dispatcher_generic *disp_gen = get_dispatcher_generic(dh);
1355     //memcpy(&me->regs, cpuframe, sizeof(arch_registers_state_t));
1356
1357 #ifdef FPU_LAZY_CONTEXT_SWITCH
1358     if (fpuframe != NULL) {
1359         assert_disabled(me->used_fpu);
1360         arch_registers_fpu_state_t *dest;
1361         if (disp_gen->fpu_thread == me) {
1362             dest = dispatcher_get_enabled_fpu_save_area(dh);
1363         } else {
1364             dest = &me->fpu_state;
1365         }
1366         fpu_copy(dest, fpuframe);
1367         fpu_trap_on();
1368     }
1369 #endif
1370
1371     assert_disabled(me->in_exception);
1372     me->in_exception = false;
1373
1374     assert_disabled(disp_gen->current == me);
1375     disp_resume(dh, cpuframe);
1376 }
1377
1378 #if 0
1379 void thread_debug_regs(struct thread *t);
1380 void thread_debug_regs(struct thread *t)
1381 {
1382   printf("%d: RIP = %lx, RSP = %lx\n", disp_get_domain_id(),
1383          t->regs.rip, t->regs.rsp);
1384   uint64_t *stack = (uint64_t *)t->regs.rsp;
1385   printf("%d: ", disp_get_domain_id());
1386   for(int i = 0; i < 30; i++) {
1387     printf("%lx ", stack[i]);
1388   }
1389   printf("\n");
1390 }
1391 #endif
1392
1393 /**
1394  * \brief Deliver an exception to the current thread, and resume.
1395  *
1396  * This may only be called from the dispatcher (on its stack and while
1397  * disabled!).
1398  *
1399  * \param handle Dispatcher handle
1400  * \param type   Exception type
1401  * \param subtype Exception subtype
1402  * \param addr   Exception address
1403  * \param regs   CPU register state at time of exception
1404  */
1405 void thread_deliver_exception_disabled(dispatcher_handle_t handle,
1406                                        enum exception_type type, int subtype,
1407                                        void *addr, arch_registers_state_t *regs)
1408 {
1409     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
1410     struct thread *thread = disp_gen->current;
1411     assert_disabled(thread != NULL);
1412     assert_disabled(disp_gen->runq != NULL);
1413
1414     // can we deliver the exception?
1415     if (thread->exception_handler == NULL || thread->exception_stack_top == NULL
1416         || thread->in_exception) {
1417         if (thread->in_exception) {
1418             sys_print("Can't deliver exception to thread: already in handler\n",
1419                       100);
1420         } else {
1421             sys_print("Can't deliver exception to thread: handler not set\n",
1422                       100);
1423         }
1424
1425         // warn on stack overflow.
1426         lvaddr_t sp = (lvaddr_t) registers_get_sp(regs);
1427         if (sp < (lvaddr_t)thread->stack ||
1428             sp > (lvaddr_t)thread->stack_top) {
1429             char str[256];
1430             snprintf(str, sizeof(str), "Error: stack bounds exceeded: sp = 0x%"
1431                      PRIxPTR " but [bottom, top] = [0x%" PRIxPTR ", 0x%"
1432                      PRIxPTR "]\n", (lvaddr_t) sp, (lvaddr_t) thread->stack,
1433                      (lvaddr_t) thread->stack_top);
1434             sys_print(str, sizeof(str));
1435         }
1436
1437         // TODO: actually delete the thread!
1438         disp_gen->current = NULL;
1439         thread_remove_from_queue(&disp_gen->runq, thread);
1440         return;
1441     }
1442
1443     thread->in_exception = true;
1444
1445     lvaddr_t stack_top = (lvaddr_t)thread->exception_stack_top;
1446
1447     // save thread's state at time of fault on top of exception stack
1448     stack_top -= sizeof(arch_registers_state_t);
1449     arch_registers_state_t *cpuframe = (void *)stack_top;
1450     memcpy(cpuframe, regs, sizeof(arch_registers_state_t));
1451
1452     arch_registers_fpu_state_t *fpuframe = NULL;
1453 #ifdef FPU_LAZY_CONTEXT_SWITCH
1454     if (thread->used_fpu) {
1455         stack_top -= sizeof(arch_registers_fpu_state_t);
1456         fpuframe = (void *)stack_top;
1457         fpu_copy(fpuframe, &thread->fpu_state);
1458     }
1459 #endif
1460
1461     // align stack
1462     stack_top -= stack_top % STACK_ALIGNMENT;
1463
1464     // XXX: sanity-check to ensure we have a sensible amount of exception stack left
1465     assert_disabled(stack_top > (lvaddr_t)thread->exception_stack + 8192);
1466
1467     // XXX: pack two small ints together to fit into a single register
1468     uintptr_t hack_arg = (uintptr_t)type << 16 | (subtype & 0xffff);
1469
1470     registers_set_initial(&thread->regs, thread,
1471                           (lvaddr_t)exception_handler_wrapper,
1472                           stack_top, (lvaddr_t)cpuframe, (lvaddr_t)fpuframe,
1473                           hack_arg, (lvaddr_t)addr);
1474
1475     disp_resume(handle, &thread->regs);
1476 }