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