root/usr/src/uts/sun4v/vm/mach_sfmmu.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.
 */

/*
 * VM - Hardware Address Translation management.
 *
 * This file describes the contents of the sun reference mmu (sfmmu)
 * specific hat data structures and the sfmmu specific hat procedures.
 * The machine independent interface is described in <vm/hat.h>.
 */

#ifndef _VM_MACH_SFMMU_H
#define _VM_MACH_SFMMU_H

#include <sys/x_call.h>
#include <sys/hypervisor_api.h>

#ifdef  __cplusplus
extern "C" {
#endif

/*
 * Define UTSB_PHYS if user TSB is always accessed via physical address.
 * On sun4v platform, user TSB is accessed via physical address.
 */
#define UTSB_PHYS       1

/*
 * Hypervisor TSB info
 */
#define NHV_TSB_INFO    4

#ifndef _ASM

struct hv_tsb_block {
        uint64_t        hv_tsb_info_pa; /* hypervisor TSB info PA */
        uint64_t        hv_tsb_info_cnt; /* hypervisor TSB info count */
        hv_tsb_info_t   hv_tsb_info[NHV_TSB_INFO]; /* hypervisor TSB info */
};

#endif /* _ASM */

#ifdef _ASM

/*
 * This macro is used to set private/shared secondary context register in
 * sfmmu_alloc_ctx().
 * Input:
 * cnum     = cnum
 * is_shctx = sfmmu private/shared flag (0: private, 1: shared)
 * tmp2 is only used in the sun4u version of this macro
 */
#define SET_SECCTX(cnum, is_shctx, tmp1, tmp2, label)                   \
        mov     MMU_SCONTEXT, tmp1;                                     \
        movrnz  is_shctx, MMU_SCONTEXT1, tmp1;                          \
        stxa    cnum, [tmp1]ASI_MMU_CTX;  /* set 2nd ctx reg. */        \
        membar  #Sync;                                                  \

/*
 * This macro is used in the MMU code to check if TL should be lowered from
 * 2 to 1 to pop trapstat's state.  See the block comment in trapstat.c
 * for details.
 */

#define TSTAT_CHECK_TL1(label, scr1, scr2)                      \
        rdpr    %tpc, scr1;                                     \
        sethi   %hi(KERNELBASE), scr2;                          \
        or      scr2, %lo(KERNELBASE), scr2;                    \
        cmp     scr1, scr2;                                     \
        bgeu    %xcc, 9f;                                       \
        nop;                                                    \
        wrpr    %g0, 1, %gl;                                    \
        ba      label;                                          \
        wrpr    %g0, 1, %tl;                                    \
9:

/*
 * The following macros allow us to share majority of the
 * SFMMU code between sun4u and sun4v platforms.
 */

#define SETUP_TSB_ASI(qlp, tmp)

#define SETUP_UTSB_ATOMIC_ASI(tmp1, tmp2)

/*
 * Macro to swtich to alternate global register on sun4u platforms
 * (not applicable to sun4v platforms)
 */
#define USE_ALTERNATE_GLOBALS(scr)

/*
 * Macro to set %gl register value on sun4v platforms
 * (not applicable to sun4u platforms)
 */
#define SET_GL_REG(val)                                         \
        wrpr    %g0, val, %gl

/*
 * Get pseudo-tagacc value and context from the MMU fault area.  Pseudo-tagacc
 * is the faulting virtual address OR'd with 0 for KCONTEXT, INVALID_CONTEXT
 * (1) for invalid context, and USER_CONTEXT (2) for user context.
 *
 * In:
 *   tagacc, ctxtype = scratch registers
 * Out:
 *   tagacc = MMU data tag access register value
 *   ctx = context type (KCONTEXT, INVALID_CONTEXT or USER_CONTEXT)
 */
#define GET_MMU_D_PTAGACC_CTXTYPE(ptagacc, ctxtype)                     \
        MMU_FAULT_STATUS_AREA(ctxtype);                                 \
        ldx     [ctxtype + MMFSA_D_ADDR], ptagacc;                      \
        ldx     [ctxtype + MMFSA_D_CTX], ctxtype;                       \
        srlx    ptagacc, MMU_PAGESHIFT, ptagacc; /* align to page boundary */ \
        cmp     ctxtype, USER_CONTEXT_TYPE;                             \
        sllx    ptagacc, MMU_PAGESHIFT, ptagacc;                        \
        movgu   %icc, USER_CONTEXT_TYPE, ctxtype;                       \
        or      ptagacc, ctxtype, ptagacc

/*
 * Synthesize/get data tag access register value from the MMU fault area
 *
 * In:
 *   tagacc, scr1 = scratch registers
 * Out:
 *   tagacc = MMU data tag access register value
 */
#define GET_MMU_D_TAGACC(tagacc, scr1)                          \
        GET_MMU_D_PTAGACC_CTXTYPE(tagacc, scr1)

/*
 * Synthesize/get data tag target register value from the MMU fault area
 *
 * In:
 *   ttarget, scr1 = scratch registers
 * Out:
 *   ttarget = MMU data tag target register value
 */
#define GET_MMU_D_TTARGET(ttarget, scr1)                        \
        MMU_FAULT_STATUS_AREA(ttarget);                         \
        ldx     [ttarget + MMFSA_D_CTX], scr1;                  \
        sllx    scr1, TTARGET_CTX_SHIFT, scr1;                  \
        ldx     [ttarget + MMFSA_D_ADDR], ttarget;              \
        srlx    ttarget, TTARGET_VA_SHIFT, ttarget;             \
        or      ttarget, scr1, ttarget

/*
 * Synthesize/get data/instruction psuedo tag access register values
 * from the MMU fault area (context is 0 for kernel, 1 for invalid, 2 for user)
 *
 * In:
 *   dtagacc, itagacc, scr1, scr2 = scratch registers
 * Out:
 *   dtagacc = MMU data tag access register value w/psuedo-context
 *   itagacc = MMU instruction tag access register value w/pseudo-context
 */
#define GET_MMU_BOTH_TAGACC(dtagacc, itagacc, scr1, scr2)       \
        MMU_FAULT_STATUS_AREA(scr1);                            \
        ldx     [scr1 + MMFSA_D_ADDR], scr2;                    \
        ldx     [scr1 + MMFSA_D_CTX], dtagacc;                  \
        srlx    scr2, MMU_PAGESHIFT, scr2;      /* align to page boundary */ \
        cmp     dtagacc, USER_CONTEXT_TYPE;                     \
        sllx    scr2, MMU_PAGESHIFT, scr2;                      \
        movgu   %icc, USER_CONTEXT_TYPE, dtagacc;               \
        or      scr2, dtagacc, dtagacc;                         \
        ldx     [scr1 + MMFSA_I_ADDR], scr2;                    \
        ldx     [scr1 + MMFSA_I_CTX], itagacc;                  \
        srlx    scr2, MMU_PAGESHIFT, scr2;      /* align to page boundry */ \
        cmp     itagacc, USER_CONTEXT_TYPE;                     \
        sllx    scr2, MMU_PAGESHIFT, scr2;                      \
        movgu   %icc, USER_CONTEXT_TYPE, itagacc;               \
        or      scr2, itagacc, itagacc

/*
 * Synthesize/get MMU data fault address from the MMU fault area
 *
 * In:
 *   daddr, scr1 = scratch registers
 * Out:
 *   daddr = MMU data fault address
 */
#define GET_MMU_D_ADDR(daddr, scr1)                             \
        MMU_FAULT_STATUS_AREA(scr1);                            \
        ldx     [scr1 + MMFSA_D_ADDR], daddr

/*
 * Get pseudo-tagacc value and context from the MMU fault area.  Pseudo-tagacc
 * is the faulting virtual address OR'd with 0 for KCONTEXT, INVALID_CONTEXT
 * (1) for invalid context, and USER_CONTEXT (2) for user context.
 *
 * In:
 *   tagacc, ctxtype = scratch registers
 * Out:
 *   tagacc = MMU instruction tag access register value
 *   ctxtype = context type (KCONTEXT, INVALID_CONTEXT or USER_CONTEXT)
 */
#define GET_MMU_I_PTAGACC_CTXTYPE(ptagacc, ctxtype)                     \
        MMU_FAULT_STATUS_AREA(ctxtype);                                 \
        ldx     [ctxtype + MMFSA_I_ADDR], ptagacc;                      \
        ldx     [ctxtype + MMFSA_I_CTX], ctxtype;                       \
        srlx    ptagacc, MMU_PAGESHIFT, ptagacc; /* align to page boundary */ \
        cmp     ctxtype, USER_CONTEXT_TYPE;                             \
        sllx    ptagacc, MMU_PAGESHIFT, ptagacc;                        \
        movgu   %icc, USER_CONTEXT_TYPE, ctxtype;                       \
        or      ptagacc, ctxtype, ptagacc

/*
 * Load ITLB entry
 *
 * In:
 *   tte = reg containing tte
 *   scr1, scr2, scr3, scr4 = scratch registers
 */
#define ITLB_STUFF(tte, scr1, scr2, scr3, scr4)         \
        mov     %o0, scr1;                              \
        mov     %o1, scr2;                              \
        mov     %o2, scr3;                              \
        mov     %o3, scr4;                              \
        MMU_FAULT_STATUS_AREA(%o2);                     \
        ldx     [%o2 + MMFSA_I_ADDR], %o0;              \
        ldx     [%o2 + MMFSA_I_CTX], %o1;               \
        mov     tte, %o2;                               \
        mov     MAP_ITLB, %o3;                          \
        ta      MMU_MAP_ADDR;                           \
        /* BEGIN CSTYLED */                             \
        brnz,a,pn %o0, ptl1_panic;                      \
          mov   PTL1_BAD_HCALL, %g1;                    \
        /* END CSTYLED */                               \
        mov     scr1, %o0;                              \
        mov     scr2, %o1;                              \
        mov     scr3, %o2;                              \
        mov     scr4, %o3

/*
 * Load DTLB entry
 *
 * In:
 *   tte = reg containing tte
 *   scr1, scr2, scr3, scr4 = scratch registers
 */
#define DTLB_STUFF(tte, scr1, scr2, scr3, scr4)         \
        mov     %o0, scr1;                              \
        mov     %o1, scr2;                              \
        mov     %o2, scr3;                              \
        mov     %o3, scr4;                              \
        MMU_FAULT_STATUS_AREA(%o2);                     \
        ldx     [%o2 + MMFSA_D_ADDR], %o0;              \
        ldx     [%o2 + MMFSA_D_CTX], %o1;               \
        mov     tte, %o2;                               \
        mov     MAP_DTLB, %o3;                          \
        ta      MMU_MAP_ADDR;                           \
        /* BEGIN CSTYLED */                             \
        brnz,a,pn %o0, ptl1_panic;                      \
          mov   PTL1_BAD_HCALL, %g1;                    \
        /* END CSTYLED */                               \
        mov     scr1, %o0;                              \
        mov     scr2, %o1;                              \
        mov     scr3, %o2;                              \
        mov     scr4, %o3

/*
 * Returns PFN given the TTE and vaddr
 *
 * In:
 *   tte = reg containing tte
 *   vaddr = reg containing vaddr
 *   scr1, scr2, scr3 = scratch registers
 * Out:
 *   tte = PFN value
 */
#define TTETOPFN(tte, vaddr, label, scr1, scr2, scr3)                   \
        and     tte, TTE_SZ_BITS, scr1;         /* scr1 = ttesz */      \
        sllx    tte, TTE_PA_LSHIFT, tte;                                \
        sllx    scr1, 1, scr2;                                          \
        add     scr2, scr1, scr2;               /* mulx 3 */            \
        add     scr2, MMU_PAGESHIFT + TTE_PA_LSHIFT, scr3;              \
        /* CSTYLED */                                                   \
        brz,pt  scr2, label##1;                                 \
        srlx    tte, scr3, tte;                                         \
        sllx    tte, scr2, tte;                                         \
        set     1, scr1;                                                \
        add     scr2, MMU_PAGESHIFT, scr3;                              \
        sllx    scr1, scr3, scr1;                                       \
        sub     scr1, 1, scr1;  /* scr1=TTE_PAGE_OFFSET(ttesz) */       \
        and     vaddr, scr1, scr2;                                      \
        srln    scr2, MMU_PAGESHIFT, scr2;                              \
        or      tte, scr2, tte;                                         \
        /* CSTYLED */                                                   \
label##1:

/*
 * TTE_SET_REF_ML is a macro that updates the reference bit if it is
 * not already set.
 *
 * Parameters:
 * tte      = reg containing tte
 * ttepa    = physical pointer to tte
 * tsbarea  = tsb miss area
 * tmp1     = tmp reg
 * tmp2     = tmp reg
 * label    = temporary label
 */

#define TTE_SET_REF_ML(tte, ttepa, tsbarea, tmp1, tmp2, label)          \
        /* BEGIN CSTYLED */                                             \
        /* check reference bit */                                       \
        btst    TTE_REF_INT, tte;                                       \
        bnz,pt  %xcc, label##2; /* if ref bit set-skip ahead */ \
        nop;                                                            \
        /* update reference bit */                                      \
label##1:                                                               \
        or      tte, TTE_REF_INT, tmp1;                                 \
        casxa   [ttepa]ASI_MEM, tte, tmp1;      /* update ref bit */    \
        cmp     tte, tmp1;                                              \
        bne,a,pn %xcc, label##1;                                        \
        ldxa    [ttepa]ASI_MEM, tte;    /* MMU_READTTE through pa */    \
        or      tte, TTE_REF_INT, tte;                                  \
label##2:                                                               \
        /* END CSTYLED */


/*
 * TTE_SET_REFMOD_ML is a macro that updates the reference and modify bits
 * if not already set.
 *
 * Parameters:
 * tte      = reg containing tte
 * ttepa    = physical pointer to tte
 * tsbarea  = tsb miss area
 * tmp1     = tmp reg
 * tmp2     = tmp reg
 * label    = temporary label
 * exitlabel = label where to jump to if write perm bit not set.
 */

#define TTE_SET_REFMOD_ML(tte, ttepa, tsbarea, tmp1, tmp2, label,       \
        exitlabel)                                                      \
        /* BEGIN CSTYLED */                                             \
        /* check reference bit */                                       \
        btst    TTE_WRPRM_INT, tte;                                     \
        bz,pn   %xcc, exitlabel;        /* exit if wr_perm no set */    \
          btst  TTE_HWWR_INT, tte;                                      \
        bnz,pn  %xcc, label##2; /* nothing to do */             \
          nop;                                                          \
        /* update reference bit */                                      \
