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