mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-23 18:38:51 +09:00
![Tom Rini](/assets/img/avatar_default.png)
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
355 lines
8.0 KiB
C
355 lines
8.0 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
#include <common.h>
|
|
#include <exports.h>
|
|
|
|
/*
|
|
* Author: Arun Dharankar <ADharankar@ATTBI.Com>
|
|
*
|
|
* A very simple thread/schedular model:
|
|
* - only one master thread, and no parent child relation maintained
|
|
* - parent thread cannot be stopped or deleted
|
|
* - no permissions or credentials
|
|
* - no elaborate safety checks
|
|
* - cooperative multi threading
|
|
* - Simple round-robin scheduleing with no priorities
|
|
* - no metering/statistics collection
|
|
*
|
|
* Basic idea of implementing this is to allow more than one tests to
|
|
* execute "simultaneously".
|
|
*
|
|
* This may be modified such thread_yield may be called in syscalls, and
|
|
* timer interrupts.
|
|
*/
|
|
|
|
|
|
#define MAX_THREADS 8
|
|
|
|
#define CTX_SIZE 512
|
|
#define STK_SIZE 8*1024
|
|
|
|
#define STATE_EMPTY 0
|
|
#define STATE_RUNNABLE 1
|
|
#define STATE_STOPPED 2
|
|
#define STATE_TERMINATED 2
|
|
|
|
#define MASTER_THREAD 0
|
|
|
|
#define RC_FAILURE (-1)
|
|
#define RC_SUCCESS (0)
|
|
|
|
typedef vu_char *jmp_ctx;
|
|
unsigned long setctxsp (vu_char *sp);
|
|
int ppc_setjmp(jmp_ctx env);
|
|
void ppc_longjmp(jmp_ctx env, int val);
|
|
#define setjmp ppc_setjmp
|
|
#define longjmp ppc_longjmp
|
|
|
|
struct lthread {
|
|
int state;
|
|
int retval;
|
|
char stack[STK_SIZE];
|
|
uchar context[CTX_SIZE];
|
|
int (*func) (void *);
|
|
void *arg;
|
|
};
|
|
static volatile struct lthread lthreads[MAX_THREADS];
|
|
static volatile int current_tid = MASTER_THREAD;
|
|
|
|
|
|
static uchar dbg = 0;
|
|
|
|
#define PDEBUG(fmt, args...) { \
|
|
if(dbg != 0) { \
|
|
printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\
|
|
printf(fmt, ##args); \
|
|
printf("\n"); \
|
|
} \
|
|
}
|
|
|
|
static int testthread (void *);
|
|
static void sched_init (void);
|
|
static int thread_create (int (*func) (void *), void *arg);
|
|
static int thread_start (int id);
|
|
static void thread_yield (void);
|
|
static int thread_delete (int id);
|
|
static int thread_join (int *ret);
|
|
|
|
#if 0 /* not used yet */
|
|
static int thread_stop (int id);
|
|
#endif /* not used yet */
|
|
|
|
/* An example of schedular test */
|
|
|
|
#define NUMTHREADS 7
|
|
int sched (int ac, char *av[])
|
|
{
|
|
int i, j;
|
|
int tid[NUMTHREADS];
|
|
int names[NUMTHREADS];
|
|
|
|
app_startup(av);
|
|
|
|
sched_init ();
|
|
|
|
for (i = 0; i < NUMTHREADS; i++) {
|
|
names[i] = i;
|
|
j = thread_create (testthread, (void *) &names[i]);
|
|
if (j == RC_FAILURE)
|
|
printf ("schedtest: Failed to create thread %d\n", i);
|
|
if (j > 0) {
|
|
printf ("schedtest: Created thread with id %d, name %d\n",
|
|
j, i);
|
|
tid[i] = j;
|
|
}
|
|
}
|
|
printf ("schedtest: Threads created\n");
|
|
|
|
printf ("sched_test: function=0x%08x\n", (unsigned)testthread);
|
|
for (i = 0; i < NUMTHREADS; i++) {
|
|
printf ("schedtest: Setting thread %d runnable\n", tid[i]);
|
|
thread_start (tid[i]);
|
|
thread_yield ();
|
|
}
|
|
printf ("schedtest: Started %d threads\n", NUMTHREADS);
|
|
|
|
while (1) {
|
|
printf ("schedtest: Waiting for threads to complete\n");
|
|
if (tstc () && getc () == 0x3) {
|
|
printf ("schedtest: Aborting threads...\n");
|
|
for (i = 0; i < NUMTHREADS; i++) {
|
|
printf ("schedtest: Deleting thread %d\n", tid[i]);
|
|
thread_delete (tid[i]);
|
|
}
|
|
return RC_SUCCESS;
|
|
}
|
|
j = -1;
|
|
i = thread_join (&j);
|
|
if (i == RC_FAILURE) {
|
|
printf ("schedtest: No threads pending, "
|
|
"exiting schedular test\n");
|
|
return RC_SUCCESS;
|
|
}
|
|
printf ("schedtest: thread is %d returned %d\n", i, j);
|
|
thread_yield ();
|
|
}
|
|
|
|
return RC_SUCCESS;
|
|
}
|
|
|
|
static int testthread (void *name)
|
|
{
|
|
int i;
|
|
|
|
printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n",
|
|
*(int *) name, (unsigned)&i);
|
|
|
|
printf ("Thread %02d, i=%d\n", *(int *) name, i);
|
|
|
|
for (i = 0; i < 0xffff * (*(int *) name + 1); i++) {
|
|
if (tstc () && getc () == 0x3) {
|
|
printf ("testthread: myname %d terminating.\n",
|
|
*(int *) name);
|
|
return *(int *) name + 1;
|
|
}
|
|
|
|
if (i % 100 == 0)
|
|
thread_yield ();
|
|
}
|
|
|
|
printf ("testthread: returning %d, i=0x%x\n",
|
|
*(int *) name + 1, i);
|
|
|
|
return *(int *) name + 1;
|
|
}
|
|
|
|
|
|
static void sched_init (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++)
|
|
lthreads[i].state = STATE_EMPTY;
|
|
|
|
current_tid = MASTER_THREAD;
|
|
lthreads[current_tid].state = STATE_RUNNABLE;
|
|
PDEBUG ("sched_init: master context = 0x%08x",
|
|
(unsigned)lthreads[current_tid].context);
|
|
return;
|
|
}
|
|
|
|
static void thread_yield (void)
|
|
{
|
|
static int i;
|
|
|
|
PDEBUG ("thread_yield: current tid=%d", current_tid);
|
|
|
|
#define SWITCH(new) \
|
|
if(lthreads[new].state == STATE_RUNNABLE) { \
|
|
PDEBUG("thread_yield: %d match, ctx=0x%08x", \
|
|
new, \
|
|
(unsigned)lthreads[current_tid].context); \
|
|
if(setjmp(lthreads[current_tid].context) == 0) { \
|
|
current_tid = new; \
|
|
PDEBUG("thread_yield: tid %d returns 0", \
|
|
new); \
|
|
longjmp(lthreads[new].context, 1); \
|
|
} else { \
|
|
PDEBUG("thread_yield: tid %d returns 1", \
|
|
new); \
|
|
return; \
|
|
} \
|
|
}
|
|
|
|
for (i = current_tid + 1; i < MAX_THREADS; i++) {
|
|
SWITCH (i);
|
|
}
|
|
|
|
if (current_tid != 0) {
|
|
for (i = 0; i <= current_tid; i++) {
|
|
SWITCH (i);
|
|
}
|
|
}
|
|
|
|
PDEBUG ("thread_yield: returning from thread_yield");
|
|
return;
|
|
}
|
|
|
|
static int thread_create (int (*func) (void *), void *arg)
|
|
{
|
|
int i;
|
|
|
|
for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
|
|
if (lthreads[i].state == STATE_EMPTY) {
|
|
lthreads[i].state = STATE_STOPPED;
|
|
lthreads[i].func = func;
|
|
lthreads[i].arg = arg;
|
|
PDEBUG ("thread_create: returns new tid %d", i);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
PDEBUG ("thread_create: returns failure");
|
|
return RC_FAILURE;
|
|
}
|
|
|
|
static int thread_delete (int id)
|
|
{
|
|
if (id <= MASTER_THREAD || id > MAX_THREADS)
|
|
return RC_FAILURE;
|
|
|
|
if (current_tid == id)
|
|
return RC_FAILURE;
|
|
|
|
lthreads[id].state = STATE_EMPTY;
|
|
return RC_SUCCESS;
|
|
}
|
|
|
|
static void thread_launcher (void)
|
|
{
|
|
PDEBUG ("thread_launcher: invoking func=0x%08x",
|
|
(unsigned)lthreads[current_tid].func);
|
|
|
|
lthreads[current_tid].retval =
|
|
lthreads[current_tid].func (lthreads[current_tid].arg);
|
|
|
|
PDEBUG ("thread_launcher: tid %d terminated", current_tid);
|
|
|
|
lthreads[current_tid].state = STATE_TERMINATED;
|
|
thread_yield ();
|
|
printf ("thread_launcher: should NEVER get here!\n");
|
|
|
|
return;
|
|
}
|
|
|
|
static int thread_start (int id)
|
|
{
|
|
PDEBUG ("thread_start: id=%d", id);
|
|
if (id <= MASTER_THREAD || id > MAX_THREADS) {
|
|
return RC_FAILURE;
|
|
}
|
|
|
|
if (lthreads[id].state != STATE_STOPPED)
|
|
return RC_FAILURE;
|
|
|
|
if (setjmp (lthreads[current_tid].context) == 0) {
|
|
lthreads[id].state = STATE_RUNNABLE;
|
|
current_tid = id;
|
|
PDEBUG ("thread_start: to be stack=0%08x",
|
|
(unsigned)lthreads[id].stack);
|
|
setctxsp ((vu_char *)<hreads[id].stack[STK_SIZE]);
|
|
thread_launcher ();
|
|
}
|
|
|
|
PDEBUG ("thread_start: Thread id=%d started, parent returns", id);
|
|
|
|
return RC_SUCCESS;
|
|
}
|
|
|
|
#if 0 /* not used so far */
|
|
static int thread_stop (int id)
|
|
{
|
|
if (id <= MASTER_THREAD || id >= MAX_THREADS)
|
|
return RC_FAILURE;
|
|
|
|
if (current_tid == id)
|
|
return RC_FAILURE;
|
|
|
|
lthreads[id].state = STATE_STOPPED;
|
|
return RC_SUCCESS;
|
|
}
|
|
#endif /* not used so far */
|
|
|
|
static int thread_join (int *ret)
|
|
{
|
|
int i, j = 0;
|
|
|
|
PDEBUG ("thread_join: *ret = %d", *ret);
|
|
|
|
if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) {
|
|
PDEBUG ("thread_join: invalid tid %d", *ret);
|
|
return RC_FAILURE;
|
|
}
|
|
|
|
if (*ret == -1) {
|
|
PDEBUG ("Checking for tid = -1");
|
|
while (1) {
|
|
/* PDEBUG("thread_join: start while-loopn"); */
|
|
j = 0;
|
|
for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
|
|
if (lthreads[i].state == STATE_TERMINATED) {
|
|
*ret = lthreads[i].retval;
|
|
lthreads[i].state = STATE_EMPTY;
|
|
/* PDEBUG("thread_join: returning retval %d of tid %d",
|
|
ret, i); */
|
|
return RC_SUCCESS;
|
|
}
|
|
|
|
if (lthreads[i].state != STATE_EMPTY) {
|
|
PDEBUG ("thread_join: %d used slots tid %d state=%d",
|
|
j, i, lthreads[i].state);
|
|
j++;
|
|
}
|
|
}
|
|
if (j == 0) {
|
|
PDEBUG ("thread_join: all slots empty!");
|
|
return RC_FAILURE;
|
|
}
|
|
/* PDEBUG("thread_join: yielding"); */
|
|
thread_yield ();
|
|
/* PDEBUG("thread_join: back from yield"); */
|
|
}
|
|
}
|
|
|
|
if (lthreads[*ret].state == STATE_TERMINATED) {
|
|
i = *ret;
|
|
*ret = lthreads[*ret].retval;
|
|
lthreads[*ret].state = STATE_EMPTY;
|
|
PDEBUG ("thread_join: returing %d for tid %d", *ret, i);
|
|
return RC_SUCCESS;
|
|
}
|
|
|
|
PDEBUG ("thread_join: thread %d is not terminated!", *ret);
|
|
return RC_FAILURE;
|
|
}
|