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_ino 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 * px_p defines which leaf, space defines which space in that leaf, offset 350 * defines the offset within the specified space. 351 * 352 * This returns the physical address of the corresponding location. 353 */ 354 static uintptr_t 355 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 356 { 357 uint64_t range_base; 358 int rval; 359 pci_regspec_t dev_regspec; 360 struct regspec xlated_regspec; 361 dev_info_t *dip = px_p->px_dip; 362 363 /* 364 * Assume that requested entity is small enough to be on the same page. 365 * PCItool checks alignment so that this will be true for single 366 * accesses. 367 */ 368 dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT; 369 if (space == PCI_CONFIG_SPACE) { 370 dev_regspec.pci_phys_hi += 371 (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M)); 372 dev_regspec.pci_phys_low = offset & PCI_REG_REG_M; 373 } else { 374 dev_regspec.pci_phys_mid = offset >> 32; 375 dev_regspec.pci_phys_low = offset & 0xffffffff; 376 } 377 dev_regspec.pci_size_hi = 0; /* Not used. */ 378 379 /* Note: object is guaranteed to be within a page. */ 380 dev_regspec.pci_size_low = 4; 381 382 rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec); 383 384 DBG(DBG_TOOLS, dip, 385 "space:0x%d, offset:0x%" PRIx64 "\n", space, offset); 386 387 if (rval != DDI_SUCCESS) 388 return (NULL); 389 390 /* Bustype here returns the high order address bits. */ 391 xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p); 392 393 range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) + 394 xlated_regspec.regspec_addr; 395 DBG(DBG_TOOLS, dip, 396 "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n", 397 xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr, 398 xlated_regspec.regspec_size, range_base); 399 400 return ((uintptr_t)range_base); 401 } 402 403 404 static int 405 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 406 uint32_t *space_p) 407 { 408 int rval; 409 uint64_t off_in_space; 410 pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 411 dev_info_t *dip = px_p->px_dip; 412 413 *space_p = PCI_MEM32_SPACE; 414 *bar_p = 0; 415 416 /* 417 * Translate BAR number into offset of the BAR in 418 * the device's config space. 419 */ 420 cfg_prg.acc_attr = 421 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 422 423 /* 424 * Note: sun4u acc function uses phys_addr which includes offset. 425 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 426 */ 427 cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 428 off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset; 429 cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 430 off_in_space); 431 432 DBG(DBG_TOOLS, dip, 433 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 434 off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 435 436 /* 437 * Get Bus Address Register (BAR) from config space. 438 * cfg_prg.offset is the offset into config space of the 439 * BAR desired. prg_p->status is modified on error. 440 */ 441 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 442 443 if (rval != SUCCESS) { 444 prg_p->status = cfg_prg.status; 445 return (rval); 446 } 447 448 DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 449 450 /* 451 * BAR has bits saying this space is IO space, unless 452 * this is the ROM address register. 453 */ 454 if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 455 (cfg_prg.offset != PCI_CONF_ROM)) { 456 *space_p = PCI_IO_SPACE; 457 *bar_p &= PCI_BASE_IO_ADDR_M; 458 459 /* 460 * BAR has bits saying this space is 64 bit memory 461 * space, unless this is the ROM address register. 462 * 463 * The 64 bit address stored in two BAR cells is not 464 * necessarily aligned on an 8-byte boundary. 465 * Need to keep the first 4 bytes read, 466 * and do a separate read of the high 4 bytes. 467 */ 468 } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 469 (cfg_prg.offset != PCI_CONF_ROM)) { 470 471 uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 472 473 /* Don't try to read the next 4 bytes past the end of BARs. */ 474 if (cfg_prg.offset >= PCI_CONF_BASE5) { 475 prg_p->status = PCITOOL_OUT_OF_RANGE; 476 return (EIO); 477 } 478 479 /* Access device. prg_p->status is modified on error. */ 480 cfg_prg.phys_addr += sizeof (uint32_t); 481 cfg_prg.offset += sizeof (uint32_t); 482 483 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 484 if (rval != SUCCESS) { 485 prg_p->status = cfg_prg.status; 486 return (rval); 487 } 488 489 /* 490 * Honor the 64 bit BAR as such, only when the upper 32 bits 491 * store a non-zero value. 492 */ 493 if (*bar_p) { 494 *space_p = PCI_MEM64_SPACE; 495 *bar_p = (*bar_p << 32) | low_bytes; 496 } else 497 *bar_p = low_bytes; 498 499 } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 500 501 /* 502 * ROM enabled. Filter ROM enable bit from the BAR. 503 * Treat as Mem32 henceforth. 504 */ 505 if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 506 *bar_p ^= PCI_BASE_ROM_ENABLE; 507 508 else { /* ROM disabled. */ 509 prg_p->status = PCITOOL_ROM_DISABLED; 510 return (EIO); 511 } 512 } 513 514 /* Accept a bar of 0 only for IO space. */ 515 if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 516 prg_p->status = PCITOOL_INVALID_ADDRESS; 517 return (EINVAL); 518 } 519 520 return (SUCCESS); 521 } 522 523 524 /* Perform register accesses on PCI leaf devices. */ 525 int 526 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 527 { 528 pcitool_reg_t prg; 529 uint64_t bar; 530 uint32_t space; 531 uint64_t off_in_space; 532 boolean_t write_flag = B_FALSE; 533 px_t *px_p = DIP_TO_STATE(dip); 534 int rval = 0; 535 536 if (cmd == PCITOOL_DEVICE_SET_REG) 537 write_flag = B_TRUE; 538 539 DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 540 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 541 mode) != DDI_SUCCESS) { 542 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 543 return (EFAULT); 544 } 545 546 if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 547 goto done_reg; 548 } 549 550 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 551 prg.bus_no, prg.dev_no, prg.func_no); 552 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 553 prg.barnum, prg.offset, prg.acc_attr); 554 555 if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 556 goto done_reg; 557 558 if (prg.barnum == 0) { /* Proper config space desired. */ 559 560 /* Enforce offset limits. */ 561 if (prg.offset >= DEV_CFG_SPACE_SIZE) { 562 DBG(DBG_TOOLS, dip, 563 "Config space offset 0x%" PRIx64 " out of range\n", 564 prg.offset); 565 prg.status = PCITOOL_OUT_OF_RANGE; 566 rval = EINVAL; 567 goto done_reg; 568 } 569 570 /* 571 * For sun4v, config space base won't be known. 572 * pxtool_get_phys_addr will return zero. 573 * Note that for sun4v, phys_addr isn't 574 * used for making config space accesses. 575 * 576 * For sun4u, assume that phys_addr will come back valid. 577 */ 578 /* 579 * Accessed entity is assumed small enough to be on one page. 580 * 581 * Since config space is less than a page and is aligned to 582 * 0x1000, a device's entire config space will be on a single 583 * page. Pass the device's base config space address here, 584 * then add the offset within that space later. This works 585 * around an issue in px_xlate_reg (called by 586 * pxtool_get_phys_addr) which accepts only a 256 byte 587 * range within a device. 588 */ 589 off_in_space = PX_GET_BDF(&prg); 590 prg.phys_addr = 591 pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space); 592 prg.phys_addr += prg.offset; 593 594 DBG(DBG_TOOLS, dip, 595 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 596 "end:%s\n", off_in_space, prg.phys_addr, 597 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 598 599 /* 600 * Access device. pr.status is modified. 601 * BDF is assumed valid at this point. 602 */ 603 rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 604 goto done_reg; 605 } 606 607 /* IO/ MEM/ MEM64 space. */ 608 609 if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 610 goto done_reg; 611 612 switch (space) { 613 case PCI_MEM32_SPACE: 614 615 DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 616 617 /* Can't write to ROM */ 618 if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 619 prg.status = PCITOOL_ROM_WRITE; 620 rval = EIO; 621 goto done_reg; 622 } 623 break; 624 625 case PCI_IO_SPACE: 626 DBG(DBG_TOOLS, dip, "IO space\n"); 627 break; 628 629 case PCI_MEM64_SPACE: 630 DBG(DBG_TOOLS, dip, 631 "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 632 break; 633 634 default: 635 DBG(DBG_TOOLS, dip, "Unknown space!\n"); 636 prg.status = PCITOOL_IO_ERROR; 637 rval = EIO; 638 goto done_reg; 639 } 640 641 /* 642 * Common code for all IO/MEM range spaces. 643 * 644 * Use offset provided by caller to index into desired space. 645 * Note that prg.status is modified on error. 646 */ 647 off_in_space = bar + prg.offset; 648 prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 649 650 DBG(DBG_TOOLS, dip, 651 "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 652 "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 653 654 rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 655 656 done_reg: 657 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 658 mode) != DDI_SUCCESS) { 659 DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 660 rval = EFAULT; 661 } 662 return (rval); 663 } 664 665 666 int 667 pxtool_init(dev_info_t *dip) 668 { 669 int instance = ddi_get_instance(dip); 670 671 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 672 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 673 DDI_NT_REGACC, 0) != DDI_SUCCESS) { 674 return (DDI_FAILURE); 675 } 676 677 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 678 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 679 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 680 ddi_remove_minor_node(dip, PCI_MINOR_REG); 681 return (DDI_FAILURE); 682 } 683 684 return (DDI_SUCCESS); 685 } 686 687 688 void 689 pxtool_uninit(dev_info_t *dip) 690 { 691 ddi_remove_minor_node(dip, PCI_MINOR_REG); 692 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 693 } 694