root/usr/src/uts/sun4v/sys/traptrace.h
/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _SYS_TRAPTRACE_H
#define _SYS_TRAPTRACE_H

#ifdef  __cplusplus
extern "C" {
#endif

#define TRAP_TENABLE_ALL        -1      /* enable all hv traptracing */
#define TRAP_TDISABLE_ALL       0       /* disable all hv traptracing */
#define TRAP_TFREEZE_ALL        -1      /* freeze all hv traptracing */
#define TRAP_TUNFREEZE_ALL      0       /* unfreeze all hv traptracing */

/*
 * Trap tracing. If TRAPTRACE is defined, every trap records info
 * in a circular buffer.  Define TRAPTRACE in Makefile.$ARCH.
 *
 * Trap trace records are TRAP_ENT_SIZE bytes, consisting of the
 * %tick, %tl, %tt, %tpc, %tstate, %sp, and a few other words:
 *
 * struct trap_trace_record {
 *      ushort_t tl, tt;
 *      long    pc;
 *      int64_t tstate, tick;
 *      long    sp, tr, f1, f2, f3, f4;
 * };
 *
 * Note that for UltraSparc III and beyond %stick is used in place of %tick
 * unless compiled with TRAPTRACE_FORCE_TICK.
 *
 * Auxilliary entries (not of just a trap), have obvious non-%tt values in
 * the TRAP_ENT_TT field
 */

#define TRAP_TBUF       (2 * PAGESIZE)  /* default size is two pages */

#ifndef _ASM

/*
 * HV Trap trace header
 */
typedef struct htrap_trace_hdr {
        uint64_t        last_offset;    /* most recently completed entry */
        uint64_t        offset;         /* next entry to be written */
        uint64_t        dummy1;
        uint64_t        dummy2;
        uint64_t        dummy3;
        uint64_t        dummy4;
        uint64_t        dummy5;
        uint64_t        dummy6;
} htrap_trace_hdr_t;

/*
 * HV Trap trace record
 */
struct htrap_trace_record {
        uint8_t         tt_ty;          /* Indicates HV or Guest entry */
        uint8_t         tt_hpstate;     /* Hyper-privilege State */
        uint8_t         tt_tl;          /* Trap level */
        uint8_t         tt_gl;          /* Global register level */
        uint16_t        tt_tt;          /* Trap type */
        uint16_t        tt_tag;         /* Extended Trap Indentifier */
        uint64_t        tt_tstate;      /* Trap state */
        uint64_t        tt_tick;        /* Tick */
        uint64_t        tt_tpc;         /* Trap PC */
        uint64_t        tt_f1;          /* Entry specific */
        uint64_t        tt_f2;          /* Entry specific */
        uint64_t        tt_f3;          /* Entry specific */
        uint64_t        tt_f4;          /* Entry specific */
};

/*
 * Kernel Trap trace record
 */
struct trap_trace_record {
        uint8_t         tt_tl;
        uint8_t         tt_gl;
        uint16_t        tt_tt;
        uintptr_t       tt_tpc;
        uint64_t        tt_tstate;
        uint64_t        tt_tick;
        uintptr_t       tt_sp;
        uintptr_t       tt_tr;
        uintptr_t       tt_f1;
        uintptr_t       tt_f2;
        uintptr_t       tt_f3;
        uintptr_t       tt_f4;
};

#define TRAP_TSIZE      ((TRAP_TBUF / sizeof (struct trap_trace_record)) * \
                        sizeof (struct trap_trace_record))

/* Rounding not needed, done for consistency */
#define HTRAP_TSIZE     ((TRAP_TBUF / sizeof (struct htrap_trace_record)) * \
                        sizeof (struct htrap_trace_record))

#else

#define TRAP_TSIZE      ((TRAP_TBUF / TRAP_ENT_SIZE) * TRAP_ENT_SIZE)
#define HTRAP_TSIZE     ((TRAP_TBUF / HTRAP_ENT_SIZE) * HTRAP_ENT_SIZE)

#endif

/*
 * Trap tracing buffer header.
 */

#ifndef _ASM

/*
 * Example buffer header stored in locore.s:
 *
 * (the actual implementation could be .skip TRAPTR_SIZE*NCPU)
 */
