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