1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/mkdev.h> 31 #include <sys/stat.h> 32 #include <sys/sunddi.h> 33 #include <vm/seg_kmem.h> 34 #include <sys/machparam.h> 35 #include <sys/ontrap.h> 36 #include <sys/pcie.h> 37 #include <sys/hotplug/pci/pcihp.h> 38 #include <sys/pci_cfgspace.h> 39 #include <sys/pci_tools.h> 40 #include "pci_tools_ext.h" 41 #include "pci_var.h" 42 #include <sys/promif.h> 43 44 #define PCIEX_BDF_OFFSET_DELTA 4 45 #define PCIEX_REG_FUNC_SHIFT (PCI_REG_FUNC_SHIFT + PCIEX_BDF_OFFSET_DELTA) 46 #define PCIEX_REG_DEV_SHIFT (PCI_REG_DEV_SHIFT + PCIEX_BDF_OFFSET_DELTA) 47 #define PCIEX_REG_BUS_SHIFT (PCI_REG_BUS_SHIFT + PCIEX_BDF_OFFSET_DELTA) 48 49 #define SUCCESS 0 50 51 int pcitool_debug = 0; 52 53 /* 54 * Offsets of BARS in config space. First entry of 0 means config space. 55 * Entries here correlate to pcitool_bars_t enumerated type. 56 */ 57 static uint8_t pci_bars[] = { 58 0x0, 59 PCI_CONF_BASE0, 60 PCI_CONF_BASE1, 61 PCI_CONF_BASE2, 62 PCI_CONF_BASE3, 63 PCI_CONF_BASE4, 64 PCI_CONF_BASE5, 65 PCI_CONF_ROM 66 }; 67 68 /* Max offset allowed into config space for a particular device. */ 69 static uint64_t max_cfg_size = PCI_CONF_HDR_SIZE; 70 71 static uint64_t pcitool_swap_endian(uint64_t data, int size); 72 static int pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 73 boolean_t write_flag); 74 static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 75 boolean_t write_flag); 76 static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, 77 boolean_t write_flag); 78 static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, 79 uint64_t virt_addr, boolean_t write_flag); 80 static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages); 81 static void pcitool_unmap(uint64_t virt_addr, size_t num_pages); 82 83 int 84 pcitool_init(dev_info_t *dip, boolean_t is_pciex) 85 { 86 int instance = ddi_get_instance(dip); 87 88 /* Create pcitool nodes for register access and interrupt routing. */ 89 90 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 91 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 92 DDI_NT_REGACC, 0) != DDI_SUCCESS) { 93 return (DDI_FAILURE); 94 } 95 96 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 97 PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 98 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 99 ddi_remove_minor_node(dip, PCI_MINOR_REG); 100 return (DDI_FAILURE); 101 } 102 103 if (is_pciex) 104 max_cfg_size = PCIE_CONF_HDR_SIZE; 105 106 return (DDI_SUCCESS); 107 } 108 109 void 110 pcitool_uninit(dev_info_t *dip) 111 { 112 ddi_remove_minor_node(dip, PCI_MINOR_INTR); 113 ddi_remove_minor_node(dip, PCI_MINOR_REG); 114 } 115 116 117 /* 118 * A note about ontrap handling: 119 * 120 * X86 systems on which this module was tested return FFs instead of bus errors 121 * when accessing devices with invalid addresses. Ontrap handling, which 122 * gracefully handles kernel bus errors, is installed anyway, in case future 123 * X86 platforms require it. 124 */ 125 126 /* 127 * Main function for handling interrupt CPU binding requests and queries. 128 * Need to implement later 129 */ 130 /*ARGSUSED*/ 131 int 132 pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode) 133 { 134 return (ENOTSUP); 135 } 136 137 138 /* 139 * Perform register accesses on the nexus device itself. 140 * No explicit PCI nexus device for X86, so not applicable. 141 */ 142 /*ARGSUSED*/ 143 int 144 pcitool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 145 { 146 return (ENOTSUP); 147 } 148 149 /* Swap endianness. */ 150 static uint64_t 151 pcitool_swap_endian(uint64_t data, int size) 152 { 153 typedef union { 154 uint64_t data64; 155 uint8_t data8[8]; 156 } data_split_t; 157 158 data_split_t orig_data; 159 data_split_t returned_data; 160 int i; 161 162 orig_data.data64 = data; 163 returned_data.data64 = 0; 164 165 for (i = 0; i < size; i++) { 166 returned_data.data8[i] = orig_data.data8[size - 1 - i]; 167 } 168 169 return (returned_data.data64); 170 } 171 172 173 /* 174 * Access device. prg is modified. 175 * 176 * Extended config space is available only through memory-mapped access. 177 * Standard config space on pci express devices is available either way, 178 * so do it memory-mapped here too, for simplicity. 179 */ 180 /*ARGSUSED*/ 181 static int 182 pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 183 boolean_t write_flag) 184 { 185 int rval = SUCCESS; 186 uint64_t virt_addr; 187 size_t num_virt_pages; 188 189 prg->status = PCITOOL_SUCCESS; 190 191 prg->phys_addr = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0, 192 "ecfga-base-address", 0); 193 if (prg->phys_addr == 0) { 194 prg->status = PCITOOL_IO_ERROR; 195 return (EIO); 196 } 197 198 prg->phys_addr += prg->offset + 199 ((prg->bus_no << PCIEX_REG_BUS_SHIFT) | 200 (prg->dev_no << PCIEX_REG_DEV_SHIFT) | 201 (prg->func_no << PCIEX_REG_FUNC_SHIFT)); 202 203 virt_addr = pcitool_map(prg->phys_addr, 204 PCITOOL_ACC_ATTR_SIZE(prg->acc_attr), &num_virt_pages); 205 if (virt_addr == NULL) { 206 prg->status = PCITOOL_IO_ERROR; 207 return (EIO); 208 } 209 210 rval = pcitool_mem_access(dip, prg, virt_addr, write_flag); 211 pcitool_unmap(virt_addr, num_virt_pages); 212 return (rval); 213 } 214 215 /* Access device. prg is modified. */ 216 /*ARGSUSED*/ 217 static int 218 pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 219 { 220 int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 221 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 222 int rval = SUCCESS; 223 uint64_t local_data; 224 225 /* 226 * NOTE: there is no way to verify whether or not the address is valid. 227 * The put functions return void and the get functions return ff on 228 * error. 229 */ 230 prg->status = PCITOOL_SUCCESS; 231 232 if (write_flag) { 233 234 if (big_endian) { 235 local_data = pcitool_swap_endian(prg->data, size); 236 } else { 237 local_data = prg->data; 238 } 239 240 switch (size) { 241 case 1: 242 (*pci_putb_func)(prg->bus_no, prg->dev_no, 243 prg->func_no, prg->offset, local_data); 244 break; 245 case 2: 246 (*pci_putw_func)(prg->bus_no, prg->dev_no, 247 prg->func_no, prg->offset, local_data); 248 break; 249 case 4: 250 (*pci_putl_func)(prg->bus_no, prg->dev_no, 251 prg->func_no, prg->offset, local_data); 252 break; 253 default: 254 rval = ENOTSUP; 255 prg->status = PCITOOL_INVALID_SIZE; 256 break; 257 } 258 } else { 259 switch (size) { 260 case 1: 261 local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no, 262 prg->func_no, prg->offset); 263 break; 264 case 2: 265 local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no, 266 prg->func_no, prg->offset); 267 break; 268 case 4: 269 local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no, 270 prg->func_no, prg->offset); 271 break; 272 default: 273 rval = ENOTSUP; 274 prg->status = PCITOOL_INVALID_SIZE; 275 break; 276 } 277 278 if (rval == SUCCESS) { 279 if (big_endian) { 280 prg->data = 281 pcitool_swap_endian(local_data, size); 282 } else { 283 prg->data = local_data; 284 } 285 } 286 } 287 prg->phys_addr = 0; /* Config space is not memory mapped on X86. */ 288 return (rval); 289 } 290 291 292 /*ARGSUSED*/ 293 static int 294 pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 295 { 296 int port = (int)prg->phys_addr; 297 size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 298 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 299 int rval = SUCCESS; 300 on_trap_data_t otd; 301 uint64_t local_data; 302 303 304 /* 305 * on_trap works like setjmp. 306 * 307 * A non-zero return here means on_trap has returned from an error. 308 * 309 * A zero return here means that on_trap has just returned from setup. 310 */ 311 if (on_trap(&otd, OT_DATA_ACCESS)) { 312 no_trap(); 313 if (pcitool_debug) 314 prom_printf( 315 "pcitool_mem_access: on_trap caught an error...\n"); 316 prg->status = PCITOOL_INVALID_ADDRESS; 317 return (EFAULT); 318 } 319 320 if (write_flag) { 321 322 if (big_endian) { 323 local_data = pcitool_swap_endian(prg->data, size); 324 } else { 325 local_data = prg->data; 326 } 327 328 if (pcitool_debug) 329 prom_printf("Writing %ld byte(s) to port 0x%x\n", 330 size, port); 331 332 switch (size) { 333 case 1: 334 outb(port, (uint8_t)local_data); 335 break; 336 case 2: 337 outw(port, (uint16_t)local_data); 338 break; 339 case 4: 340 outl(port, (uint32_t)local_data); 341 break; 342 default: 343 rval = ENOTSUP; 344 prg->status = PCITOOL_INVALID_SIZE; 345 break; 346 } 347 } else { 348 if (pcitool_debug) 349 prom_printf("Reading %ld byte(s) from port 0x%x\n", 350 size, port); 351 352 switch (size) { 353 case 1: 354 local_data = inb(port); 355 break; 356 case 2: 357 local_data = inw(port); 358 break; 359 case 4: 360 local_data = inl(port); 361 break; 362 default: 363 rval = ENOTSUP; 364 prg->status = PCITOOL_INVALID_SIZE; 365 break; 366 } 367 368 if (rval == SUCCESS) { 369 if (big_endian) { 370 prg->data = 371 pcitool_swap_endian(local_data, size); 372 } else { 373 prg->data = local_data; 374 } 375 } 376 } 377 378 no_trap(); 379 return (rval); 380 } 381 382 /*ARGSUSED*/ 383 static int 384 pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr, 385 boolean_t write_flag) 386 { 387 size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 388 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 389 int rval = DDI_SUCCESS; 390 on_trap_data_t otd; 391 uint64_t local_data; 392 393 /* 394 * on_trap works like setjmp. 395 * 396 * A non-zero return here means on_trap has returned from an error. 397 * 398 * A zero return here means that on_trap has just returned from setup. 399 */ 400 if (on_trap(&otd, OT_DATA_ACCESS)) { 401 no_trap(); 402 if (pcitool_debug) 403 prom_printf( 404 "pcitool_mem_access: on_trap caught an error...\n"); 405 prg->status = PCITOOL_INVALID_ADDRESS; 406 return (EFAULT); 407 } 408 409 if (write_flag) { 410 411 if (big_endian) { 412 local_data = pcitool_swap_endian(prg->data, size); 413 } else { 414 local_data = prg->data; 415 } 416 417 switch (size) { 418 case 1: 419 *((uint8_t *)(uintptr_t)virt_addr) = local_data; 420 break; 421 case 2: 422 *((uint16_t *)(uintptr_t)virt_addr) = local_data; 423 break; 424 case 4: 425 *((uint32_t *)(uintptr_t)virt_addr) = local_data; 426 break; 427 case 8: 428 *((uint64_t *)(uintptr_t)virt_addr) = local_data; 429 break; 430 default: 431 rval = ENOTSUP; 432 prg->status = PCITOOL_INVALID_SIZE; 433 break; 434 } 435 } else { 436 switch (size) { 437 case 1: 438 local_data = *((uint8_t *)(uintptr_t)virt_addr); 439 break; 440 case 2: 441 local_data = *((uint16_t *)(uintptr_t)virt_addr); 442 break; 443 case 4: 444 local_data = *((uint32_t *)(uintptr_t)virt_addr); 445 break; 446 case 8: 447 local_data = *((uint64_t *)(uintptr_t)virt_addr); 448 break; 449 default: 450 rval = ENOTSUP; 451 prg->status = PCITOOL_INVALID_SIZE; 452 break; 453 } 454 455 if (rval == SUCCESS) { 456 if (big_endian) { 457 prg->data = 458 pcitool_swap_endian(local_data, size); 459 } else { 460 prg->data = local_data; 461 } 462 } 463 } 464 465 no_trap(); 466 return (rval); 467 } 468 469 /* 470 * Map up to 2 pages which contain the address we want to access. 471 * 472 * Mapping should span no more than 8 bytes. With X86 it is possible for an 473 * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary. 474 * We'll never have to map more than two pages. 475 */ 476 477 static uint64_t 478 pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages) 479 { 480 481 uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET; 482 uint64_t offset = phys_addr & MMU_PAGEOFFSET; 483 void *virt_base; 484 uint64_t returned_addr; 485 486 if (pcitool_debug) 487 prom_printf("pcitool_map: Called with PA:0x%p\n", 488 (uint8_t *)(uintptr_t)phys_addr); 489 490 *num_pages = 1; 491 492 /* Desired mapping would span more than two pages. */ 493 if ((offset + size) > (MMU_PAGESIZE * 2)) { 494 if (pcitool_debug) 495 prom_printf("boundary violation: " 496 "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%lx\n", 497 offset, (uintptr_t)size, (uintptr_t)MMU_PAGESIZE); 498 return (NULL); 499 500 } else if ((offset + size) > MMU_PAGESIZE) { 501 (*num_pages)++; 502 } 503 504 /* Get page(s) of virtual space. */ 505 virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP); 506 if (virt_base == NULL) { 507 if (pcitool_debug) 508 prom_printf("Couldn't get virtual base address.\n"); 509 return (NULL); 510 } 511 512 if (pcitool_debug) 513 prom_printf("Got base virtual address:0x%p\n", virt_base); 514 515 /* Now map the allocated virtual space to the physical address. */ 516 hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages), 517 mmu_btop(page_base), PROT_READ | PROT_WRITE | HAT_STRICTORDER, 518 HAT_LOAD_LOCK); 519 520 returned_addr = ((uintptr_t)(virt_base)) + offset; 521 522 if (pcitool_debug) 523 prom_printf("pcitool_map: returning VA:0x%p\n", 524 (void *)(uintptr_t)returned_addr); 525 526 return (returned_addr); 527 } 528 529 /* Unmap the mapped page(s). */ 530 static void 531 pcitool_unmap(uint64_t virt_addr, size_t num_pages) 532 { 533 void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET); 534 535 hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages), 536 HAT_UNLOAD_UNLOCK); 537 vmem_free(heap_arena, base_virt_addr, ptob(num_pages)); 538 } 539 540 541 /* Perform register accesses on PCI leaf devices. */ 542 int 543 pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 544 { 545 boolean_t write_flag = B_FALSE; 546 int rval = 0; 547 pcitool_reg_t prg; 548 uint8_t size; 549 550 uint64_t base_addr; 551 uint64_t virt_addr; 552 size_t num_virt_pages; 553 554 switch (cmd) { 555 case (PCITOOL_DEVICE_SET_REG): 556 write_flag = B_TRUE; 557 558 /*FALLTHRU*/ 559 case (PCITOOL_DEVICE_GET_REG): 560 if (pcitool_debug) 561 prom_printf("pci_dev_reg_ops set/get reg\n"); 562 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 563 DDI_SUCCESS) { 564 if (pcitool_debug) 565 prom_printf("Error reading arguments\n"); 566 return (EFAULT); 567 } 568 569 if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 570 prg.status = PCITOOL_OUT_OF_RANGE; 571 rval = EINVAL; 572 goto done_reg; 573 } 574 575 if (pcitool_debug) 576 prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n", 577 prg.bus_no, prg.dev_no, prg.func_no); 578 /* Validate address arguments of bus / dev / func */ 579 if (((prg.bus_no & 580 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != 581 prg.bus_no) || 582 ((prg.dev_no & 583 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != 584 prg.dev_no) || 585 ((prg.func_no & 586 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != 587 prg.func_no)) { 588 prg.status = PCITOOL_INVALID_ADDRESS; 589 rval = EINVAL; 590 goto done_reg; 591 } 592 593 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 594 595 /* Proper config space desired. */ 596 if (prg.barnum == 0) { 597 598 if (pcitool_debug) 599 prom_printf( 600 "config access: offset:0x%" PRIx64 ", " 601 "phys_addr:0x%" PRIx64 "\n", 602 prg.offset, prg.phys_addr); 603 604 if (prg.offset >= max_cfg_size) { 605 prg.status = PCITOOL_OUT_OF_RANGE; 606 rval = EINVAL; 607 goto done_reg; 608 } 609 610 /* Access device. prg is modified. */ 611 if (max_cfg_size == PCIE_CONF_HDR_SIZE) 612 rval = pcitool_pciex_cfg_access(dip, &prg, 613 write_flag); 614 else 615 rval = pcitool_cfg_access(dip, &prg, 616 write_flag); 617 618 if (pcitool_debug) 619 prom_printf( 620 "config access: data:0x%" PRIx64 "\n", 621 prg.data); 622 623 /* IO/ MEM/ MEM64 space. */ 624 } else { 625 626 pcitool_reg_t prg2; 627 bcopy(&prg, &prg2, sizeof (pcitool_reg_t)); 628 629 /* 630 * Translate BAR number into offset of the BAR in 631 * the device's config space. 632 */ 633 prg2.offset = pci_bars[prg2.barnum]; 634 prg2.acc_attr = 635 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 636 637 if (pcitool_debug) 638 prom_printf( 639 "barnum:%d, bar_offset:0x%" PRIx64 "\n", 640 prg2.barnum, prg2.offset); 641 /* 642 * Get Bus Address Register (BAR) from config space. 643 * prg2.offset is the offset into config space of the 644 * BAR desired. prg.status is modified on error. 645 */ 646 rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 647 if (rval != SUCCESS) { 648 if (pcitool_debug) 649 prom_printf("BAR access failed\n"); 650 prg.status = prg2.status; 651 goto done_reg; 652 } 653 /* 654 * Reference proper PCI space based on the BAR. 655 * If 64 bit MEM space, need to load other half of the 656 * BAR first. 657 */ 658 659 if (pcitool_debug) 660 prom_printf("bar returned is 0x%" PRIx64 "\n", 661 prg2.data); 662 if (!prg2.data) { 663 if (pcitool_debug) 664 prom_printf("BAR data == 0\n"); 665 rval = EINVAL; 666 prg.status = PCITOOL_INVALID_ADDRESS; 667 goto done_reg; 668 } 669 if (prg2.data == 0xffffffff) { 670 if (pcitool_debug) 671 prom_printf("BAR data == -1\n"); 672 rval = EINVAL; 673 prg.status = PCITOOL_INVALID_ADDRESS; 674 goto done_reg; 675 } 676 677 /* 678 * BAR has bits saying this space is IO space, unless 679 * this is the ROM address register. 680 */ 681 if (((PCI_BASE_SPACE_M & prg2.data) == 682 PCI_BASE_SPACE_IO) && 683 (prg2.offset != PCI_CONF_ROM)) { 684 if (pcitool_debug) 685 prom_printf("IO space\n"); 686 687 prg2.data &= PCI_BASE_IO_ADDR_M; 688 prg.phys_addr = prg2.data + prg.offset; 689 690 rval = pcitool_io_access(dip, &prg, write_flag); 691 if ((rval != SUCCESS) && (pcitool_debug)) 692 prom_printf("IO access failed\n"); 693 694 goto done_reg; 695 696 697 /* 698 * BAR has bits saying this space is 64 bit memory 699 * space, unless this is the ROM address register. 700 * 701 * The 64 bit address stored in two BAR cells is not 702 * necessarily aligned on an 8-byte boundary. 703 * Need to keep the first 4 bytes read, 704 * and do a separate read of the high 4 bytes. 705 */ 706 707 } else if ((PCI_BASE_TYPE_ALL & prg2.data) && 708 (prg2.offset != PCI_CONF_ROM)) { 709 710 uint32_t low_bytes = 711 (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL); 712 713 /* 714 * Don't try to read the next 4 bytes 715 * past the end of BARs. 716 */ 717 if (prg2.offset >= PCI_CONF_BASE5) { 718 prg.status = PCITOOL_OUT_OF_RANGE; 719 rval = EIO; 720 goto done_reg; 721 } 722 723 /* 724 * Access device. 725 * prg2.status is modified on error. 726 */ 727 prg2.offset += 4; 728 rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 729 if (rval != SUCCESS) { 730 prg.status = prg2.status; 731 goto done_reg; 732 } 733 734 if (prg2.data == 0xffffffff) { 735 prg.status = PCITOOL_INVALID_ADDRESS; 736 prg.status = EFAULT; 737 goto done_reg; 738 } 739 740 prg2.data = (prg2.data << 32) + low_bytes; 741 if (pcitool_debug) 742 prom_printf( 743 "64 bit mem space. " 744 "64-bit bar is 0x%" PRIx64 "\n", 745 prg2.data); 746 747 /* Mem32 space, including ROM */ 748 } else { 749 750 if (prg2.offset == PCI_CONF_ROM) { 751 if (pcitool_debug) 752 prom_printf( 753 "Additional ROM " 754 "checking\n"); 755 /* Can't write to ROM */ 756 if (write_flag) { 757 prg.status = PCITOOL_ROM_WRITE; 758 rval = EIO; 759 goto done_reg; 760 761 /* ROM disabled for reading */ 762 } else if (!(prg2.data & 0x00000001)) { 763 prg.status = 764 PCITOOL_ROM_DISABLED; 765 rval = EIO; 766 goto done_reg; 767 } 768 } 769 770 if (pcitool_debug) 771 prom_printf("32 bit mem space\n"); 772 } 773 774 /* Common code for all IO/MEM range spaces. */ 775 776 base_addr = prg2.data; 777 if (pcitool_debug) 778 prom_printf( 779 "addr portion of bar is 0x%" PRIx64 ", " 780 "base=0x%" PRIx64 ", " 781 "offset:0x%" PRIx64 "\n", 782 prg2.data, base_addr, prg.offset); 783 /* 784 * Use offset provided by caller to index into 785 * desired space, then access. 786 * Note that prg.status is modified on error. 787 */ 788 prg.phys_addr = base_addr + prg.offset; 789 790 virt_addr = pcitool_map(prg.phys_addr, size, 791 &num_virt_pages); 792 if (virt_addr == NULL) { 793 prg.status = PCITOOL_IO_ERROR; 794 rval = EIO; 795 goto done_reg; 796 } 797 798 rval = pcitool_mem_access(dip, &prg, virt_addr, 799 write_flag); 800 pcitool_unmap(virt_addr, num_virt_pages); 801 } 802 done_reg: 803 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 804 DDI_SUCCESS) { 805 if (pcitool_debug) 806 prom_printf("Error returning arguments.\n"); 807 rval = EFAULT; 808 } 809 break; 810 default: 811 rval = ENOTTY; 812 break; 813 } 814 return (rval); 815 } 816