xref: /onnv-gate/usr/src/uts/sun4u/io/pci/pci_tools.c (revision 777:9283e4b2d9d5)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
29117Sschwartz #include <sys/stat.h>
30117Sschwartz #include <sys/sunddi.h>
31117Sschwartz #include <sys/param.h>
32117Sschwartz 
330Sstevel@tonic-gate #include <sys/sysmacros.h>
340Sstevel@tonic-gate #include <sys/machsystm.h>
350Sstevel@tonic-gate #include <sys/promif.h>
360Sstevel@tonic-gate #include <sys/cpuvar.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
390Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #include <sys/pci_tools.h>
42*777Sschwartz #include <sys/pci/pci_tools_ext.h>
43*777Sschwartz 
44*777Sschwartz /*
45*777Sschwartz  * Number of interrupts supported per PCI bus.
46*777Sschwartz  */
47*777Sschwartz #define	PCI_MAX_INO		0x3f
48*777Sschwartz 
49*777Sschwartz /*
50*777Sschwartz  * PCI Space definitions.
51*777Sschwartz  */
52*777Sschwartz #define	PCI_CONFIG_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
53*777Sschwartz #define	PCI_IO_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_IO))
54*777Sschwartz #define	PCI_MEM_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
55*777Sschwartz #define	PCI_MEM64_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
580Sstevel@tonic-gate  * Extract 64 bit parent or size values from 32 bit cells of
590Sstevel@tonic-gate  * pci_ranges_t property.
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  * Only bits 42-32 are relevant in parent_high.
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP(ranges, bank) \
640Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
650Sstevel@tonic-gate 	ranges[bank].parent_low)
660Sstevel@tonic-gate 
670Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
680Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].size_high)) << 32) | \
690Sstevel@tonic-gate 	ranges[bank].size_low)
700Sstevel@tonic-gate 
71117Sschwartz #define	PCI_BAR_OFFSET(x)	(pci_bars[x.barnum])
72117Sschwartz 
730Sstevel@tonic-gate /* Big and little endian as boolean values. */
740Sstevel@tonic-gate #define	BE B_TRUE
750Sstevel@tonic-gate #define	LE B_FALSE
760Sstevel@tonic-gate 
770Sstevel@tonic-gate #define	SUCCESS	0
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
800Sstevel@tonic-gate typedef union {
810Sstevel@tonic-gate 	uint64_t u64;
820Sstevel@tonic-gate 	uint32_t u32;
830Sstevel@tonic-gate 	uint16_t u16;
840Sstevel@tonic-gate 	uint8_t u8;
850Sstevel@tonic-gate } peek_poke_value_t;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * Offsets of BARS in config space.  First entry of 0 means config space.
890Sstevel@tonic-gate  * Entries here correlate to pcitool_bars_t enumerated type.
900Sstevel@tonic-gate  */
910Sstevel@tonic-gate static uint8_t pci_bars[] = {
920Sstevel@tonic-gate 	0x0,
930Sstevel@tonic-gate 	PCI_CONF_BASE0,
940Sstevel@tonic-gate 	PCI_CONF_BASE1,
950Sstevel@tonic-gate 	PCI_CONF_BASE2,
960Sstevel@tonic-gate 	PCI_CONF_BASE3,
970Sstevel@tonic-gate 	PCI_CONF_BASE4,
980Sstevel@tonic-gate 	PCI_CONF_BASE5,
990Sstevel@tonic-gate 	PCI_CONF_ROM
1000Sstevel@tonic-gate };
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate /*LINTLIBRARY*/
1030Sstevel@tonic-gate 
104117Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
105117Sschwartz     uint64_t paddr, uint64_t *value_p);
106117Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
107117Sschwartz     uint64_t paddr, uint64_t value);
108117Sschwartz static boolean_t pcitool_validate_cpuid(uint32_t cpu_id);
109117Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
110117Sschwartz     uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
111117Sschwartz     uint32_t *pcitool_status);
112117Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
113117Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
114117Sschwartz     uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
115117Sschwartz     boolean_t *is_io_space);
116117Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
117117Sschwartz     uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
118117Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
119117Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
120117Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
1230Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate /*
1260Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_peek
1270Sstevel@tonic-gate  *
1280Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1290Sstevel@tonic-gate  * Size is 1, 2, 4 or 8 bytes.
1300Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1310Sstevel@tonic-gate  * value_p is where the value is returned.
1320Sstevel@tonic-gate  */
1330Sstevel@tonic-gate static int
134117Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
135117Sschwartz 	uint64_t paddr, uint64_t *value_p)
1360Sstevel@tonic-gate {
1370Sstevel@tonic-gate 	on_trap_data_t otd;
1380Sstevel@tonic-gate 	int err = DDI_SUCCESS;
139117Sschwartz 	peek_poke_value_t peek_value;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	/* Set up trap handling to make the access safe. */
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 	/*
1480Sstevel@tonic-gate 	 * on_trap works like setjmp.
1490Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
1500Sstevel@tonic-gate 	 * but to call peek_fault instead.
1510Sstevel@tonic-gate 	 * Call pci_do_phys_peek after trap handling is setup.
1520Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
1530Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
1540Sstevel@tonic-gate 	 */
1550Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
1560Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&peek_fault;
1570Sstevel@tonic-gate 		err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
1580Sstevel@tonic-gate 	} else {
1590Sstevel@tonic-gate 		err = DDI_FAILURE;
1600Sstevel@tonic-gate 	}
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
1630Sstevel@tonic-gate 	no_trap();
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	if (err != DDI_FAILURE) {
1660Sstevel@tonic-gate 		switch (size) {
1670Sstevel@tonic-gate 		case 8:
1680Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u64;
1690Sstevel@tonic-gate 			break;
1700Sstevel@tonic-gate 		case 4:
1710Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u32;
1720Sstevel@tonic-gate 			break;
1730Sstevel@tonic-gate 		case 2:
1740Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u16;
1750Sstevel@tonic-gate 			break;
1760Sstevel@tonic-gate 		case 1:
1770Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u8;
1780Sstevel@tonic-gate 			break;
1790Sstevel@tonic-gate 		default:
1800Sstevel@tonic-gate 			err = DDI_FAILURE;
1810Sstevel@tonic-gate 		}
1820Sstevel@tonic-gate 	}
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	return (err);
1850Sstevel@tonic-gate }
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_poke
1890Sstevel@tonic-gate  *
1900Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1910Sstevel@tonic-gate  * Size is 1,2,4 or 8 bytes.
1920Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1930Sstevel@tonic-gate  * value contains the value to be written.
1940Sstevel@tonic-gate  */
1950Sstevel@tonic-gate static int
196117Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
197117Sschwartz 	uint64_t paddr, uint64_t value)
1980Sstevel@tonic-gate {
1990Sstevel@tonic-gate 	on_trap_data_t otd;
2000Sstevel@tonic-gate 	int err = DDI_SUCCESS;
2010Sstevel@tonic-gate 	peek_poke_value_t poke_value;
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	switch (size) {
2060Sstevel@tonic-gate 	case 8:
2070Sstevel@tonic-gate 		poke_value.u64 = value;
2080Sstevel@tonic-gate 		break;
2090Sstevel@tonic-gate 	case 4:
2100Sstevel@tonic-gate 		poke_value.u32 = (uint32_t)value;
2110Sstevel@tonic-gate 		break;
2120Sstevel@tonic-gate 	case 2:
2130Sstevel@tonic-gate 		poke_value.u16 = (uint16_t)value;
2140Sstevel@tonic-gate 		break;
2150Sstevel@tonic-gate 	case 1:
2160Sstevel@tonic-gate 		poke_value.u8 = (uint8_t)value;
2170Sstevel@tonic-gate 		break;
2180Sstevel@tonic-gate 	default:
2190Sstevel@tonic-gate 		return (DDI_FAILURE);
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	mutex_enter(&pbm_p->pbm_pokefault_mutex);
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	/*
2270Sstevel@tonic-gate 	 * on_trap works like setjmp.
2280Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
2290Sstevel@tonic-gate 	 * but to call poke_fault instead.
2300Sstevel@tonic-gate 	 * Call pci_do_phys_poke after trap handling is setup.
2310Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
2320Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
2330Sstevel@tonic-gate 	 */
2340Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
2350Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&poke_fault;
2360Sstevel@tonic-gate 		err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
2370Sstevel@tonic-gate 	}
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	/* Let the dust settle and errors occur if they will. */
2400Sstevel@tonic-gate 	pbm_clear_error(pbm_p);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	/* Check for an error. */
2430Sstevel@tonic-gate 	if (otd.ot_trap == OT_DATA_ACCESS) {
2440Sstevel@tonic-gate 		err = DDI_FAILURE;
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
2480Sstevel@tonic-gate 	mutex_exit(&pbm_p->pbm_pokefault_mutex);
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	no_trap();
2510Sstevel@tonic-gate 	return (err);
2520Sstevel@tonic-gate }
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate /*
2560Sstevel@tonic-gate  * Validate the cpu_id passed in.
257117Sschwartz  * A value of B_TRUE will be returned for success.
2580Sstevel@tonic-gate  */
259117Sschwartz static boolean_t
260117Sschwartz pcitool_validate_cpuid(uint32_t cpuid)
2610Sstevel@tonic-gate {
262117Sschwartz 	extern const int _ncpu;
263117Sschwartz 	extern cpu_t	*cpu[];
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	ASSERT(mutex_owned(&cpu_lock));
2660Sstevel@tonic-gate 
267117Sschwartz 	return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid])));
2680Sstevel@tonic-gate }
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate /* Return the number of interrupts on a pci bus. */
2720Sstevel@tonic-gate static int
2730Sstevel@tonic-gate pcitool_intr_get_max_ino(uint32_t *arg, int mode)
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	uint32_t num_intr = PCI_MAX_INO;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) !=
2780Sstevel@tonic-gate 	    DDI_SUCCESS) {
2790Sstevel@tonic-gate 		return (EFAULT);
2800Sstevel@tonic-gate 	} else {
2810Sstevel@tonic-gate 		return (SUCCESS);
2820Sstevel@tonic-gate 	}
2830Sstevel@tonic-gate }
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate /*
2870Sstevel@tonic-gate  * Get interrupt information for a given ino.
2880Sstevel@tonic-gate  * Returns info only for inos mapped to devices.
2890Sstevel@tonic-gate  *
2900Sstevel@tonic-gate  * Returned info is valid only when iget.num_devs is returned > 0.
2910Sstevel@tonic-gate  * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
2920Sstevel@tonic-gate  */
2930Sstevel@tonic-gate /*ARGSUSED*/
2940Sstevel@tonic-gate static int
2950Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
2960Sstevel@tonic-gate {
2970Sstevel@tonic-gate 	/* Array part isn't used here, but oh well... */
2980Sstevel@tonic-gate 	pcitool_intr_get_t partial_iget;
2990Sstevel@tonic-gate 	pcitool_intr_get_t *iget = &partial_iget;
3000Sstevel@tonic-gate 	size_t	iget_kmem_alloc_size = 0;
3010Sstevel@tonic-gate 	ib_t *ib_p = pci_p->pci_ib_p;
3020Sstevel@tonic-gate 	volatile uint64_t *imregp;
3030Sstevel@tonic-gate 	uint64_t imregval;
3040Sstevel@tonic-gate 	uint32_t ino;
3050Sstevel@tonic-gate 	uint8_t num_devs_ret;
3060Sstevel@tonic-gate 	int copyout_rval;
3070Sstevel@tonic-gate 	int rval = SUCCESS;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	/* Read in just the header part, no array section. */
3100Sstevel@tonic-gate 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
3110Sstevel@tonic-gate 	    DDI_SUCCESS) {
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 		return (EFAULT);
3140Sstevel@tonic-gate 	}
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	ino = partial_iget.ino;
3170Sstevel@tonic-gate 	num_devs_ret = partial_iget.num_devs_ret;
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	/* Validate argument. */
3200Sstevel@tonic-gate 	if (ino > PCI_MAX_INO) {
3210Sstevel@tonic-gate 		partial_iget.status = PCITOOL_INVALID_INO;
3220Sstevel@tonic-gate 		partial_iget.num_devs_ret = 0;
3230Sstevel@tonic-gate 		rval = EINVAL;
3240Sstevel@tonic-gate 		goto done_get_intr;
3250Sstevel@tonic-gate 	}
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	/* Caller wants device information returned. */
3280Sstevel@tonic-gate 	if (num_devs_ret > 0) {
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 		/*
3310Sstevel@tonic-gate 		 * Allocate room.
3320Sstevel@tonic-gate 		 * Note if num_devs_ret == 0 iget remains pointing to
3330Sstevel@tonic-gate 		 * partial_iget.
3340Sstevel@tonic-gate 		 */
3350Sstevel@tonic-gate 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
3360Sstevel@tonic-gate 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 		/* Read in whole structure to verify there's room. */
3390Sstevel@tonic-gate 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
3400Sstevel@tonic-gate 		    SUCCESS) {
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 			/* Be consistent and just return EFAULT here. */
3430Sstevel@tonic-gate 			kmem_free(iget, iget_kmem_alloc_size);
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 			return (EFAULT);
3460Sstevel@tonic-gate 		}
3470Sstevel@tonic-gate 	}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
3500Sstevel@tonic-gate 	iget->ino = ino;
3510Sstevel@tonic-gate 	iget->num_devs_ret = num_devs_ret;
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	imregp = ib_intr_map_reg_addr(ib_p, ino);
3540Sstevel@tonic-gate 	imregval = *imregp;
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	/*
3570Sstevel@tonic-gate 	 * Read "valid" bit.  If set, interrupts are enabled.
3580Sstevel@tonic-gate 	 * This bit happens to be the same on Fire and Tomatillo.
3590Sstevel@tonic-gate 	 */
3600Sstevel@tonic-gate 	if (imregval & COMMON_INTR_MAP_REG_VALID) {
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 		/*
3630Sstevel@tonic-gate 		 * The following looks up the ib_ino_info and returns
3640Sstevel@tonic-gate 		 * info of devices mapped to this ino.
3650Sstevel@tonic-gate 		 */
3660Sstevel@tonic-gate 		iget->num_devs = ib_get_ino_devs(
3670Sstevel@tonic-gate 		    ib_p, ino, &iget->num_devs_ret, iget->dev);
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 		/*
3700Sstevel@tonic-gate 		 * Consider only inos mapped to devices (as opposed to
3710Sstevel@tonic-gate 		 * inos mapped to the bridge itself.
3720Sstevel@tonic-gate 		 */
3730Sstevel@tonic-gate 		if (iget->num_devs > 0) {
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 			/*
3760Sstevel@tonic-gate 			 * These 2 items are platform specific,
3770Sstevel@tonic-gate 			 * extracted from the bridge.
3780Sstevel@tonic-gate 			 */
3790Sstevel@tonic-gate 			iget->ctlr = 0;
380117Sschwartz 			iget->cpu_id = ib_map_reg_get_cpu(imregval);
3810Sstevel@tonic-gate 		}
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate done_get_intr:
3840Sstevel@tonic-gate 	iget->drvr_version = PCITOOL_DRVR_VERSION;
3850Sstevel@tonic-gate 	copyout_rval = ddi_copyout(iget, arg,
3860Sstevel@tonic-gate 	    PCITOOL_IGET_SIZE(num_devs_ret), mode);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	if (iget_kmem_alloc_size > 0) {
3890Sstevel@tonic-gate 		kmem_free(iget, iget_kmem_alloc_size);
3900Sstevel@tonic-gate 	}
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	if (copyout_rval != DDI_SUCCESS) {
3930Sstevel@tonic-gate 		rval = EFAULT;
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	return (rval);
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate  * Associate a new CPU with a given ino.
4010Sstevel@tonic-gate  *
4020Sstevel@tonic-gate  * Operate only on inos which are already mapped to devices.
4030Sstevel@tonic-gate  */
4040Sstevel@tonic-gate static int
4050Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
4060Sstevel@tonic-gate {
407117Sschwartz 	ib_t *ib_p = pci_p->pci_ib_p;
408117Sschwartz 	int rval = SUCCESS;
409117Sschwartz 
4100Sstevel@tonic-gate 	uint8_t zero = 0;
4110Sstevel@tonic-gate 	pcitool_intr_set_t iset;
4120Sstevel@tonic-gate 	uint32_t old_cpu_id;
4130Sstevel@tonic-gate 	hrtime_t start_time;
4140Sstevel@tonic-gate 	uint64_t imregval;
4150Sstevel@tonic-gate 	uint64_t new_imregval;
4160Sstevel@tonic-gate 	volatile uint64_t *imregp;
4170Sstevel@tonic-gate 	volatile uint64_t *idregp;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) !=
420117Sschwartz 	    DDI_SUCCESS)
4210Sstevel@tonic-gate 		return (EFAULT);
4220Sstevel@tonic-gate 
423117Sschwartz 	/* Validate input argument and that ino given belongs to a device. */
424117Sschwartz 	if ((iset.ino > PCI_MAX_INO) ||
425117Sschwartz 	    (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
4260Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4270Sstevel@tonic-gate 		rval = EINVAL;
4280Sstevel@tonic-gate 		goto done_set_intr;
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
4320Sstevel@tonic-gate 	idregp = IB_INO_INTR_STATE_REG(ib_p, iset.ino);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	DEBUG4(DBG_TOOLS, dip, "set_intr: cpu:%d, ino:0x%x, mapreg @ "
4350Sstevel@tonic-gate 	    "0x%llx, intr_stat @ 0x%llx\n",
4360Sstevel@tonic-gate 	    iset.cpu_id, iset.ino, imregp, idregp);
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 	/* Save original mapreg value. */
4390Sstevel@tonic-gate 	imregval = *imregp;
4400Sstevel@tonic-gate 	DEBUG1(DBG_TOOLS, dip, "orig mapreg value: 0x%llx\n", imregval);
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	/* Is this request a noop? */
443117Sschwartz 	if ((old_cpu_id = ib_map_reg_get_cpu(imregval)) == iset.cpu_id) {
4440Sstevel@tonic-gate 		iset.status = PCITOOL_SUCCESS;
4450Sstevel@tonic-gate 		goto done_set_intr;
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	/* Operate only on inos which are already enabled. */
4490Sstevel@tonic-gate 	if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
4500Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4510Sstevel@tonic-gate 		rval = EINVAL;
4520Sstevel@tonic-gate 		goto done_set_intr;
4530Sstevel@tonic-gate 	}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	/* Clear the interrupt valid/enable bit for particular ino. */
4560Sstevel@tonic-gate 	DEBUG0(DBG_TOOLS, dip, "Clearing intr_enabled...\n");
4570Sstevel@tonic-gate 	*imregp = imregval & ~COMMON_INTR_MAP_REG_VALID;
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	/* Wait until there are no more pending interrupts. */
4600Sstevel@tonic-gate 	start_time = gethrtime();
4610Sstevel@tonic-gate 
462117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "About to check for pending interrupts...\n");
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	while (IB_INO_INTR_PENDING(idregp, iset.ino)) {
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Waiting for pending ints to clear\n");
467117Sschwartz 		if ((gethrtime() - start_time) < pci_intrpend_timeout)
4680Sstevel@tonic-gate 			continue;
4690Sstevel@tonic-gate 
470117Sschwartz 		else {	/* Timed out waiting. */
4710Sstevel@tonic-gate 			iset.status = PCITOOL_PENDING_INTRTIMEOUT;
4720Sstevel@tonic-gate 			rval = ETIME;
4730Sstevel@tonic-gate 			goto done_set_intr;
4740Sstevel@tonic-gate 		}
4750Sstevel@tonic-gate 	}
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	new_imregval = *imregp;
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	DEBUG1(DBG_TOOLS, dip,
4800Sstevel@tonic-gate 	    "after disabling intr, mapreg value: 0x%llx\n", new_imregval);
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	/*
4830Sstevel@tonic-gate 	 * Get lock, validate cpu and write new mapreg value.
4840Sstevel@tonic-gate 	 * Return original cpu value to caller via iset.cpu_id.
4850Sstevel@tonic-gate 	 */
4860Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
487117Sschwartz 	if (pcitool_validate_cpuid(iset.cpu_id)) {
488117Sschwartz 
489117Sschwartz 		/* Prepare new mapreg value with intr enabled and new cpu_id. */
490117Sschwartz 		new_imregval &=
491117Sschwartz 		    COMMON_INTR_MAP_REG_IGN | COMMON_INTR_MAP_REG_INO;
492117Sschwartz 		new_imregval = ib_get_map_reg(new_imregval, iset.cpu_id);
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 		DEBUG1(DBG_TOOLS, dip, "Writing new mapreg value:0x%llx\n",
4950Sstevel@tonic-gate 		    new_imregval);
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 		*imregp = new_imregval;
498117Sschwartz 
499117Sschwartz 		ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino);
500117Sschwartz 
5010Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
502117Sschwartz 
5030Sstevel@tonic-gate 		iset.cpu_id = old_cpu_id;
5040Sstevel@tonic-gate 		iset.status = PCITOOL_SUCCESS;
5050Sstevel@tonic-gate 
506117Sschwartz 	} else {	/* Invalid cpu.  Restore original register image. */
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip,
5090Sstevel@tonic-gate 		    "Invalid cpuid: writing orig mapreg value\n");
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 		*imregp = imregval;
5120Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
5130Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_CPUID;
5140Sstevel@tonic-gate 		rval = EINVAL;
5150Sstevel@tonic-gate 	}
5160Sstevel@tonic-gate done_set_intr:
5170Sstevel@tonic-gate 	iset.drvr_version = PCITOOL_DRVR_VERSION;
5180Sstevel@tonic-gate 	if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) !=
519117Sschwartz 	    DDI_SUCCESS)
5200Sstevel@tonic-gate 		rval = EFAULT;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	return (rval);
5230Sstevel@tonic-gate }
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */
5270Sstevel@tonic-gate int
5280Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
5290Sstevel@tonic-gate {
5300Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
5310Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
5320Sstevel@tonic-gate 	int		rval = SUCCESS;
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	switch (cmd) {
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	/* Return the number of interrupts supported by a PCI bus. */
5370Sstevel@tonic-gate 	case PCITOOL_DEVICE_NUM_INTR:
5380Sstevel@tonic-gate 		rval = pcitool_intr_get_max_ino(arg, mode);
5390Sstevel@tonic-gate 		break;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	/* Get interrupt information for a given ino. */
5420Sstevel@tonic-gate 	case PCITOOL_DEVICE_GET_INTR:
5430Sstevel@tonic-gate 		rval = pcitool_get_intr(dip, arg, mode, pci_p);
5440Sstevel@tonic-gate 		break;
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	/* Associate a new CPU with a given ino. */
5470Sstevel@tonic-gate 	case PCITOOL_DEVICE_SET_INTR:
5480Sstevel@tonic-gate 		rval = pcitool_set_intr(dip, arg, mode, pci_p);
5490Sstevel@tonic-gate 		break;
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	default:
5520Sstevel@tonic-gate 		rval = ENOTTY;
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	return (rval);
5560Sstevel@tonic-gate }
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate /*
560117Sschwartz  * Wrapper around pcitool_phys_peek/poke.
5610Sstevel@tonic-gate  *
562117Sschwartz  * Validates arguments and calls pcitool_phys_peek/poke appropriately.
5630Sstevel@tonic-gate  *
5640Sstevel@tonic-gate  * Dip is of the nexus,
5650Sstevel@tonic-gate  * phys_addr is the address to write in physical space,
5660Sstevel@tonic-gate  * max_addr is the upper bound on the physical space used for bounds checking,
5670Sstevel@tonic-gate  * pcitool_status returns more detailed status in addition to a more generic
5680Sstevel@tonic-gate  * errno-style function return value.
5690Sstevel@tonic-gate  * other args are self-explanatory.
5700Sstevel@tonic-gate  */
5710Sstevel@tonic-gate static int
572117Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
573117Sschwartz 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
574117Sschwartz 	uint32_t *pcitool_status)
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	int rval = SUCCESS;
5780Sstevel@tonic-gate 	dev_info_t *dip = pci_p->pci_dip;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	/* Upper bounds checking. */
5810Sstevel@tonic-gate 	if (phys_addr > max_addr) {
5820Sstevel@tonic-gate 		DEBUG2(DBG_TOOLS, dip,
5830Sstevel@tonic-gate 		    "Phys addr 0x%llx out of range (max 0x%llx).\n",
5840Sstevel@tonic-gate 		    phys_addr, max_addr);
5850Sstevel@tonic-gate 		*pcitool_status = PCITOOL_INVALID_ADDRESS;
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 		rval = EINVAL;
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	/* Alignment checking. */
5900Sstevel@tonic-gate 	} else if (!IS_P2ALIGNED(phys_addr, size)) {
5910Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
5920Sstevel@tonic-gate 		*pcitool_status = PCITOOL_NOT_ALIGNED;
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 		rval = EINVAL;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	/* Made it through checks.  Do the access. */
5970Sstevel@tonic-gate 	} else if (write) {
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
600117Sschwartz 		    "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
6010Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
6020Sstevel@tonic-gate 
603117Sschwartz 		if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
604117Sschwartz 		    *data) != DDI_SUCCESS) {
6050Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
606117Sschwartz 			    "%d byte %s pcitool_phys_poke at addr "
6070Sstevel@tonic-gate 			    "0x%llx failed\n",
6080Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
6090Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 			rval = EFAULT;
6120Sstevel@tonic-gate 		}
6130Sstevel@tonic-gate 
614117Sschwartz 	} else {	/* Read */
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
617117Sschwartz 		    "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
6180Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
6190Sstevel@tonic-gate 
620117Sschwartz 		if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
621117Sschwartz 		    data) != DDI_SUCCESS) {
6220Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
623117Sschwartz 			    "%d byte %s pcitool_phys_peek at addr "
6240Sstevel@tonic-gate 			    "0x%llx failed\n",
6250Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
6260Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 			rval = EFAULT;
6290Sstevel@tonic-gate 		}
6300Sstevel@tonic-gate 	}
6310Sstevel@tonic-gate 	return (rval);
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate /*
6350Sstevel@tonic-gate  * Perform register accesses on the nexus device itself.
6360Sstevel@tonic-gate  */
6370Sstevel@tonic-gate int
6380Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
6390Sstevel@tonic-gate {
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	pci_t			*pci_p = DEV_TO_SOFTSTATE(dev);
6420Sstevel@tonic-gate 	dev_info_t		*dip = pci_p->pci_dip;
643117Sschwartz 	pci_nexus_regspec_t	*pci_rp = NULL;
644117Sschwartz 	boolean_t		write_flag = B_FALSE;
6450Sstevel@tonic-gate 	pcitool_reg_t		prg;
6460Sstevel@tonic-gate 	uint64_t		base_addr;
6470Sstevel@tonic-gate 	uint64_t		max_addr;
6480Sstevel@tonic-gate 	uint32_t		reglen;
6490Sstevel@tonic-gate 	uint8_t			size;
6500Sstevel@tonic-gate 	uint32_t		rval = 0;
6510Sstevel@tonic-gate 
652117Sschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
6530Sstevel@tonic-gate 		write_flag = B_TRUE;
6540Sstevel@tonic-gate 
655117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
6560Sstevel@tonic-gate 
657117Sschwartz 	/* Read data from userland. */
658117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
659117Sschwartz 	    DDI_SUCCESS) {
660117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
661117Sschwartz 		return (EFAULT);
662117Sschwartz 	}
6630Sstevel@tonic-gate 
664117Sschwartz 	/* Read reg property which contains starting addr and size of banks. */
665117Sschwartz 	if (ddi_prop_lookup_int_array(
666117Sschwartz 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
667117Sschwartz 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
668117Sschwartz 		if (((reglen * sizeof (int)) %
669117Sschwartz 		    sizeof (pci_nexus_regspec_t)) != 0) {
670117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
671117Sschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
672117Sschwartz 			rval = EIO;
6730Sstevel@tonic-gate 			goto done;
6740Sstevel@tonic-gate 		}
675117Sschwartz 	}
6760Sstevel@tonic-gate 
677117Sschwartz 	/* Bounds check the bank number. */
678117Sschwartz 	if (prg.barnum >=
679117Sschwartz 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
680117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
681117Sschwartz 		rval = EINVAL;
682117Sschwartz 		goto done;
6830Sstevel@tonic-gate 	}
6840Sstevel@tonic-gate 
685117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
686117Sschwartz 	base_addr = pci_rp[prg.barnum].phys_addr;
687117Sschwartz 	max_addr = base_addr + pci_rp[prg.barnum].size;
688117Sschwartz 	prg.phys_addr = base_addr + prg.offset;
689117Sschwartz 
690117Sschwartz 	DEBUG4(DBG_TOOLS, dip,
691117Sschwartz 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
692117Sschwartz 	    "addr:0x%llx, max_addr:0x%llx\n",
693117Sschwartz 	    base_addr, prg.offset, prg.phys_addr, max_addr);
694117Sschwartz 
695117Sschwartz 	/* Access device.  prg.status is modified. */
696117Sschwartz 	rval = pcitool_access(pci_p,
697117Sschwartz 	    prg.phys_addr, max_addr, &prg.data, size, write_flag,
698117Sschwartz 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
699117Sschwartz 
7000Sstevel@tonic-gate done:
701117Sschwartz 	if (pci_rp != NULL)
702117Sschwartz 		ddi_prop_free(pci_rp);
703117Sschwartz 
7040Sstevel@tonic-gate 	prg.drvr_version = PCITOOL_DRVR_VERSION;
7050Sstevel@tonic-gate 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
7060Sstevel@tonic-gate 	    DDI_SUCCESS) {
7070Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
7080Sstevel@tonic-gate 		return (EFAULT);
7090Sstevel@tonic-gate 	}
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	return (rval);
7120Sstevel@tonic-gate }
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 
715117Sschwartz static int
716117Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
717117Sschwartz {
718117Sschwartz 	int rval = SUCCESS;
719117Sschwartz 
720117Sschwartz 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
721117Sschwartz 		prg->status = PCITOOL_OUT_OF_RANGE;
722117Sschwartz 		rval = EINVAL;
723117Sschwartz 
724117Sschwartz 	/* Validate address arguments of bus / dev / func */
725117Sschwartz 	} else if (((prg->bus_no &
726117Sschwartz 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
727117Sschwartz 	    ((prg->dev_no &
728117Sschwartz 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
729117Sschwartz 	    ((prg->func_no &
730117Sschwartz 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
731117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
732117Sschwartz 		rval = EINVAL;
733117Sschwartz 	}
734117Sschwartz 
735117Sschwartz 	return (rval);
736117Sschwartz }
737117Sschwartz 
738117Sschwartz static int
739117Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
740117Sschwartz 	uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
741117Sschwartz {
742117Sschwartz 
743117Sschwartz 	uint8_t bar_offset;
744117Sschwartz 	int rval;
745117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
746117Sschwartz 
747117Sschwartz 	*bar = 0;
748117Sschwartz 	*is_io_space = B_FALSE;
749117Sschwartz 
750117Sschwartz 	/*
751117Sschwartz 	 * Translate BAR number into offset of the BAR in
752117Sschwartz 	 * the device's config space.
753117Sschwartz 	 */
754117Sschwartz 	bar_offset = PCI_BAR_OFFSET((*prg));
755117Sschwartz 
756117Sschwartz 	DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
757117Sschwartz 	    prg->barnum, bar_offset);
758117Sschwartz 
759117Sschwartz 	/*
760117Sschwartz 	 * Get Bus Address Register (BAR) from config space.
761117Sschwartz 	 * bar_offset is the offset into config space of the BAR desired.
762117Sschwartz 	 * prg->status is modified on error.
763117Sschwartz 	 */
764117Sschwartz 	rval = pcitool_access(pci_p, config_base_addr + bar_offset,
765117Sschwartz 	    config_max_addr, bar,
766117Sschwartz 	    4,		/* 4 bytes. */
767117Sschwartz 	    B_FALSE,	/* Read */
768117Sschwartz 	    B_FALSE, 	/* Little endian. */
769117Sschwartz 	    &prg->status);
770117Sschwartz 	if (rval != SUCCESS)
771117Sschwartz 		return (rval);
772117Sschwartz 
773117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
774117Sschwartz 	if (!(*bar)) {
775117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
776117Sschwartz 		return (EINVAL);
777117Sschwartz 	}
778117Sschwartz 
779117Sschwartz 	/*
780117Sschwartz 	 * BAR has bits saying this space is IO space, unless
781117Sschwartz 	 * this is the ROM address register.
782117Sschwartz 	 */
783117Sschwartz 	if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
784117Sschwartz 	    (bar_offset != PCI_CONF_ROM)) {
785117Sschwartz 		*is_io_space = B_TRUE;
786117Sschwartz 		*bar &= PCI_BASE_IO_ADDR_M;
787117Sschwartz 
788117Sschwartz 	/*
789117Sschwartz 	 * BAR has bits saying this space is 64 bit memory
790117Sschwartz 	 * space, unless this is the ROM address register.
791117Sschwartz 	 *
792117Sschwartz 	 * The 64 bit address stored in two BAR cells is not necessarily
793117Sschwartz 	 * aligned on an 8-byte boundary.  Need to keep the first 4
794117Sschwartz 	 * bytes read, and do a separate read of the high 4 bytes.
795117Sschwartz 	 */
796117Sschwartz 
797117Sschwartz 	} else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
798117Sschwartz 
799117Sschwartz 		uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
800117Sschwartz 
801117Sschwartz 		/* Don't try to read past the end of BARs. */
802117Sschwartz 		if (bar_offset >= PCI_CONF_BASE5) {
803117Sschwartz 			prg->status = PCITOOL_OUT_OF_RANGE;
804117Sschwartz 			return (EIO);
805117Sschwartz 		}
806117Sschwartz 
807117Sschwartz 		/* Access device.  prg->status is modified on error. */
808117Sschwartz 		rval = pcitool_access(pci_p,
809117Sschwartz 		    config_base_addr + bar_offset + 4, config_max_addr, bar,
810117Sschwartz 		    4,		/* 4 bytes. */
811117Sschwartz 		    B_FALSE,	/* Read */
812117Sschwartz 		    B_FALSE, 	/* Little endian. */
813117Sschwartz 		    &prg->status);
814117Sschwartz 		if (rval != SUCCESS)
815117Sschwartz 			return (rval);
816117Sschwartz 
817117Sschwartz 		*bar = (*bar << 32) + low_bytes;
818117Sschwartz 	}
819117Sschwartz 
820117Sschwartz 	return (SUCCESS);
821117Sschwartz }
822117Sschwartz 
823117Sschwartz 
824117Sschwartz static int
825117Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
826117Sschwartz 	uint64_t max_addr, uint8_t size, boolean_t write_flag)
827117Sschwartz {
828117Sschwartz 	int rval;
829117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
830117Sschwartz 
831117Sschwartz 	/* Access config space and we're done. */
832117Sschwartz 	prg->phys_addr = base_addr + prg->offset;
833117Sschwartz 
834117Sschwartz 	DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
835117Sschwartz 	    "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
836117Sschwartz 	    base_addr, prg->offset, prg->phys_addr,
837117Sschwartz 	    (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
838117Sschwartz 
839117Sschwartz 	/* Access device.  pr.status is modified. */
840117Sschwartz 	rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
841117Sschwartz 	    write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
842117Sschwartz 
843117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
844117Sschwartz 
845117Sschwartz 	return (rval);
846117Sschwartz }
847117Sschwartz 
8480Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
8490Sstevel@tonic-gate int
8500Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
8510Sstevel@tonic-gate {
8520Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
8530Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
8540Sstevel@tonic-gate 	pci_ranges_t	*rp = pci_p->pci_ranges;
8550Sstevel@tonic-gate 	pcitool_reg_t	prg;
8560Sstevel@tonic-gate 	uint64_t	max_addr;
8570Sstevel@tonic-gate 	uint64_t	base_addr;
8580Sstevel@tonic-gate 	uint64_t	range_prop;
8590Sstevel@tonic-gate 	uint64_t	range_prop_size;
8600Sstevel@tonic-gate 	uint64_t	bar = 0;
8610Sstevel@tonic-gate 	int		rval = 0;
8620Sstevel@tonic-gate 	boolean_t	write_flag = B_FALSE;
863117Sschwartz 	boolean_t	is_io_space = B_FALSE;
8640Sstevel@tonic-gate 	uint8_t		size;
8650Sstevel@tonic-gate 
866117Sschwartz 	if (cmd == PCITOOL_DEVICE_SET_REG)
8670Sstevel@tonic-gate 		write_flag = B_TRUE;
8680Sstevel@tonic-gate 
869117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
870117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
871117Sschwartz 	    DDI_SUCCESS) {
872117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
873117Sschwartz 		return (EFAULT);
874117Sschwartz 	}
8750Sstevel@tonic-gate 
876117Sschwartz 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
877117Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
8780Sstevel@tonic-gate 
879117Sschwartz 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
880117Sschwartz 		goto done_reg;
881117Sschwartz 
882117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
8830Sstevel@tonic-gate 
884117Sschwartz 	/* Get config space first. */
885117Sschwartz 	range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
886117Sschwartz 	range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
887117Sschwartz 	max_addr = range_prop + range_prop_size;
8880Sstevel@tonic-gate 
889117Sschwartz 	/*
890117Sschwartz 	 * Build device address based on base addr from range prop, and bus,
891117Sschwartz 	 * dev and func values passed in.  This address is where config space
892117Sschwartz 	 * begins.
893117Sschwartz 	 */
894117Sschwartz 	base_addr = range_prop +
895117Sschwartz 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
896117Sschwartz 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
897117Sschwartz 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
8980Sstevel@tonic-gate 
899117Sschwartz 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
900117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
901117Sschwartz 		rval = EINVAL;
902117Sschwartz 		goto done_reg;
903117Sschwartz 	}
9040Sstevel@tonic-gate 
905117Sschwartz 	DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
906117Sschwartz 	    "func:0x%x, addr:0x%x\n", range_prop,
907117Sschwartz 	    prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
908117Sschwartz 	    prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
9090Sstevel@tonic-gate 
910117Sschwartz 	/* Proper config space desired. */
911117Sschwartz 	if (prg.barnum == 0) {
912117Sschwartz 
913117Sschwartz 		rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
914117Sschwartz 		    size, write_flag);
9150Sstevel@tonic-gate 
916117Sschwartz 	} else {	/* IO / MEM / MEM64 space. */
9170Sstevel@tonic-gate 
918117Sschwartz 		if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
919117Sschwartz 		    &is_io_space) != SUCCESS)
920117Sschwartz 			goto done_reg;
9210Sstevel@tonic-gate 
922117Sschwartz 		/* IO space. */
923117Sschwartz 		if (is_io_space) {
9240Sstevel@tonic-gate 
925117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
9260Sstevel@tonic-gate 
927117Sschwartz 			/* Reposition to focus on IO space. */
928117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
929117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
930117Sschwartz 			    PCI_IO_RANGE_BANK);
9310Sstevel@tonic-gate 
932117Sschwartz 		/* 64 bit memory space. */
933117Sschwartz 		} else if ((bar >> 32) != 0) {
934117Sschwartz 
935117Sschwartz 			DEBUG1(DBG_TOOLS, dip,
936117Sschwartz 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
9370Sstevel@tonic-gate 
938117Sschwartz 			/* Reposition to MEM64 range space. */
939117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp,
940117Sschwartz 			    PCI_MEM64_RANGE_BANK);
941117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
942117Sschwartz 			    PCI_MEM64_RANGE_BANK);
9430Sstevel@tonic-gate 
944117Sschwartz 		} else {	/* Mem32 space, including ROM */
9450Sstevel@tonic-gate 
946117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
9470Sstevel@tonic-gate 
948117Sschwartz 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9490Sstevel@tonic-gate 
950117Sschwartz 				DEBUG0(DBG_TOOLS, dip,
951117Sschwartz 				    "Additional ROM checking\n");
9520Sstevel@tonic-gate 
953117Sschwartz 				/* Can't write to ROM */
954117Sschwartz 				if (write_flag) {
955117Sschwartz 					prg.status = PCITOOL_ROM_WRITE;
956117Sschwartz 					rval = EIO;
957117Sschwartz 					goto done_reg;
9580Sstevel@tonic-gate 
959117Sschwartz 				/* ROM disabled for reading */
960117Sschwartz 				} else if (!(bar & 0x00000001)) {
961117Sschwartz 					prg.status = PCITOOL_ROM_DISABLED;
9620Sstevel@tonic-gate 					rval = EIO;
9630Sstevel@tonic-gate 					goto done_reg;
9640Sstevel@tonic-gate 				}
9650Sstevel@tonic-gate 			}
9660Sstevel@tonic-gate 
967117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
968117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
969117Sschwartz 			    PCI_MEM_RANGE_BANK);
970117Sschwartz 		}
9710Sstevel@tonic-gate 
972117Sschwartz 		/* Common code for all IO/MEM range spaces. */
973117Sschwartz 		max_addr = range_prop + range_prop_size;
974117Sschwartz 		base_addr = range_prop + bar;
975117Sschwartz 
976117Sschwartz 		DEBUG3(DBG_TOOLS, dip,
977117Sschwartz 		    "addr portion of bar is 0x%llx, base=0x%llx, "
978117Sschwartz 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
9790Sstevel@tonic-gate 
980117Sschwartz 		/*
981117Sschwartz 		 * Use offset provided by caller to index into
982117Sschwartz 		 * desired space, then access.
983117Sschwartz 		 * Note that prg.status is modified on error.
984117Sschwartz 		 */
985117Sschwartz 		prg.phys_addr = base_addr + prg.offset;
986117Sschwartz 		rval = pcitool_access(pci_p, prg.phys_addr,
987117Sschwartz 		    max_addr, &prg.data, size, write_flag,
988117Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
989117Sschwartz 	}
990117Sschwartz 
9910Sstevel@tonic-gate done_reg:
992117Sschwartz 	prg.drvr_version = PCITOOL_DRVR_VERSION;
993117Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
994117Sschwartz 	    DDI_SUCCESS) {
995117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
996117Sschwartz 		rval = EFAULT;
9970Sstevel@tonic-gate 	}
9980Sstevel@tonic-gate 	return (rval);
9990Sstevel@tonic-gate }
1000117Sschwartz 
1001117Sschwartz int
1002117Sschwartz pcitool_init(dev_info_t *dip)
1003117Sschwartz {
1004117Sschwartz 	int instance = ddi_get_instance(dip);
1005117Sschwartz 
1006117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
1007117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
1008117Sschwartz 	    DDI_NT_REGACC, 0) != DDI_SUCCESS)
1009117Sschwartz 		return (DDI_FAILURE);
1010117Sschwartz 
1011117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
1012117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
1013117Sschwartz 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
1014117Sschwartz 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
1015117Sschwartz 		return (DDI_FAILURE);
1016117Sschwartz 	}
1017117Sschwartz 
1018117Sschwartz 	return (DDI_SUCCESS);
1019117Sschwartz }
1020117Sschwartz 
1021117Sschwartz void
1022117Sschwartz pcitool_uninit(dev_info_t *dip)
1023117Sschwartz {
1024117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
1025117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
1026117Sschwartz }
1027