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/types.h> 30 #include <sys/mkdev.h> 31 #include <sys/stat.h> 32 #include <sys/sunddi.h> 33 #include <vm/seg_kmem.h> 34 #include <sys/machparam.h> 35 #include <sys/sunndi.h> 36 #include <sys/ontrap.h> 37 #include <sys/psm.h> 38 #include <sys/pcie.h> 39 #include <sys/hotplug/pci/pcihp.h> 40 #include <sys/pci_cfgspace.h> 41 #include <sys/pci_tools.h> 42 #include <io/pci/pci_tools_ext.h> 43 #include <io/pcplusmp/apic.h> 44 #include <io/pci/pci_var.h> 45 #include <sys/promif.h> 46 #include <sys/x86_archext.h> 47 48 #define PCIEX_BDF_OFFSET_DELTA 4 49 #define PCIEX_REG_FUNC_SHIFT (PCI_REG_FUNC_SHIFT + PCIEX_BDF_OFFSET_DELTA) 50 #define PCIEX_REG_DEV_SHIFT (PCI_REG_DEV_SHIFT + PCIEX_BDF_OFFSET_DELTA) 51 #define PCIEX_REG_BUS_SHIFT (PCI_REG_BUS_SHIFT + PCIEX_BDF_OFFSET_DELTA) 52 53 #define SUCCESS 0 54 55 int pcitool_debug = 0; 56 57 /* 58 * Offsets of BARS in config space. First entry of 0 means config space. 59 * Entries here correlate to pcitool_bars_t enumerated type. 60 */ 61 static uint8_t pci_bars[] = { 62 0x0, 63 PCI_CONF_BASE0, 64 PCI_CONF_BASE1, 65 PCI_CONF_BASE2, 66 PCI_CONF_BASE3, 67 PCI_CONF_BASE4, 68 PCI_CONF_BASE5, 69 PCI_CONF_ROM 70 }; 71 72 /* Max offset allowed into config space for a particular device. */ 73 static uint64_t max_cfg_size = PCI_CONF_HDR_SIZE; 74 75 static uint64_t pcitool_swap_endian(uint64_t data, int size); 76 static int pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 77 boolean_t write_flag); 78 static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 79 boolean_t write_flag); 80 static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, 81 boolean_t write_flag); 82 static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, 83 uint64_t virt_addr, boolean_t write_flag); 84 static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages); 85 static void pcitool_unmap(uint64_t virt_addr, size_t num_pages); 86 87 /* Extern decalrations */ 88 extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 89 psm_intr_op_t, int *); 90 91 int 92 pcitool_init(dev_info_t *dip, boolean_t is_pciex) 93 { 94 int instance = ddi_get_instance(dip); 95 96 /* Create pcitool nodes for register access and interrupt routing. */ 97 98 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 99 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 100 DDI_NT_REGACC, 0) != DDI_SUCCESS) { 101 return (DDI_FAILURE); 102 } 103 104 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 105 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 106 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 107 ddi_remove_minor_node(dip, PCI_MINOR_REG); 108 return (DDI_FAILURE); 109 } 110 111 if (is_pciex) 112 max_cfg_size = PCIE_CONF_HDR_SIZE; 113 114 return (DDI_SUCCESS); 115 } 116 117 void 118 pcitool_uninit(dev_info_t *dip) 119 { 120 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 121 ddi_remove_minor_node(dip, PCI_MINOR_REG); 122 } 123 124 125 /* Return the number of interrupts on a pci bus. */ 126 static int 127 pcitool_intr_get_max_ino(uint32_t *arg, int mode) 128 { 129 uint32_t num_intr = APIC_MAX_VECTOR; 130 131 if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) != 132 DDI_SUCCESS) 133 return (EFAULT); 134 else 135 return (SUCCESS); 136 } 137 138 139 /*ARGSUSED*/ 140 static int 141 pcitool_set_intr(dev_info_t *dip, void *arg, int mode) 142 { 143 ddi_intr_handle_impl_t info_hdl; 144 pcitool_intr_set_t iset; 145 uint32_t old_cpu; 146 int ret, result; 147 int rval = SUCCESS; 148 149 if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != 150 DDI_SUCCESS) 151 return (EFAULT); 152 153 if (iset.ino > APIC_MAX_VECTOR) { 154 rval = EINVAL; 155 iset.status = PCITOOL_INVALID_INO; 156 goto done_set_intr; 157 } 158 159 iset.status = PCITOOL_SUCCESS; 160 161 if ((old_cpu = pci_get_cpu_from_vecirq(iset.ino, IS_VEC)) == -1) { 162 iset.status = PCITOOL_IO_ERROR; 163 rval = EINVAL; 164 goto done_set_intr; 165 } 166 167 old_cpu &= ~PSMGI_CPU_USER_BOUND; 168 169 /* 170 * For this locally-declared and used handle, ih_private will contain a 171 * CPU value, not an ihdl_plat_t as used for global interrupt handling. 172 */ 173 info_hdl.ih_vector = iset.ino; 174 info_hdl.ih_private = (void *)(uintptr_t)iset.cpu_id; 175 ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_SET_CPU, &result); 176 177 iset.drvr_version = PCITOOL_DRVR_VERSION; 178 if (ret != PSM_SUCCESS) { 179 switch (result) { 180 case EIO: /* Error making the change */ 181 rval = EIO; 182 iset.status = PCITOOL_IO_ERROR; 183 break; 184 case ENXIO: /* Couldn't convert vector to irq */ 185 rval = EINVAL; 186 iset.status = PCITOOL_INVALID_INO; 187 break; 188 case EINVAL: /* CPU out of range */ 189 rval = EINVAL; 190 iset.status = PCITOOL_INVALID_CPUID; 191 break; 192 } 193 } 194 195 /* Return original CPU. */ 196 iset.cpu_id = old_cpu; 197 198 done_set_intr: 199 if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != 200 DDI_SUCCESS) 201 rval = EFAULT; 202 return (rval); 203 } 204 205 206 /* It is assumed that dip != NULL */ 207 static void 208 pcitool_get_intr_dev_info(dev_info_t *dip, pcitool_intr_dev_t *devs) 209 { 210 (void) strncpy(devs->driver_name, 211 ddi_driver_name(dip), MAXMODCONFNAME-1); 212 devs->driver_name[MAXMODCONFNAME] = '\0'; 213 (void) ddi_pathname(dip, devs->path); 214 devs->dev_inst = ddi_get_instance(dip); 215 } 216 217 218 /*ARGSUSED*/ 219 static int 220 pcitool_get_intr(dev_info_t *dip, void *arg, int mode) 221 { 222 /* Array part isn't used here, but oh well... */ 223 pcitool_intr_get_t partial_iget; 224 pcitool_intr_get_t *iget = &partial_iget; 225 size_t iget_kmem_alloc_size = 0; 226 uint8_t num_devs_ret; 227 int copyout_rval; 228 int rval = SUCCESS; 229 int circ; 230 int i; 231 232 ddi_intr_handle_impl_t info_hdl; 233 apic_get_intr_t intr_info; 234 235 /* Read in just the header part, no array section. */ 236 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 237 DDI_SUCCESS) 238 return (EFAULT); 239 240 /* Validate argument. */ 241 if (partial_iget.ino > APIC_MAX_VECTOR) { 242 partial_iget.status = PCITOOL_INVALID_INO; 243 partial_iget.num_devs_ret = 0; 244 rval = EINVAL; 245 goto done_get_intr; 246 } 247 248 num_devs_ret = partial_iget.num_devs_ret; 249 intr_info.avgi_dip_list = NULL; 250 intr_info.avgi_req_flags = 251 PSMGI_REQ_CPUID | PSMGI_REQ_NUM_DEVS | PSMGI_INTRBY_VEC; 252 /* 253 * For this locally-declared and used handle, ih_private will contain a 254 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 255 * global interrupt handling. 256 */ 257 info_hdl.ih_private = &intr_info; 258 info_hdl.ih_vector = partial_iget.ino; 259 260 /* Caller wants device information returned. */ 261 if (num_devs_ret > 0) { 262 263 intr_info.avgi_req_flags |= PSMGI_REQ_GET_DEVS; 264 265 /* 266 * Allocate room. 267 * If num_devs_ret == 0 iget remains pointing to partial_iget. 268 */ 269 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 270 iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 271 272 /* Read in whole structure to verify there's room. */ 273 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 274 SUCCESS) { 275 276 /* Be consistent and just return EFAULT here. */ 277 kmem_free(iget, iget_kmem_alloc_size); 278 279 return (EFAULT); 280 } 281 } 282 283 bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 284 iget->ino = info_hdl.ih_vector; 285 286 /* 287 * Lock device tree branch from the pci root nexus on down if info will 288 * be extracted from dips returned from the tree. 289 */ 290 if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) { 291 ndi_devi_enter(dip, &circ); 292 } 293 294 /* Call psm_intr_ops(PSM_INTR_OP_GET_INTR) to get information. */ 295 if ((rval = (*psm_intr_ops)(NULL, &info_hdl, 296 PSM_INTR_OP_GET_INTR, NULL)) != PSM_SUCCESS) { 297 iget->status = PCITOOL_IO_ERROR; 298 iget->num_devs_ret = 0; 299 rval = EINVAL; 300 goto done_get_intr; 301 } 302 303 /* 304 * Fill in the pcitool_intr_get_t to be returned, 305 * with the CPU, num_devs_ret and num_devs. 306 */ 307 iget->cpu_id = intr_info.avgi_cpu_id & ~PSMGI_CPU_USER_BOUND; 308 309 /* Number of devices returned by apic. */ 310 iget->num_devs = intr_info.avgi_num_devs; 311 312 /* Device info was returned. */ 313 if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) { 314 315 /* 316 * num devs returned is num devs ret by apic, 317 * space permitting. 318 */ 319 iget->num_devs_ret = min(num_devs_ret, intr_info.avgi_num_devs); 320 321 /* 322 * Loop thru list of dips and extract driver, name and instance. 323 * Fill in the pcitool_intr_dev_t's with this info. 324 */ 325 for (i = 0; i < iget->num_devs_ret; i++) 326 pcitool_get_intr_dev_info(intr_info.avgi_dip_list[i], 327 &iget->dev[i]); 328 329 /* Free kmem_alloc'ed memory of the apic_get_intr_t */ 330 kmem_free(intr_info.avgi_dip_list, 331 intr_info.avgi_num_devs * sizeof (dev_info_t *)); 332 } 333 334 done_get_intr: 335 336 if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) { 337 ndi_devi_exit(dip, circ); 338 } 339 340 iget->drvr_version = PCITOOL_DRVR_VERSION; 341 copyout_rval = ddi_copyout(iget, arg, 342 PCITOOL_IGET_SIZE(num_devs_ret), mode); 343 344 if (iget_kmem_alloc_size > 0) 345 kmem_free(iget, iget_kmem_alloc_size); 346 347 if (copyout_rval != DDI_SUCCESS) 348 rval = EFAULT; 349 350 return (rval); 351 } 352 353 354 /* 355 * Main function for handling interrupt CPU binding requests and queries. 356 * Need to implement later 357 */ 358 /*ARGSUSED*/ 359 int 360 pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode) 361 { 362 int rval; 363 364 switch (cmd) { 365 366 /* Associate a new CPU with a given vector */ 367 case PCITOOL_DEVICE_SET_INTR: 368 rval = pcitool_set_intr(dip, arg, mode); 369 break; 370 371 case PCITOOL_DEVICE_GET_INTR: 372 rval = pcitool_get_intr(dip, arg, mode); 373 break; 374 375 case PCITOOL_DEVICE_NUM_INTR: 376 rval = pcitool_intr_get_max_ino(arg, mode); 377 break; 378 379 default: 380 rval = ENOTSUP; 381 } 382 383 return (rval); 384 } 385 386 387 /* 388 * A note about ontrap handling: 389 * 390 * X86 systems on which this module was tested return FFs instead of bus errors 391 * when accessing devices with invalid addresses. Ontrap handling, which 392 * gracefully handles kernel bus errors, is installed anyway, in case future 393 * X86 platforms require it. 394 */ 395 396 /* 397 * Perform register accesses on the nexus device itself. 398 * No explicit PCI nexus device for X86, so not applicable. 399 */ 400 401 /*ARGSUSED*/ 402 int 403 pcitool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 404 { 405 return (ENOTSUP); 406 } 407 408 /* Swap endianness. */ 409 static uint64_t 410 pcitool_swap_endian(uint64_t data, int size) 411 { 412 typedef union { 413 uint64_t data64; 414 uint8_t data8[8]; 415 } data_split_t; 416 417 data_split_t orig_data; 418 data_split_t returned_data; 419 int i; 420 421 orig_data.data64 = data; 422 returned_data.data64 = 0; 423 424 for (i = 0; i < size; i++) { 425 returned_data.data8[i] = orig_data.data8[size - 1 - i]; 426 } 427 428 return (returned_data.data64); 429 } 430 431 432 /* 433 * Access device. prg is modified. 434 * 435 * Extended config space is available only through memory-mapped access. 436 * Standard config space on pci express devices is available either way, 437 * so do it memory-mapped here too, for simplicity. 438 */ 439 /*ARGSUSED*/ 440 static int 441 pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 442 boolean_t write_flag) 443 { 444 int rval = SUCCESS; 445 uint64_t virt_addr; 446 size_t num_virt_pages; 447 448 prg->status = PCITOOL_SUCCESS; 449 450 prg->phys_addr = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0, 451 "ecfga-base-address", 0); 452 if (prg->phys_addr == 0) { 453 prg->status = PCITOOL_IO_ERROR; 454 return (EIO); 455 } 456 457 prg->phys_addr += prg->offset + 458 ((prg->bus_no << PCIEX_REG_BUS_SHIFT) | 459 (prg->dev_no << PCIEX_REG_DEV_SHIFT) | 460 (prg->func_no << PCIEX_REG_FUNC_SHIFT)); 461 462 virt_addr = pcitool_map(prg->phys_addr, 463 PCITOOL_ACC_ATTR_SIZE(prg->acc_attr), &num_virt_pages); 464 if (virt_addr == NULL) { 465 prg->status = PCITOOL_IO_ERROR; 466 return (EIO); 467 } 468 469 rval = pcitool_mem_access(dip, prg, virt_addr, write_flag); 470 pcitool_unmap(virt_addr, num_virt_pages); 471 return (rval); 472 } 473 474 /* Access device. prg is modified. */ 475 /*ARGSUSED*/ 476 static int 477 pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 478 { 479 int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 480 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 481 int rval = SUCCESS; 482 uint64_t local_data; 483 484 /* 485 * NOTE: there is no way to verify whether or not the address is valid. 486 * The put functions return void and the get functions return ff on 487 * error. 488 */ 489 prg->status = PCITOOL_SUCCESS; 490 491 if (write_flag) { 492 493 if (big_endian) { 494 local_data = pcitool_swap_endian(prg->data, size); 495 } else { 496 local_data = prg->data; 497 } 498 499 switch (size) { 500 case 1: 501 (*pci_putb_func)(prg->bus_no, prg->dev_no, 502 prg->func_no, prg->offset, local_data); 503 break; 504 case 2: 505 (*pci_putw_func)(prg->bus_no, prg->dev_no, 506 prg->func_no, prg->offset, local_data); 507 break; 508 case 4: 509 (*pci_putl_func)(prg->bus_no, prg->dev_no, 510 prg->func_no, prg->offset, local_data); 511 break; 512 default: 513 rval = ENOTSUP; 514 prg->status = PCITOOL_INVALID_SIZE; 515 break; 516 } 517 } else { 518 switch (size) { 519 case 1: 520 local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no, 521 prg->func_no, prg->offset); 522 break; 523 case 2: 524 local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no, 525 prg->func_no, prg->offset); 526 break; 527 case 4: 528 local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no, 529 prg->func_no, prg->offset); 530 break; 531 default: 532 rval = ENOTSUP; 533 prg->status = PCITOOL_INVALID_SIZE; 534 break; 535 } 536 537 if (rval == SUCCESS) { 538 if (big_endian) { 539 prg->data = 540 pcitool_swap_endian(local_data, size); 541 } else { 542 prg->data = local_data; 543 } 544 } 545 } 546 prg->phys_addr = 0; /* Config space is not memory mapped on X86. */ 547 return (rval); 548 } 549 550 551 /*ARGSUSED*/ 552 static int 553 pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 554 { 555 int port = (int)prg->phys_addr; 556 size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 557 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 558 int rval = SUCCESS; 559 on_trap_data_t otd; 560 uint64_t local_data; 561 562 563 /* 564 * on_trap works like setjmp. 565 * 566 * A non-zero return here means on_trap has returned from an error. 567 * 568 * A zero return here means that on_trap has just returned from setup. 569 */ 570 if (on_trap(&otd, OT_DATA_ACCESS)) { 571 no_trap(); 572 if (pcitool_debug) 573 prom_printf( 574 "pcitool_mem_access: on_trap caught an error...\n"); 575 prg->status = PCITOOL_INVALID_ADDRESS; 576 return (EFAULT); 577 } 578 579 if (write_flag) { 580 581 if (big_endian) { 582 local_data = pcitool_swap_endian(prg->data, size); 583 } else { 584 local_data = prg->data; 585 } 586 587 if (pcitool_debug) 588 prom_printf("Writing %ld byte(s) to port 0x%x\n", 589 size, port); 590 591 switch (size) { 592 case 1: 593 outb(port, (uint8_t)local_data); 594 break; 595 case 2: 596 outw(port, (uint16_t)local_data); 597 break; 598 case 4: 599 outl(port, (uint32_t)local_data); 600 break; 601 default: 602 rval = ENOTSUP; 603 prg->status = PCITOOL_INVALID_SIZE; 604 break; 605 } 606 } else { 607 if (pcitool_debug) 608 prom_printf("Reading %ld byte(s) from port 0x%x\n", 609 size, port); 610 611 switch (size) { 612 case 1: 613 local_data = inb(port); 614 break; 615 case 2: 616 local_data = inw(port); 617 break; 618 case 4: 619 local_data = inl(port); 620 break; 621 default: 622 rval = ENOTSUP; 623 prg->status = PCITOOL_INVALID_SIZE; 624 break; 625 } 626 627 if (rval == SUCCESS) { 628 if (big_endian) { 629 prg->data = 630 pcitool_swap_endian(local_data, size); 631 } else { 632 prg->data = local_data; 633 } 634 } 635 } 636 637 no_trap(); 638 return (rval); 639 } 640 641 /*ARGSUSED*/ 642 static int 643 pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr, 644 boolean_t write_flag) 645 { 646 size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 647 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 648 int rval = DDI_SUCCESS; 649 on_trap_data_t otd; 650 uint64_t local_data; 651 652 /* 653 * on_trap works like setjmp. 654 * 655 * A non-zero return here means on_trap has returned from an error. 656 * 657 * A zero return here means that on_trap has just returned from setup. 658 */ 659 if (on_trap(&otd, OT_DATA_ACCESS)) { 660 no_trap(); 661 if (pcitool_debug) 662 prom_printf( 663 "pcitool_mem_access: on_trap caught an error...\n"); 664 prg->status = PCITOOL_INVALID_ADDRESS; 665 return (EFAULT); 666 } 667 668 if (write_flag) { 669 670 if (big_endian) { 671 local_data = pcitool_swap_endian(prg->data, size); 672 } else { 673 local_data = prg->data; 674 } 675 676 switch (size) { 677 case 1: 678 *((uint8_t *)(uintptr_t)virt_addr) = local_data; 679 break; 680 case 2: 681 *((uint16_t *)(uintptr_t)virt_addr) = local_data; 682 break; 683 case 4: 684 *((uint32_t *)(uintptr_t)virt_addr) = local_data; 685 break; 686 case 8: 687 *((uint64_t *)(uintptr_t)virt_addr) = local_data; 688 break; 689 default: 690 rval = ENOTSUP; 691 prg->status = PCITOOL_INVALID_SIZE; 692 break; 693 } 694 } else { 695 switch (size) { 696 case 1: 697 local_data = *((uint8_t *)(uintptr_t)virt_addr); 698 break; 699 case 2: 700 local_data = *((uint16_t *)(uintptr_t)virt_addr); 701 break; 702 case 4: 703 local_data = *((uint32_t *)(uintptr_t)virt_addr); 704 break; 705 case 8: 706 local_data = *((uint64_t *)(uintptr_t)virt_addr); 707 break; 708 default: 709 rval = ENOTSUP; 710 prg->status = PCITOOL_INVALID_SIZE; 711 break; 712 } 713 714 if (rval == SUCCESS) { 715 if (big_endian) { 716 prg->data = 717 pcitool_swap_endian(local_data, size); 718 } else { 719 prg->data = local_data; 720 } 721 } 722 } 723 724 no_trap(); 725 return (rval); 726 } 727 728 /* 729 * Map up to 2 pages which contain the address we want to access. 730 * 731 * Mapping should span no more than 8 bytes. With X86 it is possible for an 732 * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary. 733 * We'll never have to map more than two pages. 734 */ 735 736 static uint64_t 737 pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages) 738 { 739 740 uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET; 741 uint64_t offset = phys_addr & MMU_PAGEOFFSET; 742 void *virt_base; 743 uint64_t returned_addr; 744 745 if (pcitool_debug) 746 prom_printf("pcitool_map: Called with PA:0x%p\n", 747 (uint8_t *)(uintptr_t)phys_addr); 748 749 *num_pages = 1; 750 751 /* Desired mapping would span more than two pages. */ 752 if ((offset + size) > (MMU_PAGESIZE * 2)) { 753 if (pcitool_debug) 754 prom_printf("boundary violation: " 755 "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%lx\n", 756 offset, (uintptr_t)size, (uintptr_t)MMU_PAGESIZE); 757 return (NULL); 758 759 } else if ((offset + size) > MMU_PAGESIZE) { 760 (*num_pages)++; 761 } 762 763 /* Get page(s) of virtual space. */ 764 virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP); 765 if (virt_base == NULL) { 766 if (pcitool_debug) 767 prom_printf("Couldn't get virtual base address.\n"); 768 return (NULL); 769 } 770 771 if (pcitool_debug) 772 prom_printf("Got base virtual address:0x%p\n", virt_base); 773 774 /* Now map the allocated virtual space to the physical address. */ 775 hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages), 776 mmu_btop(page_base), PROT_READ | PROT_WRITE | HAT_STRICTORDER, 777 HAT_LOAD_LOCK); 778 779 returned_addr = ((uintptr_t)(virt_base)) + offset; 780 781 if (pcitool_debug) 782 prom_printf("pcitool_map: returning VA:0x%p\n", 783 (void *)(uintptr_t)returned_addr); 784 785 return (returned_addr); 786 } 787 788 /* Unmap the mapped page(s). */ 789 static void 790 pcitool_unmap(uint64_t virt_addr, size_t num_pages) 791 { 792 void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET); 793 794 hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages), 795 HAT_UNLOAD_UNLOCK); 796 vmem_free(heap_arena, base_virt_addr, ptob(num_pages)); 797 } 798 799 800 /* Perform register accesses on PCI leaf devices. */ 801 int 802 pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 803 { 804 boolean_t write_flag = B_FALSE; 805 int rval = 0; 806 pcitool_reg_t prg; 807 uint8_t size; 808 809 uint64_t base_addr; 810 uint64_t virt_addr; 811 size_t num_virt_pages; 812 813 switch (cmd) { 814 case (PCITOOL_DEVICE_SET_REG): 815 write_flag = B_TRUE; 816 817 /*FALLTHRU*/ 818 case (PCITOOL_DEVICE_GET_REG): 819 if (pcitool_debug) 820 prom_printf("pci_dev_reg_ops set/get reg\n"); 821 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 822 DDI_SUCCESS) { 823 if (pcitool_debug) 824 prom_printf("Error reading arguments\n"); 825 return (EFAULT); 826 } 827 828 if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 829 prg.status = PCITOOL_OUT_OF_RANGE; 830 rval = EINVAL; 831 goto done_reg; 832 } 833 834 if (pcitool_debug) 835 prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n", 836 prg.bus_no, prg.dev_no, prg.func_no); 837 /* Validate address arguments of bus / dev / func */ 838 if (((prg.bus_no & 839 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != 840 prg.bus_no) || 841 ((prg.dev_no & 842 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != 843 prg.dev_no) || 844 ((prg.func_no & 845 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != 846 prg.func_no)) { 847 prg.status = PCITOOL_INVALID_ADDRESS; 848 rval = EINVAL; 849 goto done_reg; 850 } 851 852 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 853 854 /* Proper config space desired. */ 855 if (prg.barnum == 0) { 856 857 if (pcitool_debug) 858 prom_printf( 859 "config access: offset:0x%" PRIx64 ", " 860 "phys_addr:0x%" PRIx64 "\n", 861 prg.offset, prg.phys_addr); 862 863 if (prg.offset >= max_cfg_size) { 864 prg.status = PCITOOL_OUT_OF_RANGE; 865 rval = EINVAL; 866 goto done_reg; 867 } 868 869 /* 870 * Access device. prg is modified. 871 * First, check for AMD northbridges for I/O access 872 * (This fix will move in future to pcitool user-land) 873 * Next, check for PCIe devices and do 874 * memory-mapped access 875 * Lastly, check for PCI devices and do I/O access 876 */ 877 if (prg.bus_no == 0 && prg.dev_no == 0x18) { 878 if (cpuid_getvendor(CPU) == X86_VENDOR_AMD) 879 rval = pcitool_cfg_access(dip, &prg, 880 write_flag); 881 } else if (max_cfg_size == PCIE_CONF_HDR_SIZE) 882 rval = pcitool_pciex_cfg_access(dip, &prg, 883 write_flag); 884 else 885 rval = pcitool_cfg_access(dip, &prg, 886 write_flag); 887 888 if (pcitool_debug) 889 prom_printf( 890 "config access: data:0x%" PRIx64 "\n", 891 prg.data); 892 893 /* IO/ MEM/ MEM64 space. */ 894 } else { 895 896 pcitool_reg_t prg2; 897 bcopy(&prg, &prg2, sizeof (pcitool_reg_t)); 898 899 /* 900 * Translate BAR number into offset of the BAR in 901 * the device's config space. 902 */ 903 prg2.offset = pci_bars[prg2.barnum]; 904 prg2.acc_attr = 905 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 906 907 if (pcitool_debug) 908 prom_printf( 909 "barnum:%d, bar_offset:0x%" PRIx64 "\n", 910 prg2.barnum, prg2.offset); 911 /* 912 * Get Bus Address Register (BAR) from config space. 913 * prg2.offset is the offset into config space of the 914 * BAR desired. prg.status is modified on error. 915 */ 916 rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 917 if (rval != SUCCESS) { 918 if (pcitool_debug) 919 prom_printf("BAR access failed\n"); 920 prg.status = prg2.status; 921 goto done_reg; 922 } 923 /* 924 * Reference proper PCI space based on the BAR. 925 * If 64 bit MEM space, need to load other half of the 926 * BAR first. 927 */ 928 929 if (pcitool_debug) 930 prom_printf("bar returned is 0x%" PRIx64 "\n", 931 prg2.data); 932 if (!prg2.data) { 933 if (pcitool_debug) 934 prom_printf("BAR data == 0\n"); 935 rval = EINVAL; 936 prg.status = PCITOOL_INVALID_ADDRESS; 937 goto done_reg; 938 } 939 if (prg2.data == 0xffffffff) { 940 if (pcitool_debug) 941 prom_printf("BAR data == -1\n"); 942 rval = EINVAL; 943 prg.status = PCITOOL_INVALID_ADDRESS; 944 goto done_reg; 945 } 946 947 /* 948 * BAR has bits saying this space is IO space, unless 949 * this is the ROM address register. 950 */ 951 if (((PCI_BASE_SPACE_M & prg2.data) == 952 PCI_BASE_SPACE_IO) && 953 (prg2.offset != PCI_CONF_ROM)) { 954 if (pcitool_debug) 955 prom_printf("IO space\n"); 956 957 prg2.data &= PCI_BASE_IO_ADDR_M; 958 prg.phys_addr = prg2.data + prg.offset; 959 960 rval = pcitool_io_access(dip, &prg, write_flag); 961 if ((rval != SUCCESS) && (pcitool_debug)) 962 prom_printf("IO access failed\n"); 963 964 goto done_reg; 965 966 967 /* 968 * BAR has bits saying this space is 64 bit memory 969 * space, unless this is the ROM address register. 970 * 971 * The 64 bit address stored in two BAR cells is not 972 * necessarily aligned on an 8-byte boundary. 973 * Need to keep the first 4 bytes read, 974 * and do a separate read of the high 4 bytes. 975 */ 976 977 } else if ((PCI_BASE_TYPE_ALL & prg2.data) && 978 (prg2.offset != PCI_CONF_ROM)) { 979 980 uint32_t low_bytes = 981 (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL); 982 983 /* 984 * Don't try to read the next 4 bytes 985 * past the end of BARs. 986 */ 987 if (prg2.offset >= PCI_CONF_BASE5) { 988 prg.status = PCITOOL_OUT_OF_RANGE; 989 rval = EIO; 990 goto done_reg; 991 } 992 993 /* 994 * Access device. 995 * prg2.status is modified on error. 996 */ 997 prg2.offset += 4; 998 rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 999 if (rval != SUCCESS) { 1000 prg.status = prg2.status; 1001 goto done_reg; 1002 } 1003 1004 if (prg2.data == 0xffffffff) { 1005 prg.status = PCITOOL_INVALID_ADDRESS; 1006 prg.status = EFAULT; 1007 goto done_reg; 1008 } 1009 1010 prg2.data = (prg2.data << 32) + low_bytes; 1011 if (pcitool_debug) 1012 prom_printf( 1013 "64 bit mem space. " 1014 "64-bit bar is 0x%" PRIx64 "\n", 1015 prg2.data); 1016 1017 /* Mem32 space, including ROM */ 1018 } else { 1019 1020 if (prg2.offset == PCI_CONF_ROM) { 1021 if (pcitool_debug) 1022 prom_printf( 1023 "Additional ROM " 1024 "checking\n"); 1025 /* Can't write to ROM */ 1026 if (write_flag) { 1027 prg.status = PCITOOL_ROM_WRITE; 1028 rval = EIO; 1029 goto done_reg; 1030 1031 /* ROM disabled for reading */ 1032 } else if (!(prg2.data & 0x00000001)) { 1033 prg.status = 1034 PCITOOL_ROM_DISABLED; 1035 rval = EIO; 1036 goto done_reg; 1037 } 1038 } 1039 1040 if (pcitool_debug) 1041 prom_printf("32 bit mem space\n"); 1042 } 1043 1044 /* Common code for all IO/MEM range spaces. */ 1045 1046 base_addr = prg2.data; 1047 if (pcitool_debug) 1048 prom_printf( 1049 "addr portion of bar is 0x%" PRIx64 ", " 1050 "base=0x%" PRIx64 ", " 1051 "offset:0x%" PRIx64 "\n", 1052 prg2.data, base_addr, prg.offset); 1053 /* 1054 * Use offset provided by caller to index into 1055 * desired space, then access. 1056 * Note that prg.status is modified on error. 1057 */ 1058 prg.phys_addr = base_addr + prg.offset; 1059 1060 virt_addr = pcitool_map(prg.phys_addr, size, 1061 &num_virt_pages); 1062 if (virt_addr == NULL) { 1063 prg.status = PCITOOL_IO_ERROR; 1064 rval = EIO; 1065 goto done_reg; 1066 } 1067 1068 rval = pcitool_mem_access(dip, &prg, virt_addr, 1069 write_flag); 1070 pcitool_unmap(virt_addr, num_virt_pages); 1071 } 1072 done_reg: 1073 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 1074 DDI_SUCCESS) { 1075 if (pcitool_debug) 1076 prom_printf("Error returning arguments.\n"); 1077 rval = EFAULT; 1078 } 1079 break; 1080 default: 1081 rval = ENOTTY; 1082 break; 1083 } 1084 return (rval); 1085 } 1086