xref: /onnv-gate/usr/src/uts/sun4u/os/plat_ecc_unum.c (revision 2840:88acfc39e478)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
5*2840Scarlsonj  * Common Development and Distribution License (the "License").
6*2840Scarlsonj  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel /*
22*2840Scarlsonj  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
231708Sstevel  * Use is subject to license terms.
241708Sstevel  */
251708Sstevel 
261708Sstevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
271708Sstevel 
281708Sstevel #include <sys/plat_ecc_unum.h>
291708Sstevel #include <sys/utsname.h>
301708Sstevel #include <sys/cmn_err.h>
311708Sstevel #include <sys/async.h>
321708Sstevel #include <sys/errno.h>
331708Sstevel #include <sys/fm/protocol.h>
341708Sstevel #include <sys/fm/cpu/UltraSPARC-III.h>
351708Sstevel #include <sys/bl.h>
361708Sstevel #include <sys/taskq.h>
371708Sstevel #include <sys/condvar.h>
381708Sstevel #include <sys/plat_ecc_dimm.h>
391708Sstevel 
401708Sstevel /*
411708Sstevel  * Pointer to platform specific function to initialize a cache of DIMM
421708Sstevel  * serial ids
431708Sstevel  */
441708Sstevel int (*p2init_sid_cache)(void);
451708Sstevel 
461708Sstevel /*
471708Sstevel  * This file contains the common code that is used for parsing
481708Sstevel  * ecc unum data and logging it appropriately as the platform
491708Sstevel  * that calls this code implements.
501708Sstevel  */
511708Sstevel 
521708Sstevel int plat_ecc_dispatch_task(plat_ecc_message_t *);
531708Sstevel static void plat_ecc_send_msg(void *);
541708Sstevel 
551708Sstevel #define	CHECK_UNUM \
561708Sstevel 	if (unum_ptr == NULL) { \
571708Sstevel 		break; \
581708Sstevel 	}
591708Sstevel 
601708Sstevel /*
611708Sstevel  * See plat_ecc_unum.h for the meaning of these variables.
621708Sstevel  */
631708Sstevel int ecc_log_fruid_enable = ECC_FRUID_ENABLE_DEFAULT;
641708Sstevel 
651708Sstevel uint32_t plat_ecc_capability_map_domain = PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT;
661708Sstevel uint32_t plat_ecc_capability_map_sc = PLAT_ECC_CAPABILITY_SC_DEFAULT;
671708Sstevel uint16_t ecc_error2_mailbox_flags = PLAT_ECC_ERROR2_SEND_DEFAULT;
681708Sstevel uint16_t ecc_indictment2_mailbox_flags = PLAT_ECC_SEND_INDICT2_DEFAULT;
691708Sstevel 
701708Sstevel /*
711708Sstevel  * We log all ECC errors using the function that is defined as
721708Sstevel  * plat_send_ecc_mailbox_msg(); We first parse the unum string and
731708Sstevel  * then pass the data to be logged to the plat_send_ecc_mailbox_msg
741708Sstevel  * function for logging. Each platform that uses this code needs to
751708Sstevel  * implement a suitable function for this purpose.
761708Sstevel  */
771708Sstevel void
plat_log_fruid_error(int synd_code,struct async_flt * ecc,char * unum,uint64_t afsr_bit)781708Sstevel plat_log_fruid_error(int synd_code, struct async_flt *ecc, char *unum,
791708Sstevel 	uint64_t afsr_bit)
801708Sstevel {
811708Sstevel 	plat_ecc_error_data_t ecc_error_data;
821708Sstevel 	enum plat_ecc_type ecc_type = PLAT_ECC_UNKNOWN;
831708Sstevel 	int board_num;
841708Sstevel 	int proc_position;
851708Sstevel 	int invalid_unum = 1;
861708Sstevel 
871708Sstevel 	bzero(&ecc_error_data, sizeof (plat_ecc_error_data_t));
881708Sstevel 	ecc_error_data.version = PLAT_ECC_VERSION;
891708Sstevel 
901708Sstevel 	switch (afsr_bit) {
911708Sstevel 	case C_AFSR_CE:
921708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_CE;
931708Sstevel 		break;
941708Sstevel 	case C_AFSR_UE:
951708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_UE;
961708Sstevel 		break;
971708Sstevel 	case C_AFSR_EDC:
981708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EDC;
991708Sstevel 		break;
1001708Sstevel 	case C_AFSR_EDU:
1011708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EDU;
1021708Sstevel 		break;
1031708Sstevel 	case C_AFSR_WDC:
1041708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_WDC;
1051708Sstevel 		break;
1061708Sstevel 	case C_AFSR_WDU:
1071708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_WDU;
1081708Sstevel 		break;
1091708Sstevel 	case C_AFSR_CPC:
1101708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_CPC;
1111708Sstevel 		break;
1121708Sstevel 	case C_AFSR_CPU:
1131708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_CPU;
1141708Sstevel 		break;
1151708Sstevel 	case C_AFSR_UCC:
1161708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_UCC;
1171708Sstevel 		break;
1181708Sstevel 	case C_AFSR_UCU:
1191708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_UCU;
1201708Sstevel 		break;
1211708Sstevel 	case C_AFSR_EMC:
1221708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EMC;
1231708Sstevel 		break;
1241708Sstevel 	case C_AFSR_EMU:
1251708Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EMU;
1261708Sstevel 		break;
1271708Sstevel 	default:
1281708Sstevel 		/*
1291708Sstevel 		 * Do not send messages with unknown error codes, since
1301708Sstevel 		 * the SC will not be able to tell what type of error
1311708Sstevel 		 * occurred.
1321708Sstevel 		 */
1331708Sstevel 		return;
1341708Sstevel 	}
1351708Sstevel 
1361708Sstevel 	ecc_error_data.detecting_proc = ecc->flt_bus_id;
1371708Sstevel 
1381708Sstevel 	if (ecc->flt_in_memory)
1391708Sstevel 		ecc_type = PLAT_ECC_MEMORY;
1401708Sstevel 	else if (ecc->flt_status & ECC_ECACHE)
1411708Sstevel 		ecc_type = PLAT_ECC_ECACHE;
1421708Sstevel 
1431708Sstevel 	switch (ecc_type) {
1441708Sstevel 	case PLAT_ECC_MEMORY: {
1451708Sstevel 		/*
1461708Sstevel 		 * The unum string is expected to be in this form:
1471708Sstevel 		 * "/N0/SB12/P0/B0/D2 J13500, ..."
1481708Sstevel 		 * for serengeti.  As this code is shared with Starcat
1491708Sstevel 		 * if N is missing then it is set to 0.
1501708Sstevel 		 * From that we will extract the bank number, dimm
1511708Sstevel 		 * number, and Jnumber.
1521708Sstevel 		 */
1531708Sstevel 		char *unum_ptr = unum;
1541708Sstevel 		char *jno_ptr = ecc_error_data.Jnumber;
1551708Sstevel 		int i;
1561708Sstevel 
1571708Sstevel 		/*
1581708Sstevel 		 * On Serengeti we expect to find 'N' in the unum string
1591708Sstevel 		 * however, on Starcat 'N' does not appear in the unum string.
1601708Sstevel 		 * We do not want this code to break at this point, so the
1611708Sstevel 		 * unum_ptr is reset to the start of unum string if we fail
1621708Sstevel 		 * to find an 'N'.
1631708Sstevel 		 */
1641708Sstevel 		unum_ptr = strchr(unum_ptr, 'N');
1651708Sstevel 		if (unum_ptr == NULL) {
1661708Sstevel 			ecc_error_data.node_no = 0;
1671708Sstevel 			unum_ptr = unum;
1681708Sstevel 		} else {
1691708Sstevel 			unum_ptr++;
1701708Sstevel 			ecc_error_data.node_no = stoi(&unum_ptr);
1711708Sstevel 		}
1721708Sstevel 
1731708Sstevel 		/*
1741708Sstevel 		 * Now pull out the SB number
1751708Sstevel 		 */
1761708Sstevel 		unum_ptr = strstr(unum_ptr, "SB");
1771708Sstevel 		CHECK_UNUM;
1781708Sstevel 		unum_ptr += 2;
1791708Sstevel 		board_num = stoi(&unum_ptr);
1801708Sstevel 
1811708Sstevel 		/*
1821708Sstevel 		 * Now pull out the Proc position (relative to the board)
1831708Sstevel 		 */
1841708Sstevel 		unum_ptr = strchr(unum_ptr, 'P');
1851708Sstevel 		CHECK_UNUM;
1861708Sstevel 		unum_ptr++;
1871708Sstevel 		proc_position = stoi(&unum_ptr);
1881708Sstevel 
1891708Sstevel 		/*
1901708Sstevel 		 * Using the SB number and Proc position we create a FRU
1911708Sstevel 		 * cpu id.
1921708Sstevel 		 */
1931708Sstevel 		ecc_error_data.proc_num =
1941708Sstevel 			plat_make_fru_cpuid(board_num, 0, proc_position);
1951708Sstevel 
1961708Sstevel 		/*
1971708Sstevel 		 * Now pull out the Memory Bank number
1981708Sstevel 		 */
1991708Sstevel 		unum_ptr = strchr(unum_ptr, 'B');
2001708Sstevel 		CHECK_UNUM;
2011708Sstevel 		unum_ptr++;
2021708Sstevel 		ecc_error_data.bank_no = (stoi(&unum_ptr) & 0x01);
2031708Sstevel 
2041708Sstevel 		/*
2051708Sstevel 		 * Now pull out the Dimm number within the Memory Bank.
2061708Sstevel 		 */
2071708Sstevel 		unum_ptr = strchr(unum_ptr, 'D');
2081708Sstevel 		CHECK_UNUM;
2091708Sstevel 		unum_ptr++;
2101708Sstevel 		ecc_error_data.ecache_dimm_no = (stoi(&unum_ptr) & 0x03);
2111708Sstevel 
2121708Sstevel 		/*
2131708Sstevel 		 * Now pull out the J-number.
2141708Sstevel 		 */
2151708Sstevel 		unum_ptr = strchr(unum_ptr, 'J');
2161708Sstevel 		CHECK_UNUM;
2171708Sstevel 		unum_ptr++;
2181708Sstevel 		for (i = PLAT_ECC_JNUMBER_LENGTH;
2191708Sstevel 		    i > 0 && *unum_ptr >= '0' && *unum_ptr <= '9'; i--)
2201708Sstevel 			*jno_ptr++ = *unum_ptr++;
2211708Sstevel 		*jno_ptr = NULL;
2221708Sstevel 
2231708Sstevel 		/*
2241708Sstevel 		 * If we get here, we can assume the unum is valid
2251708Sstevel 		 */
2261708Sstevel 		invalid_unum = 0;
2271708Sstevel 		break;
2281708Sstevel 	}
2291708Sstevel 	case PLAT_ECC_ECACHE: {
2301708Sstevel 		/*
2311708Sstevel 		 * The unum string is expected to be in this form:
2321708Sstevel 		 * "[/N0/][SB|IO]12/P0/E0 J13500, ..."
2331708Sstevel 		 * for serengeti.  As this code is shared with Starcat
2341708Sstevel 		 * if N is missing then it is set to 0.  IO may only appear
2351708Sstevel 		 * on Starcats.  From that we will extract the bank number,
2361708Sstevel 		 * dimm number, and Jnumber.
2371708Sstevel 		 */
2381708Sstevel 		char *unum_ptr = unum;
2391708Sstevel 		char *jno_ptr = ecc_error_data.Jnumber;
2401708Sstevel 		int is_maxcat = 0;
2411708Sstevel 		int i;
2421708Sstevel 
2431708Sstevel 		/*
2441708Sstevel 		 * On Serengeti we expect to find 'N' in the unum string
2451708Sstevel 		 * however, on Starcat 'N' does not appear in the unum string.
2461708Sstevel 		 * We do not want this code to break at this point, so the
2471708Sstevel 		 * unum_ptr is reset to the start of unum string if we fail
2481708Sstevel 		 * to find an 'N'.
2491708Sstevel 		 */
2501708Sstevel 		unum_ptr = strchr(unum_ptr, 'N');
2511708Sstevel 		if (unum_ptr == NULL) {
2521708Sstevel 			ecc_error_data.node_no = 0;
2531708Sstevel 			unum_ptr = unum;
2541708Sstevel 		} else {
2551708Sstevel 			unum_ptr++;
2561708Sstevel 			ecc_error_data.node_no = stoi(&unum_ptr);
2571708Sstevel 		}
2581708Sstevel 
2591708Sstevel 		/*
2601708Sstevel 		 * Now pull out the SB/IO number
2611708Sstevel 		 */
2621708Sstevel 		unum_ptr = strstr(unum_ptr, "SB");
2631708Sstevel 		if (unum_ptr == NULL) {
2641708Sstevel 
2651708Sstevel 			/*
2661708Sstevel 			 * Since this is an E$ error, it must have occurred on
2671708Sstevel 			 * either a System Board (represented by "SB" in the
2681708Sstevel 			 * unum string) or a Maxcat board ("IO" in the unum
2691708Sstevel 			 * string).  Since we failed the "SB" check, we'll
2701708Sstevel 			 * assume this is a maxcat board.
2711708Sstevel 			 */
2721708Sstevel 			is_maxcat = 1;
2731708Sstevel 			unum_ptr = strstr(unum, "IO");
2741708Sstevel 		}
2751708Sstevel 		CHECK_UNUM;
2761708Sstevel 		unum_ptr += 2;
2771708Sstevel 		board_num = stoi(&unum_ptr);
2781708Sstevel 
2791708Sstevel 		/*
2801708Sstevel 		 * Now pull out the Proc position (relative to the board)
2811708Sstevel 		 */
2821708Sstevel 		unum_ptr = strchr(unum_ptr, 'P');
2831708Sstevel 		CHECK_UNUM;
2841708Sstevel 		unum_ptr++;
2851708Sstevel 		proc_position = stoi(&unum_ptr);
2861708Sstevel 
2871708Sstevel 		/*
2881708Sstevel 		 * Using the SB/IO number, slot 0/1 value (is_maxcat), and
2891708Sstevel 		 * proc position, we create the cpu id.
2901708Sstevel 		 */
2911708Sstevel 		ecc_error_data.proc_num = plat_make_fru_cpuid(board_num,
2921708Sstevel 		    is_maxcat, proc_position);
2931708Sstevel 
2941708Sstevel 		ecc_error_data.bank_no = 0;	/* not used */
2951708Sstevel 
2961708Sstevel 		unum_ptr = strchr(unum_ptr, 'E');
2971708Sstevel 		CHECK_UNUM;
2981708Sstevel 		unum_ptr++;
2991708Sstevel 		ecc_error_data.ecache_dimm_no = (stoi(&unum_ptr) & 0x01);
3001708Sstevel 
3011708Sstevel 		unum_ptr = strchr(unum_ptr, 'J');
3021708Sstevel 		CHECK_UNUM;
3031708Sstevel 		unum_ptr++;
3041708Sstevel 		for (i = PLAT_ECC_JNUMBER_LENGTH;
3051708Sstevel 		    i > 0 && *unum_ptr >= '0' && *unum_ptr <= '9'; i--)
3061708Sstevel 			*jno_ptr++ = *unum_ptr++;
3071708Sstevel 		*jno_ptr = NULL;
3081708Sstevel 
3091708Sstevel 		/*
3101708Sstevel 		 * If we get here, we can assume the unum is valid
3111708Sstevel 		 */
3121708Sstevel 		invalid_unum = 0;
3131708Sstevel 		break;
3141708Sstevel 	}
3151708Sstevel 	default:
3161708Sstevel 		/*
3171708Sstevel 		 * Unknown error
3181708Sstevel 		 */
3191708Sstevel 		break;
3201708Sstevel 	}
3211708Sstevel 
3221708Sstevel 	/*
3231708Sstevel 	 * This is where CHECK_UNUM goes when it finds an error
3241708Sstevel 	 */
3251708Sstevel 
3261708Sstevel 	if (ECC_SYND_DATA_BEGIN <= synd_code &&
3271708Sstevel 	    synd_code < ECC_SYND_ECC_BEGIN) {
3281708Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
3291708Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_DATA;
3301708Sstevel 		ecc_error_data.databit_no = synd_code;
3311708Sstevel 	} else if (ECC_SYND_ECC_BEGIN <= synd_code &&
3321708Sstevel 	    synd_code < ECC_SYND_MTAG_BEGIN) {
3331708Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
3341708Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_ECC;
3351708Sstevel 		ecc_error_data.databit_no = synd_code - ECC_SYND_ECC_BEGIN;
3361708Sstevel 	} else if (ECC_SYND_MTAG_BEGIN <= synd_code &&
3371708Sstevel 	    synd_code < ECC_SYND_MECC_BEGIN) {
3381708Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
3391708Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_MTAG_D;
3401708Sstevel 		ecc_error_data.databit_no = synd_code - ECC_SYND_MTAG_BEGIN;
3411708Sstevel 	} else if (ECC_SYND_MECC_BEGIN <= synd_code &&
3421708Sstevel 	    synd_code < ECC_SYND_M2) {
3431708Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
3441708Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_MTAG_E;
3451708Sstevel 		ecc_error_data.databit_no = synd_code - ECC_SYND_MECC_BEGIN;
3461708Sstevel 	} else {
3471708Sstevel 		switch (synd_code) {
3481708Sstevel 		case ECC_SYND_M2:
3491708Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M2;
3501708Sstevel 			break;
3511708Sstevel 		case ECC_SYND_M3:
3521708Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M3;
3531708Sstevel 			break;
3541708Sstevel 		case ECC_SYND_M4:
3551708Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M4;
3561708Sstevel 			break;
3571708Sstevel 		case ECC_SYND_M:
3581708Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M;
3591708Sstevel 			break;
3601708Sstevel 		default:
3611708Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_UNK;
3621708Sstevel 			break;
3631708Sstevel 		}
3641708Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_MULTI;
3651708Sstevel 		ecc_error_data.databit_no = 0; /* not used */
3661708Sstevel 	}
3671708Sstevel 
3681708Sstevel #ifdef DEBUG
3691708Sstevel 	if (invalid_unum &&
3701708Sstevel 	    (ecc_error_data.error_code != PLAT_ERROR_CODE_UE) &&
3711708Sstevel 	    unum && *unum)
3721708Sstevel 		cmn_err(CE_WARN, "Unexpected unum string format: %s\n", unum);
3731708Sstevel #endif
3741708Sstevel 
3751708Sstevel 	/*
3761708Sstevel 	 * Send this data off as a mailbox message to the SC.
3771708Sstevel 	 */
3781708Sstevel 	(void) plat_send_ecc_mailbox_msg(PLAT_ECC_ERROR_MESSAGE,
3791708Sstevel 	    &ecc_error_data);
3801708Sstevel }
3811708Sstevel 
3821708Sstevel /*
3831708Sstevel  * The unum string for memory is expected to be in this form:
3841708Sstevel  * "[/N0/]SB12/P0/B0/D2 [J13500]"
3851708Sstevel  * Or if the unum was generated as the result of a UE:
3861708Sstevel  * "[/N0/]SB12/P0/B0 [J13500, ...]"
3871708Sstevel  * From that we will extract the board number, processor position,
3881708Sstevel  * bank number and jnumber.
3891708Sstevel  *
3901708Sstevel  * Return (1) for an invalid unum string.  If the unum is for an
3911708Sstevel  * individual DIMM and there is no jnumber, jnumber will be set
3921708Sstevel  * to -1 and the caller can decide if the unum is valid.  This
3931708Sstevel  * is because Serengeti does not have jnumbers for bank unums
3941708Sstevel  * which may be used to create DIMM unums (e.g. for acquiring
3951708Sstevel  * DIMM serial ids).
3961708Sstevel  */
3971708Sstevel 
3981708Sstevel int
parse_unum_memory(char * unum,int * board,int * pos,int * bank,int * dimm,int * jnumber)3991708Sstevel parse_unum_memory(char *unum, int *board, int *pos, int *bank, int *dimm,
4001708Sstevel     int *jnumber)
4011708Sstevel {
4021708Sstevel 	char *c;
4031708Sstevel 
4041708Sstevel 	if ((c = strstr(unum, "SB")) == NULL)
4051708Sstevel 		return (1);
4061708Sstevel 	c += 2;
4071708Sstevel 	*board = (uint8_t)stoi(&c);
4081708Sstevel 
4091708Sstevel 	if (*c++ != '/' || *c++ != 'P')
4101708Sstevel 		return (1);
4111708Sstevel 	*pos = stoi(&c);
4121708Sstevel 
4131708Sstevel 	if (*c++ != '/' || *c++ != 'B')
4141708Sstevel 		return (1);
4151708Sstevel 	*bank = stoi(&c);
4161708Sstevel 
4171708Sstevel 	if ((c = strchr(c, 'D')) == NULL) {
4181708Sstevel 		*dimm = -1;
4191708Sstevel 		*jnumber = 0;
4201708Sstevel 		return (0);
4211708Sstevel 	}
4221708Sstevel 	c++;
4231708Sstevel 	*dimm = stoi(&c);
4241708Sstevel 
4251708Sstevel 	if ((c = strchr(c, 'J')) == NULL) {
4261708Sstevel 		*jnumber = -1;
4271708Sstevel 		return (0);
4281708Sstevel 	}
4291708Sstevel 
4301708Sstevel 	c++;
4311708Sstevel 	*jnumber = (uint16_t)stoi(&c);
4321708Sstevel 
4331708Sstevel 	return (0);
4341708Sstevel }
4351708Sstevel 
4361708Sstevel /*
4371708Sstevel  * The unum string for ecache is expected to be in this form:
4381708Sstevel  * "[/N0/][SB|IO]12/P0/E0 J13500, ..."
4391708Sstevel  * From that we will extract the board number, processor position and
4401708Sstevel  * junmber.
4411708Sstevel  *
4421708Sstevel  * return (1) for any invalid unum string.
4431708Sstevel  */
4441708Sstevel static int
parse_unum_ecache(char * unum,int * board,int * pos,int * jnumber,int * maxcat)4451708Sstevel parse_unum_ecache(char *unum, int *board, int *pos, int *jnumber, int *maxcat)
4461708Sstevel {
4471708Sstevel 	char *c;
4481708Sstevel 
4491708Sstevel 	if ((c = strstr(unum, "SB")) == NULL) {
4501708Sstevel 		/*
4511708Sstevel 		 * Since this is an E$ error, it must have occurred on
4521708Sstevel 		 * either a System Board (represented by "SB" in the
4531708Sstevel 		 * unum string) or a Maxcat board ("IO" in the unum
4541708Sstevel 		 * string).
4551708Sstevel 		 */
4561708Sstevel 		if ((c = strstr(unum, "IO")) == NULL)
4571708Sstevel 			return (1);
4581708Sstevel 		*maxcat = 1;
4591708Sstevel 	}
4601708Sstevel 
4611708Sstevel 	c += 2;
4621708Sstevel 	*board = (uint8_t)stoi(&c);
4631708Sstevel 
4641708Sstevel 	if (*c++ != '/' || *c++ != 'P')
4651708Sstevel 		return (1);
4661708Sstevel 	*pos = stoi(&c);
4671708Sstevel 
4681708Sstevel 	if ((c = strchr(c, 'J')) == NULL)
4691708Sstevel 		return (1);
4701708Sstevel 
4711708Sstevel 	c++;
4721708Sstevel 	*jnumber = (uint16_t)stoi(&c);
4731708Sstevel 
4741708Sstevel 	return (0);
4751708Sstevel }
4761708Sstevel 
4771708Sstevel /* The following array maps the error to its corresponding set */
4781708Sstevel static int plat_ecc_e2d_map[PLAT_ECC_ERROR2_NUMVALS] = {
4791708Sstevel 	PLAT_ECC_ERROR2_NONE,			/* 0x00 */
4801708Sstevel 	PLAT_ECC_ERROR2_SEND_L2_XXC,		/* 0x01 */
4811708Sstevel 	PLAT_ECC_ERROR2_SEND_L2_XXU,		/* 0x02 */
4821708Sstevel 	PLAT_ECC_ERROR2_SEND_L3_XXC,		/* 0x03 */
4831708Sstevel 	PLAT_ECC_ERROR2_SEND_L3_XXU,		/* 0x04 */
4841708Sstevel 	PLAT_ECC_ERROR2_SEND_MEM_ERRS,		/* 0x05 */
4851708Sstevel 	PLAT_ECC_ERROR2_SEND_MEM_ERRS,		/* 0x06 */
4861708Sstevel 	PLAT_ECC_ERROR2_SEND_MEM_ERRS,		/* 0x07 */
4871708Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x08 */
4881708Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x09 */
4891708Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x0a */
4901708Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x0b */
4911708Sstevel 	PLAT_ECC_ERROR2_SEND_L2_TAG_ERRS,	/* 0x0c */
4921708Sstevel 	PLAT_ECC_ERROR2_SEND_L2_TAG_ERRS,	/* 0x0d */
4931708Sstevel 	PLAT_ECC_ERROR2_SEND_L3_TAG_ERRS,	/* 0x0e */
4941708Sstevel 	PLAT_ECC_ERROR2_SEND_L3_TAG_ERRS,	/* 0x0f */
4951708Sstevel 	PLAT_ECC_ERROR2_SEND_L1_PARITY,		/* 0x10 */
4961708Sstevel 	PLAT_ECC_ERROR2_SEND_L1_PARITY,		/* 0x11 */
4971708Sstevel 	PLAT_ECC_ERROR2_SEND_TLB_PARITY,	/* 0x12 */
4981708Sstevel 	PLAT_ECC_ERROR2_SEND_TLB_PARITY,	/* 0x13 */
4991708Sstevel 	PLAT_ECC_ERROR2_SEND_IV_ERRS,		/* 0x14 */
5001708Sstevel 	PLAT_ECC_ERROR2_SEND_IV_ERRS,		/* 0x15 */
5011708Sstevel 	PLAT_ECC_ERROR2_SEND_MTAG_XXC,		/* 0x16 */
5021708Sstevel 	PLAT_ECC_ERROR2_SEND_IV_MTAG_XXC,	/* 0x17 */
5031708Sstevel 	PLAT_ECC_ERROR2_SEND_L3_XXC,		/* 0x18 */
5041708Sstevel 	PLAT_ECC_ERROR2_SEND_PCACHE		/* 0x19 */
5051708Sstevel };
5061708Sstevel 
5071708Sstevel /*
5081708Sstevel  * log enhanced error information to SC.
5091708Sstevel  */
5101708Sstevel void
plat_log_fruid_error2(int msg_type,char * unum,struct async_flt * aflt,plat_ecc_ch_async_flt_t * ecc_ch_flt)5111708Sstevel plat_log_fruid_error2(int msg_type, char *unum, struct async_flt *aflt,
5121708Sstevel     plat_ecc_ch_async_flt_t *ecc_ch_flt)
5131708Sstevel {
5141708Sstevel 	plat_ecc_error2_data_t e2d = {0};
5151708Sstevel 	int board, pos, bank, dimm, jnumber;
5161708Sstevel 	int maxcat = 0;
5171708Sstevel 	uint16_t flags;
5181708Sstevel 
5191708Sstevel 	/* Check the flags */
5201708Sstevel 	flags = plat_ecc_e2d_map[msg_type];
5211708Sstevel 	if ((ecc_error2_mailbox_flags & flags) == 0)
5221708Sstevel 		return;
5231708Sstevel 
5241708Sstevel 	/* Fill the header */
5251708Sstevel 	e2d.ee2d_major_version = PLAT_ECC_ERROR2_VERSION_MAJOR;
5261708Sstevel 	e2d.ee2d_minor_version = PLAT_ECC_ERROR2_VERSION_MINOR;
5271708Sstevel 	e2d.ee2d_msg_type = PLAT_ECC_ERROR2_MESSAGE;
5281708Sstevel 	e2d.ee2d_msg_length = sizeof (plat_ecc_error2_data_t);
5291708Sstevel 
5301708Sstevel 	/* Fill the data */
5311708Sstevel 	if (aflt->flt_in_memory) {
5321708Sstevel 		if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
5331708Sstevel 		    &jnumber) || (dimm != -1 && jnumber == -1))
5341708Sstevel 			return;
5351708Sstevel 		/*
5361708Sstevel 		 * Using the SB number and Proc position we create a FRU
5371708Sstevel 		 * cpu id.
5381708Sstevel 		 */
5391708Sstevel 		e2d.ee2d_owning_proc = plat_make_fru_cpuid(board, 0, pos);
5401708Sstevel 		e2d.ee2d_jnumber = jnumber;
5411708Sstevel 		e2d.ee2d_bank_number = bank;
5421708Sstevel 	} else if (aflt->flt_status & ECC_ECACHE) {
5431708Sstevel 		if (parse_unum_ecache(unum, &board, &pos, &jnumber, &maxcat))
5441708Sstevel 			return;
5451708Sstevel 		/*
5461708Sstevel 		 * Using the SB number and Proc position we create a FRU
5471708Sstevel 		 * cpu id.
5481708Sstevel 		 */
5491708Sstevel 		e2d.ee2d_owning_proc = plat_make_fru_cpuid(board, maxcat, pos);
5501708Sstevel 		e2d.ee2d_jnumber = jnumber;
551*2840Scarlsonj 		e2d.ee2d_bank_number = (uint8_t)-1;
5521708Sstevel 	} else {
5531708Sstevel 		/*
5541708Sstevel 		 * L1 Cache
5551708Sstevel 		 */
5561708Sstevel 		e2d.ee2d_owning_proc = aflt->flt_bus_id;
557*2840Scarlsonj 		e2d.ee2d_jnumber = (uint16_t)-1;
558*2840Scarlsonj 		e2d.ee2d_bank_number = (uint8_t)-1;
5591708Sstevel 	}
5601708Sstevel 
5611708Sstevel 	e2d.ee2d_type = (uint8_t)msg_type;
5621708Sstevel 	e2d.ee2d_afar_status = (uint8_t)ecc_ch_flt->ecaf_afar_status;
5631708Sstevel 	e2d.ee2d_synd_status = (uint8_t)ecc_ch_flt->ecaf_synd_status;
5641708Sstevel 	e2d.ee2d_detecting_proc = aflt->flt_bus_id;
5651708Sstevel 	e2d.ee2d_cpu_impl = cpunodes[e2d.ee2d_owning_proc].implementation;
5661708Sstevel 	e2d.ee2d_timestamp = aflt->flt_id;
5671708Sstevel 	e2d.ee2d_afsr = aflt->flt_stat;
5681708Sstevel 	e2d.ee2d_afar = aflt->flt_addr;
5691708Sstevel 
5701708Sstevel 	e2d.ee2d_sdw_afsr = ecc_ch_flt->ecaf_sdw_afsr;
5711708Sstevel 	e2d.ee2d_sdw_afar = ecc_ch_flt->ecaf_sdw_afar;
5721708Sstevel 	e2d.ee2d_afsr_ext = ecc_ch_flt->ecaf_afsr_ext;
5731708Sstevel 	e2d.ee2d_sdw_afsr_ext = ecc_ch_flt->ecaf_sdw_afsr_ext;
5741708Sstevel 
5751708Sstevel 	/* Send the message to SC */
5761708Sstevel 	(void) plat_send_ecc_mailbox_msg(PLAT_ECC_ERROR2_MESSAGE, &e2d);
5771708Sstevel }
5781708Sstevel 
5791708Sstevel uint8_t ecc_indictment_mailbox_disable = PLAT_ECC_INDICTMENT_OK;
5801708Sstevel uint8_t ecc_indictment_mailbox_flags = PLAT_ECC_SEND_DEFAULT_INDICT;
5811708Sstevel 
5821708Sstevel /*
5831708Sstevel  * We log all Solaris indictments of failing hardware.  We pull the system
5841708Sstevel  * board number and jnumber out of the unum string, and calculate the cpuid
5851708Sstevel  * from some members of the unum string.  The rest of the structure is filled
5861708Sstevel  * in through the other arguments.  The data structure is then passed to
5871708Sstevel  * plat_ecc_dispatch_task().  This function should only be loaded into memory
5881708Sstevel  * or called on platforms that define a plat_send_ecc_mailbox_msg() function.
5891708Sstevel  */
5901708Sstevel static int
plat_log_fruid_indictment(int msg_type,struct async_flt * aflt,char * unum)5911708Sstevel plat_log_fruid_indictment(int msg_type, struct async_flt *aflt, char *unum)
5921708Sstevel {
5931708Sstevel 	plat_ecc_message_t *wrapperp;
5941708Sstevel 	plat_ecc_indict_msg_contents_t *contentsp;
5951708Sstevel 	char *unum_ptr;
5961708Sstevel 	int is_maxcat = 0;
5971708Sstevel 
5981708Sstevel 	switch (ecc_indictment_mailbox_disable) {
5991708Sstevel 	case (PLAT_ECC_INDICTMENT_OK):
6001708Sstevel 	case (PLAT_ECC_INDICTMENT_SUSPECT):
6011708Sstevel 		break;
6021708Sstevel 	case (PLAT_ECC_INDICTMENT_NO_SEND):
6031708Sstevel 	default:
6041708Sstevel 		return (ECONNREFUSED);
6051708Sstevel 	}
6061708Sstevel 
6071708Sstevel 	switch (msg_type) {
6081708Sstevel 	case (PLAT_ECC_INDICT_DIMM):
6091708Sstevel 		if ((ecc_indictment_mailbox_flags &
6101708Sstevel 		    PLAT_ECC_SEND_DIMM_INDICT) == 0)
6111708Sstevel 			return (ECONNREFUSED);
6121708Sstevel 		break;
6131708Sstevel 	case (PLAT_ECC_INDICT_ECACHE_CORRECTABLES):
6141708Sstevel 		if ((ecc_indictment_mailbox_flags &
6151708Sstevel 		    PLAT_ECC_SEND_ECACHE_XXC_INDICT) == 0)
6161708Sstevel 			return (ECONNREFUSED);
6171708Sstevel 		break;
6181708Sstevel 	case (PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE):
6191708Sstevel 		if ((ecc_indictment_mailbox_flags &
6201708Sstevel 		    PLAT_ECC_SEND_ECACHE_XXU_INDICT) == 0)
6211708Sstevel 			return (ECONNREFUSED);
6221708Sstevel 		break;
6231708Sstevel 	default:
6241708Sstevel 		return (ECONNREFUSED);
6251708Sstevel 	}
6261708Sstevel 
6271708Sstevel 	/* LINTED: E_TRUE_LOGICAL_EXPR */
6281708Sstevel 	ASSERT(sizeof (plat_ecc_indictment_data_t) == PLAT_ECC_INDICT_SIZE);
6291708Sstevel 
6301708Sstevel 	wrapperp = (plat_ecc_message_t *)
6311708Sstevel 	    kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
6321708Sstevel 
6331708Sstevel 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
6341708Sstevel 	wrapperp->ecc_msg_type = PLAT_ECC_INDICTMENT_MESSAGE;
6351708Sstevel 	wrapperp->ecc_msg_len = sizeof (plat_ecc_indictment_data_t);
6361708Sstevel 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
6371708Sstevel 
6381708Sstevel 	contentsp = &(((plat_ecc_indictment_data_t *)
6391708Sstevel 	    wrapperp->ecc_msg_data)->msg_contents);
6401708Sstevel 
6411708Sstevel 	/*
6421708Sstevel 	 * Find board_num, jnumber, and proc position from the unum string.
6431708Sstevel 	 * Use the board number, is_maxcat, and proc position to calculate
6441708Sstevel 	 * cpuid.
6451708Sstevel 	 */
6461708Sstevel 	unum_ptr = strstr(unum, "SB");
6471708Sstevel 	if (unum_ptr == NULL) {
6481708Sstevel 		is_maxcat = 1;
6491708Sstevel 		unum_ptr = strstr(unum, "IO");
6501708Sstevel 		if (unum_ptr == NULL) {
6511708Sstevel 			kmem_free(wrapperp->ecc_msg_data,
6521708Sstevel 			    wrapperp->ecc_msg_len);
6531708Sstevel 			kmem_free(wrapperp, sizeof (plat_ecc_message_t));
6541708Sstevel 			return (EINVAL);
6551708Sstevel 		}
6561708Sstevel 	}
6571708Sstevel 	unum_ptr += 2;
6581708Sstevel 	contentsp->board_num = (uint8_t)stoi(&unum_ptr);
6591708Sstevel 
6601708Sstevel 	unum_ptr = strchr(unum_ptr, 'P');
6611708Sstevel 	if (unum_ptr == NULL) {
6621708Sstevel 		kmem_free(wrapperp->ecc_msg_data, wrapperp->ecc_msg_len);
6631708Sstevel 		kmem_free(wrapperp, sizeof (plat_ecc_message_t));
6641708Sstevel 		return (EINVAL);
6651708Sstevel 	}
6661708Sstevel 	unum_ptr++;
6671708Sstevel 	contentsp->detecting_proc =
6681708Sstevel 	    (uint16_t)plat_make_fru_cpuid(contentsp->board_num, is_maxcat,
6691708Sstevel 	    stoi(&unum_ptr));
6701708Sstevel 
6711708Sstevel 	unum_ptr = strchr(unum_ptr, 'J');
6721708Sstevel 	if (unum_ptr == NULL) {
6731708Sstevel 		kmem_free(wrapperp->ecc_msg_data, wrapperp->ecc_msg_len);
6741708Sstevel 		kmem_free(wrapperp, sizeof (plat_ecc_message_t));
6751708Sstevel 		return (EINVAL);
6761708Sstevel 	}
6771708Sstevel 	unum_ptr++;
6781708Sstevel 	contentsp->jnumber = (uint16_t)stoi(&unum_ptr);
6791708Sstevel 
6801708Sstevel 	/*
6811708Sstevel 	 * Fill in the rest of the data
6821708Sstevel 	 */
6831708Sstevel 	contentsp->version = PLAT_ECC_INDICTMENT_VERSION;
6841708Sstevel 	contentsp->indictment_type = msg_type;
6851708Sstevel 	contentsp->indictment_uncertain = ecc_indictment_mailbox_disable;
6861708Sstevel 	contentsp->syndrome = aflt->flt_synd;
6871708Sstevel 	contentsp->afsr = aflt->flt_stat;
6881708Sstevel 	contentsp->afar = aflt->flt_addr;
6891708Sstevel 
6901708Sstevel 	/*
6911708Sstevel 	 * Build the solaris_version string:
6921708Sstevel 	 */
6931708Sstevel 	(void) snprintf(contentsp->solaris_version,
6941708Sstevel 	    PLAT_ECC_VERSION_LENGTH, "%s %s", utsname.release, utsname.version);
6951708Sstevel 
6961708Sstevel 	/*
6971708Sstevel 	 * Send the data on to the queuing function
6981708Sstevel 	 */
6991708Sstevel 	return (plat_ecc_dispatch_task(wrapperp));
7001708Sstevel }
7011708Sstevel 
7021708Sstevel /* The following array maps the indictment to its corresponding set */
7031708Sstevel static int plat_ecc_i2d_map[PLAT_ECC_INDICT2_NUMVALS] = {
7041708Sstevel 	PLAT_ECC_INDICT2_NONE,			/* 0x00 */
7051708Sstevel 	PLAT_ECC_SEND_INDICT2_L2_XXU,		/* 0x01 */
7061708Sstevel 	PLAT_ECC_SEND_INDICT2_L2_XXC_SERD,	/* 0x02 */
7071708Sstevel 	PLAT_ECC_SEND_INDICT2_L2_TAG_SERD,	/* 0x03 */
7081708Sstevel 	PLAT_ECC_SEND_INDICT2_L3_XXU,		/* 0x04 */
7091708Sstevel 	PLAT_ECC_SEND_INDICT2_L3_XXC_SERD,	/* 0x05 */
7101708Sstevel 	PLAT_ECC_SEND_INDICT2_L3_TAG_SERD,	/* 0x06 */
7111708Sstevel 	PLAT_ECC_SEND_INDICT2_L1_SERD,		/* 0x07 */
7121708Sstevel 	PLAT_ECC_SEND_INDICT2_L1_SERD,		/* 0x08 */
7131708Sstevel 	PLAT_ECC_SEND_INDICT2_TLB_SERD,		/* 0x09 */
7141708Sstevel 	PLAT_ECC_SEND_INDICT2_TLB_SERD,		/* 0x0a */
7151708Sstevel 	PLAT_ECC_SEND_INDICT2_FPU,		/* 0x0b */
7161708Sstevel 	PLAT_ECC_SEND_INDICT2_PCACHE_SERD	/* 0x0c */
7171708Sstevel };
7181708Sstevel 
7191708Sstevel static int
plat_log_fruid_indictment2(int msg_type,struct async_flt * aflt,char * unum)7201708Sstevel plat_log_fruid_indictment2(int msg_type, struct async_flt *aflt, char *unum)
7211708Sstevel {
7221708Sstevel 	plat_ecc_message_t *wrapperp;
7231708Sstevel 	plat_ecc_indictment2_data_t *i2d;
7241708Sstevel 	int board, pos, jnumber;
7251708Sstevel 	int maxcat = 0;
7261708Sstevel 	uint16_t flags;
7271708Sstevel 
7281708Sstevel 	/*
7291708Sstevel 	 * If the unum is null or empty, skip parsing it
7301708Sstevel 	 */
7311708Sstevel 	if (unum && unum[0] != '\0') {
7321708Sstevel 		if (parse_unum_ecache(unum, &board, &pos, &jnumber, &maxcat))
7331708Sstevel 			return (EINVAL);
7341708Sstevel 	}
7351708Sstevel 
7361708Sstevel 	if ((ecc_indictment_mailbox_disable != PLAT_ECC_INDICTMENT_OK) &&
7371708Sstevel 	    (ecc_indictment_mailbox_disable != PLAT_ECC_INDICTMENT_SUSPECT))
7381708Sstevel 		return (ECONNREFUSED);
7391708Sstevel 
7401708Sstevel 	/* Check the flags */
7411708Sstevel 	flags = plat_ecc_i2d_map[msg_type];
7421708Sstevel 	if ((ecc_indictment2_mailbox_flags & flags) == 0)
7431708Sstevel 		return (ECONNREFUSED);
7441708Sstevel 
7451708Sstevel 	wrapperp = (plat_ecc_message_t *)
7461708Sstevel 	    kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
7471708Sstevel 
7481708Sstevel 	/* Initialize the wrapper */
7491708Sstevel 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
7501708Sstevel 	wrapperp->ecc_msg_type = PLAT_ECC_INDICTMENT2_MESSAGE;
7511708Sstevel 	wrapperp->ecc_msg_len = sizeof (plat_ecc_indictment2_data_t);
7521708Sstevel 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
7531708Sstevel 
7541708Sstevel 	i2d = (plat_ecc_indictment2_data_t *)wrapperp->ecc_msg_data;
7551708Sstevel 
7561708Sstevel 	/* Fill the header */
7571708Sstevel 	i2d->ei2d_major_version = PLAT_ECC_INDICT2_MAJOR_VERSION;
7581708Sstevel 	i2d->ei2d_minor_version = PLAT_ECC_INDICT2_MINOR_VERSION;
7591708Sstevel 	i2d->ei2d_msg_type = PLAT_ECC_INDICTMENT2_MESSAGE;
7601708Sstevel 	i2d->ei2d_msg_length = sizeof (plat_ecc_indictment2_data_t);
7611708Sstevel 
7621708Sstevel 	/* Fill the data */
7631708Sstevel 	if (unum && unum[0] != '\0') {
7641708Sstevel 		i2d->ei2d_arraigned_proc = plat_make_fru_cpuid(board, maxcat,
7651708Sstevel 		    pos);
7661708Sstevel 		i2d->ei2d_board_num = board;
7671708Sstevel 		i2d->ei2d_jnumber = jnumber;
7681708Sstevel 	} else {
7691708Sstevel 		i2d->ei2d_arraigned_proc = aflt->flt_inst;
7701708Sstevel 		i2d->ei2d_board_num = (uint8_t)
7711708Sstevel 		    plat_make_fru_boardnum(i2d->ei2d_arraigned_proc);
772*2840Scarlsonj 		i2d->ei2d_jnumber = (uint16_t)-1;
7731708Sstevel 	}
7741708Sstevel 
7751708Sstevel 	i2d->ei2d_type = msg_type;
7761708Sstevel 	i2d->ei2d_uncertain = ecc_indictment_mailbox_disable;
7771708Sstevel 	i2d->ei2d_cpu_impl = cpunodes[i2d->ei2d_arraigned_proc].implementation;
7781708Sstevel 	i2d->ei2d_timestamp = aflt->flt_id;
7791708Sstevel 
7801708Sstevel 	/*
7811708Sstevel 	 * Send the data on to the queuing function
7821708Sstevel 	 */
7831708Sstevel 	return (plat_ecc_dispatch_task(wrapperp));
7841708Sstevel }
7851708Sstevel 
7861708Sstevel int
plat_ecc_capability_send(void)7871708Sstevel plat_ecc_capability_send(void)
7881708Sstevel {
7891708Sstevel 	plat_ecc_message_t *wrapperp;
7901708Sstevel 	plat_capability_data_t	*cap;
7911708Sstevel 	int ver_len;
7921708Sstevel 
7931708Sstevel 	wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
7941708Sstevel 
7951708Sstevel 	ver_len = strlen(utsname.release) + strlen(utsname.version) + 2;
7961708Sstevel 
7971708Sstevel 	/* Initialize the wrapper */
7981708Sstevel 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
7991708Sstevel 	wrapperp->ecc_msg_type = PLAT_ECC_CAPABILITY_MESSAGE;
8001708Sstevel 	wrapperp->ecc_msg_len = sizeof (plat_capability_data_t) + ver_len;
8011708Sstevel 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
8021708Sstevel 
8031708Sstevel 	cap = (plat_capability_data_t *)wrapperp->ecc_msg_data;
8041708Sstevel 
8051708Sstevel 	/* Fill the header */
8061708Sstevel 	cap->capd_major_version = PLAT_ECC_CAP_VERSION_MAJOR;
8071708Sstevel 	cap->capd_minor_version = PLAT_ECC_CAP_VERSION_MINOR;
8081708Sstevel 	cap->capd_msg_type = PLAT_ECC_CAPABILITY_MESSAGE;
8091708Sstevel 	cap->capd_msg_length = wrapperp->ecc_msg_len;
8101708Sstevel 
8111708Sstevel 	/* Set the default domain capability */
8121708Sstevel 	cap->capd_capability = PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT;
8131708Sstevel 
8141708Sstevel 	/*
8151708Sstevel 	 * Build the solaris_version string:
8161708Sstevel 	 * utsname.release + " " + utsname.version
8171708Sstevel 	 */
8181708Sstevel 	(void) snprintf(cap->capd_solaris_version, ver_len, "%s %s",
8191708Sstevel 	    utsname.release, utsname.version);
8201708Sstevel 
8211708Sstevel 	/*
8221708Sstevel 	 * Send the data on to the queuing function
8231708Sstevel 	 */
8241708Sstevel 	return (plat_ecc_dispatch_task(wrapperp));
8251708Sstevel }
8261708Sstevel 
8271708Sstevel int
plat_ecc_capability_sc_get(int type)8281708Sstevel plat_ecc_capability_sc_get(int type)
8291708Sstevel {
8301708Sstevel 	switch (type) {
8311708Sstevel 		case PLAT_ECC_ERROR_MESSAGE:
8321708Sstevel 			if (ecc_log_fruid_enable &&
8331708Sstevel 			    (!(plat_ecc_capability_map_sc &
8341708Sstevel 			    PLAT_ECC_CAPABILITY_ERROR2)))
8351708Sstevel 				return (1);
8361708Sstevel 			break;
8371708Sstevel 		case PLAT_ECC_ERROR2_MESSAGE:
8381708Sstevel 			if (plat_ecc_capability_map_sc &
8391708Sstevel 			    PLAT_ECC_CAPABILITY_ERROR2)
8401708Sstevel 				return (1);
8411708Sstevel 			break;
8421708Sstevel 		case PLAT_ECC_INDICTMENT_MESSAGE:
8431708Sstevel 			if (!(plat_ecc_capability_map_sc &
8441708Sstevel 			    PLAT_ECC_CAPABILITY_INDICT2) ||
8451708Sstevel 			    !(plat_ecc_capability_map_domain &
8461708Sstevel 			    PLAT_ECC_CAPABILITY_FMA))
8471708Sstevel 				return (1);
8481708Sstevel 			break;
8491708Sstevel 		case PLAT_ECC_INDICTMENT2_MESSAGE:
8501708Sstevel 			if (plat_ecc_capability_map_sc &
8511708Sstevel 			    PLAT_ECC_CAPABILITY_INDICT2)
8521708Sstevel 				return (1);
8531708Sstevel 			break;
8541708Sstevel 		case PLAT_ECC_DIMM_SID_MESSAGE:
8551708Sstevel 			if (plat_ecc_capability_map_sc &
8561708Sstevel 			    PLAT_ECC_CAPABILITY_DIMM_SID)
8571708Sstevel 				return (1);
8581708Sstevel 		default:
8591708Sstevel 			return (0);
8601708Sstevel 	}
8611708Sstevel 	return (0);
8621708Sstevel }
8631708Sstevel 
8641708Sstevel int plat_ecc_cap_sc_set_cnt = 0;
8651708Sstevel 
8661708Sstevel void
plat_ecc_capability_sc_set(uint32_t cap)8671708Sstevel plat_ecc_capability_sc_set(uint32_t cap)
8681708Sstevel {
8691708Sstevel 	plat_ecc_capability_map_sc = cap;
8701708Sstevel 
8711708Sstevel 	if (!plat_ecc_cap_sc_set_cnt && (cap & PLAT_ECC_CAPABILITY_DIMM_SID))
8721708Sstevel 		if (p2init_sid_cache)
8731708Sstevel 			p2init_sid_cache();
8741708Sstevel 
8751708Sstevel 	plat_ecc_cap_sc_set_cnt++;
8761708Sstevel }
8771708Sstevel 
8781708Sstevel /*
8791708Sstevel  * The following table represents mapping between the indictment1 reason
8801708Sstevel  * to its type.
8811708Sstevel  */
8821708Sstevel 
8831708Sstevel static plat_ecc_bl_map_t plat_ecc_bl_map_v1[] = {
8841708Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT_ECACHE_CORRECTABLES	},
8851708Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT_ECACHE_CORRECTABLES	},
8861708Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE	},
8871708Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE	}
8881708Sstevel };
8891708Sstevel 
8901708Sstevel /*
8911708Sstevel  * The following table represents mapping between the indictment2 reason
8921708Sstevel  * to its type.
8931708Sstevel  */
8941708Sstevel 
8951708Sstevel static plat_ecc_bl_map_t plat_ecc_bl_map_v2[] = {
8961708Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT2_L2_SERD	},
8971708Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT2_L3_SERD	},
8981708Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT2_L2_UE		},
8991708Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT2_L3_UE		},
9001708Sstevel 	{ "l2cachetag",		PLAT_ECC_INDICT2_L2_TAG_SERD	},
9011708Sstevel 	{ "l3cachetag",		PLAT_ECC_INDICT2_L3_TAG_SERD	},
9021708Sstevel 	{ "icache",		PLAT_ECC_INDICT2_ICACHE_SERD	},
9031708Sstevel 	{ "dcache",		PLAT_ECC_INDICT2_DCACHE_SERD	},
9041708Sstevel 	{ "pcache",		PLAT_ECC_INDICT2_PCACHE_SERD	},
9051708Sstevel 	{ "itlb",		PLAT_ECC_INDICT2_ITLB_SERD	},
9061708Sstevel 	{ "dtlb",		PLAT_ECC_INDICT2_DTLB_SERD	},
9071708Sstevel 	{ "fpu",		PLAT_ECC_INDICT2_FPU		}
9081708Sstevel };
9091708Sstevel 
9101708Sstevel /*
9111708Sstevel  * The following function returns the indictment type for a given version
9121708Sstevel  */
9131708Sstevel static int
flt_name_to_msg_type(const char * fault,int indict_version)9141708Sstevel flt_name_to_msg_type(const char *fault, int indict_version)
9151708Sstevel {
9161708Sstevel 	plat_ecc_bl_map_t *mapp;
9171708Sstevel 	char *fltnm = "fault.cpu.";
9181708Sstevel 	int mapsz;
9191708Sstevel 	char *p;
9201708Sstevel 	int i;
9211708Sstevel 
9221708Sstevel 	/* Check if it starts with proper fault name */
9231708Sstevel 	if (strncmp(fault, fltnm, strlen(fltnm)) != 0)
9241708Sstevel 		return (PLAT_ECC_INDICT_NONE);
9251708Sstevel 
9261708Sstevel 	fault += strlen(fltnm); /* c = "ultraSPARC-IV.icache" */
9271708Sstevel 
9281708Sstevel 	/* Skip the cpu type */
9291708Sstevel 	if ((p = strchr(fault, '.')) == NULL)
9301708Sstevel 		return (PLAT_ECC_INDICT_NONE);
9311708Sstevel 
9321708Sstevel 	p++;	/* skip the "." */
9331708Sstevel 
9341708Sstevel 	if (indict_version ==  0) {
9351708Sstevel 		mapp = plat_ecc_bl_map_v1;
9361708Sstevel 		mapsz = sizeof (plat_ecc_bl_map_v1) /
9371708Sstevel 		    sizeof (plat_ecc_bl_map_t);
9381708Sstevel 	} else {
9391708Sstevel 		mapp = plat_ecc_bl_map_v2;
9401708Sstevel 		mapsz = sizeof (plat_ecc_bl_map_v2) /
9411708Sstevel 		    sizeof (plat_ecc_bl_map_t);
9421708Sstevel 	}
9431708Sstevel 	for (i = 0; i < mapsz; i++) {
9441708Sstevel 		if (strcmp(p, mapp[i].ebm_reason) == 0) {
9451708Sstevel 			return (mapp[i].ebm_type);
9461708Sstevel 		}
9471708Sstevel 	}
9481708Sstevel 	return (PLAT_ECC_INDICT_NONE);
9491708Sstevel }
9501708Sstevel 
9511708Sstevel /*
9521708Sstevel  * Blacklisting
9531708Sstevel  */
9541708Sstevel int
plat_blacklist(int cmd,const char * scheme,nvlist_t * fmri,const char * class)9551708Sstevel plat_blacklist(int cmd, const char *scheme, nvlist_t *fmri, const char *class)
9561708Sstevel {
9571708Sstevel 	struct async_flt aflt;
9581708Sstevel 	char *unum;
9591708Sstevel 	int msg_type, is_old_indict;
9601708Sstevel 
9611708Sstevel 	if (fmri == NULL)
9621708Sstevel 		return (EINVAL);
9631708Sstevel 	if (cmd != BLIOC_INSERT)
9641708Sstevel 		return (ENOTSUP);
9651708Sstevel 
9661708Sstevel 	/*
9671708Sstevel 	 * We support both the blacklisting of CPUs via mem-schemed
9681708Sstevel 	 * FMRIs that name E$ J-numbers, and CPUs via cpu-schemed FMRIs
9691708Sstevel 	 * that name the cpuid.
9701708Sstevel 	 */
9711708Sstevel 	if (strcmp(scheme, FM_FMRI_SCHEME_MEM) == 0) {
9721708Sstevel 		if (nvlist_lookup_string(fmri, FM_FMRI_MEM_UNUM, &unum))
9731708Sstevel 			return (EINVAL);
974*2840Scarlsonj 		aflt.flt_inst = (uint_t)-1;
9751708Sstevel 	} else if (strcmp(scheme, FM_FMRI_SCHEME_CPU) == 0) {
9761708Sstevel 		if (nvlist_lookup_uint32(fmri, FM_FMRI_CPU_ID, &aflt.flt_inst))
9771708Sstevel 			return (EINVAL);
9781708Sstevel 		unum = NULL;
9791708Sstevel 	} else {
9801708Sstevel 		return (ENOTSUP);
9811708Sstevel 	}
9821708Sstevel 
9831708Sstevel 	/*
9841708Sstevel 	 * If the SC cannot handle indictment2, so fall back to old one.
9851708Sstevel 	 * Also if the domain does not support FMA, then send only the old one.
9861708Sstevel 	 */
9871708Sstevel 
9881708Sstevel 	is_old_indict = plat_ecc_capability_sc_get(PLAT_ECC_INDICTMENT_MESSAGE);
9891708Sstevel 
9901708Sstevel 	if (is_old_indict)
9911708Sstevel 		msg_type = flt_name_to_msg_type(class, 0);
9921708Sstevel 	else
9931708Sstevel 		msg_type = flt_name_to_msg_type(class, 1);
9941708Sstevel 
9951708Sstevel 	if (msg_type == PLAT_ECC_INDICT_NONE)
9961708Sstevel 		return (ENOTSUP);
9971708Sstevel 
9981708Sstevel 	/*
9991708Sstevel 	 * The current blacklisting interfaces are designed for a world where
10001708Sstevel 	 * the SC is much more involved in the diagnosis and error reporting
10011708Sstevel 	 * process than it is in the FMA world.  As such, the existing
10021708Sstevel 	 * interfaces want all kinds of information about the error that's
10031708Sstevel 	 * triggering the blacklist.  In the FMA world, we don't have access
10041708Sstevel 	 * to any of that information by the time we're doing the blacklist,
10051708Sstevel 	 * so we fake values.
10061708Sstevel 	 */
10071708Sstevel 	aflt.flt_id = gethrtime();
10081708Sstevel 	aflt.flt_addr = -1;
10091708Sstevel 	aflt.flt_stat = -1;
1010*2840Scarlsonj 	aflt.flt_synd = (ushort_t)-1;
10111708Sstevel 
10121708Sstevel 	if (is_old_indict) {
10131708Sstevel 		if (unum && unum[0] != '\0')
10141708Sstevel 			return (plat_log_fruid_indictment(msg_type, &aflt,
10151708Sstevel 			    unum));
10161708Sstevel 		else
10171708Sstevel 			return (ENOTSUP);
10181708Sstevel 	} else {
10191708Sstevel 		return (plat_log_fruid_indictment2(msg_type, &aflt, unum));
10201708Sstevel 	}
10211708Sstevel }
10221708Sstevel 
10231708Sstevel static kcondvar_t plat_ecc_condvar;
10241708Sstevel static kmutex_t plat_ecc_mutex;
10251708Sstevel static taskq_t *plat_ecc_taskq;
10261708Sstevel 
10271708Sstevel /*
10281708Sstevel  * plat_ecc_dispatch_task: Dispatch the task on a taskq and wait for the
10291708Sstevel  * return value.  We use cv_wait_sig to wait for the return values.  If a
10301708Sstevel  * signal interrupts us, we return EINTR.  Otherwise, we return the value
10311708Sstevel  * returned by the mailbox functions.
10321708Sstevel  *
10331708Sstevel  * To avoid overloading the lower-level mailbox routines, we use a taskq
10341708Sstevel  * to serialize all messages.  Currently, it is expected that only one
10351708Sstevel  * process (fmd) will use this ioctl, so the delay caused by the taskq
10361708Sstevel  * should not have much of an effect.
10371708Sstevel  */
10381708Sstevel int
plat_ecc_dispatch_task(plat_ecc_message_t * msg)10391708Sstevel plat_ecc_dispatch_task(plat_ecc_message_t *msg)
10401708Sstevel {
10411708Sstevel 	int ret;
10421708Sstevel 
10431708Sstevel 	ASSERT(msg != NULL);
10441708Sstevel 	ASSERT(plat_ecc_taskq != NULL);
10451708Sstevel 
10461708Sstevel 	if (taskq_dispatch(plat_ecc_taskq, plat_ecc_send_msg,
10471708Sstevel 	    (void *)msg, TQ_NOSLEEP) == NULL) {
10481708Sstevel 		kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
10491708Sstevel 		kmem_free(msg, sizeof (plat_ecc_message_t));
10501708Sstevel 		return (ENOMEM);
10511708Sstevel 	}
10521708Sstevel 	mutex_enter(&plat_ecc_mutex);
10531708Sstevel 
10541708Sstevel 	/*
10551708Sstevel 	 * It's possible that the taskq function completed before we
10561708Sstevel 	 * acquired the mutex.  Check for this first.  If this did not
10571708Sstevel 	 * happen, we wait for the taskq function to signal us, or an
10581708Sstevel 	 * interrupt.  We also check ecc_msg_status to protect against
10591708Sstevel 	 * spurious wakeups from cv_wait_sig.
10601708Sstevel 	 */
10611708Sstevel 	if (msg->ecc_msg_status == PLAT_ECC_MSG_SENT) {
10621708Sstevel 		ret = msg->ecc_msg_ret;
10631708Sstevel 		kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
10641708Sstevel 		kmem_free(msg, sizeof (plat_ecc_message_t));
10651708Sstevel 	} else {
10661708Sstevel 		msg->ecc_msg_status = PLAT_ECC_TASK_DISPATCHED;
10671708Sstevel 
10681708Sstevel 		while ((ret = cv_wait_sig(&plat_ecc_condvar,
10691708Sstevel 		    &plat_ecc_mutex)) != 0 &&
10701708Sstevel 		    msg->ecc_msg_status == PLAT_ECC_TASK_DISPATCHED)
10711708Sstevel 			;
10721708Sstevel 
10731708Sstevel 		if ((ret == 0) && (msg->ecc_msg_status != PLAT_ECC_MSG_SENT)) {
10741708Sstevel 			/* An interrupt was received */
10751708Sstevel 			msg->ecc_msg_status = PLAT_ECC_INTERRUPT_RECEIVED;
10761708Sstevel 			ret = EINTR;
10771708Sstevel 		} else {
10781708Sstevel 			ret = msg->ecc_msg_ret;
10791708Sstevel 			kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
10801708Sstevel 			kmem_free(msg, sizeof (plat_ecc_message_t));
10811708Sstevel 		}
10821708Sstevel 	}
10831708Sstevel 	mutex_exit(&plat_ecc_mutex);
10841708Sstevel 	return (ret);
10851708Sstevel }
10861708Sstevel 
10871708Sstevel static void
plat_ecc_send_msg(void * arg)10881708Sstevel plat_ecc_send_msg(void *arg)
10891708Sstevel {
10901708Sstevel 	plat_ecc_message_t *msg = arg;
10911708Sstevel 	int ret;
10921708Sstevel 
10931708Sstevel 	/*
10941708Sstevel 	 * Send this data off as a mailbox message to the SC.
10951708Sstevel 	 */
10961708Sstevel 	ret = plat_send_ecc_mailbox_msg(msg->ecc_msg_type, msg->ecc_msg_data);
10971708Sstevel 
10981708Sstevel 	mutex_enter(&plat_ecc_mutex);
10991708Sstevel 
11001708Sstevel 	/*
11011708Sstevel 	 * If the dispatching function received an interrupt, don't bother
11021708Sstevel 	 * signalling it, and throw away the results.  Otherwise, set the
11031708Sstevel 	 * return value and signal the condvar.
11041708Sstevel 	 */
11051708Sstevel 	if (msg->ecc_msg_status == PLAT_ECC_INTERRUPT_RECEIVED) {
11061708Sstevel 		kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
11071708Sstevel 		kmem_free(msg, sizeof (plat_ecc_message_t));
11081708Sstevel 	} else {
11091708Sstevel 		msg->ecc_msg_ret = ret;
11101708Sstevel 		msg->ecc_msg_status = PLAT_ECC_MSG_SENT;
11111708Sstevel 		cv_broadcast(&plat_ecc_condvar);
11121708Sstevel 	}
11131708Sstevel 
11141708Sstevel 	mutex_exit(&plat_ecc_mutex);
11151708Sstevel }
11161708Sstevel 
11171708Sstevel void
plat_ecc_init(void)11181708Sstevel plat_ecc_init(void)
11191708Sstevel {
11201708Sstevel 	int	bd;
11211708Sstevel 
11221708Sstevel 	mutex_init(&plat_ecc_mutex, NULL, MUTEX_DEFAULT, NULL);
11231708Sstevel 	cv_init(&plat_ecc_condvar, NULL, CV_DEFAULT, NULL);
11241708Sstevel 	plat_ecc_taskq = taskq_create("plat_ecc_taskq", 1, minclsyspri,
11251708Sstevel 	    PLAT_ECC_TASKQ_MIN, PLAT_ECC_TASKQ_MAX, TASKQ_PREPOPULATE);
11261708Sstevel 	ASSERT(plat_ecc_taskq != NULL);
11271708Sstevel 
11281708Sstevel 	for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
11291708Sstevel 		mutex_init(&domain_dimm_sids[bd].pdsb_lock,
11301708Sstevel 		    NULL, MUTEX_DEFAULT, NULL);
11311708Sstevel 	}
11321708Sstevel 
11331708Sstevel }
1134