/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1990-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2019 Peter Tribble. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Set the following variable in /etc/system to tell the kernel * not to shutdown the machine if the temperature reaches * the Thermal Warning limit. */ int oven_test = 0; /* * To indicate if the prom has the property of "thermal-interrupt". */ static int thermal_interrupt_enabled = 0; /* * adb debug_sysio_errs to 1 if you don't want your system to panic on * sbus ue errors. adb sysio_err_flag to 0 if you don't want your system * to check for sysio errors at all. */ int sysio_err_flag = 1; uint_t debug_sysio_errs = 0; /* * bto_cnt = number of bus errors and timeouts allowed within bto_secs * use /etc/system to change the bto_cnt to a very large number if * it's a problem! */ int bto_secs = 10; int bto_cnt = 10; static uint_t sysio_ue_intr(struct sbus_soft_state *softsp); static uint_t sysio_ce_intr(struct sbus_soft_state *softsp); static uint_t sbus_err_intr(struct sbus_soft_state *softsp); static void sysio_log_ce_err(struct async_flt *ecc, char *unum); static void sysio_log_ue_err(struct async_flt *ecc, char *unum); static void sbus_clear_intr(struct sbus_soft_state *softsp, uint64_t *pafsr); static void sbus_log_error(struct sbus_soft_state *softsp, uint64_t *pafsr, uint64_t *pafar, ushort_t id, ushort_t inst, int cleared, on_trap_data_t *ontrap_data); static int sbus_check_bto(struct sbus_soft_state *softsp); static void sbus_log_csr_error(struct async_flt *aflt, char *unum); static uint_t sbus_ctrl_ecc_err(struct sbus_soft_state *softsp); static uint_t sysio_dis_err(struct sbus_soft_state *softsp); static uint_t sysio_init_err(struct sbus_soft_state *softsp); static uint_t sysio_thermal_warn_intr(struct sbus_soft_state *softsp); static int sbus_pil[] = {SBUS_UE_PIL, SBUS_CE_PIL, SBUS_ERR_PIL, SBUS_PF_PIL, SBUS_THERMAL_PIL, SBUS_PM_PIL}; int sysio_err_init(struct sbus_soft_state *softsp, caddr_t address) { if (sysio_err_flag == 0) { cmn_err(CE_CONT, "Warning: sysio errors not initialized\n"); return (DDI_SUCCESS); } /* * Get the address of the already mapped-in sysio/sbus error registers. * Simply add each registers offset to the already mapped in address * that was retrieved from the device node's "address" property, * and passed as an argument to this function. * * Define a macro for the pointer arithmetic ... */ #define REG_ADDR(b, o) (uint64_t *)((caddr_t)(b) + (o)) softsp->sysio_ecc_reg = REG_ADDR(address, OFF_SYSIO_ECC_REGS); softsp->sysio_ue_reg = REG_ADDR(address, OFF_SYSIO_UE_REGS); softsp->sysio_ce_reg = REG_ADDR(address, OFF_SYSIO_CE_REGS); softsp->sbus_err_reg = REG_ADDR(address, OFF_SBUS_ERR_REGS); #undef REG_ADDR /* * create the interrupt-priorities property if it doesn't * already exist to provide a hint as to the PIL level for * our interrupt. */ { int len; if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS, "interrupt-priorities", &len) != DDI_PROP_SUCCESS) { /* Create the interrupt-priorities property. */ (void) ddi_prop_update_int_array(DDI_DEV_T_NONE, softsp->dip, "interrupt-priorities", (int *)sbus_pil, sizeof (sbus_pil) / sizeof (int)); } } (void) ddi_add_intr(softsp->dip, 0, NULL, NULL, (uint_t (*)())sysio_ue_intr, (caddr_t)softsp); (void) ddi_add_intr(softsp->dip, 1, NULL, NULL, (uint_t (*)())sysio_ce_intr, (caddr_t)softsp); (void) ddi_add_intr(softsp->dip, 2, NULL, NULL, (uint_t (*)())sbus_err_intr, (caddr_t)softsp); /* * If the thermal-interrupt property is in place, * then register the thermal warning interrupt handler and * program its mapping register */ thermal_interrupt_enabled = ddi_getprop(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS, "thermal-interrupt", -1); if (thermal_interrupt_enabled == 1) { (void) ddi_add_intr(softsp->dip, 4, NULL, NULL, (uint_t (*)())sysio_thermal_warn_intr, (caddr_t)softsp); } bus_func_register(BF_TYPE_UE, (busfunc_t)sbus_ctrl_ecc_err, softsp); bus_func_register(BF_TYPE_ERRDIS, (busfunc_t)sysio_dis_err, softsp); (void) sysio_init_err(softsp); return (DDI_SUCCESS); } int sysio_err_resume_init(struct sbus_soft_state *softsp) { (void) sysio_init_err(softsp); return (DDI_SUCCESS); } int sysio_err_uninit(struct sbus_soft_state *softsp) { /* remove the interrupts from the interrupt list */ (void) sysio_dis_err(softsp); ddi_remove_intr(softsp->dip, 0, NULL); ddi_remove_intr(softsp->dip, 1, NULL); ddi_remove_intr(softsp->dip, 2, NULL); if (thermal_interrupt_enabled == 1) { ddi_remove_intr(softsp->dip, 4, NULL); } bus_func_unregister(BF_TYPE_UE, (busfunc_t)sbus_ctrl_ecc_err, softsp); bus_func_unregister(BF_TYPE_ERRDIS, (busfunc_t)sysio_dis_err, softsp); return (DDI_SUCCESS); } static uint_t sysio_init_err(struct sbus_soft_state *softsp) { volatile uint64_t tmp_mondo_vec, tmpreg; volatile uint64_t *mondo_vec_reg; uint_t cpu_id, acpu_id; acpu_id = intr_dist_cpuid(); /* * Program the mondo vector accordingly. This MUST be the * last thing we do. Once we program the mondo, the device * may begin to interrupt. Store it in the hardware reg. */ mondo_vec_reg = (uint64_t *)(softsp->intr_mapping_reg + UE_ECC_MAPREG); cpu_id = acpu_id; tmp_mondo_vec = (cpu_id << INTERRUPT_CPU_FIELD) | INTERRUPT_VALID; *mondo_vec_reg = tmp_mondo_vec; mondo_vec_reg = (uint64_t *)(softsp->intr_mapping_reg + CE_ECC_MAPREG); cpu_id = acpu_id; tmp_mondo_vec = (cpu_id << INTERRUPT_CPU_FIELD) | INTERRUPT_VALID; *mondo_vec_reg = tmp_mondo_vec; mondo_vec_reg = (uint64_t *)(softsp->intr_mapping_reg + SBUS_ERR_MAPREG); cpu_id = acpu_id; tmp_mondo_vec = (cpu_id << INTERRUPT_CPU_FIELD) | INTERRUPT_VALID; *mondo_vec_reg = tmp_mondo_vec; if (thermal_interrupt_enabled == 1) { mondo_vec_reg = (softsp->intr_mapping_reg + THERMAL_MAPREG); cpu_id = acpu_id; tmp_mondo_vec = (cpu_id << INTERRUPT_CPU_FIELD) | INTERRUPT_VALID; *mondo_vec_reg = tmp_mondo_vec; } /* Flush store buffers */ tmpreg = *softsp->sbus_ctrl_reg; /* * XXX - This may already be set by the OBP. */ tmpreg = SYSIO_APCKEN; *softsp->sysio_ctrl_reg |= tmpreg; tmpreg = (SECR_ECC_EN | SECR_UE_INTEN | SECR_CE_INTEN); *softsp->sysio_ecc_reg = tmpreg; tmpreg = SB_CSR_ERRINT_EN; *softsp->sbus_err_reg |= tmpreg; /* Initialize timeout/bus error counter */ softsp->bto_timestamp = 0; softsp->bto_ctr = 0; return (0); } static uint_t sysio_dis_err(struct sbus_soft_state *softsp) { volatile uint64_t tmpreg; volatile uint64_t *mondo_vec_reg, *clear_vec_reg; *softsp->sysio_ctrl_reg &= ~SYSIO_APCKEN; *softsp->sysio_ecc_reg = 0; *softsp->sbus_err_reg &= ~SB_CSR_ERRINT_EN; /* Flush store buffers */ tmpreg = *softsp->sbus_ctrl_reg; #ifdef lint tmpreg = tmpreg; #endif /* Unmap mapping registers */ mondo_vec_reg = (softsp->intr_mapping_reg + UE_ECC_MAPREG); clear_vec_reg = (softsp->clr_intr_reg + UE_ECC_CLEAR); *mondo_vec_reg = 0; *clear_vec_reg = 0; mondo_vec_reg = (softsp->intr_mapping_reg + CE_ECC_MAPREG); clear_vec_reg = (softsp->clr_intr_reg + CE_ECC_CLEAR); *mondo_vec_reg = 0; *clear_vec_reg = 0; mondo_vec_reg = (softsp->intr_mapping_reg + SBUS_ERR_MAPREG); clear_vec_reg = (softsp->clr_intr_reg + SBUS_ERR_CLEAR); *mondo_vec_reg = 0; *clear_vec_reg = 0; /* Flush store buffers */ tmpreg = *softsp->sbus_ctrl_reg; return (BF_NONE); } /* * Gather information about the error into an async_flt structure, and then * enqueue the error for reporting and processing and panic. */ static uint_t sysio_ue_intr(struct sbus_soft_state *softsp) { volatile uint64_t t_afsr; volatile uint64_t t_afar; volatile uint64_t *ue_reg, *afar_reg, *clear_reg; struct async_flt ecc; uint64_t offset; /* * Disable all further sbus errors, for this sbus instance, for * what is guaranteed to be a fatal error. And grab any other cpus. */ (void) sysio_dis_err(softsp); /* disabled sysio errors */ /* * Then read and clear the afsr/afar and clear interrupt regs. */ ue_reg = (uint64_t *)softsp->sysio_ue_reg; t_afsr = *ue_reg; afar_reg = (uint64_t *)ue_reg + 1; t_afar = *afar_reg; *ue_reg = t_afsr; clear_reg = (softsp->clr_intr_reg + UE_ECC_CLEAR); *clear_reg = 0; /* * The AFSR DW_OFFSET field contains the offset of the doubleword with * the ECC error relative to the 64-byte aligned PA. We multiply by 8 * to convert to a byte offset, and then add this to flt_addr. */ offset = ((t_afsr & SB_UE_AFSR_OFF) >> SB_UE_DW_SHIFT) * 8; bzero(&ecc, sizeof (ecc)); ecc.flt_id = gethrtime(); ecc.flt_stat = t_afsr; ecc.flt_addr = P2ALIGN(t_afar, 64) + offset; ecc.flt_func = sysio_log_ue_err; ecc.flt_bus_id = softsp->upa_id; ecc.flt_inst = ddi_get_instance(softsp->dip); ecc.flt_status = ECC_IOBUS; ecc.flt_in_memory = (pf_is_memory(t_afar >> MMU_PAGESHIFT)) ? 1: 0; ecc.flt_class = BUS_FAULT; ecc.flt_panic = (debug_sysio_errs == 0); errorq_dispatch(ue_queue, &ecc, sizeof (ecc), ecc.flt_panic); /* * If the UE is in memory and fatal, save the fault info so the * panic code will know to check for copyback errors. */ if (ecc.flt_panic && ecc.flt_in_memory) panic_aflt = ecc; /* * We must also check for other bus UE errors, and panic if * any fatal ones are detected at this point. */ if (bus_func_invoke(BF_TYPE_UE) == BF_FATAL) ecc.flt_panic = 1; if (ecc.flt_panic) cmn_err(CE_PANIC, "Fatal Sbus%d UE Error", ecc.flt_inst); return (DDI_INTR_CLAIMED); } /* * callback logging function from the common error handling code */ static void sysio_log_ue_err(struct async_flt *ecc, char *unum) { uint64_t t_afsr = ecc->flt_stat; uint64_t t_afar = ecc->flt_addr; ushort_t id = ecc->flt_bus_id; ushort_t inst = ecc->flt_inst; if (t_afsr & SB_UE_AFSR_P_PIO) { cmn_err(CE_WARN, "SBus%d UE Primary Error from PIO: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_UE_AFSR_P_DRD) { cmn_err(CE_WARN, "SBus%d UE Primary Error DMA read: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if (t_afsr & SB_UE_AFSR_P_DWR) { cmn_err(CE_WARN, "SBus%d UE Primary Error DVMA write: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } /* * We should never hit the secondary error panics. */ if (t_afsr & SB_UE_AFSR_S_PIO) { cmn_err(CE_WARN, "SBus%d UE Secondary Error from PIO: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_UE_AFSR_S_DRD) { cmn_err(CE_WARN, "SBus%d UE Secondary Error DMA read: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if (t_afsr & SB_UE_AFSR_S_DWR) { cmn_err(CE_WARN, "SBus%d UE Secondary Error DMA write: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if ((debug_sysio_errs) || (aft_verbose)) { (void) read_ecc_data(ecc, 1, 0); cmn_err(CE_CONT, "\tOffset 0x%x, Size %d, UPA MID 0x%x\n", (uint32_t)((t_afsr & SB_UE_AFSR_OFF) >> SB_UE_DW_SHIFT), (uint32_t)((t_afsr & SB_UE_AFSR_SIZE) >> SB_UE_SIZE_SHIFT), (uint32_t)((t_afsr & SB_UE_AFSR_MID) >> SB_UE_MID_SHIFT)); } } /* * gather the information about the error, plus a pointer to * the callback logging function, and call the generic ce_error handler. */ static uint_t sysio_ce_intr(struct sbus_soft_state *softsp) { volatile uint64_t t_afsr; volatile uint64_t t_afar; volatile uint64_t *afar_reg, *clear_reg, *ce_reg; struct async_flt ecc; uint64_t offset; ce_reg = (uint64_t *)softsp->sysio_ce_reg; t_afsr = *ce_reg; afar_reg = (uint64_t *)ce_reg + 1; t_afar = *afar_reg; *ce_reg = t_afsr; clear_reg = (softsp->clr_intr_reg + CE_ECC_CLEAR); *clear_reg = 0; /* * The AFSR DW_OFFSET field contains the offset of the doubleword with * the ECC error relative to the 64-byte aligned PA. We multiply by 8 * to convert to a byte offset, and then add this to flt_addr. */ offset = ((t_afsr & SB_UE_AFSR_OFF) >> SB_UE_DW_SHIFT) * 8; bzero(&ecc, sizeof (ecc)); ecc.flt_id = gethrtime(); ecc.flt_stat = t_afsr; ecc.flt_addr = P2ALIGN(t_afar, 64) + offset; ecc.flt_func = sysio_log_ce_err; ecc.flt_bus_id = softsp->upa_id; ecc.flt_inst = ddi_get_instance(softsp->dip); ecc.flt_status = ECC_IOBUS; ecc.flt_synd = (ushort_t)((t_afsr & SB_CE_AFSR_SYND) >> SB_CE_SYND_SHIFT); ecc.flt_in_memory = (pf_is_memory(t_afar >> MMU_PAGESHIFT)) ? 1: 0; ecc.flt_class = BUS_FAULT; ce_scrub(&ecc); errorq_dispatch(ce_queue, &ecc, sizeof (ecc), ERRORQ_ASYNC); return (DDI_INTR_CLAIMED); } /* * callback logging function from the common error handling code */ static void sysio_log_ce_err(struct async_flt *ecc, char *unum) { uint64_t t_afsr = ecc->flt_stat; uint64_t t_afar = ecc->flt_addr; ushort_t id = ecc->flt_bus_id; ushort_t inst = ecc->flt_inst; int ce_verbose = ce_verbose_memory; char *syndrome_str = "!\tSyndrome 0x%x, Offset 0x%x, Size %d, " "UPA MID 0x%x\n"; if ((!ce_verbose_memory) && (!debug_sysio_errs)) return; if (t_afsr & SB_CE_AFSR_P_PIO) { char *fmtstr = "!SBus%d CE Primary Error from PIO: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d\n"; if ((debug_sysio_errs) || (ce_verbose > 1)) fmtstr++; cmn_err(CE_CONT, fmtstr, inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_CE_AFSR_P_DRD) { char *fmtstr = "!SBus%d CE Primary Error DMA read: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s " "Id %d\n"; if ((debug_sysio_errs) || (ce_verbose > 1)) fmtstr++; cmn_err(CE_CONT, fmtstr, inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if (t_afsr & SB_CE_AFSR_P_DWR) { char *fmtstr = "!SBus%d CE Primary Error DMA write: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s Id %d\n"; if ((debug_sysio_errs) || (ce_verbose > 1)) fmtstr++; cmn_err(CE_CONT, fmtstr, inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if (t_afsr & SB_CE_AFSR_S_PIO) { char *fmtstr = "!SBus%d CE Secondary Error from PIO: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d\n"; if ((debug_sysio_errs) || (ce_verbose > 1)) fmtstr++; cmn_err(CE_CONT, fmtstr, inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_CE_AFSR_S_DRD) { char *fmtstr = "!SBus%d CE Secondary Error DMA read: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s " "Id %d\n"; if ((debug_sysio_errs) || (ce_verbose > 1)) fmtstr++; cmn_err(CE_CONT, fmtstr, inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if (t_afsr & SB_CE_AFSR_S_DWR) { char *fmtstr = "!SBus%d CE Secondary Error DMA write: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x MemMod %s " "Id %d\n"; if ((debug_sysio_errs) || (ce_verbose > 1)) fmtstr++; cmn_err(CE_CONT, fmtstr, inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, unum, id); } if ((debug_sysio_errs) || (ce_verbose > 1)) syndrome_str++; cmn_err(CE_CONT, syndrome_str, (uint32_t)((t_afsr & SB_CE_AFSR_SYND) >> SB_CE_SYND_SHIFT), (uint32_t)((t_afsr & SB_CE_AFSR_OFF) >> SB_CE_OFFSET_SHIFT), (uint32_t)((t_afsr & SB_CE_AFSR_SIZE) >> SB_CE_SIZE_SHIFT), (uint32_t)((t_afsr & SB_CE_AFSR_MID) >> SB_CE_MID_SHIFT)); } static uint_t sbus_err_intr(struct sbus_soft_state *softsp) { volatile uint64_t t_afsr; volatile uint64_t t_afar; ushort_t id, inst; int cleared = 0; volatile uint64_t *afar_reg; on_trap_data_t *otp = softsp->ontrap_data; t_afsr = *softsp->sbus_err_reg; afar_reg = (uint64_t *)softsp->sbus_err_reg + 1; t_afar = *afar_reg; if (otp == NULL || !(otp->ot_prot & OT_DATA_ACCESS)) { sbus_clear_intr(softsp, (uint64_t *)&t_afsr); cleared = 1; } id = (ushort_t)softsp->upa_id; inst = (ushort_t)ddi_get_instance(softsp->dip); if (debug_sysio_errs) { if (otp != NULL && (otp->ot_prot & OT_DATA_ACCESS)) otp->ot_trap |= OT_DATA_ACCESS; if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); cmn_err(CE_CONT, "SBus%d Error: AFSR 0x%08x.%08x " "AFAR 0x%08x.%08x Id %d\n", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); debug_enter("sbus_err_intr"); } else { sbus_log_error(softsp, (uint64_t *)&t_afsr, (uint64_t *)&t_afar, id, inst, cleared, otp); } if (!cleared) { sbus_clear_intr(softsp, (uint64_t *)&t_afsr); } return (DDI_INTR_CLAIMED); } static void sbus_clear_intr(struct sbus_soft_state *softsp, uint64_t *pafsr) { volatile uint64_t *clear_reg; *softsp->sbus_err_reg = *pafsr; clear_reg = (softsp->clr_intr_reg + SBUS_ERR_CLEAR); *clear_reg = 0; } static void sbus_log_error(struct sbus_soft_state *softsp, uint64_t *pafsr, uint64_t *pafar, ushort_t id, ushort_t inst, int cleared, on_trap_data_t *otp) { uint64_t t_afsr; uint64_t t_afar; int level = CE_WARN; t_afsr = *pafsr; t_afar = *pafar; if (t_afsr & SB_AFSR_P_LE) { if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); cmn_err(CE_PANIC, "SBus%d Primary Error Late PIO: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_AFSR_P_TO) { if (otp != NULL && (otp->ot_prot & OT_DATA_ACCESS)) { otp->ot_trap |= OT_DATA_ACCESS; return; } if (sbus_check_bto(softsp)) { if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); level = CE_PANIC; } cmn_err(level, "SBus%d Primary Error Timeout: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_AFSR_P_BERR) { if (otp != NULL && (otp->ot_prot & OT_DATA_ACCESS)) { otp->ot_trap |= OT_DATA_ACCESS; return; } if (sbus_check_bto(softsp)) { if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); level = CE_PANIC; } cmn_err(level, "SBus%d Primary Error Bus Error: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d\n", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_AFSR_S_LE) { if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); cmn_err(CE_PANIC, "SBus%d Secondary Late PIO Error: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_AFSR_S_TO) { if (sbus_check_bto(softsp)) { if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); level = CE_PANIC; } cmn_err(level, "SBus%d Secondary Timeout Error: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } if (t_afsr & SB_AFSR_S_BERR) { if (sbus_check_bto(softsp)) { if (!cleared) sbus_clear_intr(softsp, (uint64_t *)&t_afsr); level = CE_PANIC; } cmn_err(level, "SBus%d Secondary Bus Error: " "AFSR 0x%08x.%08x AFAR 0x%08x.%08x Id %d", inst, (uint32_t)(t_afsr>>32), (uint32_t)t_afsr, (uint32_t)(t_afar>>32), (uint32_t)t_afar, id); } } static int sbus_check_bto(struct sbus_soft_state *softsp) { hrtime_t now = gethrtime(); /* high PIL safe */ hrtime_t diff = now - softsp->bto_timestamp; if (diff > ((hrtime_t)bto_secs * NANOSEC) || diff < 0LL) { /* * Reset error counter as this bus error has occurred * after more than bto_secs duration. */ softsp->bto_timestamp = now; softsp->bto_ctr = 0; } if (softsp->bto_ctr++ >= bto_cnt) return (1); return (0); } static uint_t sbus_ctrl_ecc_err(struct sbus_soft_state *softsp) { uint64_t t_sb_csr; ushort_t id, inst; t_sb_csr = *softsp->sbus_ctrl_reg; id = (ushort_t)softsp->upa_id; inst = (ushort_t)ddi_get_instance(softsp->dip); if (debug_sysio_errs) { cmn_err(CE_CONT, "sbus_ctrl_ecc_error: SBus%d Control Reg " "0x%016llx Id %d\n", inst, (u_longlong_t)t_sb_csr, id); } if (t_sb_csr & (SB_CSR_DPERR_S14|SB_CSR_DPERR_S13|SB_CSR_DPERR_S3| SB_CSR_DPERR_S2|SB_CSR_DPERR_S1|SB_CSR_DPERR_S0|SB_CSR_PIO_PERRS)) { struct async_flt aflt; *softsp->sbus_ctrl_reg = t_sb_csr; /* clear error bits */ bzero(&aflt, sizeof (aflt)); aflt.flt_id = gethrtime(); aflt.flt_stat = t_sb_csr; aflt.flt_func = sbus_log_csr_error; aflt.flt_bus_id = id; aflt.flt_inst = inst; aflt.flt_status = ECC_IOBUS; aflt.flt_class = BUS_FAULT; aflt.flt_panic = 1; errorq_dispatch(ue_queue, &aflt, sizeof (aflt), aflt.flt_panic); return (BF_FATAL); } return (BF_NONE); } /*ARGSUSED*/ static void sbus_log_csr_error(struct async_flt *aflt, char *unum) { uint64_t t_sb_csr = aflt->flt_stat; uint_t id = aflt->flt_bus_id; uint_t inst = aflt->flt_inst; /* * Print out SBus error information. */ if (t_sb_csr & SB_CSR_DPERR_S14) { cmn_err(CE_WARN, "SBus%d Slot 14 DVMA Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_DPERR_S13) { cmn_err(CE_WARN, "SBus%d Slot 13 DVMA Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_DPERR_S3) { cmn_err(CE_WARN, "SBus%d Slot 3 DVMA Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_DPERR_S2) { cmn_err(CE_WARN, "SBus%d Slot 2 DVMA Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_DPERR_S1) { cmn_err(CE_WARN, "SBus%d Slot 1 DVMA Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_DPERR_S0) { cmn_err(CE_WARN, "SBus%d Slot 0 DVMA Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S15) { cmn_err(CE_WARN, "SBus%d Slot 15 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S14) { cmn_err(CE_WARN, "SBus%d Slot 14 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S13) { cmn_err(CE_WARN, "SBus%d Slot 13 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S3) { cmn_err(CE_WARN, "SBus%d Slot 3 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S2) { cmn_err(CE_WARN, "SBus%d Slot 2 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S1) { cmn_err(CE_WARN, "SBus%d Slot 1 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } if (t_sb_csr & SB_CSR_PPERR_S0) { cmn_err(CE_WARN, "SBus%d Slot 0 PIO Parity Error: AFSR 0x%08x.%08x Id %d", inst, (uint32_t)(t_sb_csr>>32), (uint32_t)t_sb_csr, id); } } /* * Sysio Thermal Warning interrupt handler */ static uint_t sysio_thermal_warn_intr(struct sbus_soft_state *softsp) { volatile uint64_t *clear_reg; volatile uint64_t tmp_mondo_vec; volatile uint64_t *mondo_vec_reg; const char thermal_warn_msg[] = "Severe over-temperature condition detected!"; /* * Take off the Thermal Warning interrupt and * remove its interrupt handler. */ mondo_vec_reg = (softsp->intr_mapping_reg + THERMAL_MAPREG); tmp_mondo_vec = *mondo_vec_reg; tmp_mondo_vec &= ~INTERRUPT_VALID; *mondo_vec_reg = tmp_mondo_vec; ddi_remove_intr(softsp->dip, 4, NULL); clear_reg = (softsp->clr_intr_reg + THERMAL_CLEAR); *clear_reg = 0; if (oven_test) { cmn_err(CE_NOTE, "OVEN TEST: %s", thermal_warn_msg); return (DDI_INTR_CLAIMED); } cmn_err(CE_WARN, "%s", thermal_warn_msg); cmn_err(CE_WARN, "Powering down..."); do_shutdown(); /* * just in case do_shutdown() fails */ (void) timeout((void(*)(void *))power_down, NULL, thermal_powerdown_delay * hz); return (DDI_INTR_CLAIMED); }