label##1:                                                               \
        or      tte, TTE_HWWR_INT | TTE_REF_INT, tmp1;                  \
        casxa   [ttepa]ASI_MEM, tte, tmp1; /* update ref/mod bit */     \
        cmp     tte, tmp1;                                              \
        bne,a,pn %xcc, label##1;                                        \
          ldxa  [ttepa]ASI_MEM, tte;    /* MMU_READTTE through pa */    \
        or      tte, TTE_HWWR_INT | TTE_REF_INT, tte;                   \
label##2:                                                               \
        /* END CSTYLED */
/*
 * Get TSB base register from the scratchpad for
 * shared contexts
 *
 * In:
 *   tsbmiss = pointer to tsbmiss area
 *   tsbmissoffset = offset to right tsb pointer
 *   tsbreg = scratch
 * Out:
 *   tsbreg = tsbreg from the specified scratchpad register
 */
#define GET_UTSBREG_SHCTX(tsbmiss, tsbmissoffset, tsbreg)               \
        ldx     [tsbmiss + tsbmissoffset], tsbreg


/*
 * Get the location of the TSB entry in the first TSB to probe
 *
 * In:
 *   tagacc = tag access register (not clobbered)
 *   tsbe, tmp1, tmp2 = scratch registers
 * Out:
 *   tsbe = pointer to the tsbe in the 1st TSB
 */

