/* TMHalAsm.S, Copyright (c) by Christian Conrad, 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/mips/TMHalAsm.S,v $ Author(s): Christian Conrad Affiliation: ETH Zuerich, TIK Version: $Revision: 1.53 $ Creation Date: Last Date of Change: $Date: 1999/12/13 21:48:35 $ by: $Author: ruf $ $Log: TMHalAsm.S,v $ Revision 1.53 1999/12/13 21:48:35 ruf GNU General Public Licence Update Revision 1.52 1999/10/29 14:53:27 jeker *** empty log message *** Revision 1.51 1999/10/28 21:21:57 jeker hack to get R3k and R4k running Revision 1.50 1999/10/27 19:07:47 jeker fixes for R3k R4k port (restoreContext) Revision 1.49 1999/10/25 18:23:59 jeker added .set mips3 for ert instructions Revision 1.48 1999/10/23 14:19:47 jeker first cleanup for R4k support Revision 1.47 1999/09/17 10:11:18 gfa minor cosmetics Revision 1.46 1999/01/08 21:28:01 cjeker moved branchDelayBit handling to TMHalAsm.S Revision 1.45 1997/04/28 09:59:23 gfa turned off as warning with .set noat in save/restore # Revision 1.44 1997/04/23 18:21:42 conrad # stack termination at the right place ! # # Revision 1.43 1997/04/23 13:26:49 gfa # fixed missing AT in save/restore # # Revision 1.42 1997/04/23 09:13:00 gfa # fixed recursive stack frames in exception context # # Revision 1.41 1997/04/23 09:10:28 gfa # formatting, comments adjusted # # Revision 1.40 1997/04/14 21:09:23 conrad # *** empty log message *** # # Revision 1.39 1997/04/14 12:33:38 conrad # no changes (attempt to reset interrupt bits in software ...) # # Revision 1.38 1997/04/11 08:45:54 gfa # removed TMTHREADID constant # # Revision 1.37 1997/04/11 08:42:04 gfa # removed a0 paramter (which is undefined in msgSend) from autoExit code # # Revision 1.36 1997/04/07 13:23:53 gfa # adjusted TMTHREADID to -2 (needs to be synced with Topsy.h) # # Revision 1.35 1997/04/07 09:23:00 gfa # changed interrupt handler management to include the argument and arg table # # Revision 1.34 97/04/04 18:21:06 gfa # replaced all k0/k1/at register references with t0/t1 in functions # that do not run in exceptions (these registers are not saved on # context switches and that's why they're called kernel scratch). # # Revision 1.33 1997/03/31 20:33:37 gfa # fixed utlb handler # # Revision 1.32 1997/03/21 19:29:51 conrad # Prepared for introducing an idle thread # # Revision 1.31 1997/03/21 19:14:30 conrad # *** empty log message *** # # Revision 1.30 1997/03/21 16:14:23 conrad # changed restoreContext() to check if a new threadPtr is NULL, if yes, restore # of a 'fake' idle context # (not yet ok with the status register ???) # # Revision 1.29 1997/03/21 13:55:07 conrad # *** empty log message *** # # Revision 1.28 1997/03/21 13:04:55 conrad # made a test on scheduler.running before calling restoreContext # # Revision 1.27 1997/03/20 14:04:17 conrad # Reverting order of two last registers for SUBU !!! # # Revision 1.26 1997/03/19 21:40:51 conrad # TMTHREADID updated from -2 to -3 (!!! beware double declared constants) # # Revision 1.25 1997/03/18 16:31:47 conrad # Adding of the exceptionContextFlag global variable # saveContext CANNOT be declared as NESTED, if so, fp is modified, which # makes it impossible to access local variables after a restore !!!!!!!! # # Revision 1.24 1997/03/13 23:55:46 gfa # fixed save/restore with v0/v1 # # Revision 1.23 1997/03/13 19:15:21 gfa # changed UTLBMisshandler to use compact code (no nops, no reordering, # runs with 27 instructions using most delay slots) # # Revision 1.22 1997/03/13 16:41:09 conrad # PC is no longer incremented by 4 for hardware interrupts # # Revision 1.21 1997/03/13 16:00:32 conrad # Adding of UTLBMiss handler # # Revision 1.20 1997/03/13 14:27:58 conrad # Removal of warning with the li (was actually a la !) # # Revision 1.19 1997/03/13 11:48:24 conrad # Increased size of frame for syscallExceptionHandler from 32 to 48 bytes # Decomposition of li's too complete operation in 3 steps (removal of warning) # # Revision 1.18 1997/03/12 17:54:22 conrad # Debugging version (correction of quite a lot of bugs :-) # # Revision 1.17 1997/03/11 20:07:35 conrad # First version to be debugged with Simulator # # Revision 1.16 1997/03/11 07:50:47 conrad # small changes # # Revision 1.15 1997/03/09 20:46:02 gfa # *** empty log message *** # # Revision 1.14 1997/03/07 13:01:00 conrad # Adding of error module # # Revision 1.13 1997/03/04 16:56:28 conrad # minor changes # # Revision 1.12 1997/02/27 14:38:40 conrad # minor changes # # Revision 1.11 1997/02/26 19:39:35 gfa # added defines for SEND/RECV OPs # # Revision 1.10 1997/02/26 17:34:39 conrad # cleaning (with gfa) # # Revision 1.9 1997/02/25 17:00:12 conrad # *** empty log message *** # # Revision 1.8 1997/02/24 21:35:26 conrad # incomplete version # # Revision 1.7 1997/02/21 16:53:32 conrad # very incomplete status due to major changes ... # # Revision 1.6 1997/02/21 09:15:45 conrad # Intermediate status (not yet completed) # # Revision 1.5 1997/02/19 17:46:51 conrad # *** empty log message *** # # Revision 1.4 1997/02/18 18:28:39 conrad # Introduction of tm-include.h # # Revision 1.3 1997/02/18 17:25:56 conrad # *** empty log message *** # # Revision 1.2 1997/02/18 08:49:05 conrad # Not yet tested (only compiled) # # Revision 1.1 1997/02/17 14:11:24 conrad # Initial revision # */ #include "asm.h" #include "cpu.h" #include "MemoryLayout.h" /* Offset values in the ProcContext structure (expressed in bytes) */ #define RETURNVALUE0_OFFSET 0*4 #define RETURNVALUE1_OFFSET 1*4 #define ARGUMENT0_OFFSET 2*4 #define ARGUMENT1_OFFSET 3*4 #define ARGUMENT2_OFFSET 4*4 #define ARGUMENT3_OFFSET 5*4 #define CALLERSAVED0_OFFSET 6*4 #define CALLERSAVED1_OFFSET 7*4 #define CALLERSAVED2_OFFSET 8*4 #define CALLERSAVED3_OFFSET 9*4 #define CALLERSAVED4_OFFSET 10*4 #define CALLERSAVED5_OFFSET 11*4 #define CALLERSAVED6_OFFSET 12*4 #define CALLERSAVED7_OFFSET 13*4 #define CALLERSAVED8_OFFSET 14*4 #define CALLERSAVED9_OFFSET 15*4 #define CALLEESAVED0_OFFSET 16*4 #define CALLEESAVED1_OFFSET 17*4 #define CALLEESAVED2_OFFSET 18*4 #define CALLEESAVED3_OFFSET 19*4 #define CALLEESAVED4_OFFSET 20*4 #define CALLEESAVED5_OFFSET 21*4 #define CALLEESAVED6_OFFSET 22*4 #define CALLEESAVED7_OFFSET 23*4 #define GLOBALPOINTER_OFFSET 24*4 #define STACKPOINTER_OFFSET 25*4 #define FRAMEPOINTER_OFFSET 26*4 #define RETURNADDRESS_OFFSET 27*4 #define PROGRAMCOUNTER_OFFSET 28*4 #define HI_OFFSET 29*4 #define LO_OFFSET 30*4 #define STATUSREGISTER_OFFSET 31*4 #define AT_OFFSET 32*4 /* Offset of the processor context in the Thread structure */ #define THREADCONTEXT_OFFSET 0*4 #define THREADID_OFFSET 1*4 #define SCHEDRUNNING_OFFSET 0*4 #define UTLBMISS_OFFSET 32*4 /* Some constants that are already defined in other places (attention!)... */ #define SYSCALL_SEND_OP 0 /* General constants */ #define BIT_UNSET 0 #define BIT_SET 1 .text .align 2 /* Save context of current thread. All registers but k0, k1, AT and * zero are saved in the ProcContext structure given by the first * argument to saveContext(), thus register a0. */ FRAME(saveContext) .set noat /* Setting of exceptionContextFlag */ la k0, exceptionContextFlag li k1, BIT_SET sw k1, 0(k0) sw a1, ARGUMENT1_OFFSET(a0) sw a2, ARGUMENT2_OFFSET(a0) sw a3, ARGUMENT3_OFFSET(a0) sw v0, RETURNVALUE0_OFFSET(a0) sw v1, RETURNVALUE1_OFFSET(a0) sw AT, AT_OFFSET(a0) sw t0, CALLERSAVED0_OFFSET(a0) sw t1, CALLERSAVED1_OFFSET(a0) sw t2, CALLERSAVED2_OFFSET(a0) sw t3, CALLERSAVED3_OFFSET(a0) sw t4, CALLERSAVED4_OFFSET(a0) sw t5, CALLERSAVED5_OFFSET(a0) sw t6, CALLERSAVED6_OFFSET(a0) sw t7, CALLERSAVED7_OFFSET(a0) sw t8, CALLERSAVED8_OFFSET(a0) sw t9, CALLERSAVED9_OFFSET(a0) sw s0, CALLEESAVED0_OFFSET(a0) sw s1, CALLEESAVED1_OFFSET(a0) sw s2, CALLEESAVED2_OFFSET(a0) sw s3, CALLEESAVED3_OFFSET(a0) sw s4, CALLEESAVED4_OFFSET(a0) sw s5, CALLEESAVED5_OFFSET(a0) sw s6, CALLEESAVED6_OFFSET(a0) sw s7, CALLEESAVED7_OFFSET(a0) sw gp, GLOBALPOINTER_OFFSET(a0) sw fp, FRAMEPOINTER_OFFSET(a0) mfhi k0 sw k0, HI_OFFSET(a0) mflo k0 sw k0, LO_OFFSET(a0) mfc0 k0, c0_status nop sw k0, STATUSREGISTER_OFFSET(a0) la k0, exceptionContext lw k1, 0(k0) /* save user a0 */ nop sw k1, ARGUMENT0_OFFSET(a0) lw k1, 4(k0) /* save user ra */ nop sw k1, RETURNADDRESS_OFFSET(a0) lw k1, 8(k0) /* save user sp */ nop sw k1, STACKPOINTER_OFFSET(a0) lw k1, 12(k0) /* save user pc */ nop sw k1, PROGRAMCOUNTER_OFFSET(a0) .set at END(saveContext) /* Restore context of new thread. All registers but k0, k1 and * zero are restored from the ProcContext structure in the thread * structure reference given by the first argument to restoreContext(), * thus register a0. * This is the R3k version of restoreContext. */ FRAME(restoreContext_R3k) .set noreorder .set noat move k1, a0 lw a0, ARGUMENT0_OFFSET(k1) lw a1, ARGUMENT1_OFFSET(k1) lw a2, ARGUMENT2_OFFSET(k1) lw a3, ARGUMENT3_OFFSET(k1) lw v0, RETURNVALUE0_OFFSET(k1) lw v1, RETURNVALUE1_OFFSET(k1) lw AT, AT_OFFSET(k1) lw t0, CALLERSAVED0_OFFSET(k1) lw t1, CALLERSAVED1_OFFSET(k1) lw t2, CALLERSAVED2_OFFSET(k1) lw t3, CALLERSAVED3_OFFSET(k1) lw t4, CALLERSAVED4_OFFSET(k1) lw t5, CALLERSAVED5_OFFSET(k1) lw t6, CALLERSAVED6_OFFSET(k1) lw t7, CALLERSAVED7_OFFSET(k1) lw t8, CALLERSAVED8_OFFSET(k1) lw t9, CALLERSAVED9_OFFSET(k1) lw s0, CALLEESAVED0_OFFSET(k1) lw s1, CALLEESAVED1_OFFSET(k1) lw s2, CALLEESAVED2_OFFSET(k1) lw s3, CALLEESAVED3_OFFSET(k1) lw s4, CALLEESAVED4_OFFSET(k1) lw s5, CALLEESAVED5_OFFSET(k1) lw s6, CALLEESAVED6_OFFSET(k1) lw s7, CALLEESAVED7_OFFSET(k1) lw gp, GLOBALPOINTER_OFFSET(k1) lw sp, STACKPOINTER_OFFSET(k1) lw fp, FRAMEPOINTER_OFFSET(k1) lw ra, RETURNADDRESS_OFFSET(k1) lw k0, HI_OFFSET(k1) nop mthi k0 lw k0, LO_OFFSET(k1) nop mtlo k0 lw k0, STATUSREGISTER_OFFSET(k1) nop mtc0 k0, c0_status /* Unsetting of exceptionContextFlag */ la k0, exceptionContextFlag sw zero, 0(k0) nop /* Setting of pc */ lw k0, PROGRAMCOUNTER_OFFSET(k1) nop j k0 rfe .set reorder .set at .end restoreContext_R3k /* And now the R4k version of restoreContext */ FRAME(restoreContext_R4k) .set noreorder .set noat move k1, a0 lw a0, ARGUMENT0_OFFSET(k1) lw a1, ARGUMENT1_OFFSET(k1) lw a2, ARGUMENT2_OFFSET(k1) lw a3, ARGUMENT3_OFFSET(k1) lw v0, RETURNVALUE0_OFFSET(k1) lw v1, RETURNVALUE1_OFFSET(k1) lw AT, AT_OFFSET(k1) lw t0, CALLERSAVED0_OFFSET(k1) lw t1, CALLERSAVED1_OFFSET(k1) lw t2, CALLERSAVED2_OFFSET(k1) lw t3, CALLERSAVED3_OFFSET(k1) lw t4, CALLERSAVED4_OFFSET(k1) lw t5, CALLERSAVED5_OFFSET(k1) lw t6, CALLERSAVED6_OFFSET(k1) lw t7, CALLERSAVED7_OFFSET(k1) lw t8, CALLERSAVED8_OFFSET(k1) lw t9, CALLERSAVED9_OFFSET(k1) lw s0, CALLEESAVED0_OFFSET(k1) lw s1, CALLEESAVED1_OFFSET(k1) lw s2, CALLEESAVED2_OFFSET(k1) lw s3, CALLEESAVED3_OFFSET(k1) lw s4, CALLEESAVED4_OFFSET(k1) lw s5, CALLEESAVED5_OFFSET(k1) lw s6, CALLEESAVED6_OFFSET(k1) lw s7, CALLEESAVED7_OFFSET(k1) lw gp, GLOBALPOINTER_OFFSET(k1) lw sp, STACKPOINTER_OFFSET(k1) lw fp, FRAMEPOINTER_OFFSET(k1) lw ra, RETURNADDRESS_OFFSET(k1) lw k0, HI_OFFSET(k1) nop mthi k0 lw k0, LO_OFFSET(k1) nop mtlo k0 lw k0, STATUSREGISTER_OFFSET(k1) nop mtc0 k0, c0_status /* Unsetting of exceptionContextFlag */ la k0, exceptionContextFlag sw zero, 0(k0) nop /* Setting of pc */ lw k0, PROGRAMCOUNTER_OFFSET(k1) nop mtc0 k0, c0_epc nop .set mips3 eret .set mips0 .set reorder .set at .end restoreContext_R4k /* SYSCALL exception handler * msgDispatch() in TMIPC is called with message parameters: * a0: from thread (thread that caused the exception) * a1: message reference * a2: - (tmMsgSend) / timeout (tmMsgRecv) * a3: either SYSCALL_SEND_OP or SYSCALL_RECV_OP * This exception handler is always set per default. */ NESTED(syscallExceptionHandler, 48, ra) /* A SYSCALL exception occurred, this means that the return address * as saved in the exceptionContext must be incremented with 4 * (if not, the SYSCALL will be generated ad aeternam !). */ mfc0 v0, c0_cause /* cause -> v0 */ la k1, scheduler /* scheduler -> k1 */ lw k1, SCHEDRUNNING_OFFSET(k1) /* Ptr to current thread -> k1 */ lw k1, THREADCONTEXT_OFFSET(k1) /* ProcContext -> k1 */ lw k0, PROGRAMCOUNTER_OFFSET(k1) /* saved pc -> k0 */ addu k0, k0, 4 /* k0 incremented */ sw k0, PROGRAMCOUNTER_OFFSET(k1) /* pc finally updated */ and v1, v0, CAUSE_BD /* branch delay bit -> v1 */ bne v1, CAUSE_BD, no_BD jal branchDelayHandler nop j end_syscall no_BD: jal msgDispatcher nop end_syscall: ENDNESTED(syscallExceptionHandler, 48) /* Hardware Interrupt exception handler * No parameters are required. This exception handler is set per default */ NESTED(hwExceptionHandler, 32, ra) mfc0 a0, c0_cause /* cause -> a0 */ nop jal intDispatcher /* decode it in a C function */ mtc0 zero, c0_cause ENDNESTED(hwExceptionHandler, 32) /* * does more or less the same as general exception handler but is called * seperately and does only dispatch the utlb miss exception. */ FRAME(UTLBMissHandler) /* At this point, only SR and PC were modified. * Before calling saveContext(), a0, sp, and ra have to be saved * in a temporary exception context. * NOTE: When an exception occurs, pc is saved in EPC register. * If not in a branch delay, epc points onto 'responsible' * instruction (see Branch Delay Bit in CAUSE register) */ .set noreorder mfc0 k1, c0_epc /* epc -> k1 */ la k0, exceptionContext /* exceptionContext -> k0 */ sw a0, 0(k0) /* user a0 in exceptionContext */ sw ra, 4(k0) /* user ra in exceptionContext */ sw sp, 8(k0) /* user sp in exceptionContext */ sw k1, 12(k0) /* user pc in exceptionContext */ /* sp is now set on the exception stack (BOOTSTACKTOP) */ la sp, -2147479556 /* Calling saveContext() with currentThreadPtr->context in a0 */ la k1, scheduler /* scheduler -> k1 */ lw a0, SCHEDRUNNING_OFFSET(k1) /* ProcContext -> a0 */ jal saveContext lw a0, THREADCONTEXT_OFFSET(a0) /* ProcContext -> a0 */ /* using branch delay slot */ /* Invoking exception handler located at offset UTLBMISS_OFFSET in * exceptionHandlerTable, the current threadId is set in a0 */ la k0, exceptionHandlerTable /* address of exc. table -> k0 */ addu k0, k0, UTLBMISS_OFFSET /* address of corresp. handler->k0 */ lw k0, 0(k0) /* UTLB handler -> k0 */ la k1, scheduler /* address of scheduler -> k1 */ lw a0, SCHEDRUNNING_OFFSET(k1) /* a0 contains ptr to current thread */ /* Jump to exception handler */ jal k0 /* jump to UTLB exc. handler */ lw a0, THREADID_OFFSET(a0) /* a0 contains thread id */ /* Calling restoreContext() with currentThreadPtr->context in a0, * the 'rfe' call is performed in restoreContext(). * Note that the necessary schedule was done by kSend(). */ la k1, scheduler /* scheduler -> k1 */ lw a0, SCHEDRUNNING_OFFSET(k1) /* a0 contains ptr to current thread */ la v0, restoreContext lw v0, 0(v0) lw a0, THREADCONTEXT_OFFSET(a0) /* a0 set to context of current thread */ jr v0 /* never comes back */ nop .end UTLBMissHandler LABEL(endUTLBMissHandler) .set reorder /* This code performs transparently to the user a tmThreadExit() when * the main thread routine returns */ FRAME(automaticThreadExit) move a1, ra la t0, endAutomaticThreadExit la t1, automaticThreadExit subu t1, t0, t1 addu a1, a1, t1 /* Exit message address -> a1 */ li a3, SYSCALL_SEND_OP syscall /* Sending of Exit Message */ END(automaticThreadExit) LABEL(endAutomaticThreadExit) /* General Exception Handler (located at 0x8000_0080) */ FRAME(generalExceptionHandler) /* At this point, only SR and PC were modified. * Before calling saveContext(), a0, sp, and ra have to be saved * in a temporary exception context. * NOTE: When an exception occurs, pc is saved in EPC register. * If not in a branch delay, epc points onto 'responsible' * instruction (see Branch Delay Bit in CAUSE register) */ mfc0 k1, c0_epc /* epc -> k1 */ la k0, exceptionContext /* exceptionContext -> k0 */ sw a0, 0(k0) /* user a0 in exceptionContext */ sw ra, 4(k0) /* user ra in exceptionContext */ sw sp, 8(k0) /* user sp in exceptionContext */ sw k1, 12(k0) /* user pc in exceptionContext */ /* sp is now set on the exception stack (BOOTSTACKTOP) */ la sp, -2147479556 /* Calling saveContext() with currentThreadPtr in a0 */ la k1, scheduler /* scheduler -> k1 */ lw a0, SCHEDRUNNING_OFFSET(k1) /* ProcContext -> a0 */ lw a0, THREADCONTEXT_OFFSET(a0) /* ProcContext -> a0 */ jal saveContext nop /* fp set to sp after fp was saved */ move fp, sp /* terminate stack bottom for gdb */ LABEL(dispatchException) /* cosmetic for gdb, no jumps end here */ /* Exception cause decoding, jump into exceptionHandlerTable */ mfc0 v0, c0_cause /* cause -> v0 */ nop and v1, v0, CAUSE_EXC_MASK /* excCode -> v1 */ la k0, exceptionHandlerTable /* address of exc. table -> k0 */ addu k0, k0, v1 /* address of corresp. handler->k0 */ lw k0, 0(k0) /* handler -> k0 */ /* General exception handler expects threadId as only parameter. * However, tmMsgSend() and tmMsgRecv() require resp. 2 and 3 parameters * (see /Topsy/mips/SyscallMsg.S for the exact parameters of these * system calls). * CONSEQUENCE: exception handler for SYSCALL is always set per default * * Setting threadId of current thread into a0, required by * exception handlers */ la k1, scheduler /* scheduler -> k1 */ lw a0, SCHEDRUNNING_OFFSET(k1) /* a0 contains ptr to current thread */ lw a0, THREADID_OFFSET(a0) /* a0 contains thread id */ /* Jump to exception handler. It is assumed that a return is performed * in each case (thus, jump and link is required) */ jal k0 /* jump to exc. handler */ nop /* Calling restoreContext() with currentThreadPtr->context in a0, * the 'rfe' call is performed in restoreContext() */ la k1, scheduler /* scheduler -> k1 */ lw a0, SCHEDRUNNING_OFFSET(k1) /* a0 contains ptr to current thread */ la v0, restoreContext lw a0, THREADCONTEXT_OFFSET(a0) /* a0 contains context of current thread */ lw v0, 0(v0) jr v0 /* never comes back */ nop END(generalExceptionHandler) LABEL(endGeneralExceptionHandler)