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