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