AmendHub

Download

jcs

/

subtext

/

uthread.c

 

(View History)

jcs   uthread: Add hooks for checking uthread canaries on every function Latest amendment: 626 on 2026-01-01

1 /*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #pragma options(!profile)
18
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <setjmp.h>
24
25 #include "uthread.h"
26 #include "util.h"
27 #include "logger.h"
28
29 #define COORDINATOR_STACK_SIZE ((1024UL * 100UL) - 1)
30 #define UTHREAD_STACK_SIZE ((1024UL * 50UL) - 1)
31 #define UTHREAD_CANARY 0xdeadf00d
32
33 jmp_buf uthread_coord_env;
34 static struct uthread uthreads[NUM_UTHREADS];
35 struct uthread *uthread_current = NULL;
36 unsigned long uthread_main_canary;
37
38 void uthread_begin(struct uthread *uthread);
39 void _profile_(unsigned char *);
40 static void * profile_exit(void);
41
42 /*
43 * | A5 world |
44 * |-------------------| 0x2251b0 CurStackBase (stacks_top)
45 * | coordinator stack | COORDINATOR_STACK_SIZE
46 * |-------------------| 0x24934d (coordinator stack bottom)
47 * | (alignment) |
48 * |-------------------| 0x20c000 (uthread_stacks_top)
49 * : (other threads) :
50 * |-------------------| 0x1da000 (uthreads[1].stack_top downward)
51 * | uthread 1 stack | STACK_SIZE
52 * | |
53 * |-------------------| 0x1cd802 (uthreads[1].stack_canary upward)
54 * |-------------------| 0x1cd801 (uthreads[1].stack_bottom)
55 * |-------------------| 0x1cd800 (uthreads[0].stack_top downward)
56 * | uthread 0 stack | STACK_SIZE
57 * | |
58 * |-------------------| 0x1c1002 (uthreads[0].stack_canary upward)
59 * |-------------------| 0x1c1001 (uthreads[0].stack_bottom)
60 * |-------------------| 0x1c1000 ApplLimit (stacks_bottom)
61 * | |
62 * | heap |
63 * | |
64 * |-------------------| 0x16f5D8 ApplZone
65 */
66 void
67 uthread_init(void)
68 {
69 static unsigned long stacks_top, stacks_bottom, uthread_stacks_top,
70 loc;
71 short i;
72
73 stacks_top = (unsigned long)CurStackBase;
74 /* align uthread stacks */
75 uthread_stacks_top = stacks_top - COORDINATOR_STACK_SIZE;
76 uthread_stacks_top -= uthread_stacks_top % 1024;
77
78 uthread_main_canary = uthread_stacks_top + 2;
79 *(unsigned long *)uthread_main_canary = UTHREAD_CANARY;
80
81 stacks_bottom = uthread_stacks_top - ((UTHREAD_STACK_SIZE + 1) *
82 NUM_UTHREADS);
83 if (stacks_bottom <= (unsigned long)ApplZone)
84 panic("stacks_bottom < ApplZone");
85
86 SetApplLimit((Ptr)stacks_bottom);
87 if (MemError())
88 panic("Failed to SetApplLimit to %lu", stacks_bottom);
89
90 loc = stacks_bottom + 1;
91 for (i = 0; i < NUM_UTHREADS; i++) {
92 uthreads[i].id = i;
93 uthreads[i].state = UTHREAD_STATE_DEAD;
94 uthreads[i].stack_bottom = loc;
95 uthreads[i].stack_canary = uthreads[i].stack_bottom + 1;
96 uthreads[i].stack_top = uthreads[i].stack_bottom +
97 UTHREAD_STACK_SIZE;
98 loc = uthreads[i].stack_top + 1;
99 }
100
101 if (loc > uthread_stacks_top + 1)
102 panic("uthreads ran into main stack");
103
104 uthread_verify();
105 }
106
107 struct uthread *
108 uthread_add(void *func, void *arg)
109 {
110 short i;
111
112 for (i = 0; i < NUM_UTHREADS; i++) {
113 if (uthreads[i].state != UTHREAD_STATE_DEAD)
114 continue;
115
116 uthreads[i].func = func;
117 uthreads[i].arg = arg;
118 uthreads[i].state = UTHREAD_STATE_SETUP;
119
120 memset((Ptr)uthreads[i].stack_bottom, 0, UTHREAD_STACK_SIZE);
121 *(unsigned long *)uthreads[i].stack_canary = UTHREAD_CANARY;
122
123 return &uthreads[i];
124 }
125
126 return NULL;
127 }
128
129 #pragma nooptimize(uthread_begin)
130 void
131 uthread_begin(struct uthread *uthread)
132 {
133 register unsigned long stack_top = uthread->stack_top;
134
135 asm {
136 move.l stack_top, a7
137 };
138
139 /* add a canary at the bottom of our stack */
140 *(unsigned long *)uthread->stack_canary = UTHREAD_CANARY;
141
142 uthread->func(uthread, uthread->arg);
143
144 uthread_verify();
145
146 /* uthread variable is probably trashed at this point */
147 if (uthread_current->state == UTHREAD_STATE_REPEAT)
148 uthread_current->state = UTHREAD_STATE_SETUP;
149 else
150 uthread_current->state = UTHREAD_STATE_DEAD;
151
152 longjmp(uthread_coord_env, UTHREAD_SETJMP_YIELDED);
153 }
154
155 #pragma nooptimize(uthread_yield)
156 void
157 uthread_yield(void)
158 {
159 volatile long magic = UTHREAD_CANARY;
160 register unsigned long _sp = 0;
161 unsigned long n;
162
163 asm {
164 move.l a7, _sp
165 };
166
167 if (uthread_current == NULL)
168 panic("uthread_yield not from a thread!");
169
170 if (uthread_current->state != UTHREAD_STATE_SLEEPING)
171 uthread_current->state = UTHREAD_STATE_YIELDING;
172
173 uthread_verify();
174
175 if (setjmp(uthread_current->env) == 0)
176 longjmp(uthread_coord_env, UTHREAD_SETJMP_YIELDED);
177 /* will not return */
178
179 if (magic != UTHREAD_CANARY)
180 panic("uthread stack canary dead!");
181 }
182
183 void
184 uthread_msleep(unsigned long millis)
185 {
186 /* Ticks is in 1/60 of a second, but it's easier to think in millis */
187 unsigned long ts = millis / ((double)1000 / (double)60);
188
189 uthread_current->state = UTHREAD_STATE_SLEEPING;
190 uthread_current->sleeping_until = Ticks + ts;
191 uthread_yield();
192 }
193
194 void
195 uthread_wakeup(struct uthread *uthread)
196 {
197 uthread->sleeping_until = 0;
198 uthread_yield();
199 }
200
201 void
202 uthread_coordinate(void)
203 {
204 short i;
205
206 for (i = 0; i < NUM_UTHREADS; i++) {
207 if (uthreads[i].state == UTHREAD_STATE_DEAD)
208 continue;
209 if (uthreads[i].state == UTHREAD_STATE_SLEEPING &&
210 Ticks < uthreads[i].sleeping_until)
211 continue;
212
213 switch (setjmp(uthread_coord_env)) {
214 case UTHREAD_SETJMP_DEFAULT:
215 uthread_current = &uthreads[i];
216
217 switch (uthread_current->state) {
218 case UTHREAD_STATE_SETUP:
219 uthread_current->state = UTHREAD_STATE_RUNNING;
220 uthread_begin(uthread_current);
221 break;
222 case UTHREAD_STATE_YIELDING:
223 case UTHREAD_STATE_SLEEPING:
224 uthread_current->state = UTHREAD_STATE_RUNNING;
225 longjmp(uthread_current->env, UTHREAD_SETJMP_YIELDED);
226 break;
227 }
228 break;
229 case UTHREAD_SETJMP_YIELDED:
230 break;
231 default:
232 panic("unknown uthread state");
233 }
234 }
235
236 uthread_current = NULL;
237 uthread_verify();
238 }
239
240 void
241 uthread_verify(void)
242 {
243 register unsigned long _sp = 0;
244 short i;
245
246 asm {
247 move.l a7, _sp
248 };
249
250 if (*(unsigned long *)uthread_main_canary != UTHREAD_CANARY)
251 panic("coordinator thread canary died");
252
253 for (i = 0; i < NUM_UTHREADS; i++) {
254 if (uthreads[i].state == UTHREAD_STATE_DEAD)
255 continue;
256
257 if (*(unsigned long *)uthreads[i].stack_canary != UTHREAD_CANARY)
258 warn("thread %d stack canary died", i);
259 }
260 }
261
262 static struct stack {
263 unsigned long ret[64];
264 unsigned depth;
265 } uthread_stacks[NUM_UTHREADS + 1] = { 0 };
266
267 void
268 _profile_(unsigned char *fname)
269 {
270 register struct stack *s;
271 register unsigned long *ret;
272
273 if (uthread_current)
274 s = &uthread_stacks[uthread_current->id];
275 else
276 s = &uthread_stacks[NUM_UTHREADS];
277
278 ret = &s->ret[s->depth++];
279
280 asm {
281 movea.l (a6),a0
282 move.l 4(a0),(ret)
283 lea @exit,a1
284 move.l a1,4(a0)
285 }
286
287 return;
288
289 /* the profiled function will return here */
290 exit:
291 asm {
292 move.l d0,-(sp) ; preserve result
293 jsr profile_exit
294 movea.l d0,a0 ; real return address
295 move.l (sp)+,d0 ; restore result
296 jmp (a0) ; return
297 }
298 }
299
300 static void *
301 profile_exit(void)
302 {
303 register struct stack *s;
304 unsigned long *ret;
305
306 if (uthread_current)
307 s = &uthread_stacks[uthread_current->id];
308 else
309 s = &uthread_stacks[NUM_UTHREADS];
310
311 ret = &s->ret[--s->depth];
312
313 uthread_verify();
314
315 return (void *)*ret;
316 }