xref: /onnv-gate/usr/src/uts/sun4u/io/pci/pci_tools.c (revision 117:1d120397ca7c)
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 
29*117Sschwartz #include <sys/stat.h>
30*117Sschwartz #include <sys/sunddi.h>
31*117Sschwartz #include <sys/param.h>
32*117Sschwartz 
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>
420Sstevel@tonic-gate #include <sys/pci_tools_var.h>
43*117Sschwartz #include <sys/pci/pci_tools_impl.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate /*
460Sstevel@tonic-gate  * Extract 64 bit parent or size values from 32 bit cells of
470Sstevel@tonic-gate  * pci_ranges_t property.
480Sstevel@tonic-gate  *
490Sstevel@tonic-gate  * Only bits 42-32 are relevant in parent_high.
500Sstevel@tonic-gate  */
510Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP(ranges, bank) \
520Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
530Sstevel@tonic-gate 	ranges[bank].parent_low)
540Sstevel@tonic-gate 
550Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
560Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].size_high)) << 32) | \
570Sstevel@tonic-gate 	ranges[bank].size_low)
580Sstevel@tonic-gate 
59*117Sschwartz #define	PCI_BAR_OFFSET(x)	(pci_bars[x.barnum])
60*117Sschwartz 
610Sstevel@tonic-gate /* Big and little endian as boolean values. */
620Sstevel@tonic-gate #define	BE B_TRUE
630Sstevel@tonic-gate #define	LE B_FALSE
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #define	SUCCESS	0
660Sstevel@tonic-gate 
670Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
680Sstevel@tonic-gate typedef union {
690Sstevel@tonic-gate 	uint64_t u64;
700Sstevel@tonic-gate 	uint32_t u32;
710Sstevel@tonic-gate 	uint16_t u16;
720Sstevel@tonic-gate 	uint8_t u8;
730Sstevel@tonic-gate } peek_poke_value_t;
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /*
760Sstevel@tonic-gate  * Offsets of BARS in config space.  First entry of 0 means config space.
770Sstevel@tonic-gate  * Entries here correlate to pcitool_bars_t enumerated type.
780Sstevel@tonic-gate  */
790Sstevel@tonic-gate static uint8_t pci_bars[] = {
800Sstevel@tonic-gate 	0x0,
810Sstevel@tonic-gate 	PCI_CONF_BASE0,
820Sstevel@tonic-gate 	PCI_CONF_BASE1,
830Sstevel@tonic-gate 	PCI_CONF_BASE2,
840Sstevel@tonic-gate 	PCI_CONF_BASE3,
850Sstevel@tonic-gate 	PCI_CONF_BASE4,
860Sstevel@tonic-gate 	PCI_CONF_BASE5,
870Sstevel@tonic-gate 	PCI_CONF_ROM
880Sstevel@tonic-gate };
890Sstevel@tonic-gate 
900Sstevel@tonic-gate /*LINTLIBRARY*/
910Sstevel@tonic-gate 
92*117Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
93*117Sschwartz     uint64_t paddr, uint64_t *value_p);
94*117Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
95*117Sschwartz     uint64_t paddr, uint64_t value);
96*117Sschwartz static boolean_t pcitool_validate_cpuid(uint32_t cpu_id);
97*117Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
98*117Sschwartz     uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
99*117Sschwartz     uint32_t *pcitool_status);
100*117Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
101*117Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
102*117Sschwartz     uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
103*117Sschwartz     boolean_t *is_io_space);
104*117Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
105*117Sschwartz     uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
106*117Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
107*117Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
108*117Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
1110Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate /*
1140Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_peek
1150Sstevel@tonic-gate  *
1160Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1170Sstevel@tonic-gate  * Size is 1, 2, 4 or 8 bytes.
1180Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1190Sstevel@tonic-gate  * value_p is where the value is returned.
1200Sstevel@tonic-gate  */
1210Sstevel@tonic-gate static int
122*117Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
123*117Sschwartz 	uint64_t paddr, uint64_t *value_p)
1240Sstevel@tonic-gate {
1250Sstevel@tonic-gate 	on_trap_data_t otd;
1260Sstevel@tonic-gate 	int err = DDI_SUCCESS;
127*117Sschwartz 	peek_poke_value_t peek_value;
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 	/* Set up trap handling to make the access safe. */
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	/*
1360Sstevel@tonic-gate 	 * on_trap works like setjmp.
1370Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
1380Sstevel@tonic-gate 	 * but to call peek_fault instead.
1390Sstevel@tonic-gate 	 * Call pci_do_phys_peek after trap handling is setup.
1400Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
1410Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
1420Sstevel@tonic-gate 	 */
1430Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
1440Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&peek_fault;
1450Sstevel@tonic-gate 		err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
1460Sstevel@tonic-gate 	} else {
1470Sstevel@tonic-gate 		err = DDI_FAILURE;
1480Sstevel@tonic-gate 	}
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
1510Sstevel@tonic-gate 	no_trap();
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	if (err != DDI_FAILURE) {
1540Sstevel@tonic-gate 		switch (size) {
1550Sstevel@tonic-gate 		case 8:
1560Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u64;
1570Sstevel@tonic-gate 			break;
1580Sstevel@tonic-gate 		case 4:
1590Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u32;
1600Sstevel@tonic-gate 			break;
1610Sstevel@tonic-gate 		case 2:
1620Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u16;
1630Sstevel@tonic-gate 			break;
1640Sstevel@tonic-gate 		case 1:
1650Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u8;
1660Sstevel@tonic-gate 			break;
1670Sstevel@tonic-gate 		default:
1680Sstevel@tonic-gate 			err = DDI_FAILURE;
1690Sstevel@tonic-gate 		}
1700Sstevel@tonic-gate 	}
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	return (err);
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate /*
1760Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_poke
1770Sstevel@tonic-gate  *
1780Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1790Sstevel@tonic-gate  * Size is 1,2,4 or 8 bytes.
1800Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1810Sstevel@tonic-gate  * value contains the value to be written.
1820Sstevel@tonic-gate  */
1830Sstevel@tonic-gate static int
184*117Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
185*117Sschwartz 	uint64_t paddr, uint64_t value)
1860Sstevel@tonic-gate {
1870Sstevel@tonic-gate 	on_trap_data_t otd;
1880Sstevel@tonic-gate 	int err = DDI_SUCCESS;
1890Sstevel@tonic-gate 	peek_poke_value_t poke_value;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	switch (size) {
1940Sstevel@tonic-gate 	case 8:
1950Sstevel@tonic-gate 		poke_value.u64 = value;
1960Sstevel@tonic-gate 		break;
1970Sstevel@tonic-gate 	case 4:
1980Sstevel@tonic-gate 		poke_value.u32 = (uint32_t)value;
1990Sstevel@tonic-gate 		break;
2000Sstevel@tonic-gate 	case 2:
2010Sstevel@tonic-gate 		poke_value.u16 = (uint16_t)value;
2020Sstevel@tonic-gate 		break;
2030Sstevel@tonic-gate 	case 1:
2040Sstevel@tonic-gate 		poke_value.u8 = (uint8_t)value;
2050Sstevel@tonic-gate 		break;
2060Sstevel@tonic-gate 	default:
2070Sstevel@tonic-gate 		return (DDI_FAILURE);
2080Sstevel@tonic-gate 	}
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	mutex_enter(&pbm_p->pbm_pokefault_mutex);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
2150Sstevel@tonic-gate 	 * on_trap works like setjmp.
2160Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
2170Sstevel@tonic-gate 	 * but to call poke_fault instead.
2180Sstevel@tonic-gate 	 * Call pci_do_phys_poke after trap handling is setup.
2190Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
2200Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
2210Sstevel@tonic-gate 	 */
2220Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
2230Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&poke_fault;
2240Sstevel@tonic-gate 		err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
2250Sstevel@tonic-gate 	}
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	/* Let the dust settle and errors occur if they will. */
2280Sstevel@tonic-gate 	pbm_clear_error(pbm_p);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	/* Check for an error. */
2310Sstevel@tonic-gate 	if (otd.ot_trap == OT_DATA_ACCESS) {
2320Sstevel@tonic-gate 		err = DDI_FAILURE;
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
2360Sstevel@tonic-gate 	mutex_exit(&pbm_p->pbm_pokefault_mutex);
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	no_trap();
2390Sstevel@tonic-gate 	return (err);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate /*
2440Sstevel@tonic-gate  * Validate the cpu_id passed in.
245*117Sschwartz  * A value of B_TRUE will be returned for success.
2460Sstevel@tonic-gate  */
247*117Sschwartz static boolean_t
248*117Sschwartz pcitool_validate_cpuid(uint32_t cpuid)
2490Sstevel@tonic-gate {
250*117Sschwartz 	extern const int _ncpu;
251*117Sschwartz 	extern cpu_t	*cpu[];
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	ASSERT(mutex_owned(&cpu_lock));
2540Sstevel@tonic-gate 
255*117Sschwartz 	return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid])));
2560Sstevel@tonic-gate }
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate /* Return the number of interrupts on a pci bus. */
2600Sstevel@tonic-gate static int
2610Sstevel@tonic-gate pcitool_intr_get_max_ino(uint32_t *arg, int mode)
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate 	uint32_t num_intr = PCI_MAX_INO;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) !=
2660Sstevel@tonic-gate 	    DDI_SUCCESS) {
2670Sstevel@tonic-gate 		return (EFAULT);
2680Sstevel@tonic-gate 	} else {
2690Sstevel@tonic-gate 		return (SUCCESS);
2700Sstevel@tonic-gate 	}
2710Sstevel@tonic-gate }
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate /*
2750Sstevel@tonic-gate  * Get interrupt information for a given ino.
2760Sstevel@tonic-gate  * Returns info only for inos mapped to devices.
2770Sstevel@tonic-gate  *
2780Sstevel@tonic-gate  * Returned info is valid only when iget.num_devs is returned > 0.
2790Sstevel@tonic-gate  * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
2800Sstevel@tonic-gate  */
2810Sstevel@tonic-gate /*ARGSUSED*/
2820Sstevel@tonic-gate static int
2830Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate 	/* Array part isn't used here, but oh well... */
2860Sstevel@tonic-gate 	pcitool_intr_get_t partial_iget;
2870Sstevel@tonic-gate 	pcitool_intr_get_t *iget = &partial_iget;
2880Sstevel@tonic-gate 	size_t	iget_kmem_alloc_size = 0;
2890Sstevel@tonic-gate 	ib_t *ib_p = pci_p->pci_ib_p;
2900Sstevel@tonic-gate 	volatile uint64_t *imregp;
2910Sstevel@tonic-gate 	uint64_t imregval;
2920Sstevel@tonic-gate 	uint32_t ino;
2930Sstevel@tonic-gate 	uint8_t num_devs_ret;
2940Sstevel@tonic-gate 	int copyout_rval;
2950Sstevel@tonic-gate 	int rval = SUCCESS;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	/* Read in just the header part, no array section. */
2980Sstevel@tonic-gate 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
2990Sstevel@tonic-gate 	    DDI_SUCCESS) {
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 		return (EFAULT);
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	ino = partial_iget.ino;
3050Sstevel@tonic-gate 	num_devs_ret = partial_iget.num_devs_ret;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	/* Validate argument. */
3080Sstevel@tonic-gate 	if (ino > PCI_MAX_INO) {
3090Sstevel@tonic-gate 		partial_iget.status = PCITOOL_INVALID_INO;
3100Sstevel@tonic-gate 		partial_iget.num_devs_ret = 0;
3110Sstevel@tonic-gate 		rval = EINVAL;
3120Sstevel@tonic-gate 		goto done_get_intr;
3130Sstevel@tonic-gate 	}
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	/* Caller wants device information returned. */
3160Sstevel@tonic-gate 	if (num_devs_ret > 0) {
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 		/*
3190Sstevel@tonic-gate 		 * Allocate room.
3200Sstevel@tonic-gate 		 * Note if num_devs_ret == 0 iget remains pointing to
3210Sstevel@tonic-gate 		 * partial_iget.
3220Sstevel@tonic-gate 		 */
3230Sstevel@tonic-gate 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
3240Sstevel@tonic-gate 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 		/* Read in whole structure to verify there's room. */
3270Sstevel@tonic-gate 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
3280Sstevel@tonic-gate 		    SUCCESS) {
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 			/* Be consistent and just return EFAULT here. */
3310Sstevel@tonic-gate 			kmem_free(iget, iget_kmem_alloc_size);
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 			return (EFAULT);
3340Sstevel@tonic-gate 		}
3350Sstevel@tonic-gate 	}
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
3380Sstevel@tonic-gate 	iget->ino = ino;
3390Sstevel@tonic-gate 	iget->num_devs_ret = num_devs_ret;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	imregp = ib_intr_map_reg_addr(ib_p, ino);
3420Sstevel@tonic-gate 	imregval = *imregp;
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	/*
3450Sstevel@tonic-gate 	 * Read "valid" bit.  If set, interrupts are enabled.
3460Sstevel@tonic-gate 	 * This bit happens to be the same on Fire and Tomatillo.
3470Sstevel@tonic-gate 	 */
3480Sstevel@tonic-gate 	if (imregval & COMMON_INTR_MAP_REG_VALID) {
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 		/*
3510Sstevel@tonic-gate 		 * The following looks up the ib_ino_info and returns
3520Sstevel@tonic-gate 		 * info of devices mapped to this ino.
3530Sstevel@tonic-gate 		 */
3540Sstevel@tonic-gate 		iget->num_devs = ib_get_ino_devs(
3550Sstevel@tonic-gate 		    ib_p, ino, &iget->num_devs_ret, iget->dev);
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 		/*
3580Sstevel@tonic-gate 		 * Consider only inos mapped to devices (as opposed to
3590Sstevel@tonic-gate 		 * inos mapped to the bridge itself.
3600Sstevel@tonic-gate 		 */
3610Sstevel@tonic-gate 		if (iget->num_devs > 0) {
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 			/*
3640Sstevel@tonic-gate 			 * These 2 items are platform specific,
3650Sstevel@tonic-gate 			 * extracted from the bridge.
3660Sstevel@tonic-gate 			 */
3670Sstevel@tonic-gate 			iget->ctlr = 0;
368*117Sschwartz 			iget->cpu_id = ib_map_reg_get_cpu(imregval);
3690Sstevel@tonic-gate 		}
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate done_get_intr:
3720Sstevel@tonic-gate 	iget->drvr_version = PCITOOL_DRVR_VERSION;
3730Sstevel@tonic-gate 	copyout_rval = ddi_copyout(iget, arg,
3740Sstevel@tonic-gate 	    PCITOOL_IGET_SIZE(num_devs_ret), mode);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	if (iget_kmem_alloc_size > 0) {
3770Sstevel@tonic-gate 		kmem_free(iget, iget_kmem_alloc_size);
3780Sstevel@tonic-gate 	}
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	if (copyout_rval != DDI_SUCCESS) {
3810Sstevel@tonic-gate 		rval = EFAULT;
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	return (rval);
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate /*
3880Sstevel@tonic-gate  * Associate a new CPU with a given ino.
3890Sstevel@tonic-gate  *
3900Sstevel@tonic-gate  * Operate only on inos which are already mapped to devices.
3910Sstevel@tonic-gate  */
3920Sstevel@tonic-gate static int
3930Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
3940Sstevel@tonic-gate {
395*117Sschwartz 	ib_t *ib_p = pci_p->pci_ib_p;
396*117Sschwartz 	int rval = SUCCESS;
397*117Sschwartz 
3980Sstevel@tonic-gate 	uint8_t zero = 0;
3990Sstevel@tonic-gate 	pcitool_intr_set_t iset;
4000Sstevel@tonic-gate 	uint32_t old_cpu_id;
4010Sstevel@tonic-gate 	hrtime_t start_time;
4020Sstevel@tonic-gate 	uint64_t imregval;
4030Sstevel@tonic-gate 	uint64_t new_imregval;
4040Sstevel@tonic-gate 	volatile uint64_t *imregp;
4050Sstevel@tonic-gate 	volatile uint64_t *idregp;
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) !=
408*117Sschwartz 	    DDI_SUCCESS)
4090Sstevel@tonic-gate 		return (EFAULT);
4100Sstevel@tonic-gate 
411*117Sschwartz 	/* Validate input argument and that ino given belongs to a device. */
412*117Sschwartz 	if ((iset.ino > PCI_MAX_INO) ||
413*117Sschwartz 	    (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
4140Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4150Sstevel@tonic-gate 		rval = EINVAL;
4160Sstevel@tonic-gate 		goto done_set_intr;
4170Sstevel@tonic-gate 	}
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
4200Sstevel@tonic-gate 	idregp = IB_INO_INTR_STATE_REG(ib_p, iset.ino);
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	DEBUG4(DBG_TOOLS, dip, "set_intr: cpu:%d, ino:0x%x, mapreg @ "
4230Sstevel@tonic-gate 	    "0x%llx, intr_stat @ 0x%llx\n",
4240Sstevel@tonic-gate 	    iset.cpu_id, iset.ino, imregp, idregp);
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	/* Save original mapreg value. */
4270Sstevel@tonic-gate 	imregval = *imregp;
4280Sstevel@tonic-gate 	DEBUG1(DBG_TOOLS, dip, "orig mapreg value: 0x%llx\n", imregval);
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	/* Is this request a noop? */
431*117Sschwartz 	if ((old_cpu_id = ib_map_reg_get_cpu(imregval)) == iset.cpu_id) {
4320Sstevel@tonic-gate 		iset.status = PCITOOL_SUCCESS;
4330Sstevel@tonic-gate 		goto done_set_intr;
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	/* Operate only on inos which are already enabled. */
4370Sstevel@tonic-gate 	if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
4380Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4390Sstevel@tonic-gate 		rval = EINVAL;
4400Sstevel@tonic-gate 		goto done_set_intr;
4410Sstevel@tonic-gate 	}
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	/* Clear the interrupt valid/enable bit for particular ino. */
4440Sstevel@tonic-gate 	DEBUG0(DBG_TOOLS, dip, "Clearing intr_enabled...\n");
4450Sstevel@tonic-gate 	*imregp = imregval & ~COMMON_INTR_MAP_REG_VALID;
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 	/* Wait until there are no more pending interrupts. */
4480Sstevel@tonic-gate 	start_time = gethrtime();
4490Sstevel@tonic-gate 
450*117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "About to check for pending interrupts...\n");
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	while (IB_INO_INTR_PENDING(idregp, iset.ino)) {
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Waiting for pending ints to clear\n");
455*117Sschwartz 		if ((gethrtime() - start_time) < pci_intrpend_timeout)
4560Sstevel@tonic-gate 			continue;
4570Sstevel@tonic-gate 
458*117Sschwartz 		else {	/* Timed out waiting. */
4590Sstevel@tonic-gate 			iset.status = PCITOOL_PENDING_INTRTIMEOUT;
4600Sstevel@tonic-gate 			rval = ETIME;
4610Sstevel@tonic-gate 			goto done_set_intr;
4620Sstevel@tonic-gate 		}
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	new_imregval = *imregp;
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	DEBUG1(DBG_TOOLS, dip,
4680Sstevel@tonic-gate 	    "after disabling intr, mapreg value: 0x%llx\n", new_imregval);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	/*
4710Sstevel@tonic-gate 	 * Get lock, validate cpu and write new mapreg value.
4720Sstevel@tonic-gate 	 * Return original cpu value to caller via iset.cpu_id.
4730Sstevel@tonic-gate 	 */
4740Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
475*117Sschwartz 	if (pcitool_validate_cpuid(iset.cpu_id)) {
476*117Sschwartz 
477*117Sschwartz 		/* Prepare new mapreg value with intr enabled and new cpu_id. */
478*117Sschwartz 		new_imregval &=
479*117Sschwartz 		    COMMON_INTR_MAP_REG_IGN | COMMON_INTR_MAP_REG_INO;
480*117Sschwartz 		new_imregval = ib_get_map_reg(new_imregval, iset.cpu_id);
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 		DEBUG1(DBG_TOOLS, dip, "Writing new mapreg value:0x%llx\n",
4830Sstevel@tonic-gate 		    new_imregval);
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 		*imregp = new_imregval;
486*117Sschwartz 
487*117Sschwartz 		ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino);
488*117Sschwartz 
4890Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
490*117Sschwartz 
4910Sstevel@tonic-gate 		iset.cpu_id = old_cpu_id;
4920Sstevel@tonic-gate 		iset.status = PCITOOL_SUCCESS;
4930Sstevel@tonic-gate 
494*117Sschwartz 	} else {	/* Invalid cpu.  Restore original register image. */
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip,
4970Sstevel@tonic-gate 		    "Invalid cpuid: writing orig mapreg value\n");
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 		*imregp = imregval;
5000Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
5010Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_CPUID;
5020Sstevel@tonic-gate 		rval = EINVAL;
5030Sstevel@tonic-gate 	}
5040Sstevel@tonic-gate done_set_intr:
5050Sstevel@tonic-gate 	iset.drvr_version = PCITOOL_DRVR_VERSION;
5060Sstevel@tonic-gate 	if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) !=
507*117Sschwartz 	    DDI_SUCCESS)
5080Sstevel@tonic-gate 		rval = EFAULT;
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	return (rval);
5110Sstevel@tonic-gate }
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */
5150Sstevel@tonic-gate int
5160Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
5170Sstevel@tonic-gate {
5180Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
5190Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
5200Sstevel@tonic-gate 	int		rval = SUCCESS;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	switch (cmd) {
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	/* Return the number of interrupts supported by a PCI bus. */
5250Sstevel@tonic-gate 	case PCITOOL_DEVICE_NUM_INTR:
5260Sstevel@tonic-gate 		rval = pcitool_intr_get_max_ino(arg, mode);
5270Sstevel@tonic-gate 		break;
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	/* Get interrupt information for a given ino. */
5300Sstevel@tonic-gate 	case PCITOOL_DEVICE_GET_INTR:
5310Sstevel@tonic-gate 		rval = pcitool_get_intr(dip, arg, mode, pci_p);
5320Sstevel@tonic-gate 		break;
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	/* Associate a new CPU with a given ino. */
5350Sstevel@tonic-gate 	case PCITOOL_DEVICE_SET_INTR:
5360Sstevel@tonic-gate 		rval = pcitool_set_intr(dip, arg, mode, pci_p);
5370Sstevel@tonic-gate 		break;
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	default:
5400Sstevel@tonic-gate 		rval = ENOTTY;
5410Sstevel@tonic-gate 	}
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	return (rval);
5440Sstevel@tonic-gate }
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate /*
548*117Sschwartz  * Wrapper around pcitool_phys_peek/poke.
5490Sstevel@tonic-gate  *
550*117Sschwartz  * Validates arguments and calls pcitool_phys_peek/poke appropriately.
5510Sstevel@tonic-gate  *
5520Sstevel@tonic-gate  * Dip is of the nexus,
5530Sstevel@tonic-gate  * phys_addr is the address to write in physical space,
5540Sstevel@tonic-gate  * max_addr is the upper bound on the physical space used for bounds checking,
5550Sstevel@tonic-gate  * pcitool_status returns more detailed status in addition to a more generic
5560Sstevel@tonic-gate  * errno-style function return value.
5570Sstevel@tonic-gate  * other args are self-explanatory.
5580Sstevel@tonic-gate  */
5590Sstevel@tonic-gate static int
560*117Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
561*117Sschwartz 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
562*117Sschwartz 	uint32_t *pcitool_status)
5630Sstevel@tonic-gate {
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	int rval = SUCCESS;
5660Sstevel@tonic-gate 	dev_info_t *dip = pci_p->pci_dip;
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	/* Upper bounds checking. */
5690Sstevel@tonic-gate 	if (phys_addr > max_addr) {
5700Sstevel@tonic-gate 		DEBUG2(DBG_TOOLS, dip,
5710Sstevel@tonic-gate 		    "Phys addr 0x%llx out of range (max 0x%llx).\n",
5720Sstevel@tonic-gate 		    phys_addr, max_addr);
5730Sstevel@tonic-gate 		*pcitool_status = PCITOOL_INVALID_ADDRESS;
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 		rval = EINVAL;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	/* Alignment checking. */
5780Sstevel@tonic-gate 	} else if (!IS_P2ALIGNED(phys_addr, size)) {
5790Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
5800Sstevel@tonic-gate 		*pcitool_status = PCITOOL_NOT_ALIGNED;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 		rval = EINVAL;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	/* Made it through checks.  Do the access. */
5850Sstevel@tonic-gate 	} else if (write) {
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
588*117Sschwartz 		    "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
5890Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
5900Sstevel@tonic-gate 
591*117Sschwartz 		if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
592*117Sschwartz 		    *data) != DDI_SUCCESS) {
5930Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
594*117Sschwartz 			    "%d byte %s pcitool_phys_poke at addr "
5950Sstevel@tonic-gate 			    "0x%llx failed\n",
5960Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
5970Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 			rval = EFAULT;
6000Sstevel@tonic-gate 		}
6010Sstevel@tonic-gate 
602*117Sschwartz 	} else {	/* Read */
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
605*117Sschwartz 		    "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
6060Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
6070Sstevel@tonic-gate 
608*117Sschwartz 		if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
609*117Sschwartz 		    data) != DDI_SUCCESS) {
6100Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
611*117Sschwartz 			    "%d byte %s pcitool_phys_peek at addr "
6120Sstevel@tonic-gate 			    "0x%llx failed\n",
6130Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
6140Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 			rval = EFAULT;
6170Sstevel@tonic-gate 		}
6180Sstevel@tonic-gate 	}
6190Sstevel@tonic-gate 	return (rval);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate /*
6230Sstevel@tonic-gate  * Perform register accesses on the nexus device itself.
6240Sstevel@tonic-gate  */
6250Sstevel@tonic-gate int
6260Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
6270Sstevel@tonic-gate {
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	pci_t			*pci_p = DEV_TO_SOFTSTATE(dev);
6300Sstevel@tonic-gate 	dev_info_t		*dip = pci_p->pci_dip;
631*117Sschwartz 	pci_nexus_regspec_t	*pci_rp = NULL;
632*117Sschwartz 	boolean_t		write_flag = B_FALSE;
6330Sstevel@tonic-gate 	pcitool_reg_t		prg;
6340Sstevel@tonic-gate 	uint64_t		base_addr;
6350Sstevel@tonic-gate 	uint64_t		max_addr;
6360Sstevel@tonic-gate 	uint32_t		reglen;
6370Sstevel@tonic-gate 	uint8_t			size;
6380Sstevel@tonic-gate 	uint32_t		rval = 0;
6390Sstevel@tonic-gate 
640*117Sschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
6410Sstevel@tonic-gate 		write_flag = B_TRUE;
6420Sstevel@tonic-gate 
643*117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
6440Sstevel@tonic-gate 
645*117Sschwartz 	/* Read data from userland. */
646*117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
647*117Sschwartz 	    DDI_SUCCESS) {
648*117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
649*117Sschwartz 		return (EFAULT);
650*117Sschwartz 	}
6510Sstevel@tonic-gate 
652*117Sschwartz 	/* Read reg property which contains starting addr and size of banks. */
653*117Sschwartz 	if (ddi_prop_lookup_int_array(
654*117Sschwartz 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
655*117Sschwartz 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
656*117Sschwartz 		if (((reglen * sizeof (int)) %
657*117Sschwartz 		    sizeof (pci_nexus_regspec_t)) != 0) {
658*117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
659*117Sschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
660*117Sschwartz 			rval = EIO;
6610Sstevel@tonic-gate 			goto done;
6620Sstevel@tonic-gate 		}
663*117Sschwartz 	}
6640Sstevel@tonic-gate 
665*117Sschwartz 	/* Bounds check the bank number. */
666*117Sschwartz 	if (prg.barnum >=
667*117Sschwartz 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
668*117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
669*117Sschwartz 		rval = EINVAL;
670*117Sschwartz 		goto done;
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 
673*117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
674*117Sschwartz 	base_addr = pci_rp[prg.barnum].phys_addr;
675*117Sschwartz 	max_addr = base_addr + pci_rp[prg.barnum].size;
676*117Sschwartz 	prg.phys_addr = base_addr + prg.offset;
677*117Sschwartz 
678*117Sschwartz 	DEBUG4(DBG_TOOLS, dip,
679*117Sschwartz 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
680*117Sschwartz 	    "addr:0x%llx, max_addr:0x%llx\n",
681*117Sschwartz 	    base_addr, prg.offset, prg.phys_addr, max_addr);
682*117Sschwartz 
683*117Sschwartz 	/* Access device.  prg.status is modified. */
684*117Sschwartz 	rval = pcitool_access(pci_p,
685*117Sschwartz 	    prg.phys_addr, max_addr, &prg.data, size, write_flag,
686*117Sschwartz 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
687*117Sschwartz 
6880Sstevel@tonic-gate done:
689*117Sschwartz 	if (pci_rp != NULL)
690*117Sschwartz 		ddi_prop_free(pci_rp);
691*117Sschwartz 
6920Sstevel@tonic-gate 	prg.drvr_version = PCITOOL_DRVR_VERSION;
6930Sstevel@tonic-gate 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
6940Sstevel@tonic-gate 	    DDI_SUCCESS) {
6950Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
6960Sstevel@tonic-gate 		return (EFAULT);
6970Sstevel@tonic-gate 	}
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 	return (rval);
7000Sstevel@tonic-gate }
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 
703*117Sschwartz static int
704*117Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
705*117Sschwartz {
706*117Sschwartz 	int rval = SUCCESS;
707*117Sschwartz 
708*117Sschwartz 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
709*117Sschwartz 		prg->status = PCITOOL_OUT_OF_RANGE;
710*117Sschwartz 		rval = EINVAL;
711*117Sschwartz 
712*117Sschwartz 	/* Validate address arguments of bus / dev / func */
713*117Sschwartz 	} else if (((prg->bus_no &
714*117Sschwartz 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
715*117Sschwartz 	    ((prg->dev_no &
716*117Sschwartz 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
717*117Sschwartz 	    ((prg->func_no &
718*117Sschwartz 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
719*117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
720*117Sschwartz 		rval = EINVAL;
721*117Sschwartz 	}
722*117Sschwartz 
723*117Sschwartz 	return (rval);
724*117Sschwartz }
725*117Sschwartz 
726*117Sschwartz static int
727*117Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
728*117Sschwartz 	uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
729*117Sschwartz {
730*117Sschwartz 
731*117Sschwartz 	uint8_t bar_offset;
732*117Sschwartz 	int rval;
733*117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
734*117Sschwartz 
735*117Sschwartz 	*bar = 0;
736*117Sschwartz 	*is_io_space = B_FALSE;
737*117Sschwartz 
738*117Sschwartz 	/*
739*117Sschwartz 	 * Translate BAR number into offset of the BAR in
740*117Sschwartz 	 * the device's config space.
741*117Sschwartz 	 */
742*117Sschwartz 	bar_offset = PCI_BAR_OFFSET((*prg));
743*117Sschwartz 
744*117Sschwartz 	DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
745*117Sschwartz 	    prg->barnum, bar_offset);
746*117Sschwartz 
747*117Sschwartz 	/*
748*117Sschwartz 	 * Get Bus Address Register (BAR) from config space.
749*117Sschwartz 	 * bar_offset is the offset into config space of the BAR desired.
750*117Sschwartz 	 * prg->status is modified on error.
751*117Sschwartz 	 */
752*117Sschwartz 	rval = pcitool_access(pci_p, config_base_addr + bar_offset,
753*117Sschwartz 	    config_max_addr, bar,
754*117Sschwartz 	    4,		/* 4 bytes. */
755*117Sschwartz 	    B_FALSE,	/* Read */
756*117Sschwartz 	    B_FALSE, 	/* Little endian. */
757*117Sschwartz 	    &prg->status);
758*117Sschwartz 	if (rval != SUCCESS)
759*117Sschwartz 		return (rval);
760*117Sschwartz 
761*117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
762*117Sschwartz 	if (!(*bar)) {
763*117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
764*117Sschwartz 		return (EINVAL);
765*117Sschwartz 	}
766*117Sschwartz 
767*117Sschwartz 	/*
768*117Sschwartz 	 * BAR has bits saying this space is IO space, unless
769*117Sschwartz 	 * this is the ROM address register.
770*117Sschwartz 	 */
771*117Sschwartz 	if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
772*117Sschwartz 	    (bar_offset != PCI_CONF_ROM)) {
773*117Sschwartz 		*is_io_space = B_TRUE;
774*117Sschwartz 		*bar &= PCI_BASE_IO_ADDR_M;
775*117Sschwartz 
776*117Sschwartz 	/*
777*117Sschwartz 	 * BAR has bits saying this space is 64 bit memory
778*117Sschwartz 	 * space, unless this is the ROM address register.
779*117Sschwartz 	 *
780*117Sschwartz 	 * The 64 bit address stored in two BAR cells is not necessarily
781*117Sschwartz 	 * aligned on an 8-byte boundary.  Need to keep the first 4
782*117Sschwartz 	 * bytes read, and do a separate read of the high 4 bytes.
783*117Sschwartz 	 */
784*117Sschwartz 
785*117Sschwartz 	} else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
786*117Sschwartz 
787*117Sschwartz 		uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
788*117Sschwartz 
789*117Sschwartz 		/* Don't try to read past the end of BARs. */
790*117Sschwartz 		if (bar_offset >= PCI_CONF_BASE5) {
791*117Sschwartz 			prg->status = PCITOOL_OUT_OF_RANGE;
792*117Sschwartz 			return (EIO);
793*117Sschwartz 		}
794*117Sschwartz 
795*117Sschwartz 		/* Access device.  prg->status is modified on error. */
796*117Sschwartz 		rval = pcitool_access(pci_p,
797*117Sschwartz 		    config_base_addr + bar_offset + 4, config_max_addr, bar,
798*117Sschwartz 		    4,		/* 4 bytes. */
799*117Sschwartz 		    B_FALSE,	/* Read */
800*117Sschwartz 		    B_FALSE, 	/* Little endian. */
801*117Sschwartz 		    &prg->status);
802*117Sschwartz 		if (rval != SUCCESS)
803*117Sschwartz 			return (rval);
804*117Sschwartz 
805*117Sschwartz 		*bar = (*bar << 32) + low_bytes;
806*117Sschwartz 	}
807*117Sschwartz 
808*117Sschwartz 	return (SUCCESS);
809*117Sschwartz }
810*117Sschwartz 
811*117Sschwartz 
812*117Sschwartz static int
813*117Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
814*117Sschwartz 	uint64_t max_addr, uint8_t size, boolean_t write_flag)
815*117Sschwartz {
816*117Sschwartz 	int rval;
817*117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
818*117Sschwartz 
819*117Sschwartz 	/* Access config space and we're done. */
820*117Sschwartz 	prg->phys_addr = base_addr + prg->offset;
821*117Sschwartz 
822*117Sschwartz 	DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
823*117Sschwartz 	    "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
824*117Sschwartz 	    base_addr, prg->offset, prg->phys_addr,
825*117Sschwartz 	    (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
826*117Sschwartz 
827*117Sschwartz 	/* Access device.  pr.status is modified. */
828*117Sschwartz 	rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
829*117Sschwartz 	    write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
830*117Sschwartz 
831*117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
832*117Sschwartz 
833*117Sschwartz 	return (rval);
834*117Sschwartz }
835*117Sschwartz 
8360Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
8370Sstevel@tonic-gate int
8380Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
8390Sstevel@tonic-gate {
8400Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
8410Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
8420Sstevel@tonic-gate 	pci_ranges_t	*rp = pci_p->pci_ranges;
8430Sstevel@tonic-gate 	pcitool_reg_t	prg;
8440Sstevel@tonic-gate 	uint64_t	max_addr;
8450Sstevel@tonic-gate 	uint64_t	base_addr;
8460Sstevel@tonic-gate 	uint64_t	range_prop;
8470Sstevel@tonic-gate 	uint64_t	range_prop_size;
8480Sstevel@tonic-gate 	uint64_t	bar = 0;
8490Sstevel@tonic-gate 	int		rval = 0;
8500Sstevel@tonic-gate 	boolean_t	write_flag = B_FALSE;
851*117Sschwartz 	boolean_t	is_io_space = B_FALSE;
8520Sstevel@tonic-gate 	uint8_t		size;
8530Sstevel@tonic-gate 
854*117Sschwartz 	if (cmd == PCITOOL_DEVICE_SET_REG)
8550Sstevel@tonic-gate 		write_flag = B_TRUE;
8560Sstevel@tonic-gate 
857*117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
858*117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
859*117Sschwartz 	    DDI_SUCCESS) {
860*117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
861*117Sschwartz 		return (EFAULT);
862*117Sschwartz 	}
8630Sstevel@tonic-gate 
864*117Sschwartz 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
865*117Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
8660Sstevel@tonic-gate 
867*117Sschwartz 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
868*117Sschwartz 		goto done_reg;
869*117Sschwartz 
870*117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
8710Sstevel@tonic-gate 
872*117Sschwartz 	/* Get config space first. */
873*117Sschwartz 	range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
874*117Sschwartz 	range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
875*117Sschwartz 	max_addr = range_prop + range_prop_size;
8760Sstevel@tonic-gate 
877*117Sschwartz 	/*
878*117Sschwartz 	 * Build device address based on base addr from range prop, and bus,
879*117Sschwartz 	 * dev and func values passed in.  This address is where config space
880*117Sschwartz 	 * begins.
881*117Sschwartz 	 */
882*117Sschwartz 	base_addr = range_prop +
883*117Sschwartz 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
884*117Sschwartz 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
885*117Sschwartz 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
8860Sstevel@tonic-gate 
887*117Sschwartz 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
888*117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
889*117Sschwartz 		rval = EINVAL;
890*117Sschwartz 		goto done_reg;
891*117Sschwartz 	}
8920Sstevel@tonic-gate 
893*117Sschwartz 	DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
894*117Sschwartz 	    "func:0x%x, addr:0x%x\n", range_prop,
895*117Sschwartz 	    prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
896*117Sschwartz 	    prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
8970Sstevel@tonic-gate 
898*117Sschwartz 	/* Proper config space desired. */
899*117Sschwartz 	if (prg.barnum == 0) {
900*117Sschwartz 
901*117Sschwartz 		rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
902*117Sschwartz 		    size, write_flag);
9030Sstevel@tonic-gate 
904*117Sschwartz 	} else {	/* IO / MEM / MEM64 space. */
9050Sstevel@tonic-gate 
906*117Sschwartz 		if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
907*117Sschwartz 		    &is_io_space) != SUCCESS)
908*117Sschwartz 			goto done_reg;
9090Sstevel@tonic-gate 
910*117Sschwartz 		/* IO space. */
911*117Sschwartz 		if (is_io_space) {
9120Sstevel@tonic-gate 
913*117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
9140Sstevel@tonic-gate 
915*117Sschwartz 			/* Reposition to focus on IO space. */
916*117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
917*117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
918*117Sschwartz 			    PCI_IO_RANGE_BANK);
9190Sstevel@tonic-gate 
920*117Sschwartz 		/* 64 bit memory space. */
921*117Sschwartz 		} else if ((bar >> 32) != 0) {
922*117Sschwartz 
923*117Sschwartz 			DEBUG1(DBG_TOOLS, dip,
924*117Sschwartz 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
9250Sstevel@tonic-gate 
926*117Sschwartz 			/* Reposition to MEM64 range space. */
927*117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp,
928*117Sschwartz 			    PCI_MEM64_RANGE_BANK);
929*117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
930*117Sschwartz 			    PCI_MEM64_RANGE_BANK);
9310Sstevel@tonic-gate 
932*117Sschwartz 		} else {	/* Mem32 space, including ROM */
9330Sstevel@tonic-gate 
934*117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
9350Sstevel@tonic-gate 
936*117Sschwartz 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9370Sstevel@tonic-gate 
938*117Sschwartz 				DEBUG0(DBG_TOOLS, dip,
939*117Sschwartz 				    "Additional ROM checking\n");
9400Sstevel@tonic-gate 
941*117Sschwartz 				/* Can't write to ROM */
942*117Sschwartz 				if (write_flag) {
943*117Sschwartz 					prg.status = PCITOOL_ROM_WRITE;
944*117Sschwartz 					rval = EIO;
945*117Sschwartz 					goto done_reg;
9460Sstevel@tonic-gate 
947*117Sschwartz 				/* ROM disabled for reading */
948*117Sschwartz 				} else if (!(bar & 0x00000001)) {
949*117Sschwartz 					prg.status = PCITOOL_ROM_DISABLED;
9500Sstevel@tonic-gate 					rval = EIO;
9510Sstevel@tonic-gate 					goto done_reg;
9520Sstevel@tonic-gate 				}
9530Sstevel@tonic-gate 			}
9540Sstevel@tonic-gate 
955*117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
956*117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
957*117Sschwartz 			    PCI_MEM_RANGE_BANK);
958*117Sschwartz 		}
9590Sstevel@tonic-gate 
960*117Sschwartz 		/* Common code for all IO/MEM range spaces. */
961*117Sschwartz 		max_addr = range_prop + range_prop_size;
962*117Sschwartz 		base_addr = range_prop + bar;
963*117Sschwartz 
964*117Sschwartz 		DEBUG3(DBG_TOOLS, dip,
965*117Sschwartz 		    "addr portion of bar is 0x%llx, base=0x%llx, "
966*117Sschwartz 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
9670Sstevel@tonic-gate 
968*117Sschwartz 		/*
969*117Sschwartz 		 * Use offset provided by caller to index into
970*117Sschwartz 		 * desired space, then access.
971*117Sschwartz 		 * Note that prg.status is modified on error.
972*117Sschwartz 		 */
973*117Sschwartz 		prg.phys_addr = base_addr + prg.offset;
974*117Sschwartz 		rval = pcitool_access(pci_p, prg.phys_addr,
975*117Sschwartz 		    max_addr, &prg.data, size, write_flag,
976*117Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
977*117Sschwartz 	}
978*117Sschwartz 
9790Sstevel@tonic-gate done_reg:
980*117Sschwartz 	prg.drvr_version = PCITOOL_DRVR_VERSION;
981*117Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
982*117Sschwartz 	    DDI_SUCCESS) {
983*117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
984*117Sschwartz 		rval = EFAULT;
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 	return (rval);
9870Sstevel@tonic-gate }
988*117Sschwartz 
989*117Sschwartz int
990*117Sschwartz pcitool_init(dev_info_t *dip)
991*117Sschwartz {
992*117Sschwartz 	int instance = ddi_get_instance(dip);
993*117Sschwartz 
994*117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
995*117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
996*117Sschwartz 	    DDI_NT_REGACC, 0) != DDI_SUCCESS)
997*117Sschwartz 		return (DDI_FAILURE);
998*117Sschwartz 
999*117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
1000*117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
1001*117Sschwartz 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
1002*117Sschwartz 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
1003*117Sschwartz 		return (DDI_FAILURE);
1004*117Sschwartz 	}
1005*117Sschwartz 
1006*117Sschwartz 	return (DDI_SUCCESS);
1007*117Sschwartz }
1008*117Sschwartz 
1009*117Sschwartz void
1010*117Sschwartz pcitool_uninit(dev_info_t *dip)
1011*117Sschwartz {
1012*117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
1013*117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
1014*117Sschwartz }
1015