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