1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 /*
  26  * Fault Management for Device Drivers
  27  *
  28  * Device drivers wishing to participate in fault management may do so by
  29  * first initializing their fault management state and capabilties via
  30  * ddi_fm_init(). If the system supports the requested FM capabilities,
  31  * the IO framework will intialize FM state and return a bit mask of the
  32  * requested capabilities.
  33  *
  34  * If the system does not support the requested FM capabilities,
  35  * the device driver must behave in accordance with the programming semantics
  36  * defined below for the capabilities returned from ddi_fm_init().
  37  * ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be
  38  * called from detach(9E) to perform FM clean-up.
  39  *
  40  * Driver Fault Management Capabilities
  41  *
  42  * DDI_FM_NOT_CAPABLE
  43  *
  44  *      This is the default fault management capability for drivers.  Drivers
  45  *      that implement no fault management capabilites or do not participate
  46  *      in fault management activities have their FM capability bitmask set
  47  *      to 0.
  48  *
  49  * DDI_FM_EREPORT_CAPABLE
  50  *
  51  *      When this capability bit is set, drivers are expected to generate error
  52  *      report events via ddi_ereport_post() for the associated faults
  53  *      that are diagnosed by the IO fault manager DE.  ddi_ereport_post()
  54  *      may be called in any context subject to the constraints specified
  55  *      by the interrupt iblock cookie  returned during initialization.
  56  *
  57  *      Error reports resulting from hardware component specific and common IO
  58  *      fault and driver defects must be accompanied by an Eversholt fault
  59  *      tree (.eft) by the Solaris fault manager (fmd(1M)) for
  60  *      diagnosis.
  61  *
  62  * DDI_FM_ERRCB_CAPABLE
  63  *
  64  *      Device drivers are expected to implement and register an error
  65  *      handler callback function.  ddi_fm_handler_register() and
  66  *      ddi_fm_handler_unregister() must be
  67  *      called in passive kernel context, typically during an attach(9E)
  68  *      or detach(9E) operation.  When called by the FM IO framework,
  69  *      the callback function should check for error conditions for the
  70  *      hardware and software under its control.  All detected errors
  71  *      should have ereport events generated for them.
  72  *
  73  *      Upon completion of the error handler callback, the driver should
  74  *      return one of the following values:
  75  *
  76  *      #define DDI_FM_OK - no error was detected
  77  *      #define DDI_FM_FATAL - a fatal error was detected
  78  *      #define DDI_FM_NONFATAL - a non-fatal error was detected
  79  *      #define DDI_FM_UNKNOWN - the error status is unknown
  80  *
  81  *      To insure single threaded access to error handling callbacks,
  82  *      the device driver may use i_ddi_fm_handler_enter() and
  83  *      i_ddi_fm_handler_exit() when entering and exiting the callback.
  84  *
  85  * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE
  86  *
  87  *      Device drivers are expected to set-up access and DMA handles
  88  *      with FM-specific attributes designed to allow nexus parent
  89  *      drivers to flag any errors seen during subsequent IO transactions.
  90  *      Drivers must set the devacc_attr_acc_flag member of their
  91  *      ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC.
  92  *      For DMA transactions, driver must set the dma_attr_flags of
  93  *      their ddi_dma_attr_t structures to DDI_DMA_FLAGERR.
  94  *
  95  *      Upon completion of an IO transaction, device drivers are expected
  96  *      to check the status of host-side hardware access and device-side
  97  *      dma completions by calling ddi_acc_err_check() or ddi_dma_err_check()
  98  *      respectively. If the handle is associated with an error detected by
  99  *      the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena
 100  *      and error expectation) is returned.  If status of DDI_FM_NONFATAL or
 101  *      DDI_FM_FATAL is returned, the ena is valid and the expectation flag
 102  *      will be set to 1 if the error was unexpected (i.e. not the result
 103  *      of a peek or poke type operation).
 104  *
 105  *      ddi_acc_err_check() and ddi_dma_err_check() may be called in any
 106  *      context subject to the constraints specified by the interrupt
 107  *      iblock cookie returned during initialization.
 108  *
 109  *      Device drivers should generate an access (DDI_FM_IO_ACC) or dma
 110  *      (DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or
 111  *      DDI_FM_FATAL is returned.
 112  *
 113  */
 114 
 115 #include <sys/types.h>
 116 #include <sys/sunddi.h>
 117 #include <sys/sunndi.h>
 118 #include <sys/kmem.h>
 119 #include <sys/nvpair.h>
 120 #include <sys/fm/protocol.h>
 121 #include <sys/ndifm.h>
 122 #include <sys/ddifm.h>
 123 #include <sys/ddi_impldefs.h>
 124 #include <sys/ddi_isa.h>
 125 #include <sys/spl.h>
 126 #include <sys/varargs.h>
 127 #include <sys/systm.h>
 128 #include <sys/disp.h>
 129 #include <sys/atomic.h>
 130 #include <sys/errorq_impl.h>
 131 #include <sys/kobj.h>
 132 #include <sys/fm/util.h>
 133 #include <sys/fm/io/ddi.h>
 134 
 135 #define ERPT_CLASS_SZ   sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \
 136                             DDI_MAX_ERPT_CLASS + 2
 137 /* Globals */
 138 int default_dmacache_sz = DEFAULT_DMACACHE_SZ;
 139 int default_acccache_sz = DEFAULT_ACCCACHE_SZ;
 140 int ddi_system_fmcap = 0;
 141 
 142 static struct i_ddi_fmkstat ddifm_kstat_template = {
 143         {"erpt_dropped", KSTAT_DATA_UINT64 },
 144         {"fm_cache_miss", KSTAT_DATA_UINT64 },
 145         {"fm_cache_full", KSTAT_DATA_UINT64 },
 146         {"acc_err", KSTAT_DATA_UINT64 },
 147         {"dma_err", KSTAT_DATA_UINT64 }
 148 };
 149 
 150 /*
 151  * Update the service state following the detection of an
 152  * error.
 153  */
 154 void
 155 ddi_fm_service_impact(dev_info_t *dip, int svc_impact)
 156 {
 157         uint64_t ena;
 158         char buf[FM_MAX_CLASS];
 159 
 160         ena = fm_ena_generate(0, FM_ENA_FMT1);
 161         mutex_enter(&(DEVI(dip)->devi_lock));
 162         if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
 163                 switch (svc_impact) {
 164                 case DDI_SERVICE_LOST:
 165                         DEVI_SET_DEVICE_DOWN(dip);
 166                         (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
 167                             DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_LOST);
 168                         ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
 169                             FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
 170                             NULL);
 171                         break;
 172                 case DDI_SERVICE_DEGRADED:
 173                         DEVI_SET_DEVICE_DEGRADED(dip);
 174                         if (DEVI_IS_DEVICE_DEGRADED(dip)) {
 175                                 (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
 176                                     DDI_FM_SERVICE_IMPACT,
 177                                     DDI_FM_SERVICE_DEGRADED);
 178                                 ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
 179                                     FM_VERSION, DATA_TYPE_UINT8,
 180                                     FM_EREPORT_VERS0, NULL);
 181                         } else if (DEVI_IS_DEVICE_DOWN(dip)) {
 182                                 (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
 183                                     DDI_FM_SERVICE_IMPACT,
 184                                     DDI_FM_SERVICE_LOST);
 185                                 ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
 186                                     FM_VERSION, DATA_TYPE_UINT8,
 187                                     FM_EREPORT_VERS0, NULL);
 188                         }
 189                         break;
 190                 case DDI_SERVICE_RESTORED:
 191                         DEVI_SET_DEVICE_UP(dip);
 192                         (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
 193                             DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_RESTORED);
 194                         ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
 195                             FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
 196                             NULL);
 197                         break;
 198                 case DDI_SERVICE_UNAFFECTED:
 199                         (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
 200                             DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_UNAFFECTED);
 201                         ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
 202                             FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
 203                             NULL);
 204                         break;
 205                 default:
 206                         break;
 207                 }
 208         }
 209         mutex_exit(&(DEVI(dip)->devi_lock));
 210 }
 211 
 212 void
 213 i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class,
 214     nvlist_t *errp, int sflag)
 215 {
 216         int i;
 217         int depth;
 218         char classp[DDI_DVR_MAX_CLASS];
 219         caddr_t stkp;
 220         char *buf;
 221         char **stkpp;
 222         char *sym;
 223         pc_t stack[DDI_FM_STKDEPTH];
 224         ulong_t off;
 225         dev_info_t *root_dip = ddi_root_node();
 226 
 227         if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip)))
 228                 return;
 229 
 230         (void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT,
 231             error_class);
 232 
 233         if (sflag == DDI_SLEEP) {
 234                 depth = getpcstack(stack, DDI_FM_STKDEPTH);
 235 
 236                 /* Allocate array of char * for nvlist payload */
 237                 stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP);
 238 
 239                 /*
 240                  * Allocate temporary 64-bit aligned buffer for stack
 241                  * symbol strings
 242                  */
 243                 buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP);
 244 
 245                 stkp = buf;
 246                 for (i = 0; i < depth; ++i) {
 247                         sym = kobj_getsymname(stack[i], &off);
 248                         (void) snprintf(stkp, DDI_FM_SYM_SZ,
 249                             "\t%s+%lx\n", sym ? sym : "?", off);
 250                         stkpp[i] = stkp;
 251                         stkp += DDI_FM_SYM_SZ;
 252                 }
 253 
 254                 if (errp)
 255                         ddi_fm_ereport_post(root_dip,
 256                             classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
 257                             FM_VERSION, DATA_TYPE_UINT8, 0,
 258                             DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
 259                             DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
 260                             DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
 261                             DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
 262                 else
 263                         ddi_fm_ereport_post(root_dip,
 264                             classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
 265                             FM_VERSION, DATA_TYPE_UINT8, 0,
 266                             DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
 267                             DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
 268                             DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
 269                             NULL);
 270 
 271                 kmem_free(stkpp, depth * sizeof (char *));
 272                 kmem_free(buf, depth * DDI_FM_SYM_SZ);
 273 
 274         } else {
 275                 if (errp)
 276                         ddi_fm_ereport_post(root_dip,
 277                             classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
 278                             FM_VERSION, DATA_TYPE_UINT8, 0,
 279                             DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
 280                             DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
 281                 else
 282                         ddi_fm_ereport_post(root_dip,
 283                             classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
 284                             FM_VERSION, DATA_TYPE_UINT8, 0,
 285                             DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
 286                             NULL);
 287         }
 288 }
 289 
 290 /*
 291  * fm_dev_ereport_postv: Common consolidation private interface to
 292  * post a device tree oriented dev_scheme ereport. The device tree is
 293  * composed of the following entities: devinfo nodes, minor nodes, and
 294  * pathinfo nodes. All entities are associated with some devinfo node,
 295  * either directly or indirectly. The intended devinfo node association
 296  * for the ereport is communicated by the 'dip' argument. A minor node,
 297  * an entity below 'dip', is represented by a non-null 'minor_name'
 298  * argument. An application specific caller, like scsi_fm_ereport_post,
 299  * can override the devinfo path with a pathinfo path via a non-null
 300  * 'devpath' argument - in this case 'dip' is the MPXIO client node and
 301  * devpath should be the path through the pHCI devinfo node to the
 302  * pathinfo node.
 303  *
 304  * This interface also allows the caller to decide if the error being
 305  * reported is know to be associated with a specific device identity
 306  * via the 'devid' argument. The caller needs to control wether the
 307  * devid appears as an authority in the FMRI because for some types of
 308  * errors, like transport errors, the identity of the device on the
 309  * other end of the transport is not guaranteed to be the current
 310  * identity of the dip. For transport errors the caller should specify
 311  * a NULL devid, even when there is a valid devid associated with the dip.
 312  *
 313  * The ddi_fm_ereport_post() implementation calls this interface with
 314  * just a dip: devpath, minor_name, and devid are all NULL. The
 315  * scsi_fm_ereport_post() implementation may call this interface with
 316  * non-null devpath, minor_name, and devid arguments depending on
 317  * wether MPXIO is enabled, and wether a transport or non-transport
 318  * error is being posted.
 319  *
 320  * Additional event payload is specified via the varargs plist and, if
 321  * not NULL, the nvlist passed in (such an nvlist will be merged into
 322  * the payload; the caller is responsible for freeing this nvlist).
 323  * Do not specify any high-level protocol event member names as part of the
 324  * payload - eg no payload to be named "class", "version", "detector" etc
 325  * or they will replace the members we construct here.
 326  *
 327  * The 'target-port-l0id' argument is SCSI specific. It is used
 328  * by SCSI enumeration code when a devid is unavailable. If non-NULL
 329  * the property-value becomes part of the ereport detector. The value
 330  * specified might match one of the target-port-l0ids values of a
 331  * libtopo disk chassis node. When libtopo finds a disk with a guaranteed
 332  * unique wWWN target-port of a single-lun 'real' disk, it can add
 333  * the target-port value to the libtopo disk chassis node target-port-l0ids
 334  * string array property. Kernel code has no idea if this type of
 335  * libtopo chassis node exists, or if matching will in fact occur.
 336  */
 337 void
 338 fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip,
 339     const char *devpath, const char *minor_name, const char *devid,
 340     const char *tpl0, const char *error_class, uint64_t ena, int sflag,
 341     nvlist_t *pl, va_list ap)
 342 {
 343         nv_alloc_t              *nva = NULL;
 344         struct i_ddi_fmhdl      *fmhdl = NULL;
 345         errorq_elem_t           *eqep;
 346         nvlist_t                *ereport = NULL;
 347         nvlist_t                *detector = NULL;
 348         char                    *name;
 349         data_type_t             type;
 350         uint8_t                 version;
 351         char                    class[ERPT_CLASS_SZ];
 352         char                    path[MAXPATHLEN];
 353 
 354         ASSERT(ap != NULL);     /* must supply at least ereport version */
 355         ASSERT(dip && eqdip && error_class);
 356 
 357         /*
 358          * This interface should be called with a fm_capable eqdip. The
 359          * ddi_fm_ereport_post* interfaces call with eqdip == dip,
 360          * ndi_fm_ereport_post* interfaces call with eqdip == ddi_parent(dip).
 361          */
 362         if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip)))
 363                 goto err;
 364 
 365         /* get ereport nvlist handle */
 366         if ((sflag == DDI_SLEEP) && !panicstr) {
 367                 /*
 368                  * Driver defect - should not call with DDI_SLEEP while in
 369                  * interrupt context.
 370                  */
 371                 if (servicing_interrupt()) {
 372                         i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, sflag);
 373                         goto err;
 374                 }
 375 
 376                 /* Use normal interfaces to allocate memory. */
 377                 if ((ereport = fm_nvlist_create(NULL)) == NULL)
 378                         goto err;
 379                 ASSERT(nva == NULL);
 380         } else {
 381                 /* Use errorq interfaces to avoid memory allocation. */
 382                 fmhdl = DEVI(eqdip)->devi_fmhdl;
 383                 ASSERT(fmhdl);
 384                 eqep = errorq_reserve(fmhdl->fh_errorq);
 385                 if (eqep == NULL)
 386                         goto err;
 387 
 388                 ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep);
 389                 nva = errorq_elem_nva(fmhdl->fh_errorq, eqep);
 390                 ASSERT(nva);
 391         }
 392         ASSERT(ereport);
 393 
 394         /*
 395          * Form parts of an ereport:
 396          *      A: version
 397          *      B: error_class
 398          *      C: ena
 399          *      D: detector     (path and optional devid authority)
 400          *      E: payload
 401          *
 402          * A: ereport version: first payload tuple must be the version.
 403          */
 404         name = va_arg(ap, char *);
 405         type = va_arg(ap, data_type_t);
 406         version = va_arg(ap, uint_t);
 407         if ((strcmp(name, FM_VERSION) != 0) || (type != DATA_TYPE_UINT8)) {
 408                 i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag);
 409                 goto err;
 410         }
 411 
 412         /* B: ereport error_class: add "io." prefix to class. */
 413         (void) snprintf(class, ERPT_CLASS_SZ, "%s.%s",
 414             DDI_IO_CLASS, error_class);
 415 
 416         /* C: ereport ena: if not passed in, generate new ena. */
 417         if (ena == 0)
 418                 ena = fm_ena_generate(0, FM_ENA_FMT1);
 419 
 420         /* D: detector: form dev scheme fmri with path and devid. */
 421         if (devpath) {
 422                 (void) strlcpy(path, devpath, sizeof (path));
 423         } else {
 424                 /* derive devpath from dip */
 425                 if (dip == ddi_root_node())
 426                         (void) strcpy(path, "/");
 427                 else
 428                         (void) ddi_pathname(dip, path);
 429         }
 430         if (minor_name) {
 431                 (void) strlcat(path, ":", sizeof (path));
 432                 (void) strlcat(path, minor_name, sizeof (path));
 433         }
 434         detector = fm_nvlist_create(nva);
 435         fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path,
 436             devid, tpl0);
 437 
 438         /* Pull parts of ereport together into ereport. */
 439         fm_ereport_set(ereport, version, class, ena, detector, NULL);
 440 
 441         /* Merge any preconstructed payload into the event. */
 442         if (pl)
 443                 (void) nvlist_merge(ereport, pl, 0);
 444 
 445         /* Add any remaining (after version) varargs payload to ereport. */
 446         name = va_arg(ap, char *);
 447         (void) i_fm_payload_set(ereport, name, ap);
 448 
 449         /* Post the ereport. */
 450         if (nva)
 451                 errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC);
 452         else
 453                 fm_ereport_post(ereport, EVCH_SLEEP);
 454         goto out;
 455 
 456         /* Count errors as drops. */
 457 err:    if (fmhdl)
 458                 atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1);
 459 
 460         /* Free up nvlists if normal interfaces were used to allocate memory */
 461 out:    if (ereport && (nva == NULL))
 462                 fm_nvlist_destroy(ereport, FM_NVA_FREE);
 463         if (detector && (nva == NULL))
 464                 fm_nvlist_destroy(detector, FM_NVA_FREE);
 465 }
 466 
 467 /*
 468  * Generate an error report for consumption by the Solaris Fault Manager,
 469  * fmd(1M).  Valid ereport classes are defined in /usr/include/sys/fm/io.
 470  *
 471  * The ENA should be set if this error is a result of an error status
 472  * returned from ddi_dma_err_check() or ddi_acc_err_check().  Otherwise,
 473  * an ENA value of 0 is appropriate.
 474  *
 475  * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called
 476  * from user, kernel, interrupt or high-interrupt context.  Otherwise,
 477  * ddi_fm_ereport_post() must be called from user or kernel context.
 478  *
 479  * The ndi_interfaces are provided for use by nexus drivers to post
 480  * ereports about children who may not themselves be fm_capable.
 481  *
 482  * All interfaces end up in the common fm_dev_ereport_postv code above.
 483  */
 484 void
 485 ddi_fm_ereport_post(dev_info_t *dip,
 486     const char *error_class, uint64_t ena, int sflag, ...)
 487 {
 488         va_list ap;
 489 
 490         ASSERT(dip && error_class);
 491         va_start(ap, sflag);
 492         fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL, NULL,
 493             error_class, ena, sflag, NULL, ap);
 494         va_end(ap);
 495 }
 496 
 497 void
 498 ndi_fm_ereport_post(dev_info_t *dip,
 499     const char *error_class, uint64_t ena, int sflag, ...)
 500 {
 501         va_list ap;
 502 
 503         ASSERT(dip && error_class && (sflag == DDI_SLEEP));
 504         va_start(ap, sflag);
 505         fm_dev_ereport_postv(dip, ddi_get_parent(dip), NULL, NULL, NULL, NULL,
 506             error_class, ena, sflag, NULL, ap);
 507         va_end(ap);
 508 }
 509 
 510 /*
 511  * Driver error handling entry.  Prevents multiple simultaneous calls into
 512  * driver error handling callback.
 513  *
 514  * May be called from a context consistent with the iblock_cookie returned
 515  * in ddi_fm_init().
 516  */
 517 void
 518 i_ddi_fm_handler_enter(dev_info_t *dip)
 519 {
 520         struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
 521 
 522         mutex_enter(&hdl->fh_lock);
 523         hdl->fh_lock_owner = curthread;
 524 }
 525 
 526 /*
 527  * Driver error handling exit.
 528  *
 529  * May be called from a context consistent with the iblock_cookie returned
 530  * in ddi_fm_init().
 531  */
 532 void
 533 i_ddi_fm_handler_exit(dev_info_t *dip)
 534 {
 535         struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
 536 
 537         hdl->fh_lock_owner = NULL;
 538         mutex_exit(&hdl->fh_lock);
 539 }
 540 
 541 boolean_t
 542 i_ddi_fm_handler_owned(dev_info_t *dip)
 543 {
 544         struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
 545 
 546         return (hdl->fh_lock_owner == curthread);
 547 }
 548 
 549 /*
 550  * Register a fault manager error handler for this device instance
 551  *
 552  * This function must be called from a driver's attach(9E) routine.
 553  */
 554 void
 555 ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler,
 556     void *impl_data)
 557 {
 558         dev_info_t *pdip;
 559         struct i_ddi_fmhdl *pfmhdl;
 560         struct i_ddi_errhdl *new_eh;
 561         struct i_ddi_fmtgt *tgt;
 562 
 563         /*
 564          * Check for proper calling context.
 565          * The DDI configuration framework does not support
 566          * DR states to allow checking for proper invocation
 567          * from a DDI_ATTACH or DDI_RESUME.  This limits context checking
 568          * to interrupt only.
 569          */
 570         if (servicing_interrupt()) {
 571                 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
 572                 return;
 573         }
 574 
 575         if (dip == ddi_root_node())
 576                 pdip = dip;
 577         else
 578                 pdip = (dev_info_t *)DEVI(dip)->devi_parent;
 579 
 580         ASSERT(pdip);
 581 
 582         if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
 583             DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
 584                 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
 585                 return;
 586         }
 587 
 588         new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP);
 589         new_eh->eh_func = handler;
 590         new_eh->eh_impl = impl_data;
 591 
 592         /* Add dip to parent's target list of registered error handlers */
 593         tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP);
 594         tgt->ft_dip = dip;
 595         tgt->ft_errhdl = new_eh;
 596 
 597         i_ddi_fm_handler_enter(pdip);
 598         pfmhdl = DEVI(pdip)->devi_fmhdl;
 599         ASSERT(pfmhdl);
 600         tgt->ft_next = pfmhdl->fh_tgts;
 601         pfmhdl->fh_tgts = tgt;
 602         i_ddi_fm_handler_exit(pdip);
 603 }
 604 
 605 /*
 606  * Unregister a fault manager error handler for this device instance
 607  *
 608  * This function must be called from a drivers attach(9E) or detach(9E)
 609  * routine.
 610  */
 611 void
 612 ddi_fm_handler_unregister(dev_info_t *dip)
 613 {
 614         dev_info_t *pdip;
 615         struct i_ddi_fmhdl *pfmhdl;
 616         struct i_ddi_fmtgt *tgt, **ptgt;
 617 
 618         /*
 619          * Check for proper calling context.
 620          * The DDI configuration framework does not support
 621          * DR states to allow checking for proper invocation
 622          * from a DDI_DETACH or DDI_SUSPEND.  This limits context checking
 623          * to interrupt only.
 624          */
 625         if (servicing_interrupt()) {
 626                 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
 627                 return;
 628         }
 629 
 630         if (dip == ddi_root_node())
 631                 pdip = dip;
 632         else
 633                 pdip = (dev_info_t *)DEVI(dip)->devi_parent;
 634 
 635         ASSERT(pdip);
 636 
 637         if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
 638             DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
 639                 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
 640                 return;
 641         }
 642 
 643         i_ddi_fm_handler_enter(pdip);
 644         pfmhdl = DEVI(pdip)->devi_fmhdl;
 645         ASSERT(pfmhdl);
 646         ptgt = &pfmhdl->fh_tgts;
 647         for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
 648                 if (dip == tgt->ft_dip) {
 649                         *ptgt = tgt->ft_next;
 650                         kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl));
 651                         kmem_free(tgt, sizeof (struct i_ddi_fmtgt));
 652                         break;
 653                 }
 654                 ptgt = &tgt->ft_next;
 655         }
 656         i_ddi_fm_handler_exit(pdip);
 657 
 658 
 659 }
 660 
 661 /*
 662  * Initialize Fault Management capabilities for this device instance (dip).
 663  * When called with the following capabilities, data structures neccessary
 664  * for fault management activities are allocated and initialized.
 665  *
 666  *      DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport
 667  *                              capable driver property.
 668  *
 669  *      DDI_FM_ERRCB_CAPABLE - check with parent for ability to register
 670  *                              an error handler.
 671  *
 672  *      DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk
 673  *                              driver property
 674  *
 675  *      DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk
 676  *                              driver property
 677  *
 678  * A driver's FM capability level may not exceed that of its parent or
 679  * system-wide FM capability.  The available capability level for this
 680  * device instance is returned in *fmcap.
 681  *
 682  * This function must be called from a driver's attach(9E) entry point.
 683  */
 684 void
 685 ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp)
 686 {
 687         struct dev_info *devi = DEVI(dip);
 688         struct i_ddi_fmhdl *fmhdl;
 689         ddi_iblock_cookie_t ibc;
 690         int pcap, newcap = DDI_FM_NOT_CAPABLE;
 691 
 692         if (!DEVI_IS_ATTACHING(dip)) {
 693                 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
 694                 *fmcap = DDI_FM_NOT_CAPABLE;
 695                 return;
 696         }
 697 
 698         if (DDI_FM_DEFAULT_CAP(*fmcap))
 699                 return;
 700 
 701         /*
 702          * Check parent for supported FM level
 703          * and correct error handling PIL
 704          */
 705         if (dip != ddi_root_node()) {
 706 
 707                 /*
 708                  * Initialize the default ibc.  The parent may change it
 709                  * depending upon its capabilities.
 710                  */
 711                 ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL);
 712 
 713                 pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc);
 714         } else {
 715                 pcap = *fmcap;
 716                 ibc = *ibcp;
 717         }
 718 
 719         /* Initialize the per-device instance FM handle */
 720         fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP);
 721 
 722         if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip),
 723             ddi_get_instance(dip), "fm", "misc",
 724             KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) /
 725             sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) {
 726                 mutex_destroy(&fmhdl->fh_lock);
 727                 kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
 728                 *fmcap = DDI_FM_NOT_CAPABLE;
 729                 return;
 730         }
 731 
 732         bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat,
 733             sizeof (struct i_ddi_fmkstat));
 734         fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat;
 735         fmhdl->fh_ksp->ks_private = fmhdl;
 736         kstat_install(fmhdl->fh_ksp);
 737 
 738         fmhdl->fh_dma_cache = NULL;
 739         fmhdl->fh_acc_cache = NULL;
 740         fmhdl->fh_tgts = NULL;
 741         fmhdl->fh_dip = dip;
 742         fmhdl->fh_ibc = ibc;
 743         mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc);
 744         devi->devi_fmhdl = fmhdl;
 745 
 746         /*
 747          * Initialize support for ereport generation
 748          */
 749         if (DDI_FM_EREPORT_CAP(*fmcap) && DDI_FM_EREPORT_CAP(pcap)) {
 750                 fmhdl->fh_errorq = ereport_errorq;
 751                 if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
 752                     "fm-ereport-capable", 0) == 0)
 753                         (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
 754                             DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0);
 755 
 756                 newcap |= DDI_FM_EREPORT_CAPABLE;
 757         }
 758 
 759         /*
 760          * Need cooperation of the parent for error handling
 761          */
 762 
 763         if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) {
 764                 if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
 765                     "fm-errcb-capable", 0) == 0)
 766                         (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
 767                             DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0);
 768 
 769                 newcap |= DDI_FM_ERRCB_CAPABLE;
 770         }
 771 
 772         /*
 773          * Support for DMA and Access error handling
 774          */
 775 
 776         if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) {
 777                 i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc);
 778 
 779                 /* Set-up dma chk capability prop */
 780                 if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
 781                     "fm-dmachk-capable", 0) == 0)
 782                         (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
 783                             DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0);
 784 
 785                 newcap |= DDI_FM_DMACHK_CAPABLE;
 786         }
 787 
 788         if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) {
 789                 i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc);
 790                 /* Set-up dma chk capability prop */
 791                 if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
 792                     "fm-accchk-capable", 0) == 0)
 793                         (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
 794                             DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0);
 795 
 796                 newcap |= DDI_FM_ACCCHK_CAPABLE;
 797         }
 798 
 799         /*
 800          * Return the capability support available
 801          * to this driver instance
 802          */
 803         fmhdl->fh_cap = newcap;
 804         *fmcap = newcap;
 805 
 806         if (ibcp != NULL)
 807                 *ibcp = ibc;
 808 }
 809 
 810 /*
 811  * Finalize Fault Management activities for this device instance.
 812  * Outstanding IO transaction must be completed prior to calling
 813  * this routine.  All previously allocated resources and error handler
 814  * registration are cleared and deallocated.
 815  *
 816  * This function must be called from a driver's detach(9E) entry point.
 817  */
 818 void
 819 ddi_fm_fini(dev_info_t *dip)
 820 {
 821         struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
 822 
 823         ASSERT(fmhdl);
 824 
 825         if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) {
 826                 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
 827                 return;
 828         }
 829 
 830         kstat_delete(fmhdl->fh_ksp);
 831 
 832         if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) {
 833                 (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
 834                     "fm-ereport-capable");
 835         }
 836 
 837         if (dip != ddi_root_node()) {
 838                 if (DDI_FM_ERRCB_CAP(fmhdl->fh_cap)) {
 839                         ddi_fm_handler_unregister(dip);
 840                         (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
 841                             "fm-errcb-capable");
 842                 }
 843 
 844                 if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) ||
 845                     DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
 846                         if (fmhdl->fh_dma_cache != NULL) {
 847                                 i_ndi_fmc_destroy(fmhdl->fh_dma_cache);
 848                                 (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
 849                                     "fm-dmachk-capable");
 850                         }
 851                         if (fmhdl->fh_acc_cache != NULL) {
 852                                 i_ndi_fmc_destroy(fmhdl->fh_acc_cache);
 853                                 (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
 854                                     "fm-accachk-capable");
 855                         }
 856                 }
 857 
 858                 i_ndi_busop_fm_fini(dip);
 859         }
 860 
 861         kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
 862         DEVI(dip)->devi_fmhdl = NULL;
 863 }
 864 
 865 /*
 866  * Return the fault management capability level for this device instance.
 867  *
 868  * This function may be called from user, kernel, or interrupt context.
 869  */
 870 int
 871 ddi_fm_capable(dev_info_t *dip)
 872 {
 873         struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
 874 
 875         if (fmhdl == NULL)
 876                 return (DDI_FM_NOT_CAPABLE);
 877 
 878         return (fmhdl->fh_cap);
 879 }
 880 
 881 /*
 882  * Routines to set and get error information for/from an access or dma handle
 883  *
 884  * These routines may be called from user, kernel, and interrupt contexts.
 885  */
 886 
 887 static void
 888 ddi_fm_acc_err_get_fail(ddi_acc_handle_t handle)
 889 {
 890         ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
 891 
 892         i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
 893         cmn_err(CE_PANIC, "ddi_fm_acc_err_get: Invalid driver version\n");
 894 }
 895 
 896 void
 897 ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version)
 898 {
 899         ndi_err_t *errp;
 900 
 901         if (handle == NULL)
 902                 return;
 903 
 904         if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
 905                 ddi_fm_acc_err_get_fail(handle);
 906                 return;
 907         }
 908 
 909         errp = ((ddi_acc_impl_t *)handle)->ahi_err;
 910         if (errp->err_status == DDI_FM_OK) {
 911                 if (de->fme_status != DDI_FM_OK)
 912                         de->fme_status = DDI_FM_OK;
 913                 return;
 914         }
 915         de->fme_status = errp->err_status;
 916         de->fme_ena = errp->err_ena;
 917         de->fme_flag = errp->err_expected;
 918         de->fme_acc_handle = handle;
 919 }
 920 
 921 void
 922 ddi_fm_dma_err_get_fail(ddi_dma_handle_t handle)
 923 {
 924         i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
 925             DVR_EVER, NULL, DDI_NOSLEEP);
 926         cmn_err(CE_PANIC, "ddi_fm_dma_err_get: Invalid driver version\n");
 927 }
 928 
 929 void
 930 ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version)
 931 {
 932         ndi_err_t *errp;
 933 
 934         if (handle == NULL)
 935                 return;
 936 
 937         if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
 938                 ddi_fm_dma_err_get_fail(handle);
 939                 return;
 940         }
 941 
 942         errp = &((ddi_dma_impl_t *)handle)->dmai_error;
 943 
 944         if (errp->err_status == DDI_FM_OK) {
 945                 if (de->fme_status != DDI_FM_OK)
 946                         de->fme_status = DDI_FM_OK;
 947                 return;
 948         }
 949         de->fme_status = errp->err_status;
 950         de->fme_ena = errp->err_ena;
 951         de->fme_flag = errp->err_expected;
 952         de->fme_dma_handle = handle;
 953 }
 954 
 955 void
 956 ddi_fm_acc_err_clear_fail(ddi_acc_handle_t handle)
 957 {
 958         ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
 959 
 960         i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
 961         cmn_err(CE_PANIC, "ddi_fm_acc_err_clear: Invalid driver version\n");
 962 }
 963 
 964 void
 965 ddi_fm_acc_err_clear(ddi_acc_handle_t handle, int version)
 966 {
 967         ndi_err_t *errp;
 968 
 969         if (handle == NULL)
 970                 return;
 971 
 972         if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
 973                 ddi_fm_acc_err_clear_fail(handle);
 974                 return;
 975         }
 976 
 977         errp = ((ddi_acc_impl_t *)handle)->ahi_err;
 978         errp->err_status = DDI_FM_OK;
 979         errp->err_ena = 0;
 980         errp->err_expected = DDI_FM_ERR_UNEXPECTED;
 981 }
 982 
 983 void
 984 ddi_fm_dma_err_clear_fail(ddi_dma_handle_t handle)
 985 {
 986         i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
 987             DVR_EVER, NULL, DDI_NOSLEEP);
 988         cmn_err(CE_PANIC, "ddi_fm_dma_err_clear: Invalid driver version\n");
 989 }
 990 
 991 void
 992 ddi_fm_dma_err_clear(ddi_dma_handle_t handle, int version)
 993 {
 994         ndi_err_t *errp;
 995 
 996         if (handle == NULL)
 997                 return;
 998 
 999         if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
1000                 ddi_fm_dma_err_clear_fail(handle);
1001                 return;
1002         }
1003 
1004         errp = &((ddi_dma_impl_t *)handle)->dmai_error;
1005 
1006         errp->err_status = DDI_FM_OK;
1007         errp->err_ena = 0;
1008         errp->err_expected = DDI_FM_ERR_UNEXPECTED;
1009 }
1010 
1011 void
1012 i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status,
1013     int flag)
1014 {
1015         ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle);
1016         ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
1017         struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl;
1018 
1019         i_hdlp->ahi_err->err_ena = ena;
1020         i_hdlp->ahi_err->err_status = status;
1021         i_hdlp->ahi_err->err_expected = flag;
1022         atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1);
1023 }
1024 
1025 void
1026 i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status,
1027     int flag)
1028 {
1029         ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
1030         struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl;
1031 
1032         hdlp->dmai_error.err_ena = ena;
1033         hdlp->dmai_error.err_status = status;
1034         hdlp->dmai_error.err_expected = flag;
1035         atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1);
1036 }
1037 
1038 ddi_fmcompare_t
1039 i_ddi_fm_acc_err_cf_get(ddi_acc_handle_t handle)
1040 {
1041         ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
1042 
1043         return (i_hdlp->ahi_err->err_cf);
1044 }
1045 
1046 ddi_fmcompare_t
1047 i_ddi_fm_dma_err_cf_get(ddi_dma_handle_t handle)
1048 {
1049         ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
1050 
1051         return (hdlp->dmai_error.err_cf);
1052 }