xref: /illumos-gate/usr/src/uts/sun4u/io/pci/pci_tools.c (revision 09b1eac2)
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
pcitool_phys_peek(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)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
pcitool_phys_poke(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)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
pcitool_intr_info(dev_info_t * dip,void * arg,int mode)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
pcitool_get_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)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
pcitool_set_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)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;
4242917a9c9Sschwartz 
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;
4297c478bd9Sstevel@tonic-gate 
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;
477*09b1eac2SEvan Yan 		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
pcitool_intr_admn(dev_t dev,void * arg,int cmd,int mode)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
pcitool_access(pci_t * pci_p,uint64_t phys_addr,uint64_t max_addr,uint64_t * data,uint8_t size,boolean_t write,boolean_t endian,uint32_t * pcitool_status)5567851eb82Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
5577851eb82Sschwartz 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
5587851eb82Sschwartz 	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
pcitool_bus_reg_ops(dev_t dev,void * arg,int cmd,int mode)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 
6417851eb82Sschwartz 	/* Read data from userland. */
6427851eb82Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
6437851eb82Sschwartz 	    DDI_SUCCESS) {
6447851eb82Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
6457851eb82Sschwartz 		return (EFAULT);
6467851eb82Sschwartz 	}
6477c478bd9Sstevel@tonic-gate 
6487851eb82Sschwartz 	/* Read reg property which contains starting addr and size of banks. */
6497851eb82Sschwartz 	if (ddi_prop_lookup_int_array(
6507851eb82Sschwartz 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6517851eb82Sschwartz 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
6527851eb82Sschwartz 		if (((reglen * sizeof (int)) %
6537851eb82Sschwartz 		    sizeof (pci_nexus_regspec_t)) != 0) {
6547851eb82Sschwartz 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
6557851eb82Sschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
6567851eb82Sschwartz 			rval = EIO;
6577c478bd9Sstevel@tonic-gate 			goto done;
6587c478bd9Sstevel@tonic-gate 		}
6597851eb82Sschwartz 	}
6607c478bd9Sstevel@tonic-gate 
6617851eb82Sschwartz 	/* Bounds check the bank number. */
6627851eb82Sschwartz 	if (prg.barnum >=
6637851eb82Sschwartz 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
6647851eb82Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
6657851eb82Sschwartz 		rval = EINVAL;
6667851eb82Sschwartz 		goto done;
6677851eb82Sschwartz 	}
6687c478bd9Sstevel@tonic-gate 
6697851eb82Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
6707851eb82Sschwartz 	base_addr = pci_rp[prg.barnum].phys_addr;
6717851eb82Sschwartz 	max_addr = base_addr + pci_rp[prg.barnum].size;
6727851eb82Sschwartz 	prg.phys_addr = base_addr + prg.offset;
6737c478bd9Sstevel@tonic-gate 
6747851eb82Sschwartz 	DEBUG4(DBG_TOOLS, dip,
6757851eb82Sschwartz 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
6767851eb82Sschwartz 	    "addr:0x%llx, max_addr:0x%llx\n",
6777851eb82Sschwartz 	    base_addr, prg.offset, prg.phys_addr, max_addr);
6787c478bd9Sstevel@tonic-gate 
6797851eb82Sschwartz 	/* Access device.  prg.status is modified. */
6807851eb82Sschwartz 	rval = pcitool_access(pci_p,
6817851eb82Sschwartz 	    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
pcitool_validate_barnum_bdf(pcitool_reg_t * prg)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
pcitool_get_bar(pci_t * pci_p,pcitool_reg_t * prg,uint64_t config_base_addr,uint64_t config_max_addr,uint64_t * bar,boolean_t * is_io_space)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
pcitool_config_request(pci_t * pci_p,pcitool_reg_t * prg,uint64_t base_addr,uint64_t max_addr,uint8_t size,boolean_t write_flag)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
pcitool_dev_reg_ops(dev_t dev,void * arg,int cmd,int mode)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 
8537851eb82Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
8547851eb82Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
8557851eb82Sschwartz 	    DDI_SUCCESS) {
8567851eb82Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
8577851eb82Sschwartz 		return (EFAULT);
8587851eb82Sschwartz 	}
8597c478bd9Sstevel@tonic-gate 
8607851eb82Sschwartz 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
8617851eb82Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
8627c478bd9Sstevel@tonic-gate 
8637851eb82Sschwartz 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
8647851eb82Sschwartz 		goto done_reg;
8657c478bd9Sstevel@tonic-gate 
8667851eb82Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
8677c478bd9Sstevel@tonic-gate 
8687851eb82Sschwartz 	/* Get config space first. */
8697851eb82Sschwartz 	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);
8717851eb82Sschwartz 	max_addr = range_prop + range_prop_size;
8727c478bd9Sstevel@tonic-gate 
8737851eb82Sschwartz 	/*
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.
8777851eb82Sschwartz 	 */
8787851eb82Sschwartz 	base_addr = range_prop +
8797851eb82Sschwartz 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
8807851eb82Sschwartz 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
8817851eb82Sschwartz 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
8827c478bd9Sstevel@tonic-gate 
8837851eb82Sschwartz 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
8847851eb82Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
8857851eb82Sschwartz 		rval = EINVAL;
8867851eb82Sschwartz 		goto done_reg;
8877851eb82Sschwartz 	}
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 
8947851eb82Sschwartz 	/* Proper config space desired. */
8957851eb82Sschwartz 	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)
9047851eb82Sschwartz 			goto done_reg;
9057c478bd9Sstevel@tonic-gate 
9067851eb82Sschwartz 		/* IO space. */
9077851eb82Sschwartz 		if (is_io_space) {
9087c478bd9Sstevel@tonic-gate 
9097851eb82Sschwartz 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
9107c478bd9Sstevel@tonic-gate 
9117851eb82Sschwartz 			/* Reposition to focus on IO space. */
9127851eb82Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
9137851eb82Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
9147851eb82Sschwartz 			    PCI_IO_RANGE_BANK);
9157c478bd9Sstevel@tonic-gate 
9167851eb82Sschwartz 		/* 64 bit memory space. */
9177851eb82Sschwartz 		} else if ((bar >> 32) != 0) {
9187851eb82Sschwartz 
9197851eb82Sschwartz 			DEBUG1(DBG_TOOLS, dip,
9207851eb82Sschwartz 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
9217851eb82Sschwartz 
9227851eb82Sschwartz 			/* Reposition to MEM64 range space. */
9237851eb82Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp,
9247851eb82Sschwartz 			    PCI_MEM64_RANGE_BANK);
9257851eb82Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
9267851eb82Sschwartz 			    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");
9317c478bd9Sstevel@tonic-gate 
9327851eb82Sschwartz 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9337851eb82Sschwartz 
9347851eb82Sschwartz 				DEBUG0(DBG_TOOLS, dip,
9357851eb82Sschwartz 				    "Additional ROM checking\n");
9367851eb82Sschwartz 
9377851eb82Sschwartz 				/* Can't write to ROM */
9387851eb82Sschwartz 				if (write_flag) {
9397851eb82Sschwartz 					prg.status = PCITOOL_ROM_WRITE;
9407c478bd9Sstevel@tonic-gate 					rval = EIO;
9417c478bd9Sstevel@tonic-gate 					goto done_reg;
9427c478bd9Sstevel@tonic-gate 
9437851eb82Sschwartz 				/* ROM disabled for reading */
9447851eb82Sschwartz 				} else if (!(bar & 0x00000001)) {
9457851eb82Sschwartz 					prg.status = PCITOOL_ROM_DISABLED;
9467851eb82Sschwartz 					rval = EIO;
9477c478bd9Sstevel@tonic-gate 					goto done_reg;
9487c478bd9Sstevel@tonic-gate 				}
9497851eb82Sschwartz 			}
9507c478bd9Sstevel@tonic-gate 
9517851eb82Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
9527851eb82Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
9537851eb82Sschwartz 			    PCI_MEM_RANGE_BANK);
9547851eb82Sschwartz 		}
9557c478bd9Sstevel@tonic-gate 
9567851eb82Sschwartz 		/* Common code for all IO/MEM range spaces. */
9577851eb82Sschwartz 		max_addr = range_prop + range_prop_size;
9587851eb82Sschwartz 		base_addr = range_prop + bar;
9597c478bd9Sstevel@tonic-gate 
9607851eb82Sschwartz 		DEBUG3(DBG_TOOLS, dip,
9617851eb82Sschwartz 		    "addr portion of bar is 0x%llx, base=0x%llx, "
9627851eb82Sschwartz 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
9637c478bd9Sstevel@tonic-gate 
9647851eb82Sschwartz 		/*
9657851eb82Sschwartz 		 * Use offset provided by caller to index into
9667851eb82Sschwartz 		 * desired space, then access.
9677851eb82Sschwartz 		 * Note that prg.status is modified on error.
9687851eb82Sschwartz 		 */
9697851eb82Sschwartz 		prg.phys_addr = base_addr + prg.offset;
9707851eb82Sschwartz 		rval = pcitool_access(pci_p, prg.phys_addr,
9717851eb82Sschwartz 		    max_addr, &prg.data, size, write_flag,
9727851eb82Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
9737851eb82Sschwartz 	}
9747c478bd9Sstevel@tonic-gate 
9757851eb82Sschwartz done_reg:
9762917a9c9Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
9777851eb82Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
9787851eb82Sschwartz 	    DDI_SUCCESS) {
9797851eb82Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
9807851eb82Sschwartz 		rval = EFAULT;
9817851eb82Sschwartz 	}
9827851eb82Sschwartz 	return (rval);
9837851eb82Sschwartz }
9847c478bd9Sstevel@tonic-gate 
9857851eb82Sschwartz int
pcitool_init(dev_info_t * dip)9867851eb82Sschwartz pcitool_init(dev_info_t *dip)
9877851eb82Sschwartz {
9887851eb82Sschwartz 	int instance = ddi_get_instance(dip);
9897c478bd9Sstevel@tonic-gate 
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);
9947c478bd9Sstevel@tonic-gate 
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 	}
10017c478bd9Sstevel@tonic-gate 
10027851eb82Sschwartz 	return (DDI_SUCCESS);
10037851eb82Sschwartz }
10047c478bd9Sstevel@tonic-gate 
10057851eb82Sschwartz void
pcitool_uninit(dev_info_t * dip)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);
10107c478bd9Sstevel@tonic-gate }
1011