#define GET_1ST_TSBE_PTR(tagacc, tsbe, tmp1, tmp2)                      \
        /* BEGIN CSTYLED */                                             \
        mov     SCRATCHPAD_UTSBREG1, tmp1                               ;\
        ldxa    [tmp1]ASI_SCRATCHPAD, tsbe      /* get tsbreg */        ;\
        and     tsbe, TSB_SOFTSZ_MASK, tmp2     /* tmp2=szc */          ;\
        andn    tsbe, TSB_SOFTSZ_MASK, tsbe     /* tsbbase */           ;\
        mov     TSB_ENTRIES(0), tmp1    /* nentries in TSB size 0 */    ;\
        sllx    tmp1, tmp2, tmp1        /* tmp1 = nentries in TSB */    ;\
        sub     tmp1, 1, tmp1           /* mask = nentries - 1 */       ;\
        srlx    tagacc, MMU_PAGESHIFT, tmp2                             ;\
        and     tmp2, tmp1, tmp1        /* tsbent = virtpage & mask */  ;\
        sllx    tmp1, TSB_ENTRY_SHIFT, tmp1     /* entry num --> ptr */ ;\
        add     tsbe, tmp1, tsbe        /* add entry offset to TSB base */ ;\
        /* END CSTYLED */


/*
 * Will probe the first TSB, and if it finds a match, will insert it
 * into the TLB and retry.
 *
 * tsbe_ptr = precomputed first TSB entry pointer (in, ro)
 * vpg_4m = 4M virtual page number for tag matching  (in, ro)
 * label = where to branch to if this is a miss (text)
 * %asi = atomic ASI to use for the TSB access
 *
 * For trapstat, we have to explicily use these registers.
 * g4 = location tag will be retrieved into from TSB (out)
 * g5 = location data(tte) will be retrieved into from TSB (out)
 */
