/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #if !defined(lint) #include "assym.h" #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * WARNING: If you add a fast trap handler which can be invoked by a * non-privileged user, you may have to use the FAST_TRAP_DONE macro * instead of "done" instruction to return back to the user mode. See * comments for the "fast_trap_done" entry point for more information. * * An alternate FAST_TRAP_DONE_CHK_INTR macro should be used for the * cases where you always want to process any pending interrupts before * returning back to the user mode. */ #define FAST_TRAP_DONE \ ba,a fast_trap_done #define FAST_TRAP_DONE_CHK_INTR \ ba,a fast_trap_done_chk_intr /* * SPARC V9 Trap Table * * Most of the trap handlers are made from common building * blocks, and some are instantiated multiple times within * the trap table. So, I build a bunch of macros, then * populate the table using only the macros. * * Many macros branch to sys_trap. Its calling convention is: * %g1 kernel trap handler * %g2, %g3 args for above * %g4 desire %pil */ #ifdef TRAPTRACE /* * Tracing macro. Adds two instructions if TRAPTRACE is defined. */ #define TT_TRACE(label) \ ba label ;\ rd %pc, %g7 #define TT_TRACE_INS 2 #define TT_TRACE_L(label) \ ba label ;\ rd %pc, %l4 ;\ clr %l4 #define TT_TRACE_L_INS 3 #else #define TT_TRACE(label) #define TT_TRACE_INS 0 #define TT_TRACE_L(label) #define TT_TRACE_L_INS 0 #endif /* * This first set are funneled to trap() with %tt as the type. * Trap will then either panic or send the user a signal. */ /* * NOT is used for traps that just shouldn't happen. * It comes in both single and quadruple flavors. */ #if !defined(lint) .global trap #endif /* !lint */ #define NOT \ TT_TRACE(trace_gen) ;\ set trap, %g1 ;\ rdpr %tt, %g3 ;\ ba,pt %xcc, sys_trap ;\ sub %g0, 1, %g4 ;\ .align 32 #define NOT4 NOT; NOT; NOT; NOT #define NOTP \ TT_TRACE(trace_gen) ;\ ba,pt %xcc, ptl1_panic ;\ mov PTL1_BAD_TRAP, %g1 ;\ .align 32 #define NOTP4 NOTP; NOTP; NOTP; NOTP /* * BAD is used for trap vectors we don't have a kernel * handler for. * It also comes in single and quadruple versions. */ #define BAD NOT #define BAD4 NOT4 #define DONE \ done; \ .align 32 /* * TRAP vectors to the trap() function. * It's main use is for user errors. */ #if !defined(lint) .global trap #endif /* !lint */ #define TRAP(arg) \ TT_TRACE(trace_gen) ;\ set trap, %g1 ;\ mov arg, %g3 ;\ ba,pt %xcc, sys_trap ;\ sub %g0, 1, %g4 ;\ .align 32 /* * SYSCALL is used for unsupported syscall interfaces (with 'which' * set to 'nosys') and legacy support of old SunOS 4.x syscalls (with * 'which' set to 'syscall_trap32'). * * The SYSCALL_TRAP* macros are used for syscall entry points. * SYSCALL_TRAP is used to support LP64 syscalls and SYSCALL_TRAP32 * is used to support ILP32. Each macro can only be used once * since they each define a symbol. The symbols are used as hot patch * points by the brand infrastructure to dynamically enable and disable * brand syscall interposition. See the comments around BRAND_CALLBACK * and brand_plat_interposition_enable() for more information. */ #define SYSCALL_NOTT(which) \ set (which), %g1 ;\ ba,pt %xcc, user_trap ;\ sub %g0, 1, %g4 ;\ .align 32 #define SYSCALL(which) \ TT_TRACE(trace_gen) ;\ SYSCALL_NOTT(which) #define SYSCALL_TRAP32 \ TT_TRACE(trace_gen) ;\ ALTENTRY(syscall_trap32_patch_point) \ SYSCALL_NOTT(syscall_trap32) #define SYSCALL_TRAP \ TT_TRACE(trace_gen) ;\ ALTENTRY(syscall_trap_patch_point) \ SYSCALL_NOTT(syscall_trap) /* * GOTO just jumps to a label. * It's used for things that can be fixed without going thru sys_trap. */ #define GOTO(label) \ .global label ;\ ba,a label ;\ .empty ;\ .align 32 /* * GOTO_TT just jumps to a label. * correctable ECC error traps at level 0 and 1 will use this macro. * It's used for things that can be fixed without going thru sys_trap. */ #define GOTO_TT(label, ttlabel) \ .global label ;\ TT_TRACE(ttlabel) ;\ ba,a label ;\ .empty ;\ .align 32 /* * Privileged traps * Takes breakpoint if privileged, calls trap() if not. */ #define PRIV(label) \ rdpr %tstate, %g1 ;\ btst TSTATE_PRIV, %g1 ;\ bnz label ;\ rdpr %tt, %g3 ;\ set trap, %g1 ;\ ba,pt %xcc, sys_trap ;\ sub %g0, 1, %g4 ;\ .align 32 /* * DTrace traps. */ #define DTRACE_PID \ .global dtrace_pid_probe ;\ set dtrace_pid_probe, %g1 ;\ ba,pt %xcc, user_trap ;\ sub %g0, 1, %g4 ;\ .align 32 #define DTRACE_RETURN \ .global dtrace_return_probe ;\ set dtrace_return_probe, %g1 ;\ ba,pt %xcc, user_trap ;\ sub %g0, 1, %g4 ;\ .align 32 /* * REGISTER WINDOW MANAGEMENT MACROS */ /* * various convenient units of padding */ #define SKIP(n) .skip 4*(n) /* * CLEAN_WINDOW is the simple handler for cleaning a register window. */ #define CLEAN_WINDOW \ TT_TRACE_L(trace_win) ;\ rdpr %cleanwin, %l0; inc %l0; wrpr %l0, %cleanwin ;\ clr %l0; clr %l1; clr %l2; clr %l3 ;\ clr %l4; clr %l5; clr %l6; clr %l7 ;\ clr %o0; clr %o1; clr %o2; clr %o3 ;\ clr %o4; clr %o5; clr %o6; clr %o7 ;\ retry; .align 128 #if !defined(lint) /* * If we get an unresolved tlb miss while in a window handler, the fault * handler will resume execution at the last instruction of the window * hander, instead of delivering the fault to the kernel. Spill handlers * use this to spill windows into the wbuf. * * The mixed handler works by checking %sp, and branching to the correct * handler. This is done by branching back to label 1: for 32b frames, * or label 2: for 64b frames; which implies the handler order is: 32b, * 64b, mixed. The 1: and 2: labels are offset into the routines to * allow the branchs' delay slots to contain useful instructions. */ /* * SPILL_32bit spills a 32-bit-wide kernel register window. It * assumes that the kernel context and the nucleus context are the * same. The stack pointer is required to be eight-byte aligned even * though this code only needs it to be four-byte aligned. */ #define SPILL_32bit(tail) \ srl %sp, 0, %sp ;\ 1: st %l0, [%sp + 0] ;\ st %l1, [%sp + 4] ;\ st %l2, [%sp + 8] ;\ st %l3, [%sp + 12] ;\ st %l4, [%sp + 16] ;\ st %l5, [%sp + 20] ;\ st %l6, [%sp + 24] ;\ st %l7, [%sp + 28] ;\ st %i0, [%sp + 32] ;\ st %i1, [%sp + 36] ;\ st %i2, [%sp + 40] ;\ st %i3, [%sp + 44] ;\ st %i4, [%sp + 48] ;\ st %i5, [%sp + 52] ;\ st %i6, [%sp + 56] ;\ st %i7, [%sp + 60] ;\ TT_TRACE_L(trace_win) ;\ saved ;\ retry ;\ SKIP(31-19-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_32bit_/**/tail ;\ .empty /* * SPILL_32bit_asi spills a 32-bit-wide register window into a 32-bit * wide address space via the designated asi. It is used to spill * non-kernel windows. The stack pointer is required to be eight-byte * aligned even though this code only needs it to be four-byte * aligned. */ #define SPILL_32bit_asi(asi_num, tail) \ srl %sp, 0, %sp ;\ 1: sta %l0, [%sp + %g0]asi_num ;\ mov 4, %g1 ;\ sta %l1, [%sp + %g1]asi_num ;\ mov 8, %g2 ;\ sta %l2, [%sp + %g2]asi_num ;\ mov 12, %g3 ;\ sta %l3, [%sp + %g3]asi_num ;\ add %sp, 16, %g4 ;\ sta %l4, [%g4 + %g0]asi_num ;\ sta %l5, [%g4 + %g1]asi_num ;\ sta %l6, [%g4 + %g2]asi_num ;\ sta %l7, [%g4 + %g3]asi_num ;\ add %g4, 16, %g4 ;\ sta %i0, [%g4 + %g0]asi_num ;\ sta %i1, [%g4 + %g1]asi_num ;\ sta %i2, [%g4 + %g2]asi_num ;\ sta %i3, [%g4 + %g3]asi_num ;\ add %g4, 16, %g4 ;\ sta %i4, [%g4 + %g0]asi_num ;\ sta %i5, [%g4 + %g1]asi_num ;\ sta %i6, [%g4 + %g2]asi_num ;\ sta %i7, [%g4 + %g3]asi_num ;\ TT_TRACE_L(trace_win) ;\ saved ;\ retry ;\ SKIP(31-25-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_32bit_/**/tail ;\ .empty #define SPILL_32bit_tt1(asi_num, tail) \ ba,a,pt %xcc, fault_32bit_/**/tail ;\ .empty ;\ .align 128 /* * FILL_32bit fills a 32-bit-wide kernel register window. It assumes * that the kernel context and the nucleus context are the same. The * stack pointer is required to be eight-byte aligned even though this * code only needs it to be four-byte aligned. */ #define FILL_32bit(tail) \ srl %sp, 0, %sp ;\ 1: TT_TRACE_L(trace_win) ;\ ld [%sp + 0], %l0 ;\ ld [%sp + 4], %l1 ;\ ld [%sp + 8], %l2 ;\ ld [%sp + 12], %l3 ;\ ld [%sp + 16], %l4 ;\ ld [%sp + 20], %l5 ;\ ld [%sp + 24], %l6 ;\ ld [%sp + 28], %l7 ;\ ld [%sp + 32], %i0 ;\ ld [%sp + 36], %i1 ;\ ld [%sp + 40], %i2 ;\ ld [%sp + 44], %i3 ;\ ld [%sp + 48], %i4 ;\ ld [%sp + 52], %i5 ;\ ld [%sp + 56], %i6 ;\ ld [%sp + 60], %i7 ;\ restored ;\ retry ;\ SKIP(31-19-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_32bit_/**/tail ;\ .empty /* * FILL_32bit_asi fills a 32-bit-wide register window from a 32-bit * wide address space via the designated asi. It is used to fill * non-kernel windows. The stack pointer is required to be eight-byte * aligned even though this code only needs it to be four-byte * aligned. */ #define FILL_32bit_asi(asi_num, tail) \ srl %sp, 0, %sp ;\ 1: TT_TRACE_L(trace_win) ;\ mov 4, %g1 ;\ lda [%sp + %g0]asi_num, %l0 ;\ mov 8, %g2 ;\ lda [%sp + %g1]asi_num, %l1 ;\ mov 12, %g3 ;\ lda [%sp + %g2]asi_num, %l2 ;\ lda [%sp + %g3]asi_num, %l3 ;\ add %sp, 16, %g4 ;\ lda [%g4 + %g0]asi_num, %l4 ;\ lda [%g4 + %g1]asi_num, %l5 ;\ lda [%g4 + %g2]asi_num, %l6 ;\ lda [%g4 + %g3]asi_num, %l7 ;\ add %g4, 16, %g4 ;\ lda [%g4 + %g0]asi_num, %i0 ;\ lda [%g4 + %g1]asi_num, %i1 ;\ lda [%g4 + %g2]asi_num, %i2 ;\ lda [%g4 + %g3]asi_num, %i3 ;\ add %g4, 16, %g4 ;\ lda [%g4 + %g0]asi_num, %i4 ;\ lda [%g4 + %g1]asi_num, %i5 ;\ lda [%g4 + %g2]asi_num, %i6 ;\ lda [%g4 + %g3]asi_num, %i7 ;\ restored ;\ retry ;\ SKIP(31-25-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_32bit_/**/tail ;\ .empty /* * SPILL_64bit spills a 64-bit-wide kernel register window. It * assumes that the kernel context and the nucleus context are the * same. The stack pointer is required to be eight-byte aligned. */ #define SPILL_64bit(tail) \ 2: stx %l0, [%sp + V9BIAS64 + 0] ;\ stx %l1, [%sp + V9BIAS64 + 8] ;\ stx %l2, [%sp + V9BIAS64 + 16] ;\ stx %l3, [%sp + V9BIAS64 + 24] ;\ stx %l4, [%sp + V9BIAS64 + 32] ;\ stx %l5, [%sp + V9BIAS64 + 40] ;\ stx %l6, [%sp + V9BIAS64 + 48] ;\ stx %l7, [%sp + V9BIAS64 + 56] ;\ stx %i0, [%sp + V9BIAS64 + 64] ;\ stx %i1, [%sp + V9BIAS64 + 72] ;\ stx %i2, [%sp + V9BIAS64 + 80] ;\ stx %i3, [%sp + V9BIAS64 + 88] ;\ stx %i4, [%sp + V9BIAS64 + 96] ;\ stx %i5, [%sp + V9BIAS64 + 104] ;\ stx %i6, [%sp + V9BIAS64 + 112] ;\ stx %i7, [%sp + V9BIAS64 + 120] ;\ TT_TRACE_L(trace_win) ;\ saved ;\ retry ;\ SKIP(31-18-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty #define SPILL_64bit_ktt1(tail) \ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty ;\ .align 128 #define SPILL_mixed_ktt1(tail) \ btst 1, %sp ;\ bz,a,pt %xcc, fault_32bit_/**/tail ;\ srl %sp, 0, %sp ;\ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty ;\ .align 128 /* * SPILL_64bit_asi spills a 64-bit-wide register window into a 64-bit * wide address space via the designated asi. It is used to spill * non-kernel windows. The stack pointer is required to be eight-byte * aligned. */ #define SPILL_64bit_asi(asi_num, tail) \ mov 0 + V9BIAS64, %g1 ;\ 2: stxa %l0, [%sp + %g1]asi_num ;\ mov 8 + V9BIAS64, %g2 ;\ stxa %l1, [%sp + %g2]asi_num ;\ mov 16 + V9BIAS64, %g3 ;\ stxa %l2, [%sp + %g3]asi_num ;\ mov 24 + V9BIAS64, %g4 ;\ stxa %l3, [%sp + %g4]asi_num ;\ add %sp, 32, %g5 ;\ stxa %l4, [%g5 + %g1]asi_num ;\ stxa %l5, [%g5 + %g2]asi_num ;\ stxa %l6, [%g5 + %g3]asi_num ;\ stxa %l7, [%g5 + %g4]asi_num ;\ add %g5, 32, %g5 ;\ stxa %i0, [%g5 + %g1]asi_num ;\ stxa %i1, [%g5 + %g2]asi_num ;\ stxa %i2, [%g5 + %g3]asi_num ;\ stxa %i3, [%g5 + %g4]asi_num ;\ add %g5, 32, %g5 ;\ stxa %i4, [%g5 + %g1]asi_num ;\ stxa %i5, [%g5 + %g2]asi_num ;\ stxa %i6, [%g5 + %g3]asi_num ;\ stxa %i7, [%g5 + %g4]asi_num ;\ TT_TRACE_L(trace_win) ;\ saved ;\ retry ;\ SKIP(31-25-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty #define SPILL_64bit_tt1(asi_num, tail) \ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty ;\ .align 128 /* * FILL_64bit fills a 64-bit-wide kernel register window. It assumes * that the kernel context and the nucleus context are the same. The * stack pointer is required to be eight-byte aligned. */ #define FILL_64bit(tail) \ 2: TT_TRACE_L(trace_win) ;\ ldx [%sp + V9BIAS64 + 0], %l0 ;\ ldx [%sp + V9BIAS64 + 8], %l1 ;\ ldx [%sp + V9BIAS64 + 16], %l2 ;\ ldx [%sp + V9BIAS64 + 24], %l3 ;\ ldx [%sp + V9BIAS64 + 32], %l4 ;\ ldx [%sp + V9BIAS64 + 40], %l5 ;\ ldx [%sp + V9BIAS64 + 48], %l6 ;\ ldx [%sp + V9BIAS64 + 56], %l7 ;\ ldx [%sp + V9BIAS64 + 64], %i0 ;\ ldx [%sp + V9BIAS64 + 72], %i1 ;\ ldx [%sp + V9BIAS64 + 80], %i2 ;\ ldx [%sp + V9BIAS64 + 88], %i3 ;\ ldx [%sp + V9BIAS64 + 96], %i4 ;\ ldx [%sp + V9BIAS64 + 104], %i5 ;\ ldx [%sp + V9BIAS64 + 112], %i6 ;\ ldx [%sp + V9BIAS64 + 120], %i7 ;\ restored ;\ retry ;\ SKIP(31-18-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty /* * FILL_64bit_asi fills a 64-bit-wide register window from a 64-bit * wide address space via the designated asi. It is used to fill * non-kernel windows. The stack pointer is required to be eight-byte * aligned. */ #define FILL_64bit_asi(asi_num, tail) \ mov V9BIAS64 + 0, %g1 ;\ 2: TT_TRACE_L(trace_win) ;\ ldxa [%sp + %g1]asi_num, %l0 ;\ mov V9BIAS64 + 8, %g2 ;\ ldxa [%sp + %g2]asi_num, %l1 ;\ mov V9BIAS64 + 16, %g3 ;\ ldxa [%sp + %g3]asi_num, %l2 ;\ mov V9BIAS64 + 24, %g4 ;\ ldxa [%sp + %g4]asi_num, %l3 ;\ add %sp, 32, %g5 ;\ ldxa [%g5 + %g1]asi_num, %l4 ;\ ldxa [%g5 + %g2]asi_num, %l5 ;\ ldxa [%g5 + %g3]asi_num, %l6 ;\ ldxa [%g5 + %g4]asi_num, %l7 ;\ add %g5, 32, %g5 ;\ ldxa [%g5 + %g1]asi_num, %i0 ;\ ldxa [%g5 + %g2]asi_num, %i1 ;\ ldxa [%g5 + %g3]asi_num, %i2 ;\ ldxa [%g5 + %g4]asi_num, %i3 ;\ add %g5, 32, %g5 ;\ ldxa [%g5 + %g1]asi_num, %i4 ;\ ldxa [%g5 + %g2]asi_num, %i5 ;\ ldxa [%g5 + %g3]asi_num, %i6 ;\ ldxa [%g5 + %g4]asi_num, %i7 ;\ restored ;\ retry ;\ SKIP(31-25-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty #endif /* !lint */ /* * SPILL_mixed spills either size window, depending on * whether %sp is even or odd, to a 32-bit address space. * This may only be used in conjunction with SPILL_32bit/ * FILL_64bit. * Clear upper 32 bits of %sp if it is odd. * We won't need to clear them in 64 bit kernel. */ #define SPILL_mixed \ btst 1, %sp ;\ bz,a,pt %xcc, 1b ;\ srl %sp, 0, %sp ;\ ba,pt %xcc, 2b ;\ nop ;\ .align 128 /* * FILL_mixed(ASI) fills either size window, depending on * whether %sp is even or odd, from a 32-bit address space. * This may only be used in conjunction with FILL_32bit/ * FILL_64bit. New versions of FILL_mixed_{tt1,asi} would be * needed for use with FILL_{32,64}bit_{tt1,asi}. Particular * attention should be paid to the instructions that belong * in the delay slots of the branches depending on the type * of fill handler being branched to. * Clear upper 32 bits of %sp if it is odd. * We won't need to clear them in 64 bit kernel. */ #define FILL_mixed \ btst 1, %sp ;\ bz,a,pt %xcc, 1b ;\ srl %sp, 0, %sp ;\ ba,pt %xcc, 2b ;\ nop ;\ .align 128 /* * SPILL_32clean/SPILL_64clean spill 32-bit and 64-bit register windows, * respectively, into the address space via the designated asi. The * unbiased stack pointer is required to be eight-byte aligned (even for * the 32-bit case even though this code does not require such strict * alignment). * * With SPARC v9 the spill trap takes precedence over the cleanwin trap * so when cansave == 0, canrestore == 6, and cleanwin == 6 the next save * will cause cwp + 2 to be spilled but will not clean cwp + 1. That * window may contain kernel data so in user_rtt we set wstate to call * these spill handlers on the first user spill trap. These handler then * spill the appropriate window but also back up a window and clean the * window that didn't get a cleanwin trap. */ #define SPILL_32clean(asi_num, tail) \ srl %sp, 0, %sp ;\ sta %l0, [%sp + %g0]asi_num ;\ mov 4, %g1 ;\ sta %l1, [%sp + %g1]asi_num ;\ mov 8, %g2 ;\ sta %l2, [%sp + %g2]asi_num ;\ mov 12, %g3 ;\ sta %l3, [%sp + %g3]asi_num ;\ add %sp, 16, %g4 ;\ sta %l4, [%g4 + %g0]asi_num ;\ sta %l5, [%g4 + %g1]asi_num ;\ sta %l6, [%g4 + %g2]asi_num ;\ sta %l7, [%g4 + %g3]asi_num ;\ add %g4, 16, %g4 ;\ sta %i0, [%g4 + %g0]asi_num ;\ sta %i1, [%g4 + %g1]asi_num ;\ sta %i2, [%g4 + %g2]asi_num ;\ sta %i3, [%g4 + %g3]asi_num ;\ add %g4, 16, %g4 ;\ sta %i4, [%g4 + %g0]asi_num ;\ sta %i5, [%g4 + %g1]asi_num ;\ sta %i6, [%g4 + %g2]asi_num ;\ sta %i7, [%g4 + %g3]asi_num ;\ TT_TRACE_L(trace_win) ;\ b .spill_clean ;\ mov WSTATE_USER32, %g7 ;\ SKIP(31-25-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_32bit_/**/tail ;\ .empty #define SPILL_64clean(asi_num, tail) \ mov 0 + V9BIAS64, %g1 ;\ stxa %l0, [%sp + %g1]asi_num ;\ mov 8 + V9BIAS64, %g2 ;\ stxa %l1, [%sp + %g2]asi_num ;\ mov 16 + V9BIAS64, %g3 ;\ stxa %l2, [%sp + %g3]asi_num ;\ mov 24 + V9BIAS64, %g4 ;\ stxa %l3, [%sp + %g4]asi_num ;\ add %sp, 32, %g5 ;\ stxa %l4, [%g5 + %g1]asi_num ;\ stxa %l5, [%g5 + %g2]asi_num ;\ stxa %l6, [%g5 + %g3]asi_num ;\ stxa %l7, [%g5 + %g4]asi_num ;\ add %g5, 32, %g5 ;\ stxa %i0, [%g5 + %g1]asi_num ;\ stxa %i1, [%g5 + %g2]asi_num ;\ stxa %i2, [%g5 + %g3]asi_num ;\ stxa %i3, [%g5 + %g4]asi_num ;\ add %g5, 32, %g5 ;\ stxa %i4, [%g5 + %g1]asi_num ;\ stxa %i5, [%g5 + %g2]asi_num ;\ stxa %i6, [%g5 + %g3]asi_num ;\ stxa %i7, [%g5 + %g4]asi_num ;\ TT_TRACE_L(trace_win) ;\ b .spill_clean ;\ mov WSTATE_USER64, %g7 ;\ SKIP(31-25-TT_TRACE_L_INS) ;\ ba,a,pt %xcc, fault_64bit_/**/tail ;\ .empty /* * Floating point disabled. */ #define FP_DISABLED_TRAP \ TT_TRACE(trace_gen) ;\ ba,pt %xcc,.fp_disabled ;\ nop ;\ .align 32 /* * Floating point exceptions. */ #define FP_IEEE_TRAP \ TT_TRACE(trace_gen) ;\ ba,pt %xcc,.fp_ieee_exception ;\ nop ;\ .align 32 #define FP_TRAP \ TT_TRACE(trace_gen) ;\ ba,pt %xcc,.fp_exception ;\ nop ;\ .align 32 #if !defined(lint) /* * ECACHE_ECC error traps at level 0 and level 1 */ #define ECACHE_ECC(table_name) \ .global table_name ;\ table_name: ;\ membar #Sync ;\ set trap, %g1 ;\ rdpr %tt, %g3 ;\ ba,pt %xcc, sys_trap ;\ sub %g0, 1, %g4 ;\ .align 32 #endif /* !lint */ /* * illegal instruction trap */ #define ILLTRAP_INSTR \ membar #Sync ;\ TT_TRACE(trace_gen) ;\ or %g0, P_UTRAP4, %g2 ;\ or %g0, T_UNIMP_INSTR, %g3 ;\ sethi %hi(.check_v9utrap), %g4 ;\ jmp %g4 + %lo(.check_v9utrap) ;\ nop ;\ .align 32 /* * tag overflow trap */ #define TAG_OVERFLOW \ TT_TRACE(trace_gen) ;\ or %g0, P_UTRAP10, %g2 ;\ or %g0, T_TAG_OVERFLOW, %g3 ;\ sethi %hi(.check_v9utrap), %g4 ;\ jmp %g4 + %lo(.check_v9utrap) ;\ nop ;\ .align 32 /* * divide by zero trap */ #define DIV_BY_ZERO \ TT_TRACE(trace_gen) ;\ or %g0, P_UTRAP11, %g2 ;\ or %g0, T_IDIV0, %g3 ;\ sethi %hi(.check_v9utrap), %g4 ;\ jmp %g4 + %lo(.check_v9utrap) ;\ nop ;\ .align 32 /* * trap instruction for V9 user trap handlers */ #define TRAP_INSTR \ TT_TRACE(trace_gen) ;\ or %g0, T_SOFTWARE_TRAP, %g3 ;\ sethi %hi(.check_v9utrap), %g4 ;\ jmp %g4 + %lo(.check_v9utrap) ;\ nop ;\ .align 32 #define TRP4 TRAP_INSTR; TRAP_INSTR; TRAP_INSTR; TRAP_INSTR /* * LEVEL_INTERRUPT is for level N interrupts. * VECTOR_INTERRUPT is for the vector trap. */ #define LEVEL_INTERRUPT(level) \ .global tt_pil/**/level ;\ tt_pil/**/level: ;\ ba,pt %xcc, pil_interrupt ;\ mov level, %g4 ;\ .align 32 #define LEVEL14_INTERRUPT \ ba pil14_interrupt ;\ mov PIL_14, %g4 ;\ .align 32 #define CPU_MONDO \ ba,a,pt %xcc, cpu_mondo ;\ .align 32 #define DEV_MONDO \ ba,a,pt %xcc, dev_mondo ;\ .align 32 /* * We take over the rtba after we set our trap table and * fault status area. The watchdog reset trap is now handled by the OS. */ #define WATCHDOG_RESET \ mov PTL1_BAD_WATCHDOG, %g1 ;\ ba,a,pt %xcc, .watchdog_trap ;\ .align 32 /* * RED is for traps that use the red mode handler. * We should never see these either. */ #define RED \ mov PTL1_BAD_RED, %g1 ;\ ba,a,pt %xcc, .watchdog_trap ;\ .align 32 /* * MMU Trap Handlers. */ /* * synthesize for trap(): SFSR in %g3 */ #define IMMU_EXCEPTION \ MMU_FAULT_STATUS_AREA(%g3) ;\ rdpr %tpc, %g2 ;\ ldx [%g3 + MMFSA_I_TYPE], %g1 ;\ ldx [%g3 + MMFSA_I_CTX], %g3 ;\ sllx %g3, SFSR_CTX_SHIFT, %g3 ;\ or %g3, %g1, %g3 ;\ ba,pt %xcc, .mmu_exception_end ;\ mov T_INSTR_EXCEPTION, %g1 ;\ .align 32 /* * synthesize for trap(): TAG_ACCESS in %g2, SFSR in %g3 */ #define DMMU_EXCEPTION \ ba,a,pt %xcc, .dmmu_exception ;\ .align 32 /* * synthesize for trap(): SFAR in %g2, SFSR in %g3 */ #define DMMU_EXC_AG_PRIV \ MMU_FAULT_STATUS_AREA(%g3) ;\ ldx [%g3 + MMFSA_D_ADDR], %g2 ;\ /* Fault type not available in MMU fault status area */ ;\ mov MMFSA_F_PRVACT, %g1 ;\ ldx [%g3 + MMFSA_D_CTX], %g3 ;\ sllx %g3, SFSR_CTX_SHIFT, %g3 ;\ ba,pt %xcc, .mmu_priv_exception ;\ or %g3, %g1, %g3 ;\ .align 32 /* * synthesize for trap(): SFAR in %g2, SFSR in %g3 */ #define DMMU_EXC_AG_NOT_ALIGNED \ MMU_FAULT_STATUS_AREA(%g3) ;\ ldx [%g3 + MMFSA_D_ADDR], %g2 ;\ /* Fault type not available in MMU fault status area */ ;\ mov MMFSA_F_UNALIGN, %g1 ;\ ldx [%g3 + MMFSA_D_CTX], %g3 ;\ sllx %g3, SFSR_CTX_SHIFT, %g3 ;\ ba,pt %xcc, .mmu_exception_not_aligned ;\ or %g3, %g1, %g3 /* SFSR */ ;\ .align 32 /* * SPARC V9 IMPL. DEP. #109(1) and (2) and #110(1) and (2) */ /* * synthesize for trap(): SFAR in %g2, SFSR in %g3 */ #define DMMU_EXC_LDDF_NOT_ALIGNED \ ba,a,pt %xcc, .dmmu_exc_lddf_not_aligned ;\ .align 32 /* * synthesize for trap(): SFAR in %g2, SFSR in %g3 */ #define DMMU_EXC_STDF_NOT_ALIGNED \ ba,a,pt %xcc, .dmmu_exc_stdf_not_aligned ;\ .align 32 #if defined(cscope) /* * Define labels to direct cscope quickly to labels that * are generated by macro expansion of DTLB_MISS(). */ .global tt0_dtlbmiss tt0_dtlbmiss: .global tt1_dtlbmiss tt1_dtlbmiss: nop #endif /* * Data miss handler (must be exactly 32 instructions) * * This handler is invoked only if the hypervisor has been instructed * not to do any TSB walk. * * Kernel and invalid context cases are handled by the sfmmu_kdtlb_miss * handler. * * User TLB miss handling depends upon whether a user process has one or * two TSBs. User TSB information (physical base and size code) is kept * in two dedicated scratchpad registers. Absence of a user TSB (primarily * second TSB) is indicated by a negative value (-1) in that register. */ /* * synthesize for miss handler: pseudo-tag access in %g2 (with context "type" * (0=kernel, 1=invalid, or 2=user) rather than context ID) */ #define DTLB_MISS(table_name) ;\ .global table_name/**/_dtlbmiss ;\ table_name/**/_dtlbmiss: ;\ GET_MMU_D_PTAGACC_CTXTYPE(%g2, %g3) /* 8 instr */ ;\ cmp %g3, INVALID_CONTEXT ;\ ble,pn %xcc, sfmmu_kdtlb_miss ;\ srlx %g2, TAG_VALO_SHIFT, %g7 /* g7 = tsb tag */ ;\ mov SCRATCHPAD_UTSBREG2, %g1 ;\ ldxa [%g1]ASI_SCRATCHPAD, %g1 /* get 2nd tsbreg */ ;\ brgez,pn %g1, sfmmu_udtlb_slowpath /* branch if 2 TSBs */ ;\ nop ;\ GET_1ST_TSBE_PTR(%g2, %g1, %g4, %g5) /* 11 instr */ ;\ ba,pt %xcc, sfmmu_udtlb_fastpath /* no 4M TSB, miss */ ;\ srlx %g2, TAG_VALO_SHIFT, %g7 /* g7 = tsb tag */ ;\ .align 128 #if defined(cscope) /* * Define labels to direct cscope quickly to labels that * are generated by macro expansion of ITLB_MISS(). */ .global tt0_itlbmiss tt0_itlbmiss: .global tt1_itlbmiss tt1_itlbmiss: nop #endif /* * Instruction miss handler. * * This handler is invoked only if the hypervisor has been instructed * not to do any TSB walk. * * ldda instructions will have their ASI patched * by sfmmu_patch_ktsb at runtime. * MUST be EXACTLY 32 instructions or we'll break. */ /* * synthesize for miss handler: TAG_ACCESS in %g2 (with context "type" * (0=kernel, 1=invalid, or 2=user) rather than context ID) */ #define ITLB_MISS(table_name) \ .global table_name/**/_itlbmiss ;\ table_name/**/_itlbmiss: ;\ GET_MMU_I_PTAGACC_CTXTYPE(%g2, %g3) /* 8 instr */ ;\ cmp %g3, INVALID_CONTEXT ;\ ble,pn %xcc, sfmmu_kitlb_miss ;\ srlx %g2, TAG_VALO_SHIFT, %g7 /* g7 = tsb tag */ ;\ mov SCRATCHPAD_UTSBREG2, %g1 ;\ ldxa [%g1]ASI_SCRATCHPAD, %g1 /* get 2nd tsbreg */ ;\ brgez,pn %g1, sfmmu_uitlb_slowpath /* branch if 2 TSBs */ ;\ nop ;\ GET_1ST_TSBE_PTR(%g2, %g1, %g4, %g5) /* 11 instr */ ;\ ba,pt %xcc, sfmmu_uitlb_fastpath /* no 4M TSB, miss */ ;\ srlx %g2, TAG_VALO_SHIFT, %g7 /* g7 = tsb tag */ ;\ .align 128 #define DTSB_MISS \ GOTO_TT(sfmmu_slow_dmmu_miss,trace_dmmu) #define ITSB_MISS \ GOTO_TT(sfmmu_slow_immu_miss,trace_immu) /* * This macro is the first level handler for fast protection faults. * It first demaps the tlb entry which generated the fault and then * attempts to set the modify bit on the hash. It needs to be * exactly 32 instructions. */ /* * synthesize for miss handler: TAG_ACCESS in %g2 (with context "type" * (0=kernel, 1=invalid, or 2=user) rather than context ID) */ #define DTLB_PROT \ GET_MMU_D_PTAGACC_CTXTYPE(%g2, %g3) /* 8 instr */ ;\ /* ;\ * g2 = pseudo-tag access register (ctx type rather than ctx ID) ;\ * g3 = ctx type (0, 1, or 2) ;\ */ ;\ TT_TRACE(trace_dataprot) /* 2 instr ifdef TRAPTRACE */ ;\ /* clobbers g1 and g6 XXXQ? */ ;\ brnz,pt %g3, sfmmu_uprot_trap /* user trap */ ;\ nop ;\ ba,a,pt %xcc, sfmmu_kprot_trap /* kernel trap */ ;\ .align 128 #define DMMU_EXCEPTION_TL1 ;\ ba,a,pt %xcc, mmu_trap_tl1 ;\ .align 32 #define MISALIGN_ADDR_TL1 ;\ ba,a,pt %xcc, mmu_trap_tl1 ;\ .align 32 /* * Trace a tsb hit * g1 = tsbe pointer (in/clobbered) * g2 = tag access register (in) * g3 - g4 = scratch (clobbered) * g5 = tsbe data (in) * g6 = scratch (clobbered) * g7 = pc we jumped here from (in) * ttextra = value to OR in to trap type (%tt) (in) */ #ifdef TRAPTRACE #define TRACE_TSBHIT(ttextra) \ membar #Sync ;\ sethi %hi(FLUSH_ADDR), %g6 ;\ flush %g6 ;\ TRACE_PTR(%g3, %g6) ;\ GET_TRACE_TICK(%g6) ;\ stxa %g6, [%g3 + TRAP_ENT_TICK]%asi ;\ stna %g2, [%g3 + TRAP_ENT_SP]%asi /* tag access */ ;\ stna %g5, [%g3 + TRAP_ENT_F1]%asi /* tsb data */ ;\ rdpr %tnpc, %g6 ;\ stna %g6, [%g3 + TRAP_ENT_F2]%asi ;\ stna %g1, [%g3 + TRAP_ENT_F3]%asi /* tsb pointer */ ;\ stna %g0, [%g3 + TRAP_ENT_F4]%asi ;\ rdpr %tpc, %g6 ;\ stna %g6, [%g3 + TRAP_ENT_TPC]%asi ;\ TRACE_SAVE_TL_GL_REGS(%g3, %g6) ;\ rdpr %tt, %g6 ;\ or %g6, (ttextra), %g1 ;\ stha %g1, [%g3 + TRAP_ENT_TT]%asi ;\ MMU_FAULT_STATUS_AREA(%g4) ;\ mov MMFSA_D_ADDR, %g1 ;\ cmp %g6, FAST_IMMU_MISS_TT ;\ move %xcc, MMFSA_I_ADDR, %g1 ;\ cmp %g6, T_INSTR_MMU_MISS ;\ move %xcc, MMFSA_I_ADDR, %g1 ;\ ldx [%g4 + %g1], %g1 ;\ stxa %g1, [%g3 + TRAP_ENT_TSTATE]%asi /* fault addr */ ;\ mov MMFSA_D_CTX, %g1 ;\ cmp %g6, FAST_IMMU_MISS_TT ;\ move %xcc, MMFSA_I_CTX, %g1 ;\ cmp %g6, T_INSTR_MMU_MISS ;\ move %xcc, MMFSA_I_CTX, %g1 ;\ ldx [%g4 + %g1], %g1 ;\ stna %g1, [%g3 + TRAP_ENT_TR]%asi ;\ TRACE_NEXT(%g3, %g4, %g6) #else #define TRACE_TSBHIT(ttextra) #endif #if defined(lint) struct scb trap_table; struct scb scb; /* trap_table/scb are the same object */ #else /* lint */ /* * ======================================================================= * SPARC V9 TRAP TABLE * * The trap table is divided into two halves: the first half is used when * taking traps when TL=0; the second half is used when taking traps from * TL>0. Note that handlers in the second half of the table might not be able * to make the same assumptions as handlers in the first half of the table. * * Worst case trap nesting so far: * * at TL=0 client issues software trap requesting service * at TL=1 nucleus wants a register window * at TL=2 register window clean/spill/fill takes a TLB miss * at TL=3 processing TLB miss * at TL=4 handle asynchronous error * * Note that a trap from TL=4 to TL=5 places Spitfire in "RED mode". * * ======================================================================= */ .section ".text" .align 4 .global trap_table, scb, trap_table0, trap_table1, etrap_table .type trap_table, #object .type trap_table0, #object .type trap_table1, #object .type scb, #object trap_table: scb: trap_table0: /* hardware traps */ NOT; /* 000 reserved */ RED; /* 001 power on reset */ WATCHDOG_RESET; /* 002 watchdog reset */ RED; /* 003 externally initiated reset */ RED; /* 004 software initiated reset */ RED; /* 005 red mode exception */ NOT; NOT; /* 006 - 007 reserved */ IMMU_EXCEPTION; /* 008 instruction access exception */ ITSB_MISS; /* 009 instruction access MMU miss */ NOT; /* 00A reserved */ NOT; NOT4; /* 00B - 00F reserved */ ILLTRAP_INSTR; /* 010 illegal instruction */ TRAP(T_PRIV_INSTR); /* 011 privileged opcode */ TRAP(T_UNIMP_LDD); /* 012 unimplemented LDD */ TRAP(T_UNIMP_STD); /* 013 unimplemented STD */ NOT4; NOT4; NOT4; /* 014 - 01F reserved */ FP_DISABLED_TRAP; /* 020 fp disabled */ FP_IEEE_TRAP; /* 021 fp exception ieee 754 */ FP_TRAP; /* 022 fp exception other */ TAG_OVERFLOW; /* 023 tag overflow */ CLEAN_WINDOW; /* 024 - 027 clean window */ DIV_BY_ZERO; /* 028 division by zero */ NOT; /* 029 internal processor error */ NOT; NOT; NOT4; /* 02A - 02F reserved */ DMMU_EXCEPTION; /* 030 data access exception */ DTSB_MISS; /* 031 data access MMU miss */ NOT; /* 032 reserved */ NOT; /* 033 data access protection */ DMMU_EXC_AG_NOT_ALIGNED; /* 034 mem address not aligned */ DMMU_EXC_LDDF_NOT_ALIGNED; /* 035 LDDF mem address not aligned */ DMMU_EXC_STDF_NOT_ALIGNED; /* 036 STDF mem address not aligned */ DMMU_EXC_AG_PRIV; /* 037 privileged action */ NOT; /* 038 LDQF mem address not aligned */ NOT; /* 039 STQF mem address not aligned */ NOT; NOT; NOT4; /* 03A - 03F reserved */ NOT; /* 040 async data error */ LEVEL_INTERRUPT(1); /* 041 interrupt level 1 */ LEVEL_INTERRUPT(2); /* 042 interrupt level 2 */ LEVEL_INTERRUPT(3); /* 043 interrupt level 3 */ LEVEL_INTERRUPT(4); /* 044 interrupt level 4 */ LEVEL_INTERRUPT(5); /* 045 interrupt level 5 */ LEVEL_INTERRUPT(6); /* 046 interrupt level 6 */ LEVEL_INTERRUPT(7); /* 047 interrupt level 7 */ LEVEL_INTERRUPT(8); /* 048 interrupt level 8 */ LEVEL_INTERRUPT(9); /* 049 interrupt level 9 */ LEVEL_INTERRUPT(10); /* 04A interrupt level 10 */ LEVEL_INTERRUPT(11); /* 04B interrupt level 11 */ LEVEL_INTERRUPT(12); /* 04C interrupt level 12 */ LEVEL_INTERRUPT(13); /* 04D interrupt level 13 */ LEVEL14_INTERRUPT; /* 04E interrupt level 14 */ LEVEL_INTERRUPT(15); /* 04F interrupt level 15 */ NOT4; NOT4; NOT4; NOT4; /* 050 - 05F reserved */ NOT; /* 060 interrupt vector */ GOTO(kmdb_trap); /* 061 PA watchpoint */ GOTO(kmdb_trap); /* 062 VA watchpoint */ NOT; /* 063 reserved */ ITLB_MISS(tt0); /* 064 instruction access MMU miss */ DTLB_MISS(tt0); /* 068 data access MMU miss */ DTLB_PROT; /* 06C data access protection */ NOT; /* 070 reserved */ NOT; /* 071 reserved */ NOT; /* 072 reserved */ NOT; /* 073 reserved */ NOT4; NOT4 /* 074 - 07B reserved */ CPU_MONDO; /* 07C cpu_mondo */ DEV_MONDO; /* 07D dev_mondo */ GOTO_TT(resumable_error, trace_gen); /* 07E resumable error */ GOTO_TT(nonresumable_error, trace_gen); /* 07F non-reasumable error */ NOT4; /* 080 spill 0 normal */ SPILL_32bit_asi(ASI_AIUP,sn0); /* 084 spill 1 normal */ SPILL_64bit_asi(ASI_AIUP,sn0); /* 088 spill 2 normal */ SPILL_32clean(ASI_AIUP,sn0); /* 08C spill 3 normal */ SPILL_64clean(ASI_AIUP,sn0); /* 090 spill 4 normal */ SPILL_32bit(not); /* 094 spill 5 normal */ SPILL_64bit(not); /* 098 spill 6 normal */ SPILL_mixed; /* 09C spill 7 normal */ NOT4; /* 0A0 spill 0 other */ SPILL_32bit_asi(ASI_AIUS,so0); /* 0A4 spill 1 other */ SPILL_64bit_asi(ASI_AIUS,so0); /* 0A8 spill 2 other */ SPILL_32bit_asi(ASI_AIUS,so0); /* 0AC spill 3 other */ SPILL_64bit_asi(ASI_AIUS,so0); /* 0B0 spill 4 other */ NOT4; /* 0B4 spill 5 other */ NOT4; /* 0B8 spill 6 other */ NOT4; /* 0BC spill 7 other */ NOT4; /* 0C0 fill 0 normal */ FILL_32bit_asi(ASI_AIUP,fn0); /* 0C4 fill 1 normal */ FILL_64bit_asi(ASI_AIUP,fn0); /* 0C8 fill 2 normal */ FILL_32bit_asi(ASI_AIUP,fn0); /* 0CC fill 3 normal */ FILL_64bit_asi(ASI_AIUP,fn0); /* 0D0 fill 4 normal */ FILL_32bit(not); /* 0D4 fill 5 normal */ FILL_64bit(not); /* 0D8 fill 6 normal */ FILL_mixed; /* 0DC fill 7 normal */ NOT4; /* 0E0 fill 0 other */ NOT4; /* 0E4 fill 1 other */ NOT4; /* 0E8 fill 2 other */ NOT4; /* 0EC fill 3 other */ NOT4; /* 0F0 fill 4 other */ NOT4; /* 0F4 fill 5 other */ NOT4; /* 0F8 fill 6 other */ NOT4; /* 0FC fill 7 other */ /* user traps */ GOTO(syscall_trap_4x); /* 100 old system call */ TRAP(T_BREAKPOINT); /* 101 user breakpoint */ TRAP(T_DIV0); /* 102 user divide by zero */ GOTO(.flushw); /* 103 flush windows */ GOTO(.clean_windows); /* 104 clean windows */ BAD; /* 105 range check ?? */ GOTO(.fix_alignment); /* 106 do unaligned references */ BAD; /* 107 unused */ SYSCALL_TRAP32; /* 108 ILP32 system call on LP64 */ GOTO(set_trap0_addr); /* 109 set trap0 address */ BAD; BAD; BAD4; /* 10A - 10F unused */ TRP4; TRP4; TRP4; TRP4; /* 110 - 11F V9 user trap handlers */ GOTO(.getcc); /* 120 get condition codes */ GOTO(.setcc); /* 121 set condition codes */ GOTO(.getpsr); /* 122 get psr */ GOTO(.setpsr); /* 123 set psr (some fields) */ GOTO(get_timestamp); /* 124 get timestamp */ GOTO(get_virtime); /* 125 get lwp virtual time */ PRIV(self_xcall); /* 126 self xcall */ GOTO(get_hrestime); /* 127 get hrestime */ BAD; /* 128 ST_SETV9STACK */ GOTO(.getlgrp); /* 129 get lgrpid */ BAD; BAD; BAD4; /* 12A - 12F unused */ BAD4; BAD4; /* 130 - 137 unused */ DTRACE_PID; /* 138 dtrace pid tracing provider */ BAD; /* 139 unused */ DTRACE_RETURN; /* 13A dtrace pid return probe */ BAD; BAD4; /* 13B - 13F unused */ SYSCALL_TRAP; /* 140 LP64 system call */ SYSCALL(nosys); /* 141 unused system call trap */ #ifdef DEBUG_USER_TRAPTRACECTL GOTO(.traptrace_freeze); /* 142 freeze traptrace */ GOTO(.traptrace_unfreeze); /* 143 unfreeze traptrace */ #else SYSCALL(nosys); /* 142 unused system call trap */ SYSCALL(nosys); /* 143 unused system call trap */ #endif BAD4; BAD4; BAD4; /* 144 - 14F unused */ BAD4; BAD4; BAD4; BAD4; /* 150 - 15F unused */ BAD4; BAD4; BAD4; BAD4; /* 160 - 16F unused */ BAD; /* 170 - unused */ BAD; /* 171 - unused */ BAD; BAD; /* 172 - 173 unused */ BAD4; BAD4; /* 174 - 17B unused */ #ifdef PTL1_PANIC_DEBUG mov PTL1_BAD_DEBUG, %g1; GOTO(ptl1_panic); /* 17C test ptl1_panic */ #else BAD; /* 17C unused */ #endif /* PTL1_PANIC_DEBUG */ PRIV(kmdb_trap); /* 17D kmdb enter (L1-A) */ PRIV(kmdb_trap); /* 17E kmdb breakpoint */ PRIV(obp_bpt); /* 17F obp breakpoint */ /* reserved */ NOT4; NOT4; NOT4; NOT4; /* 180 - 18F reserved */ NOT4; NOT4; NOT4; NOT4; /* 190 - 19F reserved */ NOT4; NOT4; NOT4; NOT4; /* 1A0 - 1AF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1B0 - 1BF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1C0 - 1CF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1D0 - 1DF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1E0 - 1EF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1F0 - 1FF reserved */ .size trap_table0, (.-trap_table0) trap_table1: NOT4; NOT4; /* 000 - 007 unused */ NOT; /* 008 instruction access exception */ ITSB_MISS; /* 009 instruction access MMU miss */ NOT; /* 00A reserved */ NOT; NOT4; /* 00B - 00F unused */ NOT4; NOT4; NOT4; NOT4; /* 010 - 01F unused */ NOT4; /* 020 - 023 unused */ CLEAN_WINDOW; /* 024 - 027 clean window */ NOT4; NOT4; /* 028 - 02F unused */ DMMU_EXCEPTION_TL1; /* 030 data access exception */ DTSB_MISS; /* 031 data access MMU miss */ NOT; /* 032 reserved */ NOT; /* 033 unused */ MISALIGN_ADDR_TL1; /* 034 mem address not aligned */ NOT; NOT; NOT; NOT4; NOT4 /* 035 - 03F unused */ NOT4; NOT4; NOT4; NOT4; /* 040 - 04F unused */ NOT4; NOT4; NOT4; NOT4; /* 050 - 05F unused */ NOT; /* 060 unused */ GOTO(kmdb_trap_tl1); /* 061 PA watchpoint */ GOTO(kmdb_trap_tl1); /* 062 VA watchpoint */ NOT; /* 063 reserved */ ITLB_MISS(tt1); /* 064 instruction access MMU miss */ DTLB_MISS(tt1); /* 068 data access MMU miss */ DTLB_PROT; /* 06C data access protection */ NOT; /* 070 reserved */ NOT; /* 071 reserved */ NOT; /* 072 reserved */ NOT; /* 073 reserved */ NOT4; NOT4; /* 074 - 07B reserved */ NOT; /* 07C reserved */ NOT; /* 07D reserved */ NOT; /* 07E resumable error */ GOTO_TT(nonresumable_error, trace_gen); /* 07F nonresumable error */ NOTP4; /* 080 spill 0 normal */ SPILL_32bit_tt1(ASI_AIUP,sn1); /* 084 spill 1 normal */ SPILL_64bit_tt1(ASI_AIUP,sn1); /* 088 spill 2 normal */ SPILL_32bit_tt1(ASI_AIUP,sn1); /* 08C spill 3 normal */ SPILL_64bit_tt1(ASI_AIUP,sn1); /* 090 spill 4 normal */ NOTP4; /* 094 spill 5 normal */ SPILL_64bit_ktt1(sk); /* 098 spill 6 normal */ SPILL_mixed_ktt1(sk); /* 09C spill 7 normal */ NOTP4; /* 0A0 spill 0 other */ SPILL_32bit_tt1(ASI_AIUS,so1); /* 0A4 spill 1 other */ SPILL_64bit_tt1(ASI_AIUS,so1); /* 0A8 spill 2 other */ SPILL_32bit_tt1(ASI_AIUS,so1); /* 0AC spill 3 other */ SPILL_64bit_tt1(ASI_AIUS,so1); /* 0B0 spill 4 other */ NOTP4; /* 0B4 spill 5 other */ NOTP4; /* 0B8 spill 6 other */ NOTP4; /* 0BC spill 7 other */ NOT4; /* 0C0 fill 0 normal */ NOT4; /* 0C4 fill 1 normal */ NOT4; /* 0C8 fill 2 normal */ NOT4; /* 0CC fill 3 normal */ NOT4; /* 0D0 fill 4 normal */ NOT4; /* 0D4 fill 5 normal */ NOT4; /* 0D8 fill 6 normal */ NOT4; /* 0DC fill 7 normal */ NOT4; NOT4; NOT4; NOT4; /* 0E0 - 0EF unused */ NOT4; NOT4; NOT4; NOT4; /* 0F0 - 0FF unused */ /* * Code running at TL>0 does not use soft traps, so * we can truncate the table here. * However: * sun4v uses (hypervisor) ta instructions at TL > 0, so * provide a safety net for now. */ /* soft traps */ BAD4; BAD4; BAD4; BAD4; /* 100 - 10F unused */ BAD4; BAD4; BAD4; BAD4; /* 110 - 11F unused */ BAD4; BAD4; BAD4; BAD4; /* 120 - 12F unused */ BAD4; BAD4; BAD4; BAD4; /* 130 - 13F unused */ BAD4; BAD4; BAD4; BAD4; /* 140 - 14F unused */ BAD4; BAD4; BAD4; BAD4; /* 150 - 15F unused */ BAD4; BAD4; BAD4; BAD4; /* 160 - 16F unused */ BAD4; BAD4; BAD4; BAD4; /* 170 - 17F unused */ /* reserved */ NOT4; NOT4; NOT4; NOT4; /* 180 - 18F reserved */ NOT4; NOT4; NOT4; NOT4; /* 190 - 19F reserved */ NOT4; NOT4; NOT4; NOT4; /* 1A0 - 1AF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1B0 - 1BF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1C0 - 1CF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1D0 - 1DF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1E0 - 1EF reserved */ NOT4; NOT4; NOT4; NOT4; /* 1F0 - 1FF reserved */ etrap_table: .size trap_table1, (.-trap_table1) .size trap_table, (.-trap_table) .size scb, (.-scb) /* * We get to exec_fault in the case of an instruction miss and tte * has no execute bit set. We go to tl0 to handle it. * * g1 = tsbe pointer (in/clobbered) * g2 = tag access register (in) * g3 - g4 = scratch (clobbered) * g5 = tsbe data (in) * g6 = scratch (clobbered) * g7 = pc we jumped here from (in) */ /* * synthesize for miss handler: TAG_ACCESS in %g2 (with context "type" * (0=kernel, 1=invalid, or 2=user) rather than context ID) */ ALTENTRY(exec_fault) set icache_is_coherent, %g6 /* check soft exec mode */ ld [%g6], %g6 brz,pn %g6, sfmmu_slow_immu_miss nop TRACE_TSBHIT(TT_MMU_EXEC) MMU_FAULT_STATUS_AREA(%g4) ldx [%g4 + MMFSA_I_ADDR], %g2 /* g2 = address */ ldx [%g4 + MMFSA_I_CTX], %g3 /* g3 = ctx */ srlx %g2, MMU_PAGESHIFT, %g2 ! align address to page boundry cmp %g3, USER_CONTEXT_TYPE sllx %g2, MMU_PAGESHIFT, %g2 movgu %icc, USER_CONTEXT_TYPE, %g3 or %g2, %g3, %g2 /* TAG_ACCESS */ mov T_INSTR_MMU_MISS, %g3 ! arg2 = traptype set trap, %g1 ba,pt %xcc, sys_trap mov -1, %g4 .mmu_exception_not_aligned: /* %g2 = sfar, %g3 = sfsr */ rdpr %tstate, %g1 btst TSTATE_PRIV, %g1 bnz,pn %icc, 2f nop CPU_ADDR(%g1, %g4) ! load CPU struct addr ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer ldn [%g1 + T_PROCP], %g1 ! load proc pointer ldn [%g1 + P_UTRAPS], %g5 ! are there utraps? brz,pt %g5, 2f nop ldn [%g5 + P_UTRAP15], %g5 ! unaligned utrap? brz,pn %g5, 2f nop btst 1, %sp bz,pt %xcc, 1f ! 32 bit user program nop ba,pt %xcc, .setup_v9utrap ! 64 bit user program nop 1: ba,pt %xcc, .setup_utrap or %g2, %g0, %g7 2: ba,pt %xcc, .mmu_exception_end mov T_ALIGNMENT, %g1 .mmu_priv_exception: rdpr %tstate, %g1 btst TSTATE_PRIV, %g1 bnz,pn %icc, 1f nop CPU_ADDR(%g1, %g4) ! load CPU struct addr ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer ldn [%g1 + T_PROCP], %g1 ! load proc pointer ldn [%g1 + P_UTRAPS], %g5 ! are there utraps? brz,pt %g5, 1f nop ldn [%g5 + P_UTRAP16], %g5 brnz,pt %g5, .setup_v9utrap nop 1: mov T_PRIV_INSTR, %g1 .mmu_exception_end: CPU_INDEX(%g4, %g5) set cpu_core, %g5 sllx %g4, CPU_CORE_SHIFT, %g4 add %g4, %g5, %g4 lduh [%g4 + CPUC_DTRACE_FLAGS], %g5 andcc %g5, CPU_DTRACE_NOFAULT, %g0 bz 1f or %g5, CPU_DTRACE_BADADDR, %g5 stuh %g5, [%g4 + CPUC_DTRACE_FLAGS] done 1: sllx %g3, 32, %g3 or %g3, %g1, %g3 set trap, %g1 ba,pt %xcc, sys_trap sub %g0, 1, %g4 .fp_disabled: CPU_ADDR(%g1, %g4) ! load CPU struct addr ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer rdpr %tstate, %g4 btst TSTATE_PRIV, %g4 bnz,a,pn %icc, ptl1_panic mov PTL1_BAD_FPTRAP, %g1 ldn [%g1 + T_PROCP], %g1 ! load proc pointer ldn [%g1 + P_UTRAPS], %g5 ! are there utraps? brz,a,pt %g5, 2f nop ldn [%g5 + P_UTRAP7], %g5 ! fp_disabled utrap? brz,a,pn %g5, 2f nop btst 1, %sp bz,a,pt %xcc, 1f ! 32 bit user program nop ba,a,pt %xcc, .setup_v9utrap ! 64 bit user program nop 1: ba,pt %xcc, .setup_utrap or %g0, %g0, %g7 2: set fp_disabled, %g1 ba,pt %xcc, sys_trap sub %g0, 1, %g4 .fp_ieee_exception: rdpr %tstate, %g1 btst TSTATE_PRIV, %g1 bnz,a,pn %icc, ptl1_panic mov PTL1_BAD_FPTRAP, %g1 CPU_ADDR(%g1, %g4) ! load CPU struct addr stx %fsr, [%g1 + CPU_TMP1] ldx [%g1 + CPU_TMP1], %g2 ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer ldn [%g1 + T_PROCP], %g1 ! load proc pointer ldn [%g1 + P_UTRAPS], %g5 ! are there utraps? brz,a,pt %g5, 1f nop ldn [%g5 + P_UTRAP8], %g5 brnz,a,pt %g5, .setup_v9utrap nop 1: set _fp_ieee_exception, %g1 ba,pt %xcc, sys_trap sub %g0, 1, %g4 /* * Register Inputs: * %g5 user trap handler * %g7 misaligned addr - for alignment traps only */ .setup_utrap: set trap, %g1 ! setup in case we go mov T_FLUSH_PCB, %g3 ! through sys_trap on sub %g0, 1, %g4 ! the save instruction below /* * If the DTrace pid provider is single stepping a copied-out * instruction, t->t_dtrace_step will be set. In that case we need * to abort the single-stepping (since execution of the instruction * was interrupted) and use the value of t->t_dtrace_npc as the %npc. */ save %sp, -SA(MINFRAME32), %sp ! window for trap handler CPU_ADDR(%g1, %g4) ! load CPU struct addr ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer ldub [%g1 + T_DTRACE_STEP], %g2 ! load t->t_dtrace_step rdpr %tnpc, %l2 ! arg1 == tnpc brz,pt %g2, 1f rdpr %tpc, %l1 ! arg0 == tpc ldub [%g1 + T_DTRACE_AST], %g2 ! load t->t_dtrace_ast ldn [%g1 + T_DTRACE_NPC], %l2 ! arg1 = t->t_dtrace_npc (step) brz,pt %g2, 1f st %g0, [%g1 + T_DTRACE_FT] ! zero all pid provider flags stub %g2, [%g1 + T_ASTFLAG] ! aston(t) if t->t_dtrace_ast 1: mov %g7, %l3 ! arg2 == misaligned address rdpr %tstate, %g1 ! cwp for trap handler rdpr %cwp, %g4 bclr TSTATE_CWP_MASK, %g1 wrpr %g1, %g4, %tstate wrpr %g0, %g5, %tnpc ! trap handler address FAST_TRAP_DONE /* NOTREACHED */ .check_v9utrap: rdpr %tstate, %g1 btst TSTATE_PRIV, %g1 bnz,a,pn %icc, 3f nop CPU_ADDR(%g4, %g1) ! load CPU struct addr ldn [%g4 + CPU_THREAD], %g5 ! load thread pointer ldn [%g5 + T_PROCP], %g5 ! load proc pointer ldn [%g5 + P_UTRAPS], %g5 ! are there utraps? cmp %g3, T_SOFTWARE_TRAP bne,a,pt %icc, 1f nop brz,pt %g5, 3f ! if p_utraps == NULL goto trap() rdpr %tt, %g3 ! delay - get actual hw trap type sub %g3, 254, %g1 ! UT_TRAP_INSTRUCTION_16 = p_utraps[18] ba,pt %icc, 2f smul %g1, CPTRSIZE, %g2 1: brz,a,pt %g5, 3f ! if p_utraps == NULL goto trap() nop cmp %g3, T_UNIMP_INSTR bne,a,pt %icc, 2f nop mov 1, %g1 st %g1, [%g4 + CPU_TL1_HDLR] ! set CPU_TL1_HDLR rdpr %tpc, %g1 ! ld trapping instruction using lduwa [%g1]ASI_AIUP, %g1 ! "AS IF USER" ASI which could fault st %g0, [%g4 + CPU_TL1_HDLR] ! clr CPU_TL1_HDLR sethi %hi(0xc1c00000), %g4 ! setup mask for illtrap instruction andcc %g1, %g4, %g4 ! and instruction with mask bnz,a,pt %icc, 3f ! if %g4 == zero, %g1 is an ILLTRAP nop ! fall thru to setup 2: ldn [%g5 + %g2], %g5 brnz,a,pt %g5, .setup_v9utrap nop 3: set trap, %g1 ba,pt %xcc, sys_trap sub %g0, 1, %g4 /* NOTREACHED */ /* * Register Inputs: * %g5 user trap handler */ .setup_v9utrap: set trap, %g1 ! setup in case we go mov T_FLUSH_PCB, %g3 ! through sys_trap on sub %g0, 1, %g4 ! the save instruction below /* * If the DTrace pid provider is single stepping a copied-out * instruction, t->t_dtrace_step will be set. In that case we need * to abort the single-stepping (since execution of the instruction * was interrupted) and use the value of t->t_dtrace_npc as the %npc. */ save %sp, -SA(MINFRAME64), %sp ! window for trap handler CPU_ADDR(%g1, %g4) ! load CPU struct addr ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer ldub [%g1 + T_DTRACE_STEP], %g2 ! load t->t_dtrace_step rdpr %tnpc, %l7 ! arg1 == tnpc brz,pt %g2, 1f rdpr %tpc, %l6 ! arg0 == tpc ldub [%g1 + T_DTRACE_AST], %g2 ! load t->t_dtrace_ast ldn [%g1 + T_DTRACE_NPC], %l7 ! arg1 == t->t_dtrace_npc (step) brz,pt %g2, 1f st %g0, [%g1 + T_DTRACE_FT] ! zero all pid provider flags stub %g2, [%g1 + T_ASTFLAG] ! aston(t) if t->t_dtrace_ast 1: rdpr %tstate, %g2 ! cwp for trap handler rdpr %cwp, %g4 bclr TSTATE_CWP_MASK, %g2 wrpr %g2, %g4, %tstate ldn [%g1 + T_PROCP], %g4 ! load proc pointer ldn [%g4 + P_AS], %g4 ! load as pointer ldn [%g4 + A_USERLIMIT], %g4 ! load as userlimit cmp %l7, %g4 ! check for single-step set bne,pt %xcc, 4f nop ldn [%g1 + T_LWP], %g1 ! load klwp pointer ld [%g1 + PCB_STEP], %g4 ! load single-step flag cmp %g4, STEP_ACTIVE ! step flags set in pcb? bne,pt %icc, 4f nop stn %g5, [%g1 + PCB_TRACEPC] ! save trap handler addr in pcb mov %l7, %g4 ! on entry to precise user trap add %l6, 4, %l7 ! handler, %l6 == pc, %l7 == npc ! at time of trap wrpr %g0, %g4, %tnpc ! generate FLTBOUNDS, ! %g4 == userlimit FAST_TRAP_DONE /* NOTREACHED */ 4: wrpr %g0, %g5, %tnpc ! trap handler address FAST_TRAP_DONE_CHK_INTR /* NOTREACHED */ .fp_exception: CPU_ADDR(%g1, %g4) stx %fsr, [%g1 + CPU_TMP1] ldx [%g1 + CPU_TMP1], %g2 /* * Cheetah takes unfinished_FPop trap for certain range of operands * to the "fitos" instruction. Instead of going through the slow * software emulation path, we try to simulate the "fitos" instruction * via "fitod" and "fdtos" provided the following conditions are met: * * fpu_exists is set (if DEBUG) * not in privileged mode * ftt is unfinished_FPop * NXM IEEE trap is not enabled * instruction at %tpc is "fitos" * * Usage: * %g1 per cpu address * %g2 %fsr * %g6 user instruction * * Note that we can take a memory access related trap while trying * to fetch the user instruction. Therefore, we set CPU_TL1_HDLR * flag to catch those traps and let the SFMMU code deal with page * fault and data access exception. */ #if defined(DEBUG) || defined(NEED_FPU_EXISTS) sethi %hi(fpu_exists), %g7 ld [%g7 + %lo(fpu_exists)], %g7 brz,pn %g7, .fp_exception_cont nop #endif rdpr %tstate, %g7 ! branch if in privileged mode btst TSTATE_PRIV, %g7 bnz,pn %xcc, .fp_exception_cont srl %g2, FSR_FTT_SHIFT, %g7 ! extract ftt from %fsr and %g7, (FSR_FTT>>FSR_FTT_SHIFT), %g7 cmp %g7, FTT_UNFIN set FSR_TEM_NX, %g5 bne,pn %xcc, .fp_exception_cont ! branch if NOT unfinished_FPop andcc %g2, %g5, %g0 bne,pn %xcc, .fp_exception_cont ! branch if FSR_TEM_NX enabled rdpr %tpc, %g5 ! get faulting PC or %g0, 1, %g7 st %g7, [%g1 + CPU_TL1_HDLR] ! set tl1_hdlr flag lda [%g5]ASI_USER, %g6 ! get user's instruction st %g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag set FITOS_INSTR_MASK, %g7 and %g6, %g7, %g7 set FITOS_INSTR, %g5 cmp %g7, %g5 bne,pn %xcc, .fp_exception_cont ! branch if not FITOS_INSTR nop /* * This is unfinished FPops trap for "fitos" instruction. We * need to simulate "fitos" via "fitod" and "fdtos" instruction * sequence. * * We need a temporary FP register to do the conversion. Since * both source and destination operands for the "fitos" instruction * have to be within %f0-%f31, we use an FP register from the upper * half to guarantee that it won't collide with the source or the * dest operand. However, we do have to save and restore its value. * * We use %d62 as a temporary FP register for the conversion and * branch to appropriate instruction within the conversion tables * based upon the rs2 and rd values. */ std %d62, [%g1 + CPU_TMP1] ! save original value srl %g6, FITOS_RS2_SHIFT, %g7 and %g7, FITOS_REG_MASK, %g7 set _fitos_fitod_table, %g4 sllx %g7, 2, %g7 jmp %g4 + %g7 ba,pt %xcc, _fitos_fitod_done .empty _fitos_fitod_table: fitod %f0, %d62 fitod %f1, %d62 fitod %f2, %d62 fitod %f3, %d62 fitod %f4, %d62 fitod %f5, %d62 fitod %f6, %d62 fitod %f7, %d62 fitod %f8, %d62 fitod %f9, %d62 fitod %f10, %d62 fitod %f11, %d62 fitod %f12, %d62 fitod %f13, %d62 fitod %f14, %d62 fitod %f15, %d62 fitod %f16, %d62 fitod %f17, %d62 fitod %f18, %d62 fitod %f19, %d62 fitod %f20, %d62 fitod %f21, %d62 fitod %f22, %d62 fitod %f23, %d62 fitod %f24, %d62 fitod %f25, %d62 fitod %f26, %d62 fitod %f27, %d62 fitod %f28, %d62 fitod %f29, %d62 fitod %f30, %d62 fitod %f31, %d62 _fitos_fitod_done: /* * Now convert data back into single precision */ srl %g6, FITOS_RD_SHIFT, %g7 and %g7, FITOS_REG_MASK, %g7 set _fitos_fdtos_table, %g4 sllx %g7, 2, %g7 jmp %g4 + %g7 ba,pt %xcc, _fitos_fdtos_done .empty _fitos_fdtos_table: fdtos %d62, %f0 fdtos %d62, %f1 fdtos %d62, %f2 fdtos %d62, %f3 fdtos %d62, %f4 fdtos %d62, %f5 fdtos %d62, %f6 fdtos %d62, %f7 fdtos %d62, %f8 fdtos %d62, %f9 fdtos %d62, %f10 fdtos %d62, %f11 fdtos %d62, %f12 fdtos %d62, %f13 fdtos %d62, %f14 fdtos %d62, %f15 fdtos %d62, %f16 fdtos %d62, %f17 fdtos %d62, %f18 fdtos %d62, %f19 fdtos %d62, %f20 fdtos %d62, %f21 fdtos %d62, %f22 fdtos %d62, %f23 fdtos %d62, %f24 fdtos %d62, %f25 fdtos %d62, %f26 fdtos %d62, %f27 fdtos %d62, %f28 fdtos %d62, %f29 fdtos %d62, %f30 fdtos %d62, %f31 _fitos_fdtos_done: ldd [%g1 + CPU_TMP1], %d62 ! restore %d62 #if DEBUG /* * Update FPop_unfinished trap kstat */ set fpustat+FPUSTAT_UNFIN_KSTAT, %g7 ldx [%g7], %g5 1: add %g5, 1, %g6 casxa [%g7] ASI_N, %g5, %g6 cmp %g5, %g6 bne,a,pn %xcc, 1b or %g0, %g6, %g5 /* * Update fpu_sim_fitos kstat */ set fpuinfo+FPUINFO_FITOS_KSTAT, %g7 ldx [%g7], %g5 1: add %g5, 1, %g6 casxa [%g7] ASI_N, %g5, %g6 cmp %g5, %g6 bne,a,pn %xcc, 1b or %g0, %g6, %g5 #endif /* DEBUG */ FAST_TRAP_DONE .fp_exception_cont: /* * Let _fp_exception deal with simulating FPop instruction. * Note that we need to pass %fsr in %g2 (already read above). */ set _fp_exception, %g1 ba,pt %xcc, sys_trap sub %g0, 1, %g4 /* * Register windows */ .flushw: .clean_windows: rdpr %tnpc, %g1 wrpr %g1, %tpc add %g1, 4, %g1 wrpr %g1, %tnpc set trap, %g1 mov T_FLUSH_PCB, %g3 ba,pt %xcc, sys_trap sub %g0, 1, %g4 /* * .spill_clean: clean the previous window, restore the wstate, and * "done". * * Entry: %g7 contains new wstate */ .spill_clean: sethi %hi(nwin_minus_one), %g5 ld [%g5 + %lo(nwin_minus_one)], %g5 ! %g5 = nwin - 1 rdpr %cwp, %g6 ! %g6 = %cwp deccc %g6 ! %g6-- movneg %xcc, %g5, %g6 ! if (%g6<0) %g6 = nwin-1 wrpr %g6, %cwp TT_TRACE_L(trace_win) clr %l0 clr %l1 clr %l2 clr %l3 clr %l4 clr %l5 clr %l6 clr %l7 wrpr %g0, %g7, %wstate saved retry ! restores correct %cwp .fix_alignment: CPU_ADDR(%g1, %g2) ! load CPU struct addr to %g1 using %g2 ldn [%g1 + CPU_THREAD], %g1 ! load thread pointer ldn [%g1 + T_PROCP], %g1 mov 1, %g2 stb %g2, [%g1 + P_FIXALIGNMENT] FAST_TRAP_DONE #define STDF_REG(REG, ADDR, TMP) \ sll REG, 3, REG ;\ mark1: set start1, TMP ;\ jmp REG + TMP ;\ nop ;\ start1: ba,pt %xcc, done1 ;\ std %f0, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f32, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f2, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f34, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f4, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f36, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f6, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f38, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f8, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f40, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f10, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f42, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f12, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f44, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f14, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f46, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f16, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f48, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f18, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f50, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f20, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f52, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f22, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f54, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f24, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f56, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f26, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f58, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f28, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f60, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f30, [ADDR + CPU_TMP1] ;\ ba,pt %xcc, done1 ;\ std %f62, [ADDR + CPU_TMP1] ;\ done1: #define LDDF_REG(REG, ADDR, TMP) \ sll REG, 3, REG ;\ mark2: set start2, TMP ;\ jmp REG + TMP ;\ nop ;\ start2: ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f0 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f32 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f2 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f34 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f4 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f36 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f6 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f38 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f8 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f40 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f10 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f42 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f12 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f44 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f14 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f46 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f16 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f48 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f18 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f50 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f20 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f52 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f22 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f54 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f24 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f56 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f26 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f58 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f28 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f60 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f30 ;\ ba,pt %xcc, done2 ;\ ldd [ADDR + CPU_TMP1], %f62 ;\ done2: .lddf_exception_not_aligned: /* %g2 = sfar, %g3 = sfsr */ mov %g2, %g5 ! stash sfar #if defined(DEBUG) || defined(NEED_FPU_EXISTS) sethi %hi(fpu_exists), %g2 ! check fpu_exists ld [%g2 + %lo(fpu_exists)], %g2 brz,a,pn %g2, 4f nop #endif CPU_ADDR(%g1, %g4) or %g0, 1, %g4 st %g4, [%g1 + CPU_TL1_HDLR] ! set tl1_hdlr flag rdpr %tpc, %g2 lda [%g2]ASI_AIUP, %g6 ! get the user's lddf instruction srl %g6, 23, %g1 ! using ldda or not? and %g1, 1, %g1 brz,a,pt %g1, 2f ! check for ldda instruction nop srl %g6, 13, %g1 ! check immflag and %g1, 1, %g1 rdpr %tstate, %g2 ! %tstate in %g2 brnz,a,pn %g1, 1f srl %g2, 31, %g1 ! get asi from %tstate srl %g6, 5, %g1 ! get asi from instruction and %g1, 0xFF, %g1 ! imm_asi field 1: cmp %g1, ASI_P ! primary address space be,a,pt %icc, 2f nop cmp %g1, ASI_PNF ! primary no fault address space be,a,pt %icc, 2f nop cmp %g1, ASI_S ! secondary address space be,a,pt %icc, 2f nop cmp %g1, ASI_SNF ! secondary no fault address space bne,a,pn %icc, 3f nop 2: lduwa [%g5]ASI_USER, %g7 ! get first half of misaligned data add %g5, 4, %g5 ! increment misaligned data address lduwa [%g5]ASI_USER, %g5 ! get second half of misaligned data sllx %g7, 32, %g7 or %g5, %g7, %g5 ! combine data CPU_ADDR(%g7, %g1) ! save data on a per-cpu basis stx %g5, [%g7 + CPU_TMP1] ! save in cpu_tmp1 srl %g6, 25, %g3 ! %g6 has the instruction and %g3, 0x1F, %g3 ! %g3 has rd LDDF_REG(%g3, %g7, %g4) CPU_ADDR(%g1, %g4) st %g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag FAST_TRAP_DONE 3: CPU_ADDR(%g1, %g4) st %g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag 4: set T_USER, %g3 ! trap type in %g3 or %g3, T_LDDF_ALIGN, %g3 mov %g5, %g2 ! misaligned vaddr in %g2 set fpu_trap, %g1 ! goto C for the little and ba,pt %xcc, sys_trap ! no fault little asi's sub %g0, 1, %g4 .stdf_exception_not_aligned: /* %g2 = sfar, %g3 = sfsr */ mov %g2, %g5 #if defined(DEBUG) || defined(NEED_FPU_EXISTS) sethi %hi(fpu_exists), %g7 ! check fpu_exists ld [%g7 + %lo(fpu_exists)], %g3 brz,a,pn %g3, 4f nop #endif CPU_ADDR(%g1, %g4) or %g0, 1, %g4 st %g4, [%g1 + CPU_TL1_HDLR] ! set tl1_hdlr flag rdpr %tpc, %g2 lda [%g2]ASI_AIUP, %g6 ! get the user's stdf instruction srl %g6, 23, %g1 ! using stda or not? and %g1, 1, %g1 brz,a,pt %g1, 2f ! check for stda instruction nop srl %g6, 13, %g1 ! check immflag and %g1, 1, %g1 rdpr %tstate, %g2 ! %tstate in %g2 brnz,a,pn %g1, 1f srl %g2, 31, %g1 ! get asi from %tstate srl %g6, 5, %g1 ! get asi from instruction and %g1, 0xff, %g1 ! imm_asi field 1: cmp %g1, ASI_P ! primary address space be,a,pt %icc, 2f nop cmp %g1, ASI_S ! secondary address space bne,a,pn %icc, 3f nop 2: srl %g6, 25, %g6 and %g6, 0x1F, %g6 ! %g6 has rd CPU_ADDR(%g7, %g1) STDF_REG(%g6, %g7, %g4) ! STDF_REG(REG, ADDR, TMP) ldx [%g7 + CPU_TMP1], %g6 srlx %g6, 32, %g7 stuwa %g7, [%g5]ASI_USER ! first half add %g5, 4, %g5 ! increment misaligned data address stuwa %g6, [%g5]ASI_USER ! second half CPU_ADDR(%g1, %g4) st %g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag FAST_TRAP_DONE 3: CPU_ADDR(%g1, %g4) st %g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag 4: set T_USER, %g3 ! trap type in %g3 or %g3, T_STDF_ALIGN, %g3 mov %g5, %g2 ! misaligned vaddr in %g2 set fpu_trap, %g1 ! goto C for the little and ba,pt %xcc, sys_trap ! nofault little asi's sub %g0, 1, %g4 #ifdef DEBUG_USER_TRAPTRACECTL .traptrace_freeze: mov %l0, %g1 ; mov %l1, %g2 ; mov %l2, %g3 ; mov %l4, %g4 TT_TRACE_L(trace_win) mov %g4, %l4 ; mov %g3, %l2 ; mov %g2, %l1 ; mov %g1, %l0 set trap_freeze, %g1 mov 1, %g2 st %g2, [%g1] FAST_TRAP_DONE .traptrace_unfreeze: set trap_freeze, %g1 st %g0, [%g1] mov %l0, %g1 ; mov %l1, %g2 ; mov %l2, %g3 ; mov %l4, %g4 TT_TRACE_L(trace_win) mov %g4, %l4 ; mov %g3, %l2 ; mov %g2, %l1 ; mov %g1, %l0 FAST_TRAP_DONE #endif /* DEBUG_USER_TRAPTRACECTL */ .getcc: CPU_ADDR(%g1, %g2) stx %o0, [%g1 + CPU_TMP1] ! save %o0 rdpr %tstate, %g3 ! get tstate srlx %g3, PSR_TSTATE_CC_SHIFT, %o0 ! shift ccr to V8 psr set PSR_ICC, %g2 and %o0, %g2, %o0 ! mask out the rest srl %o0, PSR_ICC_SHIFT, %o0 ! right justify wrpr %g0, 0, %gl mov %o0, %g1 ! move ccr to normal %g1 wrpr %g0, 1, %gl ! cannot assume globals retained their values after increasing %gl CPU_ADDR(%g1, %g2) ldx [%g1 + CPU_TMP1], %o0 ! restore %o0 FAST_TRAP_DONE .setcc: CPU_ADDR(%g1, %g2) stx %o0, [%g1 + CPU_TMP1] ! save %o0 wrpr %g0, 0, %gl mov %g1, %o0 wrpr %g0, 1, %gl ! cannot assume globals retained their values after increasing %gl CPU_ADDR(%g1, %g2) sll %o0, PSR_ICC_SHIFT, %g2 set PSR_ICC, %g3 and %g2, %g3, %g2 ! mask out rest sllx %g2, PSR_TSTATE_CC_SHIFT, %g2 rdpr %tstate, %g3 ! get tstate srl %g3, 0, %g3 ! clear upper word or %g3, %g2, %g3 ! or in new bits wrpr %g3, %tstate ldx [%g1 + CPU_TMP1], %o0 ! restore %o0 FAST_TRAP_DONE /* * getpsr(void) * Note that the xcc part of the ccr is not provided. * The V8 code shows why the V9 trap is not faster: * #define GETPSR_TRAP() \ * mov %psr, %i0; jmp %l2; rett %l2+4; nop; */ .type .getpsr, #function .getpsr: rdpr %tstate, %g1 ! get tstate srlx %g1, PSR_TSTATE_CC_SHIFT, %o0 ! shift ccr to V8 psr set PSR_ICC, %g2 and %o0, %g2, %o0 ! mask out the rest rd %fprs, %g1 ! get fprs and %g1, FPRS_FEF, %g2 ! mask out dirty upper/lower sllx %g2, PSR_FPRS_FEF_SHIFT, %g2 ! shift fef to V8 psr.ef or %o0, %g2, %o0 ! or result into psr.ef set V9_PSR_IMPLVER, %g2 ! SI assigned impl/ver: 0xef or %o0, %g2, %o0 ! or psr.impl/ver FAST_TRAP_DONE SET_SIZE(.getpsr) /* * setpsr(newpsr) * Note that there is no support for ccr.xcc in the V9 code. */ .type .setpsr, #function .setpsr: rdpr %tstate, %g1 ! get tstate ! setx TSTATE_V8_UBITS, %g2 or %g0, CCR_ICC, %g3 sllx %g3, TSTATE_CCR_SHIFT, %g2 andn %g1, %g2, %g1 ! zero current user bits set PSR_ICC, %g2 and %g2, %o0, %g2 ! clear all but psr.icc bits sllx %g2, PSR_TSTATE_CC_SHIFT, %g3 ! shift to tstate.ccr.icc wrpr %g1, %g3, %tstate ! write tstate set PSR_EF, %g2 and %g2, %o0, %g2 ! clear all but fp enable bit srlx %g2, PSR_FPRS_FEF_SHIFT, %g4 ! shift ef to V9 fprs.fef wr %g0, %g4, %fprs ! write fprs CPU_ADDR(%g1, %g2) ! load CPU struct addr to %g1 ldn [%g1 + CPU_THREAD], %g2 ! load thread pointer ldn [%g2 + T_LWP], %g3 ! load klwp pointer ldn [%g3 + LWP_FPU], %g2 ! get lwp_fpu pointer stuw %g4, [%g2 + FPU_FPRS] ! write fef value to fpu_fprs srlx %g4, 2, %g4 ! shift fef value to bit 0 stub %g4, [%g2 + FPU_EN] ! write fef value to fpu_en FAST_TRAP_DONE SET_SIZE(.setpsr) /* * getlgrp * get home lgrpid on which the calling thread is currently executing. */ .type .getlgrp, #function .getlgrp: ! Thanks for the incredibly helpful comments CPU_ADDR(%g1, %g2) ! load CPU struct addr to %g1 using %g2 ld [%g1 + CPU_ID], %o0 ! load cpu_id ldn [%g1 + CPU_THREAD], %g2 ! load thread pointer ldn [%g2 + T_LPL], %g2 ! load lpl pointer ld [%g2 + LPL_LGRPID], %g1 ! load lpl_lgrpid sra %g1, 0, %o1 FAST_TRAP_DONE SET_SIZE(.getlgrp) /* * Entry for old 4.x trap (trap 0). */ ENTRY_NP(syscall_trap_4x) CPU_ADDR(%g1, %g2) ! load CPU struct addr to %g1 using %g2 ldn [%g1 + CPU_THREAD], %g2 ! load thread pointer ldn [%g2 + T_LWP], %g2 ! load klwp pointer ld [%g2 + PCB_TRAP0], %g2 ! lwp->lwp_pcb.pcb_trap0addr brz,pn %g2, 1f ! has it been set? st %l0, [%g1 + CPU_TMP1] ! delay - save some locals st %l1, [%g1 + CPU_TMP2] rdpr %tnpc, %l1 ! save old tnpc wrpr %g0, %g2, %tnpc ! setup tnpc mov %g1, %l0 ! save CPU struct addr wrpr %g0, 0, %gl mov %l1, %g6 ! pass tnpc to user code in %g6 wrpr %g0, 1, %gl ld [%l0 + CPU_TMP2], %l1 ! restore locals ld [%l0 + CPU_TMP1], %l0 FAST_TRAP_DONE_CHK_INTR 1: ! ! check for old syscall mmap which is the only different one which ! must be the same. Others are handled in the compatibility library. ! mov %g1, %l0 ! save CPU struct addr wrpr %g0, 0, %gl cmp %g1, OSYS_mmap ! compare to old 4.x mmap movz %icc, SYS_mmap, %g1 wrpr %g0, 1, %gl ld [%l0 + CPU_TMP1], %l0 SYSCALL(syscall_trap32) SET_SIZE(syscall_trap_4x) /* * Handler for software trap 9. * Set trap0 emulation address for old 4.x system call trap. * XXX - this should be a system call. */ ENTRY_NP(set_trap0_addr) CPU_ADDR(%g1, %g2) ! load CPU struct addr to %g1 using %g2 st %l0, [%g1 + CPU_TMP1] ! save some locals st %l1, [%g1 + CPU_TMP2] mov %g1, %l0 ! preserve CPU addr wrpr %g0, 0, %gl mov %g1, %l1 wrpr %g0, 1, %gl ! cannot assume globals retained their values after increasing %gl ldn [%l0 + CPU_THREAD], %g2 ! load thread pointer ldn [%g2 + T_LWP], %g2 ! load klwp pointer andn %l1, 3, %l1 ! force alignment st %l1, [%g2 + PCB_TRAP0] ! lwp->lwp_pcb.pcb_trap0addr ld [%l0 + CPU_TMP2], %l1 ! restore locals ld [%l0 + CPU_TMP1], %l0 FAST_TRAP_DONE SET_SIZE(set_trap0_addr) /* * mmu_trap_tl1 * trap handler for unexpected mmu traps. * simply checks if the trap was a user lddf/stdf alignment trap, in which * case we go to fpu_trap or a user trap from the window handler, in which * case we go save the state on the pcb. Otherwise, we go to ptl1_panic. */ .type mmu_trap_tl1, #function mmu_trap_tl1: #ifdef TRAPTRACE TRACE_PTR(%g5, %g6) GET_TRACE_TICK(%g6) stxa %g6, [%g5 + TRAP_ENT_TICK]%asi TRACE_SAVE_TL_GL_REGS(%g5, %g6) rdpr %tt, %g6 stha %g6, [%g5 + TRAP_ENT_TT]%asi rdpr %tstate, %g6 stxa %g6, [%g5 + TRAP_ENT_TSTATE]%asi stna %sp, [%g5 + TRAP_ENT_SP]%asi stna %g0, [%g5 + TRAP_ENT_TR]%asi rdpr %tpc, %g6 stna %g6, [%g5 + TRAP_ENT_TPC]%asi MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + MMFSA_D_ADDR], %g6 stna %g6, [%g5 + TRAP_ENT_F1]%asi ! MMU fault address CPU_PADDR(%g7, %g6); add %g7, CPU_TL1_HDLR, %g7 lda [%g7]ASI_MEM, %g6 stna %g6, [%g5 + TRAP_ENT_F2]%asi MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + MMFSA_D_TYPE], %g7 ! XXXQ should be a MMFSA_F_ constant? ldx [%g6 + MMFSA_D_CTX], %g6 sllx %g6, SFSR_CTX_SHIFT, %g6 or %g6, %g7, %g6 stna %g6, [%g5 + TRAP_ENT_F3]%asi ! MMU context/type set 0xdeadbeef, %g6 stna %g6, [%g5 + TRAP_ENT_F4]%asi TRACE_NEXT(%g5, %g6, %g7) #endif /* TRAPTRACE */ CPU_PADDR(%g7, %g6); add %g7, CPU_TL1_HDLR, %g7 ! %g7 = &cpu_m.tl1_hdlr (PA) lda [%g7]ASI_MEM, %g6 brz,a,pt %g6, 1f nop sta %g0, [%g7]ASI_MEM ! XXXQ need to setup registers for sfmmu_mmu_trap? ba,a,pt %xcc, sfmmu_mmu_trap ! handle page faults 1: rdpr %tpc, %g7 /* in user_rtt? */ set rtt_fill_start, %g6 cmp %g7, %g6 blu,pn %xcc, 6f .empty set rtt_fill_end, %g6 cmp %g7, %g6 bgeu,pn %xcc, 6f nop set fault_rtt_fn1, %g7 ba,a 7f 6: ! check to see if the trap pc is in a window spill/fill handling rdpr %tpc, %g7 /* tpc should be in the trap table */ set trap_table, %g6 cmp %g7, %g6 blu,a,pn %xcc, ptl1_panic mov PTL1_BAD_MMUTRAP, %g1 set etrap_table, %g6 cmp %g7, %g6 bgeu,a,pn %xcc, ptl1_panic mov PTL1_BAD_MMUTRAP, %g1 ! pc is inside the trap table, convert to trap type srl %g7, 5, %g6 ! XXXQ need #define and %g6, 0x1ff, %g6 ! XXXQ need #define ! and check for a window trap type and %g6, WTRAP_TTMASK, %g6 cmp %g6, WTRAP_TYPE bne,a,pn %xcc, ptl1_panic mov PTL1_BAD_MMUTRAP, %g1 andn %g7, WTRAP_ALIGN, %g7 /* 128 byte aligned */ add %g7, WTRAP_FAULTOFF, %g7 7: ! Arguments are passed in the global set active after the ! 'done' instruction. Before switching sets, must save ! the calculated next pc wrpr %g0, %g7, %tnpc wrpr %g0, 1, %gl rdpr %tt, %g5 MMU_FAULT_STATUS_AREA(%g7) cmp %g5, T_ALIGNMENT be,pn %xcc, 1f ldx [%g7 + MMFSA_D_ADDR], %g6 ldx [%g7 + MMFSA_D_CTX], %g7 srlx %g6, MMU_PAGESHIFT, %g6 /* align address */ cmp %g7, USER_CONTEXT_TYPE sllx %g6, MMU_PAGESHIFT, %g6 movgu %icc, USER_CONTEXT_TYPE, %g7 or %g6, %g7, %g6 /* TAG_ACCESS */ 1: done SET_SIZE(mmu_trap_tl1) /* * Several traps use kmdb_trap and kmdb_trap_tl1 as their handlers. These * traps are valid only when kmdb is loaded. When the debugger is active, * the code below is rewritten to transfer control to the appropriate * debugger entry points. */ .global kmdb_trap .align 8 kmdb_trap: ba,a trap_table0 jmp %g1 + 0 nop .global kmdb_trap_tl1 .align 8 kmdb_trap_tl1: ba,a trap_table0 jmp %g1 + 0 nop /* * This entry is copied from OBP's trap table during boot. */ .global obp_bpt .align 8 obp_bpt: NOT #ifdef TRAPTRACE /* * TRAPTRACE support. * labels here are branched to with "rd %pc, %g7" in the delay slot. * Return is done by "jmp %g7 + 4". */ trace_dmmu: TRACE_PTR(%g3, %g6) GET_TRACE_TICK(%g6) stxa %g6, [%g3 + TRAP_ENT_TICK]%asi TRACE_SAVE_TL_GL_REGS(%g3, %g6) rdpr %tt, %g6 stha %g6, [%g3 + TRAP_ENT_TT]%asi rdpr %tstate, %g6 stxa %g6, [%g3 + TRAP_ENT_TSTATE]%asi stna %sp, [%g3 + TRAP_ENT_SP]%asi rdpr %tpc, %g6 stna %g6, [%g3 + TRAP_ENT_TPC]%asi MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + MMFSA_D_ADDR], %g4 stxa %g4, [%g3 + TRAP_ENT_TR]%asi ldx [%g6 + MMFSA_D_CTX], %g4 stxa %g4, [%g3 + TRAP_ENT_F1]%asi ldx [%g6 + MMFSA_D_TYPE], %g4 stxa %g4, [%g3 + TRAP_ENT_F2]%asi stxa %g6, [%g3 + TRAP_ENT_F3]%asi stna %g0, [%g3 + TRAP_ENT_F4]%asi TRACE_NEXT(%g3, %g4, %g5) jmp %g7 + 4 nop trace_immu: TRACE_PTR(%g3, %g6) GET_TRACE_TICK(%g6) stxa %g6, [%g3 + TRAP_ENT_TICK]%asi TRACE_SAVE_TL_GL_REGS(%g3, %g6) rdpr %tt, %g6 stha %g6, [%g3 + TRAP_ENT_TT]%asi rdpr %tstate, %g6 stxa %g6, [%g3 + TRAP_ENT_TSTATE]%asi stna %sp, [%g3 + TRAP_ENT_SP]%asi rdpr %tpc, %g6 stna %g6, [%g3 + TRAP_ENT_TPC]%asi MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + MMFSA_I_ADDR], %g4 stxa %g4, [%g3 + TRAP_ENT_TR]%asi ldx [%g6 + MMFSA_I_CTX], %g4 stxa %g4, [%g3 + TRAP_ENT_F1]%asi ldx [%g6 + MMFSA_I_TYPE], %g4 stxa %g4, [%g3 + TRAP_ENT_F2]%asi stxa %g6, [%g3 + TRAP_ENT_F3]%asi stna %g0, [%g3 + TRAP_ENT_F4]%asi TRACE_NEXT(%g3, %g4, %g5) jmp %g7 + 4 nop trace_gen: TRACE_PTR(%g3, %g6) GET_TRACE_TICK(%g6) stxa %g6, [%g3 + TRAP_ENT_TICK]%asi TRACE_SAVE_TL_GL_REGS(%g3, %g6) rdpr %tt, %g6 stha %g6, [%g3 + TRAP_ENT_TT]%asi rdpr %tstate, %g6 stxa %g6, [%g3 + TRAP_ENT_TSTATE]%asi stna %sp, [%g3 + TRAP_ENT_SP]%asi rdpr %tpc, %g6 stna %g6, [%g3 + TRAP_ENT_TPC]%asi stna %g0, [%g3 + TRAP_ENT_TR]%asi stna %g0, [%g3 + TRAP_ENT_F1]%asi stna %g0, [%g3 + TRAP_ENT_F2]%asi stna %g0, [%g3 + TRAP_ENT_F3]%asi stna %g0, [%g3 + TRAP_ENT_F4]%asi TRACE_NEXT(%g3, %g4, %g5) jmp %g7 + 4 nop trace_win: TRACE_WIN_INFO(0, %l0, %l1, %l2) ! Keep the locals as clean as possible, caller cleans %l4 clr %l2 clr %l1 jmp %l4 + 4 clr %l0 /* * Trace a tsb hit * g1 = tsbe pointer (in/clobbered) * g2 = tag access register (in) * g3 - g4 = scratch (clobbered) * g5 = tsbe data (in) * g6 = scratch (clobbered) * g7 = pc we jumped here from (in) */ ! Do not disturb %g5, it will be used after the trace ALTENTRY(trace_tsbhit) TRACE_TSBHIT(0) jmp %g7 + 4 nop /* * Trace a TSB miss * * g1 = tsb8k pointer (in) * g2 = tag access register (in) * g3 = tsb4m pointer (in) * g4 = tsbe tag (in/clobbered) * g5 - g6 = scratch (clobbered) * g7 = pc we jumped here from (in) */ .global trace_tsbmiss trace_tsbmiss: membar #Sync sethi %hi(FLUSH_ADDR), %g6 flush %g6 TRACE_PTR(%g5, %g6) GET_TRACE_TICK(%g6) stxa %g6, [%g5 + TRAP_ENT_TICK]%asi stna %g2, [%g5 + TRAP_ENT_SP]%asi ! tag access stna %g4, [%g5 + TRAP_ENT_F1]%asi ! XXX? tsb tag rdpr %tnpc, %g6 stna %g6, [%g5 + TRAP_ENT_F2]%asi stna %g1, [%g5 + TRAP_ENT_F3]%asi ! tsb8k pointer rdpr %tpc, %g6 stna %g6, [%g5 + TRAP_ENT_TPC]%asi TRACE_SAVE_TL_GL_REGS(%g5, %g6) rdpr %tt, %g6 or %g6, TT_MMU_MISS, %g4 stha %g4, [%g5 + TRAP_ENT_TT]%asi mov MMFSA_D_ADDR, %g4 cmp %g6, FAST_IMMU_MISS_TT move %xcc, MMFSA_I_ADDR, %g4 cmp %g6, T_INSTR_MMU_MISS move %xcc, MMFSA_I_ADDR, %g4 MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + %g4], %g6 stxa %g6, [%g5 + TRAP_ENT_TSTATE]%asi ! tag target cmp %g4, MMFSA_D_ADDR move %xcc, MMFSA_D_CTX, %g4 movne %xcc, MMFSA_I_CTX, %g4 MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + %g4], %g6 stxa %g6, [%g5 + TRAP_ENT_F4]%asi ! context ID stna %g3, [%g5 + TRAP_ENT_TR]%asi ! tsb4m pointer TRACE_NEXT(%g5, %g4, %g6) jmp %g7 + 4 nop /* * g2 = tag access register (in) * g3 = ctx type (0, 1 or 2) (in) (not used) */ trace_dataprot: membar #Sync sethi %hi(FLUSH_ADDR), %g6 flush %g6 TRACE_PTR(%g1, %g6) GET_TRACE_TICK(%g6) stxa %g6, [%g1 + TRAP_ENT_TICK]%asi rdpr %tpc, %g6 stna %g6, [%g1 + TRAP_ENT_TPC]%asi rdpr %tstate, %g6 stxa %g6, [%g1 + TRAP_ENT_TSTATE]%asi stna %g2, [%g1 + TRAP_ENT_SP]%asi ! tag access reg stna %g0, [%g1 + TRAP_ENT_F1]%asi stna %g0, [%g1 + TRAP_ENT_F2]%asi stna %g0, [%g1 + TRAP_ENT_F3]%asi stna %g0, [%g1 + TRAP_ENT_F4]%asi TRACE_SAVE_TL_GL_REGS(%g1, %g6) rdpr %tt, %g6 stha %g6, [%g1 + TRAP_ENT_TT]%asi mov MMFSA_D_CTX, %g4 cmp %g6, FAST_IMMU_MISS_TT move %xcc, MMFSA_I_CTX, %g4 cmp %g6, T_INSTR_MMU_MISS move %xcc, MMFSA_I_CTX, %g4 MMU_FAULT_STATUS_AREA(%g6) ldx [%g6 + %g4], %g6 stxa %g6, [%g1 + TRAP_ENT_TR]%asi ! context ID TRACE_NEXT(%g1, %g4, %g5) jmp %g7 + 4 nop #endif /* TRAPTRACE */ /* * Handle watchdog reset trap. Enable the MMU using the MMU_ENABLE * HV service, which requires the return target to be specified as a VA * since we are enabling the MMU. We set the target to ptl1_panic. */ .type .watchdog_trap, #function .watchdog_trap: mov 1, %o0 setx ptl1_panic, %g2, %o1 mov MMU_ENABLE, %o5 ta FAST_TRAP done SET_SIZE(.watchdog_trap) /* * synthesize for trap(): SFAR in %g2, SFSR in %g3 */ .type .dmmu_exc_lddf_not_aligned, #function .dmmu_exc_lddf_not_aligned: MMU_FAULT_STATUS_AREA(%g3) ldx [%g3 + MMFSA_D_ADDR], %g2 /* Fault type not available in MMU fault status area */ mov MMFSA_F_UNALIGN, %g1 ldx [%g3 + MMFSA_D_CTX], %g3 sllx %g3, SFSR_CTX_SHIFT, %g3 btst 1, %sp bnz,pt %xcc, .lddf_exception_not_aligned or %g3, %g1, %g3 /* SFSR */ ba,a,pt %xcc, .mmu_exception_not_aligned SET_SIZE(.dmmu_exc_lddf_not_aligned) /* * synthesize for trap(): SFAR in %g2, SFSR in %g3 */ .type .dmmu_exc_stdf_not_aligned, #function .dmmu_exc_stdf_not_aligned: MMU_FAULT_STATUS_AREA(%g3) ldx [%g3 + MMFSA_D_ADDR], %g2 /* Fault type not available in MMU fault status area */ mov MMFSA_F_UNALIGN, %g1 ldx [%g3 + MMFSA_D_CTX], %g3 sllx %g3, SFSR_CTX_SHIFT, %g3 btst 1, %sp bnz,pt %xcc, .stdf_exception_not_aligned or %g3, %g1, %g3 /* SFSR */ ba,a,pt %xcc, .mmu_exception_not_aligned SET_SIZE(.dmmu_exc_stdf_not_aligned) .type .dmmu_exception, #function .dmmu_exception: MMU_FAULT_STATUS_AREA(%g3) ldx [%g3 + MMFSA_D_ADDR], %g2 ldx [%g3 + MMFSA_D_TYPE], %g1 ldx [%g3 + MMFSA_D_CTX], %g4 srlx %g2, MMU_PAGESHIFT, %g2 /* align address */ sllx %g2, MMU_PAGESHIFT, %g2 sllx %g4, SFSR_CTX_SHIFT, %g3 or %g3, %g1, %g3 /* SFSR */ cmp %g4, USER_CONTEXT_TYPE movgeu %icc, USER_CONTEXT_TYPE, %g4 or %g2, %g4, %g2 /* TAG_ACCESS */ ba,pt %xcc, .mmu_exception_end mov T_DATA_EXCEPTION, %g1 SET_SIZE(.dmmu_exception) /* * fast_trap_done, fast_trap_done_chk_intr: * * Due to the design of UltraSPARC pipeline, pending interrupts are not * taken immediately after a RETRY or DONE instruction which causes IE to * go from 0 to 1. Instead, the instruction at %tpc or %tnpc is allowed * to execute first before taking any interrupts. If that instruction * results in other traps, and if the corresponding trap handler runs * entirely at TL=1 with interrupts disabled, then pending interrupts * won't be taken until after yet another instruction following the %tpc * or %tnpc. * * A malicious user program can use this feature to block out interrupts * for extended durations, which can result in send_mondo_timeout kernel * panic. * * This problem is addressed by servicing any pending interrupts via * sys_trap before returning back to the user mode from a fast trap * handler. The "done" instruction within a fast trap handler, which * runs entirely at TL=1 with interrupts disabled, is replaced with the * FAST_TRAP_DONE macro, which branches control to this fast_trap_done * entry point. * * We check for any pending interrupts here and force a sys_trap to * service those interrupts, if any. To minimize overhead, pending * interrupts are checked if the %tpc happens to be at 16K boundary, * which allows a malicious program to execute at most 4K consecutive * instructions before we service any pending interrupts. If a worst * case fast trap handler takes about 2 usec, then interrupts will be * blocked for at most 8 msec, less than a clock tick. * * For the cases where we don't know if the %tpc will cross a 16K * boundary, we can't use the above optimization and always process * any pending interrupts via fast_frap_done_chk_intr entry point. * * Entry Conditions: * %pstate am:0 priv:1 ie:0 * globals are AG (not normal globals) */ .global fast_trap_done, fast_trap_done_chk_intr fast_trap_done: rdpr %tpc, %g5 sethi %hi(0xffffc000), %g6 ! 1's complement of 0x3fff andncc %g5, %g6, %g0 ! check lower 14 bits of %tpc bz,pn %icc, 1f ! branch if zero (lower 32 bits only) nop done fast_trap_done_chk_intr: 1: rd SOFTINT, %g6 brnz,pn %g6, 2f ! branch if any pending intr nop done 2: /* * We get here if there are any pending interrupts. * Adjust %tpc/%tnpc as we'll be resuming via "retry" * instruction. */ rdpr %tnpc, %g5 wrpr %g0, %g5, %tpc add %g5, 4, %g5 wrpr %g0, %g5, %tnpc /* * Force a dummy sys_trap call so that interrupts can be serviced. */ set fast_trap_dummy_call, %g1 ba,pt %xcc, sys_trap mov -1, %g4 fast_trap_dummy_call: retl nop /* * Currently the brand syscall interposition code is not enabled by * default. Instead, when a branded zone is first booted the brand * infrastructure will patch the trap table so that the syscall * entry points are redirected to syscall_wrapper32 and syscall_wrapper * for ILP32 and LP64 syscalls respectively. this is done in * brand_plat_interposition_enable(). Note that the syscall wrappers * below do not collect any trap trace data since the syscall hot patch * points are reached after trap trace data has already been collected. */ #define BRAND_CALLBACK(callback_id) \ CPU_ADDR(%g2, %g1) /* load CPU struct addr to %g2 */ ;\ ldn [%g2 + CPU_THREAD], %g3 /* load thread pointer */ ;\ ldn [%g3 + T_PROCP], %g3 /* get proc pointer */ ;\ ldn [%g3 + P_BRAND], %g3 /* get brand pointer */ ;\ brz %g3, 1f /* No brand? No callback. */ ;\ nop ;\ ldn [%g3 + B_MACHOPS], %g3 /* get machops list */ ;\ ldn [%g3 + (callback_id << 3)], %g3 ;\ brz %g3, 1f ;\ /* \ * This isn't pretty. We want a low-latency way for the callback \ * routine to decline to do anything. We just pass in an address \ * the routine can directly jmp back to, pretending that nothing \ * has happened. \ * \ * %g1: return address (where the brand handler jumps back to) \ * %g2: address of CPU structure \ * %g3: address of brand handler (where we will jump to) \ */ \ mov %pc, %g1 ;\ add %g1, 16, %g1 ;\ jmp %g3 ;\ nop ;\ 1: ENTRY_NP(syscall_wrapper32) BRAND_CALLBACK(BRAND_CB_SYSCALL32) SYSCALL_NOTT(syscall_trap32) SET_SIZE(syscall_wrapper32) ENTRY_NP(syscall_wrapper) BRAND_CALLBACK(BRAND_CB_SYSCALL) SYSCALL_NOTT(syscall_trap) SET_SIZE(syscall_wrapper) #endif /* lint */