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