#define PROBE_1ST_DTSB(tsbe_ptr, vpg_4m, label) /* g4/g5 clobbered */   \
        /* BEGIN CSTYLED */                                             \
        ldda    [tsbe_ptr]ASI_QUAD_LDD_PHYS, %g4 /* g4 = tag, g5 = data */ ;\
        cmp     %g4, vpg_4m             /* compare tag w/ TSB */        ;\
        bne,pn  %xcc, label##1  /* branch if !match */          ;\
          nop                                                           ;\
        brgez,pn %g5, label##1                                  ;\
          nop                                                           ;\
        TT_TRACE(trace_tsbhit)                                          ;\
        DTLB_STUFF(%g5, %g1, %g2, %g3, %g4)                             ;\
        /* trapstat expects tte in %g5 */                               ;\
        retry                           /* retry faulted instruction */ ;\
label##1:                                                               \
        /* END CSTYLED */


/*
 * Same as above, only if the TTE doesn't have the execute
 * bit set, will branch to exec_fault directly.
 */
#define PROBE_1ST_ITSB(tsbe_ptr, vpg_4m, label)                         \
        /* BEGIN CSTYLED */                                             \
        ldda    [tsbe_ptr]ASI_QUAD_LDD_PHYS, %g4 /* g4 = tag, g5 = data */ ;\
        cmp     %g4, vpg_4m             /* compare tag w/ TSB */        ;\
        bne,pn  %xcc, label##1  /* branch if !match */          ;\
          nop                                                           ;\
        brgez,pn %g5, label##1                                  ;\
          nop                                                           ;\
        andcc   %g5, TTE_EXECPRM_INT, %g0  /* check execute bit */      ;\
        bz,pn   %icc, exec_fault                                        ;\
          nop                                                           ;\
        TT_TRACE(trace_tsbhit)                                          ;\
        ITLB_STUFF(%g5, %g1, %g2, %g3, %g4)                             ;\
        retry                           /* retry faulted instruction */ ;\
