xref: /openbmc/qemu/util/coroutine-sigaltstack.c (revision 9afa888c)
110817bf0SDaniel P. Berrange /*
210817bf0SDaniel P. Berrange  * sigaltstack coroutine initialization code
310817bf0SDaniel P. Berrange  *
410817bf0SDaniel P. Berrange  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
510817bf0SDaniel P. Berrange  * Copyright (C) 2011  Kevin Wolf <kwolf@redhat.com>
610817bf0SDaniel P. Berrange  * Copyright (C) 2012  Alex Barcelo <abarcelo@ac.upc.edu>
710817bf0SDaniel P. Berrange ** This file is partly based on pth_mctx.c, from the GNU Portable Threads
810817bf0SDaniel P. Berrange **  Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com>
910817bf0SDaniel P. Berrange  *
1010817bf0SDaniel P. Berrange  * This library is free software; you can redistribute it and/or
1110817bf0SDaniel P. Berrange  * modify it under the terms of the GNU Lesser General Public
1210817bf0SDaniel P. Berrange  * License as published by the Free Software Foundation; either
1310817bf0SDaniel P. Berrange  * version 2.1 of the License, or (at your option) any later version.
1410817bf0SDaniel P. Berrange  *
1510817bf0SDaniel P. Berrange  * This library is distributed in the hope that it will be useful,
1610817bf0SDaniel P. Berrange  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1710817bf0SDaniel P. Berrange  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1810817bf0SDaniel P. Berrange  * Lesser General Public License for more details.
1910817bf0SDaniel P. Berrange  *
2010817bf0SDaniel P. Berrange  * You should have received a copy of the GNU Lesser General Public
2110817bf0SDaniel P. Berrange  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
2210817bf0SDaniel P. Berrange  */
2310817bf0SDaniel P. Berrange 
2410817bf0SDaniel P. Berrange /* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
2510817bf0SDaniel P. Berrange #undef _FORTIFY_SOURCE
26*9afa888cSDaniel P. Berrangé #define _FORTIFY_SOURCE 0
27*9afa888cSDaniel P. Berrangé 
28aafd7584SPeter Maydell #include "qemu/osdep.h"
2910817bf0SDaniel P. Berrange #include <pthread.h>
3010817bf0SDaniel P. Berrange #include "qemu/coroutine_int.h"
3110817bf0SDaniel P. Berrange 
32ff76097aSDaniele Buono #ifdef CONFIG_SAFESTACK
33ff76097aSDaniele Buono #error "SafeStack is not compatible with code run in alternate signal stacks"
34ff76097aSDaniele Buono #endif
35ff76097aSDaniele Buono 
3610817bf0SDaniel P. Berrange typedef struct {
3710817bf0SDaniel P. Berrange     Coroutine base;
3810817bf0SDaniel P. Berrange     void *stack;
392f4aa232SPeter Lieven     size_t stack_size;
4010817bf0SDaniel P. Berrange     sigjmp_buf env;
41be87a393SPeter Lieven } CoroutineSigAltStack;
4210817bf0SDaniel P. Berrange 
4310817bf0SDaniel P. Berrange /**
4410817bf0SDaniel P. Berrange  * Per-thread coroutine bookkeeping
4510817bf0SDaniel P. Berrange  */
4610817bf0SDaniel P. Berrange typedef struct {
4710817bf0SDaniel P. Berrange     /** Currently executing coroutine */
4810817bf0SDaniel P. Berrange     Coroutine *current;
4910817bf0SDaniel P. Berrange 
5010817bf0SDaniel P. Berrange     /** The default coroutine */
51be87a393SPeter Lieven     CoroutineSigAltStack leader;
5210817bf0SDaniel P. Berrange 
5310817bf0SDaniel P. Berrange     /** Information for the signal handler (trampoline) */
5410817bf0SDaniel P. Berrange     sigjmp_buf tr_reenter;
5510817bf0SDaniel P. Berrange     volatile sig_atomic_t tr_called;
5610817bf0SDaniel P. Berrange     void *tr_handler;
5710817bf0SDaniel P. Berrange } CoroutineThreadState;
5810817bf0SDaniel P. Berrange 
5910817bf0SDaniel P. Berrange static pthread_key_t thread_state_key;
6010817bf0SDaniel P. Berrange 
coroutine_get_thread_state(void)6110817bf0SDaniel P. Berrange static CoroutineThreadState *coroutine_get_thread_state(void)
6210817bf0SDaniel P. Berrange {
6310817bf0SDaniel P. Berrange     CoroutineThreadState *s = pthread_getspecific(thread_state_key);
6410817bf0SDaniel P. Berrange 
6510817bf0SDaniel P. Berrange     if (!s) {
6610817bf0SDaniel P. Berrange         s = g_malloc0(sizeof(*s));
6710817bf0SDaniel P. Berrange         s->current = &s->leader.base;
6810817bf0SDaniel P. Berrange         pthread_setspecific(thread_state_key, s);
6910817bf0SDaniel P. Berrange     }
7010817bf0SDaniel P. Berrange     return s;
7110817bf0SDaniel P. Berrange }
7210817bf0SDaniel P. Berrange 
qemu_coroutine_thread_cleanup(void * opaque)7310817bf0SDaniel P. Berrange static void qemu_coroutine_thread_cleanup(void *opaque)
7410817bf0SDaniel P. Berrange {
7510817bf0SDaniel P. Berrange     CoroutineThreadState *s = opaque;
7610817bf0SDaniel P. Berrange 
7710817bf0SDaniel P. Berrange     g_free(s);
7810817bf0SDaniel P. Berrange }
7910817bf0SDaniel P. Berrange 
coroutine_init(void)8010817bf0SDaniel P. Berrange static void __attribute__((constructor)) coroutine_init(void)
8110817bf0SDaniel P. Berrange {
8210817bf0SDaniel P. Berrange     int ret;
8310817bf0SDaniel P. Berrange 
8410817bf0SDaniel P. Berrange     ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
8510817bf0SDaniel P. Berrange     if (ret != 0) {
8610817bf0SDaniel P. Berrange         fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
8710817bf0SDaniel P. Berrange         abort();
8810817bf0SDaniel P. Berrange     }
8910817bf0SDaniel P. Berrange }
9010817bf0SDaniel P. Berrange 
9110817bf0SDaniel P. Berrange /* "boot" function
9210817bf0SDaniel P. Berrange  * This is what starts the coroutine, is called from the trampoline
9310817bf0SDaniel P. Berrange  * (from the signal handler when it is not signal handling, read ahead
9410817bf0SDaniel P. Berrange  * for more information).
9510817bf0SDaniel P. Berrange  */
coroutine_bootstrap(CoroutineSigAltStack * self,Coroutine * co)96be87a393SPeter Lieven static void coroutine_bootstrap(CoroutineSigAltStack *self, Coroutine *co)
9710817bf0SDaniel P. Berrange {
9810817bf0SDaniel P. Berrange     /* Initialize longjmp environment and switch back the caller */
9910817bf0SDaniel P. Berrange     if (!sigsetjmp(self->env, 0)) {
10010817bf0SDaniel P. Berrange         siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
10110817bf0SDaniel P. Berrange     }
10210817bf0SDaniel P. Berrange 
10310817bf0SDaniel P. Berrange     while (true) {
10410817bf0SDaniel P. Berrange         co->entry(co->entry_arg);
10510817bf0SDaniel P. Berrange         qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
10610817bf0SDaniel P. Berrange     }
10710817bf0SDaniel P. Berrange }
10810817bf0SDaniel P. Berrange 
10910817bf0SDaniel P. Berrange /*
11010817bf0SDaniel P. Berrange  * This is used as the signal handler. This is called with the brand new stack
11110817bf0SDaniel P. Berrange  * (thanks to sigaltstack). We have to return, given that this is a signal
11210817bf0SDaniel P. Berrange  * handler and the sigmask and some other things are changed.
11310817bf0SDaniel P. Berrange  */
coroutine_trampoline(int signal)11410817bf0SDaniel P. Berrange static void coroutine_trampoline(int signal)
11510817bf0SDaniel P. Berrange {
116be87a393SPeter Lieven     CoroutineSigAltStack *self;
11710817bf0SDaniel P. Berrange     Coroutine *co;
11810817bf0SDaniel P. Berrange     CoroutineThreadState *coTS;
11910817bf0SDaniel P. Berrange 
12010817bf0SDaniel P. Berrange     /* Get the thread specific information */
12110817bf0SDaniel P. Berrange     coTS = coroutine_get_thread_state();
12210817bf0SDaniel P. Berrange     self = coTS->tr_handler;
12310817bf0SDaniel P. Berrange     coTS->tr_called = 1;
12410817bf0SDaniel P. Berrange     co = &self->base;
12510817bf0SDaniel P. Berrange 
12610817bf0SDaniel P. Berrange     /*
12710817bf0SDaniel P. Berrange      * Here we have to do a bit of a ping pong between the caller, given that
12810817bf0SDaniel P. Berrange      * this is a signal handler and we have to do a return "soon". Then the
12910817bf0SDaniel P. Berrange      * caller can reestablish everything and do a siglongjmp here again.
13010817bf0SDaniel P. Berrange      */
13110817bf0SDaniel P. Berrange     if (!sigsetjmp(coTS->tr_reenter, 0)) {
13210817bf0SDaniel P. Berrange         return;
13310817bf0SDaniel P. Berrange     }
13410817bf0SDaniel P. Berrange 
13510817bf0SDaniel P. Berrange     /*
13610817bf0SDaniel P. Berrange      * Ok, the caller has siglongjmp'ed back to us, so now prepare
13710817bf0SDaniel P. Berrange      * us for the real machine state switching. We have to jump
13810817bf0SDaniel P. Berrange      * into another function here to get a new stack context for
13910817bf0SDaniel P. Berrange      * the auto variables (which have to be auto-variables
14010817bf0SDaniel P. Berrange      * because the start of the thread happens later). Else with
14110817bf0SDaniel P. Berrange      * PIC (i.e. Position Independent Code which is used when PTH
14210817bf0SDaniel P. Berrange      * is built as a shared library) most platforms would
14310817bf0SDaniel P. Berrange      * horrible core dump as experience showed.
14410817bf0SDaniel P. Berrange      */
14510817bf0SDaniel P. Berrange     coroutine_bootstrap(self, co);
14610817bf0SDaniel P. Berrange }
14710817bf0SDaniel P. Berrange 
qemu_coroutine_new(void)14810817bf0SDaniel P. Berrange Coroutine *qemu_coroutine_new(void)
14910817bf0SDaniel P. Berrange {
150be87a393SPeter Lieven     CoroutineSigAltStack *co;
15110817bf0SDaniel P. Berrange     CoroutineThreadState *coTS;
15210817bf0SDaniel P. Berrange     struct sigaction sa;
15310817bf0SDaniel P. Berrange     struct sigaction osa;
15410817bf0SDaniel P. Berrange     stack_t ss;
15510817bf0SDaniel P. Berrange     stack_t oss;
15610817bf0SDaniel P. Berrange     sigset_t sigs;
15710817bf0SDaniel P. Berrange     sigset_t osigs;
15810817bf0SDaniel P. Berrange     sigjmp_buf old_env;
159f4be8225SMax Reitz     static pthread_mutex_t sigusr2_mutex = PTHREAD_MUTEX_INITIALIZER;
16010817bf0SDaniel P. Berrange 
16110817bf0SDaniel P. Berrange     /* The way to manipulate stack is with the sigaltstack function. We
16210817bf0SDaniel P. Berrange      * prepare a stack, with it delivering a signal to ourselves and then
16310817bf0SDaniel P. Berrange      * put sigsetjmp/siglongjmp where needed.
16410817bf0SDaniel P. Berrange      * This has been done keeping coroutine-ucontext as a model and with the
16510817bf0SDaniel P. Berrange      * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics
16610817bf0SDaniel P. Berrange      * of the coroutines and see pth_mctx.c (from the pth project) for the
16710817bf0SDaniel P. Berrange      * sigaltstack way of manipulating stacks.
16810817bf0SDaniel P. Berrange      */
16910817bf0SDaniel P. Berrange 
17010817bf0SDaniel P. Berrange     co = g_malloc0(sizeof(*co));
1712f4aa232SPeter Lieven     co->stack_size = COROUTINE_STACK_SIZE;
1722f4aa232SPeter Lieven     co->stack = qemu_alloc_stack(&co->stack_size);
17310817bf0SDaniel P. Berrange     co->base.entry_arg = &old_env; /* stash away our jmp_buf */
17410817bf0SDaniel P. Berrange 
17510817bf0SDaniel P. Berrange     coTS = coroutine_get_thread_state();
17610817bf0SDaniel P. Berrange     coTS->tr_handler = co;
17710817bf0SDaniel P. Berrange 
17810817bf0SDaniel P. Berrange     /*
17910817bf0SDaniel P. Berrange      * Preserve the SIGUSR2 signal state, block SIGUSR2,
18010817bf0SDaniel P. Berrange      * and establish our signal handler. The signal will
18110817bf0SDaniel P. Berrange      * later transfer control onto the signal stack.
18210817bf0SDaniel P. Berrange      */
18310817bf0SDaniel P. Berrange     sigemptyset(&sigs);
18410817bf0SDaniel P. Berrange     sigaddset(&sigs, SIGUSR2);
18510817bf0SDaniel P. Berrange     pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
18610817bf0SDaniel P. Berrange     sa.sa_handler = coroutine_trampoline;
18710817bf0SDaniel P. Berrange     sigfillset(&sa.sa_mask);
18810817bf0SDaniel P. Berrange     sa.sa_flags = SA_ONSTACK;
189f4be8225SMax Reitz 
190f4be8225SMax Reitz     /*
191f4be8225SMax Reitz      * sigaction() is a process-global operation.  We must not run
192f4be8225SMax Reitz      * this code in multiple threads at once.
193f4be8225SMax Reitz      */
194f4be8225SMax Reitz     pthread_mutex_lock(&sigusr2_mutex);
19510817bf0SDaniel P. Berrange     if (sigaction(SIGUSR2, &sa, &osa) != 0) {
19610817bf0SDaniel P. Berrange         abort();
19710817bf0SDaniel P. Berrange     }
19810817bf0SDaniel P. Berrange 
19910817bf0SDaniel P. Berrange     /*
20010817bf0SDaniel P. Berrange      * Set the new stack.
20110817bf0SDaniel P. Berrange      */
20210817bf0SDaniel P. Berrange     ss.ss_sp = co->stack;
2032f4aa232SPeter Lieven     ss.ss_size = co->stack_size;
20410817bf0SDaniel P. Berrange     ss.ss_flags = 0;
20510817bf0SDaniel P. Berrange     if (sigaltstack(&ss, &oss) < 0) {
20610817bf0SDaniel P. Berrange         abort();
20710817bf0SDaniel P. Berrange     }
20810817bf0SDaniel P. Berrange 
20910817bf0SDaniel P. Berrange     /*
21010817bf0SDaniel P. Berrange      * Now transfer control onto the signal stack and set it up.
21110817bf0SDaniel P. Berrange      * It will return immediately via "return" after the sigsetjmp()
21210817bf0SDaniel P. Berrange      * was performed. Be careful here with race conditions.  The
21310817bf0SDaniel P. Berrange      * signal can be delivered the first time sigsuspend() is
21410817bf0SDaniel P. Berrange      * called.
21510817bf0SDaniel P. Berrange      */
21610817bf0SDaniel P. Berrange     coTS->tr_called = 0;
21710817bf0SDaniel P. Berrange     pthread_kill(pthread_self(), SIGUSR2);
21810817bf0SDaniel P. Berrange     sigfillset(&sigs);
21910817bf0SDaniel P. Berrange     sigdelset(&sigs, SIGUSR2);
22010817bf0SDaniel P. Berrange     while (!coTS->tr_called) {
22110817bf0SDaniel P. Berrange         sigsuspend(&sigs);
22210817bf0SDaniel P. Berrange     }
22310817bf0SDaniel P. Berrange 
22410817bf0SDaniel P. Berrange     /*
22510817bf0SDaniel P. Berrange      * Inform the system that we are back off the signal stack by
22610817bf0SDaniel P. Berrange      * removing the alternative signal stack. Be careful here: It
22710817bf0SDaniel P. Berrange      * first has to be disabled, before it can be removed.
22810817bf0SDaniel P. Berrange      */
22910817bf0SDaniel P. Berrange     sigaltstack(NULL, &ss);
23010817bf0SDaniel P. Berrange     ss.ss_flags = SS_DISABLE;
23110817bf0SDaniel P. Berrange     if (sigaltstack(&ss, NULL) < 0) {
23210817bf0SDaniel P. Berrange         abort();
23310817bf0SDaniel P. Berrange     }
23410817bf0SDaniel P. Berrange     sigaltstack(NULL, &ss);
23510817bf0SDaniel P. Berrange     if (!(oss.ss_flags & SS_DISABLE)) {
23610817bf0SDaniel P. Berrange         sigaltstack(&oss, NULL);
23710817bf0SDaniel P. Berrange     }
23810817bf0SDaniel P. Berrange 
23910817bf0SDaniel P. Berrange     /*
24010817bf0SDaniel P. Berrange      * Restore the old SIGUSR2 signal handler and mask
24110817bf0SDaniel P. Berrange      */
24210817bf0SDaniel P. Berrange     sigaction(SIGUSR2, &osa, NULL);
243f4be8225SMax Reitz     pthread_mutex_unlock(&sigusr2_mutex);
244f4be8225SMax Reitz 
24510817bf0SDaniel P. Berrange     pthread_sigmask(SIG_SETMASK, &osigs, NULL);
24610817bf0SDaniel P. Berrange 
24710817bf0SDaniel P. Berrange     /*
24810817bf0SDaniel P. Berrange      * Now enter the trampoline again, but this time not as a signal
24910817bf0SDaniel P. Berrange      * handler. Instead we jump into it directly. The functionally
25010817bf0SDaniel P. Berrange      * redundant ping-pong pointer arithmetic is necessary to avoid
25110817bf0SDaniel P. Berrange      * type-conversion warnings related to the `volatile' qualifier and
25210817bf0SDaniel P. Berrange      * the fact that `jmp_buf' usually is an array type.
25310817bf0SDaniel P. Berrange      */
25410817bf0SDaniel P. Berrange     if (!sigsetjmp(old_env, 0)) {
25510817bf0SDaniel P. Berrange         siglongjmp(coTS->tr_reenter, 1);
25610817bf0SDaniel P. Berrange     }
25710817bf0SDaniel P. Berrange 
25810817bf0SDaniel P. Berrange     /*
25910817bf0SDaniel P. Berrange      * Ok, we returned again, so now we're finished
26010817bf0SDaniel P. Berrange      */
26110817bf0SDaniel P. Berrange 
26210817bf0SDaniel P. Berrange     return &co->base;
26310817bf0SDaniel P. Berrange }
26410817bf0SDaniel P. Berrange 
qemu_coroutine_delete(Coroutine * co_)26510817bf0SDaniel P. Berrange void qemu_coroutine_delete(Coroutine *co_)
26610817bf0SDaniel P. Berrange {
267be87a393SPeter Lieven     CoroutineSigAltStack *co = DO_UPCAST(CoroutineSigAltStack, base, co_);
26810817bf0SDaniel P. Berrange 
2692f4aa232SPeter Lieven     qemu_free_stack(co->stack, co->stack_size);
27010817bf0SDaniel P. Berrange     g_free(co);
27110817bf0SDaniel P. Berrange }
27210817bf0SDaniel P. Berrange 
qemu_coroutine_switch(Coroutine * from_,Coroutine * to_,CoroutineAction action)27310817bf0SDaniel P. Berrange CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
27410817bf0SDaniel P. Berrange                                       CoroutineAction action)
27510817bf0SDaniel P. Berrange {
276be87a393SPeter Lieven     CoroutineSigAltStack *from = DO_UPCAST(CoroutineSigAltStack, base, from_);
277be87a393SPeter Lieven     CoroutineSigAltStack *to = DO_UPCAST(CoroutineSigAltStack, base, to_);
27810817bf0SDaniel P. Berrange     CoroutineThreadState *s = coroutine_get_thread_state();
27910817bf0SDaniel P. Berrange     int ret;
28010817bf0SDaniel P. Berrange 
28110817bf0SDaniel P. Berrange     s->current = to_;
28210817bf0SDaniel P. Berrange 
28310817bf0SDaniel P. Berrange     ret = sigsetjmp(from->env, 0);
28410817bf0SDaniel P. Berrange     if (ret == 0) {
28510817bf0SDaniel P. Berrange         siglongjmp(to->env, action);
28610817bf0SDaniel P. Berrange     }
28710817bf0SDaniel P. Berrange     return ret;
28810817bf0SDaniel P. Berrange }
28910817bf0SDaniel P. Berrange 
qemu_coroutine_self(void)29010817bf0SDaniel P. Berrange Coroutine *qemu_coroutine_self(void)
29110817bf0SDaniel P. Berrange {
29210817bf0SDaniel P. Berrange     CoroutineThreadState *s = coroutine_get_thread_state();
29310817bf0SDaniel P. Berrange 
29410817bf0SDaniel P. Berrange     return s->current;
29510817bf0SDaniel P. Berrange }
29610817bf0SDaniel P. Berrange 
qemu_in_coroutine(void)29710817bf0SDaniel P. Berrange bool qemu_in_coroutine(void)
29810817bf0SDaniel P. Berrange {
29910817bf0SDaniel P. Berrange     CoroutineThreadState *s = pthread_getspecific(thread_state_key);
30010817bf0SDaniel P. Berrange 
30110817bf0SDaniel P. Berrange     return s && s->current->caller;
30210817bf0SDaniel P. Berrange }
30310817bf0SDaniel P. Berrange 
304