17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 23*7aec1d6eScindi * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * Fault Management for Device Drivers 317c478bd9Sstevel@tonic-gate * 327c478bd9Sstevel@tonic-gate * Device drivers wishing to participate in fault management may do so by 337c478bd9Sstevel@tonic-gate * first initializing their fault management state and capabilties via 347c478bd9Sstevel@tonic-gate * ddi_fm_init(). If the system supports the requested FM capabilities, 357c478bd9Sstevel@tonic-gate * the IO framework will intialize FM state and return a bit mask of the 367c478bd9Sstevel@tonic-gate * requested capabilities. 377c478bd9Sstevel@tonic-gate * 387c478bd9Sstevel@tonic-gate * If the system does not support the requested FM capabilities, 397c478bd9Sstevel@tonic-gate * the device driver must behave in accordance with the programming semantics 407c478bd9Sstevel@tonic-gate * defined below for the capabilities returned from ddi_fm_init(). 417c478bd9Sstevel@tonic-gate * ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be 427c478bd9Sstevel@tonic-gate * called from detach(9E) to perform FM clean-up. 437c478bd9Sstevel@tonic-gate * 447c478bd9Sstevel@tonic-gate * Driver Fault Management Capabilities 457c478bd9Sstevel@tonic-gate * 467c478bd9Sstevel@tonic-gate * DDI_FM_NOT_CAPABLE 477c478bd9Sstevel@tonic-gate * 487c478bd9Sstevel@tonic-gate * This is the default fault management capability for drivers. Drivers 497c478bd9Sstevel@tonic-gate * that implement no fault management capabilites or do not participate 507c478bd9Sstevel@tonic-gate * in fault management activities have their FM capability bitmask set 517c478bd9Sstevel@tonic-gate * to 0. 527c478bd9Sstevel@tonic-gate * 537c478bd9Sstevel@tonic-gate * DDI_FM_EREPORT_CAPABLE 547c478bd9Sstevel@tonic-gate * 557c478bd9Sstevel@tonic-gate * When this capability bit is set, drivers are expected to generate error 567c478bd9Sstevel@tonic-gate * report events via ddi_ereport_post() for the associated faults 577c478bd9Sstevel@tonic-gate * that are diagnosed by the IO fault manager DE. ddi_ereport_post() 587c478bd9Sstevel@tonic-gate * may be called in any context subject to the constraints specified 597c478bd9Sstevel@tonic-gate * by the interrupt iblock cookie returned during initialization. 607c478bd9Sstevel@tonic-gate * 617c478bd9Sstevel@tonic-gate * Error reports resulting from hardware component specific and common IO 627c478bd9Sstevel@tonic-gate * fault and driver defects must be accompanied by an Eversholt fault 637c478bd9Sstevel@tonic-gate * tree (.eft) by the Solaris fault manager (fmd(1M)) for 647c478bd9Sstevel@tonic-gate * diagnosis. 657c478bd9Sstevel@tonic-gate * 667c478bd9Sstevel@tonic-gate * DDI_FM_ERRCB_CAPABLE 677c478bd9Sstevel@tonic-gate * 687c478bd9Sstevel@tonic-gate * Device drivers are expected to implement and register an error 697c478bd9Sstevel@tonic-gate * handler callback function. ddi_fm_handler_register() and 707c478bd9Sstevel@tonic-gate * ddi_fm_handler_unregister() must be 717c478bd9Sstevel@tonic-gate * called in passive kernel context, typically during an attach(9E) 727c478bd9Sstevel@tonic-gate * or detach(9E) operation. When called by the FM IO framework, 737c478bd9Sstevel@tonic-gate * the callback function should check for error conditions for the 747c478bd9Sstevel@tonic-gate * hardware and software under its control. All detected errors 757c478bd9Sstevel@tonic-gate * should have ereport events generated for them. 767c478bd9Sstevel@tonic-gate * 777c478bd9Sstevel@tonic-gate * Upon completion of the error handler callback, the driver should 787c478bd9Sstevel@tonic-gate * return one of the following values: 797c478bd9Sstevel@tonic-gate * 807c478bd9Sstevel@tonic-gate * #define DDI_FM_OK - no error was detected 817c478bd9Sstevel@tonic-gate * #define DDI_FM_FATAL - a fatal error was detected 827c478bd9Sstevel@tonic-gate * #define DDI_FM_NONFATAL - a non-fatal error was detected 837c478bd9Sstevel@tonic-gate * #define DDI_FM_UNKNOWN - the error status is unknown 847c478bd9Sstevel@tonic-gate * 857c478bd9Sstevel@tonic-gate * To insure single threaded access to error handling callbacks, 867c478bd9Sstevel@tonic-gate * the device driver may use i_ddi_fm_handler_enter() and 877c478bd9Sstevel@tonic-gate * i_ddi_fm_handler_exit() when entering and exiting the callback. 887c478bd9Sstevel@tonic-gate * 897c478bd9Sstevel@tonic-gate * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE 907c478bd9Sstevel@tonic-gate * 917c478bd9Sstevel@tonic-gate * Device drivers are expected to set-up access and DMA handles 927c478bd9Sstevel@tonic-gate * with FM-specific attributes designed to allow nexus parent 937c478bd9Sstevel@tonic-gate * drivers to flag any errors seen during subsequent IO transactions. 947c478bd9Sstevel@tonic-gate * Drivers must set the devacc_attr_acc_flag member of their 957c478bd9Sstevel@tonic-gate * ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC. 967c478bd9Sstevel@tonic-gate * For DMA transactions, driver must set the dma_attr_flags of 977c478bd9Sstevel@tonic-gate * their ddi_dma_attr_t structures to DDI_DMA_FLAGERR. 987c478bd9Sstevel@tonic-gate * 997c478bd9Sstevel@tonic-gate * Upon completion of an IO transaction, device drivers are expected 1007c478bd9Sstevel@tonic-gate * to check the status of host-side hardware access and device-side 1017c478bd9Sstevel@tonic-gate * dma completions by calling ddi_acc_err_check() or ddi_dma_err_check() 1027c478bd9Sstevel@tonic-gate * respectively. If the handle is associated with an error detected by 1037c478bd9Sstevel@tonic-gate * the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena 1047c478bd9Sstevel@tonic-gate * and error expectation) is returned. If status of DDI_FM_NONFATAL or 1057c478bd9Sstevel@tonic-gate * DDI_FM_FATAL is returned, the ena is valid and the expectation flag 1067c478bd9Sstevel@tonic-gate * will be set to 1 if the error was unexpected (i.e. not the result 1077c478bd9Sstevel@tonic-gate * of a peek or poke type operation). 1087c478bd9Sstevel@tonic-gate * 1097c478bd9Sstevel@tonic-gate * ddi_acc_err_check() and ddi_dma_err_check() may be called in any 1107c478bd9Sstevel@tonic-gate * context subject to the constraints specified by the interrupt 1117c478bd9Sstevel@tonic-gate * iblock cookie returned during initialization. 1127c478bd9Sstevel@tonic-gate * 1137c478bd9Sstevel@tonic-gate * Device drivers should generate an access (DDI_FM_IO_ACC) or dma 1147c478bd9Sstevel@tonic-gate * (DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or 1157c478bd9Sstevel@tonic-gate * DDI_FM_FATAL is returned. 1167c478bd9Sstevel@tonic-gate * 1177c478bd9Sstevel@tonic-gate */ 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate #include <sys/types.h> 1207c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 1217c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 1227c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 1237c478bd9Sstevel@tonic-gate #include <sys/nvpair.h> 1247c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h> 1257c478bd9Sstevel@tonic-gate #include <sys/ndifm.h> 1267c478bd9Sstevel@tonic-gate #include <sys/ddifm.h> 1277c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 1287c478bd9Sstevel@tonic-gate #include <sys/ddi_isa.h> 1297c478bd9Sstevel@tonic-gate #include <sys/spl.h> 1307c478bd9Sstevel@tonic-gate #include <sys/varargs.h> 1317c478bd9Sstevel@tonic-gate #include <sys/systm.h> 1327c478bd9Sstevel@tonic-gate #include <sys/disp.h> 1337c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 1347c478bd9Sstevel@tonic-gate #include <sys/errorq_impl.h> 1357c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 1367c478bd9Sstevel@tonic-gate #include <sys/fm/util.h> 1377c478bd9Sstevel@tonic-gate #include <sys/fm/io/ddi.h> 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate #define ERPT_CLASS_SZ sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \ 1407c478bd9Sstevel@tonic-gate DDI_MAX_ERPT_CLASS + 2 1417c478bd9Sstevel@tonic-gate /* Globals */ 1427c478bd9Sstevel@tonic-gate int default_dmacache_sz = DEFAULT_DMACACHE_SZ; 1437c478bd9Sstevel@tonic-gate int default_acccache_sz = DEFAULT_ACCCACHE_SZ; 1447c478bd9Sstevel@tonic-gate int ddi_system_fmcap = 0; 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate static struct i_ddi_fmkstat ddifm_kstat_template = { 1477c478bd9Sstevel@tonic-gate {"erpt_dropped", KSTAT_DATA_UINT64 }, 1487c478bd9Sstevel@tonic-gate {"fm_cache_full", KSTAT_DATA_UINT64 }, 1497c478bd9Sstevel@tonic-gate {"fm_cache_grew", KSTAT_DATA_UINT64 }, 1507c478bd9Sstevel@tonic-gate {"acc_err", KSTAT_DATA_UINT64 }, 1517c478bd9Sstevel@tonic-gate {"dma_err", KSTAT_DATA_UINT64 } 1527c478bd9Sstevel@tonic-gate }; 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate /* 1557c478bd9Sstevel@tonic-gate * Update the service state following the detection of an 1567c478bd9Sstevel@tonic-gate * error. 1577c478bd9Sstevel@tonic-gate */ 1587c478bd9Sstevel@tonic-gate void 1597c478bd9Sstevel@tonic-gate ddi_fm_service_impact(dev_info_t *dip, int svc_impact) 1607c478bd9Sstevel@tonic-gate { 1617c478bd9Sstevel@tonic-gate mutex_enter(&(DEVI(dip)->devi_lock)); 1627c478bd9Sstevel@tonic-gate if (!DEVI_IS_DEVICE_OFFLINE(dip)) { 1637c478bd9Sstevel@tonic-gate switch (svc_impact) { 1647c478bd9Sstevel@tonic-gate case DDI_SERVICE_LOST: 1657c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_DOWN(dip); 1667c478bd9Sstevel@tonic-gate break; 1677c478bd9Sstevel@tonic-gate case DDI_SERVICE_DEGRADED: 1687c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_DEGRADED(dip); 1697c478bd9Sstevel@tonic-gate break; 1707c478bd9Sstevel@tonic-gate case DDI_SERVICE_RESTORED: 1717c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_UP(dip); 1727c478bd9Sstevel@tonic-gate break; 1737c478bd9Sstevel@tonic-gate case DDI_SERVICE_UNAFFECTED: 1747c478bd9Sstevel@tonic-gate default: 1757c478bd9Sstevel@tonic-gate break; 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate mutex_exit(&(DEVI(dip)->devi_lock)); 1797c478bd9Sstevel@tonic-gate } 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate static int 1827c478bd9Sstevel@tonic-gate erpt_post_sleep(dev_info_t *dip, const char *error_class, uint64_t ena, 1837c478bd9Sstevel@tonic-gate uint8_t version, va_list ap) 1847c478bd9Sstevel@tonic-gate { 1857c478bd9Sstevel@tonic-gate char *devid, *name; 1867c478bd9Sstevel@tonic-gate char device_path[MAXPATHLEN]; 1877c478bd9Sstevel@tonic-gate char ddi_error_class[ERPT_CLASS_SZ]; 1887c478bd9Sstevel@tonic-gate nvlist_t *ereport, *detector = NULL; 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * Driver defect - should not call with DDI_SLEEP while 1927c478bd9Sstevel@tonic-gate * in interrupt context 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate if (servicing_interrupt()) { 1957c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 1967c478bd9Sstevel@tonic-gate return (1); 1977c478bd9Sstevel@tonic-gate } 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate if ((ereport = fm_nvlist_create(NULL)) == NULL) 2007c478bd9Sstevel@tonic-gate return (1); 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate /* 2037c478bd9Sstevel@tonic-gate * Use the dev_path/devid for this device instance. 2047c478bd9Sstevel@tonic-gate */ 2057c478bd9Sstevel@tonic-gate detector = fm_nvlist_create(NULL); 2067c478bd9Sstevel@tonic-gate if (dip == ddi_root_node()) { 2077c478bd9Sstevel@tonic-gate device_path[0] = '/'; 2087c478bd9Sstevel@tonic-gate device_path[1] = '\0'; 2097c478bd9Sstevel@tonic-gate } else { 2107c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, device_path); 2117c478bd9Sstevel@tonic-gate } 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 2147c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devid) == DDI_SUCCESS) { 2157c478bd9Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 2167c478bd9Sstevel@tonic-gate device_path, devid); 2177c478bd9Sstevel@tonic-gate ddi_prop_free(devid); 2187c478bd9Sstevel@tonic-gate } else { 2197c478bd9Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 2207c478bd9Sstevel@tonic-gate device_path, NULL); 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate if (ena == 0) 2247c478bd9Sstevel@tonic-gate ena = fm_ena_generate(0, FM_ENA_FMT1); 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", 2277c478bd9Sstevel@tonic-gate DDI_IO_CLASS, error_class); 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate fm_ereport_set(ereport, version, ddi_error_class, 2307c478bd9Sstevel@tonic-gate ena, detector, NULL); 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate name = va_arg(ap, char *); 2337c478bd9Sstevel@tonic-gate (void) i_fm_payload_set(ereport, name, ap); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate fm_ereport_post(ereport, EVCH_SLEEP); 2367c478bd9Sstevel@tonic-gate fm_nvlist_destroy(ereport, FM_NVA_FREE); 2377c478bd9Sstevel@tonic-gate fm_nvlist_destroy(detector, FM_NVA_FREE); 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate return (0); 2407c478bd9Sstevel@tonic-gate } 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate static int 2437c478bd9Sstevel@tonic-gate erpt_post_nosleep(dev_info_t *dip, struct i_ddi_fmhdl *fmhdl, 2447c478bd9Sstevel@tonic-gate const char *error_class, uint64_t ena, uint8_t version, va_list ap) 2457c478bd9Sstevel@tonic-gate { 2467c478bd9Sstevel@tonic-gate char *name; 2477c478bd9Sstevel@tonic-gate char device_path[MAXPATHLEN]; 2487c478bd9Sstevel@tonic-gate char ddi_error_class[ERPT_CLASS_SZ]; 2497c478bd9Sstevel@tonic-gate nvlist_t *ereport, *detector; 2507c478bd9Sstevel@tonic-gate nv_alloc_t *nva; 2517c478bd9Sstevel@tonic-gate errorq_elem_t *eqep; 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate eqep = errorq_reserve(fmhdl->fh_errorq); 2547c478bd9Sstevel@tonic-gate if (eqep == NULL) 2557c478bd9Sstevel@tonic-gate return (1); 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep); 2587c478bd9Sstevel@tonic-gate nva = errorq_elem_nva(fmhdl->fh_errorq, eqep); 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate ASSERT(ereport); 2617c478bd9Sstevel@tonic-gate ASSERT(nva); 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate /* 2647c478bd9Sstevel@tonic-gate * Use the dev_path/devid for this device instance. 2657c478bd9Sstevel@tonic-gate */ 2667c478bd9Sstevel@tonic-gate detector = fm_nvlist_create(nva); 2677c478bd9Sstevel@tonic-gate if (dip == ddi_root_node()) { 2687c478bd9Sstevel@tonic-gate device_path[0] = '/'; 2697c478bd9Sstevel@tonic-gate device_path[1] = '\0'; 2707c478bd9Sstevel@tonic-gate } else { 2717c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, device_path); 2727c478bd9Sstevel@tonic-gate } 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 2757c478bd9Sstevel@tonic-gate device_path, NULL); 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate if (ena == 0) 2787c478bd9Sstevel@tonic-gate ena = fm_ena_generate(0, FM_ENA_FMT1); 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", 2817c478bd9Sstevel@tonic-gate DDI_IO_CLASS, error_class); 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate fm_ereport_set(ereport, version, ddi_error_class, 2847c478bd9Sstevel@tonic-gate ena, detector, NULL); 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate name = va_arg(ap, char *); 2877c478bd9Sstevel@tonic-gate (void) i_fm_payload_set(ereport, name, ap); 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC); 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate return (0); 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate void 2957c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, 2967c478bd9Sstevel@tonic-gate nvlist_t *errp, int sflag) 2977c478bd9Sstevel@tonic-gate { 2987c478bd9Sstevel@tonic-gate int i; 2997c478bd9Sstevel@tonic-gate int depth; 3007c478bd9Sstevel@tonic-gate char classp[DDI_DVR_MAX_CLASS]; 3017c478bd9Sstevel@tonic-gate caddr_t stkp; 3027c478bd9Sstevel@tonic-gate char *buf; 3037c478bd9Sstevel@tonic-gate char **stkpp; 3047c478bd9Sstevel@tonic-gate char *sym; 3057c478bd9Sstevel@tonic-gate pc_t stack[DDI_FM_STKDEPTH]; 3067c478bd9Sstevel@tonic-gate ulong_t off; 3077c478bd9Sstevel@tonic-gate dev_info_t *root_dip = ddi_root_node(); 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip))) 3107c478bd9Sstevel@tonic-gate return; 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate (void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT, 3137c478bd9Sstevel@tonic-gate error_class); 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate if (sflag == DDI_SLEEP) { 3167c478bd9Sstevel@tonic-gate depth = getpcstack(stack, DDI_FM_STKDEPTH); 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate /* Allocate array of char * for nvlist payload */ 3197c478bd9Sstevel@tonic-gate stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP); 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate /* 3227c478bd9Sstevel@tonic-gate * Allocate temporary 64-bit aligned buffer for stack 3237c478bd9Sstevel@tonic-gate * symbol strings 3247c478bd9Sstevel@tonic-gate */ 3257c478bd9Sstevel@tonic-gate buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate stkp = buf; 3287c478bd9Sstevel@tonic-gate for (i = 0; i < depth; ++i) { 3297c478bd9Sstevel@tonic-gate sym = kobj_getsymname(stack[i], &off); 3307c478bd9Sstevel@tonic-gate (void) snprintf(stkp, DDI_FM_SYM_SZ, 3317c478bd9Sstevel@tonic-gate "\t%s+%lx\n", sym ? sym : "?", off); 3327c478bd9Sstevel@tonic-gate stkpp[i] = stkp; 3337c478bd9Sstevel@tonic-gate stkp += DDI_FM_SYM_SZ; 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate if (errp) 3377c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 3387c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 3397c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 3407c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 3417c478bd9Sstevel@tonic-gate DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth, 3427c478bd9Sstevel@tonic-gate DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp, 3437c478bd9Sstevel@tonic-gate DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL); 3447c478bd9Sstevel@tonic-gate else 3457c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 3467c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 3477c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 3487c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 3497c478bd9Sstevel@tonic-gate DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth, 3507c478bd9Sstevel@tonic-gate DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp, 3517c478bd9Sstevel@tonic-gate NULL); 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate kmem_free(stkpp, depth * sizeof (char *)); 3547c478bd9Sstevel@tonic-gate kmem_free(buf, depth * DDI_FM_SYM_SZ); 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate } else { 3577c478bd9Sstevel@tonic-gate if (errp) 3587c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 3597c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 3607c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 3617c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 3627c478bd9Sstevel@tonic-gate DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL); 3637c478bd9Sstevel@tonic-gate else 3647c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(root_dip, 3657c478bd9Sstevel@tonic-gate classp, fm_ena_generate(0, FM_ENA_FMT1), sflag, 3667c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 3677c478bd9Sstevel@tonic-gate DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip), 3687c478bd9Sstevel@tonic-gate NULL); 3697c478bd9Sstevel@tonic-gate } 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate /* 3737c478bd9Sstevel@tonic-gate * Generate an error report for consumption by the Solaris Fault Manager, 3747c478bd9Sstevel@tonic-gate * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io. The 3757c478bd9Sstevel@tonic-gate * ENA should be set if this error is a result of an error status returned 3767c478bd9Sstevel@tonic-gate * from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise, an ENA 3777c478bd9Sstevel@tonic-gate * value of 0 is appropriate. 3787c478bd9Sstevel@tonic-gate * 3797c478bd9Sstevel@tonic-gate * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called 3807c478bd9Sstevel@tonic-gate * from user, kernel, interrupt or high-interrupt context. Otherwise, 3817c478bd9Sstevel@tonic-gate * ddi_fm_ereport_post() must be called from user or kernel context. 3827c478bd9Sstevel@tonic-gate */ 3837c478bd9Sstevel@tonic-gate void 3847c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, 3857c478bd9Sstevel@tonic-gate int sflag, ...) 3867c478bd9Sstevel@tonic-gate { 3877c478bd9Sstevel@tonic-gate int ret; 3887c478bd9Sstevel@tonic-gate char *name; 3897c478bd9Sstevel@tonic-gate data_type_t type; 3907c478bd9Sstevel@tonic-gate uint8_t version; 3917c478bd9Sstevel@tonic-gate va_list ap; 3927c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) { 3957c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, sflag); 3967c478bd9Sstevel@tonic-gate return; 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate va_start(ap, sflag); 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate /* First payload tuple should be the version */ 4047c478bd9Sstevel@tonic-gate name = va_arg(ap, char *); 4057c478bd9Sstevel@tonic-gate type = va_arg(ap, data_type_t); 4067c478bd9Sstevel@tonic-gate version = va_arg(ap, uint_t); 4077c478bd9Sstevel@tonic-gate if (strcmp(name, FM_VERSION) != 0 && type != DATA_TYPE_UINT8) { 4087c478bd9Sstevel@tonic-gate va_end(ap); 4097c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag); 4107c478bd9Sstevel@tonic-gate return; 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate if (sflag == DDI_SLEEP) 4147c478bd9Sstevel@tonic-gate ret = erpt_post_sleep(dip, error_class, ena, version, ap); 4157c478bd9Sstevel@tonic-gate else 4167c478bd9Sstevel@tonic-gate ret = erpt_post_nosleep(dip, fmhdl, error_class, ena, version, 4177c478bd9Sstevel@tonic-gate ap); 4187c478bd9Sstevel@tonic-gate va_end(ap); 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate if (ret != 0) 4217c478bd9Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1); 4227c478bd9Sstevel@tonic-gate 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate /* 4267c478bd9Sstevel@tonic-gate * Driver error handling entry. Prevents multiple simultaneous calls into 4277c478bd9Sstevel@tonic-gate * driver error handling callback. 4287c478bd9Sstevel@tonic-gate * 4297c478bd9Sstevel@tonic-gate * May be called from a context consistent with the iblock_cookie returned 4307c478bd9Sstevel@tonic-gate * in ddi_fm_init(). 4317c478bd9Sstevel@tonic-gate */ 4327c478bd9Sstevel@tonic-gate void 4337c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(dev_info_t *dip) 4347c478bd9Sstevel@tonic-gate { 4357c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl; 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate mutex_enter(&hdl->fh_lock); 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate /* 4417c478bd9Sstevel@tonic-gate * Driver error handling exit. 4427c478bd9Sstevel@tonic-gate * 4437c478bd9Sstevel@tonic-gate * May be called from a context consistent with the iblock_cookie returned 4447c478bd9Sstevel@tonic-gate * in ddi_fm_init(). 4457c478bd9Sstevel@tonic-gate */ 4467c478bd9Sstevel@tonic-gate void 4477c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(dev_info_t *dip) 4487c478bd9Sstevel@tonic-gate { 4497c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl; 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate mutex_exit(&hdl->fh_lock); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate /* 4557c478bd9Sstevel@tonic-gate * Register a fault manager error handler for this device instance 4567c478bd9Sstevel@tonic-gate * 4577c478bd9Sstevel@tonic-gate * This function must be called from a driver's attach(9E) routine. 4587c478bd9Sstevel@tonic-gate */ 4597c478bd9Sstevel@tonic-gate void 4607c478bd9Sstevel@tonic-gate ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler, 4617c478bd9Sstevel@tonic-gate void *impl_data) 4627c478bd9Sstevel@tonic-gate { 4637c478bd9Sstevel@tonic-gate dev_info_t *pdip; 4647c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *pfmhdl; 4657c478bd9Sstevel@tonic-gate struct i_ddi_errhdl *new_eh; 4667c478bd9Sstevel@tonic-gate struct i_ddi_fmtgt *tgt; 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* 4697c478bd9Sstevel@tonic-gate * Check for proper calling context. 4707c478bd9Sstevel@tonic-gate * The DDI configuration framework does not support 4717c478bd9Sstevel@tonic-gate * DR states to allow checking for proper invocation 4727c478bd9Sstevel@tonic-gate * from a DDI_ATTACH or DDI_RESUME. This limits context checking 4737c478bd9Sstevel@tonic-gate * to interrupt only. 4747c478bd9Sstevel@tonic-gate */ 4757c478bd9Sstevel@tonic-gate if (servicing_interrupt()) { 4767c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 4777c478bd9Sstevel@tonic-gate return; 4787c478bd9Sstevel@tonic-gate } 4797c478bd9Sstevel@tonic-gate 480*7aec1d6eScindi if (dip == ddi_root_node()) 481*7aec1d6eScindi pdip = dip; 482*7aec1d6eScindi else 4837c478bd9Sstevel@tonic-gate pdip = (dev_info_t *)DEVI(dip)->devi_parent; 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate ASSERT(pdip); 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) && 4887c478bd9Sstevel@tonic-gate DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) { 4897c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 4907c478bd9Sstevel@tonic-gate return; 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP); 4947c478bd9Sstevel@tonic-gate new_eh->eh_func = handler; 4957c478bd9Sstevel@tonic-gate new_eh->eh_impl = impl_data; 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate /* Add dip to parent's target list of registered error handlers */ 4987c478bd9Sstevel@tonic-gate tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP); 4997c478bd9Sstevel@tonic-gate tgt->ft_dip = dip; 5007c478bd9Sstevel@tonic-gate tgt->ft_errhdl = new_eh; 5017c478bd9Sstevel@tonic-gate 5027c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(pdip); 5037c478bd9Sstevel@tonic-gate pfmhdl = DEVI(pdip)->devi_fmhdl; 5047c478bd9Sstevel@tonic-gate ASSERT(pfmhdl); 5057c478bd9Sstevel@tonic-gate tgt->ft_next = pfmhdl->fh_tgts; 5067c478bd9Sstevel@tonic-gate pfmhdl->fh_tgts = tgt; 5077c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(pdip); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate /* 5117c478bd9Sstevel@tonic-gate * Unregister a fault manager error handler for this device instance 5127c478bd9Sstevel@tonic-gate * 5137c478bd9Sstevel@tonic-gate * This function must be called from a drivers attach(9E) or detach(9E) 5147c478bd9Sstevel@tonic-gate * routine. 5157c478bd9Sstevel@tonic-gate */ 5167c478bd9Sstevel@tonic-gate void 5177c478bd9Sstevel@tonic-gate ddi_fm_handler_unregister(dev_info_t *dip) 5187c478bd9Sstevel@tonic-gate { 5197c478bd9Sstevel@tonic-gate dev_info_t *pdip; 5207c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *pfmhdl; 5217c478bd9Sstevel@tonic-gate struct i_ddi_fmtgt *tgt, **ptgt; 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate /* 5247c478bd9Sstevel@tonic-gate * Check for proper calling context. 5257c478bd9Sstevel@tonic-gate * The DDI configuration framework does not support 5267c478bd9Sstevel@tonic-gate * DR states to allow checking for proper invocation 5277c478bd9Sstevel@tonic-gate * from a DDI_DETACH or DDI_SUSPEND. This limits context checking 5287c478bd9Sstevel@tonic-gate * to interrupt only. 5297c478bd9Sstevel@tonic-gate */ 5307c478bd9Sstevel@tonic-gate if (servicing_interrupt()) { 5317c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 5327c478bd9Sstevel@tonic-gate return; 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate 535*7aec1d6eScindi if (dip == ddi_root_node()) 536*7aec1d6eScindi pdip = dip; 537*7aec1d6eScindi else 5387c478bd9Sstevel@tonic-gate pdip = (dev_info_t *)DEVI(dip)->devi_parent; 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate ASSERT(pdip); 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) && 5437c478bd9Sstevel@tonic-gate DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) { 5447c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 5457c478bd9Sstevel@tonic-gate return; 5467c478bd9Sstevel@tonic-gate } 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(pdip); 5497c478bd9Sstevel@tonic-gate pfmhdl = DEVI(pdip)->devi_fmhdl; 5507c478bd9Sstevel@tonic-gate ASSERT(pfmhdl); 5517c478bd9Sstevel@tonic-gate ptgt = &pfmhdl->fh_tgts; 5527c478bd9Sstevel@tonic-gate for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) { 5537c478bd9Sstevel@tonic-gate if (dip == tgt->ft_dip) { 5547c478bd9Sstevel@tonic-gate *ptgt = tgt->ft_next; 5557c478bd9Sstevel@tonic-gate kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl)); 5567c478bd9Sstevel@tonic-gate kmem_free(tgt, sizeof (struct i_ddi_fmtgt)); 5577c478bd9Sstevel@tonic-gate break; 5587c478bd9Sstevel@tonic-gate } 5597c478bd9Sstevel@tonic-gate ptgt = &tgt->ft_next; 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(pdip); 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate /* 5677c478bd9Sstevel@tonic-gate * Initialize Fault Management capabilities for this device instance (dip). 5687c478bd9Sstevel@tonic-gate * When called with the following capabilities, data structures neccessary 5697c478bd9Sstevel@tonic-gate * for fault management activities are allocated and initialized. 5707c478bd9Sstevel@tonic-gate * 5717c478bd9Sstevel@tonic-gate * DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport 5727c478bd9Sstevel@tonic-gate * capable driver property. 5737c478bd9Sstevel@tonic-gate * 5747c478bd9Sstevel@tonic-gate * DDI_FM_ERRCB_CAPABLE - check with parent for ability to register 5757c478bd9Sstevel@tonic-gate * an error handler. 5767c478bd9Sstevel@tonic-gate * 5777c478bd9Sstevel@tonic-gate * DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk 5787c478bd9Sstevel@tonic-gate * driver property 5797c478bd9Sstevel@tonic-gate * 5807c478bd9Sstevel@tonic-gate * DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk 5817c478bd9Sstevel@tonic-gate * driver property 5827c478bd9Sstevel@tonic-gate * 5837c478bd9Sstevel@tonic-gate * A driver's FM capability level may not exceed that of its parent or 5847c478bd9Sstevel@tonic-gate * system-wide FM capability. The available capability level for this 5857c478bd9Sstevel@tonic-gate * device instance is returned in *fmcap. 5867c478bd9Sstevel@tonic-gate * 5877c478bd9Sstevel@tonic-gate * This function must be called from a driver's attach(9E) entry point. 5887c478bd9Sstevel@tonic-gate */ 5897c478bd9Sstevel@tonic-gate void 590*7aec1d6eScindi ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp) 5917c478bd9Sstevel@tonic-gate { 5927c478bd9Sstevel@tonic-gate struct dev_info *devi = DEVI(dip); 5937c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 594*7aec1d6eScindi ddi_iblock_cookie_t ibc; 5957c478bd9Sstevel@tonic-gate int pcap, newcap = DDI_FM_NOT_CAPABLE; 5967c478bd9Sstevel@tonic-gate 5977c478bd9Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) { 5987c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 5997c478bd9Sstevel@tonic-gate *fmcap = DDI_FM_NOT_CAPABLE; 6007c478bd9Sstevel@tonic-gate return; 6017c478bd9Sstevel@tonic-gate } 6027c478bd9Sstevel@tonic-gate 6037c478bd9Sstevel@tonic-gate if (DDI_FM_DEFAULT_CAP(*fmcap)) 6047c478bd9Sstevel@tonic-gate return; 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate /* 6077c478bd9Sstevel@tonic-gate * Check parent for supported FM level 6087c478bd9Sstevel@tonic-gate * and correct error handling PIL 6097c478bd9Sstevel@tonic-gate */ 6107c478bd9Sstevel@tonic-gate if (dip != ddi_root_node()) { 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate /* 6137c478bd9Sstevel@tonic-gate * Initialize the default ibc. The parent may change it 6147c478bd9Sstevel@tonic-gate * depending upon its capabilities. 6157c478bd9Sstevel@tonic-gate */ 616*7aec1d6eScindi ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL); 6177c478bd9Sstevel@tonic-gate 618*7aec1d6eScindi pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc); 6197c478bd9Sstevel@tonic-gate } else { 6207c478bd9Sstevel@tonic-gate pcap = *fmcap; 621*7aec1d6eScindi ibc = *ibcp; 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate /* Initialize the per-device instance FM handle */ 6257c478bd9Sstevel@tonic-gate fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP); 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip), 6287c478bd9Sstevel@tonic-gate ddi_get_instance(dip), "fm", "misc", 6297c478bd9Sstevel@tonic-gate KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) / 6307c478bd9Sstevel@tonic-gate sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) { 6317c478bd9Sstevel@tonic-gate mutex_destroy(&fmhdl->fh_lock); 6327c478bd9Sstevel@tonic-gate kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl)); 6337c478bd9Sstevel@tonic-gate *fmcap = DDI_FM_NOT_CAPABLE; 6347c478bd9Sstevel@tonic-gate return; 6357c478bd9Sstevel@tonic-gate } 6367c478bd9Sstevel@tonic-gate 6377c478bd9Sstevel@tonic-gate bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat, 6387c478bd9Sstevel@tonic-gate sizeof (struct i_ddi_fmkstat)); 6397c478bd9Sstevel@tonic-gate fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat; 6407c478bd9Sstevel@tonic-gate fmhdl->fh_ksp->ks_private = fmhdl; 6417c478bd9Sstevel@tonic-gate kstat_install(fmhdl->fh_ksp); 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate fmhdl->fh_dma_cache = NULL; 6447c478bd9Sstevel@tonic-gate fmhdl->fh_acc_cache = NULL; 6457c478bd9Sstevel@tonic-gate fmhdl->fh_tgts = NULL; 6467c478bd9Sstevel@tonic-gate fmhdl->fh_dip = dip; 647*7aec1d6eScindi fmhdl->fh_ibc = ibc; 6487c478bd9Sstevel@tonic-gate mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc); 6497c478bd9Sstevel@tonic-gate devi->devi_fmhdl = fmhdl; 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate /* 6527c478bd9Sstevel@tonic-gate * Initialize support for ereport generation 6537c478bd9Sstevel@tonic-gate */ 6547c478bd9Sstevel@tonic-gate if (DDI_FM_EREPORT_CAP(*fmcap) && 6557c478bd9Sstevel@tonic-gate DDI_FM_EREPORT_CAP(ddi_system_fmcap)) { 6567c478bd9Sstevel@tonic-gate fmhdl->fh_errorq = ereport_errorq; 6577c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6587c478bd9Sstevel@tonic-gate "fm-ereport-capable", 0) == 0) 6597c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 6607c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0); 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate newcap |= DDI_FM_EREPORT_CAPABLE; 6637c478bd9Sstevel@tonic-gate } 6647c478bd9Sstevel@tonic-gate 6657c478bd9Sstevel@tonic-gate /* 6667c478bd9Sstevel@tonic-gate * Need cooperation of the parent for error handling 6677c478bd9Sstevel@tonic-gate */ 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) { 6707c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6717c478bd9Sstevel@tonic-gate "fm-errcb-capable", 0) == 0) 6727c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 6737c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0); 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate newcap |= DDI_FM_ERRCB_CAPABLE; 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate /* 6797c478bd9Sstevel@tonic-gate * Support for DMA and Access error handling 6807c478bd9Sstevel@tonic-gate */ 6817c478bd9Sstevel@tonic-gate 6827c478bd9Sstevel@tonic-gate if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) { 683*7aec1d6eScindi i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc); 6847c478bd9Sstevel@tonic-gate 6857c478bd9Sstevel@tonic-gate /* Set-up dma chk capability prop */ 6867c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6877c478bd9Sstevel@tonic-gate "fm-dmachk-capable", 0) == 0) 6887c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 6897c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0); 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate newcap |= DDI_FM_DMACHK_CAPABLE; 6927c478bd9Sstevel@tonic-gate } 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) { 695*7aec1d6eScindi i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc); 6967c478bd9Sstevel@tonic-gate /* Set-up dma chk capability prop */ 6977c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6987c478bd9Sstevel@tonic-gate "fm-accchk-capable", 0) == 0) 6997c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 7007c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0); 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate newcap |= DDI_FM_ACCCHK_CAPABLE; 7037c478bd9Sstevel@tonic-gate } 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate /* 7067c478bd9Sstevel@tonic-gate * Return the capability support available 7077c478bd9Sstevel@tonic-gate * to this driver instance 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate fmhdl->fh_cap = newcap; 7107c478bd9Sstevel@tonic-gate *fmcap = newcap; 711*7aec1d6eScindi 712*7aec1d6eScindi if (ibcp != NULL) 713*7aec1d6eScindi *ibcp = ibc; 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate /* 7177c478bd9Sstevel@tonic-gate * Finalize Fault Management activities for this device instance. 7187c478bd9Sstevel@tonic-gate * Outstanding IO transaction must be completed prior to calling 7197c478bd9Sstevel@tonic-gate * this routine. All previously allocated resources and error handler 7207c478bd9Sstevel@tonic-gate * registration are cleared and deallocated. 7217c478bd9Sstevel@tonic-gate * 7227c478bd9Sstevel@tonic-gate * This function must be called from a driver's detach(9E) entry point. 7237c478bd9Sstevel@tonic-gate */ 7247c478bd9Sstevel@tonic-gate void 7257c478bd9Sstevel@tonic-gate ddi_fm_fini(dev_info_t *dip) 7267c478bd9Sstevel@tonic-gate { 7277c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 7287c478bd9Sstevel@tonic-gate 7297c478bd9Sstevel@tonic-gate if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) { 7307c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 7317c478bd9Sstevel@tonic-gate return; 7327c478bd9Sstevel@tonic-gate } 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate if (DDI_FM_DEFAULT_CAP(fmhdl->fh_cap)) 7357c478bd9Sstevel@tonic-gate return; 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate kstat_delete(fmhdl->fh_ksp); 7407c478bd9Sstevel@tonic-gate 7417c478bd9Sstevel@tonic-gate if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) { 7427c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 7437c478bd9Sstevel@tonic-gate "fm-ereport-capable"); 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate 7467c478bd9Sstevel@tonic-gate if (dip != ddi_root_node()) { 7477c478bd9Sstevel@tonic-gate ddi_fm_handler_unregister(dip); 7487c478bd9Sstevel@tonic-gate 7497c478bd9Sstevel@tonic-gate if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) || 7507c478bd9Sstevel@tonic-gate DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 7517c478bd9Sstevel@tonic-gate if (fmhdl->fh_dma_cache != NULL) { 7527c478bd9Sstevel@tonic-gate i_ndi_fmc_destroy(fmhdl->fh_dma_cache); 7537c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 7547c478bd9Sstevel@tonic-gate "fm-dmachk-capable"); 7557c478bd9Sstevel@tonic-gate } 7567c478bd9Sstevel@tonic-gate if (fmhdl->fh_acc_cache != NULL) { 7577c478bd9Sstevel@tonic-gate i_ndi_fmc_destroy(fmhdl->fh_acc_cache); 7587c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 7597c478bd9Sstevel@tonic-gate "fm-accachk-capable"); 7607c478bd9Sstevel@tonic-gate } 7617c478bd9Sstevel@tonic-gate } 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate i_ndi_busop_fm_fini(dip); 7647c478bd9Sstevel@tonic-gate } 7657c478bd9Sstevel@tonic-gate 7667c478bd9Sstevel@tonic-gate kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl)); 7677c478bd9Sstevel@tonic-gate DEVI(dip)->devi_fmhdl = NULL; 7687c478bd9Sstevel@tonic-gate } 7697c478bd9Sstevel@tonic-gate 7707c478bd9Sstevel@tonic-gate /* 7717c478bd9Sstevel@tonic-gate * Return the fault management capability level for this device instance. 7727c478bd9Sstevel@tonic-gate * 7737c478bd9Sstevel@tonic-gate * This function may be called from user, kernel, or interrupt context. 7747c478bd9Sstevel@tonic-gate */ 7757c478bd9Sstevel@tonic-gate int 7767c478bd9Sstevel@tonic-gate ddi_fm_capable(dev_info_t *dip) 7777c478bd9Sstevel@tonic-gate { 7787c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate if (fmhdl == NULL) 7817c478bd9Sstevel@tonic-gate return (DDI_FM_NOT_CAPABLE); 7827c478bd9Sstevel@tonic-gate 7837c478bd9Sstevel@tonic-gate return (fmhdl->fh_cap); 7847c478bd9Sstevel@tonic-gate } 7857c478bd9Sstevel@tonic-gate 7867c478bd9Sstevel@tonic-gate /* 7877c478bd9Sstevel@tonic-gate * Routines to set and get error information for/from an access or dma handle 7887c478bd9Sstevel@tonic-gate * 7897c478bd9Sstevel@tonic-gate * These routines may be called from user, kernel, and interrupt contexts. 7907c478bd9Sstevel@tonic-gate */ 7917c478bd9Sstevel@tonic-gate void 7927c478bd9Sstevel@tonic-gate ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version) 7937c478bd9Sstevel@tonic-gate { 7947c478bd9Sstevel@tonic-gate ndi_err_t *errp = ((ddi_acc_impl_t *)handle)->ahi_err; 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate if (version != DDI_FME_VER0) { 7977c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle); 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP); 8007c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "ddi_fm_dma_err_get: " 8017c478bd9Sstevel@tonic-gate "Invalid driver version\n"); 8027c478bd9Sstevel@tonic-gate } 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate de->fme_status = errp->err_status; 8057c478bd9Sstevel@tonic-gate de->fme_ena = errp->err_ena; 8067c478bd9Sstevel@tonic-gate de->fme_flag = errp->err_expected; 8077c478bd9Sstevel@tonic-gate de->fme_acc_handle = handle; 8087c478bd9Sstevel@tonic-gate } 8097c478bd9Sstevel@tonic-gate 8107c478bd9Sstevel@tonic-gate void 8117c478bd9Sstevel@tonic-gate ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version) 8127c478bd9Sstevel@tonic-gate { 8137c478bd9Sstevel@tonic-gate ndi_err_t *errp = &((ddi_dma_impl_t *)handle)->dmai_error; 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate if (version != DDI_FME_VER0) { 8167c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip, 8177c478bd9Sstevel@tonic-gate DVR_EVER, NULL, DDI_NOSLEEP); 8187c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "ddi_fm_dma_err_get: " 8197c478bd9Sstevel@tonic-gate "Invalid driver version\n"); 8207c478bd9Sstevel@tonic-gate } 8217c478bd9Sstevel@tonic-gate 8227c478bd9Sstevel@tonic-gate de->fme_status = errp->err_status; 8237c478bd9Sstevel@tonic-gate de->fme_ena = errp->err_ena; 8247c478bd9Sstevel@tonic-gate de->fme_flag = errp->err_expected; 8257c478bd9Sstevel@tonic-gate de->fme_dma_handle = handle; 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate 8287c478bd9Sstevel@tonic-gate void 8297c478bd9Sstevel@tonic-gate i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status, 8307c478bd9Sstevel@tonic-gate int flag) 8317c478bd9Sstevel@tonic-gate { 8327c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle); 8337c478bd9Sstevel@tonic-gate ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle; 8347c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl; 8357c478bd9Sstevel@tonic-gate 8367c478bd9Sstevel@tonic-gate i_hdlp->ahi_err->err_ena = ena; 8377c478bd9Sstevel@tonic-gate i_hdlp->ahi_err->err_status = status; 8387c478bd9Sstevel@tonic-gate i_hdlp->ahi_err->err_expected = flag; 8397c478bd9Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1); 8407c478bd9Sstevel@tonic-gate } 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate void 8437c478bd9Sstevel@tonic-gate i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status, 8447c478bd9Sstevel@tonic-gate int flag) 8457c478bd9Sstevel@tonic-gate { 8467c478bd9Sstevel@tonic-gate ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle; 8477c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl; 8487c478bd9Sstevel@tonic-gate 8497c478bd9Sstevel@tonic-gate hdlp->dmai_error.err_ena = ena; 8507c478bd9Sstevel@tonic-gate hdlp->dmai_error.err_status = status; 8517c478bd9Sstevel@tonic-gate hdlp->dmai_error.err_expected = flag; 8527c478bd9Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1); 8537c478bd9Sstevel@tonic-gate } 854