1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * Fault Management for Device Drivers 31*7c478bd9Sstevel@tonic-gate * 32*7c478bd9Sstevel@tonic-gate * Device drivers wishing to participate in fault management may do so by 33*7c478bd9Sstevel@tonic-gate * first initializing their fault management state and capabilties via 34*7c478bd9Sstevel@tonic-gate * ddi_fm_init(). If the system supports the requested FM capabilities, 35*7c478bd9Sstevel@tonic-gate * the IO framework will intialize FM state and return a bit mask of the 36*7c478bd9Sstevel@tonic-gate * requested capabilities. 37*7c478bd9Sstevel@tonic-gate * 38*7c478bd9Sstevel@tonic-gate * If the system does not support the requested FM capabilities, 39*7c478bd9Sstevel@tonic-gate * the device driver must behave in accordance with the programming semantics 40*7c478bd9Sstevel@tonic-gate * defined below for the capabilities returned from ddi_fm_init(). 41*7c478bd9Sstevel@tonic-gate * ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be 42*7c478bd9Sstevel@tonic-gate * called from detach(9E) to perform FM clean-up. 43*7c478bd9Sstevel@tonic-gate * 44*7c478bd9Sstevel@tonic-gate * Driver Fault Management Capabilities 45*7c478bd9Sstevel@tonic-gate * 46*7c478bd9Sstevel@tonic-gate * DDI_FM_NOT_CAPABLE 47*7c478bd9Sstevel@tonic-gate * 48*7c478bd9Sstevel@tonic-gate * This is the default fault management capability for drivers. Drivers 49*7c478bd9Sstevel@tonic-gate * that implement no fault management capabilites or do not participate 50*7c478bd9Sstevel@tonic-gate * in fault management activities have their FM capability bitmask set 51*7c478bd9Sstevel@tonic-gate * to 0. 52*7c478bd9Sstevel@tonic-gate * 53*7c478bd9Sstevel@tonic-gate * DDI_FM_EREPORT_CAPABLE 54*7c478bd9Sstevel@tonic-gate * 55*7c478bd9Sstevel@tonic-gate * When this capability bit is set, drivers are expected to generate error 56*7c478bd9Sstevel@tonic-gate * report events via ddi_ereport_post() for the associated faults 57*7c478bd9Sstevel@tonic-gate * that are diagnosed by the IO fault manager DE. ddi_ereport_post() 58*7c478bd9Sstevel@tonic-gate * may be called in any context subject to the constraints specified 59*7c478bd9Sstevel@tonic-gate * by the interrupt iblock cookie returned during initialization. 60*7c478bd9Sstevel@tonic-gate * 61*7c478bd9Sstevel@tonic-gate * Error reports resulting from hardware component specific and common IO 62*7c478bd9Sstevel@tonic-gate * fault and driver defects must be accompanied by an Eversholt fault 63*7c478bd9Sstevel@tonic-gate * tree (.eft) by the Solaris fault manager (fmd(1M)) for 64*7c478bd9Sstevel@tonic-gate * diagnosis. 65*7c478bd9Sstevel@tonic-gate * 66*7c478bd9Sstevel@tonic-gate * DDI_FM_ERRCB_CAPABLE 67*7c478bd9Sstevel@tonic-gate * 68*7c478bd9Sstevel@tonic-gate * Device drivers are expected to implement and register an error 69*7c478bd9Sstevel@tonic-gate * handler callback function. ddi_fm_handler_register() and 70*7c478bd9Sstevel@tonic-gate * ddi_fm_handler_unregister() must be 71*7c478bd9Sstevel@tonic-gate * called in passive kernel context, typically during an attach(9E) 72*7c478bd9Sstevel@tonic-gate * or detach(9E) operation. When called by the FM IO framework, 73*7c478bd9Sstevel@tonic-gate * the callback function should check for error conditions for the 74*7c478bd9Sstevel@tonic-gate * hardware and software under its control. All detected errors 75*7c478bd9Sstevel@tonic-gate * should have ereport events generated for them. 76*7c478bd9Sstevel@tonic-gate * 77*7c478bd9Sstevel@tonic-gate * Upon completion of the error handler callback, the driver should 78*7c478bd9Sstevel@tonic-gate * return one of the following values: 79*7c478bd9Sstevel@tonic-gate * 80*7c478bd9Sstevel@tonic-gate * #define DDI_FM_OK - no error was detected 81*7c478bd9Sstevel@tonic-gate * #define DDI_FM_FATAL - a fatal error was detected 82*7c478bd9Sstevel@tonic-gate * #define DDI_FM_NONFATAL - a non-fatal error was detected 83*7c478bd9Sstevel@tonic-gate * #define DDI_FM_UNKNOWN - the error status is unknown 84*7c478bd9Sstevel@tonic-gate * 85*7c478bd9Sstevel@tonic-gate * To insure single threaded access to error handling callbacks, 86*7c478bd9Sstevel@tonic-gate * the device driver may use i_ddi_fm_handler_enter() and 87*7c478bd9Sstevel@tonic-gate * i_ddi_fm_handler_exit() when entering and exiting the callback. 88*7c478bd9Sstevel@tonic-gate * 89*7c478bd9Sstevel@tonic-gate * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE 90*7c478bd9Sstevel@tonic-gate * 91*7c478bd9Sstevel@tonic-gate * Device drivers are expected to set-up access and DMA handles 92*7c478bd9Sstevel@tonic-gate * with FM-specific attributes designed to allow nexus parent 93*7c478bd9Sstevel@tonic-gate * drivers to flag any errors seen during subsequent IO transactions. 94*7c478bd9Sstevel@tonic-gate * Drivers must set the devacc_attr_acc_flag member of their 95*7c478bd9Sstevel@tonic-gate * ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC. 96*7c478bd9Sstevel@tonic-gate * For DMA transactions, driver must set the dma_attr_flags of 97*7c478bd9Sstevel@tonic-gate * their ddi_dma_attr_t structures to DDI_DMA_FLAGERR. 98*7c478bd9Sstevel@tonic-gate * 99*7c478bd9Sstevel@tonic-gate * Upon completion of an IO transaction, device drivers are expected 100*7c478bd9Sstevel@tonic-gate * to check the status of host-side hardware access and device-side 101*7c478bd9Sstevel@tonic-gate * dma completions by calling ddi_acc_err_check() or ddi_dma_err_check() 102*7c478bd9Sstevel@tonic-gate * respectively. If the handle is associated with an error detected by 103*7c478bd9Sstevel@tonic-gate * the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena 104*7c478bd9Sstevel@tonic-gate * and error expectation) is returned. If status of DDI_FM_NONFATAL or 105*7c478bd9Sstevel@tonic-gate * DDI_FM_FATAL is returned, the ena is valid and the expectation flag 106*7c478bd9Sstevel@tonic-gate * will be set to 1 if the error was unexpected (i.e. not the result 107*7c478bd9Sstevel@tonic-gate * of a peek or poke type operation). 108*7c478bd9Sstevel@tonic-gate * 109*7c478bd9Sstevel@tonic-gate * ddi_acc_err_check() and ddi_dma_err_check() may be called in any 110*7c478bd9Sstevel@tonic-gate * context subject to the constraints specified by the interrupt 111*7c478bd9Sstevel@tonic-gate * iblock cookie returned during initialization. 112*7c478bd9Sstevel@tonic-gate * 113*7c478bd9Sstevel@tonic-gate * Device drivers should generate an access (DDI_FM_IO_ACC) or dma 114*7c478bd9Sstevel@tonic-gate * (DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or 115*7c478bd9Sstevel@tonic-gate * DDI_FM_FATAL is returned. 116*7c478bd9Sstevel@tonic-gate * 117*7c478bd9Sstevel@tonic-gate */ 118*7c478bd9Sstevel@tonic-gate 119*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 120*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 121*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 122*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 123*7c478bd9Sstevel@tonic-gate #include <sys/nvpair.h> 124*7c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h> 125*7c478bd9Sstevel@tonic-gate #include <sys/ndifm.h> 126*7c478bd9Sstevel@tonic-gate #include <sys/ddifm.h> 127*7c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 128*7c478bd9Sstevel@tonic-gate #include <sys/ddi_isa.h> 129*7c478bd9Sstevel@tonic-gate #include <sys/spl.h> 130*7c478bd9Sstevel@tonic-gate #include <sys/varargs.h> 131*7c478bd9Sstevel@tonic-gate #include <sys/systm.h> 132*7c478bd9Sstevel@tonic-gate #include <sys/disp.h> 133*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 134*7c478bd9Sstevel@tonic-gate #include <sys/errorq_impl.h> 135*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 136*7c478bd9Sstevel@tonic-gate #include <sys/fm/util.h> 137*7c478bd9Sstevel@tonic-gate #include <sys/fm/io/ddi.h> 138*7c478bd9Sstevel@tonic-gate 139*7c478bd9Sstevel@tonic-gate #define ERPT_CLASS_SZ sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \ 140*7c478bd9Sstevel@tonic-gate DDI_MAX_ERPT_CLASS + 2 141*7c478bd9Sstevel@tonic-gate /* Globals */ 142*7c478bd9Sstevel@tonic-gate int default_dmacache_sz = DEFAULT_DMACACHE_SZ; 143*7c478bd9Sstevel@tonic-gate int default_acccache_sz = DEFAULT_ACCCACHE_SZ; 144*7c478bd9Sstevel@tonic-gate int ddi_system_fmcap = 0; 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate static struct i_ddi_fmkstat ddifm_kstat_template = { 147*7c478bd9Sstevel@tonic-gate {"erpt_dropped", KSTAT_DATA_UINT64 }, 148*7c478bd9Sstevel@tonic-gate {"fm_cache_full", KSTAT_DATA_UINT64 }, 149*7c478bd9Sstevel@tonic-gate {"fm_cache_grew", KSTAT_DATA_UINT64 }, 150*7c478bd9Sstevel@tonic-gate {"acc_err", KSTAT_DATA_UINT64 }, 151*7c478bd9Sstevel@tonic-gate {"dma_err", KSTAT_DATA_UINT64 } 152*7c478bd9Sstevel@tonic-gate }; 153*7c478bd9Sstevel@tonic-gate 154*7c478bd9Sstevel@tonic-gate /* 155*7c478bd9Sstevel@tonic-gate * Update the service state following the detection of an 156*7c478bd9Sstevel@tonic-gate * error. 157*7c478bd9Sstevel@tonic-gate */ 158*7c478bd9Sstevel@tonic-gate void 159*7c478bd9Sstevel@tonic-gate ddi_fm_service_impact(dev_info_t *dip, int svc_impact) 160*7c478bd9Sstevel@tonic-gate { 161*7c478bd9Sstevel@tonic-gate mutex_enter(&(DEVI(dip)->devi_lock)); 162*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_DEVICE_OFFLINE(dip)) { 163*7c478bd9Sstevel@tonic-gate switch (svc_impact) { 164*7c478bd9Sstevel@tonic-gate case DDI_SERVICE_LOST: 165*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_DOWN(dip); 166*7c478bd9Sstevel@tonic-gate break; 167*7c478bd9Sstevel@tonic-gate case DDI_SERVICE_DEGRADED: 168*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_DEGRADED(dip); 169*7c478bd9Sstevel@tonic-gate break; 170*7c478bd9Sstevel@tonic-gate case DDI_SERVICE_RESTORED: 171*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_UP(dip); 172*7c478bd9Sstevel@tonic-gate break; 173*7c478bd9Sstevel@tonic-gate case DDI_SERVICE_UNAFFECTED: 174*7c478bd9Sstevel@tonic-gate default: 175*7c478bd9Sstevel@tonic-gate break; 176*7c478bd9Sstevel@tonic-gate } 177*7c478bd9Sstevel@tonic-gate } 178*7c478bd9Sstevel@tonic-gate mutex_exit(&(DEVI(dip)->devi_lock)); 179*7c478bd9Sstevel@tonic-gate } 180*7c478bd9Sstevel@tonic-gate 181*7c478bd9Sstevel@tonic-gate static int 182*7c478bd9Sstevel@tonic-gate erpt_post_sleep(dev_info_t *dip, const char *error_class, uint64_t ena, 183*7c478bd9Sstevel@tonic-gate uint8_t version, va_list ap) 184*7c478bd9Sstevel@tonic-gate { 185*7c478bd9Sstevel@tonic-gate char *devid, *name; 186*7c478bd9Sstevel@tonic-gate char device_path[MAXPATHLEN]; 187*7c478bd9Sstevel@tonic-gate char ddi_error_class[ERPT_CLASS_SZ]; 188*7c478bd9Sstevel@tonic-gate nvlist_t *ereport, *detector = NULL; 189*7c478bd9Sstevel@tonic-gate 190*7c478bd9Sstevel@tonic-gate /* 191*7c478bd9Sstevel@tonic-gate * Driver defect - should not call with DDI_SLEEP while 192*7c478bd9Sstevel@tonic-gate * in interrupt context 193*7c478bd9Sstevel@tonic-gate */ 194*7c478bd9Sstevel@tonic-gate if (servicing_interrupt()) { 195*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 196*7c478bd9Sstevel@tonic-gate return (1); 197*7c478bd9Sstevel@tonic-gate } 198*7c478bd9Sstevel@tonic-gate 199*7c478bd9Sstevel@tonic-gate if ((ereport = fm_nvlist_create(NULL)) == NULL) 200*7c478bd9Sstevel@tonic-gate return (1); 201*7c478bd9Sstevel@tonic-gate 202*7c478bd9Sstevel@tonic-gate /* 203*7c478bd9Sstevel@tonic-gate * Use the dev_path/devid for this device instance. 204*7c478bd9Sstevel@tonic-gate */ 205*7c478bd9Sstevel@tonic-gate detector = fm_nvlist_create(NULL); 206*7c478bd9Sstevel@tonic-gate if (dip == ddi_root_node()) { 207*7c478bd9Sstevel@tonic-gate device_path[0] = '/'; 208*7c478bd9Sstevel@tonic-gate device_path[1] = '\0'; 209*7c478bd9Sstevel@tonic-gate } else { 210*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, device_path); 211*7c478bd9Sstevel@tonic-gate } 212*7c478bd9Sstevel@tonic-gate 213*7c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 214*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devid) == DDI_SUCCESS) { 215*7c478bd9Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 216*7c478bd9Sstevel@tonic-gate device_path, devid); 217*7c478bd9Sstevel@tonic-gate ddi_prop_free(devid); 218*7c478bd9Sstevel@tonic-gate } else { 219*7c478bd9Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 220*7c478bd9Sstevel@tonic-gate device_path, NULL); 221*7c478bd9Sstevel@tonic-gate } 222*7c478bd9Sstevel@tonic-gate 223*7c478bd9Sstevel@tonic-gate if (ena == 0) 224*7c478bd9Sstevel@tonic-gate ena = fm_ena_generate(0, FM_ENA_FMT1); 225*7c478bd9Sstevel@tonic-gate 226*7c478bd9Sstevel@tonic-gate (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", 227*7c478bd9Sstevel@tonic-gate DDI_IO_CLASS, error_class); 228*7c478bd9Sstevel@tonic-gate 229*7c478bd9Sstevel@tonic-gate fm_ereport_set(ereport, version, ddi_error_class, 230*7c478bd9Sstevel@tonic-gate ena, detector, NULL); 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate name = va_arg(ap, char *); 233*7c478bd9Sstevel@tonic-gate (void) i_fm_payload_set(ereport, name, ap); 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate fm_ereport_post(ereport, EVCH_SLEEP); 236*7c478bd9Sstevel@tonic-gate fm_nvlist_destroy(ereport, FM_NVA_FREE); 237*7c478bd9Sstevel@tonic-gate fm_nvlist_destroy(detector, FM_NVA_FREE); 238*7c478bd9Sstevel@tonic-gate 239*7c478bd9Sstevel@tonic-gate return (0); 240*7c478bd9Sstevel@tonic-gate } 241*7c478bd9Sstevel@tonic-gate 242*7c478bd9Sstevel@tonic-gate static int 243*7c478bd9Sstevel@tonic-gate erpt_post_nosleep(dev_info_t *dip, struct i_ddi_fmhdl *fmhdl, 244*7c478bd9Sstevel@tonic-gate const char *error_class, uint64_t ena, uint8_t version, va_list ap) 245*7c478bd9Sstevel@tonic-gate { 246*7c478bd9Sstevel@tonic-gate char *name; 247*7c478bd9Sstevel@tonic-gate char device_path[MAXPATHLEN]; 248*7c478bd9Sstevel@tonic-gate char ddi_error_class[ERPT_CLASS_SZ]; 249*7c478bd9Sstevel@tonic-gate nvlist_t *ereport, *detector; 250*7c478bd9Sstevel@tonic-gate nv_alloc_t *nva; 251*7c478bd9Sstevel@tonic-gate errorq_elem_t *eqep; 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate eqep = errorq_reserve(fmhdl->fh_errorq); 254*7c478bd9Sstevel@tonic-gate if (eqep == NULL) 255*7c478bd9Sstevel@tonic-gate return (1); 256*7c478bd9Sstevel@tonic-gate 257*7c478bd9Sstevel@tonic-gate ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep); 258*7c478bd9Sstevel@tonic-gate nva = errorq_elem_nva(fmhdl->fh_errorq, eqep); 259*7c478bd9Sstevel@tonic-gate 260*7c478bd9Sstevel@tonic-gate ASSERT(ereport); 261*7c478bd9Sstevel@tonic-gate ASSERT(nva); 262*7c478bd9Sstevel@tonic-gate 263*7c478bd9Sstevel@tonic-gate /* 264*7c478bd9Sstevel@tonic-gate * Use the dev_path/devid for this device instance. 265*7c478bd9Sstevel@tonic-gate */ 266*7c478bd9Sstevel@tonic-gate detector = fm_nvlist_create(nva); 267*7c478bd9Sstevel@tonic-gate if (dip == ddi_root_node()) { 268*7c478bd9Sstevel@tonic-gate device_path[0] = '/'; 269*7c478bd9Sstevel@tonic-gate device_path[1] = '\0'; 270*7c478bd9Sstevel@tonic-gate } else { 271*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, device_path); 272*7c478bd9Sstevel@tonic-gate } 273*7c478bd9Sstevel@tonic-gate 274*7c478bd9Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 275*7c478bd9Sstevel@tonic-gate device_path, NULL); 276*7c478bd9Sstevel@tonic-gate 277*7c478bd9Sstevel@tonic-gate if (ena == 0) 278*7c478bd9Sstevel@tonic-gate ena = fm_ena_generate(0, FM_ENA_FMT1); 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", 281*7c478bd9Sstevel@tonic-gate DDI_IO_CLASS, error_class); 282*7c478bd9Sstevel@tonic-gate 283*7c478bd9Sstevel@tonic-gate fm_ereport_set(ereport, version, ddi_error_class, 284*7c478bd9Sstevel@tonic-gate ena, detector, NULL); 285*7c478bd9Sstevel@tonic-gate 286*7c478bd9Sstevel@tonic-gate name = va_arg(ap, char *); 287*7c478bd9Sstevel@tonic-gate (void) i_fm_payload_set(ereport, name, ap); 288*7c478bd9Sstevel@tonic-gate 289*7c478bd9Sstevel@tonic-gate errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC); 290*7c478bd9Sstevel@tonic-gate 291*7c478bd9Sstevel@tonic-gate return (0); 292*7c478bd9Sstevel@tonic-gate } 293*7c478bd9Sstevel@tonic-gate 294*7c478bd9Sstevel@tonic-gate void 295*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, 296*7c478bd9Sstevel@tonic-gate nvlist_t *errp, int sflag) 297*7c478bd9Sstevel@tonic-gate { 298*7c478bd9Sstevel@tonic-gate int i; 299*7c478bd9Sstevel@tonic-gate int depth; 300*7c478bd9Sstevel@tonic-gate char classp[DDI_DVR_MAX_CLASS]; 301*7c478bd9Sstevel@tonic-gate caddr_t stkp; 302*7c478bd9Sstevel@tonic-gate char *buf; 303*7c478bd9Sstevel@tonic-gate char **stkpp; 304*7c478bd9Sstevel@tonic-gate char *sym; 305*7c478bd9Sstevel@tonic-gate pc_t stack[DDI_FM_STKDEPTH]; 306*7c478bd9Sstevel@tonic-gate ulong_t off; 307*7c478bd9Sstevel@tonic-gate dev_info_t *root_dip = ddi_root_node(); 308*7c478bd9Sstevel@tonic-gate 309*7c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip))) 310*7c478bd9Sstevel@tonic-gate return; 311*7c478bd9Sstevel@tonic-gate 312*7c478bd9Sstevel@tonic-gate (void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT, 313*7c478bd9Sstevel@tonic-gate error_class); 314*7c478bd9Sstevel@tonic-gate 315*7c478bd9Sstevel@tonic-gate if (sflag == DDI_SLEEP) { 316*7c478bd9Sstevel@tonic-gate depth = getpcstack(stack, DDI_FM_STKDEPTH); 317*7c478bd9Sstevel@tonic-gate 318*7c478bd9Sstevel@tonic-gate /* Allocate array of char * for nvlist payload */ 319*7c478bd9Sstevel@tonic-gate stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP); 320*7c478bd9Sstevel@tonic-gate 321*7c478bd9Sstevel@tonic-gate /* 322*7c478bd9Sstevel@tonic-gate * Allocate temporary 64-bit aligned buffer for stack 323*7c478bd9Sstevel@tonic-gate * symbol strings 324*7c478bd9Sstevel@tonic-gate */ 325*7c478bd9Sstevel@tonic-gate buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP); 326*7c478bd9Sstevel@tonic-gate 327*7c478bd9Sstevel@tonic-gate stkp = buf; 328*7c478bd9Sstevel@tonic-gate for (i = 0; i < depth; ++i) { 329*7c478bd9Sstevel@tonic-gate sym = kobj_getsymname(stack[i], &off); 330*7c478bd9Sstevel@tonic-gate (void) snprintf(stkp, DDI_FM_SYM_SZ, 331*7c478bd9Sstevel@tonic-gate "\t%s+%lx\n", sym ? sym : "?", off); 332*7c478bd9Sstevel@tonic-gate stkpp[i] = stkp; 333*7c478bd9Sstevel@tonic-gate stkp += DDI_FM_SYM_SZ; 334*7c478bd9Sstevel@tonic-gate } 335*7c478bd9Sstevel@tonic-gate 336*7c478bd9Sstevel@tonic-gate if (errp) 337*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 338*7c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 339*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 340*7c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 341*7c478bd9Sstevel@tonic-gate DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth, 342*7c478bd9Sstevel@tonic-gate DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp, 343*7c478bd9Sstevel@tonic-gate DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL); 344*7c478bd9Sstevel@tonic-gate else 345*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 346*7c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 347*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 348*7c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 349*7c478bd9Sstevel@tonic-gate DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth, 350*7c478bd9Sstevel@tonic-gate DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp, 351*7c478bd9Sstevel@tonic-gate NULL); 352*7c478bd9Sstevel@tonic-gate 353*7c478bd9Sstevel@tonic-gate kmem_free(stkpp, depth * sizeof (char *)); 354*7c478bd9Sstevel@tonic-gate kmem_free(buf, depth * DDI_FM_SYM_SZ); 355*7c478bd9Sstevel@tonic-gate 356*7c478bd9Sstevel@tonic-gate } else { 357*7c478bd9Sstevel@tonic-gate if (errp) 358*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 359*7c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 360*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 361*7c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 362*7c478bd9Sstevel@tonic-gate DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL); 363*7c478bd9Sstevel@tonic-gate else 364*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 365*7c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 366*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 367*7c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 368*7c478bd9Sstevel@tonic-gate NULL); 369*7c478bd9Sstevel@tonic-gate } 370*7c478bd9Sstevel@tonic-gate } 371*7c478bd9Sstevel@tonic-gate 372*7c478bd9Sstevel@tonic-gate /* 373*7c478bd9Sstevel@tonic-gate * Generate an error report for consumption by the Solaris Fault Manager, 374*7c478bd9Sstevel@tonic-gate * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io. The 375*7c478bd9Sstevel@tonic-gate * ENA should be set if this error is a result of an error status returned 376*7c478bd9Sstevel@tonic-gate * from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise, an ENA 377*7c478bd9Sstevel@tonic-gate * value of 0 is appropriate. 378*7c478bd9Sstevel@tonic-gate * 379*7c478bd9Sstevel@tonic-gate * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called 380*7c478bd9Sstevel@tonic-gate * from user, kernel, interrupt or high-interrupt context. Otherwise, 381*7c478bd9Sstevel@tonic-gate * ddi_fm_ereport_post() must be called from user or kernel context. 382*7c478bd9Sstevel@tonic-gate */ 383*7c478bd9Sstevel@tonic-gate void 384*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, 385*7c478bd9Sstevel@tonic-gate int sflag, ...) 386*7c478bd9Sstevel@tonic-gate { 387*7c478bd9Sstevel@tonic-gate int ret; 388*7c478bd9Sstevel@tonic-gate char *name; 389*7c478bd9Sstevel@tonic-gate data_type_t type; 390*7c478bd9Sstevel@tonic-gate uint8_t version; 391*7c478bd9Sstevel@tonic-gate va_list ap; 392*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) { 395*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, sflag); 396*7c478bd9Sstevel@tonic-gate return; 397*7c478bd9Sstevel@tonic-gate } 398*7c478bd9Sstevel@tonic-gate 399*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 400*7c478bd9Sstevel@tonic-gate 401*7c478bd9Sstevel@tonic-gate va_start(ap, sflag); 402*7c478bd9Sstevel@tonic-gate 403*7c478bd9Sstevel@tonic-gate /* First payload tuple should be the version */ 404*7c478bd9Sstevel@tonic-gate name = va_arg(ap, char *); 405*7c478bd9Sstevel@tonic-gate type = va_arg(ap, data_type_t); 406*7c478bd9Sstevel@tonic-gate version = va_arg(ap, uint_t); 407*7c478bd9Sstevel@tonic-gate if (strcmp(name, FM_VERSION) != 0 && type != DATA_TYPE_UINT8) { 408*7c478bd9Sstevel@tonic-gate va_end(ap); 409*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag); 410*7c478bd9Sstevel@tonic-gate return; 411*7c478bd9Sstevel@tonic-gate } 412*7c478bd9Sstevel@tonic-gate 413*7c478bd9Sstevel@tonic-gate if (sflag == DDI_SLEEP) 414*7c478bd9Sstevel@tonic-gate ret = erpt_post_sleep(dip, error_class, ena, version, ap); 415*7c478bd9Sstevel@tonic-gate else 416*7c478bd9Sstevel@tonic-gate ret = erpt_post_nosleep(dip, fmhdl, error_class, ena, version, 417*7c478bd9Sstevel@tonic-gate ap); 418*7c478bd9Sstevel@tonic-gate va_end(ap); 419*7c478bd9Sstevel@tonic-gate 420*7c478bd9Sstevel@tonic-gate if (ret != 0) 421*7c478bd9Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1); 422*7c478bd9Sstevel@tonic-gate 423*7c478bd9Sstevel@tonic-gate } 424*7c478bd9Sstevel@tonic-gate 425*7c478bd9Sstevel@tonic-gate /* 426*7c478bd9Sstevel@tonic-gate * Driver error handling entry. Prevents multiple simultaneous calls into 427*7c478bd9Sstevel@tonic-gate * driver error handling callback. 428*7c478bd9Sstevel@tonic-gate * 429*7c478bd9Sstevel@tonic-gate * May be called from a context consistent with the iblock_cookie returned 430*7c478bd9Sstevel@tonic-gate * in ddi_fm_init(). 431*7c478bd9Sstevel@tonic-gate */ 432*7c478bd9Sstevel@tonic-gate void 433*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(dev_info_t *dip) 434*7c478bd9Sstevel@tonic-gate { 435*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl; 436*7c478bd9Sstevel@tonic-gate 437*7c478bd9Sstevel@tonic-gate mutex_enter(&hdl->fh_lock); 438*7c478bd9Sstevel@tonic-gate } 439*7c478bd9Sstevel@tonic-gate 440*7c478bd9Sstevel@tonic-gate /* 441*7c478bd9Sstevel@tonic-gate * Driver error handling exit. 442*7c478bd9Sstevel@tonic-gate * 443*7c478bd9Sstevel@tonic-gate * May be called from a context consistent with the iblock_cookie returned 444*7c478bd9Sstevel@tonic-gate * in ddi_fm_init(). 445*7c478bd9Sstevel@tonic-gate */ 446*7c478bd9Sstevel@tonic-gate void 447*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(dev_info_t *dip) 448*7c478bd9Sstevel@tonic-gate { 449*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl; 450*7c478bd9Sstevel@tonic-gate 451*7c478bd9Sstevel@tonic-gate mutex_exit(&hdl->fh_lock); 452*7c478bd9Sstevel@tonic-gate } 453*7c478bd9Sstevel@tonic-gate 454*7c478bd9Sstevel@tonic-gate /* 455*7c478bd9Sstevel@tonic-gate * Register a fault manager error handler for this device instance 456*7c478bd9Sstevel@tonic-gate * 457*7c478bd9Sstevel@tonic-gate * This function must be called from a driver's attach(9E) routine. 458*7c478bd9Sstevel@tonic-gate */ 459*7c478bd9Sstevel@tonic-gate void 460*7c478bd9Sstevel@tonic-gate ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler, 461*7c478bd9Sstevel@tonic-gate void *impl_data) 462*7c478bd9Sstevel@tonic-gate { 463*7c478bd9Sstevel@tonic-gate dev_info_t *pdip; 464*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *pfmhdl; 465*7c478bd9Sstevel@tonic-gate struct i_ddi_errhdl *new_eh; 466*7c478bd9Sstevel@tonic-gate struct i_ddi_fmtgt *tgt; 467*7c478bd9Sstevel@tonic-gate 468*7c478bd9Sstevel@tonic-gate /* 469*7c478bd9Sstevel@tonic-gate * Check for proper calling context. 470*7c478bd9Sstevel@tonic-gate * The DDI configuration framework does not support 471*7c478bd9Sstevel@tonic-gate * DR states to allow checking for proper invocation 472*7c478bd9Sstevel@tonic-gate * from a DDI_ATTACH or DDI_RESUME. This limits context checking 473*7c478bd9Sstevel@tonic-gate * to interrupt only. 474*7c478bd9Sstevel@tonic-gate */ 475*7c478bd9Sstevel@tonic-gate if (servicing_interrupt()) { 476*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 477*7c478bd9Sstevel@tonic-gate return; 478*7c478bd9Sstevel@tonic-gate } 479*7c478bd9Sstevel@tonic-gate 480*7c478bd9Sstevel@tonic-gate pdip = (dev_info_t *)DEVI(dip)->devi_parent; 481*7c478bd9Sstevel@tonic-gate 482*7c478bd9Sstevel@tonic-gate ASSERT(pdip); 483*7c478bd9Sstevel@tonic-gate 484*7c478bd9Sstevel@tonic-gate if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) && 485*7c478bd9Sstevel@tonic-gate DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) { 486*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 487*7c478bd9Sstevel@tonic-gate return; 488*7c478bd9Sstevel@tonic-gate } 489*7c478bd9Sstevel@tonic-gate 490*7c478bd9Sstevel@tonic-gate new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP); 491*7c478bd9Sstevel@tonic-gate new_eh->eh_func = handler; 492*7c478bd9Sstevel@tonic-gate new_eh->eh_impl = impl_data; 493*7c478bd9Sstevel@tonic-gate 494*7c478bd9Sstevel@tonic-gate /* Add dip to parent's target list of registered error handlers */ 495*7c478bd9Sstevel@tonic-gate tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP); 496*7c478bd9Sstevel@tonic-gate tgt->ft_dip = dip; 497*7c478bd9Sstevel@tonic-gate tgt->ft_errhdl = new_eh; 498*7c478bd9Sstevel@tonic-gate 499*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(pdip); 500*7c478bd9Sstevel@tonic-gate pfmhdl = DEVI(pdip)->devi_fmhdl; 501*7c478bd9Sstevel@tonic-gate ASSERT(pfmhdl); 502*7c478bd9Sstevel@tonic-gate tgt->ft_next = pfmhdl->fh_tgts; 503*7c478bd9Sstevel@tonic-gate pfmhdl->fh_tgts = tgt; 504*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(pdip); 505*7c478bd9Sstevel@tonic-gate } 506*7c478bd9Sstevel@tonic-gate 507*7c478bd9Sstevel@tonic-gate /* 508*7c478bd9Sstevel@tonic-gate * Unregister a fault manager error handler for this device instance 509*7c478bd9Sstevel@tonic-gate * 510*7c478bd9Sstevel@tonic-gate * This function must be called from a drivers attach(9E) or detach(9E) 511*7c478bd9Sstevel@tonic-gate * routine. 512*7c478bd9Sstevel@tonic-gate */ 513*7c478bd9Sstevel@tonic-gate void 514*7c478bd9Sstevel@tonic-gate ddi_fm_handler_unregister(dev_info_t *dip) 515*7c478bd9Sstevel@tonic-gate { 516*7c478bd9Sstevel@tonic-gate dev_info_t *pdip; 517*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *pfmhdl; 518*7c478bd9Sstevel@tonic-gate struct i_ddi_fmtgt *tgt, **ptgt; 519*7c478bd9Sstevel@tonic-gate 520*7c478bd9Sstevel@tonic-gate /* 521*7c478bd9Sstevel@tonic-gate * Check for proper calling context. 522*7c478bd9Sstevel@tonic-gate * The DDI configuration framework does not support 523*7c478bd9Sstevel@tonic-gate * DR states to allow checking for proper invocation 524*7c478bd9Sstevel@tonic-gate * from a DDI_DETACH or DDI_SUSPEND. This limits context checking 525*7c478bd9Sstevel@tonic-gate * to interrupt only. 526*7c478bd9Sstevel@tonic-gate */ 527*7c478bd9Sstevel@tonic-gate if (servicing_interrupt()) { 528*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 529*7c478bd9Sstevel@tonic-gate return; 530*7c478bd9Sstevel@tonic-gate } 531*7c478bd9Sstevel@tonic-gate 532*7c478bd9Sstevel@tonic-gate pdip = (dev_info_t *)DEVI(dip)->devi_parent; 533*7c478bd9Sstevel@tonic-gate 534*7c478bd9Sstevel@tonic-gate ASSERT(pdip); 535*7c478bd9Sstevel@tonic-gate 536*7c478bd9Sstevel@tonic-gate if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) && 537*7c478bd9Sstevel@tonic-gate DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) { 538*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 539*7c478bd9Sstevel@tonic-gate return; 540*7c478bd9Sstevel@tonic-gate } 541*7c478bd9Sstevel@tonic-gate 542*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(pdip); 543*7c478bd9Sstevel@tonic-gate pfmhdl = DEVI(pdip)->devi_fmhdl; 544*7c478bd9Sstevel@tonic-gate ASSERT(pfmhdl); 545*7c478bd9Sstevel@tonic-gate ptgt = &pfmhdl->fh_tgts; 546*7c478bd9Sstevel@tonic-gate for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) { 547*7c478bd9Sstevel@tonic-gate if (dip == tgt->ft_dip) { 548*7c478bd9Sstevel@tonic-gate *ptgt = tgt->ft_next; 549*7c478bd9Sstevel@tonic-gate kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl)); 550*7c478bd9Sstevel@tonic-gate kmem_free(tgt, sizeof (struct i_ddi_fmtgt)); 551*7c478bd9Sstevel@tonic-gate break; 552*7c478bd9Sstevel@tonic-gate } 553*7c478bd9Sstevel@tonic-gate ptgt = &tgt->ft_next; 554*7c478bd9Sstevel@tonic-gate } 555*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(pdip); 556*7c478bd9Sstevel@tonic-gate 557*7c478bd9Sstevel@tonic-gate 558*7c478bd9Sstevel@tonic-gate } 559*7c478bd9Sstevel@tonic-gate 560*7c478bd9Sstevel@tonic-gate /* 561*7c478bd9Sstevel@tonic-gate * Initialize Fault Management capabilities for this device instance (dip). 562*7c478bd9Sstevel@tonic-gate * When called with the following capabilities, data structures neccessary 563*7c478bd9Sstevel@tonic-gate * for fault management activities are allocated and initialized. 564*7c478bd9Sstevel@tonic-gate * 565*7c478bd9Sstevel@tonic-gate * DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport 566*7c478bd9Sstevel@tonic-gate * capable driver property. 567*7c478bd9Sstevel@tonic-gate * 568*7c478bd9Sstevel@tonic-gate * DDI_FM_ERRCB_CAPABLE - check with parent for ability to register 569*7c478bd9Sstevel@tonic-gate * an error handler. 570*7c478bd9Sstevel@tonic-gate * 571*7c478bd9Sstevel@tonic-gate * DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk 572*7c478bd9Sstevel@tonic-gate * driver property 573*7c478bd9Sstevel@tonic-gate * 574*7c478bd9Sstevel@tonic-gate * DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk 575*7c478bd9Sstevel@tonic-gate * driver property 576*7c478bd9Sstevel@tonic-gate * 577*7c478bd9Sstevel@tonic-gate * A driver's FM capability level may not exceed that of its parent or 578*7c478bd9Sstevel@tonic-gate * system-wide FM capability. The available capability level for this 579*7c478bd9Sstevel@tonic-gate * device instance is returned in *fmcap. 580*7c478bd9Sstevel@tonic-gate * 581*7c478bd9Sstevel@tonic-gate * This function must be called from a driver's attach(9E) entry point. 582*7c478bd9Sstevel@tonic-gate */ 583*7c478bd9Sstevel@tonic-gate void 584*7c478bd9Sstevel@tonic-gate ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc) 585*7c478bd9Sstevel@tonic-gate { 586*7c478bd9Sstevel@tonic-gate struct dev_info *devi = DEVI(dip); 587*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 588*7c478bd9Sstevel@tonic-gate int pcap, newcap = DDI_FM_NOT_CAPABLE; 589*7c478bd9Sstevel@tonic-gate 590*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) { 591*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 592*7c478bd9Sstevel@tonic-gate *fmcap = DDI_FM_NOT_CAPABLE; 593*7c478bd9Sstevel@tonic-gate return; 594*7c478bd9Sstevel@tonic-gate } 595*7c478bd9Sstevel@tonic-gate 596*7c478bd9Sstevel@tonic-gate if (DDI_FM_DEFAULT_CAP(*fmcap)) 597*7c478bd9Sstevel@tonic-gate return; 598*7c478bd9Sstevel@tonic-gate 599*7c478bd9Sstevel@tonic-gate /* 600*7c478bd9Sstevel@tonic-gate * Check parent for supported FM level 601*7c478bd9Sstevel@tonic-gate * and correct error handling PIL 602*7c478bd9Sstevel@tonic-gate */ 603*7c478bd9Sstevel@tonic-gate if (dip != ddi_root_node()) { 604*7c478bd9Sstevel@tonic-gate 605*7c478bd9Sstevel@tonic-gate /* 606*7c478bd9Sstevel@tonic-gate * Initialize the default ibc. The parent may change it 607*7c478bd9Sstevel@tonic-gate * depending upon its capabilities. 608*7c478bd9Sstevel@tonic-gate */ 609*7c478bd9Sstevel@tonic-gate *ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL); 610*7c478bd9Sstevel@tonic-gate 611*7c478bd9Sstevel@tonic-gate pcap = i_ndi_busop_fm_init(dip, *fmcap, ibc); 612*7c478bd9Sstevel@tonic-gate } else { 613*7c478bd9Sstevel@tonic-gate pcap = *fmcap; 614*7c478bd9Sstevel@tonic-gate } 615*7c478bd9Sstevel@tonic-gate 616*7c478bd9Sstevel@tonic-gate /* Initialize the per-device instance FM handle */ 617*7c478bd9Sstevel@tonic-gate fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP); 618*7c478bd9Sstevel@tonic-gate 619*7c478bd9Sstevel@tonic-gate if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip), 620*7c478bd9Sstevel@tonic-gate ddi_get_instance(dip), "fm", "misc", 621*7c478bd9Sstevel@tonic-gate KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) / 622*7c478bd9Sstevel@tonic-gate sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) { 623*7c478bd9Sstevel@tonic-gate mutex_destroy(&fmhdl->fh_lock); 624*7c478bd9Sstevel@tonic-gate kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl)); 625*7c478bd9Sstevel@tonic-gate *fmcap = DDI_FM_NOT_CAPABLE; 626*7c478bd9Sstevel@tonic-gate return; 627*7c478bd9Sstevel@tonic-gate } 628*7c478bd9Sstevel@tonic-gate 629*7c478bd9Sstevel@tonic-gate bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat, 630*7c478bd9Sstevel@tonic-gate sizeof (struct i_ddi_fmkstat)); 631*7c478bd9Sstevel@tonic-gate fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat; 632*7c478bd9Sstevel@tonic-gate fmhdl->fh_ksp->ks_private = fmhdl; 633*7c478bd9Sstevel@tonic-gate kstat_install(fmhdl->fh_ksp); 634*7c478bd9Sstevel@tonic-gate 635*7c478bd9Sstevel@tonic-gate fmhdl->fh_dma_cache = NULL; 636*7c478bd9Sstevel@tonic-gate fmhdl->fh_acc_cache = NULL; 637*7c478bd9Sstevel@tonic-gate fmhdl->fh_tgts = NULL; 638*7c478bd9Sstevel@tonic-gate fmhdl->fh_dip = dip; 639*7c478bd9Sstevel@tonic-gate fmhdl->fh_ibc = *ibc; 640*7c478bd9Sstevel@tonic-gate mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc); 641*7c478bd9Sstevel@tonic-gate devi->devi_fmhdl = fmhdl; 642*7c478bd9Sstevel@tonic-gate 643*7c478bd9Sstevel@tonic-gate /* 644*7c478bd9Sstevel@tonic-gate * Initialize support for ereport generation 645*7c478bd9Sstevel@tonic-gate */ 646*7c478bd9Sstevel@tonic-gate if (DDI_FM_EREPORT_CAP(*fmcap) && 647*7c478bd9Sstevel@tonic-gate DDI_FM_EREPORT_CAP(ddi_system_fmcap)) { 648*7c478bd9Sstevel@tonic-gate fmhdl->fh_errorq = ereport_errorq; 649*7c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 650*7c478bd9Sstevel@tonic-gate "fm-ereport-capable", 0) == 0) 651*7c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 652*7c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0); 653*7c478bd9Sstevel@tonic-gate 654*7c478bd9Sstevel@tonic-gate newcap |= DDI_FM_EREPORT_CAPABLE; 655*7c478bd9Sstevel@tonic-gate } 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate /* 658*7c478bd9Sstevel@tonic-gate * Need cooperation of the parent for error handling 659*7c478bd9Sstevel@tonic-gate */ 660*7c478bd9Sstevel@tonic-gate 661*7c478bd9Sstevel@tonic-gate if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) { 662*7c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 663*7c478bd9Sstevel@tonic-gate "fm-errcb-capable", 0) == 0) 664*7c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 665*7c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0); 666*7c478bd9Sstevel@tonic-gate 667*7c478bd9Sstevel@tonic-gate newcap |= DDI_FM_ERRCB_CAPABLE; 668*7c478bd9Sstevel@tonic-gate } 669*7c478bd9Sstevel@tonic-gate 670*7c478bd9Sstevel@tonic-gate /* 671*7c478bd9Sstevel@tonic-gate * Support for DMA and Access error handling 672*7c478bd9Sstevel@tonic-gate */ 673*7c478bd9Sstevel@tonic-gate 674*7c478bd9Sstevel@tonic-gate if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) { 675*7c478bd9Sstevel@tonic-gate i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, *ibc); 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate /* Set-up dma chk capability prop */ 678*7c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 679*7c478bd9Sstevel@tonic-gate "fm-dmachk-capable", 0) == 0) 680*7c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 681*7c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0); 682*7c478bd9Sstevel@tonic-gate 683*7c478bd9Sstevel@tonic-gate newcap |= DDI_FM_DMACHK_CAPABLE; 684*7c478bd9Sstevel@tonic-gate } 685*7c478bd9Sstevel@tonic-gate 686*7c478bd9Sstevel@tonic-gate if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) { 687*7c478bd9Sstevel@tonic-gate i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, *ibc); 688*7c478bd9Sstevel@tonic-gate /* Set-up dma chk capability prop */ 689*7c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 690*7c478bd9Sstevel@tonic-gate "fm-accchk-capable", 0) == 0) 691*7c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 692*7c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0); 693*7c478bd9Sstevel@tonic-gate 694*7c478bd9Sstevel@tonic-gate newcap |= DDI_FM_ACCCHK_CAPABLE; 695*7c478bd9Sstevel@tonic-gate } 696*7c478bd9Sstevel@tonic-gate 697*7c478bd9Sstevel@tonic-gate /* 698*7c478bd9Sstevel@tonic-gate * Return the capability support available 699*7c478bd9Sstevel@tonic-gate * to this driver instance 700*7c478bd9Sstevel@tonic-gate */ 701*7c478bd9Sstevel@tonic-gate fmhdl->fh_cap = newcap; 702*7c478bd9Sstevel@tonic-gate *fmcap = newcap; 703*7c478bd9Sstevel@tonic-gate } 704*7c478bd9Sstevel@tonic-gate 705*7c478bd9Sstevel@tonic-gate /* 706*7c478bd9Sstevel@tonic-gate * Finalize Fault Management activities for this device instance. 707*7c478bd9Sstevel@tonic-gate * Outstanding IO transaction must be completed prior to calling 708*7c478bd9Sstevel@tonic-gate * this routine. All previously allocated resources and error handler 709*7c478bd9Sstevel@tonic-gate * registration are cleared and deallocated. 710*7c478bd9Sstevel@tonic-gate * 711*7c478bd9Sstevel@tonic-gate * This function must be called from a driver's detach(9E) entry point. 712*7c478bd9Sstevel@tonic-gate */ 713*7c478bd9Sstevel@tonic-gate void 714*7c478bd9Sstevel@tonic-gate ddi_fm_fini(dev_info_t *dip) 715*7c478bd9Sstevel@tonic-gate { 716*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 717*7c478bd9Sstevel@tonic-gate 718*7c478bd9Sstevel@tonic-gate if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) { 719*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 720*7c478bd9Sstevel@tonic-gate return; 721*7c478bd9Sstevel@tonic-gate } 722*7c478bd9Sstevel@tonic-gate 723*7c478bd9Sstevel@tonic-gate if (DDI_FM_DEFAULT_CAP(fmhdl->fh_cap)) 724*7c478bd9Sstevel@tonic-gate return; 725*7c478bd9Sstevel@tonic-gate 726*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 727*7c478bd9Sstevel@tonic-gate 728*7c478bd9Sstevel@tonic-gate kstat_delete(fmhdl->fh_ksp); 729*7c478bd9Sstevel@tonic-gate 730*7c478bd9Sstevel@tonic-gate if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) { 731*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 732*7c478bd9Sstevel@tonic-gate "fm-ereport-capable"); 733*7c478bd9Sstevel@tonic-gate } 734*7c478bd9Sstevel@tonic-gate 735*7c478bd9Sstevel@tonic-gate if (dip != ddi_root_node()) { 736*7c478bd9Sstevel@tonic-gate ddi_fm_handler_unregister(dip); 737*7c478bd9Sstevel@tonic-gate 738*7c478bd9Sstevel@tonic-gate if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) || 739*7c478bd9Sstevel@tonic-gate DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 740*7c478bd9Sstevel@tonic-gate if (fmhdl->fh_dma_cache != NULL) { 741*7c478bd9Sstevel@tonic-gate i_ndi_fmc_destroy(fmhdl->fh_dma_cache); 742*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 743*7c478bd9Sstevel@tonic-gate "fm-dmachk-capable"); 744*7c478bd9Sstevel@tonic-gate } 745*7c478bd9Sstevel@tonic-gate if (fmhdl->fh_acc_cache != NULL) { 746*7c478bd9Sstevel@tonic-gate i_ndi_fmc_destroy(fmhdl->fh_acc_cache); 747*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 748*7c478bd9Sstevel@tonic-gate "fm-accachk-capable"); 749*7c478bd9Sstevel@tonic-gate } 750*7c478bd9Sstevel@tonic-gate } 751*7c478bd9Sstevel@tonic-gate 752*7c478bd9Sstevel@tonic-gate i_ndi_busop_fm_fini(dip); 753*7c478bd9Sstevel@tonic-gate } 754*7c478bd9Sstevel@tonic-gate 755*7c478bd9Sstevel@tonic-gate kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl)); 756*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_fmhdl = NULL; 757*7c478bd9Sstevel@tonic-gate } 758*7c478bd9Sstevel@tonic-gate 759*7c478bd9Sstevel@tonic-gate /* 760*7c478bd9Sstevel@tonic-gate * Return the fault management capability level for this device instance. 761*7c478bd9Sstevel@tonic-gate * 762*7c478bd9Sstevel@tonic-gate * This function may be called from user, kernel, or interrupt context. 763*7c478bd9Sstevel@tonic-gate */ 764*7c478bd9Sstevel@tonic-gate int 765*7c478bd9Sstevel@tonic-gate ddi_fm_capable(dev_info_t *dip) 766*7c478bd9Sstevel@tonic-gate { 767*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 768*7c478bd9Sstevel@tonic-gate 769*7c478bd9Sstevel@tonic-gate if (fmhdl == NULL) 770*7c478bd9Sstevel@tonic-gate return (DDI_FM_NOT_CAPABLE); 771*7c478bd9Sstevel@tonic-gate 772*7c478bd9Sstevel@tonic-gate return (fmhdl->fh_cap); 773*7c478bd9Sstevel@tonic-gate } 774*7c478bd9Sstevel@tonic-gate 775*7c478bd9Sstevel@tonic-gate /* 776*7c478bd9Sstevel@tonic-gate * Routines to set and get error information for/from an access or dma handle 777*7c478bd9Sstevel@tonic-gate * 778*7c478bd9Sstevel@tonic-gate * These routines may be called from user, kernel, and interrupt contexts. 779*7c478bd9Sstevel@tonic-gate */ 780*7c478bd9Sstevel@tonic-gate void 781*7c478bd9Sstevel@tonic-gate ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version) 782*7c478bd9Sstevel@tonic-gate { 783*7c478bd9Sstevel@tonic-gate ndi_err_t *errp = ((ddi_acc_impl_t *)handle)->ahi_err; 784*7c478bd9Sstevel@tonic-gate 785*7c478bd9Sstevel@tonic-gate if (version != DDI_FME_VER0) { 786*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle); 787*7c478bd9Sstevel@tonic-gate 788*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP); 789*7c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "ddi_fm_dma_err_get: " 790*7c478bd9Sstevel@tonic-gate "Invalid driver version\n"); 791*7c478bd9Sstevel@tonic-gate } 792*7c478bd9Sstevel@tonic-gate 793*7c478bd9Sstevel@tonic-gate de->fme_status = errp->err_status; 794*7c478bd9Sstevel@tonic-gate de->fme_ena = errp->err_ena; 795*7c478bd9Sstevel@tonic-gate de->fme_flag = errp->err_expected; 796*7c478bd9Sstevel@tonic-gate de->fme_acc_handle = handle; 797*7c478bd9Sstevel@tonic-gate } 798*7c478bd9Sstevel@tonic-gate 799*7c478bd9Sstevel@tonic-gate void 800*7c478bd9Sstevel@tonic-gate ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version) 801*7c478bd9Sstevel@tonic-gate { 802*7c478bd9Sstevel@tonic-gate ndi_err_t *errp = &((ddi_dma_impl_t *)handle)->dmai_error; 803*7c478bd9Sstevel@tonic-gate 804*7c478bd9Sstevel@tonic-gate if (version != DDI_FME_VER0) { 805*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip, 806*7c478bd9Sstevel@tonic-gate DVR_EVER, NULL, DDI_NOSLEEP); 807*7c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "ddi_fm_dma_err_get: " 808*7c478bd9Sstevel@tonic-gate "Invalid driver version\n"); 809*7c478bd9Sstevel@tonic-gate } 810*7c478bd9Sstevel@tonic-gate 811*7c478bd9Sstevel@tonic-gate de->fme_status = errp->err_status; 812*7c478bd9Sstevel@tonic-gate de->fme_ena = errp->err_ena; 813*7c478bd9Sstevel@tonic-gate de->fme_flag = errp->err_expected; 814*7c478bd9Sstevel@tonic-gate de->fme_dma_handle = handle; 815*7c478bd9Sstevel@tonic-gate } 816*7c478bd9Sstevel@tonic-gate 817*7c478bd9Sstevel@tonic-gate void 818*7c478bd9Sstevel@tonic-gate i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status, 819*7c478bd9Sstevel@tonic-gate int flag) 820*7c478bd9Sstevel@tonic-gate { 821*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle); 822*7c478bd9Sstevel@tonic-gate ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle; 823*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl; 824*7c478bd9Sstevel@tonic-gate 825*7c478bd9Sstevel@tonic-gate i_hdlp->ahi_err->err_ena = ena; 826*7c478bd9Sstevel@tonic-gate i_hdlp->ahi_err->err_status = status; 827*7c478bd9Sstevel@tonic-gate i_hdlp->ahi_err->err_expected = flag; 828*7c478bd9Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1); 829*7c478bd9Sstevel@tonic-gate } 830*7c478bd9Sstevel@tonic-gate 831*7c478bd9Sstevel@tonic-gate void 832*7c478bd9Sstevel@tonic-gate i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status, 833*7c478bd9Sstevel@tonic-gate int flag) 834*7c478bd9Sstevel@tonic-gate { 835*7c478bd9Sstevel@tonic-gate ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle; 836*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl; 837*7c478bd9Sstevel@tonic-gate 838*7c478bd9Sstevel@tonic-gate hdlp->dmai_error.err_ena = ena; 839*7c478bd9Sstevel@tonic-gate hdlp->dmai_error.err_status = status; 840*7c478bd9Sstevel@tonic-gate hdlp->dmai_error.err_expected = flag; 841*7c478bd9Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1); 842*7c478bd9Sstevel@tonic-gate } 843