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