label##1:                                                               \
        /* END CSTYLED */

/*
 * vpg_4m = 4M virtual page number for tag matching (in)
 * tsbe_ptr = precomputed second TSB entry pointer (in)
 * label = label to use to make branch targets unique (text)
 *
 * For trapstat, we have to explicity use these registers.
 * g4 = tag portion of TSBE (out)
 * g5 = data portion of TSBE (out)
 */
#define PROBE_2ND_DTSB(tsbe_ptr, vpg_4m, label)                         \
        /* BEGIN CSTYLED */                                             \
        ldda    [tsbe_ptr]ASI_QUAD_LDD_PHYS, %g4  /* g4 = tag, g5 = data */ ;\
        /* since we are looking at 2nd tsb, if it's valid, it must be 4M */ ;\
        cmp     %g4, vpg_4m                                             ;\
        bne,pn  %xcc, label##1                                  ;\
          nop                                                           ;\
        brgez,pn %g5, label##1                                  ;\
          nop                                                           ;\
        mov     tsbe_ptr, %g1           /* trace_tsbhit wants ptr in %g1 */ ;\
        TT_TRACE(trace_tsbhit)                                          ;\
        DTLB_STUFF(%g5, %g1, %g2, %g3, %g4)                             ;\
        /* trapstat expects tte in %g5 */                               ;\
        retry                           /* retry faulted instruction */ ;\
