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