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