typedef union {
    struct {
        caddr_t         vaddr_base;     /* virtual address of top of buffer */
        uint64_t        paddr_base;     /* physical address of buffer */
        uint_t          last_offset;    /* to "know" what trace completed */
        uint_t          offset;         /* current index into buffer (bytes) */
        uint_t          limit;          /* upper limit on index */
        uchar_t         asi;            /* cache for real asi */
        caddr_t         hvaddr_base;    /* HV virtual addr of top of buffer */
        uint64_t        hpaddr_base;    /* HV physical addr of buffer */
        uint_t          hlimit;         /* HV upper limit on index */
        } d;
    char                cache_linesize[64];
} TRAP_TRACE_CTL;

#ifdef _KERNEL

extern TRAP_TRACE_CTL   trap_trace_ctl[];       /* allocated in locore.s */
extern int              trap_trace_bufsize;     /* default buffer size */
extern char             trap_tr0[];             /* prealloc buf for boot cpu */
extern int              trap_freeze;            /* freeze the trap trace */
extern caddr_t          ttrace_buf;             /* kmem64 buffer */
extern int              ttrace_index;           /* index used */
extern size_t           calc_traptrace_sz(void);

extern int              htrap_trace_bufsize;    /* default hv buffer size */
extern int              mach_htraptrace_enable;
extern void mach_htraptrace_setup(int);
extern void mach_htraptrace_configure(int);
extern void mach_htraptrace_cleanup(int);

#endif

/*
 * freeze the trap trace
 */
#define TRAPTRACE_FREEZE        trap_freeze = 1;
#define TRAPTRACE_UNFREEZE      trap_freeze = 0;

#else /* _ASM */

#include <sys/machthread.h>
#include <sys/machclock.h>

/*
 * Offsets of words in trap_trace_ctl:
 */
/*
 * XXX This should be done with genassym
 */
#define TRAPTR_VBASE    0               /* virtual address of buffer */
#define TRAPTR_LAST_OFFSET 16           /* last completed trace entry */
#define TRAPTR_OFFSET   20              /* next trace entry pointer */
#define TRAPTR_LIMIT    24              /* pointer past end of buffer */
#define TRAPTR_PBASE    8               /* start of buffer */
#define TRAPTR_ASIBUF   28              /* cache of current asi */

#define TRAPTR_HVBASE   32              /* HV virtual address of buffer */
#define TRAPTR_HPBASE   40              /* HV start of buffer */
#define TRAPTR_HLIMIT   48              /* HV pointer past end of buffer */

#define TRAPTR_SIZE_SHIFT       6       /* shift count -- per CPU indexing */
#define TRAPTR_SIZE             (1<<TRAPTR_SIZE_SHIFT)

#define TRAPTR_ASI      ASI_MEM         /* ASI to use for TRAPTR access */

/*
 * Use new %stick register for UltraSparc III and beyond for
 * sane debugging of mixed speed CPU systems. Use TRAPTRACE_FORCE_TICK
 * for finer granularity on same speed systems. Note that traptrace
 * %tick or %stick reads use the NO_SUSPEND_CHECK version of the
 * register read macros. This requires fewer registers and a few less
 * instructions to execute. As a result, if a suspend operation occurs
 * while traptrace is executing GET_TRACE_TICK between the time that
 * the counter offset variable is read and the hardware register is read,
 * this traptrace entry in the log will have an incorrect %tick value
 * since it is derived from a pre-suspend offset variable and a post-
 * suspend hardware counter.
 */
#ifdef  TRAPTRACE_FORCE_TICK
#define GET_TRACE_TICK(reg, scr)                                \
        RD_TICK_NO_SUSPEND_CHECK(reg, scr);
#else
#define GET_TRACE_TICK(reg, scr)                                \
        RD_TICKSTICK_FLAG(reg, scr, traptrace_use_stick);
#endif

/*
 * TRACE_PTR(ptr, scr1) - get trap trace entry physical pointer.
 *      ptr is the register to receive the trace pointer.
 *      scr1 is a different register to be used as scratch.
 * TRACING now needs a known processor state.  Hence the assertion.
 *      NOTE: this caches and resets %asi
 */
