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