/* TMHal.c, Copyright (c) by George Fankhauser, Swiss Federal Institute of Technology, Computer Engineering and Networks Laboratory. TOPSY -- A Teachable Operating System. Implementation of a tiny and simple micro kernel for teaching purposes. For further information, please visit http://www.tik.ee.ethz.ch/~topsy This software is provided under the terms of the GNU General Public Licence. A full copy of the GNU GPL is provided in the file COPYING found in the development root of Topsy. */ /* Revision 1.11 1999/10/23 19:07:16 jeker added Makefile.SimOS.mips and did some code cleanup Revision 1.10 1999/10/22 19:22:34 gfa *** empty log message *** Revision 1.7 1999/06/06 20:55:23 jeker putting everything together for Topsy 2.0 Revision 1.6 1999/05/29 14:32:08 gfa *** empty log message *** Revision 1.5 1999/05/19 14:30:46 gfa ome linux tweakin' done... Revision 1.4 1999/05/19 11:43:20 gfa new link script for linux Revision 1.3 1999/05/13 17:05:55 jeker Initial revision */ #include "TMHal.h" #include "IOHal.h" #include "Support.h" #include "TMClock.h" #include "TMScheduler.h" #include "TMIPC.h" #include "Unix.h" ExceptionHandler exceptionHandlerTable[MAXNBOFEXCEPTIONS+1]; InterruptHandler interruptHandlerTable[MAXNBOFINTERRUPTS]; void* interruptHandlerArgTable[MAXNBOFINTERRUPTS]; static struct ucontext* signal_context = NULL; static int signal_no; #ifdef __sparc__ static char* gwin_save_area[SPARC_MAXREGWINDOW * sizeof(struct rwindow)]; #endif // memory protection stuff void setKernelProtected(Boolean mode) { int prot; //int size = sysconf(_SC_PAGESIZE); if (mode) { prot = PROT_WRITE | PROT_EXEC; // since we need to restore after // protecting we can't use read } else { prot = PROT_NONE; } mprotect(0x00000000, USERBRKAT, prot); // where's kernel space? // mprot from 0 to user start } void saveContext(ProcContextPtr contextPtr) { // copy context from signal stack to thread's processor context #ifdef __sparc__ if (signal_context->uc_mcontext.gwins != NULL) { PANIC("save gwin..."); byteCopy(gwin_save_area, (char*)signal_context->uc_mcontext.gwins, sizeof(gwin_save_area)); } #endif byteCopy(&(contextPtr->uc), signal_context, sizeof(struct ucontext)); setKernelProtected(FALSE); } void restoreContext(ProcContextPtr contextPtr) { #ifdef __solaris__ // really is: has memProtect and setContext... if (THREAD_MODE(schedulerRunning()->id) == USER) { setKernelProtected(TRUE); } #ifdef __sparc__ contextPtr->uc.uc_mcontext.gregs[REG_PSR] |= 1; if (contextPtr->uc.uc_mcontext.gwins != NULL) { PANIC("sparc gwin restore needed..."); byteCopy((char*)contextPtr->uc.uc_mcontext.gwins, gwin_save_area, sizeof(gwin_save_area)); } #endif // solaris can restore a user/sigcontext explicitly setcontext( &(contextPtr->uc)); #endif #ifdef __linux__ #define GS REG_GS #define FS REG_FS #define ES REG_ES #define DS REG_DS #define EDI REG_EDI #define ESI REG_ESI #define EBP REG_EBP #define ESP REG_ESP #define EBX REG_EBX #define EDX REG_EDX #define ECX REG_ECX #define EAX REG_EAX #define TRAPNO REG_TRAPNO #define ERR REG_ERR #define EIP REG_EIP #define CS REG_CS #define EFL REG_EFL #define UESP REG_UESP #define SS REG_SS struct __jmp_buf_tag buf; if (signal_context != NULL) { // replace sigcontext on stack by context to be restored by linux byteCopy(signal_context, &(contextPtr->uc), sizeof(struct ucontext)); // sigreturn(dummy); // implicit syscall by stack frame signal code } else { // the very first context is not on a signal stack and needs // to jump explicitely buf.__jmpbuf[JB_BX] = (int)contextPtr->uc.uc_mcontext.gregs[EBX]; buf.__jmpbuf[JB_SI] = (int)contextPtr->uc.uc_mcontext.gregs[ESI]; buf.__jmpbuf[JB_DI] = (int)contextPtr->uc.uc_mcontext.gregs[EDI]; buf.__jmpbuf[JB_BP] = (int)contextPtr->uc.uc_mcontext.gregs[EBP]; buf.__jmpbuf[JB_SP] = (int)contextPtr->uc.uc_mcontext.gregs[ESP]; buf.__jmpbuf[JB_PC] = (int)contextPtr->uc.uc_mcontext.gregs[EIP]; longjmp(&buf, 0); } #endif // does not return... } /* general signal/exception handler */ void generalExceptionHandler(int signo, siginfo_t* info, struct ucontext* uc) { signal_context = uc; signal_no = signo; saveContext(schedulerRunning()->contextPtr); { // dispatch signal (exception) exceptionHandlerTable[signo](schedulerRunning()->id); } restoreContext(schedulerRunning()->contextPtr); } void syscallExceptionHandler(ThreadId from) { // grabbing parameter of tmMsgSend/Recv from calling thread's stack Message* msg; #ifdef i386 greg_t* g = signal_context->uc_mcontext.gregs; if (signal_no == SEND) { // for tmMsgSend the user stack is shorter (2 args) msg = (Message*)(*(int*)(g[ESP]+40)); // msgptr (arg1) msg->from = *(ThreadId*)(g[ESP]+36); // to (arg0) msgDispatcher( from, // sending thread msg, 0, // timeout (not used for send) SYSCALL_SEND_OP); // send op id } else if (signal_no == RECV) { // for tmMsgRecv the user stack is a little longer (4 args) msg = (Message*)(*(int*)(g[ESP]+60)); // msgptr (arg2) msgDispatcher( from, // recv threadid msg, *(int*)(g[ESP]+64), // timeout (arg3) SYSCALL_RECV_OP); // recv op } // on return of msgDispatcher (TMIPC.c), the syscall return value // is written into the contexts mc.g[EAX] register with tmSetReturnRegister #endif #ifdef sparc greg_t* g = signal_context->uc_mcontext.gregs; if (signal_no == SEND) { msg = (Message*)(*(int*)(g[REG_SP]+36)); // msgptr (arg1) msg->from=(ThreadId)(*(int*)(g[REG_SP]+32)); // to (arg0) msgDispatcher( from, // sending thread msg, 0, // timeout (not for send) SYSCALL_SEND_OP); // send op id } else if (signal_no == RECV) { msg = (Message*)(*(int*)(g[REG_SP]+40)); // msgptr (arg2) msgDispatcher( from, // receiving threadid msg, *(int*)(g[REG_SP]+44), // timeout (arg3) SYSCALL_RECV_OP); // recv op } // on return of msgDispatcher (TMIPC.c), the syscall return value // is written into the context's return register with tmSetReturnRegister #endif } /* * This code should run in kernel and in user space. Thus no relative * calls (across address spaces) must be used. A simple, however non-portable * way to do this is a small piece of assembly that traps twice to unix. * A C version would be kill(getpid(), SIGEMT); */ void automaticThreadExit() { #ifdef i386 asm("movl $0x14,%eax"); // getpid (unix syscall) asm("int $0x80 "); // pid in eax asm("movl %eax,0x8(%esp,1)"); asm("movl $31,0x4(%esp,1)"); // SIGEMT (or UNUSED) as exit msg asm("movl $0x25,%eax"); // SIGEMT in eax asm("int $0x80 "); // kill (unix syscall) #endif #ifdef sparc asm(" mov 0x14, %g1"); // getpid (solaris syscall) asm(" ta 8"); // pid in i0 asm(" mov 0x7, %o1"); // SIGEMT is used as exit msg replacement asm(" mov %o1, %i1"); // SIGEMT in i1 asm(" mov 0x25, %g1"); // kill (solaris syscall) asm(" ta 8"); #endif #ifdef mips asm("mov a0, 0x14"); // getpid (irix syscall) asm("syscall"); // pid in v0 returned asm("mov a1, v0"); asm("mov a2, 0x7"); // SIGEMT is used as exit msg replacement asm("mov a0, 0x25"); // SIGEMT in a0 asm("syscall"); // kill (irix syscall) #endif // as of here we should be dead } void endAutomaticThreadExit() {} // is there another way to do labels in C?! InterruptHandler tmSetInterruptHandler( InterruptId intId, InterruptHandler intHandler,void* arg) { // no interrupts in unix processes :-) PANIC("no interrupts in unix processes"); return intHandler; } ExceptionHandler tmSetExceptionHandler( ExceptionId excId, ExceptionHandler excHandler) { ExceptionHandler prev; #if defined( __svr4__ ) || defined ( linux ) struct sigaction act; sigset_t set; sigfillset(&set); sigdelset(&set, SIGILL); sigdelset(&set, SIGTRAP); #ifndef linux sigdelset(&set, SIGEMT); #endif sigdelset(&set, SIGFPE); sigdelset(&set, SIGBUS); sigdelset(&set, SIGSEGV); // all signals handled by the same exception dispatcher // this is similar to the MIPS exception model act.sa_sigaction = generalExceptionHandler; #ifdef linux act.sa_flags = SA_SIGINFO; #else act.sa_flags = SA_ONSTACK|SA_SIGINFO; #endif act.sa_mask = set; sigaction(excId, &act, NULL); #else struct sigvec sv; sv.sv_handler = generalExceptionHandler; sv.sv_flags = SV_ONSTACK; sv.sv_mask = SIGALRM; sigvec(excId, &sv, NULL); // all signals caught by same function #endif prev = exceptionHandlerTable[excId]; exceptionHandlerTable[excId] = excHandler; // signal dispatcher // uses this table return prev; } void tmInstallExceptionCode() { /* not needed in unix */ } void tmSetReturnValue(ProcContextPtr contextPtr, Register value) { SETRETURNVALUE(contextPtr, value); } void tmSetStackPointer(ProcContextPtr contextPtr, Register value) { SETSTACKPOINTER(contextPtr, value); } void tmSetProgramCounter(ProcContextPtr contextPtr, Register value) { SETPROGRAMCOUNTER(contextPtr, value); } void tmSetStatusRegister(ProcContextPtr contextPtr, Register value) { SETSTATUSREGISTER(contextPtr, value); } void tmSetReturnAddress(ProcContextPtr contextPtr, Register value) { SETRETADDR(contextPtr, value); } void tmSetFramePointer(ProcContextPtr contextPtr, Register value) { SETFRAMEPOINTER(contextPtr, value); } void tmSetArgument0(ProcContextPtr contextPtr, Register value) { SETARGUMENT0(contextPtr, value); } void tmSetArgument1(ProcContextPtr contextPtr, Register value) { SETARGUMENT1(contextPtr, value); } void tmSetMachineDependentRegisters(ProcContextPtr contextPtr, AddressSpace space) { #ifdef i386 // find out which segments are used in this user process // we will reuse it for all topsy threads // no idea how to get these index register values // (besides inline asm)... contextPtr->uc.uc_mcontext.gregs[SS] = 43; contextPtr->uc.uc_mcontext.gregs[CS] = 35; contextPtr->uc.uc_mcontext.gregs[DS] = 43; contextPtr->uc.uc_mcontext.gregs[ES] = 43; #endif #ifdef sparc // get a full sample getcontext(&(contextPtr->uc)); #endif } void enableInterruptInContext( InterruptId intId, ProcContextPtr contextPtr) { // not needed, unix version runs always in user mode, even the kernel :-) } void disableInterruptInContext( InterruptId intId, ProcContextPtr contextPtr) { // not needed, unix version runs always in user mode, even the kernel :-) } void enableAllInterruptsInContext( ProcContextPtr contextPtr) { // not needed, unix version runs always in user mode, even the kernel :-) } Register tmGetProgramCounter(ProcContextPtr contextPtr) { GETPROGRAMCOUNTER(contextPtr); } Register tmGetFaultingAddress(ProcContextPtr contextPtr) { GETFAULTINGADDRESS(contextPtr); } void msgAdjust(ThreadPtr pFromOrToThread, Message *msg, Boolean ToKernel) { // not needed in unix port } void powerSaver(void) { // not needed, unix version runs always :-) }