root/arch/sparc/kernel/spiterrs.S
/* SPDX-License-Identifier: GPL-2.0 */
        /* We need to carefully read the error status, ACK the errors,
         * prevent recursive traps, and pass the information on to C
         * code for logging.
         *
         * We pass the AFAR in as-is, and we encode the status
         * information as described in asm-sparc64/sfafsr.h
         */
        .type           __spitfire_access_error,#function
__spitfire_access_error:
        /* Disable ESTATE error reporting so that we do not take
         * recursive traps and RED state the processor.
         */
        stxa            %g0, [%g0] ASI_ESTATE_ERROR_EN
        membar          #Sync

        mov             UDBE_UE, %g1
        ldxa            [%g0] ASI_AFSR, %g4     ! Get AFSR

        /* __spitfire_cee_trap branches here with AFSR in %g4 and
         * UDBE_CE in %g1.  It only clears ESTATE_ERR_CE in the ESTATE
         * Error Enable register.
         */
__spitfire_cee_trap_continue:
        ldxa            [%g0] ASI_AFAR, %g5     ! Get AFAR

        rdpr            %tt, %g3
        and             %g3, 0x1ff, %g3         ! Paranoia
        sllx            %g3, SFSTAT_TRAP_TYPE_SHIFT, %g3
        or              %g4, %g3, %g4
        rdpr            %tl, %g3
        cmp             %g3, 1
        mov             1, %g3
        bleu            %xcc, 1f
         sllx           %g3, SFSTAT_TL_GT_ONE_SHIFT, %g3

        or              %g4, %g3, %g4

        /* Read in the UDB error register state, clearing the sticky
         * error bits as-needed.  We only clear them if the UE bit is
         * set.  Likewise, __spitfire_cee_trap below will only do so
         * if the CE bit is set.
         *
         * NOTE: UltraSparc-I/II have high and low UDB error
         *       registers, corresponding to the two UDB units
         *       present on those chips.  UltraSparc-IIi only
         *       has a single UDB, called "SDB" in the manual.
         *       For IIi the upper UDB register always reads
         *       as zero so for our purposes things will just
         *       work with the checks below.
         */
1:      ldxa            [%g0] ASI_UDBH_ERROR_R, %g3
        and             %g3, 0x3ff, %g7         ! Paranoia
        sllx            %g7, SFSTAT_UDBH_SHIFT, %g7
        or              %g4, %g7, %g4
        andcc           %g3, %g1, %g3           ! UDBE_UE or UDBE_CE
        be,pn           %xcc, 1f
         nop
        stxa            %g3, [%g0] ASI_UDB_ERROR_W
        membar          #Sync

1:      mov             0x18, %g3
        ldxa            [%g3] ASI_UDBL_ERROR_R, %g3
        and             %g3, 0x3ff, %g7         ! Paranoia
        sllx            %g7, SFSTAT_UDBL_SHIFT, %g7
        or              %g4, %g7, %g4
        andcc           %g3, %g1, %g3           ! UDBE_UE or UDBE_CE
        be,pn           %xcc, 1f
         nop
        mov             0x18, %g7
        stxa            %g3, [%g7] ASI_UDB_ERROR_W
        membar          #Sync

1:      /* Ok, now that we've latched the error state, clear the
         * sticky bits in the AFSR.
         */
        stxa            %g4, [%g0] ASI_AFSR
        membar          #Sync

        rdpr            %tl, %g2
        cmp             %g2, 1
        rdpr            %pil, %g2
        bleu,pt         %xcc, 1f
         wrpr           %g0, PIL_NORMAL_MAX, %pil

        ba,pt           %xcc, etraptl1
         rd             %pc, %g7

        ba,a,pt         %xcc, 2f
         nop

1:      ba,pt           %xcc, etrap_irq
         rd             %pc, %g7

2:
#ifdef CONFIG_TRACE_IRQFLAGS
        call    trace_hardirqs_off
         nop
#endif
        mov             %l4, %o1
        mov             %l5, %o2
        call            spitfire_access_error
         add            %sp, PTREGS_OFF, %o0
        ba,a,pt         %xcc, rtrap
        .size           __spitfire_access_error,.-__spitfire_access_error

        /* This is the trap handler entry point for ECC correctable
         * errors.  They are corrected, but we listen for the trap so
         * that the event can be logged.
         *
         * Disrupting errors are either:
         * 1) single-bit ECC errors during UDB reads to system
         *    memory
         * 2) data parity errors during write-back events
         *
         * As far as I can make out from the manual, the CEE trap is
         * only for correctable errors during memory read accesses by
         * the front-end of the processor.
         *
         * The code below is only for trap level 1 CEE events, as it
         * is the only situation where we can safely record and log.
         * For trap level >1 we just clear the CE bit in the AFSR and
         * return.
         *
         * This is just like __spiftire_access_error above, but it
         * specifically handles correctable errors.  If an
         * uncorrectable error is indicated in the AFSR we will branch
         * directly above to __spitfire_access_error to handle it
         * instead.  Uncorrectable therefore takes priority over
         * correctable, and the error logging C code will notice this
         * case by inspecting the trap type.
         */
        .type           __spitfire_cee_trap,#function
