1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  * Assembly code support for the Cheetah module
  27  */
  28 
  29 #include "assym.h"
  30 
  31 #include <sys/asm_linkage.h>
  32 #include <sys/mmu.h>
  33 #include <vm/hat_sfmmu.h>
  34 #include <sys/machparam.h>
  35 #include <sys/machcpuvar.h>
  36 #include <sys/machthread.h>
  37 #include <sys/machtrap.h>
  38 #include <sys/privregs.h>
  39 #include <sys/asm_linkage.h>
  40 #include <sys/trap.h>
  41 #include <sys/cheetahregs.h>
  42 #include <sys/us3_module.h>
  43 #include <sys/xc_impl.h>
  44 #include <sys/intreg.h>
  45 #include <sys/async.h>
  46 #include <sys/clock.h>
  47 #include <sys/cheetahasm.h>
  48 
  49 #ifdef TRAPTRACE
  50 #include <sys/traptrace.h>
  51 #endif /* TRAPTRACE */
  52 
  53 /* BEGIN CSTYLED */
  54 
  55 /*
  56  * Cheetah version to flush an Ecache line by index (aliased address)
  57  */
  58 #define ECACHE_REFLUSH_LINE(ecache_size, alias_address, scr2)           \
  59         ldxa    [alias_address]ASI_MEM, %g0
  60 
  61 #define ECACHE_FLUSH_LINE(physaddr, ecache_size, scr1, scr2)            \
  62         xor     physaddr, ecache_size, scr1;                            \
  63         add     ecache_size, ecache_size, scr2;                         \
  64         sub     scr2, 1, scr2;                                          \
  65         and     scr1, scr2, scr1;                                       \
  66         ASM_LDX(scr2, ecache_flushaddr);                                \
  67         add     scr1, scr2, scr1;                                       \
  68         ECACHE_REFLUSH_LINE(ecache_size, scr1, scr2)
  69 
  70 /* END CSTYLED */
  71 
  72 
  73 /*
  74  * Fast ECC error at TL>0 handler
  75  * We get here via trap 70 at TL>0->Software trap 0 at TL>0.  We enter
  76  * this routine with %g1 and %g2 already saved in %tpc, %tnpc and %tstate.
  77  * For a complete description of the Fast ECC at TL>0 handling see the
  78  * comment block "Cheetah/Cheetah+ Fast ECC at TL>0 trap strategy" in
  79  * us3_common_asm.s
  80  */
  81 
  82         .section ".text"
  83         .align  64
  84         ENTRY_NP(fast_ecc_tl1_err)
  85 
  86         /*
  87          * This macro turns off the D$/I$ if they are on and saves their
  88          * original state in ch_err_tl1_tmp, saves all the %g registers in the
  89          * ch_err_tl1_data structure, updates the ch_err_tl1_flags and saves
  90          * the %tpc in ch_err_tl1_tpc.  At the end of this macro, %g1 will
  91          * point to the ch_err_tl1_data structure and the original D$/I$ state
  92          * will be saved in ch_err_tl1_tmp.  All %g registers except for %g1
  93          * will be available.
  94          */
  95         CH_ERR_TL1_FECC_ENTER;
  96 
  97         /*
  98          * Get the diagnostic logout data.  %g4 must be initialized to
  99          * current CEEN state, %g5 must point to logout structure in
 100          * ch_err_tl1_data_t.  %g3 will contain the nesting count upon
 101          * return.
 102          */
 103         ldxa    [%g0]ASI_ESTATE_ERR, %g4
 104         and     %g4, EN_REG_CEEN, %g4
 105         add     %g1, CH_ERR_TL1_LOGOUT, %g5
 106         DO_TL1_CPU_LOGOUT(%g3, %g2, %g4, %g5, %g6, %g3, %g4)
 107 
 108         /*
 109          * If the logout nesting count is exceeded, we're probably
 110          * not making any progress, try to panic instead.
 111          */
 112         cmp     %g3, CLO_NESTING_MAX
 113         bge     fecc_tl1_err
 114           nop
 115 
 116         /*
 117          * Save the current CEEN and NCEEN state in %g7 and turn them off
 118          * before flushing the Ecache.
 119          */
 120         ldxa    [%g0]ASI_ESTATE_ERR, %g7
 121         andn    %g7, EN_REG_CEEN | EN_REG_NCEEN, %g5
 122         stxa    %g5, [%g0]ASI_ESTATE_ERR
 123         membar  #Sync
 124 
 125         /*
 126          * Flush the Ecache, using the largest possible cache size with the
 127          * smallest possible line size since we can't get the actual sizes
 128          * from the cpu_node due to DTLB misses.
 129          */
 130         set     CH_ECACHE_8M_SIZE, %g4
 131         set     CH_ECACHE_MIN_LSIZE, %g5
 132 
 133         /*
 134          * Use a different flush address to avoid recursion if the error
 135          * exists in ecache_flushaddr.
 136          */
 137         ASM_LDX(%g6, ecache_tl1_flushaddr)
 138         cmp     %g6, -1         ! check if address is valid
 139         be      %xcc, fecc_tl1_err
 140           nop
 141         CH_ECACHE_FLUSHALL(%g4, %g5, %g6)
 142 
 143         /*
 144          * Restore CEEN and NCEEN to the previous state.
 145          */
 146         stxa    %g7, [%g0]ASI_ESTATE_ERR
 147         membar  #Sync
 148 
 149         /*
 150          * If we turned off the D$, then flush it and turn it back on.
 151          */
 152         ldxa    [%g1 + CH_ERR_TL1_TMP]%asi, %g3
 153         andcc   %g3, CH_ERR_TSTATE_DC_ON, %g0
 154         bz      %xcc, 3f
 155           nop
 156 
 157         /*
 158          * Flush the D$.
 159          */
 160         ASM_LD(%g4, dcache_size)
 161         ASM_LD(%g5, dcache_linesize)
 162         CH_DCACHE_FLUSHALL(%g4, %g5, %g6)
 163 
 164         /*
 165          * Turn the D$ back on.
 166          */
 167         ldxa    [%g0]ASI_DCU, %g3
 168         or      %g3, DCU_DC, %g3
 169         stxa    %g3, [%g0]ASI_DCU
 170         membar  #Sync
 171 3:
 172         /*
 173          * If we turned off the I$, then flush it and turn it back on.
 174          */
 175         ldxa    [%g1 + CH_ERR_TL1_TMP]%asi, %g3
 176         andcc   %g3, CH_ERR_TSTATE_IC_ON, %g0
 177         bz      %xcc, 4f
 178           nop
 179 
 180         /*
 181          * Flush the I$.
 182          */
 183         ASM_LD(%g4, icache_size)
 184         ASM_LD(%g5, icache_linesize)
 185         CH_ICACHE_FLUSHALL(%g4, %g5, %g6, %g3)
 186 
 187         /*
 188          * Turn the I$ back on.  Changing DCU_IC requires flush.
 189          */
 190         ldxa    [%g0]ASI_DCU, %g3
 191         or      %g3, DCU_IC, %g3
 192         stxa    %g3, [%g0]ASI_DCU
 193         flush   %g0     
 194 4:
 195 
 196 #ifdef TRAPTRACE
 197         /*
 198          * Get current trap trace entry physical pointer.
 199          */
 200         CPU_INDEX(%g6, %g5)
 201         sll     %g6, TRAPTR_SIZE_SHIFT, %g6
 202         set     trap_trace_ctl, %g5
 203         add     %g6, %g5, %g6
 204         ld      [%g6 + TRAPTR_LIMIT], %g5
 205         tst     %g5
 206         be      %icc, skip_traptrace
 207           nop
 208         ldx     [%g6 + TRAPTR_PBASE], %g5
 209         ld      [%g6 + TRAPTR_OFFSET], %g4
 210         add     %g5, %g4, %g5
 211 
 212         /*
 213          * Create trap trace entry.
 214          */
 215         rd      %asi, %g7
 216         wr      %g0, TRAPTR_ASI, %asi
 217         rd      STICK, %g4
 218         stxa    %g4, [%g5 + TRAP_ENT_TICK]%asi
 219         rdpr    %tl, %g4
 220         stha    %g4, [%g5 + TRAP_ENT_TL]%asi
 221         rdpr    %tt, %g4
 222         stha    %g4, [%g5 + TRAP_ENT_TT]%asi
 223         rdpr    %tpc, %g4
 224         stna    %g4, [%g5 + TRAP_ENT_TPC]%asi
 225         rdpr    %tstate, %g4
 226         stxa    %g4, [%g5 + TRAP_ENT_TSTATE]%asi
 227         stna    %sp, [%g5 + TRAP_ENT_SP]%asi
 228         stna    %g0, [%g5 + TRAP_ENT_TR]%asi
 229         wr      %g0, %g7, %asi
 230         ldxa    [%g1 + CH_ERR_TL1_SDW_AFAR]%asi, %g3
 231         ldxa    [%g1 + CH_ERR_TL1_SDW_AFSR]%asi, %g4
 232         wr      %g0, TRAPTR_ASI, %asi
 233         stna    %g3, [%g5 + TRAP_ENT_F1]%asi
 234         stna    %g4, [%g5 + TRAP_ENT_F2]%asi
 235         wr      %g0, %g7, %asi
 236         ldxa    [%g1 + CH_ERR_TL1_AFAR]%asi, %g3
 237         ldxa    [%g1 + CH_ERR_TL1_AFSR]%asi, %g4
 238         wr      %g0, TRAPTR_ASI, %asi
 239         stna    %g3, [%g5 + TRAP_ENT_F3]%asi
 240         stna    %g4, [%g5 + TRAP_ENT_F4]%asi
 241         wr      %g0, %g7, %asi
 242 
 243         /*
 244          * Advance trap trace pointer.
 245          */
 246         ld      [%g6 + TRAPTR_OFFSET], %g5
 247         ld      [%g6 + TRAPTR_LIMIT], %g4
 248         st      %g5, [%g6 + TRAPTR_LAST_OFFSET]
 249         add     %g5, TRAP_ENT_SIZE, %g5
 250         sub     %g4, TRAP_ENT_SIZE, %g4
 251         cmp     %g5, %g4
 252         movge   %icc, 0, %g5
 253         st      %g5, [%g6 + TRAPTR_OFFSET]
 254 skip_traptrace:
 255 #endif  /* TRAPTRACE */
 256 
 257         /*
 258          * If nesting count is not zero, skip all the AFSR/AFAR
 259          * handling and just do the necessary cache-flushing.
 260          */
 261         ldxa    [%g1 + CH_ERR_TL1_NEST_CNT]%asi, %g2
 262         brnz    %g2, 6f
 263           nop
 264 
 265         /*
 266          * If a UCU followed by a WDU has occurred go ahead and panic
 267          * since a UE will occur (on the retry) before the UCU and WDU
 268          * messages are enqueued.
 269          */
 270         ldxa    [%g1 + CH_ERR_TL1_AFSR]%asi, %g3
 271         set     1, %g4
 272         sllx    %g4, C_AFSR_UCU_SHIFT, %g4
 273         btst    %g4, %g3                ! UCU in original AFSR?
 274         bz      %xcc, 6f
 275           nop
 276         ldxa    [%g0]ASI_AFSR, %g4      ! current AFSR
 277         or      %g3, %g4, %g3           ! %g3 = original + current AFSR
 278         set     1, %g4
 279         sllx    %g4, C_AFSR_WDU_SHIFT, %g4
 280         btst    %g4, %g3                ! WDU in original or current AFSR?
 281         bnz     %xcc, fecc_tl1_err
 282           nop
 283 
 284 6:
 285         /*
 286          * We fall into this macro if we've successfully logged the error in
 287          * the ch_err_tl1_data structure and want the PIL15 softint to pick
 288          * it up and log it.  %g1 must point to the ch_err_tl1_data structure.
 289          * Restores the %g registers and issues retry.
 290          */
 291         CH_ERR_TL1_EXIT;
 292 
 293         /*
 294          * Establish panic exit label.
 295          */
 296         CH_ERR_TL1_PANIC_EXIT(fecc_tl1_err);
 297 
 298         SET_SIZE(fast_ecc_tl1_err)
 299 
 300 
 301 /*
 302  * scrubphys - Pass in the aligned physical memory address
 303  * that you want to scrub, along with the ecache set size.
 304  *
 305  *      1) Displacement flush the E$ line corresponding to %addr.
 306  *         The first ldxa guarantees that the %addr is no longer in
 307  *         M, O, or E (goes to I or S (if instruction fetch also happens).
 308  *      2) "Write" the data using a CAS %addr,%g0,%g0.
 309  *         The casxa guarantees a transition from I to M or S to M.
 310  *      3) Displacement flush the E$ line corresponding to %addr.
 311  *         The second ldxa pushes the M line out of the ecache, into the
 312  *         writeback buffers, on the way to memory.
 313  *      4) The "membar #Sync" pushes the cache line out of the writeback
 314  *         buffers onto the bus, on the way to dram finally.
 315  *
 316  * This is a modified version of the algorithm suggested by Gary Lauterbach.
 317  * In theory the CAS %addr,%g0,%g0 is supposed to mark the addr's cache line
 318  * as modified, but then we found out that for spitfire, if it misses in the
 319  * E$ it will probably install as an M, but if it hits in the E$, then it
 320  * will stay E, if the store doesn't happen. So the first displacement flush
 321  * should ensure that the CAS will miss in the E$.  Arrgh.
 322  */
 323         ENTRY(scrubphys)
 324         rdpr    %pstate, %o4
 325         andn    %o4, PSTATE_IE | PSTATE_AM, %o5
 326         wrpr    %o5, %g0, %pstate       ! clear IE, AM bits
 327 
 328         ECACHE_FLUSH_LINE(%o0, %o1, %o2, %o3)
 329         casxa   [%o0]ASI_MEM, %g0, %g0
 330         ECACHE_REFLUSH_LINE(%o1, %o2, %o3)
 331 
 332         wrpr    %g0, %o4, %pstate       ! restore earlier pstate register value
 333 
 334         retl
 335         membar  #Sync                   ! move the data out of the load buffer
 336         SET_SIZE(scrubphys)
 337 
 338 
 339 /*
 340  * clearphys - Pass in the physical memory address of the checkblock
 341  * that you want to push out, cleared with a recognizable pattern,
 342  * from the ecache.
 343  *
 344  * To ensure that the ecc gets recalculated after the bad data is cleared,
 345  * we must write out enough data to fill the w$ line (64 bytes). So we read
 346  * in an entire ecache subblock's worth of data, and write it back out.
 347  * Then we overwrite the 16 bytes of bad data with the pattern.
 348  */
 349         ENTRY(clearphys)
 350         /* turn off IE, AM bits */
 351         rdpr    %pstate, %o4
 352         andn    %o4, PSTATE_IE | PSTATE_AM, %o5
 353         wrpr    %o5, %g0, %pstate
 354 
 355         /* turn off NCEEN */
 356         ldxa    [%g0]ASI_ESTATE_ERR, %o5
 357         andn    %o5, EN_REG_NCEEN, %o3
 358         stxa    %o3, [%g0]ASI_ESTATE_ERR
 359         membar  #Sync
 360 
 361         /* align address passed with 64 bytes subblock size */
 362         mov     CH_ECACHE_SUBBLK_SIZE, %o2
 363         andn    %o0, (CH_ECACHE_SUBBLK_SIZE - 1), %g1
 364 
 365         /* move the good data into the W$ */
 366 1:
 367         subcc   %o2, 8, %o2
 368         ldxa    [%g1 + %o2]ASI_MEM, %g2
 369         bge     1b
 370           stxa  %g2, [%g1 + %o2]ASI_MEM
 371 
 372         /* now overwrite the bad data */
 373         setx    0xbadecc00badecc01, %g1, %g2
 374         stxa    %g2, [%o0]ASI_MEM
 375         mov     8, %g1
 376         stxa    %g2, [%o0 + %g1]ASI_MEM
 377         
 378         ECACHE_FLUSH_LINE(%o0, %o1, %o2, %o3)
 379         casxa   [%o0]ASI_MEM, %g0, %g0
 380         ECACHE_REFLUSH_LINE(%o1, %o2, %o3)
 381 
 382         /* clear the AFSR */
 383         ldxa    [%g0]ASI_AFSR, %o1
 384         stxa    %o1, [%g0]ASI_AFSR
 385         membar  #Sync
 386 
 387         /* turn NCEEN back on */
 388         stxa    %o5, [%g0]ASI_ESTATE_ERR
 389         membar  #Sync
 390 
 391         /* return and re-enable IE and AM */
 392         retl
 393           wrpr  %g0, %o4, %pstate
 394         SET_SIZE(clearphys)
 395 
 396 
 397 /*
 398  * Cheetah Ecache displacement flush the specified line from the E$
 399  *
 400  * Register usage:
 401  *      %o0 - 64 bit physical address for flushing
 402  *      %o1 - Ecache set size
 403  */
 404         ENTRY(ecache_flush_line)
 405 
 406         ECACHE_FLUSH_LINE(%o0, %o1, %o2, %o3)
 407 
 408         retl
 409           nop
 410         SET_SIZE(ecache_flush_line)
 411 
 412 /*
 413  * This routine will not be called in Cheetah systems.
 414  */
 415         ENTRY(flush_ipb)
 416         retl
 417         nop
 418         SET_SIZE(flush_ipb)
 419