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