#define TRACE_PTR(ptr, scr1)                            \
        sethi   %hi(trap_freeze), ptr;                  \
        ld      [ptr + %lo(trap_freeze)], ptr;          \
        /* CSTYLED */                                   \
        brnz,pn ptr, .+20; /* skip assertion */         \
        rdpr    %pstate, scr1;                          \
        andcc   scr1, PSTATE_IE | PSTATE_AM, scr1;      \
        /* CSTYLED */                                   \
        bne,a,pn %icc, trace_ptr_panic;                 \
        rd      %pc, %g1;                               \
        CPU_INDEX(scr1, ptr);                           \
        sll     scr1, TRAPTR_SIZE_SHIFT, scr1;          \
        set     trap_trace_ctl, ptr;                    \
        add     ptr, scr1, scr1;                        \
        rd      %asi, ptr;                              \
        stb     ptr, [scr1 + TRAPTR_ASIBUF];            \
        sethi   %hi(trap_freeze), ptr;                  \
        ld      [ptr + %lo(trap_freeze)], ptr;          \
        /* CSTYLED */                                   \
        brnz,pn ptr, .+20; /* skip assertion */         \
        ld      [scr1 + TRAPTR_LIMIT], ptr;             \
        tst     ptr;                                    \
        /* CSTYLED */                                   \
        be,a,pn %icc, trace_ptr_panic;                  \
        rd      %pc, %g1;                               \
        ldx     [scr1 + TRAPTR_PBASE], ptr;             \
        ld      [scr1 + TRAPTR_OFFSET], scr1;           \
        wr      %g0, TRAPTR_ASI, %asi;                  \
        add     ptr, scr1, ptr;

/*
 * TRACE_NEXT(scr1, scr2, scr3) - advance the trap trace pointer.
 *      scr1, scr2, scr3 are scratch registers.
 *      This routine will skip updating the trap pointers if the
 *      global freeze register is set (e.g. in panic).
 *      (we also restore the asi register)
 */
#define TRACE_NEXT(scr1, scr2, scr3)                    \
        CPU_INDEX(scr2, scr1);                          \
        sll     scr2, TRAPTR_SIZE_SHIFT, scr2;          \
        set     trap_trace_ctl, scr1;                   \
        add     scr1, scr2, scr2;                       \
        ldub    [scr2 + TRAPTR_ASIBUF], scr1;           \
        wr      %g0, scr1, %asi;                        \
        sethi   %hi(trap_freeze), scr1;                 \
        ld      [scr1 + %lo(trap_freeze)], scr1;        \
        /* CSTYLED */                                   \
        brnz    scr1, .+36; /* skip update on freeze */ \
        ld      [scr2 + TRAPTR_OFFSET], scr1;           \
        ld      [scr2 + TRAPTR_LIMIT], scr3;            \
        st      scr1, [scr2 + TRAPTR_LAST_OFFSET];      \
        add     scr1, TRAP_ENT_SIZE, scr1;              \
        sub     scr3, TRAP_ENT_SIZE, scr3;              \
        cmp     scr1, scr3;                             \
        movge   %icc, 0, scr1;                          \
        st      scr1, [scr2 + TRAPTR_OFFSET];

/*
 * macro to save %tl, %gl to trap trace record at addr
 */
#define TRACE_SAVE_TL_GL_REGS(addr, scr1)               \
        rdpr    %tl, scr1;                              \
        stba    scr1, [addr + TRAP_ENT_TL]%asi;         \
        rdpr    %gl, scr1;                              \
        stba    scr1, [addr + TRAP_ENT_GL]%asi

/*
 * macro to save tl to trap trace record at addr
 */
#define TRACE_SAVE_TL_VAL(addr, tl)                     \
        stba    tl, [addr + TRAP_ENT_TL]%asi

/*
 * macro to save gl to trap trace record at addr
 */
#define TRACE_SAVE_GL_VAL(addr, gl)                     \
        stba    gl, [addr + TRAP_ENT_GL]%asi


/*
 * Trace macro for sys_trap return entries:
 *      prom_rtt, priv_rtt, and user_rtt
 *      %l7 - regs
 *      %l6 - trap %pil for prom_rtt and priv_rtt; THREAD_REG for user_rtt
 */
