1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#ifdef _FORTIFY_SOURCE
26#undef _FORTIFY_SOURCE
27#endif
28#include "qemu/osdep.h"
29#include <pthread.h>
30#include "qemu-common.h"
31#include "qemu/coroutine_int.h"
32
33#ifdef CONFIG_SAFESTACK
34#error "SafeStack is not compatible with code run in alternate signal stacks"
35#endif
36
37typedef struct {
38 Coroutine base;
39 void *stack;
40 size_t stack_size;
41 sigjmp_buf env;
42} CoroutineSigAltStack;
43
44
45
46
47typedef struct {
48
49 Coroutine *current;
50
51
52 CoroutineSigAltStack leader;
53
54
55 sigjmp_buf tr_reenter;
56 volatile sig_atomic_t tr_called;
57 void *tr_handler;
58} CoroutineThreadState;
59
60static pthread_key_t thread_state_key;
61
62static CoroutineThreadState *coroutine_get_thread_state(void)
63{
64 CoroutineThreadState *s = pthread_getspecific(thread_state_key);
65
66 if (!s) {
67 s = g_malloc0(sizeof(*s));
68 s->current = &s->leader.base;
69 pthread_setspecific(thread_state_key, s);
70 }
71 return s;
72}
73
74static void qemu_coroutine_thread_cleanup(void *opaque)
75{
76 CoroutineThreadState *s = opaque;
77
78 g_free(s);
79}
80
81static void __attribute__((constructor)) coroutine_init(void)
82{
83 int ret;
84
85 ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
86 if (ret != 0) {
87 fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
88 abort();
89 }
90}
91
92
93
94
95
96
97static void coroutine_bootstrap(CoroutineSigAltStack *self, Coroutine *co)
98{
99
100 if (!sigsetjmp(self->env, 0)) {
101 siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
102 }
103
104 while (true) {
105 co->entry(co->entry_arg);
106 qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
107 }
108}
109
110
111
112
113
114
115static void coroutine_trampoline(int signal)
116{
117 CoroutineSigAltStack *self;
118 Coroutine *co;
119 CoroutineThreadState *coTS;
120
121
122 coTS = coroutine_get_thread_state();
123 self = coTS->tr_handler;
124 coTS->tr_called = 1;
125 co = &self->base;
126
127
128
129
130
131
132 if (!sigsetjmp(coTS->tr_reenter, 0)) {
133 return;
134 }
135
136
137
138
139
140
141
142
143
144
145
146 coroutine_bootstrap(self, co);
147}
148
149Coroutine *qemu_coroutine_new(void)
150{
151 CoroutineSigAltStack *co;
152 CoroutineThreadState *coTS;
153 struct sigaction sa;
154 struct sigaction osa;
155 stack_t ss;
156 stack_t oss;
157 sigset_t sigs;
158 sigset_t osigs;
159 sigjmp_buf old_env;
160 static pthread_mutex_t sigusr2_mutex = PTHREAD_MUTEX_INITIALIZER;
161
162
163
164
165
166
167
168
169
170
171 co = g_malloc0(sizeof(*co));
172 co->stack_size = COROUTINE_STACK_SIZE;
173 co->stack = qemu_alloc_stack(&co->stack_size);
174 co->base.entry_arg = &old_env;
175
176 coTS = coroutine_get_thread_state();
177 coTS->tr_handler = co;
178
179
180
181
182
183
184 sigemptyset(&sigs);
185 sigaddset(&sigs, SIGUSR2);
186 pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
187 sa.sa_handler = coroutine_trampoline;
188 sigfillset(&sa.sa_mask);
189 sa.sa_flags = SA_ONSTACK;
190
191
192
193
194
195 pthread_mutex_lock(&sigusr2_mutex);
196 if (sigaction(SIGUSR2, &sa, &osa) != 0) {
197 abort();
198 }
199
200
201
202
203 ss.ss_sp = co->stack;
204 ss.ss_size = co->stack_size;
205 ss.ss_flags = 0;
206 if (sigaltstack(&ss, &oss) < 0) {
207 abort();
208 }
209
210
211
212
213
214
215
216
217 coTS->tr_called = 0;
218 pthread_kill(pthread_self(), SIGUSR2);
219 sigfillset(&sigs);
220 sigdelset(&sigs, SIGUSR2);
221 while (!coTS->tr_called) {
222 sigsuspend(&sigs);
223 }
224
225
226
227
228
229
230 sigaltstack(NULL, &ss);
231 ss.ss_flags = SS_DISABLE;
232 if (sigaltstack(&ss, NULL) < 0) {
233 abort();
234 }
235 sigaltstack(NULL, &ss);
236 if (!(oss.ss_flags & SS_DISABLE)) {
237 sigaltstack(&oss, NULL);
238 }
239
240
241
242
243 sigaction(SIGUSR2, &osa, NULL);
244 pthread_mutex_unlock(&sigusr2_mutex);
245
246 pthread_sigmask(SIG_SETMASK, &osigs, NULL);
247
248
249
250
251
252
253
254
255 if (!sigsetjmp(old_env, 0)) {
256 siglongjmp(coTS->tr_reenter, 1);
257 }
258
259
260
261
262
263 return &co->base;
264}
265
266void qemu_coroutine_delete(Coroutine *co_)
267{
268 CoroutineSigAltStack *co = DO_UPCAST(CoroutineSigAltStack, base, co_);
269
270 qemu_free_stack(co->stack, co->stack_size);
271 g_free(co);
272}
273
274CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
275 CoroutineAction action)
276{
277 CoroutineSigAltStack *from = DO_UPCAST(CoroutineSigAltStack, base, from_);
278 CoroutineSigAltStack *to = DO_UPCAST(CoroutineSigAltStack, base, to_);
279 CoroutineThreadState *s = coroutine_get_thread_state();
280 int ret;
281
282 s->current = to_;
283
284 ret = sigsetjmp(from->env, 0);
285 if (ret == 0) {
286 siglongjmp(to->env, action);
287 }
288 return ret;
289}
290
291Coroutine *qemu_coroutine_self(void)
292{
293 CoroutineThreadState *s = coroutine_get_thread_state();
294
295 return s->current;
296}
297
298bool qemu_in_coroutine(void)
299{
300 CoroutineThreadState *s = pthread_getspecific(thread_state_key);
301
302 return s && s->current->caller;
303}
304
305