103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
5055d7c80Scarlsonj  * Common Development and Distribution License (the "License").
6055d7c80Scarlsonj  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel /*
22055d7c80Scarlsonj  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2303831d35Sstevel  * Use is subject to license terms.
2403831d35Sstevel  */
2503831d35Sstevel 
2603831d35Sstevel #include <sys/plat_ecc_unum.h>
2703831d35Sstevel #include <sys/utsname.h>
2803831d35Sstevel #include <sys/cmn_err.h>
2903831d35Sstevel #include <sys/async.h>
3003831d35Sstevel #include <sys/errno.h>
3103831d35Sstevel #include <sys/fm/protocol.h>
3203831d35Sstevel #include <sys/fm/cpu/UltraSPARC-III.h>
3303831d35Sstevel #include <sys/bl.h>
3403831d35Sstevel #include <sys/taskq.h>
3503831d35Sstevel #include <sys/condvar.h>
3603831d35Sstevel #include <sys/plat_ecc_dimm.h>
3703831d35Sstevel 
3803831d35Sstevel /*
3903831d35Sstevel  * Pointer to platform specific function to initialize a cache of DIMM
4003831d35Sstevel  * serial ids
4103831d35Sstevel  */
4203831d35Sstevel int (*p2init_sid_cache)(void);
4303831d35Sstevel 
4403831d35Sstevel /*
4503831d35Sstevel  * This file contains the common code that is used for parsing
4603831d35Sstevel  * ecc unum data and logging it appropriately as the platform
4703831d35Sstevel  * that calls this code implements.
4803831d35Sstevel  */
4903831d35Sstevel 
5003831d35Sstevel int plat_ecc_dispatch_task(plat_ecc_message_t *);
5103831d35Sstevel static void plat_ecc_send_msg(void *);
5203831d35Sstevel 
5303831d35Sstevel #define	CHECK_UNUM \
5403831d35Sstevel 	if (unum_ptr == NULL) { \
5503831d35Sstevel 		break; \
5603831d35Sstevel 	}
5703831d35Sstevel 
5803831d35Sstevel /*
5903831d35Sstevel  * See plat_ecc_unum.h for the meaning of these variables.
6003831d35Sstevel  */
6103831d35Sstevel int ecc_log_fruid_enable = ECC_FRUID_ENABLE_DEFAULT;
6203831d35Sstevel 
6303831d35Sstevel uint32_t plat_ecc_capability_map_domain = PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT;
6403831d35Sstevel uint32_t plat_ecc_capability_map_sc = PLAT_ECC_CAPABILITY_SC_DEFAULT;
6503831d35Sstevel uint16_t ecc_error2_mailbox_flags = PLAT_ECC_ERROR2_SEND_DEFAULT;
6603831d35Sstevel uint16_t ecc_indictment2_mailbox_flags = PLAT_ECC_SEND_INDICT2_DEFAULT;
6703831d35Sstevel 
6803831d35Sstevel /*
6903831d35Sstevel  * We log all ECC errors using the function that is defined as
7003831d35Sstevel  * plat_send_ecc_mailbox_msg(); We first parse the unum string and
7103831d35Sstevel  * then pass the data to be logged to the plat_send_ecc_mailbox_msg
7203831d35Sstevel  * function for logging. Each platform that uses this code needs to
7303831d35Sstevel  * implement a suitable function for this purpose.
7403831d35Sstevel  */
7503831d35Sstevel void
plat_log_fruid_error(int synd_code,struct async_flt * ecc,char * unum,uint64_t afsr_bit)7603831d35Sstevel plat_log_fruid_error(int synd_code, struct async_flt *ecc, char *unum,
7735e1ee58SToomas Soome     uint64_t afsr_bit)
7803831d35Sstevel {
7903831d35Sstevel 	plat_ecc_error_data_t ecc_error_data;
8003831d35Sstevel 	enum plat_ecc_type ecc_type = PLAT_ECC_UNKNOWN;
8103831d35Sstevel 	int board_num;
8203831d35Sstevel 	int proc_position;
8303831d35Sstevel 	int invalid_unum = 1;
8403831d35Sstevel 
8503831d35Sstevel 	bzero(&ecc_error_data, sizeof (plat_ecc_error_data_t));
8603831d35Sstevel 	ecc_error_data.version = PLAT_ECC_VERSION;
8703831d35Sstevel 
8803831d35Sstevel 	switch (afsr_bit) {
8903831d35Sstevel 	case C_AFSR_CE:
9003831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_CE;
9103831d35Sstevel 		break;
9203831d35Sstevel 	case C_AFSR_UE:
9303831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_UE;
9403831d35Sstevel 		break;
9503831d35Sstevel 	case C_AFSR_EDC:
9603831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EDC;
9703831d35Sstevel 		break;
9803831d35Sstevel 	case C_AFSR_EDU:
9903831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EDU;
10003831d35Sstevel 		break;
10103831d35Sstevel 	case C_AFSR_WDC:
10203831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_WDC;
10303831d35Sstevel 		break;
10403831d35Sstevel 	case C_AFSR_WDU:
10503831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_WDU;
10603831d35Sstevel 		break;
10703831d35Sstevel 	case C_AFSR_CPC:
10803831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_CPC;
10903831d35Sstevel 		break;
11003831d35Sstevel 	case C_AFSR_CPU:
11103831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_CPU;
11203831d35Sstevel 		break;
11303831d35Sstevel 	case C_AFSR_UCC:
11403831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_UCC;
11503831d35Sstevel 		break;
11603831d35Sstevel 	case C_AFSR_UCU:
11703831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_UCU;
11803831d35Sstevel 		break;
11903831d35Sstevel 	case C_AFSR_EMC:
12003831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EMC;
12103831d35Sstevel 		break;
12203831d35Sstevel 	case C_AFSR_EMU:
12303831d35Sstevel 		ecc_error_data.error_code = PLAT_ERROR_CODE_EMU;
12403831d35Sstevel 		break;
12503831d35Sstevel 	default:
12603831d35Sstevel 		/*
12703831d35Sstevel 		 * Do not send messages with unknown error codes, since
12803831d35Sstevel 		 * the SC will not be able to tell what type of error
12903831d35Sstevel 		 * occurred.
13003831d35Sstevel 		 */
13103831d35Sstevel 		return;
13203831d35Sstevel 	}
13303831d35Sstevel 
13403831d35Sstevel 	ecc_error_data.detecting_proc = ecc->flt_bus_id;
13503831d35Sstevel 
13603831d35Sstevel 	if (ecc->flt_in_memory)
13703831d35Sstevel 		ecc_type = PLAT_ECC_MEMORY;
13803831d35Sstevel 	else if (ecc->flt_status & ECC_ECACHE)
13903831d35Sstevel 		ecc_type = PLAT_ECC_ECACHE;
14003831d35Sstevel 
14103831d35Sstevel 	switch (ecc_type) {
14203831d35Sstevel 	case PLAT_ECC_MEMORY: {
14303831d35Sstevel 		/*
14403831d35Sstevel 		 * The unum string is expected to be in this form:
14503831d35Sstevel 		 * "/N0/SB12/P0/B0/D2 J13500, ..."
14603831d35Sstevel 		 * for serengeti.  As this code is shared with Starcat
14703831d35Sstevel 		 * if N is missing then it is set to 0.
14803831d35Sstevel 		 * From that we will extract the bank number, dimm
14903831d35Sstevel 		 * number, and Jnumber.
15003831d35Sstevel 		 */
15103831d35Sstevel 		char *unum_ptr = unum;
15203831d35Sstevel 		char *jno_ptr = ecc_error_data.Jnumber;
15303831d35Sstevel 		int i;
15403831d35Sstevel 
15503831d35Sstevel 		/*
15603831d35Sstevel 		 * On Serengeti we expect to find 'N' in the unum string
15703831d35Sstevel 		 * however, on Starcat 'N' does not appear in the unum string.
15803831d35Sstevel 		 * We do not want this code to break at this point, so the
15903831d35Sstevel 		 * unum_ptr is reset to the start of unum string if we fail
16003831d35Sstevel 		 * to find an 'N'.
16103831d35Sstevel 		 */
16203831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'N');
16303831d35Sstevel 		if (unum_ptr == NULL) {
16403831d35Sstevel 			ecc_error_data.node_no = 0;
16503831d35Sstevel 			unum_ptr = unum;
16603831d35Sstevel 		} else {
16703831d35Sstevel 			unum_ptr++;
16803831d35Sstevel 			ecc_error_data.node_no = stoi(&unum_ptr);
16903831d35Sstevel 		}
17003831d35Sstevel 
17103831d35Sstevel 		/*
17203831d35Sstevel 		 * Now pull out the SB number
17303831d35Sstevel 		 */
17403831d35Sstevel 		unum_ptr = strstr(unum_ptr, "SB");
17503831d35Sstevel 		CHECK_UNUM;
17603831d35Sstevel 		unum_ptr += 2;
17703831d35Sstevel 		board_num = stoi(&unum_ptr);
17803831d35Sstevel 
17903831d35Sstevel 		/*
18003831d35Sstevel 		 * Now pull out the Proc position (relative to the board)
18103831d35Sstevel 		 */
18203831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'P');
18303831d35Sstevel 		CHECK_UNUM;
18403831d35Sstevel 		unum_ptr++;
18503831d35Sstevel 		proc_position = stoi(&unum_ptr);
18603831d35Sstevel 
18703831d35Sstevel 		/*
18803831d35Sstevel 		 * Using the SB number and Proc position we create a FRU
18903831d35Sstevel 		 * cpu id.
19003831d35Sstevel 		 */
19103831d35Sstevel 		ecc_error_data.proc_num =
19235e1ee58SToomas Soome 		    plat_make_fru_cpuid(board_num, 0, proc_position);
19303831d35Sstevel 
19403831d35Sstevel 		/*
19503831d35Sstevel 		 * Now pull out the Memory Bank number
19603831d35Sstevel 		 */
19703831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'B');
19803831d35Sstevel 		CHECK_UNUM;
19903831d35Sstevel 		unum_ptr++;
20003831d35Sstevel 		ecc_error_data.bank_no = (stoi(&unum_ptr) & 0x01);
20103831d35Sstevel 
20203831d35Sstevel 		/*
20303831d35Sstevel 		 * Now pull out the Dimm number within the Memory Bank.
20403831d35Sstevel 		 */
20503831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'D');
20603831d35Sstevel 		CHECK_UNUM;
20703831d35Sstevel 		unum_ptr++;
20803831d35Sstevel 		ecc_error_data.ecache_dimm_no = (stoi(&unum_ptr) & 0x03);
20903831d35Sstevel 
21003831d35Sstevel 		/*
21103831d35Sstevel 		 * Now pull out the J-number.
21203831d35Sstevel 		 */
21303831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'J');
21403831d35Sstevel 		CHECK_UNUM;
21503831d35Sstevel 		unum_ptr++;
21603831d35Sstevel 		for (i = PLAT_ECC_JNUMBER_LENGTH;
21703831d35Sstevel 		    i > 0 && *unum_ptr >= '0' && *unum_ptr <= '9'; i--)
21803831d35Sstevel 			*jno_ptr++ = *unum_ptr++;
21935e1ee58SToomas Soome 		*jno_ptr = '\0';
22003831d35Sstevel 
22103831d35Sstevel 		/*
22203831d35Sstevel 		 * If we get here, we can assume the unum is valid
22303831d35Sstevel 		 */
22403831d35Sstevel 		invalid_unum = 0;
22503831d35Sstevel 		break;
22603831d35Sstevel 	}
22703831d35Sstevel 	case PLAT_ECC_ECACHE: {
22803831d35Sstevel 		/*
22903831d35Sstevel 		 * The unum string is expected to be in this form:
23003831d35Sstevel 		 * "[/N0/][SB|IO]12/P0/E0 J13500, ..."
23103831d35Sstevel 		 * for serengeti.  As this code is shared with Starcat
23203831d35Sstevel 		 * if N is missing then it is set to 0.  IO may only appear
23303831d35Sstevel 		 * on Starcats.  From that we will extract the bank number,
23403831d35Sstevel 		 * dimm number, and Jnumber.
23503831d35Sstevel 		 */
23603831d35Sstevel 		char *unum_ptr = unum;
23703831d35Sstevel 		char *jno_ptr = ecc_error_data.Jnumber;
23803831d35Sstevel 		int is_maxcat = 0;
23903831d35Sstevel 		int i;
24003831d35Sstevel 
24103831d35Sstevel 		/*
24203831d35Sstevel 		 * On Serengeti we expect to find 'N' in the unum string
24303831d35Sstevel 		 * however, on Starcat 'N' does not appear in the unum string.
24403831d35Sstevel 		 * We do not want this code to break at this point, so the
24503831d35Sstevel 		 * unum_ptr is reset to the start of unum string if we fail
24603831d35Sstevel 		 * to find an 'N'.
24703831d35Sstevel 		 */
24803831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'N');
24903831d35Sstevel 		if (unum_ptr == NULL) {
25003831d35Sstevel 			ecc_error_data.node_no = 0;
25103831d35Sstevel 			unum_ptr = unum;
25203831d35Sstevel 		} else {
25303831d35Sstevel 			unum_ptr++;
25403831d35Sstevel 			ecc_error_data.node_no = stoi(&unum_ptr);
25503831d35Sstevel 		}
25603831d35Sstevel 
25703831d35Sstevel 		/*
25803831d35Sstevel 		 * Now pull out the SB/IO number
25903831d35Sstevel 		 */
26003831d35Sstevel 		unum_ptr = strstr(unum_ptr, "SB");
26103831d35Sstevel 		if (unum_ptr == NULL) {
26203831d35Sstevel 
26303831d35Sstevel 			/*
26403831d35Sstevel 			 * Since this is an E$ error, it must have occurred on
26503831d35Sstevel 			 * either a System Board (represented by "SB" in the
26603831d35Sstevel 			 * unum string) or a Maxcat board ("IO" in the unum
26703831d35Sstevel 			 * string).  Since we failed the "SB" check, we'll
26803831d35Sstevel 			 * assume this is a maxcat board.
26903831d35Sstevel 			 */
27003831d35Sstevel 			is_maxcat = 1;
27103831d35Sstevel 			unum_ptr = strstr(unum, "IO");
27203831d35Sstevel 		}
27303831d35Sstevel 		CHECK_UNUM;
27403831d35Sstevel 		unum_ptr += 2;
27503831d35Sstevel 		board_num = stoi(&unum_ptr);
27603831d35Sstevel 
27703831d35Sstevel 		/*
27803831d35Sstevel 		 * Now pull out the Proc position (relative to the board)
27903831d35Sstevel 		 */
28003831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'P');
28103831d35Sstevel 		CHECK_UNUM;
28203831d35Sstevel 		unum_ptr++;
28303831d35Sstevel 		proc_position = stoi(&unum_ptr);
28403831d35Sstevel 
28503831d35Sstevel 		/*
28603831d35Sstevel 		 * Using the SB/IO number, slot 0/1 value (is_maxcat), and
28703831d35Sstevel 		 * proc position, we create the cpu id.
28803831d35Sstevel 		 */
28903831d35Sstevel 		ecc_error_data.proc_num = plat_make_fru_cpuid(board_num,
29003831d35Sstevel 		    is_maxcat, proc_position);
29103831d35Sstevel 
29203831d35Sstevel 		ecc_error_data.bank_no = 0;	/* not used */
29303831d35Sstevel 
29403831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'E');
29503831d35Sstevel 		CHECK_UNUM;
29603831d35Sstevel 		unum_ptr++;
29703831d35Sstevel 		ecc_error_data.ecache_dimm_no = (stoi(&unum_ptr) & 0x01);
29803831d35Sstevel 
29903831d35Sstevel 		unum_ptr = strchr(unum_ptr, 'J');
30003831d35Sstevel 		CHECK_UNUM;
30103831d35Sstevel 		unum_ptr++;
30203831d35Sstevel 		for (i = PLAT_ECC_JNUMBER_LENGTH;
30303831d35Sstevel 		    i > 0 && *unum_ptr >= '0' && *unum_ptr <= '9'; i--)
30403831d35Sstevel 			*jno_ptr++ = *unum_ptr++;
30535e1ee58SToomas Soome 		*jno_ptr = '\0';
30603831d35Sstevel 
30703831d35Sstevel 		/*
30803831d35Sstevel 		 * If we get here, we can assume the unum is valid
30903831d35Sstevel 		 */
31003831d35Sstevel 		invalid_unum = 0;
31103831d35Sstevel 		break;
31203831d35Sstevel 	}
31303831d35Sstevel 	default:
31403831d35Sstevel 		/*
31503831d35Sstevel 		 * Unknown error
31603831d35Sstevel 		 */
31703831d35Sstevel 		break;
31803831d35Sstevel 	}
31903831d35Sstevel 
32003831d35Sstevel 	/*
32103831d35Sstevel 	 * This is where CHECK_UNUM goes when it finds an error
32203831d35Sstevel 	 */
32303831d35Sstevel 
32403831d35Sstevel 	if (ECC_SYND_DATA_BEGIN <= synd_code &&
32503831d35Sstevel 	    synd_code < ECC_SYND_ECC_BEGIN) {
32603831d35Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
32703831d35Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_DATA;
32803831d35Sstevel 		ecc_error_data.databit_no = synd_code;
32903831d35Sstevel 	} else if (ECC_SYND_ECC_BEGIN <= synd_code &&
33003831d35Sstevel 	    synd_code < ECC_SYND_MTAG_BEGIN) {
33103831d35Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
33203831d35Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_ECC;
33303831d35Sstevel 		ecc_error_data.databit_no = synd_code - ECC_SYND_ECC_BEGIN;
33403831d35Sstevel 	} else if (ECC_SYND_MTAG_BEGIN <= synd_code &&
33503831d35Sstevel 	    synd_code < ECC_SYND_MECC_BEGIN) {
33603831d35Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
33703831d35Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_MTAG_D;
33803831d35Sstevel 		ecc_error_data.databit_no = synd_code - ECC_SYND_MTAG_BEGIN;
33903831d35Sstevel 	} else if (ECC_SYND_MECC_BEGIN <= synd_code &&
34003831d35Sstevel 	    synd_code < ECC_SYND_M2) {
34103831d35Sstevel 		ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE;
34203831d35Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_MTAG_E;
34303831d35Sstevel 		ecc_error_data.databit_no = synd_code - ECC_SYND_MECC_BEGIN;
34403831d35Sstevel 	} else {
34503831d35Sstevel 		switch (synd_code) {
34603831d35Sstevel 		case ECC_SYND_M2:
34703831d35Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M2;
34803831d35Sstevel 			break;
34903831d35Sstevel 		case ECC_SYND_M3:
35003831d35Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M3;
35103831d35Sstevel 			break;
35203831d35Sstevel 		case ECC_SYND_M4:
35303831d35Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M4;
35403831d35Sstevel 			break;
35503831d35Sstevel 		case ECC_SYND_M:
35603831d35Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_M;
35703831d35Sstevel 			break;
35803831d35Sstevel 		default:
35903831d35Sstevel 			ecc_error_data.error_type = PLAT_ERROR_TYPE_UNK;
36003831d35Sstevel 			break;
36103831d35Sstevel 		}
36203831d35Sstevel 		ecc_error_data.databit_type = PLAT_BIT_TYPE_MULTI;
36303831d35Sstevel 		ecc_error_data.databit_no = 0; /* not used */
36403831d35Sstevel 	}
36503831d35Sstevel 
36603831d35Sstevel #ifdef DEBUG
36703831d35Sstevel 	if (invalid_unum &&
36803831d35Sstevel 	    (ecc_error_data.error_code != PLAT_ERROR_CODE_UE) &&
36903831d35Sstevel 	    unum && *unum)
37003831d35Sstevel 		cmn_err(CE_WARN, "Unexpected unum string format: %s\n", unum);
37103831d35Sstevel #endif
37203831d35Sstevel 
37303831d35Sstevel 	/*
37403831d35Sstevel 	 * Send this data off as a mailbox message to the SC.
37503831d35Sstevel 	 */
37603831d35Sstevel 	(void) plat_send_ecc_mailbox_msg(PLAT_ECC_ERROR_MESSAGE,
37703831d35Sstevel 	    &ecc_error_data);
37803831d35Sstevel }
37903831d35Sstevel 
38003831d35Sstevel /*
38103831d35Sstevel  * The unum string for memory is expected to be in this form:
38203831d35Sstevel  * "[/N0/]SB12/P0/B0/D2 [J13500]"
38303831d35Sstevel  * Or if the unum was generated as the result of a UE:
38403831d35Sstevel  * "[/N0/]SB12/P0/B0 [J13500, ...]"
38503831d35Sstevel  * From that we will extract the board number, processor position,
38603831d35Sstevel  * bank number and jnumber.
38703831d35Sstevel  *
38803831d35Sstevel  * Return (1) for an invalid unum string.  If the unum is for an
38903831d35Sstevel  * individual DIMM and there is no jnumber, jnumber will be set
39003831d35Sstevel  * to -1 and the caller can decide if the unum is valid.  This
39103831d35Sstevel  * is because Serengeti does not have jnumbers for bank unums
39203831d35Sstevel  * which may be used to create DIMM unums (e.g. for acquiring
39303831d35Sstevel  * DIMM serial ids).
39403831d35Sstevel  */
39503831d35Sstevel 
39603831d35Sstevel int
parse_unum_memory(char * unum,int * board,int * pos,int * bank,int * dimm,int * jnumber)39703831d35Sstevel parse_unum_memory(char *unum, int *board, int *pos, int *bank, int *dimm,
39803831d35Sstevel     int *jnumber)
39903831d35Sstevel {
40003831d35Sstevel 	char *c;
40103831d35Sstevel 
40203831d35Sstevel 	if ((c = strstr(unum, "SB")) == NULL)
40303831d35Sstevel 		return (1);
40403831d35Sstevel 	c += 2;
40503831d35Sstevel 	*board = (uint8_t)stoi(&c);
40603831d35Sstevel 
40703831d35Sstevel 	if (*c++ != '/' || *c++ != 'P')
40803831d35Sstevel 		return (1);
40903831d35Sstevel 	*pos = stoi(&c);
41003831d35Sstevel 
41103831d35Sstevel 	if (*c++ != '/' || *c++ != 'B')
41203831d35Sstevel 		return (1);
41303831d35Sstevel 	*bank = stoi(&c);
41403831d35Sstevel 
41503831d35Sstevel 	if ((c = strchr(c, 'D')) == NULL) {
41603831d35Sstevel 		*dimm = -1;
41703831d35Sstevel 		*jnumber = 0;
41803831d35Sstevel 		return (0);
41903831d35Sstevel 	}
42003831d35Sstevel 	c++;
42103831d35Sstevel 	*dimm = stoi(&c);
42203831d35Sstevel 
42303831d35Sstevel 	if ((c = strchr(c, 'J')) == NULL) {
42403831d35Sstevel 		*jnumber = -1;
42503831d35Sstevel 		return (0);
42603831d35Sstevel 	}
42703831d35Sstevel 
42803831d35Sstevel 	c++;
42903831d35Sstevel 	*jnumber = (uint16_t)stoi(&c);
43003831d35Sstevel 
43103831d35Sstevel 	return (0);
43203831d35Sstevel }
43303831d35Sstevel 
43403831d35Sstevel /*
43503831d35Sstevel  * The unum string for ecache is expected to be in this form:
43603831d35Sstevel  * "[/N0/][SB|IO]12/P0/E0 J13500, ..."
43703831d35Sstevel  * From that we will extract the board number, processor position and
43803831d35Sstevel  * junmber.
43903831d35Sstevel  *
44003831d35Sstevel  * return (1) for any invalid unum string.
44103831d35Sstevel  */
44203831d35Sstevel static int
parse_unum_ecache(char * unum,int * board,int * pos,int * jnumber,int * maxcat)44303831d35Sstevel parse_unum_ecache(char *unum, int *board, int *pos, int *jnumber, int *maxcat)
44403831d35Sstevel {
44503831d35Sstevel 	char *c;
44603831d35Sstevel 
44703831d35Sstevel 	if ((c = strstr(unum, "SB")) == NULL) {
44803831d35Sstevel 		/*
44903831d35Sstevel 		 * Since this is an E$ error, it must have occurred on
45003831d35Sstevel 		 * either a System Board (represented by "SB" in the
45103831d35Sstevel 		 * unum string) or a Maxcat board ("IO" in the unum
45203831d35Sstevel 		 * string).
45303831d35Sstevel 		 */
45403831d35Sstevel 		if ((c = strstr(unum, "IO")) == NULL)
45503831d35Sstevel 			return (1);
45603831d35Sstevel 		*maxcat = 1;
45703831d35Sstevel 	}
45803831d35Sstevel 
45903831d35Sstevel 	c += 2;
46003831d35Sstevel 	*board = (uint8_t)stoi(&c);
46103831d35Sstevel 
46203831d35Sstevel 	if (*c++ != '/' || *c++ != 'P')
46303831d35Sstevel 		return (1);
46403831d35Sstevel 	*pos = stoi(&c);
46503831d35Sstevel 
46603831d35Sstevel 	if ((c = strchr(c, 'J')) == NULL)
46703831d35Sstevel 		return (1);
46803831d35Sstevel 
46903831d35Sstevel 	c++;
47003831d35Sstevel 	*jnumber = (uint16_t)stoi(&c);
47103831d35Sstevel 
47203831d35Sstevel 	return (0);
47303831d35Sstevel }
47403831d35Sstevel 
47503831d35Sstevel /* The following array maps the error to its corresponding set */
47603831d35Sstevel static int plat_ecc_e2d_map[PLAT_ECC_ERROR2_NUMVALS] = {
47703831d35Sstevel 	PLAT_ECC_ERROR2_NONE,			/* 0x00 */
47803831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L2_XXC,		/* 0x01 */
47903831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L2_XXU,		/* 0x02 */
48003831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L3_XXC,		/* 0x03 */
48103831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L3_XXU,		/* 0x04 */
48203831d35Sstevel 	PLAT_ECC_ERROR2_SEND_MEM_ERRS,		/* 0x05 */
48303831d35Sstevel 	PLAT_ECC_ERROR2_SEND_MEM_ERRS,		/* 0x06 */
48403831d35Sstevel 	PLAT_ECC_ERROR2_SEND_MEM_ERRS,		/* 0x07 */
48503831d35Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x08 */
48603831d35Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x09 */
48703831d35Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x0a */
48803831d35Sstevel 	PLAT_ECC_ERROR2_SEND_BUS_ERRS,		/* 0x0b */
48903831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L2_TAG_ERRS,	/* 0x0c */
49003831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L2_TAG_ERRS,	/* 0x0d */
49103831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L3_TAG_ERRS,	/* 0x0e */
49203831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L3_TAG_ERRS,	/* 0x0f */
49303831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L1_PARITY,		/* 0x10 */
49403831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L1_PARITY,		/* 0x11 */
49503831d35Sstevel 	PLAT_ECC_ERROR2_SEND_TLB_PARITY,	/* 0x12 */
49603831d35Sstevel 	PLAT_ECC_ERROR2_SEND_TLB_PARITY,	/* 0x13 */
49703831d35Sstevel 	PLAT_ECC_ERROR2_SEND_IV_ERRS,		/* 0x14 */
49803831d35Sstevel 	PLAT_ECC_ERROR2_SEND_IV_ERRS,		/* 0x15 */
49903831d35Sstevel 	PLAT_ECC_ERROR2_SEND_MTAG_XXC,		/* 0x16 */
50003831d35Sstevel 	PLAT_ECC_ERROR2_SEND_IV_MTAG_XXC,	/* 0x17 */
50103831d35Sstevel 	PLAT_ECC_ERROR2_SEND_L3_XXC,		/* 0x18 */
50203831d35Sstevel 	PLAT_ECC_ERROR2_SEND_PCACHE		/* 0x19 */
50303831d35Sstevel };
50403831d35Sstevel 
50503831d35Sstevel /*
50603831d35Sstevel  * log enhanced error information to SC.
50703831d35Sstevel  */
50803831d35Sstevel void
plat_log_fruid_error2(int msg_type,char * unum,struct async_flt * aflt,plat_ecc_ch_async_flt_t * ecc_ch_flt)50903831d35Sstevel plat_log_fruid_error2(int msg_type, char *unum, struct async_flt *aflt,
51003831d35Sstevel     plat_ecc_ch_async_flt_t *ecc_ch_flt)
51103831d35Sstevel {
51203831d35Sstevel 	plat_ecc_error2_data_t e2d = {0};
51303831d35Sstevel 	int board, pos, bank, dimm, jnumber;
51403831d35Sstevel 	int maxcat = 0;
51503831d35Sstevel 	uint16_t flags;
51603831d35Sstevel 
51703831d35Sstevel 	/* Check the flags */
51803831d35Sstevel 	flags = plat_ecc_e2d_map[msg_type];
51903831d35Sstevel 	if ((ecc_error2_mailbox_flags & flags) == 0)
52003831d35Sstevel 		return;
52103831d35Sstevel 
52203831d35Sstevel 	/* Fill the header */
52303831d35Sstevel 	e2d.ee2d_major_version = PLAT_ECC_ERROR2_VERSION_MAJOR;
52403831d35Sstevel 	e2d.ee2d_minor_version = PLAT_ECC_ERROR2_VERSION_MINOR;
52503831d35Sstevel 	e2d.ee2d_msg_type = PLAT_ECC_ERROR2_MESSAGE;
52603831d35Sstevel 	e2d.ee2d_msg_length = sizeof (plat_ecc_error2_data_t);
52703831d35Sstevel 
52803831d35Sstevel 	/* Fill the data */
52903831d35Sstevel 	if (aflt->flt_in_memory) {
53003831d35Sstevel 		if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
53103831d35Sstevel 		    &jnumber) || (dimm != -1 && jnumber == -1))
53203831d35Sstevel 			return;
53303831d35Sstevel 		/*
53403831d35Sstevel 		 * Using the SB number and Proc position we create a FRU
53503831d35Sstevel 		 * cpu id.
53603831d35Sstevel 		 */
53703831d35Sstevel 		e2d.ee2d_owning_proc = plat_make_fru_cpuid(board, 0, pos);
53803831d35Sstevel 		e2d.ee2d_jnumber = jnumber;
53903831d35Sstevel 		e2d.ee2d_bank_number = bank;
54003831d35Sstevel 	} else if (aflt->flt_status & ECC_ECACHE) {
54103831d35Sstevel 		if (parse_unum_ecache(unum, &board, &pos, &jnumber, &maxcat))
54203831d35Sstevel 			return;
54303831d35Sstevel 		/*
54403831d35Sstevel 		 * Using the SB number and Proc position we create a FRU
54503831d35Sstevel 		 * cpu id.
54603831d35Sstevel 		 */
54703831d35Sstevel 		e2d.ee2d_owning_proc = plat_make_fru_cpuid(board, maxcat, pos);
54803831d35Sstevel 		e2d.ee2d_jnumber = jnumber;
549055d7c80Scarlsonj 		e2d.ee2d_bank_number = (uint8_t)-1;
55003831d35Sstevel 	} else {
55103831d35Sstevel 		/*
55203831d35Sstevel 		 * L1 Cache
55303831d35Sstevel 		 */
55403831d35Sstevel 		e2d.ee2d_owning_proc = aflt->flt_bus_id;
555055d7c80Scarlsonj 		e2d.ee2d_jnumber = (uint16_t)-1;
556055d7c80Scarlsonj 		e2d.ee2d_bank_number = (uint8_t)-1;
55703831d35Sstevel 	}
55803831d35Sstevel 
55903831d35Sstevel 	e2d.ee2d_type = (uint8_t)msg_type;
56003831d35Sstevel 	e2d.ee2d_afar_status = (uint8_t)ecc_ch_flt->ecaf_afar_status;
56103831d35Sstevel 	e2d.ee2d_synd_status = (uint8_t)ecc_ch_flt->ecaf_synd_status;
56203831d35Sstevel 	e2d.ee2d_detecting_proc = aflt->flt_bus_id;
56303831d35Sstevel 	e2d.ee2d_cpu_impl = cpunodes[e2d.ee2d_owning_proc].implementation;
56403831d35Sstevel 	e2d.ee2d_timestamp = aflt->flt_id;
56503831d35Sstevel 	e2d.ee2d_afsr = aflt->flt_stat;
56603831d35Sstevel 	e2d.ee2d_afar = aflt->flt_addr;
56703831d35Sstevel 
56803831d35Sstevel 	e2d.ee2d_sdw_afsr = ecc_ch_flt->ecaf_sdw_afsr;
56903831d35Sstevel 	e2d.ee2d_sdw_afar = ecc_ch_flt->ecaf_sdw_afar;
57003831d35Sstevel 	e2d.ee2d_afsr_ext = ecc_ch_flt->ecaf_afsr_ext;
57103831d35Sstevel 	e2d.ee2d_sdw_afsr_ext = ecc_ch_flt->ecaf_sdw_afsr_ext;
57203831d35Sstevel 
57303831d35Sstevel 	/* Send the message to SC */
57403831d35Sstevel 	(void) plat_send_ecc_mailbox_msg(PLAT_ECC_ERROR2_MESSAGE, &e2d);
57503831d35Sstevel }
57603831d35Sstevel 
57703831d35Sstevel uint8_t ecc_indictment_mailbox_disable = PLAT_ECC_INDICTMENT_OK;
57803831d35Sstevel uint8_t ecc_indictment_mailbox_flags = PLAT_ECC_SEND_DEFAULT_INDICT;
57903831d35Sstevel 
58003831d35Sstevel /*
58103831d35Sstevel  * We log all Solaris indictments of failing hardware.  We pull the system
58203831d35Sstevel  * board number and jnumber out of the unum string, and calculate the cpuid
58303831d35Sstevel  * from some members of the unum string.  The rest of the structure is filled
58403831d35Sstevel  * in through the other arguments.  The data structure is then passed to
58503831d35Sstevel  * plat_ecc_dispatch_task().  This function should only be loaded into memory
58603831d35Sstevel  * or called on platforms that define a plat_send_ecc_mailbox_msg() function.
58703831d35Sstevel  */
58803831d35Sstevel static int
plat_log_fruid_indictment(int msg_type,struct async_flt * aflt,char * unum)58903831d35Sstevel plat_log_fruid_indictment(int msg_type, struct async_flt *aflt, char *unum)
59003831d35Sstevel {
59103831d35Sstevel 	plat_ecc_message_t *wrapperp;
59203831d35Sstevel 	plat_ecc_indict_msg_contents_t *contentsp;
59303831d35Sstevel 	char *unum_ptr;
59403831d35Sstevel 	int is_maxcat = 0;
59503831d35Sstevel 
59603831d35Sstevel 	switch (ecc_indictment_mailbox_disable) {
59703831d35Sstevel 	case (PLAT_ECC_INDICTMENT_OK):
59803831d35Sstevel 	case (PLAT_ECC_INDICTMENT_SUSPECT):
59903831d35Sstevel 		break;
60003831d35Sstevel 	case (PLAT_ECC_INDICTMENT_NO_SEND):
60103831d35Sstevel 	default:
60203831d35Sstevel 		return (ECONNREFUSED);
60303831d35Sstevel 	}
60403831d35Sstevel 
60503831d35Sstevel 	switch (msg_type) {
60603831d35Sstevel 	case (PLAT_ECC_INDICT_DIMM):
60703831d35Sstevel 		if ((ecc_indictment_mailbox_flags &
60803831d35Sstevel 		    PLAT_ECC_SEND_DIMM_INDICT) == 0)
60903831d35Sstevel 			return (ECONNREFUSED);
61003831d35Sstevel 		break;
61103831d35Sstevel 	case (PLAT_ECC_INDICT_ECACHE_CORRECTABLES):
61203831d35Sstevel 		if ((ecc_indictment_mailbox_flags &
61303831d35Sstevel 		    PLAT_ECC_SEND_ECACHE_XXC_INDICT) == 0)
61403831d35Sstevel 			return (ECONNREFUSED);
61503831d35Sstevel 		break;
61603831d35Sstevel 	case (PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE):
61703831d35Sstevel 		if ((ecc_indictment_mailbox_flags &
61803831d35Sstevel 		    PLAT_ECC_SEND_ECACHE_XXU_INDICT) == 0)
61903831d35Sstevel 			return (ECONNREFUSED);
62003831d35Sstevel 		break;
62103831d35Sstevel 	default:
62203831d35Sstevel 		return (ECONNREFUSED);
62303831d35Sstevel 	}
62403831d35Sstevel 
62503831d35Sstevel 	/* LINTED: E_TRUE_LOGICAL_EXPR */
62603831d35Sstevel 	ASSERT(sizeof (plat_ecc_indictment_data_t) == PLAT_ECC_INDICT_SIZE);
62703831d35Sstevel 
62803831d35Sstevel 	wrapperp = (plat_ecc_message_t *)
62903831d35Sstevel 	    kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
63003831d35Sstevel 
63103831d35Sstevel 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
63203831d35Sstevel 	wrapperp->ecc_msg_type = PLAT_ECC_INDICTMENT_MESSAGE;
63303831d35Sstevel 	wrapperp->ecc_msg_len = sizeof (plat_ecc_indictment_data_t);
63403831d35Sstevel 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
63503831d35Sstevel 
63603831d35Sstevel 	contentsp = &(((plat_ecc_indictment_data_t *)
63703831d35Sstevel 	    wrapperp->ecc_msg_data)->msg_contents);
63803831d35Sstevel 
63903831d35Sstevel 	/*
64003831d35Sstevel 	 * Find board_num, jnumber, and proc position from the unum string.
64103831d35Sstevel 	 * Use the board number, is_maxcat, and proc position to calculate
64203831d35Sstevel 	 * cpuid.
64303831d35Sstevel 	 */
64403831d35Sstevel 	unum_ptr = strstr(unum, "SB");
64503831d35Sstevel 	if (unum_ptr == NULL) {
64603831d35Sstevel 		is_maxcat = 1;
64703831d35Sstevel 		unum_ptr = strstr(unum, "IO");
64803831d35Sstevel 		if (unum_ptr == NULL) {
64903831d35Sstevel 			kmem_free(wrapperp->ecc_msg_data,
65003831d35Sstevel 			    wrapperp->ecc_msg_len);
65103831d35Sstevel 			kmem_free(wrapperp, sizeof (plat_ecc_message_t));
65203831d35Sstevel 			return (EINVAL);
65303831d35Sstevel 		}
65403831d35Sstevel 	}
65503831d35Sstevel 	unum_ptr += 2;
65603831d35Sstevel 	contentsp->board_num = (uint8_t)stoi(&unum_ptr);
65703831d35Sstevel 
65803831d35Sstevel 	unum_ptr = strchr(unum_ptr, 'P');
65903831d35Sstevel 	if (unum_ptr == NULL) {
66003831d35Sstevel 		kmem_free(wrapperp->ecc_msg_data, wrapperp->ecc_msg_len);
66103831d35Sstevel 		kmem_free(wrapperp, sizeof (plat_ecc_message_t));
66203831d35Sstevel 		return (EINVAL);
66303831d35Sstevel 	}
66403831d35Sstevel 	unum_ptr++;
66503831d35Sstevel 	contentsp->detecting_proc =
66603831d35Sstevel 	    (uint16_t)plat_make_fru_cpuid(contentsp->board_num, is_maxcat,
66703831d35Sstevel 	    stoi(&unum_ptr));
66803831d35Sstevel 
66903831d35Sstevel 	unum_ptr = strchr(unum_ptr, 'J');
67003831d35Sstevel 	if (unum_ptr == NULL) {
67103831d35Sstevel 		kmem_free(wrapperp->ecc_msg_data, wrapperp->ecc_msg_len);
67203831d35Sstevel 		kmem_free(wrapperp, sizeof (plat_ecc_message_t));
67303831d35Sstevel 		return (EINVAL);
67403831d35Sstevel 	}
67503831d35Sstevel 	unum_ptr++;
67603831d35Sstevel 	contentsp->jnumber = (uint16_t)stoi(&unum_ptr);
67703831d35Sstevel 
67803831d35Sstevel 	/*
67903831d35Sstevel 	 * Fill in the rest of the data
68003831d35Sstevel 	 */
68103831d35Sstevel 	contentsp->version = PLAT_ECC_INDICTMENT_VERSION;
68203831d35Sstevel 	contentsp->indictment_type = msg_type;
68303831d35Sstevel 	contentsp->indictment_uncertain = ecc_indictment_mailbox_disable;
68403831d35Sstevel 	contentsp->syndrome = aflt->flt_synd;
68503831d35Sstevel 	contentsp->afsr = aflt->flt_stat;
68603831d35Sstevel 	contentsp->afar = aflt->flt_addr;
68703831d35Sstevel 
68803831d35Sstevel 	/*
68903831d35Sstevel 	 * Build the solaris_version string:
69003831d35Sstevel 	 */
69103831d35Sstevel 	(void) snprintf(contentsp->solaris_version,
69203831d35Sstevel 	    PLAT_ECC_VERSION_LENGTH, "%s %s", utsname.release, utsname.version);
69303831d35Sstevel 
69403831d35Sstevel 	/*
69503831d35Sstevel 	 * Send the data on to the queuing function
69603831d35Sstevel 	 */
69703831d35Sstevel 	return (plat_ecc_dispatch_task(wrapperp));
69803831d35Sstevel }
69903831d35Sstevel 
70003831d35Sstevel /* The following array maps the indictment to its corresponding set */
70103831d35Sstevel static int plat_ecc_i2d_map[PLAT_ECC_INDICT2_NUMVALS] = {
70203831d35Sstevel 	PLAT_ECC_INDICT2_NONE,			/* 0x00 */
70303831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L2_XXU,		/* 0x01 */
70403831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L2_XXC_SERD,	/* 0x02 */
70503831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L2_TAG_SERD,	/* 0x03 */
70603831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L3_XXU,		/* 0x04 */
70703831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L3_XXC_SERD,	/* 0x05 */
70803831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L3_TAG_SERD,	/* 0x06 */
70903831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L1_SERD,		/* 0x07 */
71003831d35Sstevel 	PLAT_ECC_SEND_INDICT2_L1_SERD,		/* 0x08 */
71103831d35Sstevel 	PLAT_ECC_SEND_INDICT2_TLB_SERD,		/* 0x09 */
71203831d35Sstevel 	PLAT_ECC_SEND_INDICT2_TLB_SERD,		/* 0x0a */
71303831d35Sstevel 	PLAT_ECC_SEND_INDICT2_FPU,		/* 0x0b */
71403831d35Sstevel 	PLAT_ECC_SEND_INDICT2_PCACHE_SERD	/* 0x0c */
71503831d35Sstevel };
71603831d35Sstevel 
71703831d35Sstevel static int
plat_log_fruid_indictment2(int msg_type,struct async_flt * aflt,char * unum)71803831d35Sstevel plat_log_fruid_indictment2(int msg_type, struct async_flt *aflt, char *unum)
71903831d35Sstevel {
72003831d35Sstevel 	plat_ecc_message_t *wrapperp;
72103831d35Sstevel 	plat_ecc_indictment2_data_t *i2d;
72203831d35Sstevel 	int board, pos, jnumber;
72303831d35Sstevel 	int maxcat = 0;
72403831d35Sstevel 	uint16_t flags;
72503831d35Sstevel 
72603831d35Sstevel 	/*
72703831d35Sstevel 	 * If the unum is null or empty, skip parsing it
72803831d35Sstevel 	 */
72903831d35Sstevel 	if (unum && unum[0] != '\0') {
73003831d35Sstevel 		if (parse_unum_ecache(unum, &board, &pos, &jnumber, &maxcat))
73103831d35Sstevel 			return (EINVAL);
73203831d35Sstevel 	}
73303831d35Sstevel 
73403831d35Sstevel 	if ((ecc_indictment_mailbox_disable != PLAT_ECC_INDICTMENT_OK) &&
73503831d35Sstevel 	    (ecc_indictment_mailbox_disable != PLAT_ECC_INDICTMENT_SUSPECT))
73603831d35Sstevel 		return (ECONNREFUSED);
73703831d35Sstevel 
73803831d35Sstevel 	/* Check the flags */
73903831d35Sstevel 	flags = plat_ecc_i2d_map[msg_type];
74003831d35Sstevel 	if ((ecc_indictment2_mailbox_flags & flags) == 0)
74103831d35Sstevel 		return (ECONNREFUSED);
74203831d35Sstevel 
74303831d35Sstevel 	wrapperp = (plat_ecc_message_t *)
74403831d35Sstevel 	    kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
74503831d35Sstevel 
74603831d35Sstevel 	/* Initialize the wrapper */
74703831d35Sstevel 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
74803831d35Sstevel 	wrapperp->ecc_msg_type = PLAT_ECC_INDICTMENT2_MESSAGE;
74903831d35Sstevel 	wrapperp->ecc_msg_len = sizeof (plat_ecc_indictment2_data_t);
75003831d35Sstevel 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
75103831d35Sstevel 
75203831d35Sstevel 	i2d = (plat_ecc_indictment2_data_t *)wrapperp->ecc_msg_data;
75303831d35Sstevel 
75403831d35Sstevel 	/* Fill the header */
75503831d35Sstevel 	i2d->ei2d_major_version = PLAT_ECC_INDICT2_MAJOR_VERSION;
75603831d35Sstevel 	i2d->ei2d_minor_version = PLAT_ECC_INDICT2_MINOR_VERSION;
75703831d35Sstevel 	i2d->ei2d_msg_type = PLAT_ECC_INDICTMENT2_MESSAGE;
75803831d35Sstevel 	i2d->ei2d_msg_length = sizeof (plat_ecc_indictment2_data_t);
75903831d35Sstevel 
76003831d35Sstevel 	/* Fill the data */
76103831d35Sstevel 	if (unum && unum[0] != '\0') {
76203831d35Sstevel 		i2d->ei2d_arraigned_proc = plat_make_fru_cpuid(board, maxcat,
76303831d35Sstevel 		    pos);
76403831d35Sstevel 		i2d->ei2d_board_num = board;
76503831d35Sstevel 		i2d->ei2d_jnumber = jnumber;
76603831d35Sstevel 	} else {
76703831d35Sstevel 		i2d->ei2d_arraigned_proc = aflt->flt_inst;
76803831d35Sstevel 		i2d->ei2d_board_num = (uint8_t)
76903831d35Sstevel 		    plat_make_fru_boardnum(i2d->ei2d_arraigned_proc);
770055d7c80Scarlsonj 		i2d->ei2d_jnumber = (uint16_t)-1;
77103831d35Sstevel 	}
77203831d35Sstevel 
77303831d35Sstevel 	i2d->ei2d_type = msg_type;
77403831d35Sstevel 	i2d->ei2d_uncertain = ecc_indictment_mailbox_disable;
77503831d35Sstevel 	i2d->ei2d_cpu_impl = cpunodes[i2d->ei2d_arraigned_proc].implementation;
77603831d35Sstevel 	i2d->ei2d_timestamp = aflt->flt_id;
77703831d35Sstevel 
77803831d35Sstevel 	/*
77903831d35Sstevel 	 * Send the data on to the queuing function
78003831d35Sstevel 	 */
78103831d35Sstevel 	return (plat_ecc_dispatch_task(wrapperp));
78203831d35Sstevel }
78303831d35Sstevel 
78403831d35Sstevel int
plat_ecc_capability_send(void)78503831d35Sstevel plat_ecc_capability_send(void)
78603831d35Sstevel {
78703831d35Sstevel 	plat_ecc_message_t *wrapperp;
78803831d35Sstevel 	plat_capability_data_t	*cap;
78903831d35Sstevel 	int ver_len;
79003831d35Sstevel 
79103831d35Sstevel 	wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
79203831d35Sstevel 
79303831d35Sstevel 	ver_len = strlen(utsname.release) + strlen(utsname.version) + 2;
79403831d35Sstevel 
79503831d35Sstevel 	/* Initialize the wrapper */
79603831d35Sstevel 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
79703831d35Sstevel 	wrapperp->ecc_msg_type = PLAT_ECC_CAPABILITY_MESSAGE;
79803831d35Sstevel 	wrapperp->ecc_msg_len = sizeof (plat_capability_data_t) + ver_len;
79903831d35Sstevel 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
80003831d35Sstevel 
80103831d35Sstevel 	cap = (plat_capability_data_t *)wrapperp->ecc_msg_data;
80203831d35Sstevel 
80303831d35Sstevel 	/* Fill the header */
80403831d35Sstevel 	cap->capd_major_version = PLAT_ECC_CAP_VERSION_MAJOR;
80503831d35Sstevel 	cap->capd_minor_version = PLAT_ECC_CAP_VERSION_MINOR;
80603831d35Sstevel 	cap->capd_msg_type = PLAT_ECC_CAPABILITY_MESSAGE;
80703831d35Sstevel 	cap->capd_msg_length = wrapperp->ecc_msg_len;
80803831d35Sstevel 
80903831d35Sstevel 	/* Set the default domain capability */
81003831d35Sstevel 	cap->capd_capability = PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT;
81103831d35Sstevel 
81203831d35Sstevel 	/*
81303831d35Sstevel 	 * Build the solaris_version string:
81403831d35Sstevel 	 * utsname.release + " " + utsname.version
81503831d35Sstevel 	 */
81603831d35Sstevel 	(void) snprintf(cap->capd_solaris_version, ver_len, "%s %s",
81703831d35Sstevel 	    utsname.release, utsname.version);
81803831d35Sstevel 
81903831d35Sstevel 	/*
82003831d35Sstevel 	 * Send the data on to the queuing function
82103831d35Sstevel 	 */
82203831d35Sstevel 	return (plat_ecc_dispatch_task(wrapperp));
82303831d35Sstevel }
82403831d35Sstevel 
82503831d35Sstevel int
plat_ecc_capability_sc_get(int type)82603831d35Sstevel plat_ecc_capability_sc_get(int type)
82703831d35Sstevel {
82803831d35Sstevel 	switch (type) {
82903831d35Sstevel 		case PLAT_ECC_ERROR_MESSAGE:
83003831d35Sstevel 			if (ecc_log_fruid_enable &&
83103831d35Sstevel 			    (!(plat_ecc_capability_map_sc &
83203831d35Sstevel 			    PLAT_ECC_CAPABILITY_ERROR2)))
83303831d35Sstevel 				return (1);
83403831d35Sstevel 			break;
83503831d35Sstevel 		case PLAT_ECC_ERROR2_MESSAGE:
83603831d35Sstevel 			if (plat_ecc_capability_map_sc &
83703831d35Sstevel 			    PLAT_ECC_CAPABILITY_ERROR2)
83803831d35Sstevel 				return (1);
83903831d35Sstevel 			break;
84003831d35Sstevel 		case PLAT_ECC_INDICTMENT_MESSAGE:
84103831d35Sstevel 			if (!(plat_ecc_capability_map_sc &
84203831d35Sstevel 			    PLAT_ECC_CAPABILITY_INDICT2) ||
84303831d35Sstevel 			    !(plat_ecc_capability_map_domain &
84403831d35Sstevel 			    PLAT_ECC_CAPABILITY_FMA))
84503831d35Sstevel 				return (1);
84603831d35Sstevel 			break;
84703831d35Sstevel 		case PLAT_ECC_INDICTMENT2_MESSAGE:
84803831d35Sstevel 			if (plat_ecc_capability_map_sc &
84903831d35Sstevel 			    PLAT_ECC_CAPABILITY_INDICT2)
85003831d35Sstevel 				return (1);
85103831d35Sstevel 			break;
85203831d35Sstevel 		case PLAT_ECC_DIMM_SID_MESSAGE:
85303831d35Sstevel 			if (plat_ecc_capability_map_sc &
85403831d35Sstevel 			    PLAT_ECC_CAPABILITY_DIMM_SID)
85503831d35Sstevel 				return (1);
856*e98a9323SToomas Soome 			/* FALLTHROUGH */
85703831d35Sstevel 		default:
85803831d35Sstevel 			return (0);
85903831d35Sstevel 	}
86003831d35Sstevel 	return (0);
86103831d35Sstevel }
86203831d35Sstevel 
86303831d35Sstevel int plat_ecc_cap_sc_set_cnt = 0;
86403831d35Sstevel 
86503831d35Sstevel void
plat_ecc_capability_sc_set(uint32_t cap)86603831d35Sstevel plat_ecc_capability_sc_set(uint32_t cap)
86703831d35Sstevel {
86803831d35Sstevel 	plat_ecc_capability_map_sc = cap;
86903831d35Sstevel 
87003831d35Sstevel 	if (!plat_ecc_cap_sc_set_cnt && (cap & PLAT_ECC_CAPABILITY_DIMM_SID))
87103831d35Sstevel 		if (p2init_sid_cache)
87203831d35Sstevel 			p2init_sid_cache();
87303831d35Sstevel 
87403831d35Sstevel 	plat_ecc_cap_sc_set_cnt++;
87503831d35Sstevel }
87603831d35Sstevel 
87703831d35Sstevel /*
87803831d35Sstevel  * The following table represents mapping between the indictment1 reason
87903831d35Sstevel  * to its type.
88003831d35Sstevel  */
88103831d35Sstevel 
88203831d35Sstevel static plat_ecc_bl_map_t plat_ecc_bl_map_v1[] = {
88303831d35Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT_ECACHE_CORRECTABLES	},
88403831d35Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT_ECACHE_CORRECTABLES	},
88503831d35Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE	},
88603831d35Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE	}
88703831d35Sstevel };
88803831d35Sstevel 
88903831d35Sstevel /*
89003831d35Sstevel  * The following table represents mapping between the indictment2 reason
89103831d35Sstevel  * to its type.
89203831d35Sstevel  */
89303831d35Sstevel 
89403831d35Sstevel static plat_ecc_bl_map_t plat_ecc_bl_map_v2[] = {
89503831d35Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT2_L2_SERD	},
89603831d35Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT2_L3_SERD	},
89703831d35Sstevel 	{ "l2cachedata",	PLAT_ECC_INDICT2_L2_UE		},
89803831d35Sstevel 	{ "l3cachedata",	PLAT_ECC_INDICT2_L3_UE		},
89903831d35Sstevel 	{ "l2cachetag",		PLAT_ECC_INDICT2_L2_TAG_SERD	},
90003831d35Sstevel 	{ "l3cachetag",		PLAT_ECC_INDICT2_L3_TAG_SERD	},
90103831d35Sstevel 	{ "icache",		PLAT_ECC_INDICT2_ICACHE_SERD	},
90203831d35Sstevel 	{ "dcache",		PLAT_ECC_INDICT2_DCACHE_SERD	},
90303831d35Sstevel 	{ "pcache",		PLAT_ECC_INDICT2_PCACHE_SERD	},
90403831d35Sstevel 	{ "itlb",		PLAT_ECC_INDICT2_ITLB_SERD	},
90503831d35Sstevel 	{ "dtlb",		PLAT_ECC_INDICT2_DTLB_SERD	},
90603831d35Sstevel 	{ "fpu",		PLAT_ECC_INDICT2_FPU		}
90703831d35Sstevel };
90803831d35Sstevel 
90903831d35Sstevel /*
91003831d35Sstevel  * The following function returns the indictment type for a given version
91103831d35Sstevel  */
91203831d35Sstevel static int
flt_name_to_msg_type(const char * fault,int indict_version)91303831d35Sstevel flt_name_to_msg_type(const char *fault, int indict_version)
91403831d35Sstevel {
91503831d35Sstevel 	plat_ecc_bl_map_t *mapp;
91603831d35Sstevel 	char *fltnm = "fault.cpu.";
91703831d35Sstevel 	int mapsz;
91803831d35Sstevel 	char *p;
91903831d35Sstevel 	int i;
92003831d35Sstevel 
92103831d35Sstevel 	/* Check if it starts with proper fault name */
92203831d35Sstevel 	if (strncmp(fault, fltnm, strlen(fltnm)) != 0)
92303831d35Sstevel 		return (PLAT_ECC_INDICT_NONE);
92403831d35Sstevel 
92503831d35Sstevel 	fault += strlen(fltnm); /* c = "ultraSPARC-IV.icache" */
92603831d35Sstevel 
92703831d35Sstevel 	/* Skip the cpu type */
92803831d35Sstevel 	if ((p = strchr(fault, '.')) == NULL)
92903831d35Sstevel 		return (PLAT_ECC_INDICT_NONE);
93003831d35Sstevel 
93103831d35Sstevel 	p++;	/* skip the "." */
93203831d35Sstevel 
93303831d35Sstevel 	if (indict_version ==  0) {
93403831d35Sstevel 		mapp = plat_ecc_bl_map_v1;
93503831d35Sstevel 		mapsz = sizeof (plat_ecc_bl_map_v1) /
93603831d35Sstevel 		    sizeof (plat_ecc_bl_map_t);
93703831d35Sstevel 	} else {
93803831d35Sstevel 		mapp = plat_ecc_bl_map_v2;
93903831d35Sstevel 		mapsz = sizeof (plat_ecc_bl_map_v2) /
94003831d35Sstevel 		    sizeof (plat_ecc_bl_map_t);
94103831d35Sstevel 	}
94203831d35Sstevel 	for (i = 0; i < mapsz; i++) {
94303831d35Sstevel 		if (strcmp(p, mapp[i].ebm_reason) == 0) {
94403831d35Sstevel 			return (mapp[i].ebm_type);
94503831d35Sstevel 		}
94603831d35Sstevel 	}
94703831d35Sstevel 	return (PLAT_ECC_INDICT_NONE);
94803831d35Sstevel }
94903831d35Sstevel 
95003831d35Sstevel /*
95103831d35Sstevel  * Blacklisting
95203831d35Sstevel  */
95303831d35Sstevel int
plat_blacklist(int cmd,const char * scheme,nvlist_t * fmri,const char * class)95403831d35Sstevel plat_blacklist(int cmd, const char *scheme, nvlist_t *fmri, const char *class)
95503831d35Sstevel {
95603831d35Sstevel 	struct async_flt aflt;
95703831d35Sstevel 	char *unum;
95803831d35Sstevel 	int msg_type, is_old_indict;
95903831d35Sstevel 
96003831d35Sstevel 	if (fmri == NULL)
96103831d35Sstevel 		return (EINVAL);
96203831d35Sstevel 	if (cmd != BLIOC_INSERT)
96303831d35Sstevel 		return (ENOTSUP);
96403831d35Sstevel 
96503831d35Sstevel 	/*
96603831d35Sstevel 	 * We support both the blacklisting of CPUs via mem-schemed
96703831d35Sstevel 	 * FMRIs that name E$ J-numbers, and CPUs via cpu-schemed FMRIs
96803831d35Sstevel 	 * that name the cpuid.
96903831d35Sstevel 	 */
97003831d35Sstevel 	if (strcmp(scheme, FM_FMRI_SCHEME_MEM) == 0) {
97103831d35Sstevel 		if (nvlist_lookup_string(fmri, FM_FMRI_MEM_UNUM, &unum))
97203831d35Sstevel 			return (EINVAL);
973055d7c80Scarlsonj 		aflt.flt_inst = (uint_t)-1;
97403831d35Sstevel 	} else if (strcmp(scheme, FM_FMRI_SCHEME_CPU) == 0) {
97503831d35Sstevel 		if (nvlist_lookup_uint32(fmri, FM_FMRI_CPU_ID, &aflt.flt_inst))
97603831d35Sstevel 			return (EINVAL);
97703831d35Sstevel 		unum = NULL;
97803831d35Sstevel 	} else {
97903831d35Sstevel 		return (ENOTSUP);
98003831d35Sstevel 	}
98103831d35Sstevel 
98203831d35Sstevel 	/*
98303831d35Sstevel 	 * If the SC cannot handle indictment2, so fall back to old one.
98403831d35Sstevel 	 * Also if the domain does not support FMA, then send only the old one.
98503831d35Sstevel 	 */
98603831d35Sstevel 
98703831d35Sstevel 	is_old_indict = plat_ecc_capability_sc_get(PLAT_ECC_INDICTMENT_MESSAGE);
98803831d35Sstevel 
98903831d35Sstevel 	if (is_old_indict)
99003831d35Sstevel 		msg_type = flt_name_to_msg_type(class, 0);
99103831d35Sstevel 	else
99203831d35Sstevel 		msg_type = flt_name_to_msg_type(class, 1);
99303831d35Sstevel 
99403831d35Sstevel 	if (msg_type == PLAT_ECC_INDICT_NONE)
99503831d35Sstevel 		return (ENOTSUP);
99603831d35Sstevel 
99703831d35Sstevel 	/*
99803831d35Sstevel 	 * The current blacklisting interfaces are designed for a world where
99903831d35Sstevel 	 * the SC is much more involved in the diagnosis and error reporting
100003831d35Sstevel 	 * process than it is in the FMA world.  As such, the existing
100103831d35Sstevel 	 * interfaces want all kinds of information about the error that's
100203831d35Sstevel 	 * triggering the blacklist.  In the FMA world, we don't have access
100303831d35Sstevel 	 * to any of that information by the time we're doing the blacklist,
100403831d35Sstevel 	 * so we fake values.
100503831d35Sstevel 	 */
100603831d35Sstevel 	aflt.flt_id = gethrtime();
100703831d35Sstevel 	aflt.flt_addr = -1;
100803831d35Sstevel 	aflt.flt_stat = -1;
1009055d7c80Scarlsonj 	aflt.flt_synd = (ushort_t)-1;
101003831d35Sstevel 
101103831d35Sstevel 	if (is_old_indict) {
101203831d35Sstevel 		if (unum && unum[0] != '\0')
101303831d35Sstevel 			return (plat_log_fruid_indictment(msg_type, &aflt,
101403831d35Sstevel 			    unum));
101503831d35Sstevel 		else
101603831d35Sstevel 			return (ENOTSUP);
101703831d35Sstevel 	} else {
101803831d35Sstevel 		return (plat_log_fruid_indictment2(msg_type, &aflt, unum));
101903831d35Sstevel 	}
102003831d35Sstevel }
102103831d35Sstevel 
102203831d35Sstevel static kcondvar_t plat_ecc_condvar;
102303831d35Sstevel static kmutex_t plat_ecc_mutex;
102403831d35Sstevel static taskq_t *plat_ecc_taskq;
102503831d35Sstevel 
102603831d35Sstevel /*
102703831d35Sstevel  * plat_ecc_dispatch_task: Dispatch the task on a taskq and wait for the
102803831d35Sstevel  * return value.  We use cv_wait_sig to wait for the return values.  If a
102903831d35Sstevel  * signal interrupts us, we return EINTR.  Otherwise, we return the value
103003831d35Sstevel  * returned by the mailbox functions.
103103831d35Sstevel  *
103203831d35Sstevel  * To avoid overloading the lower-level mailbox routines, we use a taskq
103303831d35Sstevel  * to serialize all messages.  Currently, it is expected that only one
103403831d35Sstevel  * process (fmd) will use this ioctl, so the delay caused by the taskq
103503831d35Sstevel  * should not have much of an effect.
103603831d35Sstevel  */
103703831d35Sstevel int
plat_ecc_dispatch_task(plat_ecc_message_t * msg)103803831d35Sstevel plat_ecc_dispatch_task(plat_ecc_message_t *msg)
103903831d35Sstevel {
104003831d35Sstevel 	int ret;
104103831d35Sstevel 
104203831d35Sstevel 	ASSERT(msg != NULL);
104303831d35Sstevel 	ASSERT(plat_ecc_taskq != NULL);
104403831d35Sstevel 
104503831d35Sstevel 	if (taskq_dispatch(plat_ecc_taskq, plat_ecc_send_msg,
1046fc8ae2ecSToomas Soome 	    (void *)msg, TQ_NOSLEEP) == TASKQID_INVALID) {
104703831d35Sstevel 		kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
104803831d35Sstevel 		kmem_free(msg, sizeof (plat_ecc_message_t));
104903831d35Sstevel 		return (ENOMEM);
105003831d35Sstevel 	}
105103831d35Sstevel 	mutex_enter(&plat_ecc_mutex);
105203831d35Sstevel 
105303831d35Sstevel 	/*
105403831d35Sstevel 	 * It's possible that the taskq function completed before we
105503831d35Sstevel 	 * acquired the mutex.  Check for this first.  If this did not
105603831d35Sstevel 	 * happen, we wait for the taskq function to signal us, or an
105703831d35Sstevel 	 * interrupt.  We also check ecc_msg_status to protect against
105803831d35Sstevel 	 * spurious wakeups from cv_wait_sig.
105903831d35Sstevel 	 */
106003831d35Sstevel 	if (msg->ecc_msg_status == PLAT_ECC_MSG_SENT) {
106103831d35Sstevel 		ret = msg->ecc_msg_ret;
106203831d35Sstevel 		kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
106303831d35Sstevel 		kmem_free(msg, sizeof (plat_ecc_message_t));
106403831d35Sstevel 	} else {
106503831d35Sstevel 		msg->ecc_msg_status = PLAT_ECC_TASK_DISPATCHED;
106603831d35Sstevel 
106703831d35Sstevel 		while ((ret = cv_wait_sig(&plat_ecc_condvar,
106803831d35Sstevel 		    &plat_ecc_mutex)) != 0 &&
106903831d35Sstevel 		    msg->ecc_msg_status == PLAT_ECC_TASK_DISPATCHED)
107003831d35Sstevel 			;
107103831d35Sstevel 
107203831d35Sstevel 		if ((ret == 0) && (msg->ecc_msg_status != PLAT_ECC_MSG_SENT)) {
107303831d35Sstevel 			/* An interrupt was received */
107403831d35Sstevel 			msg->ecc_msg_status = PLAT_ECC_INTERRUPT_RECEIVED;
107503831d35Sstevel 			ret = EINTR;
107603831d35Sstevel 		} else {
107703831d35Sstevel 			ret = msg->ecc_msg_ret;
107803831d35Sstevel 			kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
107903831d35Sstevel 			kmem_free(msg, sizeof (plat_ecc_message_t));
108003831d35Sstevel 		}
108103831d35Sstevel 	}
108203831d35Sstevel 	mutex_exit(&plat_ecc_mutex);
108303831d35Sstevel 	return (ret);
108403831d35Sstevel }
108503831d35Sstevel 
108603831d35Sstevel static void
plat_ecc_send_msg(void * arg)108703831d35Sstevel plat_ecc_send_msg(void *arg)
108803831d35Sstevel {
108903831d35Sstevel 	plat_ecc_message_t *msg = arg;
109003831d35Sstevel 	int ret;
109103831d35Sstevel 
109203831d35Sstevel 	/*
109303831d35Sstevel 	 * Send this data off as a mailbox message to the SC.
109403831d35Sstevel 	 */
109503831d35Sstevel 	ret = plat_send_ecc_mailbox_msg(msg->ecc_msg_type, msg->ecc_msg_data);
109603831d35Sstevel 
109703831d35Sstevel 	mutex_enter(&plat_ecc_mutex);
109803831d35Sstevel 
109903831d35Sstevel 	/*
110003831d35Sstevel 	 * If the dispatching function received an interrupt, don't bother
110103831d35Sstevel 	 * signalling it, and throw away the results.  Otherwise, set the
110203831d35Sstevel 	 * return value and signal the condvar.
110303831d35Sstevel 	 */
110403831d35Sstevel 	if (msg->ecc_msg_status == PLAT_ECC_INTERRUPT_RECEIVED) {
110503831d35Sstevel 		kmem_free(msg->ecc_msg_data, msg->ecc_msg_len);
110603831d35Sstevel 		kmem_free(msg, sizeof (plat_ecc_message_t));
110703831d35Sstevel 	} else {
110803831d35Sstevel 		msg->ecc_msg_ret = ret;
110903831d35Sstevel 		msg->ecc_msg_status = PLAT_ECC_MSG_SENT;
111003831d35Sstevel 		cv_broadcast(&plat_ecc_condvar);
111103831d35Sstevel 	}
111203831d35Sstevel 
111303831d35Sstevel 	mutex_exit(&plat_ecc_mutex);
111403831d35Sstevel }
111503831d35Sstevel 
111603831d35Sstevel void
plat_ecc_init(void)111703831d35Sstevel plat_ecc_init(void)
111803831d35Sstevel {
111903831d35Sstevel 	int	bd;
112003831d35Sstevel 
112103831d35Sstevel 	mutex_init(&plat_ecc_mutex, NULL, MUTEX_DEFAULT, NULL);
112203831d35Sstevel 	cv_init(&plat_ecc_condvar, NULL, CV_DEFAULT, NULL);
112303831d35Sstevel 	plat_ecc_taskq = taskq_create("plat_ecc_taskq", 1, minclsyspri,
112403831d35Sstevel 	    PLAT_ECC_TASKQ_MIN, PLAT_ECC_TASKQ_MAX, TASKQ_PREPOPULATE);
112503831d35Sstevel 	ASSERT(plat_ecc_taskq != NULL);
112603831d35Sstevel 
112703831d35Sstevel 	for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
112803831d35Sstevel 		mutex_init(&domain_dimm_sids[bd].pdsb_lock,
112903831d35Sstevel 		    NULL, MUTEX_DEFAULT, NULL);
113003831d35Sstevel 	}
113103831d35Sstevel 
113203831d35Sstevel }
1133