cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cerr-sb1.c (15858B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2001,2002,2003 Broadcom Corporation
      4 */
      5#include <linux/sched.h>
      6#include <asm/mipsregs.h>
      7#include <asm/sibyte/sb1250.h>
      8#include <asm/sibyte/sb1250_regs.h>
      9
     10#if !defined(CONFIG_SIBYTE_BUS_WATCHER) || defined(CONFIG_SIBYTE_BW_TRACE)
     11#include <asm/io.h>
     12#include <asm/sibyte/sb1250_scd.h>
     13#endif
     14
     15/*
     16 * We'd like to dump the L2_ECC_TAG register on errors, but errata make
     17 * that unsafe... So for now we don't.	(BCM1250/BCM112x erratum SOC-48.)
     18 */
     19#undef DUMP_L2_ECC_TAG_ON_ERROR
     20
     21/* SB1 definitions */
     22
     23/* XXX should come from config1 XXX */
     24#define SB1_CACHE_INDEX_MASK   0x1fe0
     25
     26#define CP0_ERRCTL_RECOVERABLE (1 << 31)
     27#define CP0_ERRCTL_DCACHE      (1 << 30)
     28#define CP0_ERRCTL_ICACHE      (1 << 29)
     29#define CP0_ERRCTL_MULTIBUS    (1 << 23)
     30#define CP0_ERRCTL_MC_TLB      (1 << 15)
     31#define CP0_ERRCTL_MC_TIMEOUT  (1 << 14)
     32
     33#define CP0_CERRI_TAG_PARITY   (1 << 29)
     34#define CP0_CERRI_DATA_PARITY  (1 << 28)
     35#define CP0_CERRI_EXTERNAL     (1 << 26)
     36
     37#define CP0_CERRI_IDX_VALID(c) (!((c) & CP0_CERRI_EXTERNAL))
     38#define CP0_CERRI_DATA	       (CP0_CERRI_DATA_PARITY)
     39
     40#define CP0_CERRD_MULTIPLE     (1 << 31)
     41#define CP0_CERRD_TAG_STATE    (1 << 30)
     42#define CP0_CERRD_TAG_ADDRESS  (1 << 29)
     43#define CP0_CERRD_DATA_SBE     (1 << 28)
     44#define CP0_CERRD_DATA_DBE     (1 << 27)
     45#define CP0_CERRD_EXTERNAL     (1 << 26)
     46#define CP0_CERRD_LOAD	       (1 << 25)
     47#define CP0_CERRD_STORE	       (1 << 24)
     48#define CP0_CERRD_FILLWB       (1 << 23)
     49#define CP0_CERRD_COHERENCY    (1 << 22)
     50#define CP0_CERRD_DUPTAG       (1 << 21)
     51
     52#define CP0_CERRD_DPA_VALID(c) (!((c) & CP0_CERRD_EXTERNAL))
     53#define CP0_CERRD_IDX_VALID(c) \
     54   (((c) & (CP0_CERRD_LOAD | CP0_CERRD_STORE)) ? (!((c) & CP0_CERRD_EXTERNAL)) : 0)
     55#define CP0_CERRD_CAUSES \
     56   (CP0_CERRD_LOAD | CP0_CERRD_STORE | CP0_CERRD_FILLWB | CP0_CERRD_COHERENCY | CP0_CERRD_DUPTAG)
     57#define CP0_CERRD_TYPES \
     58   (CP0_CERRD_TAG_STATE | CP0_CERRD_TAG_ADDRESS | CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE | CP0_CERRD_EXTERNAL)
     59#define CP0_CERRD_DATA	       (CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE)
     60
     61static uint32_t extract_ic(unsigned short addr, int data);
     62static uint32_t extract_dc(unsigned short addr, int data);
     63
     64static inline void breakout_errctl(unsigned int val)
     65{
     66	if (val & CP0_ERRCTL_RECOVERABLE)
     67		printk(" recoverable");
     68	if (val & CP0_ERRCTL_DCACHE)
     69		printk(" dcache");
     70	if (val & CP0_ERRCTL_ICACHE)
     71		printk(" icache");
     72	if (val & CP0_ERRCTL_MULTIBUS)
     73		printk(" multiple-buserr");
     74	printk("\n");
     75}
     76
     77static inline void breakout_cerri(unsigned int val)
     78{
     79	if (val & CP0_CERRI_TAG_PARITY)
     80		printk(" tag-parity");
     81	if (val & CP0_CERRI_DATA_PARITY)
     82		printk(" data-parity");
     83	if (val & CP0_CERRI_EXTERNAL)
     84		printk(" external");
     85	printk("\n");
     86}
     87
     88static inline void breakout_cerrd(unsigned int val)
     89{
     90	switch (val & CP0_CERRD_CAUSES) {
     91	case CP0_CERRD_LOAD:
     92		printk(" load,");
     93		break;
     94	case CP0_CERRD_STORE:
     95		printk(" store,");
     96		break;
     97	case CP0_CERRD_FILLWB:
     98		printk(" fill/wb,");
     99		break;
    100	case CP0_CERRD_COHERENCY:
    101		printk(" coherency,");
    102		break;
    103	case CP0_CERRD_DUPTAG:
    104		printk(" duptags,");
    105		break;
    106	default:
    107		printk(" NO CAUSE,");
    108		break;
    109	}
    110	if (!(val & CP0_CERRD_TYPES))
    111		printk(" NO TYPE");
    112	else {
    113		if (val & CP0_CERRD_MULTIPLE)
    114			printk(" multi-err");
    115		if (val & CP0_CERRD_TAG_STATE)
    116			printk(" tag-state");
    117		if (val & CP0_CERRD_TAG_ADDRESS)
    118			printk(" tag-address");
    119		if (val & CP0_CERRD_DATA_SBE)
    120			printk(" data-SBE");
    121		if (val & CP0_CERRD_DATA_DBE)
    122			printk(" data-DBE");
    123		if (val & CP0_CERRD_EXTERNAL)
    124			printk(" external");
    125	}
    126	printk("\n");
    127}
    128
    129#ifndef CONFIG_SIBYTE_BUS_WATCHER
    130
    131static void check_bus_watcher(void)
    132{
    133	uint32_t status, l2_err, memio_err;
    134#ifdef DUMP_L2_ECC_TAG_ON_ERROR
    135	uint64_t l2_tag;
    136#endif
    137
    138	/* Destructive read, clears register and interrupt */
    139	status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
    140	/* Bit 31 is always on, but there's no #define for that */
    141	if (status & ~(1UL << 31)) {
    142		l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
    143#ifdef DUMP_L2_ECC_TAG_ON_ERROR
    144		l2_tag = in64(IOADDR(A_L2_ECC_TAG));
    145#endif
    146		memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
    147		printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
    148		printk("\nLast recorded signature:\n");
    149		printk("Request %02x from %d, answered by %d with Dcode %d\n",
    150		       (unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
    151		       (int)(G_SCD_BERR_TID(status) >> 6),
    152		       (int)G_SCD_BERR_RID(status),
    153		       (int)G_SCD_BERR_DCODE(status));
    154#ifdef DUMP_L2_ECC_TAG_ON_ERROR
    155		printk("Last L2 tag w/ bad ECC: %016llx\n", l2_tag);
    156#endif
    157	} else {
    158		printk("Bus watcher indicates no error\n");
    159	}
    160}
    161#else
    162extern void check_bus_watcher(void);
    163#endif
    164
    165asmlinkage void sb1_cache_error(void)
    166{
    167	uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res;
    168	unsigned long long cerr_dpa;
    169
    170#ifdef CONFIG_SIBYTE_BW_TRACE
    171	/* Freeze the trace buffer now */
    172	csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
    173	printk("Trace buffer frozen\n");
    174#endif
    175
    176	printk("Cache error exception on CPU %x:\n",
    177	       (read_c0_prid() >> 25) & 0x7);
    178
    179	__asm__ __volatile__ (
    180	"	.set	push\n\t"
    181	"	.set	mips64\n\t"
    182	"	.set	noat\n\t"
    183	"	mfc0	%0, $26\n\t"
    184	"	mfc0	%1, $27\n\t"
    185	"	mfc0	%2, $27, 1\n\t"
    186	"	dmfc0	$1, $27, 3\n\t"
    187	"	dsrl32	%3, $1, 0 \n\t"
    188	"	sll	%4, $1, 0 \n\t"
    189	"	mfc0	%5, $30\n\t"
    190	"	.set	pop"
    191	: "=r" (errctl), "=r" (cerr_i), "=r" (cerr_d),
    192	  "=r" (dpahi), "=r" (dpalo), "=r" (eepc));
    193
    194	cerr_dpa = (((uint64_t)dpahi) << 32) | dpalo;
    195	printk(" c0_errorepc ==	  %08x\n", eepc);
    196	printk(" c0_errctl   ==	  %08x", errctl);
    197	breakout_errctl(errctl);
    198	if (errctl & CP0_ERRCTL_ICACHE) {
    199		printk(" c0_cerr_i   ==	  %08x", cerr_i);
    200		breakout_cerri(cerr_i);
    201		if (CP0_CERRI_IDX_VALID(cerr_i)) {
    202			/* Check index of EPC, allowing for delay slot */
    203			if (((eepc & SB1_CACHE_INDEX_MASK) != (cerr_i & SB1_CACHE_INDEX_MASK)) &&
    204			    ((eepc & SB1_CACHE_INDEX_MASK) != ((cerr_i & SB1_CACHE_INDEX_MASK) - 4)))
    205				printk(" cerr_i idx doesn't match eepc\n");
    206			else {
    207				res = extract_ic(cerr_i & SB1_CACHE_INDEX_MASK,
    208						 (cerr_i & CP0_CERRI_DATA) != 0);
    209				if (!(res & cerr_i))
    210					printk("...didn't see indicated icache problem\n");
    211			}
    212		}
    213	}
    214	if (errctl & CP0_ERRCTL_DCACHE) {
    215		printk(" c0_cerr_d   ==	  %08x", cerr_d);
    216		breakout_cerrd(cerr_d);
    217		if (CP0_CERRD_DPA_VALID(cerr_d)) {
    218			printk(" c0_cerr_dpa == %010llx\n", cerr_dpa);
    219			if (!CP0_CERRD_IDX_VALID(cerr_d)) {
    220				res = extract_dc(cerr_dpa & SB1_CACHE_INDEX_MASK,
    221						 (cerr_d & CP0_CERRD_DATA) != 0);
    222				if (!(res & cerr_d))
    223					printk("...didn't see indicated dcache problem\n");
    224			} else {
    225				if ((cerr_dpa & SB1_CACHE_INDEX_MASK) != (cerr_d & SB1_CACHE_INDEX_MASK))
    226					printk(" cerr_d idx doesn't match cerr_dpa\n");
    227				else {
    228					res = extract_dc(cerr_d & SB1_CACHE_INDEX_MASK,
    229							 (cerr_d & CP0_CERRD_DATA) != 0);
    230					if (!(res & cerr_d))
    231						printk("...didn't see indicated problem\n");
    232				}
    233			}
    234		}
    235	}
    236
    237	check_bus_watcher();
    238
    239	/*
    240	 * Calling panic() when a fatal cache error occurs scrambles the
    241	 * state of the system (and the cache), making it difficult to
    242	 * investigate after the fact.	However, if you just stall the CPU,
    243	 * the other CPU may keep on running, which is typically very
    244	 * undesirable.
    245	 */
    246#ifdef CONFIG_SB1_CERR_STALL
    247	while (1)
    248		;
    249#else
    250	panic("unhandled cache error");
    251#endif
    252}
    253
    254
    255/* Parity lookup table. */
    256static const uint8_t parity[256] = {
    257	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    258	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    259	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    260	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    261	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    262	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    263	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    264	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    265	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    266	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    267	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    268	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    269	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    270	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    271	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    272	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
    273};
    274
    275/* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */
    276static const uint64_t mask_72_64[8] = {
    277	0x0738C808099264FFULL,
    278	0x38C808099264FF07ULL,
    279	0xC808099264FF0738ULL,
    280	0x08099264FF0738C8ULL,
    281	0x099264FF0738C808ULL,
    282	0x9264FF0738C80809ULL,
    283	0x64FF0738C8080992ULL,
    284	0xFF0738C808099264ULL
    285};
    286
    287/* Calculate the parity on a range of bits */
    288static char range_parity(uint64_t dword, int max, int min)
    289{
    290	char parity = 0;
    291	int i;
    292	dword >>= min;
    293	for (i=max-min; i>=0; i--) {
    294		if (dword & 0x1)
    295			parity = !parity;
    296		dword >>= 1;
    297	}
    298	return parity;
    299}
    300
    301/* Calculate the 4-bit even byte-parity for an instruction */
    302static unsigned char inst_parity(uint32_t word)
    303{
    304	int i, j;
    305	char parity = 0;
    306	for (j=0; j<4; j++) {
    307		char byte_parity = 0;
    308		for (i=0; i<8; i++) {
    309			if (word & 0x80000000)
    310				byte_parity = !byte_parity;
    311			word <<= 1;
    312		}
    313		parity <<= 1;
    314		parity |= byte_parity;
    315	}
    316	return parity;
    317}
    318
    319static uint32_t extract_ic(unsigned short addr, int data)
    320{
    321	unsigned short way;
    322	int valid;
    323	uint32_t taghi, taglolo, taglohi;
    324	unsigned long long taglo, va;
    325	uint64_t tlo_tmp;
    326	uint8_t lru;
    327	int res = 0;
    328
    329	printk("Icache index 0x%04x  ", addr);
    330	for (way = 0; way < 4; way++) {
    331		/* Index-load-tag-I */
    332		__asm__ __volatile__ (
    333		"	.set	push		\n\t"
    334		"	.set	noreorder	\n\t"
    335		"	.set	mips64		\n\t"
    336		"	.set	noat		\n\t"
    337		"	cache	4, 0(%3)	\n\t"
    338		"	mfc0	%0, $29		\n\t"
    339		"	dmfc0	$1, $28		\n\t"
    340		"	dsrl32	%1, $1, 0	\n\t"
    341		"	sll	%2, $1, 0	\n\t"
    342		"	.set	pop"
    343		: "=r" (taghi), "=r" (taglohi), "=r" (taglolo)
    344		: "r" ((way << 13) | addr));
    345
    346		taglo = ((unsigned long long)taglohi << 32) | taglolo;
    347		if (way == 0) {
    348			lru = (taghi >> 14) & 0xff;
    349			printk("[Bank %d Set 0x%02x]  LRU > %d %d %d %d > MRU\n",
    350				    ((addr >> 5) & 0x3), /* bank */
    351				    ((addr >> 7) & 0x3f), /* index */
    352				    (lru & 0x3),
    353				    ((lru >> 2) & 0x3),
    354				    ((lru >> 4) & 0x3),
    355				    ((lru >> 6) & 0x3));
    356		}
    357		va = (taglo & 0xC0000FFFFFFFE000ULL) | addr;
    358		if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3))
    359			va |= 0x3FFFF00000000000ULL;
    360		valid = ((taghi >> 29) & 1);
    361		if (valid) {
    362			tlo_tmp = taglo & 0xfff3ff;
    363			if (((taglo >> 10) & 1) ^ range_parity(tlo_tmp, 23, 0)) {
    364				printk("   ** bad parity in VTag0/G/ASID\n");
    365				res |= CP0_CERRI_TAG_PARITY;
    366			}
    367			if (((taglo >> 11) & 1) ^ range_parity(taglo, 63, 24)) {
    368				printk("   ** bad parity in R/VTag1\n");
    369				res |= CP0_CERRI_TAG_PARITY;
    370			}
    371		}
    372		if (valid ^ ((taghi >> 27) & 1)) {
    373			printk("   ** bad parity for valid bit\n");
    374			res |= CP0_CERRI_TAG_PARITY;
    375		}
    376		printk(" %d  [VA %016llx]  [Vld? %d]  raw tags: %08X-%016llX\n",
    377			    way, va, valid, taghi, taglo);
    378
    379		if (data) {
    380			uint32_t datahi, insta, instb;
    381			uint8_t predecode;
    382			int offset;
    383
    384			/* (hit all banks and ways) */
    385			for (offset = 0; offset < 4; offset++) {
    386				/* Index-load-data-I */
    387				__asm__ __volatile__ (
    388				"	.set	push\n\t"
    389				"	.set	noreorder\n\t"
    390				"	.set	mips64\n\t"
    391				"	.set	noat\n\t"
    392				"	cache	6, 0(%3)  \n\t"
    393				"	mfc0	%0, $29, 1\n\t"
    394				"	dmfc0  $1, $28, 1\n\t"
    395				"	dsrl32 %1, $1, 0 \n\t"
    396				"	sll    %2, $1, 0 \n\t"
    397				"	.set	pop	    \n"
    398				: "=r" (datahi), "=r" (insta), "=r" (instb)
    399				: "r" ((way << 13) | addr | (offset << 3)));
    400				predecode = (datahi >> 8) & 0xff;
    401				if (((datahi >> 16) & 1) != (uint32_t)range_parity(predecode, 7, 0)) {
    402					printk("   ** bad parity in predecode\n");
    403					res |= CP0_CERRI_DATA_PARITY;
    404				}
    405				/* XXXKW should/could check predecode bits themselves */
    406				if (((datahi >> 4) & 0xf) ^ inst_parity(insta)) {
    407					printk("   ** bad parity in instruction a\n");
    408					res |= CP0_CERRI_DATA_PARITY;
    409				}
    410				if ((datahi & 0xf) ^ inst_parity(instb)) {
    411					printk("   ** bad parity in instruction b\n");
    412					res |= CP0_CERRI_DATA_PARITY;
    413				}
    414				printk("  %05X-%08X%08X", datahi, insta, instb);
    415			}
    416			printk("\n");
    417		}
    418	}
    419	return res;
    420}
    421
    422/* Compute the ECC for a data doubleword */
    423static uint8_t dc_ecc(uint64_t dword)
    424{
    425	uint64_t t;
    426	uint32_t w;
    427	uint8_t	 p;
    428	int	 i;
    429
    430	p = 0;
    431	for (i = 7; i >= 0; i--)
    432	{
    433		p <<= 1;
    434		t = dword & mask_72_64[i];
    435		w = (uint32_t)(t >> 32);
    436		p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF]
    437		      ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]);
    438		w = (uint32_t)(t & 0xFFFFFFFF);
    439		p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF]
    440		      ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]);
    441	}
    442	return p;
    443}
    444
    445struct dc_state {
    446	unsigned char val;
    447	char *name;
    448};
    449
    450static struct dc_state dc_states[] = {
    451	{ 0x00, "INVALID" },
    452	{ 0x0f, "COH-SHD" },
    453	{ 0x13, "NCO-E-C" },
    454	{ 0x19, "NCO-E-D" },
    455	{ 0x16, "COH-E-C" },
    456	{ 0x1c, "COH-E-D" },
    457	{ 0xff, "*ERROR*" }
    458};
    459
    460#define DC_TAG_VALID(state) \
    461    (((state) == 0x0) || ((state) == 0xf) || ((state) == 0x13) || \
    462     ((state) == 0x19) || ((state) == 0x16) || ((state) == 0x1c))
    463
    464static char *dc_state_str(unsigned char state)
    465{
    466	struct dc_state *dsc = dc_states;
    467	while (dsc->val != 0xff) {
    468		if (dsc->val == state)
    469			break;
    470		dsc++;
    471	}
    472	return dsc->name;
    473}
    474
    475static uint32_t extract_dc(unsigned short addr, int data)
    476{
    477	int valid, way;
    478	unsigned char state;
    479	uint32_t taghi, taglolo, taglohi;
    480	unsigned long long taglo, pa;
    481	uint8_t ecc, lru;
    482	int res = 0;
    483
    484	printk("Dcache index 0x%04x  ", addr);
    485	for (way = 0; way < 4; way++) {
    486		__asm__ __volatile__ (
    487		"	.set	push\n\t"
    488		"	.set	noreorder\n\t"
    489		"	.set	mips64\n\t"
    490		"	.set	noat\n\t"
    491		"	cache	5, 0(%3)\n\t"	/* Index-load-tag-D */
    492		"	mfc0	%0, $29, 2\n\t"
    493		"	dmfc0	$1, $28, 2\n\t"
    494		"	dsrl32	%1, $1, 0\n\t"
    495		"	sll	%2, $1, 0\n\t"
    496		"	.set	pop"
    497		: "=r" (taghi), "=r" (taglohi), "=r" (taglolo)
    498		: "r" ((way << 13) | addr));
    499
    500		taglo = ((unsigned long long)taglohi << 32) | taglolo;
    501		pa = (taglo & 0xFFFFFFE000ULL) | addr;
    502		if (way == 0) {
    503			lru = (taghi >> 14) & 0xff;
    504			printk("[Bank %d Set 0x%02x]  LRU > %d %d %d %d > MRU\n",
    505				    ((addr >> 11) & 0x2) | ((addr >> 5) & 1), /* bank */
    506				    ((addr >> 6) & 0x3f), /* index */
    507				    (lru & 0x3),
    508				    ((lru >> 2) & 0x3),
    509				    ((lru >> 4) & 0x3),
    510				    ((lru >> 6) & 0x3));
    511		}
    512		state = (taghi >> 25) & 0x1f;
    513		valid = DC_TAG_VALID(state);
    514		printk(" %d  [PA %010llx]  [state %s (%02x)]  raw tags: %08X-%016llX\n",
    515			    way, pa, dc_state_str(state), state, taghi, taglo);
    516		if (valid) {
    517			if (((taglo >> 11) & 1) ^ range_parity(taglo, 39, 26)) {
    518				printk("   ** bad parity in PTag1\n");
    519				res |= CP0_CERRD_TAG_ADDRESS;
    520			}
    521			if (((taglo >> 10) & 1) ^ range_parity(taglo, 25, 13)) {
    522				printk("   ** bad parity in PTag0\n");
    523				res |= CP0_CERRD_TAG_ADDRESS;
    524			}
    525		} else {
    526			res |= CP0_CERRD_TAG_STATE;
    527		}
    528
    529		if (data) {
    530			uint32_t datalohi, datalolo, datahi;
    531			unsigned long long datalo;
    532			int offset;
    533			char bad_ecc = 0;
    534
    535			for (offset = 0; offset < 4; offset++) {
    536				/* Index-load-data-D */
    537				__asm__ __volatile__ (
    538				"	.set	push\n\t"
    539				"	.set	noreorder\n\t"
    540				"	.set	mips64\n\t"
    541				"	.set	noat\n\t"
    542				"	cache	7, 0(%3)\n\t" /* Index-load-data-D */
    543				"	mfc0	%0, $29, 3\n\t"
    544				"	dmfc0	$1, $28, 3\n\t"
    545				"	dsrl32	%1, $1, 0 \n\t"
    546				"	sll	%2, $1, 0 \n\t"
    547				"	.set	pop"
    548				: "=r" (datahi), "=r" (datalohi), "=r" (datalolo)
    549				: "r" ((way << 13) | addr | (offset << 3)));
    550				datalo = ((unsigned long long)datalohi << 32) | datalolo;
    551				ecc = dc_ecc(datalo);
    552				if (ecc != datahi) {
    553					int bits;
    554					bad_ecc |= 1 << (3-offset);
    555					ecc ^= datahi;
    556					bits = hweight8(ecc);
    557					res |= (bits == 1) ? CP0_CERRD_DATA_SBE : CP0_CERRD_DATA_DBE;
    558				}
    559				printk("  %02X-%016llX", datahi, datalo);
    560			}
    561			printk("\n");
    562			if (bad_ecc)
    563				printk("  dwords w/ bad ECC: %d %d %d %d\n",
    564				       !!(bad_ecc & 8), !!(bad_ecc & 4),
    565				       !!(bad_ecc & 2), !!(bad_ecc & 1));
    566		}
    567	}
    568	return res;
    569}