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