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