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 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/cpuvar.h> 31 #include <sys/kmem.h> 32 #include <sys/sunddi.h> 33 #include <sys/hotplug/pci/pcihp.h> 34 #include "px_obj.h" 35 #include <sys/pci_tools.h> 36 #include "px_tools_ext.h" 37 #include "px_tools_var.h" 38 39 /* 40 * PCI Space definitions. 41 */ 42 #define PCI_CONFIG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 43 #define PCI_IO_SPACE (PCI_REG_ADDR_G(PCI_ADDR_IO)) 44 #define PCI_MEM32_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 45 #define PCI_MEM64_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 46 47 /* 48 * Config space range for a device. IEEE 1275 spec defines for PCI. 49 * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA 50 */ 51 #define DEV_CFG_SPACE_SIZE \ 52 (1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA)) 53 54 /* 55 * Offsets of BARS in config space. First entry of 0 means config space. 56 * Entries here correlate to pcitool_bars_t enumerated type. 57 */ 58 uint8_t pci_bars[] = { 59 0x0, 60 PCI_CONF_BASE0, 61 PCI_CONF_BASE1, 62 PCI_CONF_BASE2, 63 PCI_CONF_BASE3, 64 PCI_CONF_BASE4, 65 PCI_CONF_BASE5, 66 PCI_CONF_ROM 67 }; 68 69 int pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]); 70 71 /* 72 * Validate the cpu_id passed in. 73 * A value of 1 will be returned for success and zero for failure. 74 */ 75 static int 76 pxtool_validate_cpuid(uint32_t cpuid) 77 { 78 extern const int _ncpu; 79 extern cpu_t *cpu[]; 80 81 ASSERT(mutex_owned(&cpu_lock)); 82 83 return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid]))); 84 } 85 86 87 static int 88 pxtool_intr_get_max_ino(uint32_t *arg, int mode) 89 { 90 if (ddi_copyout(&pxtool_num_inos, arg, sizeof (uint32_t), mode) != 91 DDI_SUCCESS) 92 return (EFAULT); 93 else 94 return (SUCCESS); 95 } 96 /* 97 * Get interrupt information for a given ino. 98 * Returns info only for inos mapped to devices. 99 * 100 * Returned info is valid only when iget.num_devs is returned > 0. 101 * If ino is not enabled or is not mapped to a device, 102 * iget.num_devs will be returned as = 0. 103 */ 104 /*ARGSUSED*/ 105 static int 106 pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 107 { 108 /* Array part isn't used here, but oh well... */ 109 pcitool_intr_get_t partial_iget; 110 uint32_t ino; 111 uint8_t num_devs_ret; 112 int copyout_rval; 113 sysino_t sysino; 114 intr_valid_state_t intr_valid_state; 115 cpuid_t old_cpu_id; 116 px_t *px_p = DIP_TO_STATE(dip); 117 pcitool_intr_get_t *iget = &partial_iget; 118 size_t iget_kmem_alloc_size = 0; 119 int rval = SUCCESS; 120 121 /* Read in just the header part, no array section. */ 122 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 123 DDI_SUCCESS) 124 return (EFAULT); 125 126 ino = partial_iget.ino; 127 num_devs_ret = partial_iget.num_devs_ret; 128 129 partial_iget.num_devs_ret = 0; /* Assume error for now. */ 130 partial_iget.status = PCITOOL_INVALID_INO; 131 rval = EINVAL; 132 133 /* Validate argument. */ 134 if (partial_iget.ino > pxtool_num_inos) { 135 goto done_get_intr; 136 } 137 138 /* Caller wants device information returned. */ 139 if (num_devs_ret > 0) { 140 141 /* 142 * Allocate room. 143 * Note if num_devs == 0 iget remains pointing to 144 * partial_iget. 145 */ 146 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 147 iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 148 149 /* Read in whole structure to verify there's room. */ 150 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 151 SUCCESS) { 152 153 /* Be consistent and just return EFAULT here. */ 154 kmem_free(iget, iget_kmem_alloc_size); 155 156 return (EFAULT); 157 } 158 } 159 160 bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 161 iget->ino = ino; 162 iget->num_devs_ret = num_devs_ret; 163 164 /* Convert leaf-wide intr to system-wide intr */ 165 if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) == 166 DDI_FAILURE) { 167 iget->status = PCITOOL_IO_ERROR; 168 rval = EIO; 169 goto done_get_intr; 170 } 171 172 /* Operate only on inos which are already enabled. */ 173 if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) == 174 DDI_FAILURE) { 175 iget->status = PCITOOL_IO_ERROR; 176 rval = EIO; 177 goto done_get_intr; 178 } 179 180 /* 181 * Consider all valid inos: those mapped to the root complex itself 182 * as well as those mapped to devices. 183 */ 184 if (intr_valid_state == INTR_VALID) { 185 186 /* 187 * The following looks up the px_ib_ino_info and returns 188 * info of devices mapped to this ino. 189 */ 190 iget->num_devs = pxtool_ib_get_ino_devs( 191 px_p, ino, &iget->num_devs_ret, iget->dev); 192 193 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 194 DDI_FAILURE) { 195 iget->status = PCITOOL_IO_ERROR; 196 rval = EIO; 197 goto done_get_intr; 198 } 199 iget->cpu_id = old_cpu_id; 200 } 201 202 iget->status = PCITOOL_SUCCESS; 203 rval = SUCCESS; 204 205 done_get_intr: 206 copyout_rval = 207 ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); 208 209 if (iget_kmem_alloc_size > 0) 210 kmem_free(iget, iget_kmem_alloc_size); 211 212 if (copyout_rval != DDI_SUCCESS) 213 rval = EFAULT; 214 215 return (rval); 216 } 217 218 219 /* 220 * Associate a new CPU with a given ino. 221 * 222 * Operate only on inos which are already mapped to devices. 223 */ 224 static int 225 pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 226 { 227 pcitool_intr_set_t iset; 228 cpuid_t old_cpu_id; 229 sysino_t sysino; 230 px_t *px_p = DIP_TO_STATE(dip); 231 px_ib_t *ib_p = px_p->px_ib_p; 232 uint8_t zero = 0; 233 int rval = SUCCESS; 234 235 if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != 236 DDI_SUCCESS) 237 return (EFAULT); 238 239 iset.status = PCITOOL_INVALID_INO; 240 rval = EINVAL; 241 242 /* Validate input argument. */ 243 if (iset.ino > pxtool_num_inos) 244 goto done_set_intr; 245 246 /* Validate that ino given belongs to a device. */ 247 if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0) 248 goto done_set_intr; 249 250 /* 251 * Get lock, validate cpu and write new mapreg value. 252 * Return original cpu value to caller via iset.cpu. 253 */ 254 mutex_enter(&cpu_lock); 255 if (pxtool_validate_cpuid(iset.cpu_id)) { 256 257 DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id); 258 259 if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) == 260 DDI_FAILURE) 261 goto done_set_intr; 262 263 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 264 DDI_FAILURE) 265 goto done_set_intr; 266 267 px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE); 268 269 px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino); 270 271 iset.cpu_id = old_cpu_id; 272 iset.status = PCITOOL_SUCCESS; 273 rval = SUCCESS; 274 275 } else { /* Invalid cpu. Restore original register image. */ 276 277 DBG(DBG_TOOLS, dip, 278 "Invalid cpuid: writing orig mapreg value\n"); 279 280 iset.status = PCITOOL_INVALID_CPUID; 281 rval = EINVAL; 282 } 283 mutex_exit(&cpu_lock); 284 285 done_set_intr: 286 if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != 287 DDI_SUCCESS) 288 rval = EFAULT; 289 290 return (rval); 291 } 292 293 294 /* Main function for handling interrupt CPU binding requests and queries. */ 295 int 296 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 297 { 298 int rval = SUCCESS; 299 300 switch (cmd) { 301 302 /* Return the number of interrupts supported by a PCI bus. */ 303 case PCITOOL_DEVICE_NUM_INTR: 304 rval = pxtool_intr_get_max_ino(arg, mode); 305 break; 306 307 /* Get interrupt information for a given ino. */ 308 case PCITOOL_DEVICE_GET_INTR: 309 rval = pxtool_get_intr(dip, arg, mode); 310 break; 311 312 /* Associate a new CPU with a given ino. */ 313 case PCITOOL_DEVICE_SET_INTR: 314 rval = pxtool_set_intr(dip, arg, mode); 315 break; 316 317 default: 318 rval = ENOTTY; 319 } 320 321 return (rval); 322 } 323 324 325 static int 326 pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 327 { 328 int rval = SUCCESS; 329 330 if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 331 prg->status = PCITOOL_OUT_OF_RANGE; 332 rval = EINVAL; 333 334 /* Validate address arguments of bus / dev / func */ 335 } else if (((prg->bus_no & 336 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 337 ((prg->dev_no & 338 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 339 ((prg->func_no & 340 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 341 prg->status = PCITOOL_INVALID_ADDRESS; 342 rval = EINVAL; 343 } 344 345 return (rval); 346 } 347 348 349 /* 350 * px_p defines which leaf, space defines which space in that leaf, offset 351 * defines the offset within the specified space. 352 * 353 * This returns the physical address of the corresponding location. 354 */ 355 static uintptr_t 356 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 357 { 358 px_ranges_t *rp; 359 uint64_t range_base; 360 int rval; 361 dev_info_t *dip = px_p->px_dip; 362 uint32_t base_offset = 0; 363 extern uint64_t px_get_range_prop(px_t *, px_ranges_t *, int); 364 365 /* 366 * Assume that requested entity is small enough to be on the same page. 367 * (Same starting and ending value "offset" passed to px_search_ranges.) 368 * PCItool checks alignment so that this will be true for single 369 * accesses. 370 * 371 * Base_offset is the offset from the specified address, where the 372 * current range begins. This is nonzero when a PCI space is split and 373 * the address is inside the second or subsequent range. 374 * 375 * NOTE: offset is a uint64_t but px_search_ranges takes a uint32_t. 376 * px_search_ranges should also take a uint64_t for base_offset. 377 * RFE is to have px_search_ranges handle a uint64_t offset. 378 */ 379 rval = px_search_ranges(px_p, space, offset, offset, &rp, 380 (uint32_t *)&base_offset); 381 DBG(DBG_TOOLS, dip, 382 "space:0x%d, offset:0x%" PRIx64 ", base_offset:0x%" PRIx64 "\n", 383 space, offset, base_offset); 384 385 if (rval != DDI_SUCCESS) 386 return (NULL); 387 else { 388 range_base = px_get_range_prop(px_p, rp, 0); 389 DBG(DBG_TOOLS, dip, "range base:0x%" PRIx64 "\n", range_base); 390 return (base_offset + range_base); 391 } 392 } 393 394 395 static int 396 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 397 uint32_t *space_p) 398 { 399 int rval; 400 uint64_t off_in_space; 401 pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 402 dev_info_t *dip = px_p->px_dip; 403 404 *space_p = PCI_MEM32_SPACE; 405 *bar_p = 0; 406 407 /* 408 * Translate BAR number into offset of the BAR in 409 * the device's config space. 410 */ 411 cfg_prg.acc_attr = 412 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 413 414 /* 415 * Note: sun4u acc function uses phys_addr which includes offset. 416 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 417 */ 418 cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 419 off_in_space = (PX_GET_BDF(prg_p) << PX_PCI_BDF_OFFSET_DELTA) + 420 cfg_prg.offset; 421 cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 422 off_in_space); 423 424 DBG(DBG_TOOLS, dip, 425 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 426 off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 427 428 /* 429 * Get Bus Address Register (BAR) from config space. 430 * cfg_prg.offset is the offset into config space of the 431 * BAR desired. prg_p->status is modified on error. 432 */ 433 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 434 435 if (rval != SUCCESS) { 436 prg_p->status = cfg_prg.status; 437 return (rval); 438 } 439 440 DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 441 442 /* 443 * BAR has bits saying this space is IO space, unless 444 * this is the ROM address register. 445 */ 446 if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 447 (cfg_prg.offset != PCI_CONF_ROM)) { 448 *space_p = PCI_IO_SPACE; 449 *bar_p &= PCI_BASE_IO_ADDR_M; 450 451 /* 452 * BAR has bits saying this space is 64 bit memory 453 * space, unless this is the ROM address register. 454 * 455 * The 64 bit address stored in two BAR cells is not 456 * necessarily aligned on an 8-byte boundary. 457 * Need to keep the first 4 bytes read, 458 * and do a separate read of the high 4 bytes. 459 */ 460 } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 461 (cfg_prg.offset != PCI_CONF_ROM)) { 462 463 uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 464 465 /* Don't try to read the next 4 bytes past the end of BARs. */ 466 if (cfg_prg.offset >= PCI_CONF_BASE5) { 467 prg_p->status = PCITOOL_OUT_OF_RANGE; 468 return (EIO); 469 } 470 471 /* Access device. prg_p->status is modified on error. */ 472 cfg_prg.phys_addr += sizeof (uint32_t); 473 cfg_prg.offset += sizeof (uint32_t); 474 475 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 476 if (rval != SUCCESS) { 477 prg_p->status = cfg_prg.status; 478 return (rval); 479 } 480 481 /* 482 * Honor the 64 bit BAR as such, only when the upper 32 bits 483 * store a non-zero value. 484 */ 485 if (*bar_p) { 486 *space_p = PCI_MEM64_SPACE; 487 *bar_p = (*bar_p << 32) | low_bytes; 488 } else 489 *bar_p = low_bytes; 490 491 } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 492 493 /* 494 * ROM enabled. Filter ROM enable bit from the BAR. 495 * Treat as Mem32 henceforth. 496 */ 497 if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 498 *bar_p ^= PCI_BASE_ROM_ENABLE; 499 500 else { /* ROM disabled. */ 501 prg_p->status = PCITOOL_ROM_DISABLED; 502 return (EIO); 503 } 504 } 505 506 /* Accept a bar of 0 only for IO space. */ 507 if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 508 prg_p->status = PCITOOL_INVALID_ADDRESS; 509 return (EINVAL); 510 } 511 512 return (SUCCESS); 513 } 514 515 516 /* Perform register accesses on PCI leaf devices. */ 517 int 518 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 519 { 520 pcitool_reg_t prg; 521 uint64_t bar; 522 uint32_t space; 523 uint64_t off_in_space; 524 boolean_t write_flag = B_FALSE; 525 px_t *px_p = DIP_TO_STATE(dip); 526 int rval = 0; 527 528 if (cmd == PCITOOL_DEVICE_SET_REG) 529 write_flag = B_TRUE; 530 531 DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 532 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 533 mode) != DDI_SUCCESS) { 534 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 535 return (EFAULT); 536 } 537 538 if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 539 goto done_reg; 540 } 541 542 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 543 prg.bus_no, prg.dev_no, prg.func_no); 544 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 545 prg.barnum, prg.offset, prg.acc_attr); 546 547 if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 548 goto done_reg; 549 550 if (prg.barnum == 0) { /* Proper config space desired. */ 551 552 /* Enforce offset limits. */ 553 if (prg.offset >= DEV_CFG_SPACE_SIZE) { 554 DBG(DBG_TOOLS, dip, 555 "Config space offset 0x%" PRIx64 " out of range\n", 556 prg.offset); 557 prg.status = PCITOOL_OUT_OF_RANGE; 558 rval = EINVAL; 559 goto done_reg; 560 } 561 562 off_in_space = (PX_GET_BDF(&prg) << PX_PCI_BDF_OFFSET_DELTA) + 563 (uint32_t)prg.offset; 564 565 /* 566 * For sun4v, config space base won't be known. 567 * pxtool_get_phys_addr will return zero. 568 * Note that for sun4v, phys_addr isn't 569 * used for making config space accesses. 570 * 571 * For sun4u, assume that phys_addr will come back valid. 572 */ 573 574 /* Accessed entity is assumed small enough to be on one page. */ 575 prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 576 off_in_space); 577 578 DBG(DBG_TOOLS, dip, 579 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 580 "end:%s\n", off_in_space, prg.phys_addr, 581 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 582 583 /* 584 * Access device. pr.status is modified. 585 * BDF is assumed valid at this point. 586 */ 587 rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 588 goto done_reg; 589 } 590 591 /* IO/ MEM/ MEM64 space. */ 592 593 if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 594 goto done_reg; 595 596 switch (space) { 597 case PCI_MEM32_SPACE: 598 599 DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 600 601 /* Can't write to ROM */ 602 if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 603 prg.status = PCITOOL_ROM_WRITE; 604 rval = EIO; 605 goto done_reg; 606 } 607 break; 608 609 case PCI_IO_SPACE: 610 DBG(DBG_TOOLS, dip, "IO space\n"); 611 break; 612 613 case PCI_MEM64_SPACE: 614 DBG(DBG_TOOLS, dip, 615 "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 616 break; 617 618 default: 619 DBG(DBG_TOOLS, dip, "Unknown space!\n"); 620 prg.status = PCITOOL_IO_ERROR; 621 rval = EIO; 622 goto done_reg; 623 } 624 625 /* 626 * Common code for all IO/MEM range spaces. 627 * 628 * Use offset provided by caller to index into desired space. 629 * Note that prg.status is modified on error. 630 */ 631 off_in_space = bar + prg.offset; 632 prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 633 634 DBG(DBG_TOOLS, dip, 635 "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 636 "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 637 638 rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 639 640 done_reg: 641 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 642 mode) != DDI_SUCCESS) { 643 DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 644 rval = EFAULT; 645 } 646 return (rval); 647 } 648 649 650 int 651 pxtool_init(dev_info_t *dip) 652 { 653 int instance = ddi_get_instance(dip); 654 655 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 656 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 657 DDI_NT_REGACC, 0) != DDI_SUCCESS) { 658 return (DDI_FAILURE); 659 } 660 661 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 662 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 663 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 664 ddi_remove_minor_node(dip, PCI_MINOR_REG); 665 return (DDI_FAILURE); 666 } 667 668 return (DDI_SUCCESS); 669 } 670 671 672 void 673 pxtool_uninit(dev_info_t *dip) 674 { 675 ddi_remove_minor_node(dip, PCI_MINOR_REG); 676 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 677 } 678