17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52917a9c9Sschwartz * Common Development and Distribution License (the "License"). 62917a9c9Sschwartz * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*09b1eac2SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267851eb82Sschwartz #include <sys/stat.h> 277851eb82Sschwartz #include <sys/sunddi.h> 287851eb82Sschwartz #include <sys/param.h> 297851eb82Sschwartz 307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 317c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> 327c478bd9Sstevel@tonic-gate #include <sys/promif.h> 337c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 347c478bd9Sstevel@tonic-gate 357c478bd9Sstevel@tonic-gate #include <sys/pci/pci_obj.h> 367c478bd9Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h> 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #include <sys/pci_tools.h> 39d4476ccbSschwartz #include <sys/pci/pci_tools_ext.h> 40d4476ccbSschwartz 41d4476ccbSschwartz /* 42d4476ccbSschwartz * Number of interrupts supported per PCI bus. 43d4476ccbSschwartz */ 44d4476ccbSschwartz #define PCI_MAX_INO 0x3f 45d4476ccbSschwartz 46d4476ccbSschwartz /* 47d4476ccbSschwartz * PCI Space definitions. 48d4476ccbSschwartz */ 49d4476ccbSschwartz #define PCI_CONFIG_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 50d4476ccbSschwartz #define PCI_IO_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_IO)) 51d4476ccbSschwartz #define PCI_MEM_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 52d4476ccbSschwartz #define PCI_MEM64_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate /* 557c478bd9Sstevel@tonic-gate * Extract 64 bit parent or size values from 32 bit cells of 567c478bd9Sstevel@tonic-gate * pci_ranges_t property. 577c478bd9Sstevel@tonic-gate * 587c478bd9Sstevel@tonic-gate * Only bits 42-32 are relevant in parent_high. 597c478bd9Sstevel@tonic-gate */ 607c478bd9Sstevel@tonic-gate #define PCI_GET_RANGE_PROP(ranges, bank) \ 617c478bd9Sstevel@tonic-gate ((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \ 627c478bd9Sstevel@tonic-gate ranges[bank].parent_low) 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate #define PCI_GET_RANGE_PROP_SIZE(ranges, bank) \ 657c478bd9Sstevel@tonic-gate ((((uint64_t)(ranges[bank].size_high)) << 32) | \ 667c478bd9Sstevel@tonic-gate ranges[bank].size_low) 677c478bd9Sstevel@tonic-gate 687851eb82Sschwartz #define PCI_BAR_OFFSET(x) (pci_bars[x.barnum]) 697851eb82Sschwartz 707c478bd9Sstevel@tonic-gate /* Big and little endian as boolean values. */ 717c478bd9Sstevel@tonic-gate #define BE B_TRUE 727c478bd9Sstevel@tonic-gate #define LE B_FALSE 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate #define SUCCESS 0 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */ 777c478bd9Sstevel@tonic-gate typedef union { 787c478bd9Sstevel@tonic-gate uint64_t u64; 797c478bd9Sstevel@tonic-gate uint32_t u32; 807c478bd9Sstevel@tonic-gate uint16_t u16; 817c478bd9Sstevel@tonic-gate uint8_t u8; 827c478bd9Sstevel@tonic-gate } peek_poke_value_t; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* 857c478bd9Sstevel@tonic-gate * Offsets of BARS in config space. First entry of 0 means config space. 867c478bd9Sstevel@tonic-gate * Entries here correlate to pcitool_bars_t enumerated type. 877c478bd9Sstevel@tonic-gate */ 887c478bd9Sstevel@tonic-gate static uint8_t pci_bars[] = { 897c478bd9Sstevel@tonic-gate 0x0, 907c478bd9Sstevel@tonic-gate PCI_CONF_BASE0, 917c478bd9Sstevel@tonic-gate PCI_CONF_BASE1, 927c478bd9Sstevel@tonic-gate PCI_CONF_BASE2, 937c478bd9Sstevel@tonic-gate PCI_CONF_BASE3, 947c478bd9Sstevel@tonic-gate PCI_CONF_BASE4, 957c478bd9Sstevel@tonic-gate PCI_CONF_BASE5, 967c478bd9Sstevel@tonic-gate PCI_CONF_ROM 977c478bd9Sstevel@tonic-gate }; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 1007c478bd9Sstevel@tonic-gate 1017851eb82Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size, 1027851eb82Sschwartz uint64_t paddr, uint64_t *value_p); 1037851eb82Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size, 1047851eb82Sschwartz uint64_t paddr, uint64_t value); 1057851eb82Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr, 1067851eb82Sschwartz uint64_t *data, uint8_t size, boolean_t write, boolean_t endian, 1077851eb82Sschwartz uint32_t *pcitool_status); 1087851eb82Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg); 1097851eb82Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, 1107851eb82Sschwartz uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar, 1117851eb82Sschwartz boolean_t *is_io_space); 1127851eb82Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, 1137851eb82Sschwartz uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag); 1147851eb82Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode); 1157851eb82Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p); 1167851eb82Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p); 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int); 1197c478bd9Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int); 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate /* 1227c478bd9Sstevel@tonic-gate * Safe C wrapper around assy language routine pci_do_phys_peek 1237c478bd9Sstevel@tonic-gate * 1247c478bd9Sstevel@tonic-gate * Type is TRUE for big endian, FALSE for little endian. 1257c478bd9Sstevel@tonic-gate * Size is 1, 2, 4 or 8 bytes. 1267c478bd9Sstevel@tonic-gate * paddr is the physical address in IO space to access read. 1277c478bd9Sstevel@tonic-gate * value_p is where the value is returned. 1287c478bd9Sstevel@tonic-gate */ 1297c478bd9Sstevel@tonic-gate static int 1307851eb82Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size, 1317851eb82Sschwartz uint64_t paddr, uint64_t *value_p) 1327c478bd9Sstevel@tonic-gate { 1337c478bd9Sstevel@tonic-gate on_trap_data_t otd; 1347c478bd9Sstevel@tonic-gate int err = DDI_SUCCESS; 1357851eb82Sschwartz peek_poke_value_t peek_value; 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p; 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = &otd; 1407c478bd9Sstevel@tonic-gate 1417c478bd9Sstevel@tonic-gate /* Set up trap handling to make the access safe. */ 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate /* 1447c478bd9Sstevel@tonic-gate * on_trap works like setjmp. 1457c478bd9Sstevel@tonic-gate * Set it up to not panic on data access error, 1467c478bd9Sstevel@tonic-gate * but to call peek_fault instead. 1477c478bd9Sstevel@tonic-gate * Call pci_do_phys_peek after trap handling is setup. 1487c478bd9Sstevel@tonic-gate * When on_trap returns FALSE, it has been setup. 1497c478bd9Sstevel@tonic-gate * When it returns TRUE, an it has caught an error. 1507c478bd9Sstevel@tonic-gate */ 1517c478bd9Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) { 1527c478bd9Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&peek_fault; 1537c478bd9Sstevel@tonic-gate err = pci_do_phys_peek(size, paddr, &peek_value.u64, type); 1547c478bd9Sstevel@tonic-gate } else { 1557c478bd9Sstevel@tonic-gate err = DDI_FAILURE; 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = NULL; 1597c478bd9Sstevel@tonic-gate no_trap(); 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate if (err != DDI_FAILURE) { 1627c478bd9Sstevel@tonic-gate switch (size) { 1637c478bd9Sstevel@tonic-gate case 8: 1647c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u64; 1657c478bd9Sstevel@tonic-gate break; 1667c478bd9Sstevel@tonic-gate case 4: 1677c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u32; 1687c478bd9Sstevel@tonic-gate break; 1697c478bd9Sstevel@tonic-gate case 2: 1707c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u16; 1717c478bd9Sstevel@tonic-gate break; 1727c478bd9Sstevel@tonic-gate case 1: 1737c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u8; 1747c478bd9Sstevel@tonic-gate break; 1757c478bd9Sstevel@tonic-gate default: 1767c478bd9Sstevel@tonic-gate err = DDI_FAILURE; 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate } 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate return (err); 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * Safe C wrapper around assy language routine pci_do_phys_poke 1857c478bd9Sstevel@tonic-gate * 1867c478bd9Sstevel@tonic-gate * Type is TRUE for big endian, FALSE for little endian. 1877c478bd9Sstevel@tonic-gate * Size is 1,2,4 or 8 bytes. 1887c478bd9Sstevel@tonic-gate * paddr is the physical address in IO space to access read. 1897c478bd9Sstevel@tonic-gate * value contains the value to be written. 1907c478bd9Sstevel@tonic-gate */ 1917c478bd9Sstevel@tonic-gate static int 1927851eb82Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size, 1937851eb82Sschwartz uint64_t paddr, uint64_t value) 1947c478bd9Sstevel@tonic-gate { 1957c478bd9Sstevel@tonic-gate on_trap_data_t otd; 1967c478bd9Sstevel@tonic-gate int err = DDI_SUCCESS; 1977c478bd9Sstevel@tonic-gate peek_poke_value_t poke_value; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p; 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate switch (size) { 2027c478bd9Sstevel@tonic-gate case 8: 2037c478bd9Sstevel@tonic-gate poke_value.u64 = value; 2047c478bd9Sstevel@tonic-gate break; 2057c478bd9Sstevel@tonic-gate case 4: 2067c478bd9Sstevel@tonic-gate poke_value.u32 = (uint32_t)value; 2077c478bd9Sstevel@tonic-gate break; 2087c478bd9Sstevel@tonic-gate case 2: 2097c478bd9Sstevel@tonic-gate poke_value.u16 = (uint16_t)value; 2107c478bd9Sstevel@tonic-gate break; 2117c478bd9Sstevel@tonic-gate case 1: 2127c478bd9Sstevel@tonic-gate poke_value.u8 = (uint8_t)value; 2137c478bd9Sstevel@tonic-gate break; 2147c478bd9Sstevel@tonic-gate default: 2157c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate mutex_enter(&pbm_p->pbm_pokefault_mutex); 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = &otd; 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate /* 2237c478bd9Sstevel@tonic-gate * on_trap works like setjmp. 2247c478bd9Sstevel@tonic-gate * Set it up to not panic on data access error, 2257c478bd9Sstevel@tonic-gate * but to call poke_fault instead. 2267c478bd9Sstevel@tonic-gate * Call pci_do_phys_poke after trap handling is setup. 2277c478bd9Sstevel@tonic-gate * When on_trap returns FALSE, it has been setup. 2287c478bd9Sstevel@tonic-gate * When it returns TRUE, an it has caught an error. 2297c478bd9Sstevel@tonic-gate */ 2307c478bd9Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) { 2317c478bd9Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&poke_fault; 2327c478bd9Sstevel@tonic-gate err = pci_do_phys_poke(size, paddr, &poke_value.u64, type); 2337c478bd9Sstevel@tonic-gate } 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate /* Let the dust settle and errors occur if they will. */ 2367c478bd9Sstevel@tonic-gate pbm_clear_error(pbm_p); 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate /* Check for an error. */ 2397c478bd9Sstevel@tonic-gate if (otd.ot_trap == OT_DATA_ACCESS) { 2407c478bd9Sstevel@tonic-gate err = DDI_FAILURE; 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = NULL; 2447c478bd9Sstevel@tonic-gate mutex_exit(&pbm_p->pbm_pokefault_mutex); 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate no_trap(); 2477c478bd9Sstevel@tonic-gate return (err); 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate 2512917a9c9Sschwartz /*ARGSUSED*/ 2527c478bd9Sstevel@tonic-gate static int 2532917a9c9Sschwartz pcitool_intr_info(dev_info_t *dip, void *arg, int mode) 2547c478bd9Sstevel@tonic-gate { 2552917a9c9Sschwartz pcitool_intr_info_t intr_info; 2562917a9c9Sschwartz int rval = SUCCESS; 2577c478bd9Sstevel@tonic-gate 2582917a9c9Sschwartz /* If we need user_version, and to ret same user version as passed in */ 2592917a9c9Sschwartz if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != 2607c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 2617c478bd9Sstevel@tonic-gate return (EFAULT); 2627c478bd9Sstevel@tonic-gate } 2632917a9c9Sschwartz 264*09b1eac2SEvan Yan if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI) 265*09b1eac2SEvan Yan return (ENOTSUP); 266*09b1eac2SEvan Yan 2672917a9c9Sschwartz intr_info.ctlr_version = 0; /* XXX how to get real version? */ 2682917a9c9Sschwartz intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; 2692917a9c9Sschwartz intr_info.num_intr = PCI_MAX_INO; 2702917a9c9Sschwartz 2712917a9c9Sschwartz intr_info.drvr_version = PCITOOL_VERSION; 2722917a9c9Sschwartz if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != 2732917a9c9Sschwartz DDI_SUCCESS) { 2742917a9c9Sschwartz rval = EFAULT; 2752917a9c9Sschwartz } 2762917a9c9Sschwartz 2772917a9c9Sschwartz return (rval); 2787c478bd9Sstevel@tonic-gate } 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate /* 2827c478bd9Sstevel@tonic-gate * Get interrupt information for a given ino. 2837c478bd9Sstevel@tonic-gate * Returns info only for inos mapped to devices. 2847c478bd9Sstevel@tonic-gate * 2857c478bd9Sstevel@tonic-gate * Returned info is valid only when iget.num_devs is returned > 0. 2867c478bd9Sstevel@tonic-gate * If ino is not enabled or is not mapped to a device, num_devs will be = 0. 2877c478bd9Sstevel@tonic-gate */ 2887c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2897c478bd9Sstevel@tonic-gate static int 2907c478bd9Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) 2917c478bd9Sstevel@tonic-gate { 2927c478bd9Sstevel@tonic-gate /* Array part isn't used here, but oh well... */ 2937c478bd9Sstevel@tonic-gate pcitool_intr_get_t partial_iget; 2947c478bd9Sstevel@tonic-gate pcitool_intr_get_t *iget = &partial_iget; 2957c478bd9Sstevel@tonic-gate size_t iget_kmem_alloc_size = 0; 2967c478bd9Sstevel@tonic-gate ib_t *ib_p = pci_p->pci_ib_p; 2977c478bd9Sstevel@tonic-gate volatile uint64_t *imregp; 2987c478bd9Sstevel@tonic-gate uint64_t imregval; 2997c478bd9Sstevel@tonic-gate uint32_t ino; 3007c478bd9Sstevel@tonic-gate uint8_t num_devs_ret; 301*09b1eac2SEvan Yan int cpu_id; 3027c478bd9Sstevel@tonic-gate int copyout_rval; 3037c478bd9Sstevel@tonic-gate int rval = SUCCESS; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate /* Read in just the header part, no array section. */ 3067c478bd9Sstevel@tonic-gate if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 3077c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate return (EFAULT); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 312*09b1eac2SEvan Yan if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) { 313*09b1eac2SEvan Yan partial_iget.status = PCITOOL_IO_ERROR; 314*09b1eac2SEvan Yan partial_iget.num_devs_ret = 0; 315*09b1eac2SEvan Yan rval = ENOTSUP; 316*09b1eac2SEvan Yan goto done_get_intr; 317*09b1eac2SEvan Yan } 318*09b1eac2SEvan Yan 3197c478bd9Sstevel@tonic-gate ino = partial_iget.ino; 3207c478bd9Sstevel@tonic-gate num_devs_ret = partial_iget.num_devs_ret; 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate /* Validate argument. */ 3237c478bd9Sstevel@tonic-gate if (ino > PCI_MAX_INO) { 3247c478bd9Sstevel@tonic-gate partial_iget.status = PCITOOL_INVALID_INO; 3257c478bd9Sstevel@tonic-gate partial_iget.num_devs_ret = 0; 3267c478bd9Sstevel@tonic-gate rval = EINVAL; 3277c478bd9Sstevel@tonic-gate goto done_get_intr; 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate /* Caller wants device information returned. */ 3317c478bd9Sstevel@tonic-gate if (num_devs_ret > 0) { 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate /* 3347c478bd9Sstevel@tonic-gate * Allocate room. 3357c478bd9Sstevel@tonic-gate * Note if num_devs_ret == 0 iget remains pointing to 3367c478bd9Sstevel@tonic-gate * partial_iget. 3377c478bd9Sstevel@tonic-gate */ 3387c478bd9Sstevel@tonic-gate iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 3397c478bd9Sstevel@tonic-gate iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate /* Read in whole structure to verify there's room. */ 3427c478bd9Sstevel@tonic-gate if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 3437c478bd9Sstevel@tonic-gate SUCCESS) { 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* Be consistent and just return EFAULT here. */ 3467c478bd9Sstevel@tonic-gate kmem_free(iget, iget_kmem_alloc_size); 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate return (EFAULT); 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 3537c478bd9Sstevel@tonic-gate iget->ino = ino; 3547c478bd9Sstevel@tonic-gate iget->num_devs_ret = num_devs_ret; 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate imregp = ib_intr_map_reg_addr(ib_p, ino); 3577c478bd9Sstevel@tonic-gate imregval = *imregp; 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate /* 3607c478bd9Sstevel@tonic-gate * Read "valid" bit. If set, interrupts are enabled. 3617c478bd9Sstevel@tonic-gate * This bit happens to be the same on Fire and Tomatillo. 3627c478bd9Sstevel@tonic-gate */ 3637c478bd9Sstevel@tonic-gate if (imregval & COMMON_INTR_MAP_REG_VALID) { 3647c478bd9Sstevel@tonic-gate /* 3657c478bd9Sstevel@tonic-gate * The following looks up the ib_ino_info and returns 3667c478bd9Sstevel@tonic-gate * info of devices mapped to this ino. 3677c478bd9Sstevel@tonic-gate */ 3687c478bd9Sstevel@tonic-gate iget->num_devs = ib_get_ino_devs( 3697c478bd9Sstevel@tonic-gate ib_p, ino, &iget->num_devs_ret, iget->dev); 3707c478bd9Sstevel@tonic-gate 371*09b1eac2SEvan Yan if (ib_get_intr_target(pci_p, ino, &cpu_id) != DDI_SUCCESS) { 372*09b1eac2SEvan Yan iget->status = PCITOOL_IO_ERROR; 373*09b1eac2SEvan Yan rval = EIO; 374*09b1eac2SEvan Yan goto done_get_intr; 375*09b1eac2SEvan Yan } 376*09b1eac2SEvan Yan 3777c478bd9Sstevel@tonic-gate /* 3787c478bd9Sstevel@tonic-gate * Consider only inos mapped to devices (as opposed to 3797c478bd9Sstevel@tonic-gate * inos mapped to the bridge itself. 3807c478bd9Sstevel@tonic-gate */ 3817c478bd9Sstevel@tonic-gate if (iget->num_devs > 0) { 3827c478bd9Sstevel@tonic-gate /* 3837c478bd9Sstevel@tonic-gate * These 2 items are platform specific, 3847c478bd9Sstevel@tonic-gate * extracted from the bridge. 3857c478bd9Sstevel@tonic-gate */ 3867c478bd9Sstevel@tonic-gate iget->ctlr = 0; 387*09b1eac2SEvan Yan iget->cpu_id = cpu_id; 3887c478bd9Sstevel@tonic-gate } 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate done_get_intr: 3912917a9c9Sschwartz iget->drvr_version = PCITOOL_VERSION; 3927c478bd9Sstevel@tonic-gate copyout_rval = ddi_copyout(iget, arg, 3937c478bd9Sstevel@tonic-gate PCITOOL_IGET_SIZE(num_devs_ret), mode); 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate if (iget_kmem_alloc_size > 0) { 3967c478bd9Sstevel@tonic-gate kmem_free(iget, iget_kmem_alloc_size); 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate if (copyout_rval != DDI_SUCCESS) { 4007c478bd9Sstevel@tonic-gate rval = EFAULT; 4017c478bd9Sstevel@tonic-gate } 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate return (rval); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate /* 4077c478bd9Sstevel@tonic-gate * Associate a new CPU with a given ino. 4087c478bd9Sstevel@tonic-gate * 4097c478bd9Sstevel@tonic-gate * Operate only on inos which are already mapped to devices. 4107c478bd9Sstevel@tonic-gate */ 4117c478bd9Sstevel@tonic-gate static int 4127c478bd9Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) 4137c478bd9Sstevel@tonic-gate { 4147851eb82Sschwartz ib_t *ib_p = pci_p->pci_ib_p; 4157851eb82Sschwartz int rval = SUCCESS; 416*09b1eac2SEvan Yan int ret = DDI_SUCCESS; 4177c478bd9Sstevel@tonic-gate uint8_t zero = 0; 4187c478bd9Sstevel@tonic-gate pcitool_intr_set_t iset; 4197c478bd9Sstevel@tonic-gate volatile uint64_t *imregp; 420*09b1eac2SEvan Yan uint64_t imregval; 421*09b1eac2SEvan Yan 4222917a9c9Sschwartz size_t copyinout_size; 423*09b1eac2SEvan Yan int old_cpu_id; 4247c478bd9Sstevel@tonic-gate 4252917a9c9Sschwartz bzero(&iset, sizeof (pcitool_intr_set_t)); 4262917a9c9Sschwartz 4272917a9c9Sschwartz /* Version 1 of pcitool_intr_set_t doesn't have flags. */ 4282917a9c9Sschwartz copyinout_size = (size_t)&iset.flags - (size_t)&iset; 4292917a9c9Sschwartz 4302917a9c9Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 4317c478bd9Sstevel@tonic-gate return (EFAULT); 4327c478bd9Sstevel@tonic-gate 4332917a9c9Sschwartz switch (iset.user_version) { 4342917a9c9Sschwartz case PCITOOL_V1: 4352917a9c9Sschwartz break; 4362917a9c9Sschwartz 4372917a9c9Sschwartz case PCITOOL_V2: 4382917a9c9Sschwartz copyinout_size = sizeof (pcitool_intr_set_t); 4392917a9c9Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 4402917a9c9Sschwartz return (EFAULT); 4412917a9c9Sschwartz break; 4422917a9c9Sschwartz 4432917a9c9Sschwartz default: 4442917a9c9Sschwartz iset.status = PCITOOL_OUT_OF_RANGE; 4452917a9c9Sschwartz rval = ENOTSUP; 4462917a9c9Sschwartz goto done_set_intr; 4472917a9c9Sschwartz } 4482917a9c9Sschwartz 449*09b1eac2SEvan Yan if ((iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) || 450*09b1eac2SEvan Yan (iset.flags & PCITOOL_INTR_FLAG_SET_MSI)) { 4512917a9c9Sschwartz iset.status = PCITOOL_IO_ERROR; 4522917a9c9Sschwartz rval = ENOTSUP; 4532917a9c9Sschwartz goto done_set_intr; 4542917a9c9Sschwartz } 4552917a9c9Sschwartz 4567851eb82Sschwartz /* Validate input argument and that ino given belongs to a device. */ 4577851eb82Sschwartz if ((iset.ino > PCI_MAX_INO) || 4587851eb82Sschwartz (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) { 4597c478bd9Sstevel@tonic-gate iset.status = PCITOOL_INVALID_INO; 4607c478bd9Sstevel@tonic-gate rval = EINVAL; 4617c478bd9Sstevel@tonic-gate goto done_set_intr; 4627c478bd9Sstevel@tonic-gate } 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino); 4657c478bd9Sstevel@tonic-gate imregval = *imregp; 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate /* Operate only on inos which are already enabled. */ 4687c478bd9Sstevel@tonic-gate if (!(imregval & COMMON_INTR_MAP_REG_VALID)) { 4697c478bd9Sstevel@tonic-gate iset.status = PCITOOL_INVALID_INO; 4707c478bd9Sstevel@tonic-gate rval = EINVAL; 4717c478bd9Sstevel@tonic-gate goto done_set_intr; 4727c478bd9Sstevel@tonic-gate } 4737c478bd9Sstevel@tonic-gate 474*09b1eac2SEvan Yan if (ib_get_intr_target(pci_p, iset.ino, &old_cpu_id) != DDI_SUCCESS) { 475*09b1eac2SEvan Yan iset.status = PCITOOL_INVALID_INO; 476*09b1eac2SEvan Yan rval = EINVAL; 4777c478bd9Sstevel@tonic-gate goto done_set_intr; 4787c478bd9Sstevel@tonic-gate } 4797c478bd9Sstevel@tonic-gate 480*09b1eac2SEvan Yan if ((ret = ib_set_intr_target(pci_p, iset.ino, 481*09b1eac2SEvan Yan iset.cpu_id)) == DDI_SUCCESS) { 4827c478bd9Sstevel@tonic-gate iset.cpu_id = old_cpu_id; 4837c478bd9Sstevel@tonic-gate iset.status = PCITOOL_SUCCESS; 484*09b1eac2SEvan Yan goto done_set_intr; 485*09b1eac2SEvan Yan } 4867c478bd9Sstevel@tonic-gate 487*09b1eac2SEvan Yan switch (ret) { 488*09b1eac2SEvan Yan case DDI_EPENDING: 489*09b1eac2SEvan Yan iset.status = PCITOOL_PENDING_INTRTIMEOUT; 490*09b1eac2SEvan Yan rval = ETIME; 491*09b1eac2SEvan Yan break; 492*09b1eac2SEvan Yan case DDI_EINVAL: 4937c478bd9Sstevel@tonic-gate iset.status = PCITOOL_INVALID_CPUID; 4947c478bd9Sstevel@tonic-gate rval = EINVAL; 495*09b1eac2SEvan Yan break; 496*09b1eac2SEvan Yan default: 497*09b1eac2SEvan Yan iset.status = PCITOOL_INVALID_INO; 498*09b1eac2SEvan Yan rval = EINVAL; 499*09b1eac2SEvan Yan break; 5007c478bd9Sstevel@tonic-gate } 5017c478bd9Sstevel@tonic-gate done_set_intr: 5022917a9c9Sschwartz iset.drvr_version = PCITOOL_VERSION; 5032917a9c9Sschwartz if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) 5047c478bd9Sstevel@tonic-gate rval = EFAULT; 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate return (rval); 5077c478bd9Sstevel@tonic-gate } 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */ 5117c478bd9Sstevel@tonic-gate int 5127c478bd9Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode) 5137c478bd9Sstevel@tonic-gate { 5147c478bd9Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev); 5157c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 5167c478bd9Sstevel@tonic-gate int rval = SUCCESS; 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate switch (cmd) { 5197c478bd9Sstevel@tonic-gate 5202917a9c9Sschwartz /* Get system interrupt information. */ 5212917a9c9Sschwartz case PCITOOL_SYSTEM_INTR_INFO: 5222917a9c9Sschwartz rval = pcitool_intr_info(dip, arg, mode); 5237c478bd9Sstevel@tonic-gate break; 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate /* Get interrupt information for a given ino. */ 5267c478bd9Sstevel@tonic-gate case PCITOOL_DEVICE_GET_INTR: 5277c478bd9Sstevel@tonic-gate rval = pcitool_get_intr(dip, arg, mode, pci_p); 5287c478bd9Sstevel@tonic-gate break; 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate /* Associate a new CPU with a given ino. */ 5317c478bd9Sstevel@tonic-gate case PCITOOL_DEVICE_SET_INTR: 5327c478bd9Sstevel@tonic-gate rval = pcitool_set_intr(dip, arg, mode, pci_p); 5337c478bd9Sstevel@tonic-gate break; 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate default: 5367c478bd9Sstevel@tonic-gate rval = ENOTTY; 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate return (rval); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate /* 5447851eb82Sschwartz * Wrapper around pcitool_phys_peek/poke. 5457c478bd9Sstevel@tonic-gate * 5467851eb82Sschwartz * Validates arguments and calls pcitool_phys_peek/poke appropriately. 5477c478bd9Sstevel@tonic-gate * 5487c478bd9Sstevel@tonic-gate * Dip is of the nexus, 5497c478bd9Sstevel@tonic-gate * phys_addr is the address to write in physical space, 5507c478bd9Sstevel@tonic-gate * max_addr is the upper bound on the physical space used for bounds checking, 5517c478bd9Sstevel@tonic-gate * pcitool_status returns more detailed status in addition to a more generic 5527c478bd9Sstevel@tonic-gate * errno-style function return value. 5537c478bd9Sstevel@tonic-gate * other args are self-explanatory. 5547c478bd9Sstevel@tonic-gate */ 5557c478bd9Sstevel@tonic-gate static int 5567851eb82Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr, 5577c478bd9Sstevel@tonic-gate uint64_t *data, uint8_t size, boolean_t write, boolean_t endian, 5587c478bd9Sstevel@tonic-gate uint32_t *pcitool_status) 5597c478bd9Sstevel@tonic-gate { 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate int rval = SUCCESS; 5627c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate /* Upper bounds checking. */ 5657c478bd9Sstevel@tonic-gate if (phys_addr > max_addr) { 5667c478bd9Sstevel@tonic-gate DEBUG2(DBG_TOOLS, dip, 5677c478bd9Sstevel@tonic-gate "Phys addr 0x%llx out of range (max 0x%llx).\n", 5687c478bd9Sstevel@tonic-gate phys_addr, max_addr); 5697c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS; 5707c478bd9Sstevel@tonic-gate 5717c478bd9Sstevel@tonic-gate rval = EINVAL; 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate /* Alignment checking. */ 5747c478bd9Sstevel@tonic-gate } else if (!IS_P2ALIGNED(phys_addr, size)) { 5757c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "not aligned.\n"); 5767c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_NOT_ALIGNED; 5777c478bd9Sstevel@tonic-gate 5787c478bd9Sstevel@tonic-gate rval = EINVAL; 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate /* Made it through checks. Do the access. */ 5817c478bd9Sstevel@tonic-gate } else if (write) { 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip, 5847851eb82Sschwartz "%d byte %s pcitool_phys_poke at addr 0x%llx\n", 5857c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr); 5867c478bd9Sstevel@tonic-gate 5877851eb82Sschwartz if (pcitool_phys_poke(pci_p, endian, size, phys_addr, 5887851eb82Sschwartz *data) != DDI_SUCCESS) { 5897c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip, 5907851eb82Sschwartz "%d byte %s pcitool_phys_poke at addr " 5917c478bd9Sstevel@tonic-gate "0x%llx failed\n", 5927c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr); 5937c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS; 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate rval = EFAULT; 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate 5987851eb82Sschwartz } else { /* Read */ 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip, 6017851eb82Sschwartz "%d byte %s pcitool_phys_peek at addr 0x%llx\n", 6027c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr); 6037c478bd9Sstevel@tonic-gate 6047851eb82Sschwartz if (pcitool_phys_peek(pci_p, endian, size, phys_addr, 6057851eb82Sschwartz data) != DDI_SUCCESS) { 6067c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip, 6077851eb82Sschwartz "%d byte %s pcitool_phys_peek at addr " 6087c478bd9Sstevel@tonic-gate "0x%llx failed\n", 6097c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr); 6107c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS; 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate rval = EFAULT; 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate return (rval); 6167c478bd9Sstevel@tonic-gate } 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate /* 6197c478bd9Sstevel@tonic-gate * Perform register accesses on the nexus device itself. 6207c478bd9Sstevel@tonic-gate */ 6217c478bd9Sstevel@tonic-gate int 6227c478bd9Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode) 6237c478bd9Sstevel@tonic-gate { 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev); 6267c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 6277851eb82Sschwartz pci_nexus_regspec_t *pci_rp = NULL; 6287851eb82Sschwartz boolean_t write_flag = B_FALSE; 6297c478bd9Sstevel@tonic-gate pcitool_reg_t prg; 6307c478bd9Sstevel@tonic-gate uint64_t base_addr; 6317c478bd9Sstevel@tonic-gate uint64_t max_addr; 6327c478bd9Sstevel@tonic-gate uint32_t reglen; 6337c478bd9Sstevel@tonic-gate uint8_t size; 6347c478bd9Sstevel@tonic-gate uint32_t rval = 0; 6357c478bd9Sstevel@tonic-gate 6367851eb82Sschwartz if (cmd == PCITOOL_NEXUS_SET_REG) 6377c478bd9Sstevel@tonic-gate write_flag = B_TRUE; 6387c478bd9Sstevel@tonic-gate 6397851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n"); 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate /* Read data from userland. */ 6427c478bd9Sstevel@tonic-gate if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 6437c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 6447c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n"); 6457c478bd9Sstevel@tonic-gate return (EFAULT); 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate 6487851eb82Sschwartz /* Read reg property which contains starting addr and size of banks. */ 6497c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array( 6507c478bd9Sstevel@tonic-gate DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6517c478bd9Sstevel@tonic-gate "reg", (int **)&pci_rp, ®len) == DDI_SUCCESS) { 6527c478bd9Sstevel@tonic-gate if (((reglen * sizeof (int)) % 6537c478bd9Sstevel@tonic-gate sizeof (pci_nexus_regspec_t)) != 0) { 6547851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed"); 6557c478bd9Sstevel@tonic-gate prg.status = PCITOOL_REGPROP_NOTWELLFORMED; 6567c478bd9Sstevel@tonic-gate rval = EIO; 6577c478bd9Sstevel@tonic-gate goto done; 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate } 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate /* Bounds check the bank number. */ 6627851eb82Sschwartz if (prg.barnum >= 6637851eb82Sschwartz (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) { 6647c478bd9Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 6657c478bd9Sstevel@tonic-gate rval = EINVAL; 6667c478bd9Sstevel@tonic-gate goto done; 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 6707c478bd9Sstevel@tonic-gate base_addr = pci_rp[prg.barnum].phys_addr; 6717c478bd9Sstevel@tonic-gate max_addr = base_addr + pci_rp[prg.barnum].size; 6727c478bd9Sstevel@tonic-gate prg.phys_addr = base_addr + prg.offset; 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate DEBUG4(DBG_TOOLS, dip, 6757c478bd9Sstevel@tonic-gate "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, " 6767c478bd9Sstevel@tonic-gate "addr:0x%llx, max_addr:0x%llx\n", 6777c478bd9Sstevel@tonic-gate base_addr, prg.offset, prg.phys_addr, max_addr); 6787c478bd9Sstevel@tonic-gate 6797c478bd9Sstevel@tonic-gate /* Access device. prg.status is modified. */ 6807851eb82Sschwartz rval = pcitool_access(pci_p, 6817c478bd9Sstevel@tonic-gate prg.phys_addr, max_addr, &prg.data, size, write_flag, 6827851eb82Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status); 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gate done: 6857851eb82Sschwartz if (pci_rp != NULL) 6867851eb82Sschwartz ddi_prop_free(pci_rp); 6877851eb82Sschwartz 6882917a9c9Sschwartz prg.drvr_version = PCITOOL_VERSION; 6897c478bd9Sstevel@tonic-gate if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 6907c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 6917c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n"); 6927c478bd9Sstevel@tonic-gate return (EFAULT); 6937c478bd9Sstevel@tonic-gate } 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate return (rval); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate 6997851eb82Sschwartz static int 7007851eb82Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg) 7017851eb82Sschwartz { 7027851eb82Sschwartz int rval = SUCCESS; 7037851eb82Sschwartz 7047851eb82Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 7057851eb82Sschwartz prg->status = PCITOOL_OUT_OF_RANGE; 7067851eb82Sschwartz rval = EINVAL; 7077851eb82Sschwartz 7087851eb82Sschwartz /* Validate address arguments of bus / dev / func */ 7097851eb82Sschwartz } else if (((prg->bus_no & 7107851eb82Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 7117851eb82Sschwartz ((prg->dev_no & 7127851eb82Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 7137851eb82Sschwartz ((prg->func_no & 7147851eb82Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 7157851eb82Sschwartz prg->status = PCITOOL_INVALID_ADDRESS; 7167851eb82Sschwartz rval = EINVAL; 7177851eb82Sschwartz } 7187851eb82Sschwartz 7197851eb82Sschwartz return (rval); 7207851eb82Sschwartz } 7217851eb82Sschwartz 7227851eb82Sschwartz static int 7237851eb82Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr, 7247851eb82Sschwartz uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space) 7257851eb82Sschwartz { 7267851eb82Sschwartz 7277851eb82Sschwartz uint8_t bar_offset; 7287851eb82Sschwartz int rval; 7297851eb82Sschwartz dev_info_t *dip = pci_p->pci_dip; 7307851eb82Sschwartz 7317851eb82Sschwartz *bar = 0; 7327851eb82Sschwartz *is_io_space = B_FALSE; 7337851eb82Sschwartz 7347851eb82Sschwartz /* 7357851eb82Sschwartz * Translate BAR number into offset of the BAR in 7367851eb82Sschwartz * the device's config space. 7377851eb82Sschwartz */ 7387851eb82Sschwartz bar_offset = PCI_BAR_OFFSET((*prg)); 7397851eb82Sschwartz 7407851eb82Sschwartz DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n", 7417851eb82Sschwartz prg->barnum, bar_offset); 7427851eb82Sschwartz 7437851eb82Sschwartz /* 7447851eb82Sschwartz * Get Bus Address Register (BAR) from config space. 7457851eb82Sschwartz * bar_offset is the offset into config space of the BAR desired. 7467851eb82Sschwartz * prg->status is modified on error. 7477851eb82Sschwartz */ 7487851eb82Sschwartz rval = pcitool_access(pci_p, config_base_addr + bar_offset, 7497851eb82Sschwartz config_max_addr, bar, 7507851eb82Sschwartz 4, /* 4 bytes. */ 7517851eb82Sschwartz B_FALSE, /* Read */ 7527851eb82Sschwartz B_FALSE, /* Little endian. */ 7537851eb82Sschwartz &prg->status); 7547851eb82Sschwartz if (rval != SUCCESS) 7557851eb82Sschwartz return (rval); 7567851eb82Sschwartz 7577851eb82Sschwartz DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar); 7587851eb82Sschwartz if (!(*bar)) { 7597851eb82Sschwartz prg->status = PCITOOL_INVALID_ADDRESS; 7607851eb82Sschwartz return (EINVAL); 7617851eb82Sschwartz } 7627851eb82Sschwartz 7637851eb82Sschwartz /* 7647851eb82Sschwartz * BAR has bits saying this space is IO space, unless 7657851eb82Sschwartz * this is the ROM address register. 7667851eb82Sschwartz */ 7677851eb82Sschwartz if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) && 7687851eb82Sschwartz (bar_offset != PCI_CONF_ROM)) { 7697851eb82Sschwartz *is_io_space = B_TRUE; 7707851eb82Sschwartz *bar &= PCI_BASE_IO_ADDR_M; 7717851eb82Sschwartz 7727851eb82Sschwartz /* 7737851eb82Sschwartz * BAR has bits saying this space is 64 bit memory 7747851eb82Sschwartz * space, unless this is the ROM address register. 7757851eb82Sschwartz * 7767851eb82Sschwartz * The 64 bit address stored in two BAR cells is not necessarily 7777851eb82Sschwartz * aligned on an 8-byte boundary. Need to keep the first 4 7787851eb82Sschwartz * bytes read, and do a separate read of the high 4 bytes. 7797851eb82Sschwartz */ 7807851eb82Sschwartz 7817851eb82Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) { 7827851eb82Sschwartz 7837851eb82Sschwartz uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL); 7847851eb82Sschwartz 7857851eb82Sschwartz /* Don't try to read past the end of BARs. */ 7867851eb82Sschwartz if (bar_offset >= PCI_CONF_BASE5) { 7877851eb82Sschwartz prg->status = PCITOOL_OUT_OF_RANGE; 7887851eb82Sschwartz return (EIO); 7897851eb82Sschwartz } 7907851eb82Sschwartz 7917851eb82Sschwartz /* Access device. prg->status is modified on error. */ 7927851eb82Sschwartz rval = pcitool_access(pci_p, 7937851eb82Sschwartz config_base_addr + bar_offset + 4, config_max_addr, bar, 7947851eb82Sschwartz 4, /* 4 bytes. */ 7957851eb82Sschwartz B_FALSE, /* Read */ 7967851eb82Sschwartz B_FALSE, /* Little endian. */ 7977851eb82Sschwartz &prg->status); 7987851eb82Sschwartz if (rval != SUCCESS) 7997851eb82Sschwartz return (rval); 8007851eb82Sschwartz 8017851eb82Sschwartz *bar = (*bar << 32) + low_bytes; 8027851eb82Sschwartz } 8037851eb82Sschwartz 8047851eb82Sschwartz return (SUCCESS); 8057851eb82Sschwartz } 8067851eb82Sschwartz 8077851eb82Sschwartz 8087851eb82Sschwartz static int 8097851eb82Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr, 8107851eb82Sschwartz uint64_t max_addr, uint8_t size, boolean_t write_flag) 8117851eb82Sschwartz { 8127851eb82Sschwartz int rval; 8137851eb82Sschwartz dev_info_t *dip = pci_p->pci_dip; 8147851eb82Sschwartz 8157851eb82Sschwartz /* Access config space and we're done. */ 8167851eb82Sschwartz prg->phys_addr = base_addr + prg->offset; 8177851eb82Sschwartz 8187851eb82Sschwartz DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, " 8197851eb82Sschwartz "offset:0x%llx, phys_addr:0x%llx, end:%s\n", 8207851eb82Sschwartz base_addr, prg->offset, prg->phys_addr, 8217851eb82Sschwartz (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl")); 8227851eb82Sschwartz 8237851eb82Sschwartz /* Access device. pr.status is modified. */ 8247851eb82Sschwartz rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size, 8257851eb82Sschwartz write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status); 8267851eb82Sschwartz 8277851eb82Sschwartz DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data); 8287851eb82Sschwartz 8297851eb82Sschwartz return (rval); 8307851eb82Sschwartz } 8317851eb82Sschwartz 8327c478bd9Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */ 8337c478bd9Sstevel@tonic-gate int 8347c478bd9Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode) 8357c478bd9Sstevel@tonic-gate { 8367c478bd9Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev); 8377c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 8387c478bd9Sstevel@tonic-gate pci_ranges_t *rp = pci_p->pci_ranges; 8397c478bd9Sstevel@tonic-gate pcitool_reg_t prg; 8407c478bd9Sstevel@tonic-gate uint64_t max_addr; 8417c478bd9Sstevel@tonic-gate uint64_t base_addr; 8427c478bd9Sstevel@tonic-gate uint64_t range_prop; 8437c478bd9Sstevel@tonic-gate uint64_t range_prop_size; 8447c478bd9Sstevel@tonic-gate uint64_t bar = 0; 8457c478bd9Sstevel@tonic-gate int rval = 0; 8467c478bd9Sstevel@tonic-gate boolean_t write_flag = B_FALSE; 8477851eb82Sschwartz boolean_t is_io_space = B_FALSE; 8487c478bd9Sstevel@tonic-gate uint8_t size; 8497c478bd9Sstevel@tonic-gate 8507851eb82Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG) 8517c478bd9Sstevel@tonic-gate write_flag = B_TRUE; 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n"); 8547c478bd9Sstevel@tonic-gate if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 8557c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 8567851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n"); 8577c478bd9Sstevel@tonic-gate return (EFAULT); 8587c478bd9Sstevel@tonic-gate } 8597c478bd9Sstevel@tonic-gate 8607c478bd9Sstevel@tonic-gate DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 8617c478bd9Sstevel@tonic-gate prg.bus_no, prg.dev_no, prg.func_no); 8627c478bd9Sstevel@tonic-gate 8637851eb82Sschwartz if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS) 8647c478bd9Sstevel@tonic-gate goto done_reg; 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* Get config space first. */ 8697c478bd9Sstevel@tonic-gate range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK); 8707851eb82Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK); 8717c478bd9Sstevel@tonic-gate max_addr = range_prop + range_prop_size; 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate /* 8747851eb82Sschwartz * Build device address based on base addr from range prop, and bus, 8757851eb82Sschwartz * dev and func values passed in. This address is where config space 8767851eb82Sschwartz * begins. 8777c478bd9Sstevel@tonic-gate */ 8787c478bd9Sstevel@tonic-gate base_addr = range_prop + 8797c478bd9Sstevel@tonic-gate (prg.bus_no << PCI_REG_BUS_SHIFT) + 8807c478bd9Sstevel@tonic-gate (prg.dev_no << PCI_REG_DEV_SHIFT) + 8817c478bd9Sstevel@tonic-gate (prg.func_no << PCI_REG_FUNC_SHIFT); 8827c478bd9Sstevel@tonic-gate 8837c478bd9Sstevel@tonic-gate if ((base_addr < range_prop) || (base_addr >= max_addr)) { 8847c478bd9Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 8857c478bd9Sstevel@tonic-gate rval = EINVAL; 8867c478bd9Sstevel@tonic-gate goto done_reg; 8877c478bd9Sstevel@tonic-gate } 8887c478bd9Sstevel@tonic-gate 8897851eb82Sschwartz DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x " 8907851eb82Sschwartz "func:0x%x, addr:0x%x\n", range_prop, 8917851eb82Sschwartz prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT, 8927851eb82Sschwartz prg.func_no << PCI_REG_FUNC_SHIFT, base_addr); 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate /* Proper config space desired. */ 8957c478bd9Sstevel@tonic-gate if (prg.barnum == 0) { 8967c478bd9Sstevel@tonic-gate 8977851eb82Sschwartz rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr, 8987851eb82Sschwartz size, write_flag); 8997c478bd9Sstevel@tonic-gate 9007851eb82Sschwartz } else { /* IO / MEM / MEM64 space. */ 9017c478bd9Sstevel@tonic-gate 9027851eb82Sschwartz if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar, 9037851eb82Sschwartz &is_io_space) != SUCCESS) 9047c478bd9Sstevel@tonic-gate goto done_reg; 9057c478bd9Sstevel@tonic-gate 9067851eb82Sschwartz /* IO space. */ 9077851eb82Sschwartz if (is_io_space) { 9087c478bd9Sstevel@tonic-gate 9097c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "IO space\n"); 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate /* Reposition to focus on IO space. */ 9127851eb82Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK); 9137c478bd9Sstevel@tonic-gate range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, 9147c478bd9Sstevel@tonic-gate PCI_IO_RANGE_BANK); 9157c478bd9Sstevel@tonic-gate 9167851eb82Sschwartz /* 64 bit memory space. */ 9177851eb82Sschwartz } else if ((bar >> 32) != 0) { 9187c478bd9Sstevel@tonic-gate 9197c478bd9Sstevel@tonic-gate DEBUG1(DBG_TOOLS, dip, 9207851eb82Sschwartz "64 bit mem space. 64-bit bar is 0x%llx\n", bar); 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate /* Reposition to MEM64 range space. */ 9237c478bd9Sstevel@tonic-gate range_prop = PCI_GET_RANGE_PROP(rp, 9247c478bd9Sstevel@tonic-gate PCI_MEM64_RANGE_BANK); 9257c478bd9Sstevel@tonic-gate range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, 9267c478bd9Sstevel@tonic-gate PCI_MEM64_RANGE_BANK); 9277c478bd9Sstevel@tonic-gate 9287851eb82Sschwartz } else { /* Mem32 space, including ROM */ 9297c478bd9Sstevel@tonic-gate 9307851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n"); 9317851eb82Sschwartz 9327851eb82Sschwartz if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) { 9337c478bd9Sstevel@tonic-gate 9347c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, 9357c478bd9Sstevel@tonic-gate "Additional ROM checking\n"); 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate /* Can't write to ROM */ 9387c478bd9Sstevel@tonic-gate if (write_flag) { 9397c478bd9Sstevel@tonic-gate prg.status = PCITOOL_ROM_WRITE; 9407c478bd9Sstevel@tonic-gate rval = EIO; 9417c478bd9Sstevel@tonic-gate goto done_reg; 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate /* ROM disabled for reading */ 9447c478bd9Sstevel@tonic-gate } else if (!(bar & 0x00000001)) { 9457851eb82Sschwartz prg.status = PCITOOL_ROM_DISABLED; 9467c478bd9Sstevel@tonic-gate rval = EIO; 9477c478bd9Sstevel@tonic-gate goto done_reg; 9487c478bd9Sstevel@tonic-gate } 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate 9517851eb82Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK); 9527c478bd9Sstevel@tonic-gate range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, 9537c478bd9Sstevel@tonic-gate PCI_MEM_RANGE_BANK); 9547c478bd9Sstevel@tonic-gate } 9557c478bd9Sstevel@tonic-gate 9567c478bd9Sstevel@tonic-gate /* Common code for all IO/MEM range spaces. */ 9577c478bd9Sstevel@tonic-gate max_addr = range_prop + range_prop_size; 9587c478bd9Sstevel@tonic-gate base_addr = range_prop + bar; 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate DEBUG3(DBG_TOOLS, dip, 9617c478bd9Sstevel@tonic-gate "addr portion of bar is 0x%llx, base=0x%llx, " 9627c478bd9Sstevel@tonic-gate "offset:0x%lx\n", bar, base_addr, prg.offset); 9637c478bd9Sstevel@tonic-gate 9647c478bd9Sstevel@tonic-gate /* 9657c478bd9Sstevel@tonic-gate * Use offset provided by caller to index into 9667c478bd9Sstevel@tonic-gate * desired space, then access. 9677c478bd9Sstevel@tonic-gate * Note that prg.status is modified on error. 9687c478bd9Sstevel@tonic-gate */ 9697c478bd9Sstevel@tonic-gate prg.phys_addr = base_addr + prg.offset; 9707851eb82Sschwartz rval = pcitool_access(pci_p, prg.phys_addr, 9717c478bd9Sstevel@tonic-gate max_addr, &prg.data, size, write_flag, 9727851eb82Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status); 9737c478bd9Sstevel@tonic-gate } 9747851eb82Sschwartz 9757c478bd9Sstevel@tonic-gate done_reg: 9762917a9c9Sschwartz prg.drvr_version = PCITOOL_VERSION; 9777c478bd9Sstevel@tonic-gate if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 9787c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 9797c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n"); 9807c478bd9Sstevel@tonic-gate rval = EFAULT; 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate return (rval); 9837c478bd9Sstevel@tonic-gate } 9847851eb82Sschwartz 9857851eb82Sschwartz int 9867851eb82Sschwartz pcitool_init(dev_info_t *dip) 9877851eb82Sschwartz { 9887851eb82Sschwartz int instance = ddi_get_instance(dip); 9897851eb82Sschwartz 9907851eb82Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 9917851eb82Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 9927851eb82Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) 9937851eb82Sschwartz return (DDI_FAILURE); 9947851eb82Sschwartz 9957851eb82Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 9967851eb82Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 9977851eb82Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 9987851eb82Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 9997851eb82Sschwartz return (DDI_FAILURE); 10007851eb82Sschwartz } 10017851eb82Sschwartz 10027851eb82Sschwartz return (DDI_SUCCESS); 10037851eb82Sschwartz } 10047851eb82Sschwartz 10057851eb82Sschwartz void 10067851eb82Sschwartz pcitool_uninit(dev_info_t *dip) 10077851eb82Sschwartz { 10087851eb82Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 10097851eb82Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 10107851eb82Sschwartz } 1011