1f8d2de6bSjchu /* 2f8d2de6bSjchu * CDDL HEADER START 3f8d2de6bSjchu * 4f8d2de6bSjchu * The contents of this file are subject to the terms of the 5*01689544Sjchu * Common Development and Distribution License (the "License"). 6*01689544Sjchu * You may not use this file except in compliance with the License. 7f8d2de6bSjchu * 8f8d2de6bSjchu * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9f8d2de6bSjchu * or http://www.opensolaris.org/os/licensing. 10f8d2de6bSjchu * See the License for the specific language governing permissions 11f8d2de6bSjchu * and limitations under the License. 12f8d2de6bSjchu * 13f8d2de6bSjchu * When distributing Covered Code, include this CDDL HEADER in each 14f8d2de6bSjchu * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15f8d2de6bSjchu * If applicable, add the following below this CDDL HEADER, with the 16f8d2de6bSjchu * fields enclosed by brackets "[]" replaced with your own identifying 17f8d2de6bSjchu * information: Portions Copyright [yyyy] [name of copyright owner] 18f8d2de6bSjchu * 19f8d2de6bSjchu * CDDL HEADER END 20f8d2de6bSjchu */ 21f8d2de6bSjchu /* 22*01689544Sjchu * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23f8d2de6bSjchu * Use is subject to license terms. 24f8d2de6bSjchu */ 25f8d2de6bSjchu 26f8d2de6bSjchu #pragma ident "%Z%%M% %I% %E% SMI" 27f8d2de6bSjchu 28f8d2de6bSjchu /* 29f8d2de6bSjchu * sun4v Fire Error Handling 30f8d2de6bSjchu */ 31f8d2de6bSjchu 32f8d2de6bSjchu #include <sys/types.h> 33f8d2de6bSjchu #include <sys/ddi.h> 34f8d2de6bSjchu #include <sys/sunddi.h> 35f8d2de6bSjchu #include <sys/fm/protocol.h> 36f8d2de6bSjchu #include <sys/fm/util.h> 37f8d2de6bSjchu #include <sys/membar.h> 38f8d2de6bSjchu #include "px_obj.h" 39f8d2de6bSjchu #include "px_err.h" 40f8d2de6bSjchu 41f8d2de6bSjchu static uint_t px_err_common_intr(px_fault_t *fault_p, px_rc_err_t *epkt); 42f8d2de6bSjchu static int px_err_check_severity(px_t *px_p, ddi_fm_error_t *derr, 43f8d2de6bSjchu px_rc_err_t *epkt, int caller); 44f8d2de6bSjchu 45f8d2de6bSjchu static int px_cb_check_errors(dev_info_t *dip, ddi_fm_error_t *derr, 46f8d2de6bSjchu px_rc_err_t *epkt, int caller); 47f8d2de6bSjchu static int px_mmu_check_errors(dev_info_t *dip, ddi_fm_error_t *derr, 48f8d2de6bSjchu px_rc_err_t *epkt, int caller); 49f8d2de6bSjchu static int px_pcie_check_errors(dev_info_t *dip, ddi_fm_error_t *derr, 50f8d2de6bSjchu px_rc_err_t *epkt, int caller); 51f8d2de6bSjchu 52f8d2de6bSjchu /* 53f8d2de6bSjchu * px_err_cb_intr: 54f8d2de6bSjchu * Interrupt handler for the Host Bus Block. 55f8d2de6bSjchu */ 56f8d2de6bSjchu uint_t 57f8d2de6bSjchu px_err_cb_intr(caddr_t arg) 58f8d2de6bSjchu { 59f8d2de6bSjchu px_fault_t *fault_p = (px_fault_t *)arg; 60f8d2de6bSjchu px_rc_err_t *epkt = (px_rc_err_t *)fault_p->px_intr_payload; 61f8d2de6bSjchu 62f8d2de6bSjchu if (epkt != NULL) { 63f8d2de6bSjchu return (px_err_common_intr(fault_p, epkt)); 64f8d2de6bSjchu } 65f8d2de6bSjchu 66f8d2de6bSjchu return (DDI_INTR_UNCLAIMED); 67f8d2de6bSjchu } 68f8d2de6bSjchu 69f8d2de6bSjchu /* 70f8d2de6bSjchu * px_err_dmc_pec_intr: 71f8d2de6bSjchu * Interrupt handler for the DMC/PEC block. 72f8d2de6bSjchu */ 73f8d2de6bSjchu uint_t 74f8d2de6bSjchu px_err_dmc_pec_intr(caddr_t arg) 75f8d2de6bSjchu { 76f8d2de6bSjchu px_fault_t *fault_p = (px_fault_t *)arg; 77f8d2de6bSjchu px_rc_err_t *epkt = (px_rc_err_t *)fault_p->px_intr_payload; 78f8d2de6bSjchu 79f8d2de6bSjchu if (epkt != NULL) { 80f8d2de6bSjchu return (px_err_common_intr(fault_p, epkt)); 81f8d2de6bSjchu } 82f8d2de6bSjchu 83f8d2de6bSjchu return (DDI_INTR_UNCLAIMED); 84f8d2de6bSjchu } 85f8d2de6bSjchu 86f8d2de6bSjchu /* 87f8d2de6bSjchu * px_err_handle: 88f8d2de6bSjchu * Common function called by trap, mondo and fabric intr. 89f8d2de6bSjchu * This function is more meaningful in sun4u implementation. Kept 90f8d2de6bSjchu * to mirror sun4u call stack. 91f8d2de6bSjchu * o check for safe access 92f8d2de6bSjchu * 93f8d2de6bSjchu * @param px_p leaf in which to check access 94f8d2de6bSjchu * @param derr fm err data structure to be updated 95f8d2de6bSjchu * @param caller PX_TRAP_CALL | PX_INTR_CALL 96f8d2de6bSjchu * @param chkjbc whether to handle hostbus registers (ignored) 97f8d2de6bSjchu * @return err PX_OK | PX_NONFATAL | 98f8d2de6bSjchu * PX_FATAL_GOS | PX_FATAL_HW | PX_STUCK_FATAL 99f8d2de6bSjchu */ 100f8d2de6bSjchu /* ARGSUSED */ 101f8d2de6bSjchu int 102f8d2de6bSjchu px_err_handle(px_t *px_p, ddi_fm_error_t *derr, int caller, 103f8d2de6bSjchu boolean_t chkxbc) 104f8d2de6bSjchu { 105f8d2de6bSjchu /* check for safe access */ 106f8d2de6bSjchu px_err_safeacc_check(px_p, derr); 107f8d2de6bSjchu 108f8d2de6bSjchu return (DDI_FM_OK); 109f8d2de6bSjchu } 110f8d2de6bSjchu 111f8d2de6bSjchu /* 112f8d2de6bSjchu * px_err_common_intr: 113f8d2de6bSjchu * Interrupt handler for the JBC/DMC/PEC block. 114f8d2de6bSjchu * o lock 115f8d2de6bSjchu * o create derr 116f8d2de6bSjchu * o check safe access 117f8d2de6bSjchu * o px_err_check_severiy(epkt) 118f8d2de6bSjchu * o dispatch 119f8d2de6bSjchu * o Idle intr state 120f8d2de6bSjchu * o unlock 121f8d2de6bSjchu * o handle error: fatal? fm_panic() : return INTR_CLAIMED) 122f8d2de6bSjchu */ 123f8d2de6bSjchu static uint_t 124f8d2de6bSjchu px_err_common_intr(px_fault_t *fault_p, px_rc_err_t *epkt) 125f8d2de6bSjchu { 126f8d2de6bSjchu px_t *px_p = DIP_TO_STATE(fault_p->px_fh_dip); 127f8d2de6bSjchu dev_info_t *rpdip = px_p->px_dip; 128f8d2de6bSjchu int err, ret; 129f8d2de6bSjchu ddi_fm_error_t derr; 130f8d2de6bSjchu 131*01689544Sjchu mutex_enter(&px_p->px_fm_mutex); 132f8d2de6bSjchu 133f8d2de6bSjchu /* Create the derr */ 134f8d2de6bSjchu bzero(&derr, sizeof (ddi_fm_error_t)); 135f8d2de6bSjchu derr.fme_version = DDI_FME_VERSION; 136f8d2de6bSjchu derr.fme_ena = fm_ena_generate(epkt->stick, FM_ENA_FMT1); 137f8d2de6bSjchu derr.fme_flag = DDI_FM_ERR_UNEXPECTED; 138f8d2de6bSjchu 139f8d2de6bSjchu /* Basically check for safe access */ 140f8d2de6bSjchu (void) px_err_handle(px_p, &derr, PX_INTR_CALL, B_FALSE); 141f8d2de6bSjchu 142f8d2de6bSjchu /* Check the severity of this error */ 143f8d2de6bSjchu err = px_err_check_severity(px_p, &derr, epkt, PX_INTR_CALL); 144f8d2de6bSjchu 145f8d2de6bSjchu /* check for error severity */ 146f8d2de6bSjchu ret = ndi_fm_handler_dispatch(rpdip, NULL, &derr); 147f8d2de6bSjchu 148f8d2de6bSjchu /* Set the intr state to idle for the leaf that received the mondo */ 149f8d2de6bSjchu if (px_lib_intr_setstate(rpdip, fault_p->px_fh_sysino, 150f8d2de6bSjchu INTR_IDLE_STATE) != DDI_SUCCESS) { 151*01689544Sjchu mutex_exit(&px_p->px_fm_mutex); 152f8d2de6bSjchu return (DDI_INTR_UNCLAIMED); 153f8d2de6bSjchu } 154f8d2de6bSjchu 155*01689544Sjchu mutex_exit(&px_p->px_fm_mutex); 156f8d2de6bSjchu 157f8d2de6bSjchu if ((err & (PX_FATAL_GOS | PX_FATAL_SW)) || (ret == DDI_FM_FATAL)) 1584fbb58f6Sjchu PX_FM_PANIC("Fatal System Bus Error has occurred\n"); 159f8d2de6bSjchu 160f8d2de6bSjchu return (DDI_INTR_CLAIMED); 161f8d2de6bSjchu } 162f8d2de6bSjchu 163f8d2de6bSjchu /* 164f8d2de6bSjchu * px_err_check_severity: 165f8d2de6bSjchu * Check the severity of the fire error based the epkt received 166f8d2de6bSjchu * 167f8d2de6bSjchu * @param px_p leaf in which to take the snap shot. 168f8d2de6bSjchu * @param derr fm err in which the ereport is to be based on 169f8d2de6bSjchu * @param epkt epkt recevied from HV 170f8d2de6bSjchu */ 171f8d2de6bSjchu static int 172f8d2de6bSjchu px_err_check_severity(px_t *px_p, ddi_fm_error_t *derr, px_rc_err_t *epkt, 173f8d2de6bSjchu int caller) 174f8d2de6bSjchu { 175f8d2de6bSjchu px_pec_t *pec_p = px_p->px_pec_p; 176f8d2de6bSjchu dev_info_t *dip = px_p->px_dip; 177f8d2de6bSjchu int err = 0; 178f8d2de6bSjchu 179f8d2de6bSjchu /* Cautious access error handling */ 180f8d2de6bSjchu if (derr->fme_flag == DDI_FM_ERR_EXPECTED) { 181f8d2de6bSjchu if (caller == PX_TRAP_CALL) { 182f8d2de6bSjchu /* 183f8d2de6bSjchu * for ddi_caut_get treat all events as nonfatal 184f8d2de6bSjchu * The trampoline will set err_ena = 0, 185f8d2de6bSjchu * err_status = NONFATAL. 186f8d2de6bSjchu */ 187f8d2de6bSjchu derr->fme_status = DDI_FM_NONFATAL; 188f8d2de6bSjchu } else { 189f8d2de6bSjchu /* 190f8d2de6bSjchu * For ddi_caut_put treat all events as nonfatal. Here 191f8d2de6bSjchu * we have the handle and can call ndi_fm_acc_err_set(). 192f8d2de6bSjchu */ 193f8d2de6bSjchu derr->fme_status = DDI_FM_NONFATAL; 194f8d2de6bSjchu ndi_fm_acc_err_set(pec_p->pec_acc_hdl, derr); 195f8d2de6bSjchu } 196f8d2de6bSjchu } 197f8d2de6bSjchu 198f8d2de6bSjchu switch (epkt->rc_descr.block) { 199f8d2de6bSjchu case BLOCK_HOSTBUS: 200f8d2de6bSjchu err = px_cb_check_errors(dip, derr, epkt, caller); 201f8d2de6bSjchu break; 202f8d2de6bSjchu case BLOCK_MMU: 203f8d2de6bSjchu err = px_mmu_check_errors(dip, derr, epkt, caller); 204f8d2de6bSjchu break; 205f8d2de6bSjchu case BLOCK_INTR: 206f8d2de6bSjchu err = PX_NONFATAL; 207f8d2de6bSjchu break; 208f8d2de6bSjchu case BLOCK_PCIE: 209f8d2de6bSjchu err = px_pcie_check_errors(dip, derr, epkt, caller); 210f8d2de6bSjchu break; 211f8d2de6bSjchu default: 212f8d2de6bSjchu err = PX_ERR_UNKNOWN; 213f8d2de6bSjchu } 214f8d2de6bSjchu 215f8d2de6bSjchu return (err); 216f8d2de6bSjchu } 217f8d2de6bSjchu 218f8d2de6bSjchu /* ARGSUSED */ 219f8d2de6bSjchu static int 220f8d2de6bSjchu px_cb_check_errors(dev_info_t *dip, ddi_fm_error_t *derr, 221f8d2de6bSjchu px_rc_err_t *epkt, int caller) 222f8d2de6bSjchu { 223f8d2de6bSjchu int fme_flag = derr->fme_flag; 224f8d2de6bSjchu boolean_t is_safeacc; 225f8d2de6bSjchu int ret, err = 0; 226f8d2de6bSjchu 227f8d2de6bSjchu is_safeacc = (fme_flag == DDI_FM_ERR_EXPECTED) || 228f8d2de6bSjchu (fme_flag == DDI_FM_ERR_PEEK) || 229f8d2de6bSjchu (fme_flag == DDI_FM_ERR_POKE); 230f8d2de6bSjchu 231f8d2de6bSjchu /* block/op/phase/cond/dir/flag... */ 232f8d2de6bSjchu switch (epkt->rc_descr.op) { 233f8d2de6bSjchu case OP_PIO: 2348c334881Sjchu err = PX_NONFATAL; 235f8d2de6bSjchu /* check handle if affected memory address is captured */ 236f8d2de6bSjchu if (epkt->rc_descr.M != 0) { 237f8d2de6bSjchu ret = px_handle_lookup(dip, ACC_HANDLE, 238f8d2de6bSjchu derr->fme_ena, (void *)epkt->addr); 239f8d2de6bSjchu } 240f8d2de6bSjchu if (ret == DDI_FM_FATAL) 2418c334881Sjchu err |= PX_FATAL_GOS; 242f8d2de6bSjchu break; 243f8d2de6bSjchu 244f8d2de6bSjchu case OP_DMA: 245f8d2de6bSjchu switch (epkt->rc_descr.phase) { 246f8d2de6bSjchu case PH_ADDR: 2478c334881Sjchu err = PX_FATAL_GOS; 248f8d2de6bSjchu break; 249f8d2de6bSjchu case PH_DATA: 250f8d2de6bSjchu if (epkt->rc_descr.cond == CND_UE) { 2518c334881Sjchu err = PX_FATAL_GOS; 252f8d2de6bSjchu break; 253f8d2de6bSjchu } 254f8d2de6bSjchu 2558c334881Sjchu err = PX_NONFATAL; 256f8d2de6bSjchu if (epkt->rc_descr.M == 1) { 257f8d2de6bSjchu ret = px_handle_lookup(dip, DMA_HANDLE, 258f8d2de6bSjchu derr->fme_ena, (void *)epkt->addr); 259f8d2de6bSjchu if (ret == DDI_FM_FATAL) 2608c334881Sjchu err |= PX_FATAL_GOS; 261f8d2de6bSjchu } 262f8d2de6bSjchu break; 263f8d2de6bSjchu default: 264f8d2de6bSjchu DBG(DBG_ERR_INTR, dip, "Unexpected epkt"); 2658c334881Sjchu err = PX_FATAL_GOS; 266f8d2de6bSjchu break; 267f8d2de6bSjchu } 268f8d2de6bSjchu break; 269f8d2de6bSjchu case OP_UNKNOWN: 2708c334881Sjchu err = PX_NONFATAL; 271*01689544Sjchu if ((epkt->rc_descr.cond == CND_UNMAP) || 272*01689544Sjchu (epkt->rc_descr.cond == CND_UE) || 273*01689544Sjchu (epkt->rc_descr.cond == CND_INT) || 274*01689544Sjchu (epkt->rc_descr.cond == CND_ILL)) 275*01689544Sjchu err |= PX_FATAL_GOS; 276*01689544Sjchu 277f8d2de6bSjchu if (epkt->rc_descr.M == 1) { 278f8d2de6bSjchu int ret1, ret2; 2798c334881Sjchu 280f8d2de6bSjchu ret1 = px_handle_lookup(dip, DMA_HANDLE, derr->fme_ena, 281f8d2de6bSjchu (void *)epkt->addr); 282f8d2de6bSjchu ret2 = px_handle_lookup(dip, ACC_HANDLE, derr->fme_ena, 283f8d2de6bSjchu (void *)epkt->addr); 2848c334881Sjchu 2858c334881Sjchu if (ret1 == DDI_FM_FATAL || ret2 == DDI_FM_FATAL) 2868c334881Sjchu err |= PX_FATAL_GOS; 287f8d2de6bSjchu } 288f8d2de6bSjchu break; 289f8d2de6bSjchu 290f8d2de6bSjchu case OP_RESERVED: 291f8d2de6bSjchu default: 292f8d2de6bSjchu DBG(DBG_ERR_INTR, NULL, "Unrecognized JBC error."); 2938c334881Sjchu err = PX_FATAL_GOS; 294f8d2de6bSjchu break; 295f8d2de6bSjchu } 296f8d2de6bSjchu 297f8d2de6bSjchu /* 298f8d2de6bSjchu * For protected safe access, consider PX_FATAL_GOS as the only 299f8d2de6bSjchu * exception for px to take immediate panic, else, treat errors 300f8d2de6bSjchu * as nonfatal. 301f8d2de6bSjchu */ 302f8d2de6bSjchu if (is_safeacc) { 303f8d2de6bSjchu if (err & PX_FATAL_GOS) 304f8d2de6bSjchu err = PX_FATAL_GOS; 305f8d2de6bSjchu else 306f8d2de6bSjchu err = PX_NONFATAL; 307f8d2de6bSjchu } 308f8d2de6bSjchu 309f8d2de6bSjchu return (err); 310f8d2de6bSjchu } 311f8d2de6bSjchu 312f8d2de6bSjchu /* ARGSUSED */ 313f8d2de6bSjchu static int 314f8d2de6bSjchu px_mmu_check_errors(dev_info_t *dip, ddi_fm_error_t *derr, 315f8d2de6bSjchu px_rc_err_t *epkt, int caller) 316f8d2de6bSjchu { 317f8d2de6bSjchu int ret, err = 0; 318f8d2de6bSjchu 319f8d2de6bSjchu switch (epkt->rc_descr.op) { 320f8d2de6bSjchu case OP_BYPASS: /* nonfatal */ 321f8d2de6bSjchu case OP_XLAT: /* nonfatal, stuck-fatal, fatal-reset */ 322f8d2de6bSjchu case OP_TBW: /* nonfatal, stuck-fatal */ 323f8d2de6bSjchu err = PX_NONFATAL; 324f8d2de6bSjchu break; 325f8d2de6bSjchu default: 326f8d2de6bSjchu err = PX_ERR_UNKNOWN; 327f8d2de6bSjchu break; 328f8d2de6bSjchu } 329f8d2de6bSjchu 330f8d2de6bSjchu if ((epkt->rc_descr.D != 0) || (epkt->rc_descr.M != 0)) { 331f8d2de6bSjchu ret = px_handle_lookup(dip, DMA_HANDLE, derr->fme_ena, 332f8d2de6bSjchu (void *)epkt->addr); 3338c334881Sjchu if (ret == DDI_FM_FATAL) 3348c334881Sjchu err |= PX_FATAL_GOS; 3358c334881Sjchu else 3368c334881Sjchu err |= PX_NONFATAL; 3378c334881Sjchu } else 3388c334881Sjchu err |= PX_NONFATAL; 339f8d2de6bSjchu 340f8d2de6bSjchu return (err); 341f8d2de6bSjchu } 342f8d2de6bSjchu 343f8d2de6bSjchu /* ARGSUSED */ 344f8d2de6bSjchu static int 345f8d2de6bSjchu px_pcie_check_errors(dev_info_t *dip, ddi_fm_error_t *derr, 346f8d2de6bSjchu px_rc_err_t *epkt, int caller) 347f8d2de6bSjchu { 3488bc7d88aSet int ret = PX_NONFATAL; 349f8d2de6bSjchu px_pec_err_t *pec = (px_pec_err_t *)epkt; 350f8d2de6bSjchu 351f8d2de6bSjchu switch (pec->pec_descr.dir) { 352f8d2de6bSjchu case DIR_INGRESS: 353f8d2de6bSjchu case DIR_EGRESS: 354f8d2de6bSjchu case DIR_LINK: 3558bc7d88aSet ret |= PX_FABRIC_ERR_SEV(pec->ue_reg_status, 3568bc7d88aSet px_fabric_die_rc_ue, px_fabric_die_rc_ue_gos); 3578bc7d88aSet ret |= PX_FABRIC_ERR_SEV(pec->ue_reg_status, 3588bc7d88aSet px_fabric_die_rc_ce, px_fabric_die_rc_ce_gos); 359f8d2de6bSjchu break; 360f8d2de6bSjchu default: 3618bc7d88aSet ret = PX_ERR_UNKNOWN; 362f8d2de6bSjchu break; 363f8d2de6bSjchu } 364f8d2de6bSjchu 365f8d2de6bSjchu return (ret); 366f8d2de6bSjchu } 367