/* TMHal.c, Copyright (c) by Lukas Ruf, lr@lpr.ch, 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. */ /* File: $Source: /usr/drwho/vault/cvs/topsy/Topsy/Threads/ia32/TMHal.c,v $ Author(s): Lukas Ruf, lr@lpr.ch Affiliation: ETH Zuerich, TIK Version: $Revision: 1.4 $ Creation Date: Last Date of Change: $Date: 1999/12/13 21:48:34 $ by: $Author: ruf $ $Log: TMHal.c,v $ Revision 1.4 1999/12/13 21:48:34 ruf GNU General Public Licence Update Revision 1.3 1999/10/23 19:07:14 jeker added Makefile.SimOS.mips and did some code cleanup Revision 1.2 1999/06/06 20:55:17 jeker putting everything together for Topsy 2.0 Revision 1.1 1999/05/13 17:05:50 jeker Initial revision */ #include "IOHal.h" #include "MMHal.h" #include "Support.h" #include "TMHal.h" #include "TMHalAsm.h" #include "TMClock.h" #include "TMError.h" #include "TMScheduler.h" #include "TMIPC.h" #include "MemoryLayout.h" #include "Exception.h" #include "tm-include.h" #include "Video.h" #include "SupportAsm.h" #include "Tools.h" #include "cpu.h" /* simplifications */ extern Scheduler scheduler; extern char *ExMsg[]; /* Segment Selectors are all defined in MMHal.c */ extern unsigned long KCSEL; /* KERNEL Code Segment Selector */ extern unsigned long KDSEL; /* KERNEL Data Segment Selector */ extern unsigned long UCSEL; /* USER Code Segment Selector */ extern unsigned long UDSEL; /* USER Data Segment Selector */ static char *ExMsg[] = { /* Exception no. */ "Division by zero", /* Ex_00 */ "Debug", /* Ex_01 */ "NMI", /* Ex_02 */ "Breakpoint", /* Ex_03 */ "INTO", /* Ex_04 */ "BOUNDS", /* Ex_05 */ "Invalid Opcode", /* Ex_06 */ "CoPro not Available", /* Ex_07 */ "Double-Fault", /* Ex_08 */ "CoPro segment overrrun", /* Ex_09 */ "Invalid TSS", /* Ex_0A */ "Segment Not Present", /* Ex_0B */ "Stack Exception", /* Ex_0C */ "General Protection", /* Ex_0D */ "Page Fault", /* Ex_0E */ "Reserved", /* Ex_0F */ "FPU error", /* Ex_10 */ "Alignment check (>=i486)", /* Ex_11 */ "Machine check (>=Pentium)", /* Ex_12 */ }; /* Exception service branch table (exception handlers). * - 32 exception handlers, */ ExceptionHandler exceptionHandlerTable[MAXNBOFEXCEPTIONS]; /* Hardware interrupt service branch table (interrupt handlers) and * corresponding arguments that may be passed to the interrupt handler. * - 16 hw interrupts * - 1 sw interrupts */ InterruptHandler interruptHandlerTable[MAXNBOFINTERRUPTS]; void* interruptHandlerArgTable[MAXNBOFINTERRUPTS]; void tmInstallExceptionCode() { int i; for (i = 0; i < MAXNBOFINTERRUPTS; i++) { interruptHandlerArgTable[i] = NULL; interruptHandlerTable[i] = NULL; } for (i = 0; i < MAXNBOFEXCEPTIONS; i++) exceptionHandlerTable[i] = NULL; /* Set Clock Handler: Vital for correct functionality :-) */ tmSetInterruptHandler(AIR_00,tmClockHandler,NULL); } /* InterruptHandler are enumareted conforming to their IDT-Slot. So, not to waste space, the interruptHandlerTable is an array of MAXNBOFINTERRUPTS addresses. To make the Interrupt No. fit into this array the Interrupt IDs are modulo MAXNBOFINTERRUPTS. */ InterruptHandler tmSetInterruptHandler( InterruptId intId, InterruptHandler intHdler, void* arg) { InterruptHandler oldIntHdler = interruptHandlerTable[intId % MAXNBOFINTERRUPTS]; interruptHandlerTable[intId % MAXNBOFINTERRUPTS] = intHdler; interruptHandlerArgTable[intId % MAXNBOFINTERRUPTS] = arg; return oldIntHdler; } /* ExceptionHandlers are enumareted conforming to their IDT-Slot. As 'ntel did preserve the 32 first slots the ExceptionHandlers' ID resembles the Address position in the array. */ ExceptionHandler tmSetExceptionHandler( ExceptionId excId, ExceptionHandler excHdler) { ExceptionHandler oldExcHdler = exceptionHandlerTable[excId]; exceptionHandlerTable[excId] = excHdler; return oldExcHdler; } /* The intDispatcher is called by the General Interrupt Handler. The ProcContext includes the executed IDT-Slot ID. So the Interrupt ID is retrieved by IDT-Slot ID modulo MAXNBOFINTERRUPTS. Please note: If an exception occurs in any case tmError() is correctly called by default even if no exception handlers are installed. I decided to provide this functionality as there is no sense in calling a single exception handler that executes nothing than a string deliverance. I collected all possible Exception Codes in Interrupt.c. Therefore I includeded the "extern *ExMsg[]" statement above. Implementation of specialized Exception Handlers is provided through normal function calls -- as used before :-) */ void intDispatcher(ProcContextPtr frame) { /* we call here the handler for interrupt offset with * its (optionally) defined argument */ int i = frame->tf_trapno; if (i < T_IR_00) { i %= MAXNBOFEXCEPTIONS; /* Make sure no array access-overflow will occur */ if (exceptionHandlerTable[i]) (exceptionHandlerTable[i])(scheduler.running->id); else tmError(scheduler.running->id,ExMsg[i]); } else { i %= MAXNBOFINTERRUPTS; if (interruptHandlerTable[i]) (interruptHandlerTable[i])(interruptHandlerArgTable[i]); } } /******************************************************************************/ void enableInterruptInContext( InterruptId intId, ProcContextPtr contextPtr) { } void disableInterruptInContext( InterruptId intId, ProcContextPtr contextPtr) { } void enableAllInterruptsInContext( ProcContextPtr contextPtr) { contextPtr->tf_eflags |= PSL_I; } /******************************************************************************/ void tmSetMachineDependentRegisters(ProcContextPtr contextPtr, AddressSpace space) { if (space == USER) { /* Setting the correct protection level of a user thread */ contextPtr->tf_cs = UCSEL | 0x3; contextPtr->tf_ds = UDSEL | 0x3; contextPtr->tf_es = UDSEL | 0x3; contextPtr->tf_fs = UDSEL | 0x3; contextPtr->tf_gs = UDSEL | 0x3; contextPtr->tf_ss = UDSEL | 0x3; /* The automaticThreadExit "start" address must be adjusted too */ *(int*)contextPtr->tf_esp = K_TO_U(*(int*)contextPtr->tf_esp); /* Now: directly adjust the top of stack pointer */ contextPtr->tf_esp = K_TO_U(contextPtr->tf_esp); /* Please note: Now, NO MORE DIRECT STACK MANIPULATION !!!! */ } else { contextPtr->tf_cs = KCSEL; contextPtr->tf_ds = KDSEL; contextPtr->tf_es = KDSEL; contextPtr->tf_fs = KDSEL; contextPtr->tf_gs = KDSEL; contextPtr->tf_ss = KDSEL; } contextPtr->tf_trapno = 0x0; /* make sure, no error code is set -> else an interrupt reset will be performed */ } void tmSetReturnValue(ProcContextPtr contextPtr, Register value) { contextPtr->tf_eax = value; } void tmSetStackPointer(ProcContextPtr contextPtr, Register value) { /* Execution speed is extended by factor 2 if all memory access is to locations residing at multiples of four */ contextPtr->tf_esp = (Register)(((unsigned long)value >> 2)<<2); } void tmSetProgramCounter(ProcContextPtr contextPtr, Register value) { contextPtr->tf_eip = value; } void tmSetStatusRegister(ProcContextPtr contextPtr, Register value) { contextPtr->tf_eflags = value; } void tmSetFramePointer(ProcContextPtr contextPtr, Register value) { contextPtr->tf_ebp = value; } void tmSetReturnAddress(ProcContextPtr contextPtr, Register value) { Register *threadLocalStack; threadLocalStack = (Register *)contextPtr->tf_esp; threadLocalStack[0] = value; } void tmSetArgument0(ProcContextPtr contextPtr, Register value) { Register *threadLocalStack; threadLocalStack = (Register*)contextPtr->tf_esp; threadLocalStack[1] = value; } void tmSetArgument1(ProcContextPtr contextPtr, Register value) { Register *threadLocalStack; threadLocalStack = (Register *)contextPtr->tf_esp; threadLocalStack[2] = value; } /*Low Level Interrupt Handling and Message Adjustment*************************/ void CascStart() { __enable_IRQ(0x22); // enable CASCADE __endOf_IRQ(0x22); return; } /**********************************************************/ /* 8259A reprogrammed in CoreLoad to map IRQs to 20h..2Fh */ /**********************************************************/ void InterruptInit() { int i; Set_Def_IDTEntry(0x20,(unsigned long)__NIR_00, INTGATE); // Timer Wrapper called in timer.S Set_Def_IDTEntry(0x21,(unsigned long)__NIR_01, INTGATE); // Keyboard Set_Def_IDTEntry(0x22,(unsigned long)__NIR_02, INTGATE); // Slave IC Set_Def_IDTEntry(0x23,(unsigned long)__NIR_03, INTGATE); // Serial Port II Set_Def_IDTEntry(0x24,(unsigned long)__NIR_04, INTGATE); // Serial Port I Set_Def_IDTEntry(0x25,(unsigned long)__NIR_05, INTGATE); // Parallel Port II Set_Def_IDTEntry(0x26,(unsigned long)__NIR_06, INTGATE); // Floppy Set_Def_IDTEntry(0x27,(unsigned long)__NIR_07, INTGATE); // Parallel Port I Set_Def_IDTEntry(0x28,(unsigned long)__NIR_08, INTGATE); // RTC Set_Def_IDTEntry(0x29,(unsigned long)__NIR_09, INTGATE); // avail. Set_Def_IDTEntry(0x2A,(unsigned long)__NIR_0A, INTGATE); // avail. Set_Def_IDTEntry(0x2B,(unsigned long)__NIR_0B, INTGATE); // avail. Set_Def_IDTEntry(0x2C,(unsigned long)__NIR_0C, INTGATE); // avail. Set_Def_IDTEntry(0x2D,(unsigned long)__NIR_0D, INTGATE); // CoProcessor Set_Def_IDTEntry(0x2E,(unsigned long)__NIR_0E, INTGATE); // Harddisk Controller Set_Def_IDTEntry(0x2F,(unsigned long)__NIR_0F, INTGATE); // reserved printf("Peripheral IRQ Handler : IRQs 0x00..0x0F (mapped to 0x20..0x2F)\n"); Set_Def_IDTEntry(0x30,(unsigned long)__INT_30, SYSGATE); // Software Interrupt for User INT Gates printf("Internal INT Handler : Installed at 0x30\n"); printf("Blocking all unregistered Interrupts..."); for (i = 0x31; (i < 256); i++) Set_Def_IDTEntry((char)i,(unsigned long)__INT_31, INTGATE); /* Block all other gates */ printf("Blocked !!\n"); CascStart(); printf("Interrupt Request Line 0x02 (Cascade) started\n"); return; } /* Exception Handling is included in Interrupt.c too as it makes use of the same functionality. */ void ExceptionInit() { Set_Def_IDTEntry(0x00,(unsigned long)__NEx_00, TRAPGATE); /* Fault : Divide by 0 */ Set_Def_IDTEntry(0x01,(unsigned long)__NEx_01, TRAPGATE); /* Fault : Debug */ Set_Def_IDTEntry(0x02,(unsigned long)__NEx_02, TRAPGATE); /* Trap : NMI */ Set_Def_IDTEntry(0x03,(unsigned long)__NEx_03, TRAPGATE); /* Trap : Breakpoint */ Set_Def_IDTEntry(0x04,(unsigned long)__NEx_04, TRAPGATE); /* Trap : INTO */ Set_Def_IDTEntry(0x05,(unsigned long)__NEx_05, TRAPGATE); /* Fault : BOUNDS */ Set_Def_IDTEntry(0x06,(unsigned long)__NEx_06, TRAPGATE); /* Fault : Invalid Opcode */ Set_Def_IDTEntry(0x07,(unsigned long)__NEx_07, TRAPGATE); /* Fault : CoPro not Available */ Set_Def_IDTEntry(0x08,(unsigned long)__NEx_08, TRAPGATE); /* Abort : Double-Fault */ Set_Def_IDTEntry(0x09,(unsigned long)__NEx_09, TRAPGATE); /* Abort : CoPro segment overrrun */ Set_Def_IDTEntry(0x0A,(unsigned long)__NEx_0A, TRAPGATE); /* Fault : Invalid TSS */ Set_Def_IDTEntry(0x0B,(unsigned long)__NEx_0B, TRAPGATE); /* Fault : Segment Not Present */ Set_Def_IDTEntry(0x0C,(unsigned long)__NEx_0C, TRAPGATE); /* Fault : Stack Exception */ Set_Def_IDTEntry(0x0D,(unsigned long)__NEx_0D, TRAPGATE); /* Fault : General Protection */ Set_Def_IDTEntry(0x0E,(unsigned long)__NEx_0E, TRAPGATE); /* Fault : Page Fault */ Set_Def_IDTEntry(0x0F,(unsigned long)__NEx_0F, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x10,(unsigned long)__NEx_10, TRAPGATE); /* Fault : FPU error */ Set_Def_IDTEntry(0x11,(unsigned long)__NEx_11, TRAPGATE); /* Fault : Alignment check (i486 + ff) */ Set_Def_IDTEntry(0x12,(unsigned long)__NEx_12, TRAPGATE); /* Abort : Machine check (Pentium + ff) */ Set_Def_IDTEntry(0x13,(unsigned long)__NEx_13, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x14,(unsigned long)__NEx_14, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x15,(unsigned long)__NEx_15, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x16,(unsigned long)__NEx_16, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x17,(unsigned long)__NEx_17, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x18,(unsigned long)__NEx_18, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x19,(unsigned long)__NEx_19, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x1A,(unsigned long)__NEx_1A, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x1B,(unsigned long)__NEx_1B, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x1C,(unsigned long)__NEx_1C, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x1D,(unsigned long)__NEx_1D, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x1E,(unsigned long)__NEx_1E, TRAPGATE); /* Fault : Reserved */ Set_Def_IDTEntry(0x1F,(unsigned long)__NEx_1F, TRAPGATE); /* Fault : Reserved */ printf("Processor Exceptions : IDT Vectors 0x00..0x1F\n"); return; } /******************************************************************************/ /* Cases to differenciate: Presumption: All threads have a completed initialize Thread Context !!! From -> To Action taken ************** ************ User -> User Nothing Kernel -> Kernel Nothing User -> Kernel Increment all Addresses Kernel -> User Decrement all Addresses */ #define ADJUST(x) (long)(x) = (long)((long)(x) + (long)(adjust)); void msgAdjust(ThreadPtr pFromOrToThread, Message *msg, Boolean ToKernel) { /* Note: Defining this Variable as signed long limits MM to 2GB !!! */ long adjust = UADDR_BASE; ProcContextPtr ctx = pFromOrToThread->contextPtr; Boolean MsgLocIsKernel = (ctx->tf_cs == KCSEL); if (MsgLocIsKernel) return; /* no adjustments required */ /* This means the sending or the receiving thread expects the message to be copied in or from a location in the Kernel Data Space. A Message from a User Thread would have MsgLocIsKernel == false and would be adjusted on the way to the Kernel. A Message to a User Thread needs adjustment as MsgLocIsKernel == false. */ if (!ToKernel) /* if we are on MsgRecv direction */ adjust = -adjust; /* we go to User Space */ switch (msg->id) { case VM_FREE : ADJUST(msg->msg.vmFree.address); break; case VM_MOVE : ADJUST(msg->msg.vmMove.address); break; case VM_PROTECT : ADJUST(msg->msg.vmProtect.startAddress); break; case VM_ALLOCREPLY : ADJUST(msg->msg.vmAllocReply.address); break; case VM_MOVEREPLY : ADJUST(msg->msg.vmMoveReply.address); break; case IO_READ : ADJUST(msg->msg.ioRead.buffer); break; case IO_WRITE : ADJUST(msg->msg.ioWrite.buffer); break; case TM_START : case IO_INIT : case IO_OPENREPLY : case IO_CLOSEREPLY : case IO_READREPLY : case IO_WRITEREPLY : case IO_INITREPLY : case IO_OPEN : case IO_CLOSE : case VM_PROTECTREPLY : case VM_CLEANUP : case VM_FREEREPLY : case VM_ALLOC : case TM_EXIT : case TM_YIELD : case TM_KILL : case TM_STARTREPLY : case TM_KILLREPLY : case TM_INFO : case TM_INFOREPLY : case UNKNOWN_SYSCALL : case ANYMSGTYPE : default: /*rien a faire -- or shit happened*/ break; } } /******************************************************************************/ /************************************/ /* HAL Level of Interrupt Handling. */ /************************************/ void _INTHandler(unsigned int esp, struct ProcContext_t frame); void _INTHandler(unsigned int esp, struct ProcContext_t frame) { Thread *currentThread; Message *msg; int irq_to_ack; cli; /* disable all interrupts Exceptions may be called without interrupts disabled: */ /* SAVE PROCESS CONTEXT: REGISTER SET IS ONLY SAVED ON STACK */ irq_to_ack = frame.tf_trapno; /* Remember IRQ to acknowledge before exit */ currentThread = schedulerRunning(); byteCopy((Address)currentThread->contextPtr,(Address)&frame,sizeof(frame)); /* on Interrupt Handler Entry: verify the stack for deciding if it was originally a kernel thread that was interrupt. For a detailed explanation please refer to ~/Topsy/Documentation/IPC/Interrupt.dok. */ if (currentThread->contextPtr->tf_cs == KCSEL) { /* tf_temp_esp: points to the location before the pushal was executed, so, it is right after tf_err. It is most important that this modifications are made to the context- region defined in the threads definition structure (what a word :-) and not directly on the stack, as tf_esp and tf_ss are not defined in this actually handled case. */ currentThread->contextPtr->tf_esp = currentThread->contextPtr->tf_temp_esp + 16; /* remove even tf_err, tf_eip, tf_cs and tf_eflags when restarting the interrupted thread. */ currentThread->contextPtr->tf_ss = KDSEL; } if (frame.tf_trapno < T_IR_00) { /* Processor Exception occured */ printf("esp: 0x%8x \n",(unsigned int)esp); DisplayContext("WARNING: Exception occured: ",currentThread->contextPtr); } if (frame.tf_trapno < T_IS_30) intDispatcher(&frame); else if (frame.tf_trapno == T_IS_30) { /* This is our cruxious SYSCALL :-) */ msg = (Message *)frame.tf_esi; /* Get Address of Message */ if (currentThread->contextPtr->tf_ds != KDSEL) { /* is it not a kernel thread ? */ msg = (Message *)U_TO_K(msg); /* if USER thread, adjust address */ } /* We are on the way to the Message handler */ msgAdjust(currentThread,msg,TRUE); msgDispatcher(currentThread->id, /* Sender is retrieved from Message */ msg, /* Address of Message is passed by esi */ (unsigned long int)frame.tf_edx, /* timeout in milliseconds (if receiving) */ (MsgOpCode)frame.tf_eax ); /* Message OpCode */ } else printf("Unknown \"random\" interrupt occured : %i :-?",frame.tf_trapno); if ((irq_to_ack >= T_IR_00) && (irq_to_ack <= T_IR_0F)) __endOf_IRQ(irq_to_ack); /* Always execute a restore context to surely process set context. */ restoreContext(schedulerRunning()->contextPtr); /* This should never be executed. */ return; } void powerSaver(void) { asm("hlt"); /* stop working until next IRQ: save power */ }