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