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
54397Sschwartz * Common Development and Distribution License (the "License").
64397Sschwartz * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*10053SEvan.Yan@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
26117Sschwartz #include <sys/stat.h>
27117Sschwartz #include <sys/sunddi.h>
28117Sschwartz #include <sys/param.h>
29117Sschwartz
300Sstevel@tonic-gate #include <sys/sysmacros.h>
310Sstevel@tonic-gate #include <sys/machsystm.h>
320Sstevel@tonic-gate #include <sys/promif.h>
330Sstevel@tonic-gate #include <sys/cpuvar.h>
340Sstevel@tonic-gate
350Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
360Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
370Sstevel@tonic-gate
380Sstevel@tonic-gate #include <sys/pci_tools.h>
39777Sschwartz #include <sys/pci/pci_tools_ext.h>
40777Sschwartz
41777Sschwartz /*
42777Sschwartz * Number of interrupts supported per PCI bus.
43777Sschwartz */
44777Sschwartz #define PCI_MAX_INO 0x3f
45777Sschwartz
46777Sschwartz /*
47777Sschwartz * PCI Space definitions.
48777Sschwartz */
49777Sschwartz #define PCI_CONFIG_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
50777Sschwartz #define PCI_IO_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_IO))
51777Sschwartz #define PCI_MEM_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM32))
52777Sschwartz #define PCI_MEM64_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM64))
530Sstevel@tonic-gate
540Sstevel@tonic-gate /*
550Sstevel@tonic-gate * Extract 64 bit parent or size values from 32 bit cells of
560Sstevel@tonic-gate * pci_ranges_t property.
570Sstevel@tonic-gate *
580Sstevel@tonic-gate * Only bits 42-32 are relevant in parent_high.
590Sstevel@tonic-gate */
600Sstevel@tonic-gate #define PCI_GET_RANGE_PROP(ranges, bank) \
610Sstevel@tonic-gate ((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
620Sstevel@tonic-gate ranges[bank].parent_low)
630Sstevel@tonic-gate
640Sstevel@tonic-gate #define PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
650Sstevel@tonic-gate ((((uint64_t)(ranges[bank].size_high)) << 32) | \
660Sstevel@tonic-gate ranges[bank].size_low)
670Sstevel@tonic-gate
68117Sschwartz #define PCI_BAR_OFFSET(x) (pci_bars[x.barnum])
69117Sschwartz
700Sstevel@tonic-gate /* Big and little endian as boolean values. */
710Sstevel@tonic-gate #define BE B_TRUE
720Sstevel@tonic-gate #define LE B_FALSE
730Sstevel@tonic-gate
740Sstevel@tonic-gate #define SUCCESS 0
750Sstevel@tonic-gate
760Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
770Sstevel@tonic-gate typedef union {
780Sstevel@tonic-gate uint64_t u64;
790Sstevel@tonic-gate uint32_t u32;
800Sstevel@tonic-gate uint16_t u16;
810Sstevel@tonic-gate uint8_t u8;
820Sstevel@tonic-gate } peek_poke_value_t;
830Sstevel@tonic-gate
840Sstevel@tonic-gate /*
850Sstevel@tonic-gate * Offsets of BARS in config space. First entry of 0 means config space.
860Sstevel@tonic-gate * Entries here correlate to pcitool_bars_t enumerated type.
870Sstevel@tonic-gate */
880Sstevel@tonic-gate static uint8_t pci_bars[] = {
890Sstevel@tonic-gate 0x0,
900Sstevel@tonic-gate PCI_CONF_BASE0,
910Sstevel@tonic-gate PCI_CONF_BASE1,
920Sstevel@tonic-gate PCI_CONF_BASE2,
930Sstevel@tonic-gate PCI_CONF_BASE3,
940Sstevel@tonic-gate PCI_CONF_BASE4,
950Sstevel@tonic-gate PCI_CONF_BASE5,
960Sstevel@tonic-gate PCI_CONF_ROM
970Sstevel@tonic-gate };
980Sstevel@tonic-gate
990Sstevel@tonic-gate /*LINTLIBRARY*/
1000Sstevel@tonic-gate
101117Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
102117Sschwartz uint64_t paddr, uint64_t *value_p);
103117Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
104117Sschwartz uint64_t paddr, uint64_t value);
105117Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
106117Sschwartz uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
107117Sschwartz uint32_t *pcitool_status);
108117Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
109117Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
110117Sschwartz uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
111117Sschwartz boolean_t *is_io_space);
112117Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
113117Sschwartz uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
114117Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
115117Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
116117Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1170Sstevel@tonic-gate
1180Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
1190Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
1200Sstevel@tonic-gate
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate * Safe C wrapper around assy language routine pci_do_phys_peek
1230Sstevel@tonic-gate *
1240Sstevel@tonic-gate * Type is TRUE for big endian, FALSE for little endian.
1250Sstevel@tonic-gate * Size is 1, 2, 4 or 8 bytes.
1260Sstevel@tonic-gate * paddr is the physical address in IO space to access read.
1270Sstevel@tonic-gate * value_p is where the value is returned.
1280Sstevel@tonic-gate */
1290Sstevel@tonic-gate static int
pcitool_phys_peek(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)130117Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
131117Sschwartz uint64_t paddr, uint64_t *value_p)
1320Sstevel@tonic-gate {
1330Sstevel@tonic-gate on_trap_data_t otd;
1340Sstevel@tonic-gate int err = DDI_SUCCESS;
135117Sschwartz peek_poke_value_t peek_value;
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p;
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate pbm_p->pbm_ontrap_data = &otd;
1400Sstevel@tonic-gate
1410Sstevel@tonic-gate /* Set up trap handling to make the access safe. */
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate * on_trap works like setjmp.
1450Sstevel@tonic-gate * Set it up to not panic on data access error,
1460Sstevel@tonic-gate * but to call peek_fault instead.
1470Sstevel@tonic-gate * Call pci_do_phys_peek after trap handling is setup.
1480Sstevel@tonic-gate * When on_trap returns FALSE, it has been setup.
1490Sstevel@tonic-gate * When it returns TRUE, an it has caught an error.
1500Sstevel@tonic-gate */
1510Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) {
1520Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&peek_fault;
1530Sstevel@tonic-gate err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
1540Sstevel@tonic-gate } else {
1550Sstevel@tonic-gate err = DDI_FAILURE;
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate pbm_p->pbm_ontrap_data = NULL;
1590Sstevel@tonic-gate no_trap();
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate if (err != DDI_FAILURE) {
1620Sstevel@tonic-gate switch (size) {
1630Sstevel@tonic-gate case 8:
1640Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u64;
1650Sstevel@tonic-gate break;
1660Sstevel@tonic-gate case 4:
1670Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u32;
1680Sstevel@tonic-gate break;
1690Sstevel@tonic-gate case 2:
1700Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u16;
1710Sstevel@tonic-gate break;
1720Sstevel@tonic-gate case 1:
1730Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u8;
1740Sstevel@tonic-gate break;
1750Sstevel@tonic-gate default:
1760Sstevel@tonic-gate err = DDI_FAILURE;
1770Sstevel@tonic-gate }
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate return (err);
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate * Safe C wrapper around assy language routine pci_do_phys_poke
1850Sstevel@tonic-gate *
1860Sstevel@tonic-gate * Type is TRUE for big endian, FALSE for little endian.
1870Sstevel@tonic-gate * Size is 1,2,4 or 8 bytes.
1880Sstevel@tonic-gate * paddr is the physical address in IO space to access read.
1890Sstevel@tonic-gate * value contains the value to be written.
1900Sstevel@tonic-gate */
1910Sstevel@tonic-gate static int
pcitool_phys_poke(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)192117Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
193117Sschwartz uint64_t paddr, uint64_t value)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate on_trap_data_t otd;
1960Sstevel@tonic-gate int err = DDI_SUCCESS;
1970Sstevel@tonic-gate peek_poke_value_t poke_value;
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p;
2000Sstevel@tonic-gate
2010Sstevel@tonic-gate switch (size) {
2020Sstevel@tonic-gate case 8:
2030Sstevel@tonic-gate poke_value.u64 = value;
2040Sstevel@tonic-gate break;
2050Sstevel@tonic-gate case 4:
2060Sstevel@tonic-gate poke_value.u32 = (uint32_t)value;
2070Sstevel@tonic-gate break;
2080Sstevel@tonic-gate case 2:
2090Sstevel@tonic-gate poke_value.u16 = (uint16_t)value;
2100Sstevel@tonic-gate break;
2110Sstevel@tonic-gate case 1:
2120Sstevel@tonic-gate poke_value.u8 = (uint8_t)value;
2130Sstevel@tonic-gate break;
2140Sstevel@tonic-gate default:
2150Sstevel@tonic-gate return (DDI_FAILURE);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate mutex_enter(&pbm_p->pbm_pokefault_mutex);
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate pbm_p->pbm_ontrap_data = &otd;
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate /*
2230Sstevel@tonic-gate * on_trap works like setjmp.
2240Sstevel@tonic-gate * Set it up to not panic on data access error,
2250Sstevel@tonic-gate * but to call poke_fault instead.
2260Sstevel@tonic-gate * Call pci_do_phys_poke after trap handling is setup.
2270Sstevel@tonic-gate * When on_trap returns FALSE, it has been setup.
2280Sstevel@tonic-gate * When it returns TRUE, an it has caught an error.
2290Sstevel@tonic-gate */
2300Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) {
2310Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&poke_fault;
2320Sstevel@tonic-gate err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate /* Let the dust settle and errors occur if they will. */
2360Sstevel@tonic-gate pbm_clear_error(pbm_p);
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate /* Check for an error. */
2390Sstevel@tonic-gate if (otd.ot_trap == OT_DATA_ACCESS) {
2400Sstevel@tonic-gate err = DDI_FAILURE;
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate pbm_p->pbm_ontrap_data = NULL;
2440Sstevel@tonic-gate mutex_exit(&pbm_p->pbm_pokefault_mutex);
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate no_trap();
2470Sstevel@tonic-gate return (err);
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate
2514397Sschwartz /*ARGSUSED*/
2520Sstevel@tonic-gate static int
pcitool_intr_info(dev_info_t * dip,void * arg,int mode)2534397Sschwartz pcitool_intr_info(dev_info_t *dip, void *arg, int mode)
2540Sstevel@tonic-gate {
2554397Sschwartz pcitool_intr_info_t intr_info;
2564397Sschwartz int rval = SUCCESS;
2570Sstevel@tonic-gate
2584397Sschwartz /* If we need user_version, and to ret same user version as passed in */
2594397Sschwartz if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
2600Sstevel@tonic-gate DDI_SUCCESS) {
2610Sstevel@tonic-gate return (EFAULT);
2620Sstevel@tonic-gate }
2634397Sschwartz
264*10053SEvan.Yan@Sun.COM if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
265*10053SEvan.Yan@Sun.COM return (ENOTSUP);
266*10053SEvan.Yan@Sun.COM
2674397Sschwartz intr_info.ctlr_version = 0; /* XXX how to get real version? */
2684397Sschwartz intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
2694397Sschwartz intr_info.num_intr = PCI_MAX_INO;
2704397Sschwartz
2714397Sschwartz intr_info.drvr_version = PCITOOL_VERSION;
2724397Sschwartz if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
2734397Sschwartz DDI_SUCCESS) {
2744397Sschwartz rval = EFAULT;
2754397Sschwartz }
2764397Sschwartz
2774397Sschwartz return (rval);
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate * Get interrupt information for a given ino.
2830Sstevel@tonic-gate * Returns info only for inos mapped to devices.
2840Sstevel@tonic-gate *
2850Sstevel@tonic-gate * Returned info is valid only when iget.num_devs is returned > 0.
2860Sstevel@tonic-gate * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
2870Sstevel@tonic-gate */
2880Sstevel@tonic-gate /*ARGSUSED*/
2890Sstevel@tonic-gate static int
pcitool_get_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)2900Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
2910Sstevel@tonic-gate {
2920Sstevel@tonic-gate /* Array part isn't used here, but oh well... */
2930Sstevel@tonic-gate pcitool_intr_get_t partial_iget;
2940Sstevel@tonic-gate pcitool_intr_get_t *iget = &partial_iget;
2950Sstevel@tonic-gate size_t iget_kmem_alloc_size = 0;
2960Sstevel@tonic-gate ib_t *ib_p = pci_p->pci_ib_p;
2970Sstevel@tonic-gate volatile uint64_t *imregp;
2980Sstevel@tonic-gate uint64_t imregval;
2990Sstevel@tonic-gate uint32_t ino;
3000Sstevel@tonic-gate uint8_t num_devs_ret;
301*10053SEvan.Yan@Sun.COM int cpu_id;
3020Sstevel@tonic-gate int copyout_rval;
3030Sstevel@tonic-gate int rval = SUCCESS;
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate /* Read in just the header part, no array section. */
3060Sstevel@tonic-gate if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
3070Sstevel@tonic-gate DDI_SUCCESS) {
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate return (EFAULT);
3100Sstevel@tonic-gate }
3110Sstevel@tonic-gate
312*10053SEvan.Yan@Sun.COM if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) {
313*10053SEvan.Yan@Sun.COM partial_iget.status = PCITOOL_IO_ERROR;
314*10053SEvan.Yan@Sun.COM partial_iget.num_devs_ret = 0;
315*10053SEvan.Yan@Sun.COM rval = ENOTSUP;
316*10053SEvan.Yan@Sun.COM goto done_get_intr;
317*10053SEvan.Yan@Sun.COM }
318*10053SEvan.Yan@Sun.COM
3190Sstevel@tonic-gate ino = partial_iget.ino;
3200Sstevel@tonic-gate num_devs_ret = partial_iget.num_devs_ret;
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate /* Validate argument. */
3230Sstevel@tonic-gate if (ino > PCI_MAX_INO) {
3240Sstevel@tonic-gate partial_iget.status = PCITOOL_INVALID_INO;
3250Sstevel@tonic-gate partial_iget.num_devs_ret = 0;
3260Sstevel@tonic-gate rval = EINVAL;
3270Sstevel@tonic-gate goto done_get_intr;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate /* Caller wants device information returned. */
3310Sstevel@tonic-gate if (num_devs_ret > 0) {
3320Sstevel@tonic-gate
3330Sstevel@tonic-gate /*
3340Sstevel@tonic-gate * Allocate room.
3350Sstevel@tonic-gate * Note if num_devs_ret == 0 iget remains pointing to
3360Sstevel@tonic-gate * partial_iget.
3370Sstevel@tonic-gate */
3380Sstevel@tonic-gate iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
3390Sstevel@tonic-gate iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate /* Read in whole structure to verify there's room. */
3420Sstevel@tonic-gate if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
3430Sstevel@tonic-gate SUCCESS) {
3440Sstevel@tonic-gate
3450Sstevel@tonic-gate /* Be consistent and just return EFAULT here. */
3460Sstevel@tonic-gate kmem_free(iget, iget_kmem_alloc_size);
3470Sstevel@tonic-gate
3480Sstevel@tonic-gate return (EFAULT);
3490Sstevel@tonic-gate }
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate
3520Sstevel@tonic-gate bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
3530Sstevel@tonic-gate iget->ino = ino;
3540Sstevel@tonic-gate iget->num_devs_ret = num_devs_ret;
3550Sstevel@tonic-gate
3560Sstevel@tonic-gate imregp = ib_intr_map_reg_addr(ib_p, ino);
3570Sstevel@tonic-gate imregval = *imregp;
3580Sstevel@tonic-gate
3590Sstevel@tonic-gate /*
3600Sstevel@tonic-gate * Read "valid" bit. If set, interrupts are enabled.
3610Sstevel@tonic-gate * This bit happens to be the same on Fire and Tomatillo.
3620Sstevel@tonic-gate */
3630Sstevel@tonic-gate if (imregval & COMMON_INTR_MAP_REG_VALID) {
3640Sstevel@tonic-gate /*
3650Sstevel@tonic-gate * The following looks up the ib_ino_info and returns
3660Sstevel@tonic-gate * info of devices mapped to this ino.
3670Sstevel@tonic-gate */
3680Sstevel@tonic-gate iget->num_devs = ib_get_ino_devs(
3690Sstevel@tonic-gate ib_p, ino, &iget->num_devs_ret, iget->dev);
3700Sstevel@tonic-gate
371*10053SEvan.Yan@Sun.COM if (ib_get_intr_target(pci_p, ino, &cpu_id) != DDI_SUCCESS) {
372*10053SEvan.Yan@Sun.COM iget->status = PCITOOL_IO_ERROR;
373*10053SEvan.Yan@Sun.COM rval = EIO;
374*10053SEvan.Yan@Sun.COM goto done_get_intr;
375*10053SEvan.Yan@Sun.COM }
376*10053SEvan.Yan@Sun.COM
3770Sstevel@tonic-gate /*
3780Sstevel@tonic-gate * Consider only inos mapped to devices (as opposed to
3790Sstevel@tonic-gate * inos mapped to the bridge itself.
3800Sstevel@tonic-gate */
3810Sstevel@tonic-gate if (iget->num_devs > 0) {
3820Sstevel@tonic-gate /*
3830Sstevel@tonic-gate * These 2 items are platform specific,
3840Sstevel@tonic-gate * extracted from the bridge.
3850Sstevel@tonic-gate */
3860Sstevel@tonic-gate iget->ctlr = 0;
387*10053SEvan.Yan@Sun.COM iget->cpu_id = cpu_id;
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate }
3900Sstevel@tonic-gate done_get_intr:
3914397Sschwartz iget->drvr_version = PCITOOL_VERSION;
3920Sstevel@tonic-gate copyout_rval = ddi_copyout(iget, arg,
3930Sstevel@tonic-gate PCITOOL_IGET_SIZE(num_devs_ret), mode);
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate if (iget_kmem_alloc_size > 0) {
3960Sstevel@tonic-gate kmem_free(iget, iget_kmem_alloc_size);
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate if (copyout_rval != DDI_SUCCESS) {
4000Sstevel@tonic-gate rval = EFAULT;
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate return (rval);
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate /*
4070Sstevel@tonic-gate * Associate a new CPU with a given ino.
4080Sstevel@tonic-gate *
4090Sstevel@tonic-gate * Operate only on inos which are already mapped to devices.
4100Sstevel@tonic-gate */
4110Sstevel@tonic-gate static int
pcitool_set_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)4120Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
4130Sstevel@tonic-gate {
414117Sschwartz ib_t *ib_p = pci_p->pci_ib_p;
415117Sschwartz int rval = SUCCESS;
416*10053SEvan.Yan@Sun.COM int ret = DDI_SUCCESS;
4170Sstevel@tonic-gate uint8_t zero = 0;
4180Sstevel@tonic-gate pcitool_intr_set_t iset;
419*10053SEvan.Yan@Sun.COM volatile uint64_t *imregp;
4200Sstevel@tonic-gate uint64_t imregval;
421*10053SEvan.Yan@Sun.COM
4224397Sschwartz size_t copyinout_size;
423*10053SEvan.Yan@Sun.COM int old_cpu_id;
4240Sstevel@tonic-gate
4254397Sschwartz bzero(&iset, sizeof (pcitool_intr_set_t));
4264397Sschwartz
4274397Sschwartz /* Version 1 of pcitool_intr_set_t doesn't have flags. */
4284397Sschwartz copyinout_size = (size_t)&iset.flags - (size_t)&iset;
4294397Sschwartz
4304397Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4310Sstevel@tonic-gate return (EFAULT);
4320Sstevel@tonic-gate
4334397Sschwartz switch (iset.user_version) {
4344397Sschwartz case PCITOOL_V1:
4354397Sschwartz break;
4364397Sschwartz
4374397Sschwartz case PCITOOL_V2:
4384397Sschwartz copyinout_size = sizeof (pcitool_intr_set_t);
4394397Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4404397Sschwartz return (EFAULT);
4414397Sschwartz break;
4424397Sschwartz
4434397Sschwartz default:
4444397Sschwartz iset.status = PCITOOL_OUT_OF_RANGE;
4454397Sschwartz rval = ENOTSUP;
4464397Sschwartz goto done_set_intr;
4474397Sschwartz }
4484397Sschwartz
449*10053SEvan.Yan@Sun.COM if ((iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) ||
450*10053SEvan.Yan@Sun.COM (iset.flags & PCITOOL_INTR_FLAG_SET_MSI)) {
4514397Sschwartz iset.status = PCITOOL_IO_ERROR;
4524397Sschwartz rval = ENOTSUP;
4534397Sschwartz goto done_set_intr;
4544397Sschwartz }
4554397Sschwartz
456117Sschwartz /* Validate input argument and that ino given belongs to a device. */
457117Sschwartz if ((iset.ino > PCI_MAX_INO) ||
458117Sschwartz (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
4590Sstevel@tonic-gate iset.status = PCITOOL_INVALID_INO;
4600Sstevel@tonic-gate rval = EINVAL;
4610Sstevel@tonic-gate goto done_set_intr;
4620Sstevel@tonic-gate }
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
4650Sstevel@tonic-gate imregval = *imregp;
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate /* Operate only on inos which are already enabled. */
4680Sstevel@tonic-gate if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
4690Sstevel@tonic-gate iset.status = PCITOOL_INVALID_INO;
4700Sstevel@tonic-gate rval = EINVAL;
4710Sstevel@tonic-gate goto done_set_intr;
4720Sstevel@tonic-gate }
4730Sstevel@tonic-gate
474*10053SEvan.Yan@Sun.COM if (ib_get_intr_target(pci_p, iset.ino, &old_cpu_id) != DDI_SUCCESS) {
475*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_INVALID_INO;
476*10053SEvan.Yan@Sun.COM rval = EINVAL;
477*10053SEvan.Yan@Sun.COM goto done_set_intr;
478*10053SEvan.Yan@Sun.COM }
4790Sstevel@tonic-gate
480*10053SEvan.Yan@Sun.COM if ((ret = ib_set_intr_target(pci_p, iset.ino,
481*10053SEvan.Yan@Sun.COM iset.cpu_id)) == DDI_SUCCESS) {
482*10053SEvan.Yan@Sun.COM iset.cpu_id = old_cpu_id;
483*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_SUCCESS;
484*10053SEvan.Yan@Sun.COM goto done_set_intr;
4850Sstevel@tonic-gate }
4860Sstevel@tonic-gate
487*10053SEvan.Yan@Sun.COM switch (ret) {
488*10053SEvan.Yan@Sun.COM case DDI_EPENDING:
489*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_PENDING_INTRTIMEOUT;
490*10053SEvan.Yan@Sun.COM rval = ETIME;
491*10053SEvan.Yan@Sun.COM break;
492*10053SEvan.Yan@Sun.COM case DDI_EINVAL:
4930Sstevel@tonic-gate iset.status = PCITOOL_INVALID_CPUID;
4940Sstevel@tonic-gate rval = EINVAL;
495*10053SEvan.Yan@Sun.COM break;
496*10053SEvan.Yan@Sun.COM default:
497*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_INVALID_INO;
498*10053SEvan.Yan@Sun.COM rval = EINVAL;
499*10053SEvan.Yan@Sun.COM break;
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate done_set_intr:
5024397Sschwartz iset.drvr_version = PCITOOL_VERSION;
5034397Sschwartz if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
5040Sstevel@tonic-gate rval = EFAULT;
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate return (rval);
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate
5100Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */
5110Sstevel@tonic-gate int
pcitool_intr_admn(dev_t dev,void * arg,int cmd,int mode)5120Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
5130Sstevel@tonic-gate {
5140Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev);
5150Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
5160Sstevel@tonic-gate int rval = SUCCESS;
5170Sstevel@tonic-gate
5180Sstevel@tonic-gate switch (cmd) {
5190Sstevel@tonic-gate
5204397Sschwartz /* Get system interrupt information. */
5214397Sschwartz case PCITOOL_SYSTEM_INTR_INFO:
5224397Sschwartz rval = pcitool_intr_info(dip, arg, mode);
5230Sstevel@tonic-gate break;
5240Sstevel@tonic-gate
5250Sstevel@tonic-gate /* Get interrupt information for a given ino. */
5260Sstevel@tonic-gate case PCITOOL_DEVICE_GET_INTR:
5270Sstevel@tonic-gate rval = pcitool_get_intr(dip, arg, mode, pci_p);
5280Sstevel@tonic-gate break;
5290Sstevel@tonic-gate
5300Sstevel@tonic-gate /* Associate a new CPU with a given ino. */
5310Sstevel@tonic-gate case PCITOOL_DEVICE_SET_INTR:
5320Sstevel@tonic-gate rval = pcitool_set_intr(dip, arg, mode, pci_p);
5330Sstevel@tonic-gate break;
5340Sstevel@tonic-gate
5350Sstevel@tonic-gate default:
5360Sstevel@tonic-gate rval = ENOTTY;
5370Sstevel@tonic-gate }
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate return (rval);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate
5430Sstevel@tonic-gate /*
544117Sschwartz * Wrapper around pcitool_phys_peek/poke.
5450Sstevel@tonic-gate *
546117Sschwartz * Validates arguments and calls pcitool_phys_peek/poke appropriately.
5470Sstevel@tonic-gate *
5480Sstevel@tonic-gate * Dip is of the nexus,
5490Sstevel@tonic-gate * phys_addr is the address to write in physical space,
5500Sstevel@tonic-gate * max_addr is the upper bound on the physical space used for bounds checking,
5510Sstevel@tonic-gate * pcitool_status returns more detailed status in addition to a more generic
5520Sstevel@tonic-gate * errno-style function return value.
5530Sstevel@tonic-gate * other args are self-explanatory.
5540Sstevel@tonic-gate */
5550Sstevel@tonic-gate static int
pcitool_access(pci_t * pci_p,uint64_t phys_addr,uint64_t max_addr,uint64_t * data,uint8_t size,boolean_t write,boolean_t endian,uint32_t * pcitool_status)556117Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
557117Sschwartz uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
558117Sschwartz uint32_t *pcitool_status)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate
5610Sstevel@tonic-gate int rval = SUCCESS;
5620Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
5630Sstevel@tonic-gate
5640Sstevel@tonic-gate /* Upper bounds checking. */
5650Sstevel@tonic-gate if (phys_addr > max_addr) {
5660Sstevel@tonic-gate DEBUG2(DBG_TOOLS, dip,
5670Sstevel@tonic-gate "Phys addr 0x%llx out of range (max 0x%llx).\n",
5680Sstevel@tonic-gate phys_addr, max_addr);
5690Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS;
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate rval = EINVAL;
5720Sstevel@tonic-gate
5730Sstevel@tonic-gate /* Alignment checking. */
5740Sstevel@tonic-gate } else if (!IS_P2ALIGNED(phys_addr, size)) {
5750Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
5760Sstevel@tonic-gate *pcitool_status = PCITOOL_NOT_ALIGNED;
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate rval = EINVAL;
5790Sstevel@tonic-gate
5800Sstevel@tonic-gate /* Made it through checks. Do the access. */
5810Sstevel@tonic-gate } else if (write) {
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
584117Sschwartz "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
5850Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
5860Sstevel@tonic-gate
587117Sschwartz if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
588117Sschwartz *data) != DDI_SUCCESS) {
5890Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
590117Sschwartz "%d byte %s pcitool_phys_poke at addr "
5910Sstevel@tonic-gate "0x%llx failed\n",
5920Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
5930Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS;
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate rval = EFAULT;
5960Sstevel@tonic-gate }
5970Sstevel@tonic-gate
598117Sschwartz } else { /* Read */
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
601117Sschwartz "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
6020Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
6030Sstevel@tonic-gate
604117Sschwartz if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
605117Sschwartz data) != DDI_SUCCESS) {
6060Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
607117Sschwartz "%d byte %s pcitool_phys_peek at addr "
6080Sstevel@tonic-gate "0x%llx failed\n",
6090Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
6100Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS;
6110Sstevel@tonic-gate
6120Sstevel@tonic-gate rval = EFAULT;
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate }
6150Sstevel@tonic-gate return (rval);
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate
6180Sstevel@tonic-gate /*
6190Sstevel@tonic-gate * Perform register accesses on the nexus device itself.
6200Sstevel@tonic-gate */
6210Sstevel@tonic-gate int
pcitool_bus_reg_ops(dev_t dev,void * arg,int cmd,int mode)6220Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
6230Sstevel@tonic-gate {
6240Sstevel@tonic-gate
6250Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev);
6260Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
627117Sschwartz pci_nexus_regspec_t *pci_rp = NULL;
628117Sschwartz boolean_t write_flag = B_FALSE;
6290Sstevel@tonic-gate pcitool_reg_t prg;
6300Sstevel@tonic-gate uint64_t base_addr;
6310Sstevel@tonic-gate uint64_t max_addr;
6320Sstevel@tonic-gate uint32_t reglen;
6330Sstevel@tonic-gate uint8_t size;
6340Sstevel@tonic-gate uint32_t rval = 0;
6350Sstevel@tonic-gate
636117Sschwartz if (cmd == PCITOOL_NEXUS_SET_REG)
6370Sstevel@tonic-gate write_flag = B_TRUE;
6380Sstevel@tonic-gate
639117Sschwartz DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
6400Sstevel@tonic-gate
641117Sschwartz /* Read data from userland. */
642117Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
643117Sschwartz DDI_SUCCESS) {
644117Sschwartz DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
645117Sschwartz return (EFAULT);
646117Sschwartz }
6470Sstevel@tonic-gate
648117Sschwartz /* Read reg property which contains starting addr and size of banks. */
649117Sschwartz if (ddi_prop_lookup_int_array(
650117Sschwartz DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
651117Sschwartz "reg", (int **)&pci_rp, ®len) == DDI_SUCCESS) {
652117Sschwartz if (((reglen * sizeof (int)) %
653117Sschwartz sizeof (pci_nexus_regspec_t)) != 0) {
654117Sschwartz DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
655117Sschwartz prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
656117Sschwartz rval = EIO;
6570Sstevel@tonic-gate goto done;
6580Sstevel@tonic-gate }
659117Sschwartz }
6600Sstevel@tonic-gate
661117Sschwartz /* Bounds check the bank number. */
662117Sschwartz if (prg.barnum >=
663117Sschwartz (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
664117Sschwartz prg.status = PCITOOL_OUT_OF_RANGE;
665117Sschwartz rval = EINVAL;
666117Sschwartz goto done;
6670Sstevel@tonic-gate }
6680Sstevel@tonic-gate
669117Sschwartz size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
670117Sschwartz base_addr = pci_rp[prg.barnum].phys_addr;
671117Sschwartz max_addr = base_addr + pci_rp[prg.barnum].size;
672117Sschwartz prg.phys_addr = base_addr + prg.offset;
673117Sschwartz
674117Sschwartz DEBUG4(DBG_TOOLS, dip,
675117Sschwartz "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
676117Sschwartz "addr:0x%llx, max_addr:0x%llx\n",
677117Sschwartz base_addr, prg.offset, prg.phys_addr, max_addr);
678117Sschwartz
679117Sschwartz /* Access device. prg.status is modified. */
680117Sschwartz rval = pcitool_access(pci_p,
681117Sschwartz prg.phys_addr, max_addr, &prg.data, size, write_flag,
682117Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
683117Sschwartz
6840Sstevel@tonic-gate done:
685117Sschwartz if (pci_rp != NULL)
686117Sschwartz ddi_prop_free(pci_rp);
687117Sschwartz
6884397Sschwartz prg.drvr_version = PCITOOL_VERSION;
6890Sstevel@tonic-gate if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
6900Sstevel@tonic-gate DDI_SUCCESS) {
6910Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
6920Sstevel@tonic-gate return (EFAULT);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate
6950Sstevel@tonic-gate return (rval);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate
699117Sschwartz static int
pcitool_validate_barnum_bdf(pcitool_reg_t * prg)700117Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
701117Sschwartz {
702117Sschwartz int rval = SUCCESS;
703117Sschwartz
704117Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
705117Sschwartz prg->status = PCITOOL_OUT_OF_RANGE;
706117Sschwartz rval = EINVAL;
707117Sschwartz
708117Sschwartz /* Validate address arguments of bus / dev / func */
709117Sschwartz } else if (((prg->bus_no &
710117Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
711117Sschwartz ((prg->dev_no &
712117Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
713117Sschwartz ((prg->func_no &
714117Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
715117Sschwartz prg->status = PCITOOL_INVALID_ADDRESS;
716117Sschwartz rval = EINVAL;
717117Sschwartz }
718117Sschwartz
719117Sschwartz return (rval);
720117Sschwartz }
721117Sschwartz
722117Sschwartz static int
pcitool_get_bar(pci_t * pci_p,pcitool_reg_t * prg,uint64_t config_base_addr,uint64_t config_max_addr,uint64_t * bar,boolean_t * is_io_space)723117Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
724117Sschwartz uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
725117Sschwartz {
726117Sschwartz
727117Sschwartz uint8_t bar_offset;
728117Sschwartz int rval;
729117Sschwartz dev_info_t *dip = pci_p->pci_dip;
730117Sschwartz
731117Sschwartz *bar = 0;
732117Sschwartz *is_io_space = B_FALSE;
733117Sschwartz
734117Sschwartz /*
735117Sschwartz * Translate BAR number into offset of the BAR in
736117Sschwartz * the device's config space.
737117Sschwartz */
738117Sschwartz bar_offset = PCI_BAR_OFFSET((*prg));
739117Sschwartz
740117Sschwartz DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
741117Sschwartz prg->barnum, bar_offset);
742117Sschwartz
743117Sschwartz /*
744117Sschwartz * Get Bus Address Register (BAR) from config space.
745117Sschwartz * bar_offset is the offset into config space of the BAR desired.
746117Sschwartz * prg->status is modified on error.
747117Sschwartz */
748117Sschwartz rval = pcitool_access(pci_p, config_base_addr + bar_offset,
749117Sschwartz config_max_addr, bar,
750117Sschwartz 4, /* 4 bytes. */
751117Sschwartz B_FALSE, /* Read */
752117Sschwartz B_FALSE, /* Little endian. */
753117Sschwartz &prg->status);
754117Sschwartz if (rval != SUCCESS)
755117Sschwartz return (rval);
756117Sschwartz
757117Sschwartz DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
758117Sschwartz if (!(*bar)) {
759117Sschwartz prg->status = PCITOOL_INVALID_ADDRESS;
760117Sschwartz return (EINVAL);
761117Sschwartz }
762117Sschwartz
763117Sschwartz /*
764117Sschwartz * BAR has bits saying this space is IO space, unless
765117Sschwartz * this is the ROM address register.
766117Sschwartz */
767117Sschwartz if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
768117Sschwartz (bar_offset != PCI_CONF_ROM)) {
769117Sschwartz *is_io_space = B_TRUE;
770117Sschwartz *bar &= PCI_BASE_IO_ADDR_M;
771117Sschwartz
772117Sschwartz /*
773117Sschwartz * BAR has bits saying this space is 64 bit memory
774117Sschwartz * space, unless this is the ROM address register.
775117Sschwartz *
776117Sschwartz * The 64 bit address stored in two BAR cells is not necessarily
777117Sschwartz * aligned on an 8-byte boundary. Need to keep the first 4
778117Sschwartz * bytes read, and do a separate read of the high 4 bytes.
779117Sschwartz */
780117Sschwartz
781117Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
782117Sschwartz
783117Sschwartz uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
784117Sschwartz
785117Sschwartz /* Don't try to read past the end of BARs. */
786117Sschwartz if (bar_offset >= PCI_CONF_BASE5) {
787117Sschwartz prg->status = PCITOOL_OUT_OF_RANGE;
788117Sschwartz return (EIO);
789117Sschwartz }
790117Sschwartz
791117Sschwartz /* Access device. prg->status is modified on error. */
792117Sschwartz rval = pcitool_access(pci_p,
793117Sschwartz config_base_addr + bar_offset + 4, config_max_addr, bar,
794117Sschwartz 4, /* 4 bytes. */
795117Sschwartz B_FALSE, /* Read */
796117Sschwartz B_FALSE, /* Little endian. */
797117Sschwartz &prg->status);
798117Sschwartz if (rval != SUCCESS)
799117Sschwartz return (rval);
800117Sschwartz
801117Sschwartz *bar = (*bar << 32) + low_bytes;
802117Sschwartz }
803117Sschwartz
804117Sschwartz return (SUCCESS);
805117Sschwartz }
806117Sschwartz
807117Sschwartz
808117Sschwartz static int
pcitool_config_request(pci_t * pci_p,pcitool_reg_t * prg,uint64_t base_addr,uint64_t max_addr,uint8_t size,boolean_t write_flag)809117Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
810117Sschwartz uint64_t max_addr, uint8_t size, boolean_t write_flag)
811117Sschwartz {
812117Sschwartz int rval;
813117Sschwartz dev_info_t *dip = pci_p->pci_dip;
814117Sschwartz
815117Sschwartz /* Access config space and we're done. */
816117Sschwartz prg->phys_addr = base_addr + prg->offset;
817117Sschwartz
818117Sschwartz DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
819117Sschwartz "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
820117Sschwartz base_addr, prg->offset, prg->phys_addr,
821117Sschwartz (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
822117Sschwartz
823117Sschwartz /* Access device. pr.status is modified. */
824117Sschwartz rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
825117Sschwartz write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
826117Sschwartz
827117Sschwartz DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
828117Sschwartz
829117Sschwartz return (rval);
830117Sschwartz }
831117Sschwartz
8320Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
8330Sstevel@tonic-gate int
pcitool_dev_reg_ops(dev_t dev,void * arg,int cmd,int mode)8340Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
8350Sstevel@tonic-gate {
8360Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev);
8370Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
8380Sstevel@tonic-gate pci_ranges_t *rp = pci_p->pci_ranges;
8390Sstevel@tonic-gate pcitool_reg_t prg;
8400Sstevel@tonic-gate uint64_t max_addr;
8410Sstevel@tonic-gate uint64_t base_addr;
8420Sstevel@tonic-gate uint64_t range_prop;
8430Sstevel@tonic-gate uint64_t range_prop_size;
8440Sstevel@tonic-gate uint64_t bar = 0;
8450Sstevel@tonic-gate int rval = 0;
8460Sstevel@tonic-gate boolean_t write_flag = B_FALSE;
847117Sschwartz boolean_t is_io_space = B_FALSE;
8480Sstevel@tonic-gate uint8_t size;
8490Sstevel@tonic-gate
850117Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG)
8510Sstevel@tonic-gate write_flag = B_TRUE;
8520Sstevel@tonic-gate
853117Sschwartz DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
854117Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
855117Sschwartz DDI_SUCCESS) {
856117Sschwartz DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
857117Sschwartz return (EFAULT);
858117Sschwartz }
8590Sstevel@tonic-gate
860117Sschwartz DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
861117Sschwartz prg.bus_no, prg.dev_no, prg.func_no);
8620Sstevel@tonic-gate
863117Sschwartz if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
864117Sschwartz goto done_reg;
865117Sschwartz
866117Sschwartz size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
8670Sstevel@tonic-gate
868117Sschwartz /* Get config space first. */
869117Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
870117Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
871117Sschwartz max_addr = range_prop + range_prop_size;
8720Sstevel@tonic-gate
873117Sschwartz /*
874117Sschwartz * Build device address based on base addr from range prop, and bus,
875117Sschwartz * dev and func values passed in. This address is where config space
876117Sschwartz * begins.
877117Sschwartz */
878117Sschwartz base_addr = range_prop +
879117Sschwartz (prg.bus_no << PCI_REG_BUS_SHIFT) +
880117Sschwartz (prg.dev_no << PCI_REG_DEV_SHIFT) +
881117Sschwartz (prg.func_no << PCI_REG_FUNC_SHIFT);
8820Sstevel@tonic-gate
883117Sschwartz if ((base_addr < range_prop) || (base_addr >= max_addr)) {
884117Sschwartz prg.status = PCITOOL_OUT_OF_RANGE;
885117Sschwartz rval = EINVAL;
886117Sschwartz goto done_reg;
887117Sschwartz }
8880Sstevel@tonic-gate
889117Sschwartz DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
890117Sschwartz "func:0x%x, addr:0x%x\n", range_prop,
891117Sschwartz prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
892117Sschwartz prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
8930Sstevel@tonic-gate
894117Sschwartz /* Proper config space desired. */
895117Sschwartz if (prg.barnum == 0) {
896117Sschwartz
897117Sschwartz rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
898117Sschwartz size, write_flag);
8990Sstevel@tonic-gate
900117Sschwartz } else { /* IO / MEM / MEM64 space. */
9010Sstevel@tonic-gate
902117Sschwartz if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
903117Sschwartz &is_io_space) != SUCCESS)
904117Sschwartz goto done_reg;
9050Sstevel@tonic-gate
906117Sschwartz /* IO space. */
907117Sschwartz if (is_io_space) {
9080Sstevel@tonic-gate
909117Sschwartz DEBUG0(DBG_TOOLS, dip, "IO space\n");
9100Sstevel@tonic-gate
911117Sschwartz /* Reposition to focus on IO space. */
912117Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
913117Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
914117Sschwartz PCI_IO_RANGE_BANK);
9150Sstevel@tonic-gate
916117Sschwartz /* 64 bit memory space. */
917117Sschwartz } else if ((bar >> 32) != 0) {
918117Sschwartz
919117Sschwartz DEBUG1(DBG_TOOLS, dip,
920117Sschwartz "64 bit mem space. 64-bit bar is 0x%llx\n", bar);
9210Sstevel@tonic-gate
922117Sschwartz /* Reposition to MEM64 range space. */
923117Sschwartz range_prop = PCI_GET_RANGE_PROP(rp,
924117Sschwartz PCI_MEM64_RANGE_BANK);
925117Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
926117Sschwartz PCI_MEM64_RANGE_BANK);
9270Sstevel@tonic-gate
928117Sschwartz } else { /* Mem32 space, including ROM */
9290Sstevel@tonic-gate
930117Sschwartz DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
9310Sstevel@tonic-gate
932117Sschwartz if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9330Sstevel@tonic-gate
934117Sschwartz DEBUG0(DBG_TOOLS, dip,
935117Sschwartz "Additional ROM checking\n");
9360Sstevel@tonic-gate
937117Sschwartz /* Can't write to ROM */
938117Sschwartz if (write_flag) {
939117Sschwartz prg.status = PCITOOL_ROM_WRITE;
940117Sschwartz rval = EIO;
941117Sschwartz goto done_reg;
9420Sstevel@tonic-gate
943117Sschwartz /* ROM disabled for reading */
944117Sschwartz } else if (!(bar & 0x00000001)) {
945117Sschwartz prg.status = PCITOOL_ROM_DISABLED;
9460Sstevel@tonic-gate rval = EIO;
9470Sstevel@tonic-gate goto done_reg;
9480Sstevel@tonic-gate }
9490Sstevel@tonic-gate }
9500Sstevel@tonic-gate
951117Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
952117Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
953117Sschwartz PCI_MEM_RANGE_BANK);
954117Sschwartz }
9550Sstevel@tonic-gate
956117Sschwartz /* Common code for all IO/MEM range spaces. */
957117Sschwartz max_addr = range_prop + range_prop_size;
958117Sschwartz base_addr = range_prop + bar;
959117Sschwartz
960117Sschwartz DEBUG3(DBG_TOOLS, dip,
961117Sschwartz "addr portion of bar is 0x%llx, base=0x%llx, "
962117Sschwartz "offset:0x%lx\n", bar, base_addr, prg.offset);
9630Sstevel@tonic-gate
964117Sschwartz /*
965117Sschwartz * Use offset provided by caller to index into
966117Sschwartz * desired space, then access.
967117Sschwartz * Note that prg.status is modified on error.
968117Sschwartz */
969117Sschwartz prg.phys_addr = base_addr + prg.offset;
970117Sschwartz rval = pcitool_access(pci_p, prg.phys_addr,
971117Sschwartz max_addr, &prg.data, size, write_flag,
972117Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
973117Sschwartz }
974117Sschwartz
9750Sstevel@tonic-gate done_reg:
9764397Sschwartz prg.drvr_version = PCITOOL_VERSION;
977117Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
978117Sschwartz DDI_SUCCESS) {
979117Sschwartz DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
980117Sschwartz rval = EFAULT;
9810Sstevel@tonic-gate }
9820Sstevel@tonic-gate return (rval);
9830Sstevel@tonic-gate }
984117Sschwartz
985117Sschwartz int
pcitool_init(dev_info_t * dip)986117Sschwartz pcitool_init(dev_info_t *dip)
987117Sschwartz {
988117Sschwartz int instance = ddi_get_instance(dip);
989117Sschwartz
990117Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
991117Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
992117Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS)
993117Sschwartz return (DDI_FAILURE);
994117Sschwartz
995117Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
996117Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
997117Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
998117Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG);
999117Sschwartz return (DDI_FAILURE);
1000117Sschwartz }
1001117Sschwartz
1002117Sschwartz return (DDI_SUCCESS);
1003117Sschwartz }
1004117Sschwartz
1005117Sschwartz void
pcitool_uninit(dev_info_t * dip)1006117Sschwartz pcitool_uninit(dev_info_t *dip)
1007117Sschwartz {
1008117Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG);
1009117Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR);
1010117Sschwartz }
1011