#define TRACE_RTT(code, scr1, scr2, scr3, scr4)         \
        rdpr    %pstate, scr4;                          \
        andn    scr4, PSTATE_IE | PSTATE_AM, scr3;      \
        wrpr    %g0, scr3, %pstate;                     \
        TRACE_PTR(scr1, scr2);                          \
        GET_TRACE_TICK(scr2, scr3);                     \
        stxa    scr2, [scr1 + TRAP_ENT_TICK]%asi;       \
        TRACE_SAVE_TL_GL_REGS(scr1, scr2);              \
        set     code, scr2;                             \
        stha    scr2, [scr1 + TRAP_ENT_TT]%asi;         \
        ldn     [%l7 + PC_OFF], scr2;                   \
        stna    scr2, [scr1 + TRAP_ENT_TPC]%asi;        \
        ldx     [%l7 + TSTATE_OFF], scr2;               \
        stxa    scr2, [scr1 + TRAP_ENT_TSTATE]%asi;     \
        stna    %sp, [scr1 + TRAP_ENT_SP]%asi;          \
        stna    %l6, [scr1 + TRAP_ENT_TR]%asi;          \
        stna    %l7, [scr1 + TRAP_ENT_F1]%asi;          \
        ldn     [THREAD_REG + T_CPU], scr2;             \
        ld      [scr2 + CPU_BASE_SPL], scr2;            \
        stna    scr2, [scr1 + TRAP_ENT_F2]%asi;         \
        stna    %g0, [scr1 + TRAP_ENT_F3]%asi;          \
        rdpr    %cwp, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_F4]%asi;         \
        TRACE_NEXT(scr1, scr2, scr3);                   \
        wrpr    %g0, scr4, %pstate

/*
 * Trace macro for spill and fill trap handlers
 *      tl and tt fields indicate which spill handler is entered
 */
#define TRACE_WIN_INFO(code, scr1, scr2, scr3)          \
        TRACE_PTR(scr1, scr2);                          \
        GET_TRACE_TICK(scr2, scr3);                     \
        stxa    scr2, [scr1 + TRAP_ENT_TICK]%asi;       \
        TRACE_SAVE_TL_GL_REGS(scr1, scr2);              \
        rdpr    %tt, scr2;                              \
        set     code, scr3;                             \
        or      scr2, scr3, scr2;                       \
        stha    scr2, [scr1 + TRAP_ENT_TT]%asi;         \
        rdpr    %tstate, scr2;                          \
        stxa    scr2, [scr1 + TRAP_ENT_TSTATE]%asi;     \
        stna    %sp, [scr1 + TRAP_ENT_SP]%asi;          \
        rdpr    %tpc, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_TPC]%asi;        \
        set     TT_FSPILL_DEBUG, scr2;                  \
        stna    scr2, [scr1 + TRAP_ENT_TR]%asi;         \
        rdpr    %pstate, scr2;                          \
        stna    scr2, [scr1 + TRAP_ENT_F1]%asi;         \
        rdpr    %cwp, scr2;                             \
        sll     scr2, 24, scr2;                         \
        rdpr    %cansave, scr3;                         \
        sll     scr3, 16, scr3;                         \
        or      scr2, scr3, scr2;                       \
        rdpr    %canrestore, scr3;                      \
        or      scr2, scr3, scr2;                       \
        stna    scr2, [scr1 + TRAP_ENT_F2]%asi;         \
        rdpr    %otherwin, scr2;                        \
        sll     scr2, 24, scr2;                         \
        rdpr    %cleanwin, scr3;                        \
        sll     scr3, 16, scr3;                         \
        or      scr2, scr3, scr2;                       \
        rdpr    %wstate, scr3;                          \
        or      scr2, scr3, scr2;                       \
        stna    scr2, [scr1 + TRAP_ENT_F3]%asi;         \
        stna    %o7, [scr1 + TRAP_ENT_F4]%asi;          \
        TRACE_NEXT(scr1, scr2, scr3)

#ifdef TRAPTRACE

#define FAULT_WINTRACE(scr1, scr2, scr3, type)          \
        TRACE_PTR(scr1, scr2);                          \
        GET_TRACE_TICK(scr2, scr3);                     \
        stxa    scr2, [scr1 + TRAP_ENT_TICK]%asi;       \
        TRACE_SAVE_TL_GL_REGS(scr1, scr2);              \
        set     type, scr2;                             \
        stha    scr2, [scr1 + TRAP_ENT_TT]%asi;         \
        rdpr    %tpc, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_TPC]%asi;        \
        rdpr    %tstate, scr2;                          \
        stxa    scr2, [scr1 + TRAP_ENT_TSTATE]%asi;     \
        stna    %sp, [scr1 + TRAP_ENT_SP]%asi;          \
        stna    %g0, [scr1 + TRAP_ENT_TR]%asi;          \
        stna    %g0, [scr1 + TRAP_ENT_F1]%asi;          \
        stna    %g4, [scr1 + TRAP_ENT_F2]%asi;          \
        rdpr    %pil, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_F3]%asi;         \
        stna    %g0, [scr1 + TRAP_ENT_F4]%asi;          \
        TRACE_NEXT(scr1, scr2, scr3)

