1*4882a593Smuzhiyun/* SPDX-License-Identifier: GPL-2.0-or-later */ 2*4882a593Smuzhiyun/* 3*4882a593Smuzhiyun * Copyright (C) 2001,2002,2003 Broadcom Corporation 4*4882a593Smuzhiyun */ 5*4882a593Smuzhiyun 6*4882a593Smuzhiyun#include <asm/asm.h> 7*4882a593Smuzhiyun#include <asm/regdef.h> 8*4882a593Smuzhiyun#include <asm/mipsregs.h> 9*4882a593Smuzhiyun#include <asm/stackframe.h> 10*4882a593Smuzhiyun#include <asm/cacheops.h> 11*4882a593Smuzhiyun#include <asm/sibyte/board.h> 12*4882a593Smuzhiyun 13*4882a593Smuzhiyun#define C0_ERRCTL $26 /* CP0: Error info */ 14*4882a593Smuzhiyun#define C0_CERR_I $27 /* CP0: Icache error */ 15*4882a593Smuzhiyun#define C0_CERR_D $27,1 /* CP0: Dcache error */ 16*4882a593Smuzhiyun 17*4882a593Smuzhiyun /* 18*4882a593Smuzhiyun * Based on SiByte sample software cache-err/cerr.S 19*4882a593Smuzhiyun * CVS revision 1.8. Only the 'unrecoverable' case 20*4882a593Smuzhiyun * is changed. 21*4882a593Smuzhiyun */ 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun .set mips64 24*4882a593Smuzhiyun .set noreorder 25*4882a593Smuzhiyun .set noat 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun /* 28*4882a593Smuzhiyun * sb1_cerr_vec: code to be copied to the Cache Error 29*4882a593Smuzhiyun * Exception vector. The code must be pushed out to memory 30*4882a593Smuzhiyun * (either by copying to Kseg0 and Kseg1 both, or by flushing 31*4882a593Smuzhiyun * the L1 and L2) since it is fetched as 0xa0000100. 32*4882a593Smuzhiyun * 33*4882a593Smuzhiyun * NOTE: Be sure this handler is at most 28 instructions long 34*4882a593Smuzhiyun * since the final 16 bytes of the exception vector memory 35*4882a593Smuzhiyun * (0x170-0x17f) are used to preserve k0, k1, and ra. 36*4882a593Smuzhiyun */ 37*4882a593Smuzhiyun 38*4882a593SmuzhiyunLEAF(except_vec2_sb1) 39*4882a593Smuzhiyun /* 40*4882a593Smuzhiyun * If this error is recoverable, we need to exit the handler 41*4882a593Smuzhiyun * without having dirtied any registers. To do this, 42*4882a593Smuzhiyun * save/restore k0 and k1 from low memory (Useg is direct 43*4882a593Smuzhiyun * mapped while ERL=1). Note that we can't save to a 44*4882a593Smuzhiyun * CPU-specific location without ruining a register in the 45*4882a593Smuzhiyun * process. This means we are vulnerable to data corruption 46*4882a593Smuzhiyun * whenever the handler is reentered by a second CPU. 47*4882a593Smuzhiyun */ 48*4882a593Smuzhiyun sd k0,0x170($0) 49*4882a593Smuzhiyun sd k1,0x178($0) 50*4882a593Smuzhiyun 51*4882a593Smuzhiyun#ifdef CONFIG_SB1_CEX_ALWAYS_FATAL 52*4882a593Smuzhiyun j handle_vec2_sb1 53*4882a593Smuzhiyun nop 54*4882a593Smuzhiyun#else 55*4882a593Smuzhiyun /* 56*4882a593Smuzhiyun * M_ERRCTL_RECOVERABLE is bit 31, which makes it easy to tell 57*4882a593Smuzhiyun * if we can fast-path out of here for a h/w-recovered error. 58*4882a593Smuzhiyun */ 59*4882a593Smuzhiyun mfc0 k1,C0_ERRCTL 60*4882a593Smuzhiyun bgtz k1,attempt_recovery 61*4882a593Smuzhiyun sll k0,k1,1 62*4882a593Smuzhiyun 63*4882a593Smuzhiyunrecovered_dcache: 64*4882a593Smuzhiyun /* 65*4882a593Smuzhiyun * Unlock CacheErr-D (which in turn unlocks CacheErr-DPA). 66*4882a593Smuzhiyun * Ought to log the occurrence of this recovered dcache error. 67*4882a593Smuzhiyun */ 68*4882a593Smuzhiyun b recovered 69*4882a593Smuzhiyun mtc0 $0,C0_CERR_D 70*4882a593Smuzhiyun 71*4882a593Smuzhiyunattempt_recovery: 72*4882a593Smuzhiyun /* 73*4882a593Smuzhiyun * k0 has C0_ERRCTL << 1, which puts 'DC' at bit 31. Any 74*4882a593Smuzhiyun * Dcache errors we can recover from will take more extensive 75*4882a593Smuzhiyun * processing. For now, they are considered "unrecoverable". 76*4882a593Smuzhiyun * Note that 'DC' becoming set (outside of ERL mode) will 77*4882a593Smuzhiyun * cause 'IC' to clear; so if there's an Icache error, we'll 78*4882a593Smuzhiyun * only find out about it if we recover from this error and 79*4882a593Smuzhiyun * continue executing. 80*4882a593Smuzhiyun */ 81*4882a593Smuzhiyun bltz k0,unrecoverable 82*4882a593Smuzhiyun sll k0,1 83*4882a593Smuzhiyun 84*4882a593Smuzhiyun /* 85*4882a593Smuzhiyun * k0 has C0_ERRCTL << 2, which puts 'IC' at bit 31. If an 86*4882a593Smuzhiyun * Icache error isn't indicated, I'm not sure why we got here. 87*4882a593Smuzhiyun * Consider that case "unrecoverable" for now. 88*4882a593Smuzhiyun */ 89*4882a593Smuzhiyun bgez k0,unrecoverable 90*4882a593Smuzhiyun 91*4882a593Smuzhiyunattempt_icache_recovery: 92*4882a593Smuzhiyun /* 93*4882a593Smuzhiyun * External icache errors are due to uncorrectable ECC errors 94*4882a593Smuzhiyun * in the L2 cache or Memory Controller and cannot be 95*4882a593Smuzhiyun * recovered here. 96*4882a593Smuzhiyun */ 97*4882a593Smuzhiyun mfc0 k0,C0_CERR_I /* delay slot */ 98*4882a593Smuzhiyun li k1,1 << 26 /* ICACHE_EXTERNAL */ 99*4882a593Smuzhiyun and k1,k0 100*4882a593Smuzhiyun bnez k1,unrecoverable 101*4882a593Smuzhiyun andi k0,0x1fe0 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun /* 104*4882a593Smuzhiyun * Since the error is internal, the 'IDX' field from 105*4882a593Smuzhiyun * CacheErr-I is valid and we can just invalidate all blocks 106*4882a593Smuzhiyun * in that set. 107*4882a593Smuzhiyun */ 108*4882a593Smuzhiyun cache Index_Invalidate_I,(0<<13)(k0) 109*4882a593Smuzhiyun cache Index_Invalidate_I,(1<<13)(k0) 110*4882a593Smuzhiyun cache Index_Invalidate_I,(2<<13)(k0) 111*4882a593Smuzhiyun cache Index_Invalidate_I,(3<<13)(k0) 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun /* Ought to log this recovered icache error */ 114*4882a593Smuzhiyun 115*4882a593Smuzhiyunrecovered: 116*4882a593Smuzhiyun /* Restore the saved registers */ 117*4882a593Smuzhiyun ld k0,0x170($0) 118*4882a593Smuzhiyun ld k1,0x178($0) 119*4882a593Smuzhiyun eret 120*4882a593Smuzhiyun 121*4882a593Smuzhiyununrecoverable: 122*4882a593Smuzhiyun /* Unrecoverable Icache or Dcache error; log it and/or fail */ 123*4882a593Smuzhiyun j handle_vec2_sb1 124*4882a593Smuzhiyun nop 125*4882a593Smuzhiyun#endif 126*4882a593Smuzhiyun 127*4882a593SmuzhiyunEND(except_vec2_sb1) 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun LEAF(handle_vec2_sb1) 130*4882a593Smuzhiyun mfc0 k0,CP0_CONFIG 131*4882a593Smuzhiyun li k1,~CONF_CM_CMASK 132*4882a593Smuzhiyun and k0,k0,k1 133*4882a593Smuzhiyun ori k0,k0,CONF_CM_UNCACHED 134*4882a593Smuzhiyun mtc0 k0,CP0_CONFIG 135*4882a593Smuzhiyun 136*4882a593Smuzhiyun SSNOP 137*4882a593Smuzhiyun SSNOP 138*4882a593Smuzhiyun SSNOP 139*4882a593Smuzhiyun SSNOP 140*4882a593Smuzhiyun bnezl $0, 1f 141*4882a593Smuzhiyun1: 142*4882a593Smuzhiyun mfc0 k0, CP0_STATUS 143*4882a593Smuzhiyun sll k0, k0, 3 # check CU0 (kernel?) 144*4882a593Smuzhiyun bltz k0, 2f 145*4882a593Smuzhiyun nop 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun /* Get a valid Kseg0 stack pointer. Any task's stack pointer 148*4882a593Smuzhiyun * will do, although if we ever want to resume execution we 149*4882a593Smuzhiyun * better not have corrupted any state. */ 150*4882a593Smuzhiyun get_saved_sp 151*4882a593Smuzhiyun move sp, k1 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun2: 154*4882a593Smuzhiyun j sb1_cache_error 155*4882a593Smuzhiyun nop 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun END(handle_vec2_sb1) 158