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 2007 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 /*ARGSUSED*/ 88 static int 89 pxtool_intr_info(dev_info_t *dip, void *arg, int mode) 90 { 91 pcitool_intr_info_t intr_info; 92 int rval = SUCCESS; 93 94 /* If we need user_version, and to ret same user version as passed in */ 95 if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != 96 DDI_SUCCESS) { 97 return (EFAULT); 98 } 99 100 intr_info.ctlr_version = 0; /* XXX how to get real version? */ 101 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; 102 intr_info.num_intr = pxtool_num_inos; 103 104 intr_info.drvr_version = PCITOOL_VERSION; 105 if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != 106 DDI_SUCCESS) { 107 rval = EFAULT; 108 } 109 110 return (rval); 111 } 112 113 114 /* 115 * Get interrupt information for a given ino. 116 * Returns info only for inos mapped to devices. 117 * 118 * Returned info is valid only when iget.num_devs is returned > 0. 119 * If ino is not enabled or is not mapped to a device, 120 * iget.num_devs will be returned as = 0. 121 */ 122 /*ARGSUSED*/ 123 static int 124 pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 125 { 126 /* Array part isn't used here, but oh well... */ 127 pcitool_intr_get_t partial_iget; 128 uint32_t ino; 129 uint8_t num_devs_ret; 130 int copyout_rval; 131 sysino_t sysino; 132 intr_valid_state_t intr_valid_state; 133 cpuid_t old_cpu_id; 134 px_t *px_p = DIP_TO_STATE(dip); 135 pcitool_intr_get_t *iget = &partial_iget; 136 size_t iget_kmem_alloc_size = 0; 137 int rval = SUCCESS; 138 139 /* Read in just the header part, no array section. */ 140 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 141 DDI_SUCCESS) 142 return (EFAULT); 143 144 ino = partial_iget.ino; 145 num_devs_ret = partial_iget.num_devs_ret; 146 147 partial_iget.num_devs_ret = 0; /* Assume error for now. */ 148 partial_iget.status = PCITOOL_INVALID_INO; 149 rval = EINVAL; 150 151 /* Validate argument. */ 152 if (partial_iget.ino > pxtool_num_inos) { 153 goto done_get_intr; 154 } 155 156 /* Caller wants device information returned. */ 157 if (num_devs_ret > 0) { 158 159 /* 160 * Allocate room. 161 * Note if num_devs == 0 iget remains pointing to 162 * partial_iget. 163 */ 164 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 165 iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 166 167 /* Read in whole structure to verify there's room. */ 168 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 169 SUCCESS) { 170 171 /* Be consistent and just return EFAULT here. */ 172 kmem_free(iget, iget_kmem_alloc_size); 173 174 return (EFAULT); 175 } 176 } 177 178 bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 179 iget->ino = ino; 180 iget->num_devs_ret = num_devs_ret; 181 182 /* Convert leaf-wide intr to system-wide intr */ 183 if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) == 184 DDI_FAILURE) { 185 iget->status = PCITOOL_IO_ERROR; 186 rval = EIO; 187 goto done_get_intr; 188 } 189 190 /* Operate only on inos which are already enabled. */ 191 if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) == 192 DDI_FAILURE) { 193 iget->status = PCITOOL_IO_ERROR; 194 rval = EIO; 195 goto done_get_intr; 196 } 197 198 /* 199 * Consider all valid inos: those mapped to the root complex itself 200 * as well as those mapped to devices. 201 */ 202 if (intr_valid_state == INTR_VALID) { 203 204 /* 205 * The following looks up the px_ino and returns 206 * info of devices mapped to this ino. 207 */ 208 iget->num_devs = pxtool_ib_get_ino_devs( 209 px_p, ino, &iget->num_devs_ret, iget->dev); 210 211 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 212 DDI_FAILURE) { 213 iget->status = PCITOOL_IO_ERROR; 214 rval = EIO; 215 goto done_get_intr; 216 } 217 iget->cpu_id = old_cpu_id; 218 } 219 220 iget->status = PCITOOL_SUCCESS; 221 rval = SUCCESS; 222 223 done_get_intr: 224 iget->drvr_version = PCITOOL_VERSION; 225 copyout_rval = 226 ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); 227 228 if (iget_kmem_alloc_size > 0) 229 kmem_free(iget, iget_kmem_alloc_size); 230 231 if (copyout_rval != DDI_SUCCESS) 232 rval = EFAULT; 233 234 return (rval); 235 } 236 237 238 /* 239 * Associate a new CPU with a given ino. 240 * 241 * Operate only on inos which are already mapped to devices. 242 */ 243 static int 244 pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 245 { 246 pcitool_intr_set_t iset; 247 cpuid_t old_cpu_id; 248 sysino_t sysino; 249 px_t *px_p = DIP_TO_STATE(dip); 250 px_ib_t *ib_p = px_p->px_ib_p; 251 uint8_t zero = 0; 252 int rval = SUCCESS; 253 size_t copyinout_size; 254 255 bzero(&iset, sizeof (pcitool_intr_set_t)); 256 257 /* Version 1 of pcitool_intr_set_t doesn't have flags. */ 258 copyinout_size = (size_t)&iset.flags - (size_t)&iset; 259 260 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 261 return (EFAULT); 262 263 switch (iset.user_version) { 264 case PCITOOL_V1: 265 break; 266 267 case PCITOOL_V2: 268 copyinout_size = sizeof (pcitool_intr_set_t); 269 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 270 return (EFAULT); 271 break; 272 273 default: 274 iset.status = PCITOOL_OUT_OF_RANGE; 275 rval = ENOTSUP; 276 goto done_set_intr; 277 } 278 279 if (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP) { 280 iset.status = PCITOOL_IO_ERROR; 281 rval = ENOTSUP; 282 goto done_set_intr; 283 } 284 285 iset.status = PCITOOL_INVALID_INO; 286 rval = EINVAL; 287 288 /* Validate input argument. */ 289 if (iset.ino > pxtool_num_inos) 290 goto done_set_intr; 291 292 /* Validate that ino given belongs to a device. */ 293 if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0) 294 goto done_set_intr; 295 296 /* 297 * Get lock, validate cpu and write new mapreg value. 298 * Return original cpu value to caller via iset.cpu. 299 */ 300 mutex_enter(&cpu_lock); 301 if (pxtool_validate_cpuid(iset.cpu_id)) { 302 303 DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id); 304 305 if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) == 306 DDI_FAILURE) 307 goto done_set_intr; 308 309 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 310 DDI_FAILURE) 311 goto done_set_intr; 312 313 px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE); 314 315 px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino); 316 317 iset.cpu_id = old_cpu_id; 318 iset.status = PCITOOL_SUCCESS; 319 rval = SUCCESS; 320 321 } else { /* Invalid cpu. Restore original register image. */ 322 323 DBG(DBG_TOOLS, dip, 324 "Invalid cpuid: writing orig mapreg value\n"); 325 326 iset.status = PCITOOL_INVALID_CPUID; 327 rval = EINVAL; 328 } 329 mutex_exit(&cpu_lock); 330 331 done_set_intr: 332 iset.drvr_version = PCITOOL_VERSION; 333 if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) 334 rval = EFAULT; 335 336 return (rval); 337 } 338 339 340 /* Main function for handling interrupt CPU binding requests and queries. */ 341 int 342 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 343 { 344 int rval = SUCCESS; 345 346 switch (cmd) { 347 348 /* Get system interrupt information. */ 349 case PCITOOL_SYSTEM_INTR_INFO: 350 rval = pxtool_intr_info(dip, arg, mode); 351 break; 352 353 /* Get interrupt information for a given ino. */ 354 case PCITOOL_DEVICE_GET_INTR: 355 rval = pxtool_get_intr(dip, arg, mode); 356 break; 357 358 /* Associate a new CPU with a given ino. */ 359 case PCITOOL_DEVICE_SET_INTR: 360 rval = pxtool_set_intr(dip, arg, mode); 361 break; 362 363 default: 364 rval = ENOTTY; 365 } 366 367 return (rval); 368 } 369 370 371 static int 372 pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 373 { 374 int rval = SUCCESS; 375 376 if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 377 prg->status = PCITOOL_OUT_OF_RANGE; 378 rval = EINVAL; 379 380 /* Validate address arguments of bus / dev / func */ 381 } else if (((prg->bus_no & 382 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 383 ((prg->dev_no & 384 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 385 ((prg->func_no & 386 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 387 prg->status = PCITOOL_INVALID_ADDRESS; 388 rval = EINVAL; 389 } 390 391 return (rval); 392 } 393 394 /* 395 * px_p defines which leaf, space defines which space in that leaf, offset 396 * defines the offset within the specified space. 397 * 398 * This returns the physical address of the corresponding location. 399 */ 400 static uintptr_t 401 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 402 { 403 uint64_t range_base; 404 int rval; 405 pci_regspec_t dev_regspec; 406 struct regspec xlated_regspec; 407 dev_info_t *dip = px_p->px_dip; 408 409 /* 410 * Assume that requested entity is small enough to be on the same page. 411 * PCItool checks alignment so that this will be true for single 412 * accesses. 413 */ 414 dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT; 415 if (space == PCI_CONFIG_SPACE) { 416 dev_regspec.pci_phys_hi += 417 (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M)); 418 dev_regspec.pci_phys_low = offset & PCI_REG_REG_M; 419 dev_regspec.pci_phys_mid = 0; /* Not used */ 420 } else { 421 dev_regspec.pci_phys_mid = offset >> 32; 422 dev_regspec.pci_phys_low = offset & 0xffffffff; 423 } 424 dev_regspec.pci_size_hi = 0; /* Not used. */ 425 426 /* Note: object is guaranteed to be within a page. */ 427 dev_regspec.pci_size_low = 4; 428 429 rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec); 430 431 DBG(DBG_TOOLS, dip, 432 "space:0x%d, offset:0x%" PRIx64 "\n", space, offset); 433 434 if (rval != DDI_SUCCESS) 435 return (NULL); 436 437 /* Bustype here returns the high order address bits. */ 438 xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p); 439 440 range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) + 441 xlated_regspec.regspec_addr; 442 DBG(DBG_TOOLS, dip, 443 "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n", 444 xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr, 445 xlated_regspec.regspec_size, range_base); 446 447 return ((uintptr_t)range_base); 448 } 449 450 451 static int 452 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 453 uint32_t *space_p) 454 { 455 int rval; 456 uint64_t off_in_space; 457 pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 458 dev_info_t *dip = px_p->px_dip; 459 460 *space_p = PCI_MEM32_SPACE; 461 *bar_p = 0; 462 463 /* 464 * Translate BAR number into offset of the BAR in 465 * the device's config space. 466 */ 467 cfg_prg.acc_attr = 468 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 469 470 /* 471 * Note: sun4u acc function uses phys_addr which includes offset. 472 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 473 */ 474 cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 475 off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset; 476 cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 477 off_in_space); 478 479 DBG(DBG_TOOLS, dip, 480 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 481 off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 482 483 /* 484 * Get Bus Address Register (BAR) from config space. 485 * cfg_prg.offset is the offset into config space of the 486 * BAR desired. prg_p->status is modified on error. 487 */ 488 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 489 490 if (rval != SUCCESS) { 491 prg_p->status = cfg_prg.status; 492 return (rval); 493 } 494 495 DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 496 497 /* 498 * BAR has bits saying this space is IO space, unless 499 * this is the ROM address register. 500 */ 501 if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 502 (cfg_prg.offset != PCI_CONF_ROM)) { 503 *space_p = PCI_IO_SPACE; 504 *bar_p &= PCI_BASE_IO_ADDR_M; 505 506 /* 507 * BAR has bits saying this space is 64 bit memory 508 * space, unless this is the ROM address register. 509 * 510 * The 64 bit address stored in two BAR cells is not 511 * necessarily aligned on an 8-byte boundary. 512 * Need to keep the first 4 bytes read, 513 * and do a separate read of the high 4 bytes. 514 */ 515 } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 516 (cfg_prg.offset != PCI_CONF_ROM)) { 517 518 uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 519 520 /* Don't try to read the next 4 bytes past the end of BARs. */ 521 if (cfg_prg.offset >= PCI_CONF_BASE5) { 522 prg_p->status = PCITOOL_OUT_OF_RANGE; 523 return (EIO); 524 } 525 526 /* Access device. prg_p->status is modified on error. */ 527 cfg_prg.phys_addr += sizeof (uint32_t); 528 cfg_prg.offset += sizeof (uint32_t); 529 530 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 531 if (rval != SUCCESS) { 532 prg_p->status = cfg_prg.status; 533 return (rval); 534 } 535 536 /* 537 * Honor the 64 bit BAR as such, only when the upper 32 bits 538 * store a non-zero value. 539 */ 540 if (*bar_p) { 541 *space_p = PCI_MEM64_SPACE; 542 *bar_p = (*bar_p << 32) | low_bytes; 543 } else 544 *bar_p = low_bytes; 545 546 } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 547 548 /* 549 * ROM enabled. Filter ROM enable bit from the BAR. 550 * Treat as Mem32 henceforth. 551 */ 552 if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 553 *bar_p ^= PCI_BASE_ROM_ENABLE; 554 555 else { /* ROM disabled. */ 556 prg_p->status = PCITOOL_ROM_DISABLED; 557 return (EIO); 558 } 559 } 560 561 /* Accept a bar of 0 only for IO space. */ 562 if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 563 prg_p->status = PCITOOL_INVALID_ADDRESS; 564 return (EINVAL); 565 } 566 567 return (SUCCESS); 568 } 569 570 571 /* Perform register accesses on PCI leaf devices. */ 572 int 573 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 574 { 575 pcitool_reg_t prg; 576 uint64_t bar; 577 uint32_t space; 578 uint64_t off_in_space; 579 boolean_t write_flag = B_FALSE; 580 px_t *px_p = DIP_TO_STATE(dip); 581 int rval = 0; 582 583 if (cmd == PCITOOL_DEVICE_SET_REG) 584 write_flag = B_TRUE; 585 586 DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 587 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 588 mode) != DDI_SUCCESS) { 589 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 590 return (EFAULT); 591 } 592 593 if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 594 goto done_reg; 595 } 596 597 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 598 prg.bus_no, prg.dev_no, prg.func_no); 599 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 600 prg.barnum, prg.offset, prg.acc_attr); 601 602 if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 603 goto done_reg; 604 605 if (prg.barnum == 0) { /* Proper config space desired. */ 606 607 /* Enforce offset limits. */ 608 if (prg.offset >= DEV_CFG_SPACE_SIZE) { 609 DBG(DBG_TOOLS, dip, 610 "Config space offset 0x%" PRIx64 " out of range\n", 611 prg.offset); 612 prg.status = PCITOOL_OUT_OF_RANGE; 613 rval = EINVAL; 614 goto done_reg; 615 } 616 617 /* 618 * For sun4v, config space base won't be known. 619 * pxtool_get_phys_addr will return zero. 620 * Note that for sun4v, phys_addr isn't 621 * used for making config space accesses. 622 * 623 * For sun4u, assume that phys_addr will come back valid. 624 */ 625 /* 626 * Accessed entity is assumed small enough to be on one page. 627 * 628 * Since config space is less than a page and is aligned to 629 * 0x1000, a device's entire config space will be on a single 630 * page. Pass the device's base config space address here, 631 * then add the offset within that space later. This works 632 * around an issue in px_xlate_reg (called by 633 * pxtool_get_phys_addr) which accepts only a 256 byte 634 * range within a device. 635 */ 636 off_in_space = PX_GET_BDF(&prg); 637 prg.phys_addr = 638 pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space); 639 prg.phys_addr += prg.offset; 640 641 DBG(DBG_TOOLS, dip, 642 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 643 "end:%s\n", off_in_space, prg.phys_addr, 644 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 645 646 /* 647 * Access device. pr.status is modified. 648 * BDF is assumed valid at this point. 649 */ 650 rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 651 goto done_reg; 652 } 653 654 /* IO/ MEM/ MEM64 space. */ 655 656 if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 657 goto done_reg; 658 659 switch (space) { 660 case PCI_MEM32_SPACE: 661 662 DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 663 664 /* Can't write to ROM */ 665 if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 666 prg.status = PCITOOL_ROM_WRITE; 667 rval = EIO; 668 goto done_reg; 669 } 670 break; 671 672 case PCI_IO_SPACE: 673 DBG(DBG_TOOLS, dip, "IO space\n"); 674 break; 675 676 case PCI_MEM64_SPACE: 677 DBG(DBG_TOOLS, dip, 678 "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 679 break; 680 681 default: 682 DBG(DBG_TOOLS, dip, "Unknown space!\n"); 683 prg.status = PCITOOL_IO_ERROR; 684 rval = EIO; 685 goto done_reg; 686 } 687 688 /* 689 * Common code for all IO/MEM range spaces. 690 * 691 * Use offset provided by caller to index into desired space. 692 * Note that prg.status is modified on error. 693 */ 694 off_in_space = bar + prg.offset; 695 prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 696 697 DBG(DBG_TOOLS, dip, 698 "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 699 "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 700 701 rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 702 703 done_reg: 704 prg.drvr_version = PCITOOL_VERSION; 705 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 706 mode) != DDI_SUCCESS) { 707 DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 708 rval = EFAULT; 709 } 710 return (rval); 711 } 712 713 714 int 715 pxtool_init(dev_info_t *dip) 716 { 717 int instance = ddi_get_instance(dip); 718 719 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 720 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 721 DDI_NT_REGACC, 0) != DDI_SUCCESS) { 722 return (DDI_FAILURE); 723 } 724 725 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 726 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 727 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 728 ddi_remove_minor_node(dip, PCI_MINOR_REG); 729 return (DDI_FAILURE); 730 } 731 732 return (DDI_SUCCESS); 733 } 734 735 736 void 737 pxtool_uninit(dev_info_t *dip) 738 { 739 ddi_remove_minor_node(dip, PCI_MINOR_REG); 740 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 741 } 742