xref: /onnv-gate/usr/src/uts/sun4u/io/pci/pci_tools.c (revision 4397:8359c43a28c3)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*4397Sschwartz  * Common Development and Distribution License (the "License").
6*4397Sschwartz  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*4397Sschwartz  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
28117Sschwartz #include <sys/stat.h>
29117Sschwartz #include <sys/sunddi.h>
30117Sschwartz #include <sys/param.h>
31117Sschwartz 
320Sstevel@tonic-gate #include <sys/sysmacros.h>
330Sstevel@tonic-gate #include <sys/machsystm.h>
340Sstevel@tonic-gate #include <sys/promif.h>
350Sstevel@tonic-gate #include <sys/cpuvar.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
380Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #include <sys/pci_tools.h>
41777Sschwartz #include <sys/pci/pci_tools_ext.h>
42777Sschwartz 
43777Sschwartz /*
44777Sschwartz  * Number of interrupts supported per PCI bus.
45777Sschwartz  */
46777Sschwartz #define	PCI_MAX_INO		0x3f
47777Sschwartz 
48777Sschwartz /*
49777Sschwartz  * PCI Space definitions.
50777Sschwartz  */
51777Sschwartz #define	PCI_CONFIG_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
52777Sschwartz #define	PCI_IO_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_IO))
53777Sschwartz #define	PCI_MEM_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
54777Sschwartz #define	PCI_MEM64_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
550Sstevel@tonic-gate 
560Sstevel@tonic-gate /*
570Sstevel@tonic-gate  * Extract 64 bit parent or size values from 32 bit cells of
580Sstevel@tonic-gate  * pci_ranges_t property.
590Sstevel@tonic-gate  *
600Sstevel@tonic-gate  * Only bits 42-32 are relevant in parent_high.
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP(ranges, bank) \
630Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
640Sstevel@tonic-gate 	ranges[bank].parent_low)
650Sstevel@tonic-gate 
660Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
670Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].size_high)) << 32) | \
680Sstevel@tonic-gate 	ranges[bank].size_low)
690Sstevel@tonic-gate 
70117Sschwartz #define	PCI_BAR_OFFSET(x)	(pci_bars[x.barnum])
71117Sschwartz 
720Sstevel@tonic-gate /* Big and little endian as boolean values. */
730Sstevel@tonic-gate #define	BE B_TRUE
740Sstevel@tonic-gate #define	LE B_FALSE
750Sstevel@tonic-gate 
760Sstevel@tonic-gate #define	SUCCESS	0
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
790Sstevel@tonic-gate typedef union {
800Sstevel@tonic-gate 	uint64_t u64;
810Sstevel@tonic-gate 	uint32_t u32;
820Sstevel@tonic-gate 	uint16_t u16;
830Sstevel@tonic-gate 	uint8_t u8;
840Sstevel@tonic-gate } peek_poke_value_t;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate /*
870Sstevel@tonic-gate  * Offsets of BARS in config space.  First entry of 0 means config space.
880Sstevel@tonic-gate  * Entries here correlate to pcitool_bars_t enumerated type.
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate static uint8_t pci_bars[] = {
910Sstevel@tonic-gate 	0x0,
920Sstevel@tonic-gate 	PCI_CONF_BASE0,
930Sstevel@tonic-gate 	PCI_CONF_BASE1,
940Sstevel@tonic-gate 	PCI_CONF_BASE2,
950Sstevel@tonic-gate 	PCI_CONF_BASE3,
960Sstevel@tonic-gate 	PCI_CONF_BASE4,
970Sstevel@tonic-gate 	PCI_CONF_BASE5,
980Sstevel@tonic-gate 	PCI_CONF_ROM
990Sstevel@tonic-gate };
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate /*LINTLIBRARY*/
1020Sstevel@tonic-gate 
103117Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
104117Sschwartz     uint64_t paddr, uint64_t *value_p);
105117Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
106117Sschwartz     uint64_t paddr, uint64_t value);
107117Sschwartz static boolean_t pcitool_validate_cpuid(uint32_t cpu_id);
108117Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
109117Sschwartz     uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
110117Sschwartz     uint32_t *pcitool_status);
111117Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
112117Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
113117Sschwartz     uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
114117Sschwartz     boolean_t *is_io_space);
115117Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
116117Sschwartz     uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
117117Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
118117Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
119117Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
1220Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate /*
1250Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_peek
1260Sstevel@tonic-gate  *
1270Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1280Sstevel@tonic-gate  * Size is 1, 2, 4 or 8 bytes.
1290Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1300Sstevel@tonic-gate  * value_p is where the value is returned.
1310Sstevel@tonic-gate  */
1320Sstevel@tonic-gate static int
133117Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
134117Sschwartz 	uint64_t paddr, uint64_t *value_p)
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate 	on_trap_data_t otd;
1370Sstevel@tonic-gate 	int err = DDI_SUCCESS;
138117Sschwartz 	peek_poke_value_t peek_value;
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	/* Set up trap handling to make the access safe. */
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	/*
1470Sstevel@tonic-gate 	 * on_trap works like setjmp.
1480Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
1490Sstevel@tonic-gate 	 * but to call peek_fault instead.
1500Sstevel@tonic-gate 	 * Call pci_do_phys_peek after trap handling is setup.
1510Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
1520Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
1550Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&peek_fault;
1560Sstevel@tonic-gate 		err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
1570Sstevel@tonic-gate 	} else {
1580Sstevel@tonic-gate 		err = DDI_FAILURE;
1590Sstevel@tonic-gate 	}
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
1620Sstevel@tonic-gate 	no_trap();
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	if (err != DDI_FAILURE) {
1650Sstevel@tonic-gate 		switch (size) {
1660Sstevel@tonic-gate 		case 8:
1670Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u64;
1680Sstevel@tonic-gate 			break;
1690Sstevel@tonic-gate 		case 4:
1700Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u32;
1710Sstevel@tonic-gate 			break;
1720Sstevel@tonic-gate 		case 2:
1730Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u16;
1740Sstevel@tonic-gate 			break;
1750Sstevel@tonic-gate 		case 1:
1760Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u8;
1770Sstevel@tonic-gate 			break;
1780Sstevel@tonic-gate 		default:
1790Sstevel@tonic-gate 			err = DDI_FAILURE;
1800Sstevel@tonic-gate 		}
1810Sstevel@tonic-gate 	}
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	return (err);
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate /*
1870Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_poke
1880Sstevel@tonic-gate  *
1890Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1900Sstevel@tonic-gate  * Size is 1,2,4 or 8 bytes.
1910Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1920Sstevel@tonic-gate  * value contains the value to be written.
1930Sstevel@tonic-gate  */
1940Sstevel@tonic-gate static int
195117Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
196117Sschwartz 	uint64_t paddr, uint64_t value)
1970Sstevel@tonic-gate {
1980Sstevel@tonic-gate 	on_trap_data_t otd;
1990Sstevel@tonic-gate 	int err = DDI_SUCCESS;
2000Sstevel@tonic-gate 	peek_poke_value_t poke_value;
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	switch (size) {
2050Sstevel@tonic-gate 	case 8:
2060Sstevel@tonic-gate 		poke_value.u64 = value;
2070Sstevel@tonic-gate 		break;
2080Sstevel@tonic-gate 	case 4:
2090Sstevel@tonic-gate 		poke_value.u32 = (uint32_t)value;
2100Sstevel@tonic-gate 		break;
2110Sstevel@tonic-gate 	case 2:
2120Sstevel@tonic-gate 		poke_value.u16 = (uint16_t)value;
2130Sstevel@tonic-gate 		break;
2140Sstevel@tonic-gate 	case 1:
2150Sstevel@tonic-gate 		poke_value.u8 = (uint8_t)value;
2160Sstevel@tonic-gate 		break;
2170Sstevel@tonic-gate 	default:
2180Sstevel@tonic-gate 		return (DDI_FAILURE);
2190Sstevel@tonic-gate 	}
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	mutex_enter(&pbm_p->pbm_pokefault_mutex);
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/*
2260Sstevel@tonic-gate 	 * on_trap works like setjmp.
2270Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
2280Sstevel@tonic-gate 	 * but to call poke_fault instead.
2290Sstevel@tonic-gate 	 * Call pci_do_phys_poke after trap handling is setup.
2300Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
2310Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
2320Sstevel@tonic-gate 	 */
2330Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
2340Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&poke_fault;
2350Sstevel@tonic-gate 		err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
2360Sstevel@tonic-gate 	}
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	/* Let the dust settle and errors occur if they will. */
2390Sstevel@tonic-gate 	pbm_clear_error(pbm_p);
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	/* Check for an error. */
2420Sstevel@tonic-gate 	if (otd.ot_trap == OT_DATA_ACCESS) {
2430Sstevel@tonic-gate 		err = DDI_FAILURE;
2440Sstevel@tonic-gate 	}
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
2470Sstevel@tonic-gate 	mutex_exit(&pbm_p->pbm_pokefault_mutex);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	no_trap();
2500Sstevel@tonic-gate 	return (err);
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate  * Validate the cpu_id passed in.
256117Sschwartz  * A value of B_TRUE will be returned for success.
2570Sstevel@tonic-gate  */
258117Sschwartz static boolean_t
259117Sschwartz pcitool_validate_cpuid(uint32_t cpuid)
2600Sstevel@tonic-gate {
261117Sschwartz 	extern const int _ncpu;
262117Sschwartz 	extern cpu_t	*cpu[];
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	ASSERT(mutex_owned(&cpu_lock));
2650Sstevel@tonic-gate 
266117Sschwartz 	return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid])));
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 
270*4397Sschwartz /*ARGSUSED*/
2710Sstevel@tonic-gate static int
272*4397Sschwartz pcitool_intr_info(dev_info_t *dip, void *arg, int mode)
2730Sstevel@tonic-gate {
274*4397Sschwartz 	pcitool_intr_info_t intr_info;
275*4397Sschwartz 	int rval = SUCCESS;
2760Sstevel@tonic-gate 
277*4397Sschwartz 	/* If we need user_version, and to ret same user version as passed in */
278*4397Sschwartz 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
2790Sstevel@tonic-gate 	    DDI_SUCCESS) {
2800Sstevel@tonic-gate 		return (EFAULT);
2810Sstevel@tonic-gate 	}
282*4397Sschwartz 
283*4397Sschwartz 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
284*4397Sschwartz 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
285*4397Sschwartz 	intr_info.num_intr = PCI_MAX_INO;
286*4397Sschwartz 
287*4397Sschwartz 	intr_info.drvr_version = PCITOOL_VERSION;
288*4397Sschwartz 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
289*4397Sschwartz 	    DDI_SUCCESS) {
290*4397Sschwartz 		rval = EFAULT;
291*4397Sschwartz 	}
292*4397Sschwartz 
293*4397Sschwartz 	return (rval);
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate /*
2980Sstevel@tonic-gate  * Get interrupt information for a given ino.
2990Sstevel@tonic-gate  * Returns info only for inos mapped to devices.
3000Sstevel@tonic-gate  *
3010Sstevel@tonic-gate  * Returned info is valid only when iget.num_devs is returned > 0.
3020Sstevel@tonic-gate  * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
3030Sstevel@tonic-gate  */
3040Sstevel@tonic-gate /*ARGSUSED*/
3050Sstevel@tonic-gate static int
3060Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate 	/* Array part isn't used here, but oh well... */
3090Sstevel@tonic-gate 	pcitool_intr_get_t partial_iget;
3100Sstevel@tonic-gate 	pcitool_intr_get_t *iget = &partial_iget;
3110Sstevel@tonic-gate 	size_t	iget_kmem_alloc_size = 0;
3120Sstevel@tonic-gate 	ib_t *ib_p = pci_p->pci_ib_p;
3130Sstevel@tonic-gate 	volatile uint64_t *imregp;
3140Sstevel@tonic-gate 	uint64_t imregval;
3150Sstevel@tonic-gate 	uint32_t ino;
3160Sstevel@tonic-gate 	uint8_t num_devs_ret;
3170Sstevel@tonic-gate 	int copyout_rval;
3180Sstevel@tonic-gate 	int rval = SUCCESS;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	/* Read in just the header part, no array section. */
3210Sstevel@tonic-gate 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
3220Sstevel@tonic-gate 	    DDI_SUCCESS) {
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 		return (EFAULT);
3250Sstevel@tonic-gate 	}
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	ino = partial_iget.ino;
3280Sstevel@tonic-gate 	num_devs_ret = partial_iget.num_devs_ret;
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	/* Validate argument. */
3310Sstevel@tonic-gate 	if (ino > PCI_MAX_INO) {
3320Sstevel@tonic-gate 		partial_iget.status = PCITOOL_INVALID_INO;
3330Sstevel@tonic-gate 		partial_iget.num_devs_ret = 0;
3340Sstevel@tonic-gate 		rval = EINVAL;
3350Sstevel@tonic-gate 		goto done_get_intr;
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	/* Caller wants device information returned. */
3390Sstevel@tonic-gate 	if (num_devs_ret > 0) {
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 		/*
3420Sstevel@tonic-gate 		 * Allocate room.
3430Sstevel@tonic-gate 		 * Note if num_devs_ret == 0 iget remains pointing to
3440Sstevel@tonic-gate 		 * partial_iget.
3450Sstevel@tonic-gate 		 */
3460Sstevel@tonic-gate 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
3470Sstevel@tonic-gate 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 		/* Read in whole structure to verify there's room. */
3500Sstevel@tonic-gate 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
3510Sstevel@tonic-gate 		    SUCCESS) {
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 			/* Be consistent and just return EFAULT here. */
3540Sstevel@tonic-gate 			kmem_free(iget, iget_kmem_alloc_size);
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 			return (EFAULT);
3570Sstevel@tonic-gate 		}
3580Sstevel@tonic-gate 	}
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
3610Sstevel@tonic-gate 	iget->ino = ino;
3620Sstevel@tonic-gate 	iget->num_devs_ret = num_devs_ret;
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	imregp = ib_intr_map_reg_addr(ib_p, ino);
3650Sstevel@tonic-gate 	imregval = *imregp;
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	/*
3680Sstevel@tonic-gate 	 * Read "valid" bit.  If set, interrupts are enabled.
3690Sstevel@tonic-gate 	 * This bit happens to be the same on Fire and Tomatillo.
3700Sstevel@tonic-gate 	 */
3710Sstevel@tonic-gate 	if (imregval & COMMON_INTR_MAP_REG_VALID) {
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 		/*
3740Sstevel@tonic-gate 		 * The following looks up the ib_ino_info and returns
3750Sstevel@tonic-gate 		 * info of devices mapped to this ino.
3760Sstevel@tonic-gate 		 */
3770Sstevel@tonic-gate 		iget->num_devs = ib_get_ino_devs(
3780Sstevel@tonic-gate 		    ib_p, ino, &iget->num_devs_ret, iget->dev);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 		/*
3810Sstevel@tonic-gate 		 * Consider only inos mapped to devices (as opposed to
3820Sstevel@tonic-gate 		 * inos mapped to the bridge itself.
3830Sstevel@tonic-gate 		 */
3840Sstevel@tonic-gate 		if (iget->num_devs > 0) {
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 			/*
3870Sstevel@tonic-gate 			 * These 2 items are platform specific,
3880Sstevel@tonic-gate 			 * extracted from the bridge.
3890Sstevel@tonic-gate 			 */
3900Sstevel@tonic-gate 			iget->ctlr = 0;
391117Sschwartz 			iget->cpu_id = ib_map_reg_get_cpu(imregval);
3920Sstevel@tonic-gate 		}
3930Sstevel@tonic-gate 	}
3940Sstevel@tonic-gate done_get_intr:
395*4397Sschwartz 	iget->drvr_version = PCITOOL_VERSION;
3960Sstevel@tonic-gate 	copyout_rval = ddi_copyout(iget, arg,
3970Sstevel@tonic-gate 	    PCITOOL_IGET_SIZE(num_devs_ret), mode);
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	if (iget_kmem_alloc_size > 0) {
4000Sstevel@tonic-gate 		kmem_free(iget, iget_kmem_alloc_size);
4010Sstevel@tonic-gate 	}
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	if (copyout_rval != DDI_SUCCESS) {
4040Sstevel@tonic-gate 		rval = EFAULT;
4050Sstevel@tonic-gate 	}
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	return (rval);
4080Sstevel@tonic-gate }
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate /*
4110Sstevel@tonic-gate  * Associate a new CPU with a given ino.
4120Sstevel@tonic-gate  *
4130Sstevel@tonic-gate  * Operate only on inos which are already mapped to devices.
4140Sstevel@tonic-gate  */
4150Sstevel@tonic-gate static int
4160Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
4170Sstevel@tonic-gate {
418117Sschwartz 	ib_t *ib_p = pci_p->pci_ib_p;
419117Sschwartz 	int rval = SUCCESS;
420117Sschwartz 
4210Sstevel@tonic-gate 	uint8_t zero = 0;
4220Sstevel@tonic-gate 	pcitool_intr_set_t iset;
4230Sstevel@tonic-gate 	uint32_t old_cpu_id;
4240Sstevel@tonic-gate 	hrtime_t start_time;
4250Sstevel@tonic-gate 	uint64_t imregval;
4260Sstevel@tonic-gate 	uint64_t new_imregval;
4270Sstevel@tonic-gate 	volatile uint64_t *imregp;
4280Sstevel@tonic-gate 	volatile uint64_t *idregp;
429*4397Sschwartz 	size_t copyinout_size;
4300Sstevel@tonic-gate 
431*4397Sschwartz 	bzero(&iset, sizeof (pcitool_intr_set_t));
432*4397Sschwartz 
433*4397Sschwartz 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
434*4397Sschwartz 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
435*4397Sschwartz 
436*4397Sschwartz 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4370Sstevel@tonic-gate 		return (EFAULT);
4380Sstevel@tonic-gate 
439*4397Sschwartz 	switch (iset.user_version) {
440*4397Sschwartz 	case PCITOOL_V1:
441*4397Sschwartz 		break;
442*4397Sschwartz 
443*4397Sschwartz 	case PCITOOL_V2:
444*4397Sschwartz 		copyinout_size = sizeof (pcitool_intr_set_t);
445*4397Sschwartz 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
446*4397Sschwartz 			return (EFAULT);
447*4397Sschwartz 		break;
448*4397Sschwartz 
449*4397Sschwartz 	default:
450*4397Sschwartz 		iset.status = PCITOOL_OUT_OF_RANGE;
451*4397Sschwartz 		rval = ENOTSUP;
452*4397Sschwartz 		goto done_set_intr;
453*4397Sschwartz 	}
454*4397Sschwartz 
455*4397Sschwartz 	if (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP) {
456*4397Sschwartz 		iset.status = PCITOOL_IO_ERROR;
457*4397Sschwartz 		rval = ENOTSUP;
458*4397Sschwartz 		goto done_set_intr;
459*4397Sschwartz 	}
460*4397Sschwartz 
461117Sschwartz 	/* Validate input argument and that ino given belongs to a device. */
462117Sschwartz 	if ((iset.ino > PCI_MAX_INO) ||
463117Sschwartz 	    (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
4640Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4650Sstevel@tonic-gate 		rval = EINVAL;
4660Sstevel@tonic-gate 		goto done_set_intr;
4670Sstevel@tonic-gate 	}
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 	imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
4700Sstevel@tonic-gate 	idregp = IB_INO_INTR_STATE_REG(ib_p, iset.ino);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	DEBUG4(DBG_TOOLS, dip, "set_intr: cpu:%d, ino:0x%x, mapreg @ "
4730Sstevel@tonic-gate 	    "0x%llx, intr_stat @ 0x%llx\n",
4740Sstevel@tonic-gate 	    iset.cpu_id, iset.ino, imregp, idregp);
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 	/* Save original mapreg value. */
4770Sstevel@tonic-gate 	imregval = *imregp;
4780Sstevel@tonic-gate 	DEBUG1(DBG_TOOLS, dip, "orig mapreg value: 0x%llx\n", imregval);
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	/* Is this request a noop? */
481117Sschwartz 	if ((old_cpu_id = ib_map_reg_get_cpu(imregval)) == iset.cpu_id) {
4820Sstevel@tonic-gate 		iset.status = PCITOOL_SUCCESS;
4830Sstevel@tonic-gate 		goto done_set_intr;
4840Sstevel@tonic-gate 	}
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	/* Operate only on inos which are already enabled. */
4870Sstevel@tonic-gate 	if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
4880Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4890Sstevel@tonic-gate 		rval = EINVAL;
4900Sstevel@tonic-gate 		goto done_set_intr;
4910Sstevel@tonic-gate 	}
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	/* Clear the interrupt valid/enable bit for particular ino. */
4940Sstevel@tonic-gate 	DEBUG0(DBG_TOOLS, dip, "Clearing intr_enabled...\n");
4950Sstevel@tonic-gate 	*imregp = imregval & ~COMMON_INTR_MAP_REG_VALID;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	/* Wait until there are no more pending interrupts. */
4980Sstevel@tonic-gate 	start_time = gethrtime();
4990Sstevel@tonic-gate 
500117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "About to check for pending interrupts...\n");
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	while (IB_INO_INTR_PENDING(idregp, iset.ino)) {
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Waiting for pending ints to clear\n");
505117Sschwartz 		if ((gethrtime() - start_time) < pci_intrpend_timeout)
5060Sstevel@tonic-gate 			continue;
5070Sstevel@tonic-gate 
508117Sschwartz 		else {	/* Timed out waiting. */
5090Sstevel@tonic-gate 			iset.status = PCITOOL_PENDING_INTRTIMEOUT;
5100Sstevel@tonic-gate 			rval = ETIME;
5110Sstevel@tonic-gate 			goto done_set_intr;
5120Sstevel@tonic-gate 		}
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	new_imregval = *imregp;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	DEBUG1(DBG_TOOLS, dip,
5180Sstevel@tonic-gate 	    "after disabling intr, mapreg value: 0x%llx\n", new_imregval);
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	/*
5210Sstevel@tonic-gate 	 * Get lock, validate cpu and write new mapreg value.
5220Sstevel@tonic-gate 	 * Return original cpu value to caller via iset.cpu_id.
5230Sstevel@tonic-gate 	 */
5240Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
525117Sschwartz 	if (pcitool_validate_cpuid(iset.cpu_id)) {
526117Sschwartz 
527117Sschwartz 		/* Prepare new mapreg value with intr enabled and new cpu_id. */
528117Sschwartz 		new_imregval &=
529117Sschwartz 		    COMMON_INTR_MAP_REG_IGN | COMMON_INTR_MAP_REG_INO;
530117Sschwartz 		new_imregval = ib_get_map_reg(new_imregval, iset.cpu_id);
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 		DEBUG1(DBG_TOOLS, dip, "Writing new mapreg value:0x%llx\n",
5330Sstevel@tonic-gate 		    new_imregval);
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 		*imregp = new_imregval;
536117Sschwartz 
537117Sschwartz 		ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino);
538117Sschwartz 
5390Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
540117Sschwartz 
5410Sstevel@tonic-gate 		iset.cpu_id = old_cpu_id;
5420Sstevel@tonic-gate 		iset.status = PCITOOL_SUCCESS;
5430Sstevel@tonic-gate 
544117Sschwartz 	} else {	/* Invalid cpu.  Restore original register image. */
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip,
5470Sstevel@tonic-gate 		    "Invalid cpuid: writing orig mapreg value\n");
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 		*imregp = imregval;
5500Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
5510Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_CPUID;
5520Sstevel@tonic-gate 		rval = EINVAL;
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate done_set_intr:
555*4397Sschwartz 	iset.drvr_version = PCITOOL_VERSION;
556*4397Sschwartz 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
5570Sstevel@tonic-gate 		rval = EFAULT;
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	return (rval);
5600Sstevel@tonic-gate }
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */
5640Sstevel@tonic-gate int
5650Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
5660Sstevel@tonic-gate {
5670Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
5680Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
5690Sstevel@tonic-gate 	int		rval = SUCCESS;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	switch (cmd) {
5720Sstevel@tonic-gate 
573*4397Sschwartz 	/* Get system interrupt information. */
574*4397Sschwartz 	case PCITOOL_SYSTEM_INTR_INFO:
575*4397Sschwartz 		rval = pcitool_intr_info(dip, arg, mode);
5760Sstevel@tonic-gate 		break;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	/* Get interrupt information for a given ino. */
5790Sstevel@tonic-gate 	case PCITOOL_DEVICE_GET_INTR:
5800Sstevel@tonic-gate 		rval = pcitool_get_intr(dip, arg, mode, pci_p);
5810Sstevel@tonic-gate 		break;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	/* Associate a new CPU with a given ino. */
5840Sstevel@tonic-gate 	case PCITOOL_DEVICE_SET_INTR:
5850Sstevel@tonic-gate 		rval = pcitool_set_intr(dip, arg, mode, pci_p);
5860Sstevel@tonic-gate 		break;
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	default:
5890Sstevel@tonic-gate 		rval = ENOTTY;
5900Sstevel@tonic-gate 	}
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	return (rval);
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate /*
597117Sschwartz  * Wrapper around pcitool_phys_peek/poke.
5980Sstevel@tonic-gate  *
599117Sschwartz  * Validates arguments and calls pcitool_phys_peek/poke appropriately.
6000Sstevel@tonic-gate  *
6010Sstevel@tonic-gate  * Dip is of the nexus,
6020Sstevel@tonic-gate  * phys_addr is the address to write in physical space,
6030Sstevel@tonic-gate  * max_addr is the upper bound on the physical space used for bounds checking,
6040Sstevel@tonic-gate  * pcitool_status returns more detailed status in addition to a more generic
6050Sstevel@tonic-gate  * errno-style function return value.
6060Sstevel@tonic-gate  * other args are self-explanatory.
6070Sstevel@tonic-gate  */
6080Sstevel@tonic-gate static int
609117Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
610117Sschwartz 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
611117Sschwartz 	uint32_t *pcitool_status)
6120Sstevel@tonic-gate {
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	int rval = SUCCESS;
6150Sstevel@tonic-gate 	dev_info_t *dip = pci_p->pci_dip;
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 	/* Upper bounds checking. */
6180Sstevel@tonic-gate 	if (phys_addr > max_addr) {
6190Sstevel@tonic-gate 		DEBUG2(DBG_TOOLS, dip,
6200Sstevel@tonic-gate 		    "Phys addr 0x%llx out of range (max 0x%llx).\n",
6210Sstevel@tonic-gate 		    phys_addr, max_addr);
6220Sstevel@tonic-gate 		*pcitool_status = PCITOOL_INVALID_ADDRESS;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 		rval = EINVAL;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	/* Alignment checking. */
6270Sstevel@tonic-gate 	} else if (!IS_P2ALIGNED(phys_addr, size)) {
6280Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
6290Sstevel@tonic-gate 		*pcitool_status = PCITOOL_NOT_ALIGNED;
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 		rval = EINVAL;
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	/* Made it through checks.  Do the access. */
6340Sstevel@tonic-gate 	} else if (write) {
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
637117Sschwartz 		    "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
6380Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
6390Sstevel@tonic-gate 
640117Sschwartz 		if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
641117Sschwartz 		    *data) != DDI_SUCCESS) {
6420Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
643117Sschwartz 			    "%d byte %s pcitool_phys_poke at addr "
6440Sstevel@tonic-gate 			    "0x%llx failed\n",
6450Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
6460Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 			rval = EFAULT;
6490Sstevel@tonic-gate 		}
6500Sstevel@tonic-gate 
651117Sschwartz 	} else {	/* Read */
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
654117Sschwartz 		    "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
6550Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
6560Sstevel@tonic-gate 
657117Sschwartz 		if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
658117Sschwartz 		    data) != DDI_SUCCESS) {
6590Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
660117Sschwartz 			    "%d byte %s pcitool_phys_peek at addr "
6610Sstevel@tonic-gate 			    "0x%llx failed\n",
6620Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
6630Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 			rval = EFAULT;
6660Sstevel@tonic-gate 		}
6670Sstevel@tonic-gate 	}
6680Sstevel@tonic-gate 	return (rval);
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate /*
6720Sstevel@tonic-gate  * Perform register accesses on the nexus device itself.
6730Sstevel@tonic-gate  */
6740Sstevel@tonic-gate int
6750Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	pci_t			*pci_p = DEV_TO_SOFTSTATE(dev);
6790Sstevel@tonic-gate 	dev_info_t		*dip = pci_p->pci_dip;
680117Sschwartz 	pci_nexus_regspec_t	*pci_rp = NULL;
681117Sschwartz 	boolean_t		write_flag = B_FALSE;
6820Sstevel@tonic-gate 	pcitool_reg_t		prg;
6830Sstevel@tonic-gate 	uint64_t		base_addr;
6840Sstevel@tonic-gate 	uint64_t		max_addr;
6850Sstevel@tonic-gate 	uint32_t		reglen;
6860Sstevel@tonic-gate 	uint8_t			size;
6870Sstevel@tonic-gate 	uint32_t		rval = 0;
6880Sstevel@tonic-gate 
689117Sschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
6900Sstevel@tonic-gate 		write_flag = B_TRUE;
6910Sstevel@tonic-gate 
692117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
6930Sstevel@tonic-gate 
694117Sschwartz 	/* Read data from userland. */
695117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
696117Sschwartz 	    DDI_SUCCESS) {
697117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
698117Sschwartz 		return (EFAULT);
699117Sschwartz 	}
7000Sstevel@tonic-gate 
701117Sschwartz 	/* Read reg property which contains starting addr and size of banks. */
702117Sschwartz 	if (ddi_prop_lookup_int_array(
703117Sschwartz 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
704117Sschwartz 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
705117Sschwartz 		if (((reglen * sizeof (int)) %
706117Sschwartz 		    sizeof (pci_nexus_regspec_t)) != 0) {
707117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
708117Sschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
709117Sschwartz 			rval = EIO;
7100Sstevel@tonic-gate 			goto done;
7110Sstevel@tonic-gate 		}
712117Sschwartz 	}
7130Sstevel@tonic-gate 
714117Sschwartz 	/* Bounds check the bank number. */
715117Sschwartz 	if (prg.barnum >=
716117Sschwartz 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
717117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
718117Sschwartz 		rval = EINVAL;
719117Sschwartz 		goto done;
7200Sstevel@tonic-gate 	}
7210Sstevel@tonic-gate 
722117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
723117Sschwartz 	base_addr = pci_rp[prg.barnum].phys_addr;
724117Sschwartz 	max_addr = base_addr + pci_rp[prg.barnum].size;
725117Sschwartz 	prg.phys_addr = base_addr + prg.offset;
726117Sschwartz 
727117Sschwartz 	DEBUG4(DBG_TOOLS, dip,
728117Sschwartz 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
729117Sschwartz 	    "addr:0x%llx, max_addr:0x%llx\n",
730117Sschwartz 	    base_addr, prg.offset, prg.phys_addr, max_addr);
731117Sschwartz 
732117Sschwartz 	/* Access device.  prg.status is modified. */
733117Sschwartz 	rval = pcitool_access(pci_p,
734117Sschwartz 	    prg.phys_addr, max_addr, &prg.data, size, write_flag,
735117Sschwartz 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
736117Sschwartz 
7370Sstevel@tonic-gate done:
738117Sschwartz 	if (pci_rp != NULL)
739117Sschwartz 		ddi_prop_free(pci_rp);
740117Sschwartz 
741*4397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
7420Sstevel@tonic-gate 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
7430Sstevel@tonic-gate 	    DDI_SUCCESS) {
7440Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
7450Sstevel@tonic-gate 		return (EFAULT);
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	return (rval);
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 
752117Sschwartz static int
753117Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
754117Sschwartz {
755117Sschwartz 	int rval = SUCCESS;
756117Sschwartz 
757117Sschwartz 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
758117Sschwartz 		prg->status = PCITOOL_OUT_OF_RANGE;
759117Sschwartz 		rval = EINVAL;
760117Sschwartz 
761117Sschwartz 	/* Validate address arguments of bus / dev / func */
762117Sschwartz 	} else if (((prg->bus_no &
763117Sschwartz 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
764117Sschwartz 	    ((prg->dev_no &
765117Sschwartz 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
766117Sschwartz 	    ((prg->func_no &
767117Sschwartz 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
768117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
769117Sschwartz 		rval = EINVAL;
770117Sschwartz 	}
771117Sschwartz 
772117Sschwartz 	return (rval);
773117Sschwartz }
774117Sschwartz 
775117Sschwartz static int
776117Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
777117Sschwartz 	uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
778117Sschwartz {
779117Sschwartz 
780117Sschwartz 	uint8_t bar_offset;
781117Sschwartz 	int rval;
782117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
783117Sschwartz 
784117Sschwartz 	*bar = 0;
785117Sschwartz 	*is_io_space = B_FALSE;
786117Sschwartz 
787117Sschwartz 	/*
788117Sschwartz 	 * Translate BAR number into offset of the BAR in
789117Sschwartz 	 * the device's config space.
790117Sschwartz 	 */
791117Sschwartz 	bar_offset = PCI_BAR_OFFSET((*prg));
792117Sschwartz 
793117Sschwartz 	DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
794117Sschwartz 	    prg->barnum, bar_offset);
795117Sschwartz 
796117Sschwartz 	/*
797117Sschwartz 	 * Get Bus Address Register (BAR) from config space.
798117Sschwartz 	 * bar_offset is the offset into config space of the BAR desired.
799117Sschwartz 	 * prg->status is modified on error.
800117Sschwartz 	 */
801117Sschwartz 	rval = pcitool_access(pci_p, config_base_addr + bar_offset,
802117Sschwartz 	    config_max_addr, bar,
803117Sschwartz 	    4,		/* 4 bytes. */
804117Sschwartz 	    B_FALSE,	/* Read */
805117Sschwartz 	    B_FALSE, 	/* Little endian. */
806117Sschwartz 	    &prg->status);
807117Sschwartz 	if (rval != SUCCESS)
808117Sschwartz 		return (rval);
809117Sschwartz 
810117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
811117Sschwartz 	if (!(*bar)) {
812117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
813117Sschwartz 		return (EINVAL);
814117Sschwartz 	}
815117Sschwartz 
816117Sschwartz 	/*
817117Sschwartz 	 * BAR has bits saying this space is IO space, unless
818117Sschwartz 	 * this is the ROM address register.
819117Sschwartz 	 */
820117Sschwartz 	if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
821117Sschwartz 	    (bar_offset != PCI_CONF_ROM)) {
822117Sschwartz 		*is_io_space = B_TRUE;
823117Sschwartz 		*bar &= PCI_BASE_IO_ADDR_M;
824117Sschwartz 
825117Sschwartz 	/*
826117Sschwartz 	 * BAR has bits saying this space is 64 bit memory
827117Sschwartz 	 * space, unless this is the ROM address register.
828117Sschwartz 	 *
829117Sschwartz 	 * The 64 bit address stored in two BAR cells is not necessarily
830117Sschwartz 	 * aligned on an 8-byte boundary.  Need to keep the first 4
831117Sschwartz 	 * bytes read, and do a separate read of the high 4 bytes.
832117Sschwartz 	 */
833117Sschwartz 
834117Sschwartz 	} else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
835117Sschwartz 
836117Sschwartz 		uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
837117Sschwartz 
838117Sschwartz 		/* Don't try to read past the end of BARs. */
839117Sschwartz 		if (bar_offset >= PCI_CONF_BASE5) {
840117Sschwartz 			prg->status = PCITOOL_OUT_OF_RANGE;
841117Sschwartz 			return (EIO);
842117Sschwartz 		}
843117Sschwartz 
844117Sschwartz 		/* Access device.  prg->status is modified on error. */
845117Sschwartz 		rval = pcitool_access(pci_p,
846117Sschwartz 		    config_base_addr + bar_offset + 4, config_max_addr, bar,
847117Sschwartz 		    4,		/* 4 bytes. */
848117Sschwartz 		    B_FALSE,	/* Read */
849117Sschwartz 		    B_FALSE, 	/* Little endian. */
850117Sschwartz 		    &prg->status);
851117Sschwartz 		if (rval != SUCCESS)
852117Sschwartz 			return (rval);
853117Sschwartz 
854117Sschwartz 		*bar = (*bar << 32) + low_bytes;
855117Sschwartz 	}
856117Sschwartz 
857117Sschwartz 	return (SUCCESS);
858117Sschwartz }
859117Sschwartz 
860117Sschwartz 
861117Sschwartz static int
862117Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
863117Sschwartz 	uint64_t max_addr, uint8_t size, boolean_t write_flag)
864117Sschwartz {
865117Sschwartz 	int rval;
866117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
867117Sschwartz 
868117Sschwartz 	/* Access config space and we're done. */
869117Sschwartz 	prg->phys_addr = base_addr + prg->offset;
870117Sschwartz 
871117Sschwartz 	DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
872117Sschwartz 	    "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
873117Sschwartz 	    base_addr, prg->offset, prg->phys_addr,
874117Sschwartz 	    (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
875117Sschwartz 
876117Sschwartz 	/* Access device.  pr.status is modified. */
877117Sschwartz 	rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
878117Sschwartz 	    write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
879117Sschwartz 
880117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
881117Sschwartz 
882117Sschwartz 	return (rval);
883117Sschwartz }
884117Sschwartz 
8850Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
8860Sstevel@tonic-gate int
8870Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
8880Sstevel@tonic-gate {
8890Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
8900Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
8910Sstevel@tonic-gate 	pci_ranges_t	*rp = pci_p->pci_ranges;
8920Sstevel@tonic-gate 	pcitool_reg_t	prg;
8930Sstevel@tonic-gate 	uint64_t	max_addr;
8940Sstevel@tonic-gate 	uint64_t	base_addr;
8950Sstevel@tonic-gate 	uint64_t	range_prop;
8960Sstevel@tonic-gate 	uint64_t	range_prop_size;
8970Sstevel@tonic-gate 	uint64_t	bar = 0;
8980Sstevel@tonic-gate 	int		rval = 0;
8990Sstevel@tonic-gate 	boolean_t	write_flag = B_FALSE;
900117Sschwartz 	boolean_t	is_io_space = B_FALSE;
9010Sstevel@tonic-gate 	uint8_t		size;
9020Sstevel@tonic-gate 
903117Sschwartz 	if (cmd == PCITOOL_DEVICE_SET_REG)
9040Sstevel@tonic-gate 		write_flag = B_TRUE;
9050Sstevel@tonic-gate 
906117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
907117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
908117Sschwartz 	    DDI_SUCCESS) {
909117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
910117Sschwartz 		return (EFAULT);
911117Sschwartz 	}
9120Sstevel@tonic-gate 
913117Sschwartz 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
914117Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
9150Sstevel@tonic-gate 
916117Sschwartz 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
917117Sschwartz 		goto done_reg;
918117Sschwartz 
919117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
9200Sstevel@tonic-gate 
921117Sschwartz 	/* Get config space first. */
922117Sschwartz 	range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
923117Sschwartz 	range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
924117Sschwartz 	max_addr = range_prop + range_prop_size;
9250Sstevel@tonic-gate 
926117Sschwartz 	/*
927117Sschwartz 	 * Build device address based on base addr from range prop, and bus,
928117Sschwartz 	 * dev and func values passed in.  This address is where config space
929117Sschwartz 	 * begins.
930117Sschwartz 	 */
931117Sschwartz 	base_addr = range_prop +
932117Sschwartz 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
933117Sschwartz 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
934117Sschwartz 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
9350Sstevel@tonic-gate 
936117Sschwartz 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
937117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
938117Sschwartz 		rval = EINVAL;
939117Sschwartz 		goto done_reg;
940117Sschwartz 	}
9410Sstevel@tonic-gate 
942117Sschwartz 	DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
943117Sschwartz 	    "func:0x%x, addr:0x%x\n", range_prop,
944117Sschwartz 	    prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
945117Sschwartz 	    prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
9460Sstevel@tonic-gate 
947117Sschwartz 	/* Proper config space desired. */
948117Sschwartz 	if (prg.barnum == 0) {
949117Sschwartz 
950117Sschwartz 		rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
951117Sschwartz 		    size, write_flag);
9520Sstevel@tonic-gate 
953117Sschwartz 	} else {	/* IO / MEM / MEM64 space. */
9540Sstevel@tonic-gate 
955117Sschwartz 		if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
956117Sschwartz 		    &is_io_space) != SUCCESS)
957117Sschwartz 			goto done_reg;
9580Sstevel@tonic-gate 
959117Sschwartz 		/* IO space. */
960117Sschwartz 		if (is_io_space) {
9610Sstevel@tonic-gate 
962117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
9630Sstevel@tonic-gate 
964117Sschwartz 			/* Reposition to focus on IO space. */
965117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
966117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
967117Sschwartz 			    PCI_IO_RANGE_BANK);
9680Sstevel@tonic-gate 
969117Sschwartz 		/* 64 bit memory space. */
970117Sschwartz 		} else if ((bar >> 32) != 0) {
971117Sschwartz 
972117Sschwartz 			DEBUG1(DBG_TOOLS, dip,
973117Sschwartz 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
9740Sstevel@tonic-gate 
975117Sschwartz 			/* Reposition to MEM64 range space. */
976117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp,
977117Sschwartz 			    PCI_MEM64_RANGE_BANK);
978117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
979117Sschwartz 			    PCI_MEM64_RANGE_BANK);
9800Sstevel@tonic-gate 
981117Sschwartz 		} else {	/* Mem32 space, including ROM */
9820Sstevel@tonic-gate 
983117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
9840Sstevel@tonic-gate 
985117Sschwartz 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9860Sstevel@tonic-gate 
987117Sschwartz 				DEBUG0(DBG_TOOLS, dip,
988117Sschwartz 				    "Additional ROM checking\n");
9890Sstevel@tonic-gate 
990117Sschwartz 				/* Can't write to ROM */
991117Sschwartz 				if (write_flag) {
992117Sschwartz 					prg.status = PCITOOL_ROM_WRITE;
993117Sschwartz 					rval = EIO;
994117Sschwartz 					goto done_reg;
9950Sstevel@tonic-gate 
996117Sschwartz 				/* ROM disabled for reading */
997117Sschwartz 				} else if (!(bar & 0x00000001)) {
998117Sschwartz 					prg.status = PCITOOL_ROM_DISABLED;
9990Sstevel@tonic-gate 					rval = EIO;
10000Sstevel@tonic-gate 					goto done_reg;
10010Sstevel@tonic-gate 				}
10020Sstevel@tonic-gate 			}
10030Sstevel@tonic-gate 
1004117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
1005117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
1006117Sschwartz 			    PCI_MEM_RANGE_BANK);
1007117Sschwartz 		}
10080Sstevel@tonic-gate 
1009117Sschwartz 		/* Common code for all IO/MEM range spaces. */
1010117Sschwartz 		max_addr = range_prop + range_prop_size;
1011117Sschwartz 		base_addr = range_prop + bar;
1012117Sschwartz 
1013117Sschwartz 		DEBUG3(DBG_TOOLS, dip,
1014117Sschwartz 		    "addr portion of bar is 0x%llx, base=0x%llx, "
1015117Sschwartz 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
10160Sstevel@tonic-gate 
1017117Sschwartz 		/*
1018117Sschwartz 		 * Use offset provided by caller to index into
1019117Sschwartz 		 * desired space, then access.
1020117Sschwartz 		 * Note that prg.status is modified on error.
1021117Sschwartz 		 */
1022117Sschwartz 		prg.phys_addr = base_addr + prg.offset;
1023117Sschwartz 		rval = pcitool_access(pci_p, prg.phys_addr,
1024117Sschwartz 		    max_addr, &prg.data, size, write_flag,
1025117Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
1026117Sschwartz 	}
1027117Sschwartz 
10280Sstevel@tonic-gate done_reg:
1029*4397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
1030117Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
1031117Sschwartz 	    DDI_SUCCESS) {
1032117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
1033117Sschwartz 		rval = EFAULT;
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 	return (rval);
10360Sstevel@tonic-gate }
1037117Sschwartz 
1038117Sschwartz int
1039117Sschwartz pcitool_init(dev_info_t *dip)
1040117Sschwartz {
1041117Sschwartz 	int instance = ddi_get_instance(dip);
1042117Sschwartz 
1043117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
1044117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
1045117Sschwartz 	    DDI_NT_REGACC, 0) != DDI_SUCCESS)
1046117Sschwartz 		return (DDI_FAILURE);
1047117Sschwartz 
1048117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
1049117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
1050117Sschwartz 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
1051117Sschwartz 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
1052117Sschwartz 		return (DDI_FAILURE);
1053117Sschwartz 	}
1054117Sschwartz 
1055117Sschwartz 	return (DDI_SUCCESS);
1056117Sschwartz }
1057117Sschwartz 
1058117Sschwartz void
1059117Sschwartz pcitool_uninit(dev_info_t *dip)
1060117Sschwartz {
1061117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
1062117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
1063117Sschwartz }
1064