#define SYSTRAP_TT      0x1300

#define SYSTRAP_TRACE(scr1, scr2, scr3)                 \
        TRACE_PTR(scr1, scr2);                          \
        GET_TRACE_TICK(scr2, scr3);                     \
        stxa    scr2, [scr1 + TRAP_ENT_TICK]%asi;       \
        TRACE_SAVE_TL_GL_REGS(scr1, scr2);              \
        set     SYSTRAP_TT, scr3;                       \
        rdpr    %tt, scr2;                              \
        or      scr3, scr2, scr2;                       \
        stha    scr2, [scr1 + TRAP_ENT_TT]%asi;         \
        rdpr    %tpc, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_TPC]%asi;        \
        rdpr    %tstate, scr2;                          \
        stxa    scr2, [scr1 + TRAP_ENT_TSTATE]%asi;     \
        stna    %g1, [scr1 + TRAP_ENT_SP]%asi;          \
        stna    %g2, [scr1 + TRAP_ENT_TR]%asi;          \
        stna    %g3, [scr1 + TRAP_ENT_F1]%asi;          \
        stna    %g4, [scr1 + TRAP_ENT_F2]%asi;          \
        rdpr    %pil, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_F3]%asi;         \
        rdpr    %cwp, scr2;                             \
        stna    scr2, [scr1 + TRAP_ENT_F4]%asi;         \
        TRACE_NEXT(scr1, scr2, scr3)

#else /* TRAPTRACE */

#define FAULT_WINTRACE(scr1, scr2, scr3, type)
#define SYSTRAP_TRACE(scr1, scr2, scr3)

#endif /* TRAPTRACE */

#endif  /* _ASM */

/*
 * Trap trace codes used in place of a %tbr value when more than one
 * entry is made by a trap.  The general scheme is that the trap-type is
 * in the same position as in the TT, and the low-order bits indicate
 * which precise entry is being made.
 */

#define TT_F32_SN0      0x1084
#define TT_F64_SN0      0x1088
#define TT_F32_NT0      0x1094
#define TT_F64_NT0      0x1098
#define TT_F32_SO0      0x10A4
#define TT_F64_SO0      0x10A8
#define TT_F32_FN0      0x10C4
#define TT_F64_FN0      0x10C8
#define TT_F32_SN1      0x1284
#define TT_F64_SN1      0x1288
#define TT_F32_NT1      0x1294
#define TT_F64_NT1      0x1298
#define TT_F32_SO1      0x12A4
#define TT_F64_SO1      0x12A8
#define TT_F32_FN1      0x12C4
#define TT_F64_FN1      0x12C8
#define TT_RTT_FN1      0x12DD

#define TT_SC_ENTR      0x880   /* enter system call */
#define TT_SC_RET       0x881   /* system call normal return */

#define TT_SYS_RTT_PROM 0x5555  /* return from trap to prom */
#define TT_SYS_RTT_PRIV 0x6666  /* return from trap to privilege */
#define TT_SYS_RTT_USER 0x7777  /* return from trap to user */

#define TT_INTR_EXIT    0x8888  /* interrupt thread exit (no pinned thread) */
#define TT_FSPILL_DEBUG 0x9999  /* fill/spill debugging */

#define TT_SERVE_INTR   0x6000  /* SERVE_INTR */
#define TT_XCALL        0xd000  /* xcall/xtrap */
#define TT_XCALL_CONT   0xdc00  /* continuation of an xcall/xtrap record */

#define TT_MMU_MISS     0x200   /* or'd into %tt to indicate a miss */
#define TT_MMU_EXEC     0x400   /* or'd into %tt to indicate exec_fault */


#ifdef  __cplusplus
}
#endif

#endif  /* _SYS_TRAPTRACE_H */