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 2006 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 Nexus Device Drivers 30 * 31 * In addition to implementing and supporting Fault Management for Device 32 * Drivers (ddifm.c), nexus drivers must support their children by 33 * reporting FM capabilities, intializing interrupt block cookies 34 * for error handling callbacks and caching mapped resources for lookup 35 * during the detection of an IO transaction error. 36 * 37 * It is typically the nexus driver that receives an error indication 38 * for a fault that may have occurred in the data path of an IO transaction. 39 * Errors may be detected or received via an interrupt, a callback from 40 * another subsystem (e.g. a cpu trap) or examination of control data. 41 * 42 * Upon detection of an error, the nexus has a responsibility to alert 43 * its children of the error and the transaction associated with that 44 * error. The actual implementation may vary depending upon the capabilities 45 * of the nexus, its underlying hardware and its children. In this file, 46 * we provide support for typical nexus driver fault management tasks. 47 * 48 * Fault Management Initialization 49 * 50 * Nexus drivers must implement two new busops, bus_fm_init() and 51 * bus_fm_fini(). bus_fm_init() is called from a child nexus or device 52 * driver and is expected to initialize any per-child state and return 53 * the FM and error interrupt priority levels of the nexus driver. 54 * Similarly, bus_fm_fini() is called by child drivers and should 55 * clean-up any resources allocated during bus_fm_init(). 56 * These functions are called from passive kernel context, typically from 57 * driver attach(9F) and detach(9F) entry points. 58 * 59 * Error Handler Dispatching 60 * 61 * Nexus drivers implemented to support error handler capabilities 62 * should invoke registered error handler callbacks for child drivers 63 * thought to be involved in the error. 64 * ndi_fm_handler_dispatch() is used to invoke 65 * all error handlers and returns one of the following status 66 * indications: 67 * 68 * DDI_FM_OK - No errors found by any child 69 * DDI_FM_FATAL - one or more children have detected a fatal error 70 * DDI_FM_NONFATAL - no fatal errors, but one or more children have 71 * detected a non-fatal error 72 * 73 * ndi_fm_handler_dispatch() may be called in any context 74 * subject to the constraints specified by the interrupt iblock cookie 75 * returned during initialization. 76 * 77 * Protected Accesses 78 * 79 * When an access handle is mapped or a DMA handle is bound via the 80 * standard busops, bus_map() or bus_dma_bindhdl(), a child driver 81 * implemented to support DDI_FM_ACCCHK_CAPABLE or 82 * DDI_FM_DMACHK_CAPABLE capabilites 83 * expects the nexus to flag any errors detected for transactions 84 * associated with the mapped or bound handles. 85 * 86 * Children nexus or device drivers will set the following flags 87 * in their ddi_device_access or dma_attr_flags when requesting 88 * the an access or DMA handle mapping: 89 * 90 * DDI_DMA_FLAGERR - nexus should set error status for any errors 91 * detected for a failed DMA transaction. 92 * DDI_ACC_FLAGERR - nexus should set error status for any errors 93 * detected for a failed PIO transaction. 94 * 95 * A nexus is expected to provide additional error detection and 96 * handling for handles with these flags set. 97 * 98 * Exclusive Bus Access 99 * 100 * In cases where a driver requires a high level of fault tolerance 101 * for a programmed IO transaction, it is neccessary to grant exclusive 102 * access to the bus resource. Exclusivity guarantees that a fault 103 * resulting from a transaction on the bus can be easily traced and 104 * reported to the driver requesting the transaction. 105 * 106 * Nexus drivers must implement two new busops to support exclusive 107 * access, bus_fm_access_enter() and bus_fm_access_exit(). The IO 108 * framework will use these functions when it must set-up access 109 * handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in 110 * their ddi_device_acc_attr_t request. 111 * 112 * Upon receipt of a bus_fm_access_enter() request, the nexus must prevent 113 * all other access requests until it receives bus_fm_access_exit() 114 * for the requested bus instance. bus_fm_access_enter() and 115 * bus_fm_access_exit() may be called from user, kernel or kernel 116 * interrupt context. 117 * 118 * Access and DMA Handle Caching 119 * 120 * To aid a nexus driver in associating access or DMA handles with 121 * a detected error, the nexus should cache all handles that are 122 * associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or 123 * DDI_DMA_FLAGERR requests from its children. ndi_fmc_insert() is 124 * called by a nexus to cache handles with the above protection flags 125 * and ndi_fmc_remove() is called when that handle is unmapped or 126 * unbound by the requesting child. ndi_fmc_insert() and 127 * ndi_fmc_remove() may be called from any user or kernel context. 128 * 129 * FM caches are allocated during ddi_fm_init() and maintained 130 * as an array of elements that may be on one of two lists: 131 * free or active. The free list is a singly-linked list of 132 * elements available for activity. ndi_fm_insert() moves the 133 * element at the head of the free to the active list. The active 134 * list is a doubly-linked searchable list. 135 * When a handle is unmapped or unbound, its associated cache 136 * entry is removed from the active list back to the free list. 137 * 138 * Upon detection of an error, the nexus may invoke ndi_fmc_error() to 139 * iterate over the handle cache of one or more of its FM compliant 140 * children. A comparison callback function is provided upon each 141 * invocation of ndi_fmc_error() to tell the IO framework if a 142 * handle is associated with an error. If so, the framework will 143 * set the error status for that handle before returning from 144 * ndi_fmc_error(). 145 * 146 * ndi_fmc_error() may be called in any context 147 * subject to the constraints specified by the interrupt iblock cookie 148 * returned during initialization of the nexus and its children. 149 * 150 */ 151 152 #include <sys/types.h> 153 #include <sys/param.h> 154 #include <sys/debug.h> 155 #include <sys/sunddi.h> 156 #include <sys/sunndi.h> 157 #include <sys/ddi.h> 158 #include <sys/ndi_impldefs.h> 159 #include <sys/devctl.h> 160 #include <sys/nvpair.h> 161 #include <sys/ddifm.h> 162 #include <sys/ndifm.h> 163 #include <sys/spl.h> 164 #include <sys/sysmacros.h> 165 #include <sys/devops.h> 166 #include <sys/atomic.h> 167 #include <sys/fm/io/ddi.h> 168 169 /* 170 * Allocate and initialize a fault management resource cache 171 * A fault management cache consists of a set of cache elements that 172 * may be on one of two lists: free or active. 173 * 174 * At creation time, every element but one is placed on the free list 175 * except for the first element. This element is reserved as the first 176 * element of the active list and serves as an anchor for the active 177 * list in ndi_fmc_insert() and ndi_fmc_remove(). In these functions, 178 * it is not neccessary to check for the existence or validity of 179 * the active list. 180 */ 181 void 182 i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc) 183 { 184 ndi_fmc_t *fcp; 185 ndi_fmcentry_t *fep; 186 187 ASSERT(qlen > 1); 188 189 fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP); 190 mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc); 191 192 /* Preallocate and initialize entries for this fm cache */ 193 fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP); 194 195 fcp->fc_len = qlen; 196 197 /* Intialize the active and free lists */ 198 fcp->fc_active = fcp->fc_tail = fcp->fc_elems; 199 fcp->fc_free = fcp->fc_elems + 1; 200 qlen--; 201 for (fep = fcp->fc_free; qlen > 1; qlen--) { 202 fep->fce_prev = fep + 1; 203 fep++; 204 } 205 206 *fcpp = fcp; 207 } 208 209 /* 210 * Destroy and resources associated with the given fault management cache. 211 */ 212 void 213 i_ndi_fmc_destroy(ndi_fmc_t *fcp) 214 { 215 if (fcp == NULL) 216 return; 217 218 kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t)); 219 kmem_free(fcp, sizeof (ndi_fmc_t)); 220 } 221 222 /* 223 * Grow an existing fault management cache by grow_sz number of entries 224 */ 225 static int 226 fmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz) 227 { 228 int olen, nlen; 229 void *resource; 230 ndi_fmcentry_t *ncp, *oep, *nep, *nnep; 231 232 ASSERT(grow_sz); 233 ASSERT(MUTEX_HELD(&fcp->fc_lock)); 234 235 /* Allocate a new cache */ 236 nlen = grow_sz + fcp->fc_len; 237 if ((ncp = kmem_zalloc(nlen * sizeof (ndi_fmcentry_t), 238 KM_NOSLEEP)) == NULL) 239 return (1); 240 241 /* Migrate old cache to new cache */ 242 oep = fcp->fc_elems; 243 olen = fcp->fc_len; 244 for (nep = ncp; ; olen--) { 245 resource = nep->fce_resource = oep->fce_resource; 246 nep->fce_bus_specific = oep->fce_bus_specific; 247 if (resource) { 248 if (flag == DMA_HANDLE) { 249 ((ddi_dma_impl_t *)resource)-> 250 dmai_error.err_fep = nep; 251 } else if (flag == ACC_HANDLE) { 252 ((ddi_acc_impl_t *)resource)-> 253 ahi_err->err_fep = nep; 254 } 255 } 256 257 /* 258 * This is the last entry. Set the tail pointer and 259 * terminate processing of the old cache. 260 */ 261 if (olen == 1) { 262 fcp->fc_tail = nep; 263 ++nep; 264 break; 265 } 266 267 /* 268 * Set the next and previous pointer for the new cache 269 * entry. 270 */ 271 nnep = nep + 1; 272 nep->fce_next = nnep; 273 nnep->fce_prev = nep; 274 275 /* Advance to the next entry */ 276 ++oep; 277 nep = nnep; 278 } 279 280 kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t)); 281 282 /* Initialize and add remaining new cache entries to the free list */ 283 olen = fcp->fc_len + 1; 284 fcp->fc_len = nlen; 285 for (fcp->fc_free = nep; nlen > olen; nlen--) { 286 nep->fce_prev = nep + 1; 287 nep++; 288 } 289 290 fcp->fc_active = ncp; 291 fcp->fc_elems = ncp; 292 293 return (0); 294 } 295 296 /* 297 * ndi_fmc_insert - 298 * Add a new entry to the specified cache. 299 * 300 * This function must be called at or below LOCK_LEVEL 301 */ 302 void 303 ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific) 304 { 305 struct dev_info *devi = DEVI(dip); 306 ndi_fmc_t *fcp; 307 ndi_fmcentry_t *fep, **fpp; 308 struct i_ddi_fmhdl *fmhdl; 309 310 ASSERT(devi); 311 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 312 313 fmhdl = devi->devi_fmhdl; 314 if (fmhdl == NULL) { 315 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 316 return; 317 } 318 319 if (flag == DMA_HANDLE) { 320 if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 321 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 322 DDI_NOSLEEP); 323 return; 324 } 325 fcp = fmhdl->fh_dma_cache; 326 fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep; 327 } else if (flag == ACC_HANDLE) { 328 if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 329 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 330 DDI_NOSLEEP); 331 return; 332 } 333 fcp = fmhdl->fh_acc_cache; 334 fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep; 335 } 336 ASSERT(*fpp == NULL); 337 338 mutex_enter(&fcp->fc_lock); 339 340 /* Get an entry from the free list */ 341 fep = fcp->fc_free; 342 if (fep == NULL) { 343 if (fmc_grow(fcp, flag, 344 (flag == ACC_HANDLE ? default_acccache_sz : 345 default_dmacache_sz)) != 0) { 346 347 /* Unable to get an entry or grow this cache */ 348 atomic_add_64( 349 &fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1); 350 mutex_exit(&fcp->fc_lock); 351 return; 352 } 353 atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1); 354 fep = fcp->fc_free; 355 } 356 fcp->fc_free = fep->fce_prev; 357 358 /* 359 * Set-up the handle resource and bus_specific information. 360 * Also remember the pointer back to the cache for quick removal. 361 */ 362 fep->fce_bus_specific = bus_specific; 363 fep->fce_resource = resource; 364 fep->fce_next = NULL; 365 *fpp = fep; 366 367 /* Add entry to the end of the active list */ 368 fep->fce_prev = fcp->fc_tail; 369 fcp->fc_tail->fce_next = fep; 370 fcp->fc_tail = fep; 371 mutex_exit(&fcp->fc_lock); 372 } 373 374 /* 375 * Remove an entry from the specified cache of access or dma mappings 376 * 377 * This function must be called at or below LOCK_LEVEL. 378 */ 379 void 380 ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource) 381 { 382 ndi_fmc_t *fcp; 383 ndi_fmcentry_t *fep; 384 struct dev_info *devi = DEVI(dip); 385 struct i_ddi_fmhdl *fmhdl; 386 387 ASSERT(devi); 388 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 389 390 fmhdl = devi->devi_fmhdl; 391 if (fmhdl == NULL) { 392 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 393 return; 394 } 395 396 /* Find cache entry pointer for this resource */ 397 if (flag == DMA_HANDLE) { 398 if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 399 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 400 DDI_NOSLEEP); 401 return; 402 } 403 fcp = fmhdl->fh_dma_cache; 404 405 ASSERT(fcp); 406 407 mutex_enter(&fcp->fc_lock); 408 fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep; 409 ((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL; 410 } else if (flag == ACC_HANDLE) { 411 if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 412 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 413 DDI_NOSLEEP); 414 return; 415 } 416 fcp = fmhdl->fh_acc_cache; 417 418 ASSERT(fcp); 419 420 mutex_enter(&fcp->fc_lock); 421 fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep; 422 ((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL; 423 } 424 425 /* 426 * Resource not in cache, return 427 */ 428 if (fep == NULL) { 429 mutex_exit(&fcp->fc_lock); 430 return; 431 } 432 433 fep->fce_prev->fce_next = fep->fce_next; 434 if (fep == fcp->fc_tail) 435 fcp->fc_tail = fep->fce_prev; 436 else 437 fep->fce_next->fce_prev = fep->fce_prev; 438 439 /* Add entry back to the free list */ 440 fep->fce_prev = fcp->fc_free; 441 fcp->fc_free = fep; 442 mutex_exit(&fcp->fc_lock); 443 } 444 445 int 446 ndi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr, 447 const void *bus_err_state) 448 { 449 int status, fatal = 0, nonfatal = 0; 450 ndi_fmc_t *fcp = NULL; 451 ndi_fmcentry_t *fep; 452 struct i_ddi_fmhdl *fmhdl; 453 454 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 455 456 fmhdl = DEVI(dip)->devi_fmhdl; 457 ASSERT(fmhdl); 458 status = DDI_FM_UNKNOWN; 459 460 if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 461 fcp = fmhdl->fh_dma_cache; 462 ASSERT(fcp); 463 } else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 464 fcp = fmhdl->fh_acc_cache; 465 ASSERT(fcp); 466 } 467 468 if (fcp != NULL) { 469 470 /* 471 * Check active resource entries 472 */ 473 mutex_enter(&fcp->fc_lock); 474 for (fep = fcp->fc_active->fce_next; fep != NULL; 475 fep = fep->fce_next) { 476 ddi_fmcompare_t compare_func; 477 478 /* 479 * Compare captured error state with handle 480 * resources. During the comparison and 481 * subsequent error handling, we block 482 * attempts to free the cache entry. 483 */ 484 compare_func = (flag == ACC_HANDLE) ? 485 i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t) 486 fep->fce_resource) : 487 i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t) 488 fep->fce_resource); 489 490 status = compare_func(dip, fep->fce_resource, 491 bus_err_state, fep->fce_bus_specific); 492 if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK) 493 continue; 494 495 if (status == DDI_FM_FATAL) 496 ++fatal; 497 else if (status == DDI_FM_NONFATAL) 498 ++nonfatal; 499 500 /* Set the error for this resource handle */ 501 if (flag == ACC_HANDLE) { 502 ddi_acc_handle_t ap = fep->fce_resource; 503 504 i_ddi_fm_acc_err_set(ap, derr->fme_ena, status, 505 DDI_FM_ERR_UNEXPECTED); 506 ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION); 507 derr->fme_acc_handle = ap; 508 } else { 509 ddi_dma_handle_t dp = fep->fce_resource; 510 511 i_ddi_fm_dma_err_set(dp, derr->fme_ena, status, 512 DDI_FM_ERR_UNEXPECTED); 513 ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION); 514 derr->fme_dma_handle = dp; 515 } 516 break; 517 } 518 mutex_exit(&fcp->fc_lock); 519 } 520 return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL : 521 DDI_FM_UNKNOWN); 522 } 523 524 /* 525 * Check error state against the handle resource stored in the specified 526 * FM cache. If tdip != NULL, we check only the cache entries for tdip. 527 * The caller must ensure that tdip is valid throughout the call and 528 * all FM data structures can be safely accesses. 529 * 530 * If tdip == NULL, we check all children that have registered their 531 * FM_DMA_CHK or FM_ACC_CHK capabilities. 532 * 533 * The following status values may be returned: 534 * 535 * DDI_FM_FATAL - if at least one cache entry comparison yields a 536 * fatal error. 537 * 538 * DDI_FM_NONFATAL - if at least one cache entry comparison yields a 539 * non-fatal error and no comparison yields a fatal error. 540 * 541 * DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or 542 * non-fatal errors. 543 * 544 */ 545 int 546 ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena, 547 const void *bus_err_state) 548 { 549 int status, fatal = 0, nonfatal = 0; 550 ddi_fm_error_t derr; 551 struct i_ddi_fmhdl *fmhdl; 552 struct i_ddi_fmtgt *tgt; 553 554 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 555 556 i_ddi_fm_handler_enter(dip); 557 fmhdl = DEVI(dip)->devi_fmhdl; 558 ASSERT(fmhdl); 559 560 bzero(&derr, sizeof (ddi_fm_error_t)); 561 derr.fme_version = DDI_FME_VERSION; 562 derr.fme_flag = DDI_FM_ERR_UNEXPECTED; 563 derr.fme_ena = ena; 564 565 for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) { 566 567 if (tdip != NULL && tdip != tgt->ft_dip) 568 continue; 569 570 /* 571 * Attempt to find the entry in this childs handle cache 572 */ 573 status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr, 574 bus_err_state); 575 576 if (status == DDI_FM_FATAL) 577 ++fatal; 578 else if (status == DDI_FM_NONFATAL) 579 ++nonfatal; 580 else 581 continue; 582 583 /* 584 * Call our child to process this error. 585 */ 586 status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr, 587 tgt->ft_errhdl->eh_impl); 588 589 if (status == DDI_FM_FATAL) 590 ++fatal; 591 else if (status == DDI_FM_NONFATAL) 592 ++nonfatal; 593 } 594 595 i_ddi_fm_handler_exit(dip); 596 597 if (fatal) 598 return (DDI_FM_FATAL); 599 else if (nonfatal) 600 return (DDI_FM_NONFATAL); 601 602 return (DDI_FM_UNKNOWN); 603 } 604 605 /* 606 * Dispatch registered error handlers for dip. If tdip != NULL, only 607 * the error handler (if available) for tdip is invoked. Otherwise, 608 * all registered error handlers are invoked. 609 * 610 * The following status values may be returned: 611 * 612 * DDI_FM_FATAL - if at least one error handler returns a 613 * fatal error. 614 * 615 * DDI_FM_NONFATAL - if at least one error handler returns a 616 * non-fatal error and none returned a fatal error. 617 * 618 * DDI_FM_UNKNOWN - if at least one error handler returns 619 * unknown status and none return fatal or non-fatal. 620 * 621 * DDI_FM_OK - if all error handlers return DDI_FM_OK 622 */ 623 int 624 ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip, 625 const ddi_fm_error_t *nerr) 626 { 627 int status; 628 int unknown = 0, fatal = 0, nonfatal = 0; 629 struct i_ddi_fmhdl *hdl; 630 struct i_ddi_fmtgt *tgt; 631 632 status = DDI_FM_UNKNOWN; 633 634 i_ddi_fm_handler_enter(dip); 635 hdl = DEVI(dip)->devi_fmhdl; 636 tgt = hdl->fh_tgts; 637 while (tgt != NULL) { 638 if (tdip == NULL || tdip == tgt->ft_dip) { 639 struct i_ddi_errhdl *errhdl; 640 641 errhdl = tgt->ft_errhdl; 642 status = errhdl->eh_func(tgt->ft_dip, nerr, 643 errhdl->eh_impl); 644 645 if (status == DDI_FM_FATAL) 646 ++fatal; 647 else if (status == DDI_FM_NONFATAL) 648 ++nonfatal; 649 else if (status == DDI_FM_UNKNOWN) 650 ++unknown; 651 652 /* Only interested in one target */ 653 if (tdip != NULL) 654 break; 655 } 656 tgt = tgt->ft_next; 657 } 658 i_ddi_fm_handler_exit(dip); 659 660 if (fatal) 661 return (DDI_FM_FATAL); 662 else if (nonfatal) 663 return (DDI_FM_NONFATAL); 664 else if (unknown) 665 return (DDI_FM_UNKNOWN); 666 else 667 return (DDI_FM_OK); 668 } 669 670 /* 671 * Set error status for specified access or DMA handle 672 * 673 * May be called in any context but caller must insure validity of 674 * handle. 675 */ 676 void 677 ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe) 678 { 679 i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status, 680 dfe->fme_flag); 681 } 682 683 void 684 ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe) 685 { 686 i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status, 687 dfe->fme_flag); 688 } 689 690 /* 691 * Call parent busop fm initialization routine. 692 * 693 * Called during driver attach(1M) 694 */ 695 int 696 i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc) 697 { 698 int pcap; 699 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 700 701 if (dip == ddi_root_node()) 702 return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE); 703 704 /* Valid operation for BUSO_REV_6 and above */ 705 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 706 return (DDI_FM_NOT_CAPABLE); 707 708 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL) 709 return (DDI_FM_NOT_CAPABLE); 710 711 pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init) 712 (pdip, dip, tcap, ibc); 713 714 return (pcap); 715 } 716 717 /* 718 * Call parent busop fm clean-up routine. 719 * 720 * Called during driver detach(1M) 721 */ 722 void 723 i_ndi_busop_fm_fini(dev_info_t *dip) 724 { 725 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 726 727 if (dip == ddi_root_node()) 728 return; 729 730 /* Valid operation for BUSO_REV_6 and above */ 731 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 732 return; 733 734 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL) 735 return; 736 737 (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip); 738 } 739 740 /* 741 * The following routines provide exclusive access to a nexus resource 742 * 743 * These busops may be called in user or kernel driver context. 744 */ 745 void 746 i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle) 747 { 748 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 749 750 /* Valid operation for BUSO_REV_6 and above */ 751 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 752 return; 753 754 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL) 755 return; 756 757 (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter) 758 (pdip, handle); 759 } 760 761 void 762 i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle) 763 { 764 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 765 766 /* Valid operation for BUSO_REV_6 and above */ 767 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 768 return; 769 770 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL) 771 return; 772 773 (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle); 774 } 775