11bc15386SPeter Tyser /*
2*1a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
31bc15386SPeter Tyser */
41bc15386SPeter Tyser
51bc15386SPeter Tyser #include <common.h>
61bc15386SPeter Tyser #include <exports.h>
71bc15386SPeter Tyser
81bc15386SPeter Tyser /*
91bc15386SPeter Tyser * Author: Arun Dharankar <ADharankar@ATTBI.Com>
101bc15386SPeter Tyser *
111bc15386SPeter Tyser * A very simple thread/schedular model:
121bc15386SPeter Tyser * - only one master thread, and no parent child relation maintained
131bc15386SPeter Tyser * - parent thread cannot be stopped or deleted
141bc15386SPeter Tyser * - no permissions or credentials
151bc15386SPeter Tyser * - no elaborate safety checks
161bc15386SPeter Tyser * - cooperative multi threading
171bc15386SPeter Tyser * - Simple round-robin scheduleing with no priorities
181bc15386SPeter Tyser * - no metering/statistics collection
191bc15386SPeter Tyser *
201bc15386SPeter Tyser * Basic idea of implementing this is to allow more than one tests to
211bc15386SPeter Tyser * execute "simultaneously".
221bc15386SPeter Tyser *
231bc15386SPeter Tyser * This may be modified such thread_yield may be called in syscalls, and
241bc15386SPeter Tyser * timer interrupts.
251bc15386SPeter Tyser */
261bc15386SPeter Tyser
271bc15386SPeter Tyser
281bc15386SPeter Tyser #define MAX_THREADS 8
291bc15386SPeter Tyser
301bc15386SPeter Tyser #define CTX_SIZE 512
311bc15386SPeter Tyser #define STK_SIZE 8*1024
321bc15386SPeter Tyser
331bc15386SPeter Tyser #define STATE_EMPTY 0
341bc15386SPeter Tyser #define STATE_RUNNABLE 1
351bc15386SPeter Tyser #define STATE_STOPPED 2
361bc15386SPeter Tyser #define STATE_TERMINATED 2
371bc15386SPeter Tyser
381bc15386SPeter Tyser #define MASTER_THREAD 0
391bc15386SPeter Tyser
401bc15386SPeter Tyser #define RC_FAILURE (-1)
411bc15386SPeter Tyser #define RC_SUCCESS (0)
421bc15386SPeter Tyser
431bc15386SPeter Tyser typedef vu_char *jmp_ctx;
441bc15386SPeter Tyser unsigned long setctxsp (vu_char *sp);
451bc15386SPeter Tyser int ppc_setjmp(jmp_ctx env);
461bc15386SPeter Tyser void ppc_longjmp(jmp_ctx env, int val);
471bc15386SPeter Tyser #define setjmp ppc_setjmp
481bc15386SPeter Tyser #define longjmp ppc_longjmp
491bc15386SPeter Tyser
501bc15386SPeter Tyser struct lthread {
511bc15386SPeter Tyser int state;
521bc15386SPeter Tyser int retval;
531bc15386SPeter Tyser char stack[STK_SIZE];
541bc15386SPeter Tyser uchar context[CTX_SIZE];
551bc15386SPeter Tyser int (*func) (void *);
561bc15386SPeter Tyser void *arg;
571bc15386SPeter Tyser };
581bc15386SPeter Tyser static volatile struct lthread lthreads[MAX_THREADS];
591bc15386SPeter Tyser static volatile int current_tid = MASTER_THREAD;
601bc15386SPeter Tyser
611bc15386SPeter Tyser
621bc15386SPeter Tyser static uchar dbg = 0;
631bc15386SPeter Tyser
641bc15386SPeter Tyser #define PDEBUG(fmt, args...) { \
651bc15386SPeter Tyser if(dbg != 0) { \
661bc15386SPeter Tyser printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\
671bc15386SPeter Tyser printf(fmt, ##args); \
681bc15386SPeter Tyser printf("\n"); \
691bc15386SPeter Tyser } \
701bc15386SPeter Tyser }
711bc15386SPeter Tyser
721bc15386SPeter Tyser static int testthread (void *);
731bc15386SPeter Tyser static void sched_init (void);
741bc15386SPeter Tyser static int thread_create (int (*func) (void *), void *arg);
751bc15386SPeter Tyser static int thread_start (int id);
761bc15386SPeter Tyser static void thread_yield (void);
771bc15386SPeter Tyser static int thread_delete (int id);
781bc15386SPeter Tyser static int thread_join (int *ret);
791bc15386SPeter Tyser
801bc15386SPeter Tyser #if 0 /* not used yet */
811bc15386SPeter Tyser static int thread_stop (int id);
821bc15386SPeter Tyser #endif /* not used yet */
831bc15386SPeter Tyser
841bc15386SPeter Tyser /* An example of schedular test */
851bc15386SPeter Tyser
861bc15386SPeter Tyser #define NUMTHREADS 7
sched(int ac,char * av[])871bc15386SPeter Tyser int sched (int ac, char *av[])
881bc15386SPeter Tyser {
891bc15386SPeter Tyser int i, j;
901bc15386SPeter Tyser int tid[NUMTHREADS];
911bc15386SPeter Tyser int names[NUMTHREADS];
921bc15386SPeter Tyser
931bc15386SPeter Tyser app_startup(av);
941bc15386SPeter Tyser
951bc15386SPeter Tyser sched_init ();
961bc15386SPeter Tyser
971bc15386SPeter Tyser for (i = 0; i < NUMTHREADS; i++) {
981bc15386SPeter Tyser names[i] = i;
991bc15386SPeter Tyser j = thread_create (testthread, (void *) &names[i]);
1001bc15386SPeter Tyser if (j == RC_FAILURE)
1011bc15386SPeter Tyser printf ("schedtest: Failed to create thread %d\n", i);
1021bc15386SPeter Tyser if (j > 0) {
1031bc15386SPeter Tyser printf ("schedtest: Created thread with id %d, name %d\n",
1041bc15386SPeter Tyser j, i);
1051bc15386SPeter Tyser tid[i] = j;
1061bc15386SPeter Tyser }
1071bc15386SPeter Tyser }
1081bc15386SPeter Tyser printf ("schedtest: Threads created\n");
1091bc15386SPeter Tyser
1101bc15386SPeter Tyser printf ("sched_test: function=0x%08x\n", (unsigned)testthread);
1111bc15386SPeter Tyser for (i = 0; i < NUMTHREADS; i++) {
1121bc15386SPeter Tyser printf ("schedtest: Setting thread %d runnable\n", tid[i]);
1131bc15386SPeter Tyser thread_start (tid[i]);
1141bc15386SPeter Tyser thread_yield ();
1151bc15386SPeter Tyser }
1161bc15386SPeter Tyser printf ("schedtest: Started %d threads\n", NUMTHREADS);
1171bc15386SPeter Tyser
1181bc15386SPeter Tyser while (1) {
1191bc15386SPeter Tyser printf ("schedtest: Waiting for threads to complete\n");
1201bc15386SPeter Tyser if (tstc () && getc () == 0x3) {
1211bc15386SPeter Tyser printf ("schedtest: Aborting threads...\n");
1221bc15386SPeter Tyser for (i = 0; i < NUMTHREADS; i++) {
1231bc15386SPeter Tyser printf ("schedtest: Deleting thread %d\n", tid[i]);
1241bc15386SPeter Tyser thread_delete (tid[i]);
1251bc15386SPeter Tyser }
1261bc15386SPeter Tyser return RC_SUCCESS;
1271bc15386SPeter Tyser }
1281bc15386SPeter Tyser j = -1;
1291bc15386SPeter Tyser i = thread_join (&j);
1301bc15386SPeter Tyser if (i == RC_FAILURE) {
1311bc15386SPeter Tyser printf ("schedtest: No threads pending, "
1321bc15386SPeter Tyser "exiting schedular test\n");
1331bc15386SPeter Tyser return RC_SUCCESS;
1341bc15386SPeter Tyser }
1351bc15386SPeter Tyser printf ("schedtest: thread is %d returned %d\n", i, j);
1361bc15386SPeter Tyser thread_yield ();
1371bc15386SPeter Tyser }
1381bc15386SPeter Tyser
1391bc15386SPeter Tyser return RC_SUCCESS;
1401bc15386SPeter Tyser }
1411bc15386SPeter Tyser
testthread(void * name)1421bc15386SPeter Tyser static int testthread (void *name)
1431bc15386SPeter Tyser {
1441bc15386SPeter Tyser int i;
1451bc15386SPeter Tyser
1461bc15386SPeter Tyser printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n",
1471bc15386SPeter Tyser *(int *) name, (unsigned)&i);
1481bc15386SPeter Tyser
1491bc15386SPeter Tyser printf ("Thread %02d, i=%d\n", *(int *) name, i);
1501bc15386SPeter Tyser
1511bc15386SPeter Tyser for (i = 0; i < 0xffff * (*(int *) name + 1); i++) {
1521bc15386SPeter Tyser if (tstc () && getc () == 0x3) {
1531bc15386SPeter Tyser printf ("testthread: myname %d terminating.\n",
1541bc15386SPeter Tyser *(int *) name);
1551bc15386SPeter Tyser return *(int *) name + 1;
1561bc15386SPeter Tyser }
1571bc15386SPeter Tyser
1581bc15386SPeter Tyser if (i % 100 == 0)
1591bc15386SPeter Tyser thread_yield ();
1601bc15386SPeter Tyser }
1611bc15386SPeter Tyser
1621bc15386SPeter Tyser printf ("testthread: returning %d, i=0x%x\n",
1631bc15386SPeter Tyser *(int *) name + 1, i);
1641bc15386SPeter Tyser
1651bc15386SPeter Tyser return *(int *) name + 1;
1661bc15386SPeter Tyser }
1671bc15386SPeter Tyser
1681bc15386SPeter Tyser
sched_init(void)1691bc15386SPeter Tyser static void sched_init (void)
1701bc15386SPeter Tyser {
1711bc15386SPeter Tyser int i;
1721bc15386SPeter Tyser
1731bc15386SPeter Tyser for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++)
1741bc15386SPeter Tyser lthreads[i].state = STATE_EMPTY;
1751bc15386SPeter Tyser
1761bc15386SPeter Tyser current_tid = MASTER_THREAD;
1771bc15386SPeter Tyser lthreads[current_tid].state = STATE_RUNNABLE;
1781bc15386SPeter Tyser PDEBUG ("sched_init: master context = 0x%08x",
1791bc15386SPeter Tyser (unsigned)lthreads[current_tid].context);
1801bc15386SPeter Tyser return;
1811bc15386SPeter Tyser }
1821bc15386SPeter Tyser
thread_yield(void)1831bc15386SPeter Tyser static void thread_yield (void)
1841bc15386SPeter Tyser {
1851bc15386SPeter Tyser static int i;
1861bc15386SPeter Tyser
1871bc15386SPeter Tyser PDEBUG ("thread_yield: current tid=%d", current_tid);
1881bc15386SPeter Tyser
1891bc15386SPeter Tyser #define SWITCH(new) \
1901bc15386SPeter Tyser if(lthreads[new].state == STATE_RUNNABLE) { \
1911bc15386SPeter Tyser PDEBUG("thread_yield: %d match, ctx=0x%08x", \
1921bc15386SPeter Tyser new, \
1931bc15386SPeter Tyser (unsigned)lthreads[current_tid].context); \
1941bc15386SPeter Tyser if(setjmp(lthreads[current_tid].context) == 0) { \
1951bc15386SPeter Tyser current_tid = new; \
1961bc15386SPeter Tyser PDEBUG("thread_yield: tid %d returns 0", \
1971bc15386SPeter Tyser new); \
1981bc15386SPeter Tyser longjmp(lthreads[new].context, 1); \
1991bc15386SPeter Tyser } else { \
2001bc15386SPeter Tyser PDEBUG("thread_yield: tid %d returns 1", \
2011bc15386SPeter Tyser new); \
2021bc15386SPeter Tyser return; \
2031bc15386SPeter Tyser } \
2041bc15386SPeter Tyser }
2051bc15386SPeter Tyser
2061bc15386SPeter Tyser for (i = current_tid + 1; i < MAX_THREADS; i++) {
2071bc15386SPeter Tyser SWITCH (i);
2081bc15386SPeter Tyser }
2091bc15386SPeter Tyser
2101bc15386SPeter Tyser if (current_tid != 0) {
2111bc15386SPeter Tyser for (i = 0; i <= current_tid; i++) {
2121bc15386SPeter Tyser SWITCH (i);
2131bc15386SPeter Tyser }
2141bc15386SPeter Tyser }
2151bc15386SPeter Tyser
2161bc15386SPeter Tyser PDEBUG ("thread_yield: returning from thread_yield");
2171bc15386SPeter Tyser return;
2181bc15386SPeter Tyser }
2191bc15386SPeter Tyser
thread_create(int (* func)(void *),void * arg)2201bc15386SPeter Tyser static int thread_create (int (*func) (void *), void *arg)
2211bc15386SPeter Tyser {
2221bc15386SPeter Tyser int i;
2231bc15386SPeter Tyser
2241bc15386SPeter Tyser for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
2251bc15386SPeter Tyser if (lthreads[i].state == STATE_EMPTY) {
2261bc15386SPeter Tyser lthreads[i].state = STATE_STOPPED;
2271bc15386SPeter Tyser lthreads[i].func = func;
2281bc15386SPeter Tyser lthreads[i].arg = arg;
2291bc15386SPeter Tyser PDEBUG ("thread_create: returns new tid %d", i);
2301bc15386SPeter Tyser return i;
2311bc15386SPeter Tyser }
2321bc15386SPeter Tyser }
2331bc15386SPeter Tyser
2341bc15386SPeter Tyser PDEBUG ("thread_create: returns failure");
2351bc15386SPeter Tyser return RC_FAILURE;
2361bc15386SPeter Tyser }
2371bc15386SPeter Tyser
thread_delete(int id)2381bc15386SPeter Tyser static int thread_delete (int id)
2391bc15386SPeter Tyser {
2401bc15386SPeter Tyser if (id <= MASTER_THREAD || id > MAX_THREADS)
2411bc15386SPeter Tyser return RC_FAILURE;
2421bc15386SPeter Tyser
2431bc15386SPeter Tyser if (current_tid == id)
2441bc15386SPeter Tyser return RC_FAILURE;
2451bc15386SPeter Tyser
2461bc15386SPeter Tyser lthreads[id].state = STATE_EMPTY;
2471bc15386SPeter Tyser return RC_SUCCESS;
2481bc15386SPeter Tyser }
2491bc15386SPeter Tyser
thread_launcher(void)2501bc15386SPeter Tyser static void thread_launcher (void)
2511bc15386SPeter Tyser {
2521bc15386SPeter Tyser PDEBUG ("thread_launcher: invoking func=0x%08x",
2531bc15386SPeter Tyser (unsigned)lthreads[current_tid].func);
2541bc15386SPeter Tyser
2551bc15386SPeter Tyser lthreads[current_tid].retval =
2561bc15386SPeter Tyser lthreads[current_tid].func (lthreads[current_tid].arg);
2571bc15386SPeter Tyser
2581bc15386SPeter Tyser PDEBUG ("thread_launcher: tid %d terminated", current_tid);
2591bc15386SPeter Tyser
2601bc15386SPeter Tyser lthreads[current_tid].state = STATE_TERMINATED;
2611bc15386SPeter Tyser thread_yield ();
2621bc15386SPeter Tyser printf ("thread_launcher: should NEVER get here!\n");
2631bc15386SPeter Tyser
2641bc15386SPeter Tyser return;
2651bc15386SPeter Tyser }
2661bc15386SPeter Tyser
thread_start(int id)2671bc15386SPeter Tyser static int thread_start (int id)
2681bc15386SPeter Tyser {
2691bc15386SPeter Tyser PDEBUG ("thread_start: id=%d", id);
2701bc15386SPeter Tyser if (id <= MASTER_THREAD || id > MAX_THREADS) {
2711bc15386SPeter Tyser return RC_FAILURE;
2721bc15386SPeter Tyser }
2731bc15386SPeter Tyser
2741bc15386SPeter Tyser if (lthreads[id].state != STATE_STOPPED)
2751bc15386SPeter Tyser return RC_FAILURE;
2761bc15386SPeter Tyser
2771bc15386SPeter Tyser if (setjmp (lthreads[current_tid].context) == 0) {
2781bc15386SPeter Tyser lthreads[id].state = STATE_RUNNABLE;
2791bc15386SPeter Tyser current_tid = id;
2801bc15386SPeter Tyser PDEBUG ("thread_start: to be stack=0%08x",
2811bc15386SPeter Tyser (unsigned)lthreads[id].stack);
2821bc15386SPeter Tyser setctxsp ((vu_char *)<hreads[id].stack[STK_SIZE]);
2831bc15386SPeter Tyser thread_launcher ();
2841bc15386SPeter Tyser }
2851bc15386SPeter Tyser
2861bc15386SPeter Tyser PDEBUG ("thread_start: Thread id=%d started, parent returns", id);
2871bc15386SPeter Tyser
2881bc15386SPeter Tyser return RC_SUCCESS;
2891bc15386SPeter Tyser }
2901bc15386SPeter Tyser
2911bc15386SPeter Tyser #if 0 /* not used so far */
2921bc15386SPeter Tyser static int thread_stop (int id)
2931bc15386SPeter Tyser {
2941bc15386SPeter Tyser if (id <= MASTER_THREAD || id >= MAX_THREADS)
2951bc15386SPeter Tyser return RC_FAILURE;
2961bc15386SPeter Tyser
2971bc15386SPeter Tyser if (current_tid == id)
2981bc15386SPeter Tyser return RC_FAILURE;
2991bc15386SPeter Tyser
3001bc15386SPeter Tyser lthreads[id].state = STATE_STOPPED;
3011bc15386SPeter Tyser return RC_SUCCESS;
3021bc15386SPeter Tyser }
3031bc15386SPeter Tyser #endif /* not used so far */
3041bc15386SPeter Tyser
thread_join(int * ret)3051bc15386SPeter Tyser static int thread_join (int *ret)
3061bc15386SPeter Tyser {
3071bc15386SPeter Tyser int i, j = 0;
3081bc15386SPeter Tyser
3091bc15386SPeter Tyser PDEBUG ("thread_join: *ret = %d", *ret);
3101bc15386SPeter Tyser
3111bc15386SPeter Tyser if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) {
3121bc15386SPeter Tyser PDEBUG ("thread_join: invalid tid %d", *ret);
3131bc15386SPeter Tyser return RC_FAILURE;
3141bc15386SPeter Tyser }
3151bc15386SPeter Tyser
3161bc15386SPeter Tyser if (*ret == -1) {
3171bc15386SPeter Tyser PDEBUG ("Checking for tid = -1");
3181bc15386SPeter Tyser while (1) {
3191bc15386SPeter Tyser /* PDEBUG("thread_join: start while-loopn"); */
3201bc15386SPeter Tyser j = 0;
3211bc15386SPeter Tyser for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
3221bc15386SPeter Tyser if (lthreads[i].state == STATE_TERMINATED) {
3231bc15386SPeter Tyser *ret = lthreads[i].retval;
3241bc15386SPeter Tyser lthreads[i].state = STATE_EMPTY;
3251bc15386SPeter Tyser /* PDEBUG("thread_join: returning retval %d of tid %d",
3261bc15386SPeter Tyser ret, i); */
3271bc15386SPeter Tyser return RC_SUCCESS;
3281bc15386SPeter Tyser }
3291bc15386SPeter Tyser
3301bc15386SPeter Tyser if (lthreads[i].state != STATE_EMPTY) {
3311bc15386SPeter Tyser PDEBUG ("thread_join: %d used slots tid %d state=%d",
3321bc15386SPeter Tyser j, i, lthreads[i].state);
3331bc15386SPeter Tyser j++;
3341bc15386SPeter Tyser }
3351bc15386SPeter Tyser }
3361bc15386SPeter Tyser if (j == 0) {
3371bc15386SPeter Tyser PDEBUG ("thread_join: all slots empty!");
3381bc15386SPeter Tyser return RC_FAILURE;
3391bc15386SPeter Tyser }
3401bc15386SPeter Tyser /* PDEBUG("thread_join: yielding"); */
3411bc15386SPeter Tyser thread_yield ();
3421bc15386SPeter Tyser /* PDEBUG("thread_join: back from yield"); */
3431bc15386SPeter Tyser }
3441bc15386SPeter Tyser }
3451bc15386SPeter Tyser
3461bc15386SPeter Tyser if (lthreads[*ret].state == STATE_TERMINATED) {
3471bc15386SPeter Tyser i = *ret;
3481bc15386SPeter Tyser *ret = lthreads[*ret].retval;
3491bc15386SPeter Tyser lthreads[*ret].state = STATE_EMPTY;
3501bc15386SPeter Tyser PDEBUG ("thread_join: returing %d for tid %d", *ret, i);
3511bc15386SPeter Tyser return RC_SUCCESS;
3521bc15386SPeter Tyser }
3531bc15386SPeter Tyser
3541bc15386SPeter Tyser PDEBUG ("thread_join: thread %d is not terminated!", *ret);
3551bc15386SPeter Tyser return RC_FAILURE;
3561bc15386SPeter Tyser }
357