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