__spitfire_cee_trap:
        ldxa            [%g0] ASI_AFSR, %g4     ! Get AFSR
        mov             1, %g3
        sllx            %g3, SFAFSR_UE_SHIFT, %g3
        andcc           %g4, %g3, %g0           ! Check for UE
        bne,pn          %xcc, __spitfire_access_error
         nop

        /* Ok, in this case we only have a correctable error.
         * Indicate we only wish to capture that state in register
         * %g1, and we only disable CE error reporting unlike UE
         * handling which disables all errors.
         */
        ldxa            [%g0] ASI_ESTATE_ERROR_EN, %g3
        andn            %g3, ESTATE_ERR_CE, %g3
        stxa            %g3, [%g0] ASI_ESTATE_ERROR_EN
        membar          #Sync

        /* Preserve AFSR in %g4, indicate UDB state to capture in %g1 */
        ba,pt           %xcc, __spitfire_cee_trap_continue
         mov            UDBE_CE, %g1
        .size           __spitfire_cee_trap,.-__spitfire_cee_trap

        .type           __spitfire_data_access_exception_tl1,#function
__spitfire_data_access_exception_tl1:
        rdpr            %pstate, %g4
        wrpr            %g4, PSTATE_MG|PSTATE_AG, %pstate
        mov             TLB_SFSR, %g3
        mov             DMMU_SFAR, %g5
        ldxa            [%g3] ASI_DMMU, %g4     ! Get SFSR
        ldxa            [%g5] ASI_DMMU, %g5     ! Get SFAR
        stxa            %g0, [%g3] ASI_DMMU     ! Clear SFSR.FaultValid bit
        membar          #Sync
        rdpr            %tt, %g3
        cmp             %g3, 0x80               ! first win spill/fill trap
        blu,pn          %xcc, 1f
         cmp            %g3, 0xff               ! last win spill/fill trap
        bgu,pn          %xcc, 1f
         nop
        ba,pt           %xcc, winfix_dax
         rdpr           %tpc, %g3
1:      sethi           %hi(109f), %g7
        ba,pt           %xcc, etraptl1
109:     or             %g7, %lo(109b), %g7
        mov             %l4, %o1
        mov             %l5, %o2
        call            spitfire_data_access_exception_tl1
         add            %sp, PTREGS_OFF, %o0
        ba,a,pt         %xcc, rtrap
        .size           __spitfire_data_access_exception_tl1,.-__spitfire_data_access_exception_tl1

        .type           __spitfire_data_access_exception,#function
__spitfire_data_access_exception:
        rdpr            %pstate, %g4
        wrpr            %g4, PSTATE_MG|PSTATE_AG, %pstate
        mov             TLB_SFSR, %g3
        mov             DMMU_SFAR, %g5
        ldxa            [%g3] ASI_DMMU, %g4     ! Get SFSR
        ldxa            [%g5] ASI_DMMU, %g5     ! Get SFAR
        stxa            %g0, [%g3] ASI_DMMU     ! Clear SFSR.FaultValid bit
        membar          #Sync
        sethi           %hi(109f), %g7
        ba,pt           %xcc, etrap
109:     or             %g7, %lo(109b), %g7
        mov             %l4, %o1
        mov             %l5, %o2
        call            spitfire_data_access_exception
         add            %sp, PTREGS_OFF, %o0
        ba,a,pt         %xcc, rtrap
        .size           __spitfire_data_access_exception,.-__spitfire_data_access_exception

        .type           __spitfire_insn_access_exception_tl1,#function
__spitfire_insn_access_exception_tl1:
        rdpr            %pstate, %g4
        wrpr            %g4, PSTATE_MG|PSTATE_AG, %pstate
        mov             TLB_SFSR, %g3
        ldxa            [%g3] ASI_IMMU, %g4     ! Get SFSR
        rdpr            %tpc, %g5               ! IMMU has no SFAR, use TPC
        stxa            %g0, [%g3] ASI_IMMU     ! Clear FaultValid bit
        membar          #Sync
        sethi           %hi(109f), %g7
        ba,pt           %xcc, etraptl1
109:     or             %g7, %lo(109b), %g7
        mov             %l4, %o1
        mov             %l5, %o2
        call            spitfire_insn_access_exception_tl1
         add            %sp, PTREGS_OFF, %o0
        ba,a,pt         %xcc, rtrap
        .size           __spitfire_insn_access_exception_tl1,.-__spitfire_insn_access_exception_tl1

        .type           __spitfire_insn_access_exception,#function
__spitfire_insn_access_exception:
        rdpr            %pstate, %g4
        wrpr            %g4, PSTATE_MG|PSTATE_AG, %pstate
        mov             TLB_SFSR, %g3
        ldxa            [%g3] ASI_IMMU, %g4     ! Get SFSR
        rdpr            %tpc, %g5               ! IMMU has no SFAR, use TPC
        stxa            %g0, [%g3] ASI_IMMU     ! Clear FaultValid bit
        membar          #Sync
        sethi           %hi(109f), %g7
        ba,pt           %xcc, etrap
109:     or             %g7, %lo(109b), %g7
        mov             %l4, %o1
        mov             %l5, %o2
        call            spitfire_insn_access_exception
         add            %sp, PTREGS_OFF, %o0
        ba,a,pt         %xcc, rtrap
        .size           __spitfire_insn_access_exception,.-__spitfire_insn_access_exception