1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License as
4  * published by the Free Software Foundation; either version 2 of
5  * the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15  * MA 02111-1307 USA
16  */
17 
18 #include <common.h>
19 #include <exports.h>
20 
21 /*
22  * Author: Arun Dharankar <ADharankar@ATTBI.Com>
23  *
24  * A very simple thread/schedular model:
25  *   - only one master thread, and no parent child relation maintained
26  *   - parent thread cannot be stopped or deleted
27  *   - no permissions or credentials
28  *   - no elaborate safety checks
29  *   - cooperative multi threading
30  *   - Simple round-robin scheduleing with no priorities
31  *   - no metering/statistics collection
32  *
33  * Basic idea of implementing this is to allow more than one tests to
34  * execute "simultaneously".
35  *
36  * This may be modified such thread_yield may be called in syscalls, and
37  * timer interrupts.
38  */
39 
40 
41 #define MAX_THREADS 8
42 
43 #define CTX_SIZE 512
44 #define STK_SIZE 8*1024
45 
46 #define STATE_EMPTY 0
47 #define STATE_RUNNABLE 1
48 #define STATE_STOPPED 2
49 #define STATE_TERMINATED 2
50 
51 #define MASTER_THREAD 0
52 
53 #define RC_FAILURE	(-1)
54 #define	RC_SUCCESS	(0)
55 
56 typedef	vu_char *jmp_ctx;
57 unsigned long setctxsp (vu_char *sp);
58 int ppc_setjmp(jmp_ctx env);
59 void ppc_longjmp(jmp_ctx env, int val);
60 #define setjmp	ppc_setjmp
61 #define longjmp	ppc_longjmp
62 
63 struct lthread {
64 	int state;
65 	int retval;
66 	char stack[STK_SIZE];
67 	uchar context[CTX_SIZE];
68 	int (*func) (void *);
69 	void *arg;
70 };
71 static volatile struct lthread lthreads[MAX_THREADS];
72 static volatile int current_tid = MASTER_THREAD;
73 
74 
75 static uchar dbg = 0;
76 
77 #define PDEBUG(fmt, args...)	 {					\
78 	if(dbg != 0) {							\
79 		printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\
80 		printf(fmt, ##args);				\
81 		printf("\n");					\
82 	}								\
83 }
84 
85 static int testthread (void *);
86 static void sched_init (void);
87 static int thread_create (int (*func) (void *), void *arg);
88 static int thread_start (int id);
89 static void thread_yield (void);
90 static int thread_delete (int id);
91 static int thread_join (int *ret);
92 
93 #if 0							/* not used yet */
94 static int thread_stop (int id);
95 #endif							/* not used yet */
96 
97 /* An example of schedular test */
98 
99 #define NUMTHREADS 7
sched(int ac,char * av[])100 int sched (int ac, char *av[])
101 {
102 	int i, j;
103 	int tid[NUMTHREADS];
104 	int names[NUMTHREADS];
105 
106 	app_startup(av);
107 
108 	sched_init ();
109 
110 	for (i = 0; i < NUMTHREADS; i++) {
111 		names[i] = i;
112 		j = thread_create (testthread, (void *) &names[i]);
113 		if (j == RC_FAILURE)
114 			printf ("schedtest: Failed to create thread %d\n", i);
115 		if (j > 0) {
116 			printf ("schedtest: Created thread with id %d, name %d\n",
117 						j, i);
118 			tid[i] = j;
119 		}
120 	}
121 	printf ("schedtest: Threads created\n");
122 
123 	printf ("sched_test: function=0x%08x\n", (unsigned)testthread);
124 	for (i = 0; i < NUMTHREADS; i++) {
125 		printf ("schedtest: Setting thread %d runnable\n", tid[i]);
126 		thread_start (tid[i]);
127 		thread_yield ();
128 	}
129 	printf ("schedtest: Started %d threads\n", NUMTHREADS);
130 
131 	while (1) {
132 		printf ("schedtest: Waiting for threads to complete\n");
133 		if (tstc () && getc () == 0x3) {
134 			printf ("schedtest: Aborting threads...\n");
135 			for (i = 0; i < NUMTHREADS; i++) {
136 				printf ("schedtest: Deleting thread %d\n", tid[i]);
137 				thread_delete (tid[i]);
138 			}
139 			return RC_SUCCESS;
140 		}
141 		j = -1;
142 		i = thread_join (&j);
143 		if (i == RC_FAILURE) {
144 			printf ("schedtest: No threads pending, "
145 						"exiting schedular test\n");
146 			return RC_SUCCESS;
147 		}
148 		printf ("schedtest: thread is %d returned %d\n", i, j);
149 		thread_yield ();
150 	}
151 
152 	return RC_SUCCESS;
153 }
154 
testthread(void * name)155 static int testthread (void *name)
156 {
157 	int i;
158 
159 	printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n",
160 		*(int *) name, (unsigned)&i);
161 
162 	printf ("Thread %02d, i=%d\n", *(int *) name, i);
163 
164 	for (i = 0; i < 0xffff * (*(int *) name + 1); i++) {
165 		if (tstc () && getc () == 0x3) {
166 			printf ("testthread: myname %d terminating.\n",
167 						*(int *) name);
168 			return *(int *) name + 1;
169 		}
170 
171 		if (i % 100 == 0)
172 			thread_yield ();
173 	}
174 
175 	printf ("testthread: returning %d, i=0x%x\n",
176 				*(int *) name + 1, i);
177 
178 	return *(int *) name + 1;
179 }
180 
181 
sched_init(void)182 static void sched_init (void)
183 {
184 	int i;
185 
186 	for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++)
187 		lthreads[i].state = STATE_EMPTY;
188 
189 	current_tid = MASTER_THREAD;
190 	lthreads[current_tid].state = STATE_RUNNABLE;
191 	PDEBUG ("sched_init: master context = 0x%08x",
192 		(unsigned)lthreads[current_tid].context);
193 	return;
194 }
195 
thread_yield(void)196 static void thread_yield (void)
197 {
198 	static int i;
199 
200 	PDEBUG ("thread_yield: current tid=%d", current_tid);
201 
202 #define SWITCH(new)							\
203 	if(lthreads[new].state == STATE_RUNNABLE) {			\
204 		PDEBUG("thread_yield: %d match, ctx=0x%08x",		\
205 			new,						\
206 			(unsigned)lthreads[current_tid].context);	\
207 		if(setjmp(lthreads[current_tid].context) == 0) {	\
208 			current_tid = new;				\
209 			PDEBUG("thread_yield: tid %d returns 0",	\
210 				new);					\
211 			longjmp(lthreads[new].context, 1);		\
212 		} else {						\
213 			PDEBUG("thread_yield: tid %d returns 1",	\
214 				new);					\
215 			return;						\
216 		}							\
217 	}
218 
219 	for (i = current_tid + 1; i < MAX_THREADS; i++) {
220 		SWITCH (i);
221 	}
222 
223 	if (current_tid != 0) {
224 		for (i = 0; i <= current_tid; i++) {
225 			SWITCH (i);
226 		}
227 	}
228 
229 	PDEBUG ("thread_yield: returning from thread_yield");
230 	return;
231 }
232 
thread_create(int (* func)(void *),void * arg)233 static int thread_create (int (*func) (void *), void *arg)
234 {
235 	int i;
236 
237 	for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
238 		if (lthreads[i].state == STATE_EMPTY) {
239 			lthreads[i].state = STATE_STOPPED;
240 			lthreads[i].func = func;
241 			lthreads[i].arg = arg;
242 			PDEBUG ("thread_create: returns new tid %d", i);
243 			return i;
244 		}
245 	}
246 
247 	PDEBUG ("thread_create: returns failure");
248 	return RC_FAILURE;
249 }
250 
thread_delete(int id)251 static int thread_delete (int id)
252 {
253 	if (id <= MASTER_THREAD || id > MAX_THREADS)
254 		return RC_FAILURE;
255 
256 	if (current_tid == id)
257 		return RC_FAILURE;
258 
259 	lthreads[id].state = STATE_EMPTY;
260 	return RC_SUCCESS;
261 }
262 
thread_launcher(void)263 static void thread_launcher (void)
264 {
265 	PDEBUG ("thread_launcher: invoking func=0x%08x",
266 		   (unsigned)lthreads[current_tid].func);
267 
268 	lthreads[current_tid].retval =
269 			lthreads[current_tid].func (lthreads[current_tid].arg);
270 
271 	PDEBUG ("thread_launcher: tid %d terminated", current_tid);
272 
273 	lthreads[current_tid].state = STATE_TERMINATED;
274 	thread_yield ();
275 	printf ("thread_launcher: should NEVER get here!\n");
276 
277 	return;
278 }
279 
thread_start(int id)280 static int thread_start (int id)
281 {
282 	PDEBUG ("thread_start: id=%d", id);
283 	if (id <= MASTER_THREAD || id > MAX_THREADS) {
284 		return RC_FAILURE;
285 	}
286 
287 	if (lthreads[id].state != STATE_STOPPED)
288 		return RC_FAILURE;
289 
290 	if (setjmp (lthreads[current_tid].context) == 0) {
291 		lthreads[id].state = STATE_RUNNABLE;
292 		current_tid = id;
293 		PDEBUG ("thread_start: to be stack=0%08x",
294 			(unsigned)lthreads[id].stack);
295 		setctxsp ((vu_char *)&lthreads[id].stack[STK_SIZE]);
296 		thread_launcher ();
297 	}
298 
299 	PDEBUG ("thread_start: Thread id=%d started, parent returns", id);
300 
301 	return RC_SUCCESS;
302 }
303 
304 #if 0	/* not used so far */
305 static int thread_stop (int id)
306 {
307 	if (id <= MASTER_THREAD || id >= MAX_THREADS)
308 		return RC_FAILURE;
309 
310 	if (current_tid == id)
311 		return RC_FAILURE;
312 
313 	lthreads[id].state = STATE_STOPPED;
314 	return RC_SUCCESS;
315 }
316 #endif	/* not used so far */
317 
thread_join(int * ret)318 static int thread_join (int *ret)
319 {
320 	int i, j = 0;
321 
322 	PDEBUG ("thread_join: *ret = %d", *ret);
323 
324 	if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) {
325 		PDEBUG ("thread_join: invalid tid %d", *ret);
326 		return RC_FAILURE;
327 	}
328 
329 	if (*ret == -1) {
330 		PDEBUG ("Checking for tid = -1");
331 		while (1) {
332 			/* PDEBUG("thread_join: start while-loopn"); */
333 			j = 0;
334 			for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
335 				if (lthreads[i].state == STATE_TERMINATED) {
336 					*ret = lthreads[i].retval;
337 					lthreads[i].state = STATE_EMPTY;
338 					/* PDEBUG("thread_join: returning retval %d of tid %d",
339 					   ret, i); */
340 					return RC_SUCCESS;
341 				}
342 
343 				if (lthreads[i].state != STATE_EMPTY) {
344 					PDEBUG ("thread_join: %d used slots tid %d state=%d",
345 						   j, i, lthreads[i].state);
346 					j++;
347 				}
348 			}
349 			if (j == 0) {
350 				PDEBUG ("thread_join: all slots empty!");
351 				return RC_FAILURE;
352 			}
353 			/*  PDEBUG("thread_join: yielding"); */
354 			thread_yield ();
355 			/*  PDEBUG("thread_join: back from yield"); */
356 		}
357 	}
358 
359 	if (lthreads[*ret].state == STATE_TERMINATED) {
360 		i = *ret;
361 		*ret = lthreads[*ret].retval;
362 		lthreads[*ret].state = STATE_EMPTY;
363 		PDEBUG ("thread_join: returing %d for tid %d", *ret, i);
364 		return RC_SUCCESS;
365 	}
366 
367 	PDEBUG ("thread_join: thread %d is not terminated!", *ret);
368 	return RC_FAILURE;
369 }
370