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