label##1:                                                               \
        /* END CSTYLED */


/*
 * Same as above, with the following additions:
 * If the TTE found is not executable, branch directly
 * to exec_fault.  If a TSB miss, branch to TSB miss handler.
 */
#define PROBE_2ND_ITSB(tsbe_ptr, vpg_4m)                                \
        /* BEGIN CSTYLED */                                             \
        ldda    [tsbe_ptr]ASI_QUAD_LDD_PHYS, %g4 /* g4 = tag, g5 = data */ ;\
        cmp     %g4, vpg_4m             /* compare tag w/ TSB */        ;\
        bne,pn  %xcc, sfmmu_tsb_miss_tt /* branch if !match */          ;\
          nop                                                           ;\
        brgez,pn %g5, sfmmu_tsb_miss_tt                                 ;\
          nop                                                           ;\
        andcc   %g5, TTE_EXECPRM_INT, %g0  /* check execute bit */      ;\
        bz,pn   %icc, exec_fault                                        ;\
          mov   tsbe_ptr, %g1           /* trap trace wants ptr in %g1 */ ;\
        TT_TRACE(trace_tsbhit)                                          ;\
        ITLB_STUFF(%g5, %g1, %g2, %g3, %g4)                             ;\
        retry                           /* retry faulted instruction */ \
        /* END CSTYLED */

/*
 * 1. Get ctx1. The traptype is supplied by caller.
 * 2. If iTSB miss, store in MMFSA_I_CTX
 * 3. if dTSB miss, store in MMFSA_D_CTX
 * 4. Thus the [D|I]TLB_STUFF will work as expected.
 */
#define SAVE_CTX1(traptype, ctx1, tmp, label)                           \
        /* BEGIN CSTYLED */                                             \
        mov     MMU_SCONTEXT1, tmp                                      ;\
        ldxa    [tmp]ASI_MMU_CTX, ctx1                                  ;\
        MMU_FAULT_STATUS_AREA(tmp)                                      ;\
        cmp     traptype, FAST_IMMU_MISS_TT                             ;\
        be,a,pn %icc, label                                             ;\
          stx   ctx1, [tmp + MMFSA_I_CTX]                               ;\
        cmp     traptype, T_INSTR_MMU_MISS                              ;\
        be,a,pn %icc, label                                             ;\
          stx   ctx1, [tmp + MMFSA_I_CTX]                               ;\
        stx     ctx1, [tmp + MMFSA_D_CTX]                               ;\
label:
        /* END CSTYLED */

#endif /* _ASM */

#ifdef  __cplusplus
}
#endif

#endif  /* _VM_MACH_SFMMU_H */