* ========== Copyright Header Begin ==========================================
* Hypervisor Software File: errors_cmp.s
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* - Do no alter or remove copyright notices
* - Redistribution and use of this software in source and binary forms, with
* or without modification, are permitted provided that the following
* - Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* This software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or maintenance of
* ========== Copyright Header End ============================================
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
#pragma ident "@(#)errors_cmp.s 1.4 07/09/11 SMI"
#include <sys/asm_linkage.h>
#include <sparcv9/misc.h>
* Dump STB diagnostic data
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_STB, %g1
* STB index from DFESR[57:55]
srlx %g4, DFESR_STB_INDEX_SHIFT, %g4
and %g4, DFESR_STB_INDEX_MASK, %g3
sllx %g3, ASI_STB_ENTRY_SHIFT, %g3
or %g3, ASI_STB_FIELD_DATA, %g5
ldxa [%g5]ASI_STB_ACCESS, %g2
stx %g2, [%g1 + ERR_STB_DATA]
or %g3, ASI_STB_FIELD_DATA_ECC, %g5
ldxa [%g5]ASI_STB_ACCESS, %g2
stx %g2, [%g1 + ERR_STB_DATA_ECC]
* Store Buffer control and address parity
or %g3, ASI_STB_FIELD_PARITY, %g5
ldxa [%g5]ASI_STB_ACCESS, %g2
stx %g2, [%g1 + ERR_STB_PARITY]
* Store Buffer address and byte marks
or %g3, ASI_STB_FIELD_MARKS, %g5
ldxa [%g5]ASI_STB_ACCESS, %g2
stx %g2, [%g1 + ERR_STB_MARKS]
* Store Buffer current STB pointer
or %g3, ASI_STB_FIELD_CURR_PTR, %g5
ldxa [%g5]ASI_STB_ACCESS, %g2
stx %g2, [%g1 + ERR_STB_CURR_PTR]
SET_SIZE(dump_store_buffer)
* Clear a StoreBuffer error
* Dump scratchpad diagnostic data
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
* get diag_buf->err-scratchpad
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_SCRATCHPAD, %g1
* Scratchpad index from D-SFAR[2:0]
srlx %g4, DSFAR_SCRATCHPAD_INDEX_SHIFT, %g4
and %g4, DSFAR_SCRATCHPAD_INDEX_MASK, %g3
sllx %g3, ASI_SCRATCHPAD_INDEX_SHIFT, %g3
or %g3, ASI_SCRATCHPAD_DATA_NP_DATA, %g5
ldxa [%g5]ASI_SCRATCHPAD_ACCESS, %g2
stx %g2, [%g1 + ERR_SCRATCHPAD_DATA]
or %g3, ASI_SCRATCHPAD_DATA_NP_ECC, %g5
ldxa [%g5]ASI_SCRATCHPAD_ACCESS, %g2
stx %g2, [%g1 + ERR_SCRATCHPAD_ECC]
SET_SIZE(dump_scratchpad)
* Dump trap stack array diagnostic data
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_TSA, %g1
* Trapstack index from D-SFAR[2:0]
srlx %g4, DSFAR_TSA_INDEX_SHIFT, %g4
and %g4, DSFAR_TSA_INDEX_MASK, %g3
sllx %g3, ASI_TSA_INDEX_SHIFT, %g3
ldxa [%g3]ASI_TSA_ACCESS, %g2
stx %g2, [%g1 + ERR_TSA_ECC]
* We can got the precise internal_processor_error
* trap from %tl - 1. Read the trap registers from that
* TL and store them. To avoid recursive errors we need to
* disable CERER.TSAC/CERER.TSAU
mov CORE_ERR_REPORT_EN, %g3
ldxa [%g3]ASI_ERR_EN, %g4
setx (ERR_TSAU | ERR_TSAC), %g5, %g6
stxa %g6, [%g3]ASI_ERR_EN
stx %g5, [%g1 + ERR_TSA_TL]
* Note that we could have got the error at TL = 0, (from Legion).
* In that case we don't want to decrement TL as
* reading the other trap registers with TL = 0 is not allowed.
wrpr %g5, %tl ! delay slot
stx %g2, [%g1 + ERR_TSA_TT]
stx %g2, [%g1 + ERR_TSA_TSTATE]
stx %g2, [%g1 + ERR_TSA_HTSTATE]
stx %g2, [%g1 + ERR_TSA_TPC]
stx %g2, [%g1 + ERR_TSA_TNPC]
* TSA ECC covers mondo queues also
mov ERROR_RESUMABLE_QUEUE_HEAD, %g5
stx %g5, [%g1 + ERR_TSA_ERR_RES_QHEAD]
mov ERROR_RESUMABLE_QUEUE_TAIL, %g5
stx %g5, [%g1 + ERR_TSA_ERR_RES_QTAIL]
mov ERROR_NONRESUMABLE_QUEUE_HEAD, %g5
stx %g5, [%g1 + ERR_TSA_ERR_NONRES_QHEAD]
mov ERROR_NONRESUMABLE_QUEUE_TAIL, %g5
stx %g5, [%g1 + ERR_TSA_ERR_NONRES_QTAIL]
mov CPU_MONDO_QUEUE_HEAD, %g5
stx %g5, [%g1 + ERR_TSA_CPU_MONDO_QHEAD]
mov CPU_MONDO_QUEUE_TAIL, %g5
stx %g5, [%g1 + ERR_TSA_CPU_MONDO_QTAIL]
mov DEV_MONDO_QUEUE_HEAD, %g5
stx %g5, [%g1 + ERR_TSA_DEV_MONDO_QHEAD]
mov DEV_MONDO_QUEUE_TAIL, %g5
stx %g5, [%g1 + ERR_TSA_DEV_MONDO_QTAIL]
* Set CORE_ERR_ENABLE back to original
stxa %g4, [%g3]ASI_ERR_EN
* Fix Trap Stack array ECC errors
srlx %g4, DSFAR_TSA_INDEX_SHIFT, %g5
and %g5, DSFAR_TSA_INDEX_MASK, %g5
cmp %g5, 7 ! TSA entry not used
be correct_trapstack_exit
setx core_array_ecc_syndrome_table, %g3, %g2
srlx %g4, DSFAR_TSA_EVEN_SYNDROME_SHIFT, %g6
and %g6, DSFAR_TSA_SYNDROME_MASK, %g6
mulx %g6, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g6
! %g3 correction mask for lower 68 bits
srlx %g4, DSFAR_TSA_ODD_SYNDROME_SHIFT, %g6
and %g6, DSFAR_TSA_SYNDROME_MASK, %g6
mulx %g6, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g6
! %g3 correction mask for upper 68 bits
ble,a %xcc, 1f ! if the syndrome is for a bit,
add %g3, ECC_LAST_BIT, %g3 ! move it up to the top 67 bits
cmp %g3, ECC_ne ! No error
be correct_trapstack_exit
cmp %g3, ECC_U ! Uncorrectable double (or 2n) bit error
cmp %g3, ECC_M ! Triple or worse (2n + 1) bit error
mov CORE_ERR_REPORT_EN, %g6
ldxa [%g6]ASI_ERR_EN, %g4
mov CORE_ERR_REPORT_EN, %g6
stxa %g4, [%g6]ASI_ERR_EN
cmp %g5, 6 ! mondo/dev/error queues
be correct_trapstack_queues
* error is in the trap registers
* We use %g3 to determine which of the trap registers is in
* error, don't change the order of these checks.
* Checkbit or unused bit error
* read/write any trap register to correct - so we read write them all
CORRECT_TSA_ALL_REGS(%g5, %g4, %g2, correct_trapstack_exit)
* We have a single bit error in one of the trap registers
* %g5 trap level when error occurred
sub %g3, TSA_TNPC_LO_BIT, %g3
add %g3, 2, %g3 ! bits[45:0] -> TNPC[47:2]
CORRECT_TSA_PREG(%tnpc, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
sub %g3, TSA_TPC_LO_BIT, %g3
add %g3, 2, %g3 ! bits[45:0] -> TPC[47:2]
CORRECT_TSA_PREG(%tpc, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
sub %g3, TSA_TT_LO_BIT, %g3
CORRECT_TSA_PREG(%tt, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_CWP_HI_BIT
sub %g3, TSA_TSTATE_CWP_LO_BIT, %g3
CORRECT_TSA_PREG(%tt, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_HTSTATE_TLZ_HI_BIT
sub %g3, TSA_HTSTATE_TLZ_LO_BIT, %g3
CORRECT_TSA_HREG(%htstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_IE_HI_BIT
mov 9, %g3 !tstate.pstate.ie bit 9
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_PRIV_HI_BIT
mov 10, %g3 !tstate.pstate.priv bit 10
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_AM_HI_BIT
mov 11, %g3 !tstate.pstate.am bit 11
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_PEF_HI_BIT
mov 12, %g3 !tstate.pstate.pef bit 12
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_HTSTATE_RED_HI_BIT
mov 5, %g3 !htstate.red 5
CORRECT_TSA_HREG(%htstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_HTSTATE_PRIV_HI_BIT
mov 2, %g3 !htstate.priv bit 2
CORRECT_TSA_HREG(%htstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_TCT_HI_BIT
mov 20, %g3 !tstate.pstate.tct bit 20
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_TLE_HI_BIT
mov 16, %g3 !tstate.pstate.tle bit 16
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_PSTATE_CLE_HI_BIT
mov 17, %g3 !tstate.pstate.cle bit 17
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_HTSTATE_IBE_HI_BIT
mov 10, %g3 !htstate.ibe bit 10
CORRECT_TSA_HREG(%htstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_ASI_HI_BIT
sub %g3, TSA_TSTATE_ASI_LO_BIT, %g3
add %g3, 24, %g3 ! tstate.asi [31:24}
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_CCR_HI_BIT
sub %g3, TSA_TSTATE_CCR_LO_BIT, %g3
add %g3, 32, %g3 ! tstate.ccr [39:32]
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_TSTATE_GL_HI_BIT
bg,a %xcc, correct_trapstack_error_in_ecc
sub %g3, TSA_TSTATE_GL_LO_BIT, %g3
add %g3, 40, %g3 ! tstate.gl [41:40]
CORRECT_TSA_PREG(%tstate, %g5, %g3, %g4, %g2, %g6, correct_trapstack_exit)
correct_trapstack_error_in_ecc:
! should not get here ...
* Set CORE_ERR_ENABLE back to original
mov CORE_ERR_REPORT_EN, %g3
ldxa [%g3]ASI_ERR_EN, %g4
stxa %g4, [%g3]ASI_ERR_EN
correct_trapstack_queues:
* error is in the queue ASI registers
* We use %g3 to determine which of the queue ASI registers is in
* error, don't change the order of these checks.
cmp %g3, TSA_NONRES_ERR_QUEUE_TAIL_HI_BIT
sub %g3, TSA_NONRES_ERR_QUEUE_TAIL_LO_BIT, %g3 ! bits [21:14] -> [13:6]
CORRECT_TSA_QUEUE(ERROR_NONRESUMABLE_QUEUE_TAIL,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_NONRES_ERR_QUEUE_HEAD_HI_BIT
sub %g3, TSA_NONRES_ERR_QUEUE_HEAD_LO_BIT, %g3 ! bits [29:22] -> [13:6]
CORRECT_TSA_QUEUE(ERROR_NONRESUMABLE_QUEUE_HEAD,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_RES_ERR_QUEUE_TAIL_HI_BIT
sub %g3, TSA_RES_ERR_QUEUE_TAIL_LO_BIT, %g3 ! bits [37:20] -> [13:6]
CORRECT_TSA_QUEUE(ERROR_RESUMABLE_QUEUE_TAIL,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_RES_ERR_QUEUE_HEAD_HI_BIT
sub %g3, TSA_RES_ERR_QUEUE_HEAD_LO_BIT, %g3 ! bits [45:38] -> [13:6]
CORRECT_TSA_QUEUE(ERROR_RESUMABLE_QUEUE_HEAD,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_DEV_QUEUE_TAIL_HI_BIT
sub %g3, TSA_DEV_QUEUE_TAIL_LO_BIT, %g3 ! bits [67:60] -> [13:6]
CORRECT_TSA_QUEUE(DEV_MONDO_QUEUE_TAIL,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_DEV_QUEUE_HEAD_HI_BIT
sub %g3, TSA_DEV_QUEUE_HEAD_LO_BIT, %g3 ! bits [75:68] -> [13:6]
CORRECT_TSA_QUEUE(DEV_MONDO_QUEUE_HEAD,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_MONDO_QUEUE_TAIL_HI_BIT
sub %g3, TSA_MONDO_QUEUE_TAIL_LO_BIT, %g3 ! bits [83:76] -> [13:6]
CORRECT_TSA_QUEUE(CPU_MONDO_QUEUE_TAIL,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
cmp %g3, TSA_MONDO_QUEUE_HEAD_HI_BIT
bg,a %xcc, correct_trapstack_error_in_ecc
sub %g3, TSA_MONDO_QUEUE_HEAD_LO_BIT, %g3 ! bits [91:84] -> [13:6]
CORRECT_TSA_QUEUE(CPU_MONDO_QUEUE_HEAD,
%g3, %g4, %g2, %g6, correct_trapstack_exit)
* Set CORE_ERR_ENABLE back to original
mov CORE_ERR_REPORT_EN, %g6
ldxa [%g6]ASI_ERR_EN, %g4
stxa %g4, [%g6]ASI_ERR_EN
* We know that TSAU is (TSAC entry + 1) so
* get the error table entry and move it forward
SET_SIZE(correct_trapstack)
* Dump Tick_compare diagnostic data
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_TCA, %g1
* TCA index from D-SFAR[2:0]
srlx %g4, DSFAR_TCA_INDEX_SHIFT, %g4
and %g4, DSFAR_TCA_INDEX_MASK, %g3
sllx %g3, ASI_TICK_INDEX_SHIFT, %g3
or %g3, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g2
stx %g2, [%g1 + ERR_TCA_DATA]
or %g3, ASI_TICK_DATA_NP_ECC, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g2
stx %g2, [%g1 + ERR_TCA_ECC]
SET_SIZE(dump_tick_compare)
* Index/syndrome for a precise TCCP error stored in DESR
srlx %g4, DSFAR_TCA_INDEX_SHIFT, %g5
and %g5, DSFAR_TCA_INDEX_MASK, %g5
srlx %g4, DSFAR_TCA_SYNDROME_SHIFT, %g4
and %g4, DSFAR_TCA_SYNDROME_MASK, %g4
ba correct_tick_compare ! tail call
SET_SIZE(correct_tick_tccp)
* Index/syndrome for a disrupting TCCD error stored in DESR
srlx %g4, DESR_TCA_INDEX_SHIFT, %g5
and %g5, DESR_TCA_INDEX_MASK, %g5
srlx %g4, DESR_TCA_SYNDROME_SHIFT, %g4
and %g4, DESR_TCA_SYNDROME_MASK, %g4
ba correct_tick_compare ! tail call
SET_SIZE(correct_tick_tccd)
* TCA_ECC_ERRATA The correct value is not returned when we read
* from the TCA diagnostic registers
ENTRY(correct_tick_compare)
setx core_array_ecc_syndrome_table, %g2, %g3
mulx %g4, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g4
cmp %g3, ECC_ne ! no error
be correct_tick_compare_exit
cmp %g3, ECC_U ! Uncorrectable double (or 2n) bit error
cmp %g3, ECC_M ! Triple or worse (2n + 1) bit error
! no correction mask for checkbit errors
* On precise internal_processor_error traps, the
* tick register index from the D-SFAR may be incorrect.
* This does not apply to disrupting sw_recoverable_error
* If it's a correction bit error, %g6 == 0, it is safe
* to read/write all HSTICK/TICK/STICK CMP registers and
* continue as we will get the correct value from the
* diagnostic register (without causing an error trap) and
* the write of this value will clear the ECC error.
* We don't have any information on whether interrupts
* are enabled for TICK_CMPR, and we can't get valid
* data from either the TICKCMPR register or the diagnostic
* register, so we clear the error by writing (TICK + delay)
* which will also trigger a TICKCMPR interrupt. The guest
* will have to figure out whether its a spurious interrupt
set ERR_TCA_INCREMENT, %g4
* We don't have any information on whether interrupts
* are enabled for HSTICK_CMPR, and we can't get valid
* data from either the HSTICKCMPR register or the diagnostic
* register, so we clear the error by writing (STICK + delay)
* which will also trigger a HSTICKCMPR interrupt. The HV
* will have to figure out whether its a spurious interrupt
* We don't have any information on whether interrupts
* are enabled for STICK_CMPR, and we can't get valid
* data from either the STICKCMPR register or the diagnostic
* register, so we clear the error by writing (STICK + delay)
* which will also trigger a STICKCMPR interrupt. The guest
* will have to figure out whether its a spurious interrupt
#else /* !TCA_ECC_ERRATA */
sllx %g5, ASI_TICK_INDEX_SHIFT, %g5
or %g5, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g5
sllx %g5, ASI_TICK_INDEX_SHIFT, %g5
or %g5, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g5
mov TCA_HSTICK_COMPARE, %g5
sllx %g5, ASI_TICK_INDEX_SHIFT, %g5
or %g5, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g5
! %g5 hstick_compare value
#endif /* TCA_ECC_ERRATA */
ba correct_tick_compare_exit
* If the error occurred in hyperprivileged mode, we
* know that this was an access to the hstick_cmpr
* register as the HV never accesses TICKCMP/STICKCMP registers
* (except when using the error injector).
bnz correct_htick_compare
mov TCA_HSTICK_COMPARE, %g5
* This precise trap was caused by an ECC error during a rdasr/rdhpr
* access to one of the tick compare registers. This ECC error will
* also cause the h/w comparisons to trigger a disrupting trap with
* the correct index set. Check if PSTATE.IE will be enabled after
* the RETRY, and if SETER.DE is set. If these conditions are met
* and we delay by at least 128 cycles, we will take the disrupting
* trap and be able to correct the ECC error. The instruction which
* caused this precise trap will then execute correctly. If either
* of these conditions is not satisfied we convert the error into a UE.
! PSTATE.IE enabled on RETRY ?
srlx %g1, TSTATE_PSTATE_SHIFT, %g1
! SETER.DE enabled after RETRY ?
mov CORE_ERR_TRAP_EN, %g1
ldxa [%g1]ASI_ERR_EN, %g1
* We can just return without correcting the error. In which
* case we will handle the error as normal, sending a report
* to the SP. This will quarantee a sufficient delay.
* We will get a disrupting trap immediately on exiting the
* error trap handler via RETRY.
* The Diagnosis Engine will receive two error reports, one for
* this precise trap, one for the disrupting trap we expect
* on exiting this error handler.
ba correct_tick_compare_exit
* We don't know which it is - TICKCMP/STICKCMP, and we won't take
* a disrupting trap if we retry the instruction, so we clear
* TICKCMP/STICKCMP to get rid of the error condition and treat
wr %g0, STICKCMP ! FIXME ?
#endif /* ERRATA_TICK_INDEX */
cmp %g5, TCA_HSTICK_COMPARE
! should not get here ...
* We know that TCUP/TCUD is (TCCP/TCCD entry + 1) so
* get the error table entry and move it forward
* We don't have any information on whether interrupts
* are enabled for TICK_CMPR, and we can't get valid
* data from either the TICKCMPR register or the diagnostic
* register, so we clear the error by writing (TICK + delay)
* which will also trigger a TICKCMPR interrupt. The guest
* will have to figure out whether its a spurious interrupt
set ERR_TCA_INCREMENT, %g4
* read tick_cmpr from diagnostic ASI
* get syndrome from D-SFAR, correction code from
* core_array_ecc_syndrome_table
* xor correction mask with value and write back to ASR
sllx %g5, ASI_TICK_INDEX_SHIFT, %g5
or %g5, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g5
ba correct_tick_compare_exit
* We don't have any information on whether interrupts
* are enabled for STICK_CMPR, and we can't get valid
* data from either the STICKCMPR register or the diagnostic
* register, so we clear the error by writing (STICK + delay)
* which will also trigger a STICKCMPR interrupt. The guest
* will have to figure out whether its a spurious interrupt
set ERR_TCA_INCREMENT, %g4
* read stick_cmpr from diagnostic ASI
* get syndrome from D-SFAR, correction code from
* core_array_ecc_syndrome_table
* xor correction mask with value and write back to ASR
sllx %g5, ASI_TICK_INDEX_SHIFT, %g5
or %g5, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g5
ba correct_tick_compare_exit
* We don't have any information on whether interrupts
* are enabled for HSTICK_CMPR, and we can't get valid
* data from either the HSTICKCMPR register or the diagnostic
* register, so we clear the error by writing (STICK + delay)
* which will also trigger a HSTICKCMPR interrupt. The HV
* will have to figure out whether its a spurious interrupt
set ERR_TCA_INCREMENT, %g4
* read hstick_compare from diagnostic ASI
* get syndrome from D-SFAR, correction code from
* core_array_ecc_syndrome_table
* xor correction mask with value and write back to ASR
sllx %g5, ASI_TICK_INDEX_SHIFT, %g5
or %g5, ASI_TICK_DATA_NP_DATA, %g5
ldxa [%g5]ASI_TICK_ACCESS, %g5
! %g5 hstick_compare value
correct_tick_compare_exit:
SET_SIZE(correct_tick_compare)
* Clear UEs from Tick register array
ENTRY(clear_tick_compare)
SET_SIZE(clear_tick_compare)
* Fix scratchpad array error
srlx %g4, DSFAR_SCRATCHPAD_INDEX_SHIFT, %g5
and %g5, DSFAR_SCRATCHPAD_INDEX_MASK, %g5
sllx %g5, ASI_SCRATCHPAD_INDEX_SHIFT, %g5
! %g5 (scratchpad register * 8) => VA for
! diagnostic ASI_SCRATCHPAD_ACCESS register access
* If this is a hypervisor scratchpad register it was reloaded
* with the correct data. As long as we didn't clobber the globals
cmp %g5, HSCRATCH_VCPU_STRUCT
cmp %g5, HSCRATCH_STRAND_STRUCT
srlx %g2, TSTATE_GL_SHIFT, %g2
and %g2, TSTATE_GL_MASK, %g2
be,pt %xcc, convert_scac_to_scau
* It's a HV scratchpad register, we haven't clobbered the
* globals, the register was corrected in the trap handler,
ba,pt %xcc, correct_scac_exit
* read scratchpad from diagnostic ASI
* get syndrome from D-SFAR, correction code from
* core_array_ecc_syndrome_table
* xor correction mask with value and write back to ASI
srlx %g4, DSFAR_SCRATCHPAD_SYNDROME_SHIFT, %g4
and %g4, DSFAR_SCRATCHPAD_SYNDROME_MASK, %g4
setx core_array_ecc_syndrome_table, %g2, %g3
mulx %g4, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g4
cmp %g3, ECC_ne ! no error
cmp %g3, ECC_U ! Uncorrectable double (or 2n) bit error
cmp %g3, ECC_M ! Triple or worse (2n + 1) bit error
! no correction mask for checkbit errors
! %g5 scratchpad register VA (index * 8)
ldxa [%g5]ASI_SCRATCHPAD_ACCESS, %g6
! %g6 scratchpad register value
stxa %g6, [%g5]ASI_SCRATCHPAD
* We know that SCAU is (SCAC entry + 1) so
* get the error table entry and move it forward
* Fix scratchpad array UE if possible
srlx %g4, DSFAR_SCRATCHPAD_INDEX_SHIFT, %g5
and %g5, DSFAR_SCRATCHPAD_INDEX_MASK, %g5
! %g5 (scratchpad register * 8) => VA for
! diagnostic ASI_SCRATCHPAD_ACCESS register access
* If this is a hypervisor scratchpad register and we
* haven't overwritten the trap globals, we can correct
blt %xcc, correct_scau_exit
bgt %xcc, correct_scau_exit
srlx %g2, TSTATE_GL_SHIFT, %g2
and %g2, TSTATE_GL_MASK, %g2
bne,pt %xcc, convert_scau_to_scac
* Error was corrected on entry to trap handler.
* See SCRATCHPAD_ERROR() macro.
* We know that SCAC is (SCAU entry - 1) so
* get the error table entry and move it back
* Populate a sun4v ereport packet for STB errors
* with invalid real address and size == 8
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
brz,pn %g2, stb_sun4v_report_exit
stx %g3, [%g2 + ERR_SUN4V_RPRT_ADDR]
st %g3, [%g2 + ERR_SUN4V_RPRT_SZ]
SET_SIZE(stb_sun4v_report)
* Populate a sun4v ereport packet for SCA errors
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
brz,pn %g2, sca_sun4v_report_exit
stub %g3, [%g2 + ERR_SUN4V_RPRT_ASI]
* Scratchpad index from D-SFAR[2:0]
srlx %g4, DSFAR_SCRATCHPAD_INDEX_SHIFT, %g4
and %g4, DSFAR_SCRATCHPAD_INDEX_MASK, %g3
sllx %g3, ASI_SCRATCHPAD_INDEX_SHIFT, %g3 ! index -> VA
stx %g3, [%g2 + ERR_SUN4V_RPRT_ADDR]
* SZ set to 8 bytes for a single ASI
st %g3, [%g2 + ERR_SUN4V_RPRT_SZ]
SET_SIZE(sca_sun4v_report)
* Populate a sun4v ereport packet for Tick_compare errors
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
brz,pn %g2, tick_sun4v_report_exit
* TCA index from D-SFAR[2:0]
* We only send a guest report for tick/stick_cmpr
srlx %g4, DSFAR_TCA_INDEX_SHIFT, %g4
and %g4, DSFAR_TCA_INDEX_MASK, %g3
cmp %g3, 2 ! HSTICK_COMPARE
stx %g0, [%g2 + ERR_SUN4V_RPRT_ATTR]
! ASR 0x17 tick_cmpr, 0x19 STICK_CMPR
stuh %g4, [%g2 + ERR_SUN4V_RPRT_REG]
SET_SIZE(tick_sun4v_report)
* Populate a sun4v ereport packet for TrapStack errors
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
brz,pn %g2, tsa_sun4v_report_exit
* Trapstack index from D-SFAR[2:0]
srlx %g4, DSFAR_TSA_INDEX_SHIFT, %g5
and %g5, DSFAR_TSA_INDEX_MASK, %g5
! index == 6 -> mondo queues, 7 -> not used
stuh %g5, [%g2 + ERR_SUN4V_RPRT_REG]
* SZ set to 8 bytes for a single ASI
st %g3, [%g2 + ERR_SUN4V_RPRT_SZ]
stx %g0, [%g2 + ERR_SUN4V_RPRT_ATTR]
SET_SIZE(tsa_sun4v_report)
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_MAMU, %g1
ldxa [%g2]ASI_STREAM, %g3
stx %g3, [%g1 + ERR_MA_CTL]
ldxa [%g2]ASI_STREAM, %g3
stx %g3, [%g1 + ERR_MA_PA]
ldxa [%g2]ASI_STREAM, %g3
stx %g3, [%g1 + ERR_MA_NP]
ldxa [%g2]ASI_STREAM, %g3
stx %g3, [%g1 + ERR_MA_SYNC]
ldxa [%g2]ASI_STREAM, %g3
stx %g3, [%g1 + ERR_MA_ADDR]
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_REG, %g1
* FRF index from D-SFAR[5:1]
and %g4, DSFAR_FRF_DBL_REG_MASK, %g3
stx %g3, [%g1 + ERR_DIAG_BUF_SPARC_DSFAR]
sllx %g3, ASI_FRF_ECC_INDEX_SHIFT, %g3
ldxa [%g3]ASI_FRF_ECC_REG, %g3
stx %g3, [%g1 + ERR_REG_ECC]
* Convert the IRF index to the Sparc V9 equivalent
srlx %g4, DSFAR_IRF_INDEX_SHIFT, %g2
and %g2, DSFAR_IRF_INDEX_MASK, %g2
CONVERT_IRF_INDEX(%g2, %g5)
andn %g4, DSFAR_IRF_INDEX_MASK, %g5
stx %g5, [%g1 + ERR_DIAG_BUF_SPARC_DSFAR]
* IRF index from D-SFAR[4:0]
mov DSFAR_IRF_INDEX_MASK, %g3
srlx %g4, DSFAR_IRF_INDEX_SHIFT, %g4
and %g4, DSFAR_IRF_INDEX_MASK, %g3
sllx %g3, ASI_IRF_ECC_INDEX_SHIFT, %g3
ldxa [%g3]ASI_IRF_ECC_REG, %g3
stx %g3, [%g1 + ERR_REG_ECC]
* Add the integer reg number to the sun4v guest report
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
srlx %g3, DSFAR_IRF_INDEX_SHIFT, %g3
and %g3, DSFAR_IRF_INDEX_MASK, %g3
* Convert the IRF index to the Sparc V9 equivalent
CONVERT_IRF_INDEX(%g3, %g5)
stub %g3, [%g2 + ERR_SUN4V_RPRT_ASI]
SET_SIZE(irf_sun4v_report)
* Add the FP reg number to the sun4v guest report
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
srlx %g3, DSFAR_FRF_INDEX_SHIFT, %g3
and %g3, DSFAR_FRF_INDEX_MASK, %g3
stub %g3, [%g2 + ERR_SUN4V_RPRT_ASI]
SET_SIZE(frf_sun4v_report)
* Check whether the FRU error is transient or persistent
* or if the floating point register file is failing.
* if (D-SFSR.[TL - 1].FRFU && D-SFAR.[TL -1] == D-SFAR) {
* previous trap was identical,
* if (D-SFSR.[TL - 1].FRFU {
STORE_ERR_RETURN_ADDR(%g7, %g1, %g2)
* If we get to here we have created a sun4v FRF
* precise non-resumable error report and the
* FP UE has been cleared.
* Either we have nested FRF traps, in which case we have a
* failed RF and we mark the CPU as bad, or we have a different
sub %g2, 2, %g2 ! (TL - 1) - 1 for diag_buf
mulx %g2, STRAND_ERR_ESR_INCR, %g2
add %g3, STRAND_ERR_DSFSR, %g3
ldx [%g3], %g3 ! D-SFSR.[TL - 1]
* Not a nested FRF error, clear it and return
* If we get to here we have created a sun4v FRF
* precise non-resumable error report and the
* FP UE has been cleared. return
* we have nested FRF errors
* mark the CPU as bad and send a CPU Sun4v report
SET_CPU_IN_ERROR(%g2, %g3)
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
st %g3, [%g2 + ERR_SUN4V_RPRT_ATTR]
mov EDESC_UE_RESUMABLE, %g3
st %g3, [%g2 + ERR_SUN4V_RPRT_EDESC]
GET_ERR_RETURN_ADDR(%g7, %g1)
* clear_frf_ue() [LEAF function]
* Clear the UE in the floating-point register file
srlx %g2, DSFAR_FRF_INDEX_SHIFT, %g2
and %g2, DSFAR_FRF_INDEX_MASK, %g2
! ensure FPRS.FEF is set. PSTATE.PEF is set by the Sparc h/w when
bz,a,pn %xcc, 1f ! no: set it
wr %g4, FPRS_FEF, %fprs ! yes: annulled
! Now clear the register in error
rd %pc, %g3 ! %g3 = base address
! an array of instruction blocks indexed by register number to
! clear the floating-point register reported in error
! The first 32 entries use single-precision register
! The next 32 entries clear the double-precision register
! double precision register pairs, clear both of them on errors
! %g2 has freg number, %g3 has base address-4
sllx %g2, 3, %g2 ! offset = freg# * 8
add %g3, %g2, %g3 ! %g3 = instruction block addr
jmp %g3 + SZ_INSTR ! jmp to clear register
* Disable Tick_compare correctable errors for a short period
* to ensure that performance is not affected if the error
* is persistent. Note that this is for h/w compare operations,
* (TCCD errors), not ASR reads (TCCP errors).
! first verify that storm prevention is enabled
CHECK_BLACKOUT_INTERVAL(%g4)
* save our return address
STORE_ERR_RETURN_ADDR(%g7, %g4, %g5)
mov CORE_ERR_REPORT_EN, %g3
ldxa [%g3]ASI_ERR_EN, %g4
setx (ERR_TCCD), %g5, %g6
bz,pn %xcc, 9f ! TCCD already off
stxa %g6, [%g3]ASI_ERR_EN
* Set up a cyclic on this strand to re-enable the TCCP/TCCD bits
* after an interval of 6 seconds. Set a flag in the
* strand struct to indicate that the cyclic has been set
mov STRAND_ERR_FLAG_TICK_CMP, %g4
lduw [%g6 + STRAND_ERR_FLAG], %g2 ! installed flags
btst %g4, %g2 ! handler installed?
STRAND2CONFIG_STRUCT(%g6, %g4)
ldx [%g4 + CONFIG_CE_BLACKOUT], %g1
brz,a,pn %g1, 9f ! zero: blackout disabled
! handler installed, set flag
SET_STRAND_ERR_FLAG(%g6, STRAND_ERR_FLAG_TICK_CMP, %g5)
setx cerer_set_error_bits, %g5, %g2
sub %g2, %g4, %g2 ! g2 = handler address
setx ERR_TCCD, %g4, %g3 ! g3 = arg 0 : bit(s) to set
mov STRAND_ERR_FLAG_TICK_CMP, %g4 ! g4 = arg 1 : cpu flags to clear
HVCALL(cyclic_add_rel) /* ( del_tick, address, arg0, arg1 ) */
GET_ERR_RETURN_ADDR(%g7, %g2)
* cyclic function used to re-enable CERER bits
* %g2 strand->err_flags to clear
ENTRY(cerer_set_error_bits)
CLEAR_STRAND_ERR_FLAG(%g6, %g2, %g5)
mov CORE_ERR_REPORT_EN, %g5
ldxa [%g5]ASI_ERR_EN, %g4
or %g4, %g1, %g4 ! enable arg0 flags
stxa %g4, [%g5]ASI_ERR_EN
SET_SIZE(cerer_set_error_bits)
* Check whether the IRFU error is transient or persistent
* or if the integre register file is failing.
* if (D-SFSR.[TL - 1].IRFU && D-SFAR.[TL -1] == D-SFAR) {
* previous trap was identical,
* if (D-SFSR.[TL - 1].IRFU {
STORE_ERR_RETURN_ADDR(%g7, %g1, %g2)
* If we get to here we have created a sun4v IRF
* precise non-resumable error report and the
* IRF UE has been cleared.
* Either we have nested IRF traps, in which case we have a
* failed RF and we mark the CPU as bad, or we have a different
sub %g2, 2, %g2 ! (TL - 1) - 1 for diag_buf
mulx %g2, STRAND_ERR_ESR_INCR, %g2
add %g3, STRAND_ERR_DSFSR, %g3
ldx [%g3], %g3 ! D-SFSR.[TL - 1]
* Not a nested IRF error, clear it and return
* If we get to here we have created a sun4v IRF
* precise non-resumable error report and the
* IRF UE has been cleared. return
* we have nested IRF errors
* mark the CPU as bad and send a CPU Sun4v report
SET_CPU_IN_ERROR(%g2, %g3)
GET_ERR_SUN4V_RPRT_BUF(%g2, %g3)
st %g3, [%g2 + ERR_SUN4V_RPRT_ATTR]
mov EDESC_UE_RESUMABLE, %g3
st %g3, [%g2 + ERR_SUN4V_RPRT_EDESC]
GET_ERR_RETURN_ADDR(%g7, %g1)
* Clear the UE in the integer register file
! get the register number within the set
srlx %g3, DSFAR_IRF_INDEX_SHIFT, %g2
and %g2, DSFAR_IRF_INDEX_MASK, %g2
* The index from the D-SFAR does not match the standard Sparc V9 register
CONVERT_IRF_INDEX(%g2, %g4)
cmp %g2, 8 ! is reg# < 8?
bl irf_glob_ue ! yes, then global reg
! Now clear the register in error
rd %pc, %g3 ! get clear instr base addr
! an array of instruction blocks indexed by register number to
! clear the non-global register reported in error.
sub %g2, 8, %g2 ! skip globals
sllx %g2, 3, %g2 ! offset = reg# * 8
add %g3, %g2, %g3 ! %g3 = instruction block addr
jmp %g3 + SZ_INSTR ! jmp to clear register
! restore gl from value in %o0, and restore %o0
wrpr %o0, %gl ! restore %gl
mov %g4, %o0 ! restore %o0
! %g1 has the gl + register number
! now re-read the global register in error
rd %pc, %g3 ! get clear instr base addr
! an array of instructions blocks indexed by global register number
! to clear the global register reported in error.
! %gl points to the error global set
sllx %g2, 3, %g2 ! offset (2 instrs)
add %g3, %g2, %g3 ! %g3 = instruction entry
mov %o0, %g4 ! save %o0 in %g4
GET_ERR_GL(%o0) ! save %gl in %o0
srlx %g1, DSFAR_IRF_GL_SHIFT, %g2 ! get global set from SFAR
and %g2, DSFAR_IRF_GL_MASK, %g2 ! %g2 has %gl value
jmp %g3 + SZ_INSTR ! jump to clear global
wrpr %g2, %gl ! set gl to error gl
* Correct an IRF ECC error
* Get register address from D-SFAR[4:0]
* Get ECC syndrome from D-SFAR[14:7]
* (Note: could get an error while doing the correction ....
* and then it all goes horribly horribly wrong !)
* Decode ECC syndrome using ecc_table[]
* - if error in data bits, ecc_table[syndrome] = [0 .. 63]
* xor correction mask with data read from IRF
* - write data back to IRF
STORE_ERR_RETURN_ADDR(%g7, %g4, %g5)
srlx %g4, DSFAR_IRF_SYNDROME_SHIFT, %g5
and %g5, DSFAR_IRF_SYNDROME_MASK, %g5
setx irf_ecc_syndrome_table, %g2, %g3
mulx %g5, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g5
! decoded ECC syndrome in %g5.
* check for multiple bit errors, and no-error
cmp %g5, ECC_ne ! no error
be,pn %xcc, correct_irfc_exit
cmp %g5, ECC_U ! Uncorrectable double (or 2n) bit error */
be,pn %xcc, convert_irfc_to_irfu
cmp %g5, ECC_M ! Triple or worse (2n + 1) bit error */
be,pn %xcc, convert_irfc_to_irfu
srlx %g4, DSFAR_IRF_INDEX_SHIFT, %g4
and %g4, DSFAR_IRF_INDEX_MASK, %g4
! data bit in error is in %g5
mov CORE_ERR_TRAP_EN, %g2
ldxa [%g2]ASI_ERR_EN, %g3
stxa %g3, [%g2]ASI_ERR_EN
* The index from the D-SFAR does not match the standard Sparc V9 register
CONVERT_IRF_INDEX(%g4, %g1)
! reg# < 8 => global register
bl,pn %xcc, correct_irfc_gl
! Now read the register in error
rd %pc, %g3 ! get read instr base addr
! an array of instruction blocks indexed by register number to
! clear the non-global register reported in error.
CORRECT_IRFC(%o0, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o1, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o2, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o3, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o4, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o5, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o6, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%o7, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l0, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l1, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l2, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l3, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l4, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l5, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l6, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%l7, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i0, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i1, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i2, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i3, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i4, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i5, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i6, %g1, %g6, correct_irfc_done)
CORRECT_IRFC(%i7, %g1, %g6, correct_irfc_done)
* The correction mask is generated as a 64-bit vector of 0's with a single
* 1 bit by decoding the syndrome (using rf_ecc-syndrome_table[]).
* We XOR that vector with the register data.
* (Note if the error is in a check bit the vector is all 0's - no need to
* Once we have the corrected data, just write it back to the register. If
* the error was in the check bits, hardware will (should) generate the
* correct check bits and write both the data and the check bits to the
* register file contents.
sub %g4, 8, %g4 ! skip globals
cmp %g5, ECC_ne ! if syndrome > ECC_ne
movge %xcc, %g0, %g1 ! no/checkbit error, clear correction mask
mulx %g4, CORRECT_IRFC_SIZE, %g4 ! offset = reg# * CORRECT_IRFC_SIZE
add %g3, %g4, %g3 ! %g3 = instruction block addr
jmp %g3 + SZ_INSTR ! jmp to correct register
* Error was in a global register
* We need a couple of non-globals registers to play with
* when we change GL to the value at the time of the erorr
! get the base address of the instruction to read the register
sllx %g4, 3, %g4 ! offset (2 instrs)
add %g3, %g4, %g3 ! %g3 = instruction entry
GET_ERR_GL(%o4) ! save %gl in %o4
! set GL to error trap GL
srlx %g6, DSFAR_IRF_GL_SHIFT, %g6
and %g6, DSFAR_IRF_GL_MASK, %g6
jmp %g3 + SZ_INSTR ! jump to clear global
wrpr %g6, %gl ! set gl to error gl
cmp %g5, ECC_ne ! if syndrome >= ECC_ne
movge %xcc, %g0, %g6 ! no/checkbit error, clear correction mask
! Now restore the register in error
rd %pc, %g3 ! get restore instr base addr
add %g3, %g4, %g3 ! %g3 = instruction entry
GET_ERR_GL(%o4) ! save %gl in %o4
! set GL to error trap GL
srlx %g6, DSFAR_IRF_GL_SHIFT, %g6
and %g6, DSFAR_IRF_GL_MASK, %g6
jmp %g3 + SZ_INSTR ! jump to clear global
wrpr %g6, %gl ! set gl to error gl
mov CORE_ERR_TRAP_EN, %g2
ldxa [%g2]ASI_ERR_EN, %g3
stxa %g3, [%g2]ASI_ERR_EN
GET_ERR_RETURN_ADDR(%g7, %g4)
set ((1 << 31) | (1 << 25) | 1), %g5
stxa %g5, [%g0]ASI_ERROR_INJECT_REG
! should get an IRFC on this instruction
stxa %g0, [%g0]ASI_ERROR_INJECT_REG
PRINT_NOTRAP("Failed to fix IRFC\r\n")
PRINT_NOTRAP("Fixed IRFC\r\n")
set ((1 << 31) | (1 << 24) | 4), %g5
stxa %g5, [%g0]ASI_ERROR_INJECT_REG
! should get an FRFC on this instruction
stxa %g0, [%g0]ASI_ERROR_INJECT_REG
set ((1 << 31) | (1 << 23) | 7), %g5
stxa %g5, [%g0]ASI_ERROR_INJECT_REG
! should get an SCAC on this instruction
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
ldxa [%g5]ASI_HSCRATCHPAD, %g5
stxa %g0, [%g0]ASI_ERROR_INJECT_REG
set ((1 << 31) | (1 << 21) | 8), %g5
stxa %g5, [%g0]ASI_ERROR_INJECT_REG
! should get an TSAC on this instruction
stxa %g0, [%g0]ASI_ERROR_INJECT_REG
SET_SIZE(inject_cmp_errors)
GET_ERR_DIAG_DATA_BUF(%g1, %g2)
* Store L2 ESR/EAR for the banks into the DIAG_BUF
set (NO_L2_BANKS - 1), %g3
! skip banks which are disabled. causes hang.
SKIP_DISABLED_L2_BANK(%g3, %g4, %g5, 2f)
setx L2_ERROR_STATUS_REG, %g4, %g5
sllx %g3, L2_BANK_SHIFT, %g2
stx %g4, [%g2] ! clear ESR RW1C
stx %g0, [%g2] ! clear ESR RW
add %g1, ERR_DIAG_BUF_L2_CACHE_ESR, %g2
mulx %g3, ERR_DIAG_BUF_L2_CACHE_ESR_INCR, %g5
! %g2 diag_buf->l2_cache.esr
add %g1, ERR_DIAG_BUF_L2_CACHE_EAR, %g2
mulx %g3, ERR_DIAG_BUF_L2_CACHE_EAR_INCR, %g5
setx L2_ERROR_ADDRESS_REG, %g4, %g5
sllx %g3, L2_BANK_SHIFT, %g4
stx %g0, [%g4] ! clear L2 EAR
* Store DRAM ESR/EAR/ND for the bank in error into the DIAG_BUF
set (NO_DRAM_BANKS - 1), %g3
! skip banks which are disabled. causes hang.
SKIP_DISABLED_DRAM_BANK(%g3, %g4, %g5, 4f)
setx DRAM_ESR_BASE, %g4, %g5
sllx %g3, DRAM_BANK_SHIFT, %g2
brz,pt %g4, 4f ! no error on this bank
stx %g4, [%g2] ! clear DRAM ESR RW1C
stx %g0, [%g2] ! clear DRAM ESR RW
add %g1, ERR_DIAG_BUF_DRAM_ESR, %g2
mulx %g3, ERR_DIAG_BUF_DRAM_ESR_INCR, %g5
add %g1, ERR_DIAG_BUF_DRAM_EAR, %g2
mulx %g3, ERR_DIAG_BUF_DRAM_EAR_INCR, %g5
setx DRAM_EAR_BASE, %g4, %g5
sllx %g3, DRAM_BANK_SHIFT, %g4
stx %g0, [%g4] ! clear DRAM EAR register
stx %g0, [%g4] ! and again for erratum 116
add %g1, ERR_DIAG_BUF_DRAM_LOC, %g2
mulx %g3, ERR_DIAG_BUF_DRAM_LOC_INCR, %g5
setx DRAM_ELR_BASE, %g4, %g5
sllx %g3, DRAM_BANK_SHIFT, %g4
stx %g0, [%g4] ! clear DRAM LOC register
stx %g0, [%g4] ! and again for erratum 116
add %g1, ERR_DIAG_BUF_DRAM_CTR, %g2
mulx %g3, ERR_DIAG_BUF_DRAM_CTR_INCR, %g5
setx DRAM_ECR_BASE, %g4, %g5
sllx %g3, DRAM_BANK_SHIFT, %g4
stx %g0, [%g4] ! clear DRAM COUNTER register
stx %g0, [%g4] ! and again for erratum 116
add %g1, ERR_DIAG_BUF_DRAM_FBD, %g2
mulx %g3, ERR_DIAG_BUF_DRAM_FBD_INCR, %g5
setx DRAM_FBD_BASE, %g4, %g5
sllx %g3, DRAM_BANK_SHIFT, %g4
stx %g0, [%g4] ! clear FBD syndrome register
stx %g0, [%g4] ! and again for erratum 116
add %g1, ERR_DIAG_BUF_DRAM_RETRY, %g2
mulx %g3, ERR_DIAG_BUF_DRAM_RETRY_INCR, %g5
setx DRAM_RETRY_BASE, %g4, %g5
sllx %g3, DRAM_BANK_SHIFT, %g4
stx %g0, [%g4] ! clear DRAM error retry register
stx %g0, [%g4] ! and again for erratum 116
add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1
add %g1, ERR_DIAG_DATA_TRAP_REGS, %g1
! %g1 diag-buf->diag_data.err_trap_regs
mulx %g3, ERR_TRAP_REGS_SIZE, %g4
add %g4, %g1, %g4 ! %g4 diag_buf->diag_data.err_trap_regs[TL]
stx %g5, [%g4 + ERR_TNPC]
stx %g5, [%g4 + ERR_TSTATE]
stx %g5, [%g4 + ERR_HTSTATE]
! restore original trap level
* Algorithm to correct (if possible) FRFC errors:
* - use DSFAR[5:1] to determine the suspect double
* floating reg (%f0 - %f62), and read reg data
* - use DSFAR[5:1] to get the ecc bits
* - use data bits[63:32] and ECC bits [13:7] to
* calculate syndrome for even single reg.
* - if syndrome indicates data CE then
* correct and store back.
* - if syndrome is UE then unrecoverable.
* - use data bits[31:0] and ECC bits [6:0] to calculate
* syndrome for odd reg. Take same actions as for even reg.
* - if neither indicate UE then write back into double
* STRAND_FP_TMP1 - data from fpreg
! make sure FPRS.FEF is set
STRAND_PUSH(%g1, %g2, %g3)
* Get the FRF Index and use it to calculate which
* freg to read by working out offset into table
! clear DSFAR[0], use only even numbered double FP registers
and %g5, DSFAR_FRF_DBL_REG_MASK, %g5
! get start address of table below
std %f0, [%g2 + STRAND_FP_TMP1]
std %f2, [%g2 + STRAND_FP_TMP1]
std %f4, [%g2 + STRAND_FP_TMP1]
std %f6, [%g2 + STRAND_FP_TMP1]
std %f8, [%g2 + STRAND_FP_TMP1]
std %f10, [%g2 + STRAND_FP_TMP1]
std %f12, [%g2 + STRAND_FP_TMP1]
std %f14, [%g2 + STRAND_FP_TMP1]
std %f16, [%g2 + STRAND_FP_TMP1]
std %f18, [%g2 + STRAND_FP_TMP1]
std %f20, [%g2 + STRAND_FP_TMP1]
std %f22, [%g2 + STRAND_FP_TMP1]
std %f24, [%g2 + STRAND_FP_TMP1]
std %f26, [%g2 + STRAND_FP_TMP1]
std %f28, [%g2 + STRAND_FP_TMP1]
std %f30, [%g2 + STRAND_FP_TMP1]
std %f32, [%g2 + STRAND_FP_TMP1]
std %f34, [%g2 + STRAND_FP_TMP1]
std %f36, [%g2 + STRAND_FP_TMP1]
std %f38, [%g2 + STRAND_FP_TMP1]
std %f40, [%g2 + STRAND_FP_TMP1]
std %f42, [%g2 + STRAND_FP_TMP1]
std %f44, [%g2 + STRAND_FP_TMP1]
std %f46, [%g2 + STRAND_FP_TMP1]
std %f48, [%g2 + STRAND_FP_TMP1]
std %f50, [%g2 + STRAND_FP_TMP1]
std %f52, [%g2 + STRAND_FP_TMP1]
std %f54, [%g2 + STRAND_FP_TMP1]
std %f56, [%g2 + STRAND_FP_TMP1]
std %f58, [%g2 + STRAND_FP_TMP1]
std %f60, [%g2 + STRAND_FP_TMP1]
std %f62, [%g2 + STRAND_FP_TMP1]
DISABLE_PSCCE(%g1, %g2, %g3)
ENABLE_PSCCE(%g1, %g3, %g4)
! FP register in error in STRAND_FP_TMP1
* Get the ECC data for the freg.
* %g5 - D-SFAR[5:1] already shifted to VA[7:3] above for
ldxa [%g5]ASI_FRF_ECC_REG, %g4
stx %g4, [%g2 + STRAND_FP_TMP2]
! FP register in error in STRAND_FP_TMP1
! FP register ECC in error in STRAND_FP_TMP2
* Calculate syndrome for 'even' single reg first.
lduw [%g2 + STRAND_FP_TMP1], %g1
GEN_FRF_CHECK(%g1, %g2, %g3, %g4, %g5, %g6)
ldx [%g3 + STRAND_FP_TMP2], %g1 ! ecc
srlx %g1, ASI_FRF_ECC_EVEN_SHIFT, %g1 ! even ecc
xor %g1, %g2, %g5 ! calculate syndrome
and %g5, FRF_SYND5_MASK, %g5 ! %g5 - synd{5:0}
* synd{6} is parity over data and ecc and
* is calculated separately from synd{5:0}
lduw [%g3 + STRAND_FP_TMP1], %g1 ! even data
ldx [%g3 + STRAND_FP_TMP2], %g2 ! ecc
srlx %g2, ASI_FRF_ECC_EVEN_SHIFT, %g2 ! even ecc
* Merge the separate syndrome bits together to get
sllx %g4, FRF_SYND6_SHIFT, %g4
or %g5, %g4, %g5 ! g5 - synd{6:0}
* FRF errors use the same syndrome table as L2 cache data
setx l2_ecc_syndrome_table, %g2, %g3
sub %g3, %g2, %g3 ! %g3 - ecc syndrome table
mulx %g5, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g5
ldub [%g6], %g5 ! %g5 - decoded ECC syndrome
* Now check error type and correct if possible.
be,pn %xcc, convert_frfc_to_frfu
be,pn %xcc, convert_frfc_to_frfu
! NotData/Triple or worse
be,pn %xcc, convert_frfc_to_frfu
! Check bit error (will be corrected by HW)
* Only reach this point if we are dealing with a
* data bit error, so now correct it.
lduw [%g2 + STRAND_FP_TMP1], %g1 ! even data
xor %g1, %g5, %g1 ! correct bit
stw %g1, [%g2 + STRAND_FP_TMP1]
* Now calculate syndrome for 'odd' single reg
lduw [%g2 + STRAND_FP_TMP1 + 4], %g1
GEN_FRF_CHECK(%g1, %g2, %g3, %g4, %g5, %g6)
ldx [%g3 + STRAND_FP_TMP2], %g1 ! ecc
and %g1, ASI_FRF_ECC_ODD_MASK, %g1 ! odd ecc
xor %g1, %g2, %g5 ! calculate syndrome
and %g5, FRF_SYND5_MASK, %g5 ! %g5 - synd{5:0}
* synd{6} is parity over data and ecc
* and is calculated separately from synd{5:0}
lduw [%g3 + STRAND_FP_TMP1 + 4], %g1 ! odd data
ldx [%g3 + STRAND_FP_TMP2], %g2 ! ecc
and %g2, ASI_FRF_ECC_ODD_MASK, %g2 ! odd ecc
* Merge the separate syndrome bits together to get
sllx %g6, FRF_SYND6_SHIFT, %g6
or %g5, %g6, %g5 ! %g5 - synd{6:0}
* FRF errors use the same syndrome table as L2 cache data
setx l2_ecc_syndrome_table, %g2, %g3
sub %g3, %g2, %g3 ! %g3 - ecc syndrome table
mulx %g5, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g5
ldub [%g6], %g5 ! %g5 - decoded ECC syndrome
* Now check error type and correct if possible.
be,pn %xcc, convert_frfc_to_frfu
be,pn %xcc, convert_frfc_to_frfu
! NotData/Triple or worse
be,pn %xcc, convert_frfc_to_frfu
! Check bit error (will be corrected by HW)
* Only reach this point if we are dealing with a
* data bit error, so now correct it.
lduw [%g2 + STRAND_FP_TMP1 + 4], %g1 ! odd data
stw %g1, [%g2 + STRAND_FP_TMP1 + 4] ! store corrected data
* We know that FRFU is (FRFC entry - 1) so get the
* error table entry and move it back to the FRFU entry
* Write back the corrected data. Note that if it was a check
* bit which was in error this will be automatically fixed
* by HW during the writeback.
* Get the FRF Index and use it to calculate which
* freg to write by working out offset into table
and %g5, DSFAR_FRF_DBL_REG_MASK, %g5
sllx %g5, 2, %g5 ! each table entry 2 instr in size
ldd [%g2 + STRAND_FP_TMP1], %f0
ldd [%g2 + STRAND_FP_TMP1], %f2
ldd [%g2 + STRAND_FP_TMP1], %f4
ldd [%g2 + STRAND_FP_TMP1], %f6
ldd [%g2 + STRAND_FP_TMP1], %f8
ldd [%g2 + STRAND_FP_TMP1], %f10
ldd [%g2 + STRAND_FP_TMP1], %f12
ldd [%g2 + STRAND_FP_TMP1], %f14
ldd [%g2 + STRAND_FP_TMP1], %f16
ldd [%g2 + STRAND_FP_TMP1], %f18
ldd [%g2 + STRAND_FP_TMP1], %f20
ldd [%g2 + STRAND_FP_TMP1], %f22
ldd [%g2 + STRAND_FP_TMP1], %f24
ldd [%g2 + STRAND_FP_TMP1], %f26
ldd [%g2 + STRAND_FP_TMP1], %f28
ldd [%g2 + STRAND_FP_TMP1], %f30
ldd [%g2 + STRAND_FP_TMP1], %f32
ldd [%g2 + STRAND_FP_TMP1], %f34
ldd [%g2 + STRAND_FP_TMP1], %f36
ldd [%g2 + STRAND_FP_TMP1], %f38
ldd [%g2 + STRAND_FP_TMP1], %f40
ldd [%g2 + STRAND_FP_TMP1], %f42
ldd [%g2 + STRAND_FP_TMP1], %f44
ldd [%g2 + STRAND_FP_TMP1], %f46
ldd [%g2 + STRAND_FP_TMP1], %f48
ldd [%g2 + STRAND_FP_TMP1], %f50
ldd [%g2 + STRAND_FP_TMP1], %f52
ldd [%g2 + STRAND_FP_TMP1], %f54
ldd [%g2 + STRAND_FP_TMP1], %f56
ldd [%g2 + STRAND_FP_TMP1], %f58
ldd [%g2 + STRAND_FP_TMP1], %f60
ldd [%g2 + STRAND_FP_TMP1], %f62
DISABLE_PSCCE(%g1, %g2, %g3)
ENABLE_PSCCE(%g1, %g2, %g3)