diff --git a/tools/KSE/ksetest/Makefile b/tools/KSE/ksetest/Makefile index f7bb415535cd..5c0a7430b055 100644 --- a/tools/KSE/ksetest/Makefile +++ b/tools/KSE/ksetest/Makefile @@ -1,20 +1,7 @@ # $FreeBSD$ -# @(#)Makefile 8.1 (Berkeley) 6/2/93 - PROG= ksetest -SRCS= kse_threads_test.c kse_asm.S -# -# To support "lazy" ps for non root/wheel users -# add -DLAZY_PS to the cflags. This helps -# keep ps from being an unnecessary load -# on large systems. -# -CFLAGS=-static -g -O -#WARNS= 0 -#DPADD= ${LIBM} ${LIBKVM} -#LDADD= -lm -lkvm -#BINGRP= kmem -#BINMODE=2555 -NOMAN=yes +NOMAN= +CFLAGS+= -g +SRCS= kse_asm.S kse_threads_test.c .include diff --git a/tools/KSE/ksetest/kse_asm.S b/tools/KSE/ksetest/kse_asm.S index a12358c62c81..ae6ac7e938e9 100644 --- a/tools/KSE/ksetest/kse_asm.S +++ b/tools/KSE/ksetest/kse_asm.S @@ -1,88 +1,149 @@ -/*- +/* + * Copyright (c) 2002 Jonathan Mini . + * Copyright (c) 2001 Daniel Eischen . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * * $FreeBSD$ */ -#include - -/*****************************************************************************/ -/* Scheduling */ -/*****************************************************************************/ - - .data - - .text - -#define TF_GS -0x04 /* don't laugh! */ -#define TF_FS 0x00 -#define TF_ES 0x04 -#define TF_DS 0x08 -#define TF_EDI 0x0c -#define TF_ESI 0x10 -#define TF_EBP 0x14 -#define TF_ISP 0x18 -#define TF_EBX 0x1c -#define TF_EDX 0x20 -#define TF_ECX 0x24 -#define TF_EAX 0x28 -#define TF_TRAPNO 0x2c -#define TF_ERR 0x30 -#define TF_EIP 0x34 -#define TF_CS 0x38 -#define TF_EFLAGS 0x3c -#define TF_ESP 0x40 -#define TF_SS 0x44 +#include +__FBSDID("$FreeBSD$"); /* - * savethread + * Where do we define these? */ -GEN_ENTRY(savethread) +#define MC_SIZE 640 /* sizeof mcontext_t */ +#define UC_MC_OFFSET 16 /* offset to mcontext from ucontext */ +#define UC_MC_LEN_OFFSET 96 /* offset to mc_len from mcontext */ +#define MC_LEN_OFFSET 80 /* offset to mc_len from mcontext */ +#define MC_FP_REGS_OFFSET 96 /* offset to FP regs from mcontext */ +#define MC_FP_CW_OFFSET 96 /* offset to FP control word */ +#define MC_OWNEDFP_OFFSET 88 /* offset to mc_ownedfp from mcontext */ +#define KM_STACK_SP_OFFSET 32 /* offset to km_stack.ss_sp */ +#define KM_STACK_SIZE_OFFSET 36 /* offset to km_stack.ss_sp */ +#define KM_FUNC_OFFSET 28 /* offset to km_func */ + +/* + * int uts_to_thread(thread_mailbox *tdp, thread_mailbox **curthreadp); + * + * Does not return on success, returns -1 otherwise. + */ +ENTRY(uts_to_thread) + movl 4(%esp), %edx /* get address of thread_mailbox */ + /* .. ucontext_t is at offset 0 */ + cmpl $0, %edx /* check for null pointer */ + jne 1f + movl $-1, %eax + jmp 5f +1: cmpl $MC_SIZE, UC_MC_LEN_OFFSET(%edx) /* is context valid? */ + je 2f + movl $-1, %eax /* bzzzt, invalid context */ + jmp 5f +2: movl 8(%esp), %eax /* get address of curthreadp */ + movl %edx, (%eax) /* we're now the current thread */ + /* + * From here on, we don't touch the old stack. + */ + addl $UC_MC_OFFSET, %edx /* add offset to mcontext */ + movl 4(%edx), %gs + movl 8(%edx), %fs + movl 12(%edx), %es + movl 16(%edx), %ds + movl 76(%edx), %ss + movl 20(%edx), %edi + movl 24(%edx), %esi + movl 28(%edx), %ebp + movl 72(%edx), %esp /* switch to context defined stack */ + subl $4, %esp /* leave space for the return address */ + movl 60(%edx), %eax /* put return address at top of stack */ + movl %eax, (%esp) + cmpl $0, MC_OWNEDFP_OFFSET(%edx) /* are FP regs valid? */ + jz 3f + frstor MC_FP_REGS_OFFSET(%edx) /* restore FP regs */ + jmp 4f +3: fninit + fldcw MC_FP_CW_OFFSET(%edx) +4: movl 48(%edx), %eax /* restore ax, bx, cx */ + movl 36(%edx), %ebx + movl 44(%edx), %ecx + pushl 68(%edx) /* flags on stack */ + pushl 40(%edx) /* %edx on stack */ + popl %edx /* %edx off stack */ + popf /* flags off stack */ +5: ret /* %eip off stack */ - /* Switch to new thread. First, save context as needed. */ - pushl %edx - movl 8(%esp), %edx /* get context area */ - - movl %eax,TF_EAX(%edx) - movl %ebx,TF_EBX(%edx) - movl %ecx,TF_ECX(%edx) - popl %eax /* get dx off the stack again */ - movl %eax,TF_EDX(%edx) - movl (%esp),%eax /* get the return address */ - movl %eax,TF_EIP(%edx) - movl %esp,TF_ESP(%edx) - movl %esp,TF_ISP(%edx) /* XXX */ - movl %ebp,TF_EBP(%edx) - movl %esi,TF_ESI(%edx) - movl %edi,TF_EDI(%edx) - movl %cs,TF_CS(%edx) - movl %ds,TF_DS(%edx) - movl %es,TF_ES(%edx) - movl %fs,TF_FS(%edx) - movl %gs,TF_GS(%edx) - ret +/* + * int thread_to_uts(thread_mailbox *tm, kse_mailbox *km); + * + * Does not return on success, returns -1 otherwise. + */ +ENTRY(thread_to_uts) + movl 4(%esp), %eax /* get address of context */ + cmpl $0, %eax /* check for null pointer */ + jne 1f + movl $-1, %eax + jmp 2f +1: pushl %edx /* save value of edx */ + movl %eax, %edx /* get address of context */ + addl $UC_MC_OFFSET, %edx /* add offset to mcontext */ + movl %gs, 4(%edx) + movl %fs, 8(%edx) + movl %es, 12(%edx) + movl %ds, 16(%edx) + movl %edi, 20(%edx) + movl %esi, 24(%edx) + movl %ebp, 28(%edx) + movl %ebx, 36(%edx) + movl $0, 48(%edx) /* store successful return in eax */ + popl %eax /* get saved value of edx */ + movl %eax, 40(%edx) /* save edx */ + movl %ecx, 44(%edx) + movl (%esp), %eax /* get return address */ + movl %eax, 60(%edx) /* save return address */ + movl %ss, 76(%edx) + /* + * Don't save floating point registers here. + * + * This is an explicit call to get the current context, so + * the caller is done with the floating point registers. + * Contexts formed by involuntary switches, such as signal delivery, + * have floating point registers saved by the kernel. + */ + fnstcw MC_FP_CW_OFFSET(%edx) + movl $0, MC_OWNEDFP_OFFSET(%edx) /* no FP */ + lahf /* get eflags */ + movl %eax, 68(%edx) /* store eflags */ + movl %esp, %eax /* setcontext pushes the return */ + addl $4, %eax /* address onto the top of the */ + movl %eax, 72(%edx) /* stack; account for this */ + movl $MC_SIZE, MC_LEN_OFFSET(%edx) /* context is now valid */ + movl 8(%esp), %edx /* get address of mailbox */ + movl KM_STACK_SP_OFFSET(%edx), %eax /* get bottom of stack */ + addl KM_STACK_SIZE_OFFSET(%edx), %eax /* add length */ + movl %eax, %esp /* switch to the uts's stack */ + pushl %edx /* push the address of the mailbox */ + pushl KM_FUNC_OFFSET(%edx) /* .. the uts can return to itself */ + pushl KM_FUNC_OFFSET(%edx) /* push the address of the uts func */ +2: ret - -GEN_ENTRY(loadthread) - mov 4(%esp), %edx /* get context area */ - -/* movl TF_ISP(%edx), %esp */ /* select which is correct */ - movl TF_ESP(%edx), %esp /* get the new stack online */ - movl TF_EBP(%edx), %ebp - movl TF_EIP(%edx),%eax - push %eax /* return adddress */ -#if 0 - movl TF_CS(%edx), %cs - movl TF_DS(%edx), %ds - movl TF_ES(%edx), %es - movl TF_FS(%edx), %fs - movl TF_GS(%edx), %gs -#endif - movl TF_ESI(%edx), %esi - movl TF_EDI(%edx), %edi - - movl TF_EDX(%edx), %eax - pushl %eax - movl TF_ECX(%edx), %ecx - movl TF_EBX(%edx), %ebx - movl TF_EAX(%edx), %eax - popl %edx - ret diff --git a/tools/KSE/ksetest/kse_threads_test.c b/tools/KSE/ksetest/kse_threads_test.c index fb4f22ee08c1..09f8786484ff 100644 --- a/tools/KSE/ksetest/kse_threads_test.c +++ b/tools/KSE/ksetest/kse_threads_test.c @@ -1,275 +1,390 @@ -/* +/*- + * Copyright (c) 2002 Jonathan Mini (mini@freebsd.org). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * * $FreeBSD$ */ -#define _KERNEL -#include -#include -#include +#include +#include +#include +#include #include -#include +#include -/*#define DEBUG*/ -/************************************************************* - * These should probably be in a .h file - **************************************************************/ -typedef void thread_fn(void *arg); +#include +#include +#include +#include +#include +#include +#include -struct user_thread { - struct thread_mailbox mbox; - char *stack; - int stack_size; - TAILQ_ENTRY(user_thread) runq_next; -}; +#undef TRACE_UTS -struct per_kse { - struct kse_mailbox mbox; - struct user_thread *curthread; -}; -/************************************************************* - * Debug stuff - **************************************************************/ -#ifdef DEBUG - -jmp_buf jb3; -#define DUMPREGS(desc) do {_setjmp(jb3); printjb(jb3, desc); } while (0) - -char *regname[] = {"%eip","%ebx","%esp","%ebp", - "%esi","%edi","fpcr","MSK0", - "MSK1","MSK2","MSK3"}; - - -static -printjb(struct _jmp_buf *jb, char *desc) -{ - - int i; - printf("jb (%s) is at 0x%x\n", desc, jb); - for( i = 0; i< _JBLEN-4; i++) { - printf("jb[%d] (%s) = 0x%x\n", i, regname[i], jb[0]._jb[i]); - } -} +#ifdef TRACE_UTS +#define UPFMT(fmt...) pfmt(#fmt) +#define UPSTR(s) pstr(s) +#define UPCHAR(c) pchar(c) #else -#define DUMPREGS(desc) do {} while (0) +#define UPFMT(fmt...) /* Nothing. */ +#define UPSTR(s) /* Nothing. */ +#define UPCHAR(c) /* Nothing. */ #endif -/************************************************************* - * Globals - **************************************************************/ -struct per_kse first_kse; /* for NOW cheat and make it global */ -TAILQ_HEAD(, user_thread) runqueue = TAILQ_HEAD_INITIALIZER(runqueue); -/************************************************************* - * Implementation parameters - **************************************************************/ -#define T_STACKSIZE (16*4096) /* thread stacksize */ -#define K_STACKSIZE (1*4096) /* KSE (UTS) stacksize */ +#define MAIN_STACK_SIZE (1024 * 1024) +#define THREAD_STACK_SIZE (32 * 1024) + +static struct kse_mailbox uts_mb; +static struct thread_mailbox *run_queue; +static struct thread_mailbox *aa; + +static int progress = 0; + +static void init_uts(void); +static void enter_uts(void); +static void pchar(char c); +static void pfmt(const char *fmt, ...); +static void pstr(const char *s); +static void runq_insert(struct thread_mailbox *tm); +static struct thread_mailbox *runq_remove(void); +static void thread_start(const void *func, int arg); +static void uts(struct kse_mailbox *km); + +extern int uts_to_thread(struct thread_mailbox *tdp, struct thread_mailbox **curthreadp); -/************************************************************* - * UTS funcions. - * Simple round_robin for now. - **************************************************************/ static void -runq_insert(struct user_thread *thread) +nano(int len) { - TAILQ_INSERT_TAIL(&runqueue, thread, runq_next); + struct timespec time_to_sleep; + struct timespec time_remaining; + + time_to_sleep.tv_sec = 0; + time_to_sleep.tv_nsec = len * 10000; + nanosleep(&time_to_sleep, &time_remaining); } -static struct user_thread * -select_thread(void) -{ - struct user_thread *thread; - - if ((thread = TAILQ_FIRST(&runqueue))) { - TAILQ_REMOVE(&runqueue, thread, runq_next); - } - return (thread); -} - -/************************************************************* - * The UTS upcall entrypoint - * Called once on startup (and left by longjump) - * and there-after, returned to by the upcall many times. - **************************************************************/ void -UTS(struct kse_mailbox *ke_mbox) +aaaa(int c) { - struct user_thread *thread; - struct thread_mailbox *completed; - struct per_kse *ksedata; - int done = 0; + for (;;) { + pchar(c); + nano(1); + } +} - /**********************************/ - /* UTS upcall starts running here. */ - /**********************************/ - /**********************************/ +static void +foof(int sig) +{ + pfmt("\n[%d]\n", sig); + thread_start(aaaa, '0' + progress++); +} - ksedata = ke_mbox->kmbx_UTS_handle; - /* If there are returned syscall threads, put them on the run queue */ - if ((completed = ke_mbox->kmbx_completed_threads)) { - ke_mbox->kmbx_completed_threads = NULL; - while (completed) { - thread = completed->UTS_handle; - completed = completed->next_completed; - runq_insert(thread); +void +spin(int arg) +{ + for (;;) enter_uts(); sched_yield(); +} + +/* + * Test Userland Thread Scheduler (UTS) suite for KSE. + */ +int +main(void) +{ + int i; + + thread_start(spin, '.'); + // thread_start(spin); + init_uts(); + for (i = 0;1;i++) { +// if (i < 1000) +// thread_start(aaaa, 'a' + (i % 26)); + pchar('A' + (i % 26)); + nano(5); + } + pstr("\n** main() exiting **\n"); + return (EX_OK); +} + + +/* + * Enter the UTS from a thread. + */ +static void +enter_uts(void) +{ + struct thread_mailbox *td; + + /* XXX: We should atomically exchange these two. */ + td = uts_mb.km_curthread; + uts_mb.km_curthread = NULL; + + thread_to_uts(td, &uts_mb); +} + +/* + * Initialise threading. + */ +static void +init_uts(void) +{ + struct thread_mailbox *tm; + int mib[2]; + char *p; + size_t len; + + /* + * Create initial thread. + */ + tm = (struct thread_mailbox *)calloc(1, sizeof(struct thread_mailbox)); + + /* Throw us into its context. */ + getcontext(&tm->tm_context); + + /* Find our stack. */ + mib[0] = CTL_KERN; + mib[1] = KERN_USRSTACK; + len = sizeof(p); + if (sysctl(mib, 2, &p, &len, NULL, 0) == -1) + pstr("sysctl(CTL_KER.KERN_USRSTACK) failed.\n"); + pfmt("main() : 0x%x\n", tm); + pfmt("eip -> 0x%x\n", tm->tm_context.uc_mcontext.mc_eip); + tm->tm_context.uc_stack.ss_sp = p - MAIN_STACK_SIZE; + tm->tm_context.uc_stack.ss_size = MAIN_STACK_SIZE; + + /* + * Create KSE mailbox. + */ + p = (char *)malloc(THREAD_STACK_SIZE); + bzero(&uts_mb, sizeof(struct kse_mailbox)); + uts_mb.km_stack.ss_sp = p; + uts_mb.km_stack.ss_size = THREAD_STACK_SIZE; + uts_mb.km_func = (void *)uts; + pfmt("uts() at : 0x%x\n", uts); + pfmt("uts stack at : 0x%x - 0x%x\n", p, p + THREAD_STACK_SIZE); + + /* + * Start KSE scheduling. + */ + pfmt("kse_new() -> %d\n", kse_new(&uts_mb, 0)); + uts_mb.km_curthread = tm; + + /* + * Arrange to deliver signals via KSE. + */ + signal(SIGURG, foof); +} + +/* + * Write a single character to stdout, in a thread-safe manner. + */ +static void +pchar(char c) +{ + + write(STDOUT_FILENO, &c, 1); +} + +/* + * Write formatted output to stdout, in a thread-safe manner. + * + * Recognises the following conversions: + * %c -> char + * %d -> signed int (base 10) + * %s -> string + * %u -> unsigned int (base 10) + * %x -> unsigned int (base 16) + */ +static void +pfmt(const char *fmt, ...) +{ + static const char digits[16] = "0123456789abcdef"; + va_list ap; + char buf[10]; + char *s; + unsigned r, u; + int c, d; + + va_start(ap, fmt); + while ((c = *fmt++)) { + if (c == '%') { + c = *fmt++; + switch (c) { + case 'c': + pchar(va_arg(ap, int)); + continue; + case 's': + pstr(va_arg(ap, char *)); + continue; + case 'd': + case 'u': + case 'x': + r = ((c == 'u') || (c == 'd')) ? 10 : 16; + if (c == 'd') { + d = va_arg(ap, unsigned); + if (d < 0) { + pchar('-'); + u = (unsigned)(d * -1); + } else + u = (unsigned)d; + } else + u = va_arg(ap, unsigned); + s = buf; + do { + *s++ = digits[u % r]; + } while (u /= r); + while (--s >= buf) + pchar(*s); + continue; + } } + pchar(c); + } + va_end(ap); +} + +static void +pstr(const char *s) +{ + + write(STDOUT_FILENO, s, strlen(s)); +} + +/* + * Insert a thread into the run queue. + */ +static void +runq_insert(struct thread_mailbox *tm) +{ + + tm->tm_next = run_queue; + run_queue = tm; +} + +/* + * Select and remove a thread from the run queue. + */ +static struct thread_mailbox * +runq_remove(void) +{ + struct thread_mailbox *p, *p1; + + if (run_queue == NULL) + return (NULL); + p1 = NULL; + for (p = run_queue; p->tm_next != NULL; p = p->tm_next) + p1 = p; + if (p1 == NULL) + run_queue = NULL; + else + p1->tm_next = NULL; + return (p); +} + +/* + * Userland thread scheduler. + */ +static void +uts(struct kse_mailbox *km) +{ + struct thread_mailbox *tm, *p; + int ret, i; + + UPSTR("\n--uts() start--\n"); + UPFMT("mailbox -> %x\n", km); + + /* + * Insert any processes back from being blocked + * in the kernel into the run queue. + */ + p = km->km_completed; + uts_mb.km_completed = NULL; + UPFMT("km_completed -> 0x%x", p); + while ((tm = p) != NULL) { + p = tm->tm_next; + UPFMT(" 0x%x", p); + runq_insert(tm); + } + UPCHAR('\n'); + + /* + * Process any signals we've recieved (but only if we have + * somewhere to deliver them to). + */ + if ((run_queue != NULL) && SIGNOTEMPTY(km->km_sigscaught)) { + for (i = 0;i < _SIG_MAXSIG;i++) + if (SIGISMEMBER(km->km_sigscaught, i)) { + signalcontext(&run_queue->tm_context, i, foof); + break; + } + bzero(&km->km_sigscaught, sizeof(sigset_t)); } - /* find highest priority thread and load it */ - if ((thread = select_thread())) { - ksedata->curthread = thread; - ke_mbox->kmbx_current_thread = &thread->mbox; - - /* loads context similar to longjmp() */ - loadthread(&thread->mbox.ctx.tfrm.tf_tf); - /* NOTREACHED */ + /* + * Pull a thread off the run queue. + */ + p = runq_remove(); +#if 0 + if ((p == aa) && (progress > 0)) { + --progress; + signalcontext(&p->tm_context, 1, foof); } - kse_yield(); /* in the kernel it does a thread_exit() */ - /* NOTREACHED */ -} +#endif -/************************************************************* - * Startup mechanism functions - **************************************************************/ -static int -kickkse(struct per_kse *ksedata, int newgroup) -{ - char * newstack; - jmp_buf jb1; - jmp_buf jb2; - struct kse_mailbox *mboxaddr; - struct per_kse *user_UTS_info; - int err; - - newstack = malloc(K_STACKSIZE); - mboxaddr = &ksedata->mbox; - mboxaddr->kmbx_stackbase = newstack; - mboxaddr->kmbx_stacksize = K_STACKSIZE; - mboxaddr->kmbx_upcall = &UTS; - mboxaddr->kmbx_UTS_handle = ksedata; - err = kse_new(mboxaddr, newgroup); - return(err); -} - - -static int -startkse(struct per_kse *ksedata) -{ - return (kickkse(ksedata, 0)); -} - -static int -startksegrp(struct per_kse *ksedata) -{ - return(kickkse(ksedata, 1)); -} - -void badreturn() -{ - printf("thread returned when shouldn't\n"); - exit(1); -} - -__inline__ void -pushontostack(struct user_thread *tcb, int value) -{ - int *SP; - - SP = (int *)(tcb->mbox.ctx.tfrm.tf_tf.tf_isp); - *--SP = value; - tcb->mbox.ctx.tfrm.tf_tf.tf_isp = (int)SP; -} - -struct user_thread * -makethread(thread_fn *fn, int arg1, void *arg2) -{ - struct user_thread *tcb; - - /* We could combine these mallocs */ - tcb = malloc(sizeof *tcb); - bzero(tcb, sizeof(*tcb)); - tcb->mbox.UTS_handle = tcb; /* back pointer */ - - /* malloc the thread's stack */ - /* We COULD mmap it with STACK characteristics */ - /* Then we could add a guard page. */ - tcb->stack_size = T_STACKSIZE; /* set the size we want */ - tcb->stack = malloc(tcb->stack_size); - - /* Make sure there are good defaults */ - savethread(&tcb->mbox.ctx.tfrm.tf_tf); - - /* set the PC to the fn */ - tcb->mbox.ctx.tfrm.tf_tf.tf_eip = (int) fn; - - /* Set the stack and push on the args and a dummy return address */ - tcb->mbox.ctx.tfrm.tf_tf.tf_ebp = - tcb->mbox.ctx.tfrm.tf_tf.tf_isp = - tcb->mbox.ctx.tfrm.tf_tf.tf_esp = - (int)(&tcb->stack[tcb->stack_size - 16]); - pushontostack(tcb, (int)arg2); - pushontostack(tcb, (int)arg1); - pushontostack(tcb, (int)&badreturn); /* safety return address */ - return (tcb); -} - -/************************************************************* - * code for three separate threads. (so we can see if it works) - *************************************************************/ -static void -thread1_code(void *arg) -{ - for(;;) { - sleep (1); - write(1,".",1); + /* + * Either schedule a thread, or idle if none ready to run. + */ + if (p != NULL) { + UPFMT("\n-- uts() scheduling 0x%x--\n", p); + UPFMT("eip -> 0x%x progress -> %d\n", + p->tm_context.uc_mcontext.mc_eip, progress); + UPSTR("curthread set\n"); + uts_to_thread(p, &km->km_curthread); + UPSTR("\n-- uts_to_thread() failed --\n"); } + kse_yield(); + pstr("** uts() exiting **\n"); + exit(EX_SOFTWARE); } -static void -thread2_code(void *arg) +/* + * Start a thread. + */ +static void +thread_start(const void *func, int arg) { - for(;;) { - sleep (3); - write(1,"+",1); - } + struct thread_mailbox *tm; + char *p; + + aa = tm = (struct thread_mailbox *)calloc(1, sizeof(struct thread_mailbox)); + pfmt("thread_start() : 0x%x %x\n", tm, &aa->tm_context); + getcontext(&tm->tm_context); + p = (char *)malloc(THREAD_STACK_SIZE); + tm->tm_context.uc_stack.ss_sp = p; + tm->tm_context.uc_stack.ss_size = THREAD_STACK_SIZE; + makecontext(&tm->tm_context, func, 2, arg); + // setcontext(&tm->tm_context); + runq_insert(tm); } - -static void -thread3_code(void *arg) -{ - for(;;) { - sleep (5); - write(1,"=",1); - } -} - - - -int main() -{ - - /* set up global structures */ - TAILQ_INIT(&runqueue); - - /* define two threads to run, they are runnable but not yet running */ - runq_insert( makethread(&thread1_code, 0, NULL)); - runq_insert( makethread(&thread2_code, 0, NULL)); - - /* and one which we will run ourself */ - first_kse.curthread = makethread(&thread3_code, 0, NULL); - - /* start two KSEs in different KSEGRPs */ - if (startkse(&first_kse)) { - perror("failed to start KSE"); - exit(1); - } - - /* startksegrp(&second_kse); */ /* we can't do 2 KSEs yet */ - /* One will be sufficient */ - - /* we are a thread, start the ball rolling */ - /* let the kernel know we are it */ - first_kse.mbox.kmbx_current_thread = &first_kse.curthread->mbox; - thread3_code(NULL); - return 0; -} -