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