/* $OpenBSD: locore.S,v 1.18 1998/09/15 10:58:53 pefo Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Digital Equipment Corporation and Ralph Campbell. * * 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. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. * * Copyright (C) 1989 Digital Equipment Corporation. * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appears in all copies. * Digital Equipment Corporation makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/loMem.s, * v 1.1 89/07/11 17:55:04 nelson Exp SPRITE (DECWRL) * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/machAsm.s, * v 9.2 90/01/29 18:00:39 shirriff Exp SPRITE (DECWRL) * from: Header: /sprite/src/kernel/vm/ds3100.md/vmPmaxAsm.s, * v 1.1 89/07/10 14:27:41 nelson Exp SPRITE (DECWRL) * from: @(#)locore.s 8.5 (Berkeley) 1/4/94 * JNPR: exception.S,v 1.5 2007/01/08 04:58:37 katta * $FreeBSD$ */ /* * Contains code that is the first executed at boot time plus * assembly language support routines. */ #include "opt_cputype.h" #include "opt_ddb.h" #include #include #include #include #include #include "assym.s" #if defined(ISA_MIPS32) #undef WITH_64BIT_CP0 #elif defined(ISA_MIPS64) #define WITH_64BIT_CP0 #elif defined(ISA_MIPS3) #define WITH_64BIT_CP0 #else #error "Please write the code for this ISA" #endif #ifdef WITH_64BIT_CP0 #define _SLL dsll #define _SRL dsrl #define _MFC0 dmfc0 #define _MTC0 dmtc0 #define WIRED_SHIFT 34 #else #define _SLL sll #define _SRL srl #define _MFC0 mfc0 #define _MTC0 mtc0 #define WIRED_SHIFT 2 #endif .set noreorder # Noreorder is default style! #if defined(ISA_MIPS32) .set mips32 #elif defined(ISA_MIPS64) .set mips64 #elif defined(ISA_MIPS3) .set mips3 #endif /* * Assume that w alaways need nops to escape CP0 hazard * TODO: Make hazard delays configurable. Stuck with 5 cycles on the moment * For more info on CP0 hazards see Chapter 7 (p.99) of "MIPS32 Architecture * For Programmers Volume III: The MIPS32 Privileged Resource Architecture" */ #define ITLBNOPFIX nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; #define HAZARD_DELAY nop;nop;nop;nop;nop; /* *---------------------------------------------------------------------------- * * MipsTLBMiss -- * * Vector code for the TLB-miss exception vector 0x80000000. * * This code is copied to the TLB exception vector address to * which the CPU jumps in response to an exception or a TLB miss. * NOTE: This code must be position independent!!! * * */ .set noat VECTOR(MipsTLBMiss, unknown) j _C_LABEL(MipsDoTLBMiss) mfc0 k0, COP_0_BAD_VADDR # get the fault address nop VECTOR_END(MipsTLBMiss) /* *---------------------------------------------------------------------------- * * MipsDoTLBMiss -- * * This is the real TLB Miss Handler code. * 'segbase' points to the base of the segment table for user processes. * * Don't check for invalid pte's here. We load them as well and * let the processor trap to load the correct value after service. *---------------------------------------------------------------------------- */ MipsDoTLBMiss: #ifndef SMP lui k1, %hi(_C_LABEL(pcpup)) #endif #k0 already has BadVA bltz k0, 1f #02: k0<0 -> 1f (kernel fault) srl k0, k0, SEGSHIFT - 2 #03: k0=seg offset (almost) #ifdef SMP GET_CPU_PCPU(k1) #else lw k1, %lo(_C_LABEL(pcpup))(k1) #endif lw k1, PC_SEGBASE(k1) beqz k1, 2f #05: make sure segbase is not null andi k0, k0, 0x7fc #06: k0=seg offset (mask 0x3) addu k1, k0, k1 #07: k1=seg entry address lw k1, 0(k1) #08: k1=seg entry mfc0 k0, COP_0_BAD_VADDR #09: k0=bad address (again) beq k1, zero, 2f #0a: ==0 -- no page table srl k0, PGSHIFT - 2 #0b: k0=VPN (aka va>>10) andi k0, k0, ((NPTEPG/2) - 1) << 3 #0c: k0=page tab offset addu k1, k1, k0 #0d: k1=pte address lw k0, 0(k1) #0e: k0=lo0 pte lw k1, 4(k1) #0f: k1=lo1 pte _SLL k0, k0, WIRED_SHIFT #10: keep bottom 30 bits _SRL k0, k0, WIRED_SHIFT #11: keep bottom 30 bits _MTC0 k0, COP_0_TLB_LO0 #12: lo0 is loaded _SLL k1, k1, WIRED_SHIFT #13: keep bottom 30 bits _SRL k1, k1, WIRED_SHIFT #14: keep bottom 30 bits _MTC0 k1, COP_0_TLB_LO1 #15: lo1 is loaded HAZARD_DELAY tlbwr #1a: write to tlb HAZARD_DELAY eret #1f: retUrn from exception 1: j _C_LABEL(MipsTLBMissException) #20: kernel exception nop #21: branch delay slot 2: j SlowFault #22: no page table present nop #23: branch delay slot .set at /* * This code is copied to the general exception vector address to * handle all execptions except RESET and TLBMiss. * NOTE: This code must be position independent!!! */ VECTOR(MipsException, unknown) /* * Find out what mode we came from and jump to the proper handler. */ .set noat mfc0 k0, COP_0_STATUS_REG # Get the status register mfc0 k1, COP_0_CAUSE_REG # Get the cause register value. and k0, k0, SR_KSU_USER # test for user mode # sneaky but the bits are # with us........ sll k0, k0, 3 # shift user bit for cause index and k1, k1, CR_EXC_CODE # Mask out the cause bits. or k1, k1, k0 # change index to user table 1: la k0, _C_LABEL(machExceptionTable) # get base of the jump table addu k0, k0, k1 # Get the address of the # function entry. Note that # the cause is already # shifted left by 2 bits so # we dont have to shift. lw k0, 0(k0) # Get the function address nop j k0 # Jump to the function. nop .set at VECTOR_END(MipsException) /* * We couldn't find a TLB entry. * Find out what mode we came from and call the appropriate handler. */ SlowFault: .set noat mfc0 k0, COP_0_STATUS_REG nop and k0, k0, SR_KSU_USER bne k0, zero, _C_LABEL(MipsUserGenException) nop .set at /* * Fall though ... */ /*---------------------------------------------------------------------------- * * MipsKernGenException -- * * Handle an exception from kernel mode. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ #if defined(ISA_MIPS32) #define STORE sw /* 32 bit mode regsave instruction */ #define LOAD lw /* 32 bit mode regload instruction */ #define RSIZE 4 /* 32 bit mode register size */ #elif defined(ISA_MIPS64) #define STORE sd /* 64 bit mode regsave instruction */ #define LOAD ld /* 64 bit mode regload instruction */ #define RSIZE 8 /* 64 bit mode register size */ #else #error "Please write code for this isa." #endif #define SAVE_REG(reg, offs, base) \ STORE reg, STAND_ARG_SIZE + (RSIZE * offs) (base) #ifdef TARGET_OCTEON #define CLEAR_STATUS \ mfc0 a0, COP_0_STATUS_REG ;\ li a2, (MIPS_SR_KX | MIPS_SR_SX | MIPS_SR_UX) ; \ or a0, a0, a2 ; \ li a2, ~(MIPS_SR_INT_IE|MIPS_SR_EXL) ; \ and a0, a0, a2 ; \ mtc0 a0, COP_0_STATUS_REG #else #define CLEAR_STATUS \ mfc0 a0, COP_0_STATUS_REG ;\ li a2, ~(MIPS_SR_INT_IE|MIPS_SR_EXL) ; \ and a0, a0, a2 ; \ mtc0 a0, COP_0_STATUS_REG #endif #define SAVE_CPU \ SAVE_REG(AT, AST, sp) ;\ .set at ; \ SAVE_REG(v0, V0, sp) ;\ SAVE_REG(v1, V1, sp) ;\ SAVE_REG(a0, A0, sp) ;\ SAVE_REG(a1, A1, sp) ;\ SAVE_REG(a2, A2, sp) ;\ SAVE_REG(a3, A3, sp) ;\ SAVE_REG(t0, T0, sp) ;\ SAVE_REG(t1, T1, sp) ;\ SAVE_REG(t2, T2, sp) ;\ SAVE_REG(t3, T3, sp) ;\ SAVE_REG(t4, T4, sp) ;\ SAVE_REG(t5, T5, sp) ;\ SAVE_REG(t6, T6, sp) ;\ SAVE_REG(t7, T7, sp) ;\ SAVE_REG(t8, T8, sp) ;\ SAVE_REG(t9, T9, sp) ;\ SAVE_REG(gp, GP, sp) ;\ SAVE_REG(s0, S0, sp) ;\ SAVE_REG(s1, S1, sp) ;\ SAVE_REG(s2, S2, sp) ;\ SAVE_REG(s3, S3, sp) ;\ SAVE_REG(s4, S4, sp) ;\ SAVE_REG(s5, S5, sp) ;\ SAVE_REG(s6, S6, sp) ;\ SAVE_REG(s7, S7, sp) ;\ SAVE_REG(s8, S8, sp) ;\ mflo v0 ;\ mfhi v1 ;\ mfc0 a0, COP_0_STATUS_REG ;\ mfc0 a1, COP_0_CAUSE_REG ;\ mfc0 a2, COP_0_BAD_VADDR ;\ mfc0 a3, COP_0_EXC_PC ;\ SAVE_REG(v0, MULLO, sp) ;\ SAVE_REG(v1, MULHI, sp) ;\ SAVE_REG(a0, SR, sp) ;\ SAVE_REG(a1, CAUSE, sp) ;\ SAVE_REG(ra, RA, sp) ;\ SAVE_REG(a2, BADVADDR, sp) ;\ SAVE_REG(a3, PC, sp) ;\ addu v0, sp, KERN_EXC_FRAME_SIZE ;\ SAVE_REG(v0, SP, sp) ;\ CLEAR_STATUS ;\ addu a0, sp, STAND_ARG_SIZE ;\ ITLBNOPFIX #define RESTORE_REG(reg, offs, base) \ LOAD reg, STAND_ARG_SIZE + (RSIZE * offs) (base) #define RESTORE_CPU \ mtc0 zero,COP_0_STATUS_REG ;\ RESTORE_REG(a0, SR, sp) ;\ RESTORE_REG(t0, MULLO, sp) ;\ RESTORE_REG(t1, MULHI, sp) ;\ mtc0 a0, COP_0_STATUS_REG ;\ mtlo t0 ;\ mthi t1 ;\ _MTC0 v0, COP_0_EXC_PC ;\ .set noat ; \ RESTORE_REG(AT, AST, sp) ;\ RESTORE_REG(v0, V0, sp) ;\ RESTORE_REG(v1, V1, sp) ;\ RESTORE_REG(a0, A0, sp) ;\ RESTORE_REG(a1, A1, sp) ;\ RESTORE_REG(a2, A2, sp) ;\ RESTORE_REG(a3, A3, sp) ;\ RESTORE_REG(t0, T0, sp) ;\ RESTORE_REG(t1, T1, sp) ;\ RESTORE_REG(t2, T2, sp) ;\ RESTORE_REG(t3, T3, sp) ;\ RESTORE_REG(t4, T4, sp) ;\ RESTORE_REG(t5, T5, sp) ;\ RESTORE_REG(t6, T6, sp) ;\ RESTORE_REG(t7, T7, sp) ;\ RESTORE_REG(t8, T8, sp) ;\ RESTORE_REG(t9, T9, sp) ;\ RESTORE_REG(s0, S0, sp) ;\ RESTORE_REG(s1, S1, sp) ;\ RESTORE_REG(s2, S2, sp) ;\ RESTORE_REG(s3, S3, sp) ;\ RESTORE_REG(s4, S4, sp) ;\ RESTORE_REG(s5, S5, sp) ;\ RESTORE_REG(s6, S6, sp) ;\ RESTORE_REG(s7, S7, sp) ;\ RESTORE_REG(s8, S8, sp) ;\ RESTORE_REG(gp, GP, sp) ;\ RESTORE_REG(ra, RA, sp) ;\ addu sp, sp, KERN_EXC_FRAME_SIZE /* * The kernel exception stack contains 18 saved general registers, * the status register and the multiply lo and high registers. * In addition, we set this up for linkage conventions. */ #define KERN_REG_SIZE (NUMSAVEREGS * RSIZE) #define KERN_EXC_FRAME_SIZE (STAND_FRAME_SIZE + KERN_REG_SIZE + 16) NNON_LEAF(MipsKernGenException, KERN_EXC_FRAME_SIZE, ra) .set noat subu sp, sp, KERN_EXC_FRAME_SIZE .mask 0x80000000, (STAND_RA_OFFSET - KERN_EXC_FRAME_SIZE) /* * Save CPU state, building 'frame'. */ SAVE_CPU /* * Call the exception handler. a0 points at the saved frame. */ la gp, _C_LABEL(_gp) la k0, _C_LABEL(trap) jalr k0 sw a3, STAND_RA_OFFSET + KERN_REG_SIZE(sp) # for debugging RESTORE_CPU # v0 contains the return address. sync eret .set at END(MipsKernGenException) #define SAVE_U_PCB_REG(reg, offs, base) \ STORE reg, U_PCB_REGS + (RSIZE * offs) (base) #define RESTORE_U_PCB_REG(reg, offs, base) \ LOAD reg, U_PCB_REGS + (RSIZE * offs) (base) /*---------------------------------------------------------------------------- * * MipsUserGenException -- * * Handle an exception from user mode. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ NNON_LEAF(MipsUserGenException, STAND_FRAME_SIZE, ra) .set noat .mask 0x80000000, (STAND_RA_OFFSET - STAND_FRAME_SIZE) /* * Save all of the registers except for the kernel temporaries in u.u_pcb. */ GET_CPU_PCPU(k1) lw k1, PC_CURPCB(k1) SAVE_U_PCB_REG(AT, AST, k1) .set at SAVE_U_PCB_REG(v0, V0, k1) SAVE_U_PCB_REG(v1, V1, k1) SAVE_U_PCB_REG(a0, A0, k1) mflo v0 SAVE_U_PCB_REG(a1, A1, k1) SAVE_U_PCB_REG(a2, A2, k1) SAVE_U_PCB_REG(a3, A3, k1) SAVE_U_PCB_REG(t0, T0, k1) mfhi v1 SAVE_U_PCB_REG(t1, T1, k1) SAVE_U_PCB_REG(t2, T2, k1) SAVE_U_PCB_REG(t3, T3, k1) SAVE_U_PCB_REG(t4, T4, k1) mfc0 a0, COP_0_STATUS_REG # First arg is the status reg. SAVE_U_PCB_REG(t5, T5, k1) SAVE_U_PCB_REG(t6, T6, k1) SAVE_U_PCB_REG(t7, T7, k1) SAVE_U_PCB_REG(s0, S0, k1) mfc0 a1, COP_0_CAUSE_REG # Second arg is the cause reg. SAVE_U_PCB_REG(s1, S1, k1) SAVE_U_PCB_REG(s2, S2, k1) SAVE_U_PCB_REG(s3, S3, k1) SAVE_U_PCB_REG(s4, S4, k1) mfc0 a2, COP_0_BAD_VADDR # Third arg is the fault addr SAVE_U_PCB_REG(s5, S5, k1) SAVE_U_PCB_REG(s6, S6, k1) SAVE_U_PCB_REG(s7, S7, k1) SAVE_U_PCB_REG(t8, T8, k1) mfc0 a3, COP_0_EXC_PC # Fourth arg is the pc. SAVE_U_PCB_REG(t9, T9, k1) SAVE_U_PCB_REG(gp, GP, k1) SAVE_U_PCB_REG(sp, SP, k1) SAVE_U_PCB_REG(s8, S8, k1) subu sp, k1, STAND_FRAME_SIZE # switch to kernel SP SAVE_U_PCB_REG(ra, RA, k1) SAVE_U_PCB_REG(v0, MULLO, k1) SAVE_U_PCB_REG(v1, MULHI, k1) SAVE_U_PCB_REG(a0, SR, k1) SAVE_U_PCB_REG(a1, CAUSE, k1) SAVE_U_PCB_REG(a2, BADVADDR, k1) SAVE_U_PCB_REG(a3, PC, k1) sw a3, STAND_RA_OFFSET(sp) # for debugging la gp, _C_LABEL(_gp) # switch to kernel GP # Turn off fpu and enter kernel mode and t0, a0, ~(SR_COP_1_BIT | SR_EXL | SR_KSU_MASK | SR_INT_ENAB) #ifdef TARGET_OCTEON or t0, t0, (MIPS_SR_KX | MIPS_SR_SX | MIPS_SR_UX) #endif mtc0 t0, COP_0_STATUS_REG addu a0, k1, U_PCB_REGS ITLBNOPFIX /* * Call the exception handler. */ la k0, _C_LABEL(trap) jalr k0 nop /* * Restore user registers and return. * First disable interrupts and set exeption level. */ DO_AST mtc0 zero, COP_0_STATUS_REG # disable int ITLBNOPFIX li v0, SR_EXL mtc0 v0, COP_0_STATUS_REG # set exeption level ITLBNOPFIX /* * The use of k1 for storing the PCB pointer must be done only * after interrupts are disabled. Otherwise it will get overwritten * by the interrupt code. */ GET_CPU_PCPU(k1) lw k1, PC_CURPCB(k1) RESTORE_U_PCB_REG(t0, MULLO, k1) RESTORE_U_PCB_REG(t1, MULHI, k1) mtlo t0 mthi t1 RESTORE_U_PCB_REG(a0, PC, k1) RESTORE_U_PCB_REG(v0, V0, k1) _MTC0 a0, COP_0_EXC_PC # set return address RESTORE_U_PCB_REG(v1, V1, k1) RESTORE_U_PCB_REG(a0, A0, k1) RESTORE_U_PCB_REG(a1, A1, k1) RESTORE_U_PCB_REG(a2, A2, k1) RESTORE_U_PCB_REG(a3, A3, k1) RESTORE_U_PCB_REG(t0, T0, k1) RESTORE_U_PCB_REG(t1, T1, k1) RESTORE_U_PCB_REG(t2, T2, k1) RESTORE_U_PCB_REG(t3, T3, k1) RESTORE_U_PCB_REG(t4, T4, k1) RESTORE_U_PCB_REG(t5, T5, k1) RESTORE_U_PCB_REG(t6, T6, k1) RESTORE_U_PCB_REG(t7, T7, k1) RESTORE_U_PCB_REG(s0, S0, k1) RESTORE_U_PCB_REG(s1, S1, k1) RESTORE_U_PCB_REG(s2, S2, k1) RESTORE_U_PCB_REG(s3, S3, k1) RESTORE_U_PCB_REG(s4, S4, k1) RESTORE_U_PCB_REG(s5, S5, k1) RESTORE_U_PCB_REG(s6, S6, k1) RESTORE_U_PCB_REG(s7, S7, k1) RESTORE_U_PCB_REG(t8, T8, k1) RESTORE_U_PCB_REG(t9, T9, k1) RESTORE_U_PCB_REG(gp, GP, k1) RESTORE_U_PCB_REG(sp, SP, k1) RESTORE_U_PCB_REG(k0, SR, k1) RESTORE_U_PCB_REG(s8, S8, k1) RESTORE_U_PCB_REG(ra, RA, k1) #ifdef TARGET_OCTEON and k0, k0, ~(MIPS_SR_KX | MIPS_SR_SX | MIPS_SR_UX) #endif or k0, k0, (MIPS_SR_INT_IE) .set noat RESTORE_U_PCB_REG(AT, AST, k1) /* * The restoration of the user SR must be done only after * k1 is no longer needed. Otherwise, k1 will get clobbered after * interrupts are enabled. */ mtc0 k0, COP_0_STATUS_REG # still exeption level ITLBNOPFIX sync eret .set at END(MipsUserGenException) /*---------------------------------------------------------------------------- * * MipsKernIntr -- * * Handle an interrupt from kernel mode. * Interrupts use the standard kernel stack. * switch_exit sets up a kernel stack after exit so interrupts won't fail. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ NNON_LEAF(MipsKernIntr, KERN_EXC_FRAME_SIZE, ra) .set noat subu sp, sp, KERN_EXC_FRAME_SIZE .mask 0x80000000, (STAND_RA_OFFSET - KERN_EXC_FRAME_SIZE) /* * Save the relevant kernel registers onto the stack. */ SAVE_CPU /* * Call the interrupt handler. */ la gp, _C_LABEL(_gp) addu a0, sp, STAND_ARG_SIZE la k0, _C_LABEL(cpu_intr) jalr k0 sw a3, STAND_RA_OFFSET + KERN_REG_SIZE(sp) /* Why no AST processing here? */ /* * Restore registers and return from the interrupt. */ lw v0, STAND_RA_OFFSET + KERN_REG_SIZE(sp) RESTORE_CPU sync eret .set at END(MipsKernIntr) /*---------------------------------------------------------------------------- * * MipsUserIntr -- * * Handle an interrupt from user mode. * Note: we save minimal state in the u.u_pcb struct and use the standard * kernel stack since there has to be a u page if we came from user mode. * If there is a pending software interrupt, then save the remaining state * and call softintr(). This is all because if we call switch() inside * interrupt(), not all the user registers have been saved in u.u_pcb. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ NNON_LEAF(MipsUserIntr, STAND_FRAME_SIZE, ra) .set noat .mask 0x80000000, (STAND_RA_OFFSET - STAND_FRAME_SIZE) /* * Save the relevant user registers into the u.u_pcb struct. * We don't need to save s0 - s8 because the compiler does it for us. */ GET_CPU_PCPU(k1) lw k1, PC_CURPCB(k1) SAVE_U_PCB_REG(AT, AST, k1) .set at SAVE_U_PCB_REG(v0, V0, k1) SAVE_U_PCB_REG(v1, V1, k1) SAVE_U_PCB_REG(a0, A0, k1) SAVE_U_PCB_REG(a1, A1, k1) SAVE_U_PCB_REG(a2, A2, k1) SAVE_U_PCB_REG(a3, A3, k1) SAVE_U_PCB_REG(t0, T0, k1) SAVE_U_PCB_REG(t1, T1, k1) SAVE_U_PCB_REG(t2, T2, k1) SAVE_U_PCB_REG(t3, T3, k1) SAVE_U_PCB_REG(t4, T4, k1) SAVE_U_PCB_REG(t5, T5, k1) SAVE_U_PCB_REG(t6, T6, k1) SAVE_U_PCB_REG(t7, T7, k1) SAVE_U_PCB_REG(t8, T8, k1) SAVE_U_PCB_REG(t9, T9, k1) SAVE_U_PCB_REG(gp, GP, k1) SAVE_U_PCB_REG(sp, SP, k1) SAVE_U_PCB_REG(ra, RA, k1) /* * save remaining user state in u.u_pcb. */ SAVE_U_PCB_REG(s0, S0, k1) SAVE_U_PCB_REG(s1, S1, k1) SAVE_U_PCB_REG(s2, S2, k1) SAVE_U_PCB_REG(s3, S3, k1) SAVE_U_PCB_REG(s4, S4, k1) SAVE_U_PCB_REG(s5, S5, k1) SAVE_U_PCB_REG(s6, S6, k1) SAVE_U_PCB_REG(s7, S7, k1) SAVE_U_PCB_REG(s8, S8, k1) mflo v0 # get lo/hi late to avoid stall mfhi v1 mfc0 a0, COP_0_STATUS_REG mfc0 a1, COP_0_CAUSE_REG mfc0 a3, COP_0_EXC_PC SAVE_U_PCB_REG(v0, MULLO, k1) SAVE_U_PCB_REG(v1, MULHI, k1) SAVE_U_PCB_REG(a0, SR, k1) SAVE_U_PCB_REG(a1, CAUSE, k1) SAVE_U_PCB_REG(a3, PC, k1) # PC in a3, note used later! subu sp, k1, STAND_FRAME_SIZE # switch to kernel SP la gp, _C_LABEL(_gp) # switch to kernel GP # Turn off fpu, disable interrupts, set kernel mode kernel mode, clear exception level. and t0, a0, ~(SR_COP_1_BIT | SR_EXL | SR_INT_ENAB | SR_KSU_MASK) #ifdef TARGET_OCTEON or t0, t0, (MIPS_SR_KX | MIPS_SR_SX | MIPS_SR_UX) #endif mtc0 t0, COP_0_STATUS_REG ITLBNOPFIX addu a0, k1, U_PCB_REGS /* * Call the interrupt handler. */ la k0, _C_LABEL(cpu_intr) jalr k0 sw a3, STAND_RA_OFFSET(sp) # for debugging /* * Since interrupts are enabled at this point, we use a1 instead of * k0 or k1 to store the PCB pointer. This is because k0 and k1 * are not preserved across interrupts. ** RRS - And how did the * get enabled? cpu_intr clears the cause register but it does * not touch the sr as far as I can see thus intr are still * disabled. */ DO_AST /* * Restore user registers and return. NOTE: interrupts are enabled. */ /* * Since interrupts are enabled at this point, we use a1 instead of * k0 or k1 to store the PCB pointer. This is because k0 and k1 * are not preserved across interrupts. */ mtc0 zero, COP_0_STATUS_REG ITLBNOPFIX li v0, SR_EXL mtc0 v0, COP_0_STATUS_REG # set exeption level bit. ITLBNOPFIX GET_CPU_PCPU(k1) lw a1, PC_CURPCB(k1) RESTORE_U_PCB_REG(s0, S0, k1) RESTORE_U_PCB_REG(s1, S1, k1) RESTORE_U_PCB_REG(s2, S2, k1) RESTORE_U_PCB_REG(s3, S3, k1) RESTORE_U_PCB_REG(s4, S4, k1) RESTORE_U_PCB_REG(s5, S5, k1) RESTORE_U_PCB_REG(s6, S6, k1) RESTORE_U_PCB_REG(s7, S7, k1) RESTORE_U_PCB_REG(s8, S8, k1) RESTORE_U_PCB_REG(t0, MULLO, k1) RESTORE_U_PCB_REG(t1, MULHI, k1) RESTORE_U_PCB_REG(t2, PC, k1) mtlo t0 mthi t1 _MTC0 t2, COP_0_EXC_PC # set return address RESTORE_U_PCB_REG(v0, V0, k1) RESTORE_U_PCB_REG(v1, V1, k1) RESTORE_U_PCB_REG(a0, A0, k1) RESTORE_U_PCB_REG(a1, A1, k1) RESTORE_U_PCB_REG(a2, A2, k1) RESTORE_U_PCB_REG(a3, A3, k1) RESTORE_U_PCB_REG(t0, T0, k1) RESTORE_U_PCB_REG(t1, T1, k1) RESTORE_U_PCB_REG(t2, T2, k1) RESTORE_U_PCB_REG(t3, T3, k1) RESTORE_U_PCB_REG(t4, T4, k1) RESTORE_U_PCB_REG(t5, T5, k1) RESTORE_U_PCB_REG(t6, T6, k1) RESTORE_U_PCB_REG(t7, T7, k1) RESTORE_U_PCB_REG(t8, T8, k1) RESTORE_U_PCB_REG(t9, T9, k1) RESTORE_U_PCB_REG(gp, GP, k1) RESTORE_U_PCB_REG(k0, SR, k1) RESTORE_U_PCB_REG(sp, SP, k1) RESTORE_U_PCB_REG(ra, RA, k1) #ifdef TARGET_OCTEON and k0, k0, ~(MIPS_SR_KX | MIPS_SR_SX | MIPS_SR_UX) #endif or k0, k0, (MIPS_SR_INT_IE|SR_EXL) .set noat RESTORE_U_PCB_REG(AT, AST, k1) /* * The restoration of the user SR must be done only after * k1 is no longer needed. Otherwise, k1 will get clobbered after * interrupts are enabled. */ mtc0 k0, COP_0_STATUS_REG # SR with EXL set. ITLBNOPFIX sync eret .set at END(MipsUserIntr) /*---------------------------------------------------------------------------- * * MipsTLBInvalidException -- * * Handle a TLB invalid exception. * The BaddVAddr, Context, and EntryHi registers contain the failed * virtual address. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ NLEAF(MipsTLBInvalidException) .set noat mfc0 k0, COP_0_STATUS_REG nop and k0, k0, SR_KSU_USER bne k0, zero, _C_LABEL(MipsUserTLBInvalidException) nop .set at END(MipsTLBInvalidException) /* * Fall through ... */ NLEAF(MipsKernTLBInvalidException) .set noat mfc0 k0, COP_0_BAD_VADDR # get the fault address li k1, VM_MAXUSER_ADDRESS sltu k1, k0, k1 beqz k1, 1f nop #ifdef SMP GET_CPU_PCPU(k1) #else lui k1, %hi(_C_LABEL(pcpup)) lw k1, %lo(_C_LABEL(pcpup))(k1) #endif lw k1, PC_SEGBASE(k1) # works for single cpu???? beqz k1, _C_LABEL(MipsKernGenException) # seg tab is null nop b 2f nop 1: li k1, (VM_MAX_KERNEL_ADDRESS) bgez k0, _C_LABEL(MipsKernGenException) # full trap processing sltu k1, k1, k0 # check fault address against bnez k1, _C_LABEL(MipsKernGenException) # kernel_segmap upper bound lui k1, %hi(_C_LABEL(kernel_segmap)) # k1=hi of segbase lw k1, %lo(_C_LABEL(kernel_segmap))(k1) # k1=segment tab base beqz k1, _C_LABEL(MipsKernGenException) # seg tab is null 2: srl k0, 20 # k0=seg offset (almost) andi k0, k0, 0xffc # k0=seg offset (mask 0x3) addu k1, k0, k1 # k1=seg entry address lw k1, 0(k1) # k1=seg entry mfc0 k0, COP_0_BAD_VADDR # k0=bad address (again) beq k1, zero, _C_LABEL(MipsKernGenException) # ==0 -- no page table srl k0, k0, PGSHIFT-2 andi k0, k0, 0xffc # compute offset from index tlbp # Probe the invalid entry addu k1, k1, k0 and k0, k0, 4 # check even/odd page nop # required for QED 5230 bne k0, zero, KernTLBIOdd nop mfc0 k0, COP_0_TLB_INDEX nop bltz k0, sys_stk_chk sltiu k0, k0, VMWIRED_ENTRIES # index below wired entries? bne k0, zero, sys_stk_chk lw k0, 0(k1) # get PTE entry _SLL k0, k0, WIRED_SHIFT # get rid of "wired" bit _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO0 # load PTE entry and k0, k0, PTE_V # check for valid entry nop # required for QED5230 beq k0, zero, _C_LABEL(MipsKernGenException) # PTE invalid lw k0, 4(k1) # get odd PTE entry _SLL k0, k0, WIRED_SHIFT _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO1 # load PTE entry HAZARD_DELAY tlbwi # write TLB HAZARD_DELAY eret KernTLBIOdd: mfc0 k0, COP_0_TLB_INDEX nop bltz k0, sys_stk_chk sltiu k0, k0, VMWIRED_ENTRIES # index below wired entries? bne k0, zero, sys_stk_chk lw k0, 0(k1) # get PTE entry _SLL k0, k0, WIRED_SHIFT # get rid of wired bit _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO1 # save PTE entry and k0, k0, PTE_V # check for valid entry nop # required for QED5230 beq k0, zero, _C_LABEL(MipsKernGenException) # PTE invalid lw k0, -4(k1) # get even PTE entry _SLL k0, k0, WIRED_SHIFT _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO0 # save PTE entry HAZARD_DELAY tlbwi # update TLB HAZARD_DELAY eret .set at END(MipsKernTLBInvalidException) NLEAF(MipsUserTLBInvalidException) .set noat mfc0 k0, COP_0_BAD_VADDR # get the fault address li k1, VM_MAXUSER_ADDRESS sltu k1, k0, k1 beqz k1, _C_LABEL(MipsUserGenException) nop #ifdef SMP GET_CPU_PCPU(k1) #else lui k1, %hi(_C_LABEL(pcpup)) lw k1, %lo(_C_LABEL(pcpup))(k1) #endif lw k1, PC_SEGBASE(k1) # works for single cpu???? beqz k1, _C_LABEL(MipsUserGenException) # seg tab is null nop 2: srl k0, 20 # k0=seg offset (almost) andi k0, k0, 0xffc # k0=seg offset (mask 0x3) addu k1, k0, k1 # k1=seg entry address lw k1, 0(k1) # k1=seg entry mfc0 k0, COP_0_BAD_VADDR # k0=bad address (again) beq k1, zero, _C_LABEL(MipsUserGenException) # ==0 -- no page table srl k0, k0, PGSHIFT-2 andi k0, k0, 0xffc # compute offset from index tlbp # Probe the invalid entry addu k1, k1, k0 and k0, k0, 4 # check even/odd page nop # required for QED 5230 bne k0, zero, UserTLBIOdd nop mfc0 k0, COP_0_TLB_INDEX nop bltz k0, _C_LABEL(MipsUserGenException) sltiu k0, k0, VMWIRED_ENTRIES # index below wired entries? bne k0, zero, _C_LABEL(MipsUserGenException) lw k0, 0(k1) # get PTE entry _SLL k0, k0, WIRED_SHIFT # get rid of "wired" bit _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO0 # load PTE entry and k0, k0, PTE_V # check for valid entry nop # required for QED5230 beq k0, zero, _C_LABEL(MipsUserGenException) # PTE invalid lw k0, 4(k1) # get odd PTE entry _SLL k0, k0, WIRED_SHIFT _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO1 # load PTE entry HAZARD_DELAY tlbwi # write TLB HAZARD_DELAY eret UserTLBIOdd: mfc0 k0, COP_0_TLB_INDEX nop bltz k0, _C_LABEL(MipsUserGenException) sltiu k0, k0, VMWIRED_ENTRIES # index below wired entries? bne k0, zero, _C_LABEL(MipsUserGenException) lw k0, 0(k1) # get PTE entry _SLL k0, k0, WIRED_SHIFT # get rid of wired bit _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO1 # save PTE entry and k0, k0, PTE_V # check for valid entry nop # required for QED5230 beq k0, zero, _C_LABEL(MipsUserGenException) # PTE invalid lw k0, -4(k1) # get even PTE entry _SLL k0, k0, WIRED_SHIFT _SRL k0, k0, WIRED_SHIFT _MTC0 k0, COP_0_TLB_LO0 # save PTE entry HAZARD_DELAY tlbwi # update TLB HAZARD_DELAY eret .set at END(MipsUserTLBInvalidException) /*---------------------------------------------------------------------------- * * MipsTLBMissException -- * * Handle a TLB miss exception from kernel mode in kernel space. * The BaddVAddr, Context, and EntryHi registers contain the failed * virtual address. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ NLEAF(MipsTLBMissException) .set noat mfc0 k0, COP_0_BAD_VADDR # k0=bad address li k1, (VM_MAX_KERNEL_ADDRESS) # check fault address against sltu k1, k1, k0 # upper bound of kernel_segmap bnez k1, _C_LABEL(MipsKernGenException) # out of bound lui k1, %hi(_C_LABEL(kernel_segmap)) # k1=hi of segbase srl k0, 20 # k0=seg offset (almost) lw k1, %lo(_C_LABEL(kernel_segmap))(k1) # k1=segment tab base beq k1, zero, _C_LABEL(MipsKernGenException) # ==0 -- no seg tab andi k0, k0, 0xffc # k0=seg offset (mask 0x3) addu k1, k0, k1 # k1=seg entry address lw k1, 0(k1) # k1=seg entry mfc0 k0, COP_0_BAD_VADDR # k0=bad address (again) beq k1, zero, _C_LABEL(MipsKernGenException) # ==0 -- no page table srl k0, 10 # k0=VPN (aka va>>10) andi k0, k0, 0xff8 # k0=page tab offset addu k1, k1, k0 # k1=pte address lw k0, 0(k1) # k0=lo0 pte lw k1, 4(k1) # k1=lo1 pte _SLL k0, WIRED_SHIFT # chop bits [31..30] _SRL k0, WIRED_SHIFT # chop bits [31..30] _MTC0 k0, COP_0_TLB_LO0 # lo0 is loaded _SLL k1, WIRED_SHIFT # chop bits [31..30] _SRL k1, WIRED_SHIFT # chop bits [31..30] _MTC0 k1, COP_0_TLB_LO1 # lo1 is loaded HAZARD_DELAY tlbwr # write to tlb HAZARD_DELAY eret # return from exception sys_stk_chk: GET_CPU_PCPU(k0) lw k0, PC_CURTHREAD(k0) lw k0, TD_REALKSTACK(k0) sltu k0, sp, k0 # check for stack overflow beqz k0, _C_LABEL(MipsKernGenException) # not stack overflow nop # stack overflow la a0, _C_LABEL(_start) - START_FRAME - 8 # set sp to a valid place sw sp, 24(a0) move sp, a0 la a0, 1f mfc0 a2, COP_0_STATUS_REG mfc0 a3, COP_0_CAUSE_REG _MFC0 a1, COP_0_EXC_PC sw a2, 16(sp) sw a3, 20(sp) move a2, ra la k0, _C_LABEL(printf) jalr k0 mfc0 a3, COP_0_BAD_VADDR la sp, _C_LABEL(_start) - START_FRAME # set sp to a valid place #if !defined(SMP) && defined(DDB) la a0, 2f la k0, _C_LABEL(trapDump) jalr k0 nop li a0, 0 lw a1, _C_LABEL(num_tlbentries) la k0, _C_LABEL(db_dump_tlb) jalr k0 addu a1, -1 3: b 3b nop #endif PANIC("kernel stack overflow") .data .globl lastktlbmiss lastktlbmiss: .word 0 lastktlbmisspc: .word 0 lastutlbmiss: .word 0 lastutlbmisspc: .word 0 1: .asciiz "ktlbmiss: PC %x RA %x ADR %x\nSR %x CR %x SP %x\n" 2: .asciiz "stack ovf" .text .set at END(MipsTLBMissException) /*---------------------------------------------------------------------------- * * MipsFPTrap -- * * Handle a floating point Trap. * * MipsFPTrap(statusReg, causeReg, pc) * unsigned statusReg; * unsigned causeReg; * unsigned pc; * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ NON_LEAF(MipsFPTrap, STAND_FRAME_SIZE, ra) subu sp, sp, STAND_FRAME_SIZE mfc0 t0, COP_0_STATUS_REG sw ra, STAND_RA_OFFSET(sp) .mask 0x80000000, (STAND_RA_OFFSET - STAND_FRAME_SIZE) or t1, t0, SR_COP_1_BIT mtc0 t1, COP_0_STATUS_REG ITLBNOPFIX cfc1 t1, FPC_CSR # stall til FP done cfc1 t1, FPC_CSR # now get status nop sll t2, t1, (31 - 17) # unimplemented operation? bgez t2, 3f # no, normal trap nop /* * We got an unimplemented operation trap so * fetch the instruction, compute the next PC and emulate the instruction. */ bgez a1, 1f # Check the branch delay bit. nop /* * The instruction is in the branch delay slot so the branch will have to * be emulated to get the resulting PC. */ sw a2, STAND_FRAME_SIZE + 8(sp) GET_CPU_PCPU(a0) lw a0, PC_CURPCB(a0) addu a0, a0, U_PCB_REGS # first arg is ptr to CPU registers move a1, a2 # second arg is instruction PC move a2, t1 # third arg is floating point CSR la t3, _C_LABEL(MipsEmulateBranch) # compute PC after branch jalr t3 # compute PC after branch move a3, zero # fourth arg is FALSE /* * Now load the floating-point instruction in the branch delay slot * to be emulated. */ lw a2, STAND_FRAME_SIZE + 8(sp) # restore EXC pc b 2f lw a0, 4(a2) # a0 = coproc instruction /* * This is not in the branch delay slot so calculate the resulting * PC (epc + 4) into v0 and continue to MipsEmulateFP(). */ 1: lw a0, 0(a2) # a0 = coproc instruction addu v0, a2, 4 # v0 = next pc 2: GET_CPU_PCPU(t2) lw t2, PC_CURPCB(t2) SAVE_U_PCB_REG(v0, PC, t2) # save new pc /* * Check to see if the instruction to be emulated is a floating-point * instruction. */ srl a3, a0, OPCODE_SHIFT beq a3, OPCODE_C1, 4f # this should never fail nop /* * Send a floating point exception signal to the current process. */ 3: GET_CPU_PCPU(a0) lw a0, PC_CURTHREAD(a0) # get current thread cfc1 a2, FPC_CSR # code = FP execptions ctc1 zero, FPC_CSR # Clear exceptions la t3, _C_LABEL(trapsignal) jalr t3 li a1, SIGFPE b FPReturn nop /* * Finally, we can call MipsEmulateFP() where a0 is the instruction to emulate. */ 4: la t3, _C_LABEL(MipsEmulateFP) jalr t3 nop /* * Turn off the floating point coprocessor and return. */ FPReturn: mfc0 t0, COP_0_STATUS_REG lw ra, STAND_RA_OFFSET(sp) and t0, t0, ~SR_COP_1_BIT mtc0 t0, COP_0_STATUS_REG ITLBNOPFIX j ra addu sp, sp, STAND_FRAME_SIZE END(MipsFPTrap) #if 0 /* * Atomic ipending update */ LEAF(set_sint) la v1, ipending 1: ll v0, 0(v1) or v0, a0 sc v0, 0(v1) beqz v0, 1b j ra nop END(set_sint) #endif /* * Interrupt counters for vmstat. */ .data .globl intrcnt .globl eintrcnt .globl intrnames .globl eintrnames intrnames: .asciiz "clock" .asciiz "rtc" .asciiz "sio" .asciiz "pe" .asciiz "pic-nic" eintrnames: .align 2 intrcnt: .word 0,0,0,0,0 eintrcnt: /* * Vector to real handler in KSEG1. */ .text VECTOR(MipsCache, unknown) la k0, _C_LABEL(MipsCacheException) li k1, MIPS_PHYS_MASK and k0, k1 li k1, MIPS_UNCACHED_MEMORY_ADDR or k0, k1 j k0 nop VECTOR_END(MipsCache) .set at /* * Panic on cache errors. A lot more could be done to recover * from some types of errors but it is tricky. */ NESTED_NOPROFILE(MipsCacheException, KERN_EXC_FRAME_SIZE, ra) .set noat .mask 0x80000000, -4 la k0, _C_LABEL(panic) # return to panic la a0, 9f # panicstr _MFC0 a1, COP_0_ERROR_PC mfc0 a2, COP_0_CACHE_ERR # 3rd arg cache error _MTC0 k0, COP_0_ERROR_PC # set return address mfc0 k0, COP_0_STATUS_REG # restore status li k1, SR_DIAG_DE # ignore further errors or k0, k1 mtc0 k0, COP_0_STATUS_REG # restore status HAZARD_DELAY eret MSG("cache error @ EPC 0x%x CachErr 0x%x"); .set at END(MipsCacheException)