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 2006 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 if (dip == ddi_root_node()) 481 pdip = dip; 482 else 483 pdip = (dev_info_t *)DEVI(dip)->devi_parent; 484 485 ASSERT(pdip); 486 487 if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) && 488 DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) { 489 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 490 return; 491 } 492 493 new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP); 494 new_eh->eh_func = handler; 495 new_eh->eh_impl = impl_data; 496 497 /* Add dip to parent's target list of registered error handlers */ 498 tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP); 499 tgt->ft_dip = dip; 500 tgt->ft_errhdl = new_eh; 501 502 i_ddi_fm_handler_enter(pdip); 503 pfmhdl = DEVI(pdip)->devi_fmhdl; 504 ASSERT(pfmhdl); 505 tgt->ft_next = pfmhdl->fh_tgts; 506 pfmhdl->fh_tgts = tgt; 507 i_ddi_fm_handler_exit(pdip); 508 } 509 510 /* 511 * Unregister a fault manager error handler for this device instance 512 * 513 * This function must be called from a drivers attach(9E) or detach(9E) 514 * routine. 515 */ 516 void 517 ddi_fm_handler_unregister(dev_info_t *dip) 518 { 519 dev_info_t *pdip; 520 struct i_ddi_fmhdl *pfmhdl; 521 struct i_ddi_fmtgt *tgt, **ptgt; 522 523 /* 524 * Check for proper calling context. 525 * The DDI configuration framework does not support 526 * DR states to allow checking for proper invocation 527 * from a DDI_DETACH or DDI_SUSPEND. This limits context checking 528 * to interrupt only. 529 */ 530 if (servicing_interrupt()) { 531 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 532 return; 533 } 534 535 if (dip == ddi_root_node()) 536 pdip = dip; 537 else 538 pdip = (dev_info_t *)DEVI(dip)->devi_parent; 539 540 ASSERT(pdip); 541 542 if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) && 543 DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) { 544 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 545 return; 546 } 547 548 i_ddi_fm_handler_enter(pdip); 549 pfmhdl = DEVI(pdip)->devi_fmhdl; 550 ASSERT(pfmhdl); 551 ptgt = &pfmhdl->fh_tgts; 552 for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) { 553 if (dip == tgt->ft_dip) { 554 *ptgt = tgt->ft_next; 555 kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl)); 556 kmem_free(tgt, sizeof (struct i_ddi_fmtgt)); 557 break; 558 } 559 ptgt = &tgt->ft_next; 560 } 561 i_ddi_fm_handler_exit(pdip); 562 563 564 } 565 566 /* 567 * Initialize Fault Management capabilities for this device instance (dip). 568 * When called with the following capabilities, data structures neccessary 569 * for fault management activities are allocated and initialized. 570 * 571 * DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport 572 * capable driver property. 573 * 574 * DDI_FM_ERRCB_CAPABLE - check with parent for ability to register 575 * an error handler. 576 * 577 * DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk 578 * driver property 579 * 580 * DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk 581 * driver property 582 * 583 * A driver's FM capability level may not exceed that of its parent or 584 * system-wide FM capability. The available capability level for this 585 * device instance is returned in *fmcap. 586 * 587 * This function must be called from a driver's attach(9E) entry point. 588 */ 589 void 590 ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp) 591 { 592 struct dev_info *devi = DEVI(dip); 593 struct i_ddi_fmhdl *fmhdl; 594 ddi_iblock_cookie_t ibc; 595 int pcap, newcap = DDI_FM_NOT_CAPABLE; 596 597 if (!DEVI_IS_ATTACHING(dip)) { 598 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 599 *fmcap = DDI_FM_NOT_CAPABLE; 600 return; 601 } 602 603 if (DDI_FM_DEFAULT_CAP(*fmcap)) 604 return; 605 606 /* 607 * Check parent for supported FM level 608 * and correct error handling PIL 609 */ 610 if (dip != ddi_root_node()) { 611 612 /* 613 * Initialize the default ibc. The parent may change it 614 * depending upon its capabilities. 615 */ 616 ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL); 617 618 pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc); 619 } else { 620 pcap = *fmcap; 621 ibc = *ibcp; 622 } 623 624 /* Initialize the per-device instance FM handle */ 625 fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP); 626 627 if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip), 628 ddi_get_instance(dip), "fm", "misc", 629 KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) / 630 sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) { 631 mutex_destroy(&fmhdl->fh_lock); 632 kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl)); 633 *fmcap = DDI_FM_NOT_CAPABLE; 634 return; 635 } 636 637 bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat, 638 sizeof (struct i_ddi_fmkstat)); 639 fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat; 640 fmhdl->fh_ksp->ks_private = fmhdl; 641 kstat_install(fmhdl->fh_ksp); 642 643 fmhdl->fh_dma_cache = NULL; 644 fmhdl->fh_acc_cache = NULL; 645 fmhdl->fh_tgts = NULL; 646 fmhdl->fh_dip = dip; 647 fmhdl->fh_ibc = ibc; 648 mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc); 649 devi->devi_fmhdl = fmhdl; 650 651 /* 652 * Initialize support for ereport generation 653 */ 654 if (DDI_FM_EREPORT_CAP(*fmcap) && 655 DDI_FM_EREPORT_CAP(ddi_system_fmcap)) { 656 fmhdl->fh_errorq = ereport_errorq; 657 if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 658 "fm-ereport-capable", 0) == 0) 659 (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 660 DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0); 661 662 newcap |= DDI_FM_EREPORT_CAPABLE; 663 } 664 665 /* 666 * Need cooperation of the parent for error handling 667 */ 668 669 if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) { 670 if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 671 "fm-errcb-capable", 0) == 0) 672 (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 673 DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0); 674 675 newcap |= DDI_FM_ERRCB_CAPABLE; 676 } 677 678 /* 679 * Support for DMA and Access error handling 680 */ 681 682 if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) { 683 i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc); 684 685 /* Set-up dma chk capability prop */ 686 if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 687 "fm-dmachk-capable", 0) == 0) 688 (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 689 DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0); 690 691 newcap |= DDI_FM_DMACHK_CAPABLE; 692 } 693 694 if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) { 695 i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc); 696 /* Set-up dma chk capability prop */ 697 if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 698 "fm-accchk-capable", 0) == 0) 699 (void) ddi_prop_create(DDI_DEV_T_ANY, dip, 700 DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0); 701 702 newcap |= DDI_FM_ACCCHK_CAPABLE; 703 } 704 705 /* 706 * Return the capability support available 707 * to this driver instance 708 */ 709 fmhdl->fh_cap = newcap; 710 *fmcap = newcap; 711 712 if (ibcp != NULL) 713 *ibcp = ibc; 714 } 715 716 /* 717 * Finalize Fault Management activities for this device instance. 718 * Outstanding IO transaction must be completed prior to calling 719 * this routine. All previously allocated resources and error handler 720 * registration are cleared and deallocated. 721 * 722 * This function must be called from a driver's detach(9E) entry point. 723 */ 724 void 725 ddi_fm_fini(dev_info_t *dip) 726 { 727 struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 728 729 if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) { 730 i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); 731 return; 732 } 733 734 if (DDI_FM_DEFAULT_CAP(fmhdl->fh_cap)) 735 return; 736 737 ASSERT(fmhdl); 738 739 kstat_delete(fmhdl->fh_ksp); 740 741 if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) { 742 (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 743 "fm-ereport-capable"); 744 } 745 746 if (dip != ddi_root_node()) { 747 ddi_fm_handler_unregister(dip); 748 749 if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) || 750 DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 751 if (fmhdl->fh_dma_cache != NULL) { 752 i_ndi_fmc_destroy(fmhdl->fh_dma_cache); 753 (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 754 "fm-dmachk-capable"); 755 } 756 if (fmhdl->fh_acc_cache != NULL) { 757 i_ndi_fmc_destroy(fmhdl->fh_acc_cache); 758 (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, 759 "fm-accachk-capable"); 760 } 761 } 762 763 i_ndi_busop_fm_fini(dip); 764 } 765 766 kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl)); 767 DEVI(dip)->devi_fmhdl = NULL; 768 } 769 770 /* 771 * Return the fault management capability level for this device instance. 772 * 773 * This function may be called from user, kernel, or interrupt context. 774 */ 775 int 776 ddi_fm_capable(dev_info_t *dip) 777 { 778 struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 779 780 if (fmhdl == NULL) 781 return (DDI_FM_NOT_CAPABLE); 782 783 return (fmhdl->fh_cap); 784 } 785 786 /* 787 * Routines to set and get error information for/from an access or dma handle 788 * 789 * These routines may be called from user, kernel, and interrupt contexts. 790 */ 791 void 792 ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version) 793 { 794 ndi_err_t *errp = ((ddi_acc_impl_t *)handle)->ahi_err; 795 796 if (version != DDI_FME_VER0) { 797 ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle); 798 799 i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP); 800 cmn_err(CE_PANIC, "ddi_fm_dma_err_get: " 801 "Invalid driver version\n"); 802 } 803 804 de->fme_status = errp->err_status; 805 de->fme_ena = errp->err_ena; 806 de->fme_flag = errp->err_expected; 807 de->fme_acc_handle = handle; 808 } 809 810 void 811 ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version) 812 { 813 ndi_err_t *errp = &((ddi_dma_impl_t *)handle)->dmai_error; 814 815 if (version != DDI_FME_VER0) { 816 i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip, 817 DVR_EVER, NULL, DDI_NOSLEEP); 818 cmn_err(CE_PANIC, "ddi_fm_dma_err_get: " 819 "Invalid driver version\n"); 820 } 821 822 de->fme_status = errp->err_status; 823 de->fme_ena = errp->err_ena; 824 de->fme_flag = errp->err_expected; 825 de->fme_dma_handle = handle; 826 } 827 828 void 829 i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status, 830 int flag) 831 { 832 ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle); 833 ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle; 834 struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl; 835 836 i_hdlp->ahi_err->err_ena = ena; 837 i_hdlp->ahi_err->err_status = status; 838 i_hdlp->ahi_err->err_expected = flag; 839 atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1); 840 } 841 842 void 843 i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status, 844 int flag) 845 { 846 ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle; 847 struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl; 848 849 hdlp->dmai_error.err_ena = ena; 850 hdlp->dmai_error.err_status = status; 851 hdlp->dmai_error.err_expected = flag; 852 atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1); 853 } 854