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/stat.h> 27 #include <sys/sunddi.h> 28 #include <sys/param.h> 29 30 #include <sys/sysmacros.h> 31 #include <sys/machsystm.h> 32 #include <sys/promif.h> 33 #include <sys/cpuvar.h> 34 35 #include <sys/pci/pci_obj.h> 36 #include <sys/hotplug/pci/pcihp.h> 37 38 #include <sys/pci_tools.h> 39 #include <sys/pci/pci_tools_ext.h> 40 41 /* 42 * Number of interrupts supported per PCI bus. 43 */ 44 #define PCI_MAX_INO 0x3f 45 46 /* 47 * PCI Space definitions. 48 */ 49 #define PCI_CONFIG_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 50 #define PCI_IO_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_IO)) 51 #define PCI_MEM_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 52 #define PCI_MEM64_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 53 54 /* 55 * Extract 64 bit parent or size values from 32 bit cells of 56 * pci_ranges_t property. 57 * 58 * Only bits 42-32 are relevant in parent_high. 59 */ 60 #define PCI_GET_RANGE_PROP(ranges, bank) \ 61 ((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \ 62 ranges[bank].parent_low) 63 64 #define PCI_GET_RANGE_PROP_SIZE(ranges, bank) \ 65 ((((uint64_t)(ranges[bank].size_high)) << 32) | \ 66 ranges[bank].size_low) 67 68 #define PCI_BAR_OFFSET(x) (pci_bars[x.barnum]) 69 70 /* Big and little endian as boolean values. */ 71 #define BE B_TRUE 72 #define LE B_FALSE 73 74 #define SUCCESS 0 75 76 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */ 77 typedef union { 78 uint64_t u64; 79 uint32_t u32; 80 uint16_t u16; 81 uint8_t u8; 82 } peek_poke_value_t; 83 84 /* 85 * Offsets of BARS in config space. First entry of 0 means config space. 86 * Entries here correlate to pcitool_bars_t enumerated type. 87 */ 88 static uint8_t pci_bars[] = { 89 0x0, 90 PCI_CONF_BASE0, 91 PCI_CONF_BASE1, 92 PCI_CONF_BASE2, 93 PCI_CONF_BASE3, 94 PCI_CONF_BASE4, 95 PCI_CONF_BASE5, 96 PCI_CONF_ROM 97 }; 98 99 /*LINTLIBRARY*/ 100 101 static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size, 102 uint64_t paddr, uint64_t *value_p); 103 static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size, 104 uint64_t paddr, uint64_t value); 105 static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr, 106 uint64_t *data, uint8_t size, boolean_t write, boolean_t endian, 107 uint32_t *pcitool_status); 108 static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg); 109 static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, 110 uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar, 111 boolean_t *is_io_space); 112 static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, 113 uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag); 114 static int pcitool_intr_get_max_ino(uint32_t *arg, int mode); 115 static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p); 116 static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p); 117 118 extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int); 119 extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int); 120 121 /* 122 * Safe C wrapper around assy language routine pci_do_phys_peek 123 * 124 * Type is TRUE for big endian, FALSE for little endian. 125 * Size is 1, 2, 4 or 8 bytes. 126 * paddr is the physical address in IO space to access read. 127 * value_p is where the value is returned. 128 */ 129 static int 130 pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size, 131 uint64_t paddr, uint64_t *value_p) 132 { 133 on_trap_data_t otd; 134 int err = DDI_SUCCESS; 135 peek_poke_value_t peek_value; 136 137 pbm_t *pbm_p = pci_p->pci_pbm_p; 138 139 pbm_p->pbm_ontrap_data = &otd; 140 141 /* Set up trap handling to make the access safe. */ 142 143 /* 144 * on_trap works like setjmp. 145 * Set it up to not panic on data access error, 146 * but to call peek_fault instead. 147 * Call pci_do_phys_peek after trap handling is setup. 148 * When on_trap returns FALSE, it has been setup. 149 * When it returns TRUE, an it has caught an error. 150 */ 151 if (!on_trap(&otd, OT_DATA_ACCESS)) { 152 otd.ot_trampoline = (uintptr_t)&peek_fault; 153 err = pci_do_phys_peek(size, paddr, &peek_value.u64, type); 154 } else { 155 err = DDI_FAILURE; 156 } 157 158 pbm_p->pbm_ontrap_data = NULL; 159 no_trap(); 160 161 if (err != DDI_FAILURE) { 162 switch (size) { 163 case 8: 164 *value_p = (uint64_t)peek_value.u64; 165 break; 166 case 4: 167 *value_p = (uint64_t)peek_value.u32; 168 break; 169 case 2: 170 *value_p = (uint64_t)peek_value.u16; 171 break; 172 case 1: 173 *value_p = (uint64_t)peek_value.u8; 174 break; 175 default: 176 err = DDI_FAILURE; 177 } 178 } 179 180 return (err); 181 } 182 183 /* 184 * Safe C wrapper around assy language routine pci_do_phys_poke 185 * 186 * Type is TRUE for big endian, FALSE for little endian. 187 * Size is 1,2,4 or 8 bytes. 188 * paddr is the physical address in IO space to access read. 189 * value contains the value to be written. 190 */ 191 static int 192 pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size, 193 uint64_t paddr, uint64_t value) 194 { 195 on_trap_data_t otd; 196 int err = DDI_SUCCESS; 197 peek_poke_value_t poke_value; 198 199 pbm_t *pbm_p = pci_p->pci_pbm_p; 200 201 switch (size) { 202 case 8: 203 poke_value.u64 = value; 204 break; 205 case 4: 206 poke_value.u32 = (uint32_t)value; 207 break; 208 case 2: 209 poke_value.u16 = (uint16_t)value; 210 break; 211 case 1: 212 poke_value.u8 = (uint8_t)value; 213 break; 214 default: 215 return (DDI_FAILURE); 216 } 217 218 mutex_enter(&pbm_p->pbm_pokefault_mutex); 219 220 pbm_p->pbm_ontrap_data = &otd; 221 222 /* 223 * on_trap works like setjmp. 224 * Set it up to not panic on data access error, 225 * but to call poke_fault instead. 226 * Call pci_do_phys_poke after trap handling is setup. 227 * When on_trap returns FALSE, it has been setup. 228 * When it returns TRUE, an it has caught an error. 229 */ 230 if (!on_trap(&otd, OT_DATA_ACCESS)) { 231 otd.ot_trampoline = (uintptr_t)&poke_fault; 232 err = pci_do_phys_poke(size, paddr, &poke_value.u64, type); 233 } 234 235 /* Let the dust settle and errors occur if they will. */ 236 pbm_clear_error(pbm_p); 237 238 /* Check for an error. */ 239 if (otd.ot_trap == OT_DATA_ACCESS) { 240 err = DDI_FAILURE; 241 } 242 243 pbm_p->pbm_ontrap_data = NULL; 244 mutex_exit(&pbm_p->pbm_pokefault_mutex); 245 246 no_trap(); 247 return (err); 248 } 249 250 251 /*ARGSUSED*/ 252 static int 253 pcitool_intr_info(dev_info_t *dip, void *arg, int mode) 254 { 255 pcitool_intr_info_t intr_info; 256 int rval = SUCCESS; 257 258 /* If we need user_version, and to ret same user version as passed in */ 259 if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != 260 DDI_SUCCESS) { 261 return (EFAULT); 262 } 263 264 if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI) 265 return (ENOTSUP); 266 267 intr_info.ctlr_version = 0; /* XXX how to get real version? */ 268 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; 269 intr_info.num_intr = PCI_MAX_INO; 270 271 intr_info.drvr_version = PCITOOL_VERSION; 272 if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != 273 DDI_SUCCESS) { 274 rval = EFAULT; 275 } 276 277 return (rval); 278 } 279 280 281 /* 282 * Get interrupt information for a given ino. 283 * Returns info only for inos mapped to devices. 284 * 285 * Returned info is valid only when iget.num_devs is returned > 0. 286 * If ino is not enabled or is not mapped to a device, num_devs will be = 0. 287 */ 288 /*ARGSUSED*/ 289 static int 290 pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) 291 { 292 /* Array part isn't used here, but oh well... */ 293 pcitool_intr_get_t partial_iget; 294 pcitool_intr_get_t *iget = &partial_iget; 295 size_t iget_kmem_alloc_size = 0; 296 ib_t *ib_p = pci_p->pci_ib_p; 297 volatile uint64_t *imregp; 298 uint64_t imregval; 299 uint32_t ino; 300 uint8_t num_devs_ret; 301 int cpu_id; 302 int copyout_rval; 303 int rval = SUCCESS; 304 305 /* Read in just the header part, no array section. */ 306 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 307 DDI_SUCCESS) { 308 309 return (EFAULT); 310 } 311 312 if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) { 313 partial_iget.status = PCITOOL_IO_ERROR; 314 partial_iget.num_devs_ret = 0; 315 rval = ENOTSUP; 316 goto done_get_intr; 317 } 318 319 ino = partial_iget.ino; 320 num_devs_ret = partial_iget.num_devs_ret; 321 322 /* Validate argument. */ 323 if (ino > PCI_MAX_INO) { 324 partial_iget.status = PCITOOL_INVALID_INO; 325 partial_iget.num_devs_ret = 0; 326 rval = EINVAL; 327 goto done_get_intr; 328 } 329 330 /* Caller wants device information returned. */ 331 if (num_devs_ret > 0) { 332 333 /* 334 * Allocate room. 335 * Note if num_devs_ret == 0 iget remains pointing to 336 * partial_iget. 337 */ 338 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 339 iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 340 341 /* Read in whole structure to verify there's room. */ 342 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 343 SUCCESS) { 344 345 /* Be consistent and just return EFAULT here. */ 346 kmem_free(iget, iget_kmem_alloc_size); 347 348 return (EFAULT); 349 } 350 } 351 352 bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 353 iget->ino = ino; 354 iget->num_devs_ret = num_devs_ret; 355 356 imregp = ib_intr_map_reg_addr(ib_p, ino); 357 imregval = *imregp; 358 359 /* 360 * Read "valid" bit. If set, interrupts are enabled. 361 * This bit happens to be the same on Fire and Tomatillo. 362 */ 363 if (imregval & COMMON_INTR_MAP_REG_VALID) { 364 /* 365 * The following looks up the ib_ino_info and returns 366 * info of devices mapped to this ino. 367 */ 368 iget->num_devs = ib_get_ino_devs( 369 ib_p, ino, &iget->num_devs_ret, iget->dev); 370 371 if (ib_get_intr_target(pci_p, ino, &cpu_id) != DDI_SUCCESS) { 372 iget->status = PCITOOL_IO_ERROR; 373 rval = EIO; 374 goto done_get_intr; 375 } 376 377 /* 378 * Consider only inos mapped to devices (as opposed to 379 * inos mapped to the bridge itself. 380 */ 381 if (iget->num_devs > 0) { 382 /* 383 * These 2 items are platform specific, 384 * extracted from the bridge. 385 */ 386 iget->ctlr = 0; 387 iget->cpu_id = cpu_id; 388 } 389 } 390 done_get_intr: 391 iget->drvr_version = PCITOOL_VERSION; 392 copyout_rval = ddi_copyout(iget, arg, 393 PCITOOL_IGET_SIZE(num_devs_ret), mode); 394 395 if (iget_kmem_alloc_size > 0) { 396 kmem_free(iget, iget_kmem_alloc_size); 397 } 398 399 if (copyout_rval != DDI_SUCCESS) { 400 rval = EFAULT; 401 } 402 403 return (rval); 404 } 405 406 /* 407 * Associate a new CPU with a given ino. 408 * 409 * Operate only on inos which are already mapped to devices. 410 */ 411 static int 412 pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) 413 { 414 ib_t *ib_p = pci_p->pci_ib_p; 415 int rval = SUCCESS; 416 int ret = DDI_SUCCESS; 417 uint8_t zero = 0; 418 pcitool_intr_set_t iset; 419 volatile uint64_t *imregp; 420 uint64_t imregval; 421 422 size_t copyinout_size; 423 int old_cpu_id; 424 425 bzero(&iset, sizeof (pcitool_intr_set_t)); 426 427 /* Version 1 of pcitool_intr_set_t doesn't have flags. */ 428 copyinout_size = (size_t)&iset.flags - (size_t)&iset; 429 430 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 431 return (EFAULT); 432 433 switch (iset.user_version) { 434 case PCITOOL_V1: 435 break; 436 437 case PCITOOL_V2: 438 copyinout_size = sizeof (pcitool_intr_set_t); 439 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 440 return (EFAULT); 441 break; 442 443 default: 444 iset.status = PCITOOL_OUT_OF_RANGE; 445 rval = ENOTSUP; 446 goto done_set_intr; 447 } 448 449 if ((iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) || 450 (iset.flags & PCITOOL_INTR_FLAG_SET_MSI)) { 451 iset.status = PCITOOL_IO_ERROR; 452 rval = ENOTSUP; 453 goto done_set_intr; 454 } 455 456 /* Validate input argument and that ino given belongs to a device. */ 457 if ((iset.ino > PCI_MAX_INO) || 458 (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) { 459 iset.status = PCITOOL_INVALID_INO; 460 rval = EINVAL; 461 goto done_set_intr; 462 } 463 464 imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino); 465 imregval = *imregp; 466 467 /* Operate only on inos which are already enabled. */ 468 if (!(imregval & COMMON_INTR_MAP_REG_VALID)) { 469 iset.status = PCITOOL_INVALID_INO; 470 rval = EINVAL; 471 goto done_set_intr; 472 } 473 474 if (ib_get_intr_target(pci_p, iset.ino, &old_cpu_id) != DDI_SUCCESS) { 475 iset.status = PCITOOL_INVALID_INO; 476 rval = EINVAL; 477 goto done_set_intr; 478 } 479 480 if ((ret = ib_set_intr_target(pci_p, iset.ino, 481 iset.cpu_id)) == DDI_SUCCESS) { 482 iset.cpu_id = old_cpu_id; 483 iset.status = PCITOOL_SUCCESS; 484 goto done_set_intr; 485 } 486 487 switch (ret) { 488 case DDI_EPENDING: 489 iset.status = PCITOOL_PENDING_INTRTIMEOUT; 490 rval = ETIME; 491 break; 492 case DDI_EINVAL: 493 iset.status = PCITOOL_INVALID_CPUID; 494 rval = EINVAL; 495 break; 496 default: 497 iset.status = PCITOOL_INVALID_INO; 498 rval = EINVAL; 499 break; 500 } 501 done_set_intr: 502 iset.drvr_version = PCITOOL_VERSION; 503 if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) 504 rval = EFAULT; 505 506 return (rval); 507 } 508 509 510 /* Main function for handling interrupt CPU binding requests and queries. */ 511 int 512 pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode) 513 { 514 pci_t *pci_p = DEV_TO_SOFTSTATE(dev); 515 dev_info_t *dip = pci_p->pci_dip; 516 int rval = SUCCESS; 517 518 switch (cmd) { 519 520 /* Get system interrupt information. */ 521 case PCITOOL_SYSTEM_INTR_INFO: 522 rval = pcitool_intr_info(dip, arg, mode); 523 break; 524 525 /* Get interrupt information for a given ino. */ 526 case PCITOOL_DEVICE_GET_INTR: 527 rval = pcitool_get_intr(dip, arg, mode, pci_p); 528 break; 529 530 /* Associate a new CPU with a given ino. */ 531 case PCITOOL_DEVICE_SET_INTR: 532 rval = pcitool_set_intr(dip, arg, mode, pci_p); 533 break; 534 535 default: 536 rval = ENOTTY; 537 } 538 539 return (rval); 540 } 541 542 543 /* 544 * Wrapper around pcitool_phys_peek/poke. 545 * 546 * Validates arguments and calls pcitool_phys_peek/poke appropriately. 547 * 548 * Dip is of the nexus, 549 * phys_addr is the address to write in physical space, 550 * max_addr is the upper bound on the physical space used for bounds checking, 551 * pcitool_status returns more detailed status in addition to a more generic 552 * errno-style function return value. 553 * other args are self-explanatory. 554 */ 555 static int 556 pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr, 557 uint64_t *data, uint8_t size, boolean_t write, boolean_t endian, 558 uint32_t *pcitool_status) 559 { 560 561 int rval = SUCCESS; 562 dev_info_t *dip = pci_p->pci_dip; 563 564 /* Upper bounds checking. */ 565 if (phys_addr > max_addr) { 566 DEBUG2(DBG_TOOLS, dip, 567 "Phys addr 0x%llx out of range (max 0x%llx).\n", 568 phys_addr, max_addr); 569 *pcitool_status = PCITOOL_INVALID_ADDRESS; 570 571 rval = EINVAL; 572 573 /* Alignment checking. */ 574 } else if (!IS_P2ALIGNED(phys_addr, size)) { 575 DEBUG0(DBG_TOOLS, dip, "not aligned.\n"); 576 *pcitool_status = PCITOOL_NOT_ALIGNED; 577 578 rval = EINVAL; 579 580 /* Made it through checks. Do the access. */ 581 } else if (write) { 582 583 DEBUG3(DBG_PHYS_ACC, dip, 584 "%d byte %s pcitool_phys_poke at addr 0x%llx\n", 585 size, (endian ? "BE" : "LE"), phys_addr); 586 587 if (pcitool_phys_poke(pci_p, endian, size, phys_addr, 588 *data) != DDI_SUCCESS) { 589 DEBUG3(DBG_PHYS_ACC, dip, 590 "%d byte %s pcitool_phys_poke at addr " 591 "0x%llx failed\n", 592 size, (endian ? "BE" : "LE"), phys_addr); 593 *pcitool_status = PCITOOL_INVALID_ADDRESS; 594 595 rval = EFAULT; 596 } 597 598 } else { /* Read */ 599 600 DEBUG3(DBG_PHYS_ACC, dip, 601 "%d byte %s pcitool_phys_peek at addr 0x%llx\n", 602 size, (endian ? "BE" : "LE"), phys_addr); 603 604 if (pcitool_phys_peek(pci_p, endian, size, phys_addr, 605 data) != DDI_SUCCESS) { 606 DEBUG3(DBG_PHYS_ACC, dip, 607 "%d byte %s pcitool_phys_peek at addr " 608 "0x%llx failed\n", 609 size, (endian ? "BE" : "LE"), phys_addr); 610 *pcitool_status = PCITOOL_INVALID_ADDRESS; 611 612 rval = EFAULT; 613 } 614 } 615 return (rval); 616 } 617 618 /* 619 * Perform register accesses on the nexus device itself. 620 */ 621 int 622 pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode) 623 { 624 625 pci_t *pci_p = DEV_TO_SOFTSTATE(dev); 626 dev_info_t *dip = pci_p->pci_dip; 627 pci_nexus_regspec_t *pci_rp = NULL; 628 boolean_t write_flag = B_FALSE; 629 pcitool_reg_t prg; 630 uint64_t base_addr; 631 uint64_t max_addr; 632 uint32_t reglen; 633 uint8_t size; 634 uint32_t rval = 0; 635 636 if (cmd == PCITOOL_NEXUS_SET_REG) 637 write_flag = B_TRUE; 638 639 DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n"); 640 641 /* Read data from userland. */ 642 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 643 DDI_SUCCESS) { 644 DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n"); 645 return (EFAULT); 646 } 647 648 /* Read reg property which contains starting addr and size of banks. */ 649 if (ddi_prop_lookup_int_array( 650 DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 651 "reg", (int **)&pci_rp, ®len) == DDI_SUCCESS) { 652 if (((reglen * sizeof (int)) % 653 sizeof (pci_nexus_regspec_t)) != 0) { 654 DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed"); 655 prg.status = PCITOOL_REGPROP_NOTWELLFORMED; 656 rval = EIO; 657 goto done; 658 } 659 } 660 661 /* Bounds check the bank number. */ 662 if (prg.barnum >= 663 (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) { 664 prg.status = PCITOOL_OUT_OF_RANGE; 665 rval = EINVAL; 666 goto done; 667 } 668 669 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 670 base_addr = pci_rp[prg.barnum].phys_addr; 671 max_addr = base_addr + pci_rp[prg.barnum].size; 672 prg.phys_addr = base_addr + prg.offset; 673 674 DEBUG4(DBG_TOOLS, dip, 675 "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, " 676 "addr:0x%llx, max_addr:0x%llx\n", 677 base_addr, prg.offset, prg.phys_addr, max_addr); 678 679 /* Access device. prg.status is modified. */ 680 rval = pcitool_access(pci_p, 681 prg.phys_addr, max_addr, &prg.data, size, write_flag, 682 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status); 683 684 done: 685 if (pci_rp != NULL) 686 ddi_prop_free(pci_rp); 687 688 prg.drvr_version = PCITOOL_VERSION; 689 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 690 DDI_SUCCESS) { 691 DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n"); 692 return (EFAULT); 693 } 694 695 return (rval); 696 } 697 698 699 static int 700 pcitool_validate_barnum_bdf(pcitool_reg_t *prg) 701 { 702 int rval = SUCCESS; 703 704 if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 705 prg->status = PCITOOL_OUT_OF_RANGE; 706 rval = EINVAL; 707 708 /* Validate address arguments of bus / dev / func */ 709 } else if (((prg->bus_no & 710 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 711 ((prg->dev_no & 712 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 713 ((prg->func_no & 714 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 715 prg->status = PCITOOL_INVALID_ADDRESS; 716 rval = EINVAL; 717 } 718 719 return (rval); 720 } 721 722 static int 723 pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr, 724 uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space) 725 { 726 727 uint8_t bar_offset; 728 int rval; 729 dev_info_t *dip = pci_p->pci_dip; 730 731 *bar = 0; 732 *is_io_space = B_FALSE; 733 734 /* 735 * Translate BAR number into offset of the BAR in 736 * the device's config space. 737 */ 738 bar_offset = PCI_BAR_OFFSET((*prg)); 739 740 DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n", 741 prg->barnum, bar_offset); 742 743 /* 744 * Get Bus Address Register (BAR) from config space. 745 * bar_offset is the offset into config space of the BAR desired. 746 * prg->status is modified on error. 747 */ 748 rval = pcitool_access(pci_p, config_base_addr + bar_offset, 749 config_max_addr, bar, 750 4, /* 4 bytes. */ 751 B_FALSE, /* Read */ 752 B_FALSE, /* Little endian. */ 753 &prg->status); 754 if (rval != SUCCESS) 755 return (rval); 756 757 DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar); 758 if (!(*bar)) { 759 prg->status = PCITOOL_INVALID_ADDRESS; 760 return (EINVAL); 761 } 762 763 /* 764 * BAR has bits saying this space is IO space, unless 765 * this is the ROM address register. 766 */ 767 if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) && 768 (bar_offset != PCI_CONF_ROM)) { 769 *is_io_space = B_TRUE; 770 *bar &= PCI_BASE_IO_ADDR_M; 771 772 /* 773 * BAR has bits saying this space is 64 bit memory 774 * space, unless this is the ROM address register. 775 * 776 * The 64 bit address stored in two BAR cells is not necessarily 777 * aligned on an 8-byte boundary. Need to keep the first 4 778 * bytes read, and do a separate read of the high 4 bytes. 779 */ 780 781 } else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) { 782 783 uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL); 784 785 /* Don't try to read past the end of BARs. */ 786 if (bar_offset >= PCI_CONF_BASE5) { 787 prg->status = PCITOOL_OUT_OF_RANGE; 788 return (EIO); 789 } 790 791 /* Access device. prg->status is modified on error. */ 792 rval = pcitool_access(pci_p, 793 config_base_addr + bar_offset + 4, config_max_addr, bar, 794 4, /* 4 bytes. */ 795 B_FALSE, /* Read */ 796 B_FALSE, /* Little endian. */ 797 &prg->status); 798 if (rval != SUCCESS) 799 return (rval); 800 801 *bar = (*bar << 32) + low_bytes; 802 } 803 804 return (SUCCESS); 805 } 806 807 808 static int 809 pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr, 810 uint64_t max_addr, uint8_t size, boolean_t write_flag) 811 { 812 int rval; 813 dev_info_t *dip = pci_p->pci_dip; 814 815 /* Access config space and we're done. */ 816 prg->phys_addr = base_addr + prg->offset; 817 818 DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, " 819 "offset:0x%llx, phys_addr:0x%llx, end:%s\n", 820 base_addr, prg->offset, prg->phys_addr, 821 (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl")); 822 823 /* Access device. pr.status is modified. */ 824 rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size, 825 write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status); 826 827 DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data); 828 829 return (rval); 830 } 831 832 /* Perform register accesses on PCI leaf devices. */ 833 int 834 pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode) 835 { 836 pci_t *pci_p = DEV_TO_SOFTSTATE(dev); 837 dev_info_t *dip = pci_p->pci_dip; 838 pci_ranges_t *rp = pci_p->pci_ranges; 839 pcitool_reg_t prg; 840 uint64_t max_addr; 841 uint64_t base_addr; 842 uint64_t range_prop; 843 uint64_t range_prop_size; 844 uint64_t bar = 0; 845 int rval = 0; 846 boolean_t write_flag = B_FALSE; 847 boolean_t is_io_space = B_FALSE; 848 uint8_t size; 849 850 if (cmd == PCITOOL_DEVICE_SET_REG) 851 write_flag = B_TRUE; 852 853 DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n"); 854 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 855 DDI_SUCCESS) { 856 DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n"); 857 return (EFAULT); 858 } 859 860 DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 861 prg.bus_no, prg.dev_no, prg.func_no); 862 863 if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS) 864 goto done_reg; 865 866 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 867 868 /* Get config space first. */ 869 range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK); 870 range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK); 871 max_addr = range_prop + range_prop_size; 872 873 /* 874 * Build device address based on base addr from range prop, and bus, 875 * dev and func values passed in. This address is where config space 876 * begins. 877 */ 878 base_addr = range_prop + 879 (prg.bus_no << PCI_REG_BUS_SHIFT) + 880 (prg.dev_no << PCI_REG_DEV_SHIFT) + 881 (prg.func_no << PCI_REG_FUNC_SHIFT); 882 883 if ((base_addr < range_prop) || (base_addr >= max_addr)) { 884 prg.status = PCITOOL_OUT_OF_RANGE; 885 rval = EINVAL; 886 goto done_reg; 887 } 888 889 DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x " 890 "func:0x%x, addr:0x%x\n", range_prop, 891 prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT, 892 prg.func_no << PCI_REG_FUNC_SHIFT, base_addr); 893 894 /* Proper config space desired. */ 895 if (prg.barnum == 0) { 896 897 rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr, 898 size, write_flag); 899 900 } else { /* IO / MEM / MEM64 space. */ 901 902 if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar, 903 &is_io_space) != SUCCESS) 904 goto done_reg; 905 906 /* IO space. */ 907 if (is_io_space) { 908 909 DEBUG0(DBG_TOOLS, dip, "IO space\n"); 910 911 /* Reposition to focus on IO space. */ 912 range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK); 913 range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, 914 PCI_IO_RANGE_BANK); 915 916 /* 64 bit memory space. */ 917 } else if ((bar >> 32) != 0) { 918 919 DEBUG1(DBG_TOOLS, dip, 920 "64 bit mem space. 64-bit bar is 0x%llx\n", bar); 921 922 /* Reposition to MEM64 range space. */ 923 range_prop = PCI_GET_RANGE_PROP(rp, 924 PCI_MEM64_RANGE_BANK); 925 range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, 926 PCI_MEM64_RANGE_BANK); 927 928 } else { /* Mem32 space, including ROM */ 929 930 DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n"); 931 932 if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) { 933 934 DEBUG0(DBG_TOOLS, dip, 935 "Additional ROM checking\n"); 936 937 /* Can't write to ROM */ 938 if (write_flag) { 939 prg.status = PCITOOL_ROM_WRITE; 940 rval = EIO; 941 goto done_reg; 942 943 /* ROM disabled for reading */ 944 } else if (!(bar & 0x00000001)) { 945 prg.status = PCITOOL_ROM_DISABLED; 946 rval = EIO; 947 goto done_reg; 948 } 949 } 950 951 range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK); 952 range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, 953 PCI_MEM_RANGE_BANK); 954 } 955 956 /* Common code for all IO/MEM range spaces. */ 957 max_addr = range_prop + range_prop_size; 958 base_addr = range_prop + bar; 959 960 DEBUG3(DBG_TOOLS, dip, 961 "addr portion of bar is 0x%llx, base=0x%llx, " 962 "offset:0x%lx\n", bar, base_addr, prg.offset); 963 964 /* 965 * Use offset provided by caller to index into 966 * desired space, then access. 967 * Note that prg.status is modified on error. 968 */ 969 prg.phys_addr = base_addr + prg.offset; 970 rval = pcitool_access(pci_p, prg.phys_addr, 971 max_addr, &prg.data, size, write_flag, 972 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status); 973 } 974 975 done_reg: 976 prg.drvr_version = PCITOOL_VERSION; 977 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 978 DDI_SUCCESS) { 979 DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n"); 980 rval = EFAULT; 981 } 982 return (rval); 983 } 984 985 int 986 pcitool_init(dev_info_t *dip) 987 { 988 int instance = ddi_get_instance(dip); 989 990 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 991 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 992 DDI_NT_REGACC, 0) != DDI_SUCCESS) 993 return (DDI_FAILURE); 994 995 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 996 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 997 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 998 ddi_remove_minor_node(dip, PCI_MINOR_REG); 999 return (DDI_FAILURE); 1000 } 1001 1002 return (DDI_SUCCESS); 1003 } 1004 1005 void 1006 pcitool_uninit(dev_info_t *dip) 1007 { 1008 ddi_remove_minor_node(dip, PCI_MINOR_REG); 1009 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 1010 } 1011