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