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