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