17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52917a9c9Sschwartz * Common Development and Distribution License (the "License").
62917a9c9Sschwartz * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22*09b1eac2SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267851eb82Sschwartz #include <sys/stat.h>
277851eb82Sschwartz #include <sys/sunddi.h>
287851eb82Sschwartz #include <sys/param.h>
297851eb82Sschwartz
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
327c478bd9Sstevel@tonic-gate #include <sys/promif.h>
337c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
347c478bd9Sstevel@tonic-gate
357c478bd9Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
367c478bd9Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
377c478bd9Sstevel@tonic-gate
387c478bd9Sstevel@tonic-gate #include <sys/pci_tools.h>
39d4476ccbSschwartz #include <sys/pci/pci_tools_ext.h>
40d4476ccbSschwartz
41d4476ccbSschwartz /*
42d4476ccbSschwartz * Number of interrupts supported per PCI bus.
43d4476ccbSschwartz */
44d4476ccbSschwartz #define PCI_MAX_INO 0x3f
45d4476ccbSschwartz
46d4476ccbSschwartz /*
47d4476ccbSschwartz * PCI Space definitions.
48d4476ccbSschwartz */
49d4476ccbSschwartz #define PCI_CONFIG_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
50d4476ccbSschwartz #define PCI_IO_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_IO))
51d4476ccbSschwartz #define PCI_MEM_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM32))
52d4476ccbSschwartz #define PCI_MEM64_RANGE_BANK (PCI_REG_ADDR_G(PCI_ADDR_MEM64))
537c478bd9Sstevel@tonic-gate
547c478bd9Sstevel@tonic-gate /*
557c478bd9Sstevel@tonic-gate * Extract 64 bit parent or size values from 32 bit cells of
567c478bd9Sstevel@tonic-gate * pci_ranges_t property.
577c478bd9Sstevel@tonic-gate *
587c478bd9Sstevel@tonic-gate * Only bits 42-32 are relevant in parent_high.
597c478bd9Sstevel@tonic-gate */
607c478bd9Sstevel@tonic-gate #define PCI_GET_RANGE_PROP(ranges, bank) \
617c478bd9Sstevel@tonic-gate ((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
627c478bd9Sstevel@tonic-gate ranges[bank].parent_low)
637c478bd9Sstevel@tonic-gate
647c478bd9Sstevel@tonic-gate #define PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
657c478bd9Sstevel@tonic-gate ((((uint64_t)(ranges[bank].size_high)) << 32) | \
667c478bd9Sstevel@tonic-gate ranges[bank].size_low)
677c478bd9Sstevel@tonic-gate
687851eb82Sschwartz #define PCI_BAR_OFFSET(x) (pci_bars[x.barnum])
697851eb82Sschwartz
707c478bd9Sstevel@tonic-gate /* Big and little endian as boolean values. */
717c478bd9Sstevel@tonic-gate #define BE B_TRUE
727c478bd9Sstevel@tonic-gate #define LE B_FALSE
737c478bd9Sstevel@tonic-gate
747c478bd9Sstevel@tonic-gate #define SUCCESS 0
757c478bd9Sstevel@tonic-gate
767c478bd9Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
777c478bd9Sstevel@tonic-gate typedef union {
787c478bd9Sstevel@tonic-gate uint64_t u64;
797c478bd9Sstevel@tonic-gate uint32_t u32;
807c478bd9Sstevel@tonic-gate uint16_t u16;
817c478bd9Sstevel@tonic-gate uint8_t u8;
827c478bd9Sstevel@tonic-gate } peek_poke_value_t;
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate * Offsets of BARS in config space. First entry of 0 means config space.
867c478bd9Sstevel@tonic-gate * Entries here correlate to pcitool_bars_t enumerated type.
877c478bd9Sstevel@tonic-gate */
887c478bd9Sstevel@tonic-gate static uint8_t pci_bars[] = {
897c478bd9Sstevel@tonic-gate 0x0,
907c478bd9Sstevel@tonic-gate PCI_CONF_BASE0,
917c478bd9Sstevel@tonic-gate PCI_CONF_BASE1,
927c478bd9Sstevel@tonic-gate PCI_CONF_BASE2,
937c478bd9Sstevel@tonic-gate PCI_CONF_BASE3,
947c478bd9Sstevel@tonic-gate PCI_CONF_BASE4,
957c478bd9Sstevel@tonic-gate PCI_CONF_BASE5,
967c478bd9Sstevel@tonic-gate PCI_CONF_ROM
977c478bd9Sstevel@tonic-gate };
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/
1007c478bd9Sstevel@tonic-gate
1017851eb82Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
1027851eb82Sschwartz uint64_t paddr, uint64_t *value_p);
1037851eb82Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
1047851eb82Sschwartz uint64_t paddr, uint64_t value);
1057851eb82Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
1067851eb82Sschwartz uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
1077851eb82Sschwartz uint32_t *pcitool_status);
1087851eb82Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
1097851eb82Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
1107851eb82Sschwartz uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
1117851eb82Sschwartz boolean_t *is_io_space);
1127851eb82Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
1137851eb82Sschwartz uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
1147851eb82Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
1157851eb82Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1167851eb82Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1177c478bd9Sstevel@tonic-gate
1187c478bd9Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
1197c478bd9Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
1207c478bd9Sstevel@tonic-gate
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate * Safe C wrapper around assy language routine pci_do_phys_peek
1237c478bd9Sstevel@tonic-gate *
1247c478bd9Sstevel@tonic-gate * Type is TRUE for big endian, FALSE for little endian.
1257c478bd9Sstevel@tonic-gate * Size is 1, 2, 4 or 8 bytes.
1267c478bd9Sstevel@tonic-gate * paddr is the physical address in IO space to access read.
1277c478bd9Sstevel@tonic-gate * value_p is where the value is returned.
1287c478bd9Sstevel@tonic-gate */
1297c478bd9Sstevel@tonic-gate static int
pcitool_phys_peek(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)1307851eb82Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
1317851eb82Sschwartz uint64_t paddr, uint64_t *value_p)
1327c478bd9Sstevel@tonic-gate {
1337c478bd9Sstevel@tonic-gate on_trap_data_t otd;
1347c478bd9Sstevel@tonic-gate int err = DDI_SUCCESS;
1357851eb82Sschwartz peek_poke_value_t peek_value;
1367c478bd9Sstevel@tonic-gate
1377c478bd9Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p;
1387c478bd9Sstevel@tonic-gate
1397c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = &otd;
1407c478bd9Sstevel@tonic-gate
1417c478bd9Sstevel@tonic-gate /* Set up trap handling to make the access safe. */
1427c478bd9Sstevel@tonic-gate
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate * on_trap works like setjmp.
1457c478bd9Sstevel@tonic-gate * Set it up to not panic on data access error,
1467c478bd9Sstevel@tonic-gate * but to call peek_fault instead.
1477c478bd9Sstevel@tonic-gate * Call pci_do_phys_peek after trap handling is setup.
1487c478bd9Sstevel@tonic-gate * When on_trap returns FALSE, it has been setup.
1497c478bd9Sstevel@tonic-gate * When it returns TRUE, an it has caught an error.
1507c478bd9Sstevel@tonic-gate */
1517c478bd9Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) {
1527c478bd9Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&peek_fault;
1537c478bd9Sstevel@tonic-gate err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
1547c478bd9Sstevel@tonic-gate } else {
1557c478bd9Sstevel@tonic-gate err = DDI_FAILURE;
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate
1587c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = NULL;
1597c478bd9Sstevel@tonic-gate no_trap();
1607c478bd9Sstevel@tonic-gate
1617c478bd9Sstevel@tonic-gate if (err != DDI_FAILURE) {
1627c478bd9Sstevel@tonic-gate switch (size) {
1637c478bd9Sstevel@tonic-gate case 8:
1647c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u64;
1657c478bd9Sstevel@tonic-gate break;
1667c478bd9Sstevel@tonic-gate case 4:
1677c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u32;
1687c478bd9Sstevel@tonic-gate break;
1697c478bd9Sstevel@tonic-gate case 2:
1707c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u16;
1717c478bd9Sstevel@tonic-gate break;
1727c478bd9Sstevel@tonic-gate case 1:
1737c478bd9Sstevel@tonic-gate *value_p = (uint64_t)peek_value.u8;
1747c478bd9Sstevel@tonic-gate break;
1757c478bd9Sstevel@tonic-gate default:
1767c478bd9Sstevel@tonic-gate err = DDI_FAILURE;
1777c478bd9Sstevel@tonic-gate }
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate
1807c478bd9Sstevel@tonic-gate return (err);
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate
1837c478bd9Sstevel@tonic-gate /*
1847c478bd9Sstevel@tonic-gate * Safe C wrapper around assy language routine pci_do_phys_poke
1857c478bd9Sstevel@tonic-gate *
1867c478bd9Sstevel@tonic-gate * Type is TRUE for big endian, FALSE for little endian.
1877c478bd9Sstevel@tonic-gate * Size is 1,2,4 or 8 bytes.
1887c478bd9Sstevel@tonic-gate * paddr is the physical address in IO space to access read.
1897c478bd9Sstevel@tonic-gate * value contains the value to be written.
1907c478bd9Sstevel@tonic-gate */
1917c478bd9Sstevel@tonic-gate static int
pcitool_phys_poke(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)1927851eb82Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
1937851eb82Sschwartz uint64_t paddr, uint64_t value)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate on_trap_data_t otd;
1967c478bd9Sstevel@tonic-gate int err = DDI_SUCCESS;
1977c478bd9Sstevel@tonic-gate peek_poke_value_t poke_value;
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate pbm_t *pbm_p = pci_p->pci_pbm_p;
2007c478bd9Sstevel@tonic-gate
2017c478bd9Sstevel@tonic-gate switch (size) {
2027c478bd9Sstevel@tonic-gate case 8:
2037c478bd9Sstevel@tonic-gate poke_value.u64 = value;
2047c478bd9Sstevel@tonic-gate break;
2057c478bd9Sstevel@tonic-gate case 4:
2067c478bd9Sstevel@tonic-gate poke_value.u32 = (uint32_t)value;
2077c478bd9Sstevel@tonic-gate break;
2087c478bd9Sstevel@tonic-gate case 2:
2097c478bd9Sstevel@tonic-gate poke_value.u16 = (uint16_t)value;
2107c478bd9Sstevel@tonic-gate break;
2117c478bd9Sstevel@tonic-gate case 1:
2127c478bd9Sstevel@tonic-gate poke_value.u8 = (uint8_t)value;
2137c478bd9Sstevel@tonic-gate break;
2147c478bd9Sstevel@tonic-gate default:
2157c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
2167c478bd9Sstevel@tonic-gate }
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate mutex_enter(&pbm_p->pbm_pokefault_mutex);
2197c478bd9Sstevel@tonic-gate
2207c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = &otd;
2217c478bd9Sstevel@tonic-gate
2227c478bd9Sstevel@tonic-gate /*
2237c478bd9Sstevel@tonic-gate * on_trap works like setjmp.
2247c478bd9Sstevel@tonic-gate * Set it up to not panic on data access error,
2257c478bd9Sstevel@tonic-gate * but to call poke_fault instead.
2267c478bd9Sstevel@tonic-gate * Call pci_do_phys_poke after trap handling is setup.
2277c478bd9Sstevel@tonic-gate * When on_trap returns FALSE, it has been setup.
2287c478bd9Sstevel@tonic-gate * When it returns TRUE, an it has caught an error.
2297c478bd9Sstevel@tonic-gate */
2307c478bd9Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) {
2317c478bd9Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&poke_fault;
2327c478bd9Sstevel@tonic-gate err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gate /* Let the dust settle and errors occur if they will. */
2367c478bd9Sstevel@tonic-gate pbm_clear_error(pbm_p);
2377c478bd9Sstevel@tonic-gate
2387c478bd9Sstevel@tonic-gate /* Check for an error. */
2397c478bd9Sstevel@tonic-gate if (otd.ot_trap == OT_DATA_ACCESS) {
2407c478bd9Sstevel@tonic-gate err = DDI_FAILURE;
2417c478bd9Sstevel@tonic-gate }
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate pbm_p->pbm_ontrap_data = NULL;
2447c478bd9Sstevel@tonic-gate mutex_exit(&pbm_p->pbm_pokefault_mutex);
2457c478bd9Sstevel@tonic-gate
2467c478bd9Sstevel@tonic-gate no_trap();
2477c478bd9Sstevel@tonic-gate return (err);
2487c478bd9Sstevel@tonic-gate }
2497c478bd9Sstevel@tonic-gate
2507c478bd9Sstevel@tonic-gate
2512917a9c9Sschwartz /*ARGSUSED*/
2527c478bd9Sstevel@tonic-gate static int
pcitool_intr_info(dev_info_t * dip,void * arg,int mode)2532917a9c9Sschwartz pcitool_intr_info(dev_info_t *dip, void *arg, int mode)
2547c478bd9Sstevel@tonic-gate {
2552917a9c9Sschwartz pcitool_intr_info_t intr_info;
2562917a9c9Sschwartz int rval = SUCCESS;
2577c478bd9Sstevel@tonic-gate
2582917a9c9Sschwartz /* If we need user_version, and to ret same user version as passed in */
2592917a9c9Sschwartz if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
2607c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
2617c478bd9Sstevel@tonic-gate return (EFAULT);
2627c478bd9Sstevel@tonic-gate }
2632917a9c9Sschwartz
264*09b1eac2SEvan Yan if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
265*09b1eac2SEvan Yan return (ENOTSUP);
266*09b1eac2SEvan Yan
2672917a9c9Sschwartz intr_info.ctlr_version = 0; /* XXX how to get real version? */
2682917a9c9Sschwartz intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
2692917a9c9Sschwartz intr_info.num_intr = PCI_MAX_INO;
2702917a9c9Sschwartz
2712917a9c9Sschwartz intr_info.drvr_version = PCITOOL_VERSION;
2722917a9c9Sschwartz if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
2732917a9c9Sschwartz DDI_SUCCESS) {
2742917a9c9Sschwartz rval = EFAULT;
2752917a9c9Sschwartz }
2762917a9c9Sschwartz
2772917a9c9Sschwartz return (rval);
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate
2807c478bd9Sstevel@tonic-gate
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate * Get interrupt information for a given ino.
2837c478bd9Sstevel@tonic-gate * Returns info only for inos mapped to devices.
2847c478bd9Sstevel@tonic-gate *
2857c478bd9Sstevel@tonic-gate * Returned info is valid only when iget.num_devs is returned > 0.
2867c478bd9Sstevel@tonic-gate * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
2877c478bd9Sstevel@tonic-gate */
2887c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2897c478bd9Sstevel@tonic-gate static int
pcitool_get_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)2907c478bd9Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate /* Array part isn't used here, but oh well... */
2937c478bd9Sstevel@tonic-gate pcitool_intr_get_t partial_iget;
2947c478bd9Sstevel@tonic-gate pcitool_intr_get_t *iget = &partial_iget;
2957c478bd9Sstevel@tonic-gate size_t iget_kmem_alloc_size = 0;
2967c478bd9Sstevel@tonic-gate ib_t *ib_p = pci_p->pci_ib_p;
2977c478bd9Sstevel@tonic-gate volatile uint64_t *imregp;
2987c478bd9Sstevel@tonic-gate uint64_t imregval;
2997c478bd9Sstevel@tonic-gate uint32_t ino;
3007c478bd9Sstevel@tonic-gate uint8_t num_devs_ret;
301*09b1eac2SEvan Yan int cpu_id;
3027c478bd9Sstevel@tonic-gate int copyout_rval;
3037c478bd9Sstevel@tonic-gate int rval = SUCCESS;
3047c478bd9Sstevel@tonic-gate
3057c478bd9Sstevel@tonic-gate /* Read in just the header part, no array section. */
3067c478bd9Sstevel@tonic-gate if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
3077c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
3087c478bd9Sstevel@tonic-gate
3097c478bd9Sstevel@tonic-gate return (EFAULT);
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate
312*09b1eac2SEvan Yan if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) {
313*09b1eac2SEvan Yan partial_iget.status = PCITOOL_IO_ERROR;
314*09b1eac2SEvan Yan partial_iget.num_devs_ret = 0;
315*09b1eac2SEvan Yan rval = ENOTSUP;
316*09b1eac2SEvan Yan goto done_get_intr;
317*09b1eac2SEvan Yan }
318*09b1eac2SEvan Yan
3197c478bd9Sstevel@tonic-gate ino = partial_iget.ino;
3207c478bd9Sstevel@tonic-gate num_devs_ret = partial_iget.num_devs_ret;
3217c478bd9Sstevel@tonic-gate
3227c478bd9Sstevel@tonic-gate /* Validate argument. */
3237c478bd9Sstevel@tonic-gate if (ino > PCI_MAX_INO) {
3247c478bd9Sstevel@tonic-gate partial_iget.status = PCITOOL_INVALID_INO;
3257c478bd9Sstevel@tonic-gate partial_iget.num_devs_ret = 0;
3267c478bd9Sstevel@tonic-gate rval = EINVAL;
3277c478bd9Sstevel@tonic-gate goto done_get_intr;
3287c478bd9Sstevel@tonic-gate }
3297c478bd9Sstevel@tonic-gate
3307c478bd9Sstevel@tonic-gate /* Caller wants device information returned. */
3317c478bd9Sstevel@tonic-gate if (num_devs_ret > 0) {
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate /*
3347c478bd9Sstevel@tonic-gate * Allocate room.
3357c478bd9Sstevel@tonic-gate * Note if num_devs_ret == 0 iget remains pointing to
3367c478bd9Sstevel@tonic-gate * partial_iget.
3377c478bd9Sstevel@tonic-gate */
3387c478bd9Sstevel@tonic-gate iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
3397c478bd9Sstevel@tonic-gate iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gate /* Read in whole structure to verify there's room. */
3427c478bd9Sstevel@tonic-gate if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
3437c478bd9Sstevel@tonic-gate SUCCESS) {
3447c478bd9Sstevel@tonic-gate
3457c478bd9Sstevel@tonic-gate /* Be consistent and just return EFAULT here. */
3467c478bd9Sstevel@tonic-gate kmem_free(iget, iget_kmem_alloc_size);
3477c478bd9Sstevel@tonic-gate
3487c478bd9Sstevel@tonic-gate return (EFAULT);
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate
3527c478bd9Sstevel@tonic-gate bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
3537c478bd9Sstevel@tonic-gate iget->ino = ino;
3547c478bd9Sstevel@tonic-gate iget->num_devs_ret = num_devs_ret;
3557c478bd9Sstevel@tonic-gate
3567c478bd9Sstevel@tonic-gate imregp = ib_intr_map_reg_addr(ib_p, ino);
3577c478bd9Sstevel@tonic-gate imregval = *imregp;
3587c478bd9Sstevel@tonic-gate
3597c478bd9Sstevel@tonic-gate /*
3607c478bd9Sstevel@tonic-gate * Read "valid" bit. If set, interrupts are enabled.
3617c478bd9Sstevel@tonic-gate * This bit happens to be the same on Fire and Tomatillo.
3627c478bd9Sstevel@tonic-gate */
3637c478bd9Sstevel@tonic-gate if (imregval & COMMON_INTR_MAP_REG_VALID) {
3647c478bd9Sstevel@tonic-gate /*
3657c478bd9Sstevel@tonic-gate * The following looks up the ib_ino_info and returns
3667c478bd9Sstevel@tonic-gate * info of devices mapped to this ino.
3677c478bd9Sstevel@tonic-gate */
3687c478bd9Sstevel@tonic-gate iget->num_devs = ib_get_ino_devs(
3697c478bd9Sstevel@tonic-gate ib_p, ino, &iget->num_devs_ret, iget->dev);
3707c478bd9Sstevel@tonic-gate
371*09b1eac2SEvan Yan if (ib_get_intr_target(pci_p, ino, &cpu_id) != DDI_SUCCESS) {
372*09b1eac2SEvan Yan iget->status = PCITOOL_IO_ERROR;
373*09b1eac2SEvan Yan rval = EIO;
374*09b1eac2SEvan Yan goto done_get_intr;
375*09b1eac2SEvan Yan }
376*09b1eac2SEvan Yan
3777c478bd9Sstevel@tonic-gate /*
3787c478bd9Sstevel@tonic-gate * Consider only inos mapped to devices (as opposed to
3797c478bd9Sstevel@tonic-gate * inos mapped to the bridge itself.
3807c478bd9Sstevel@tonic-gate */
3817c478bd9Sstevel@tonic-gate if (iget->num_devs > 0) {
3827c478bd9Sstevel@tonic-gate /*
3837c478bd9Sstevel@tonic-gate * These 2 items are platform specific,
3847c478bd9Sstevel@tonic-gate * extracted from the bridge.
3857c478bd9Sstevel@tonic-gate */
3867c478bd9Sstevel@tonic-gate iget->ctlr = 0;
387*09b1eac2SEvan Yan iget->cpu_id = cpu_id;
3887c478bd9Sstevel@tonic-gate }
3897c478bd9Sstevel@tonic-gate }
3907c478bd9Sstevel@tonic-gate done_get_intr:
3912917a9c9Sschwartz iget->drvr_version = PCITOOL_VERSION;
3927c478bd9Sstevel@tonic-gate copyout_rval = ddi_copyout(iget, arg,
3937c478bd9Sstevel@tonic-gate PCITOOL_IGET_SIZE(num_devs_ret), mode);
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate if (iget_kmem_alloc_size > 0) {
3967c478bd9Sstevel@tonic-gate kmem_free(iget, iget_kmem_alloc_size);
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate
3997c478bd9Sstevel@tonic-gate if (copyout_rval != DDI_SUCCESS) {
4007c478bd9Sstevel@tonic-gate rval = EFAULT;
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate
4037c478bd9Sstevel@tonic-gate return (rval);
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate
4067c478bd9Sstevel@tonic-gate /*
4077c478bd9Sstevel@tonic-gate * Associate a new CPU with a given ino.
4087c478bd9Sstevel@tonic-gate *
4097c478bd9Sstevel@tonic-gate * Operate only on inos which are already mapped to devices.
4107c478bd9Sstevel@tonic-gate */
4117c478bd9Sstevel@tonic-gate static int
pcitool_set_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)4127c478bd9Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
4137c478bd9Sstevel@tonic-gate {
4147851eb82Sschwartz ib_t *ib_p = pci_p->pci_ib_p;
4157851eb82Sschwartz int rval = SUCCESS;
416*09b1eac2SEvan Yan int ret = DDI_SUCCESS;
4177c478bd9Sstevel@tonic-gate uint8_t zero = 0;
4187c478bd9Sstevel@tonic-gate pcitool_intr_set_t iset;
4197c478bd9Sstevel@tonic-gate volatile uint64_t *imregp;
420*09b1eac2SEvan Yan uint64_t imregval;
421*09b1eac2SEvan Yan
4222917a9c9Sschwartz size_t copyinout_size;
423*09b1eac2SEvan Yan int old_cpu_id;
4242917a9c9Sschwartz
4252917a9c9Sschwartz bzero(&iset, sizeof (pcitool_intr_set_t));
4262917a9c9Sschwartz
4272917a9c9Sschwartz /* Version 1 of pcitool_intr_set_t doesn't have flags. */
4282917a9c9Sschwartz copyinout_size = (size_t)&iset.flags - (size_t)&iset;
4297c478bd9Sstevel@tonic-gate
4302917a9c9Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4317c478bd9Sstevel@tonic-gate return (EFAULT);
4327c478bd9Sstevel@tonic-gate
4332917a9c9Sschwartz switch (iset.user_version) {
4342917a9c9Sschwartz case PCITOOL_V1:
4352917a9c9Sschwartz break;
4362917a9c9Sschwartz
4372917a9c9Sschwartz case PCITOOL_V2:
4382917a9c9Sschwartz copyinout_size = sizeof (pcitool_intr_set_t);
4392917a9c9Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4402917a9c9Sschwartz return (EFAULT);
4412917a9c9Sschwartz break;
4422917a9c9Sschwartz
4432917a9c9Sschwartz default:
4442917a9c9Sschwartz iset.status = PCITOOL_OUT_OF_RANGE;
4452917a9c9Sschwartz rval = ENOTSUP;
4462917a9c9Sschwartz goto done_set_intr;
4472917a9c9Sschwartz }
4482917a9c9Sschwartz
449*09b1eac2SEvan Yan if ((iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) ||
450*09b1eac2SEvan Yan (iset.flags & PCITOOL_INTR_FLAG_SET_MSI)) {
4512917a9c9Sschwartz iset.status = PCITOOL_IO_ERROR;
4522917a9c9Sschwartz rval = ENOTSUP;
4532917a9c9Sschwartz goto done_set_intr;
4542917a9c9Sschwartz }
4552917a9c9Sschwartz
4567851eb82Sschwartz /* Validate input argument and that ino given belongs to a device. */
4577851eb82Sschwartz if ((iset.ino > PCI_MAX_INO) ||
4587851eb82Sschwartz (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
4597c478bd9Sstevel@tonic-gate iset.status = PCITOOL_INVALID_INO;
4607c478bd9Sstevel@tonic-gate rval = EINVAL;
4617c478bd9Sstevel@tonic-gate goto done_set_intr;
4627c478bd9Sstevel@tonic-gate }
4637c478bd9Sstevel@tonic-gate
4647c478bd9Sstevel@tonic-gate imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
4657c478bd9Sstevel@tonic-gate imregval = *imregp;
4667c478bd9Sstevel@tonic-gate
4677c478bd9Sstevel@tonic-gate /* Operate only on inos which are already enabled. */
4687c478bd9Sstevel@tonic-gate if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
4697c478bd9Sstevel@tonic-gate iset.status = PCITOOL_INVALID_INO;
4707c478bd9Sstevel@tonic-gate rval = EINVAL;
4717c478bd9Sstevel@tonic-gate goto done_set_intr;
4727c478bd9Sstevel@tonic-gate }
4737c478bd9Sstevel@tonic-gate
474*09b1eac2SEvan Yan if (ib_get_intr_target(pci_p, iset.ino, &old_cpu_id) != DDI_SUCCESS) {
475*09b1eac2SEvan Yan iset.status = PCITOOL_INVALID_INO;
476*09b1eac2SEvan Yan rval = EINVAL;
477*09b1eac2SEvan Yan goto done_set_intr;
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate
480*09b1eac2SEvan Yan if ((ret = ib_set_intr_target(pci_p, iset.ino,
481*09b1eac2SEvan Yan iset.cpu_id)) == DDI_SUCCESS) {
4827c478bd9Sstevel@tonic-gate iset.cpu_id = old_cpu_id;
4837c478bd9Sstevel@tonic-gate iset.status = PCITOOL_SUCCESS;
484*09b1eac2SEvan Yan goto done_set_intr;
485*09b1eac2SEvan Yan }
4867c478bd9Sstevel@tonic-gate
487*09b1eac2SEvan Yan switch (ret) {
488*09b1eac2SEvan Yan case DDI_EPENDING:
489*09b1eac2SEvan Yan iset.status = PCITOOL_PENDING_INTRTIMEOUT;
490*09b1eac2SEvan Yan rval = ETIME;
491*09b1eac2SEvan Yan break;
492*09b1eac2SEvan Yan case DDI_EINVAL:
4937c478bd9Sstevel@tonic-gate iset.status = PCITOOL_INVALID_CPUID;
4947c478bd9Sstevel@tonic-gate rval = EINVAL;
495*09b1eac2SEvan Yan break;
496*09b1eac2SEvan Yan default:
497*09b1eac2SEvan Yan iset.status = PCITOOL_INVALID_INO;
498*09b1eac2SEvan Yan rval = EINVAL;
499*09b1eac2SEvan Yan break;
5007c478bd9Sstevel@tonic-gate }
5017c478bd9Sstevel@tonic-gate done_set_intr:
5022917a9c9Sschwartz iset.drvr_version = PCITOOL_VERSION;
5032917a9c9Sschwartz if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
5047c478bd9Sstevel@tonic-gate rval = EFAULT;
5057c478bd9Sstevel@tonic-gate
5067c478bd9Sstevel@tonic-gate return (rval);
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */
5117c478bd9Sstevel@tonic-gate int
pcitool_intr_admn(dev_t dev,void * arg,int cmd,int mode)5127c478bd9Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
5137c478bd9Sstevel@tonic-gate {
5147c478bd9Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev);
5157c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
5167c478bd9Sstevel@tonic-gate int rval = SUCCESS;
5177c478bd9Sstevel@tonic-gate
5187c478bd9Sstevel@tonic-gate switch (cmd) {
5197c478bd9Sstevel@tonic-gate
5202917a9c9Sschwartz /* Get system interrupt information. */
5212917a9c9Sschwartz case PCITOOL_SYSTEM_INTR_INFO:
5222917a9c9Sschwartz rval = pcitool_intr_info(dip, arg, mode);
5237c478bd9Sstevel@tonic-gate break;
5247c478bd9Sstevel@tonic-gate
5257c478bd9Sstevel@tonic-gate /* Get interrupt information for a given ino. */
5267c478bd9Sstevel@tonic-gate case PCITOOL_DEVICE_GET_INTR:
5277c478bd9Sstevel@tonic-gate rval = pcitool_get_intr(dip, arg, mode, pci_p);
5287c478bd9Sstevel@tonic-gate break;
5297c478bd9Sstevel@tonic-gate
5307c478bd9Sstevel@tonic-gate /* Associate a new CPU with a given ino. */
5317c478bd9Sstevel@tonic-gate case PCITOOL_DEVICE_SET_INTR:
5327c478bd9Sstevel@tonic-gate rval = pcitool_set_intr(dip, arg, mode, pci_p);
5337c478bd9Sstevel@tonic-gate break;
5347c478bd9Sstevel@tonic-gate
5357c478bd9Sstevel@tonic-gate default:
5367c478bd9Sstevel@tonic-gate rval = ENOTTY;
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate return (rval);
5407c478bd9Sstevel@tonic-gate }
5417c478bd9Sstevel@tonic-gate
5427c478bd9Sstevel@tonic-gate
5437c478bd9Sstevel@tonic-gate /*
5447851eb82Sschwartz * Wrapper around pcitool_phys_peek/poke.
5457c478bd9Sstevel@tonic-gate *
5467851eb82Sschwartz * Validates arguments and calls pcitool_phys_peek/poke appropriately.
5477c478bd9Sstevel@tonic-gate *
5487c478bd9Sstevel@tonic-gate * Dip is of the nexus,
5497c478bd9Sstevel@tonic-gate * phys_addr is the address to write in physical space,
5507c478bd9Sstevel@tonic-gate * max_addr is the upper bound on the physical space used for bounds checking,
5517c478bd9Sstevel@tonic-gate * pcitool_status returns more detailed status in addition to a more generic
5527c478bd9Sstevel@tonic-gate * errno-style function return value.
5537c478bd9Sstevel@tonic-gate * other args are self-explanatory.
5547c478bd9Sstevel@tonic-gate */
5557c478bd9Sstevel@tonic-gate static int
pcitool_access(pci_t * pci_p,uint64_t phys_addr,uint64_t max_addr,uint64_t * data,uint8_t size,boolean_t write,boolean_t endian,uint32_t * pcitool_status)5567851eb82Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
5577851eb82Sschwartz uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
5587851eb82Sschwartz uint32_t *pcitool_status)
5597c478bd9Sstevel@tonic-gate {
5607c478bd9Sstevel@tonic-gate
5617c478bd9Sstevel@tonic-gate int rval = SUCCESS;
5627c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
5637c478bd9Sstevel@tonic-gate
5647c478bd9Sstevel@tonic-gate /* Upper bounds checking. */
5657c478bd9Sstevel@tonic-gate if (phys_addr > max_addr) {
5667c478bd9Sstevel@tonic-gate DEBUG2(DBG_TOOLS, dip,
5677c478bd9Sstevel@tonic-gate "Phys addr 0x%llx out of range (max 0x%llx).\n",
5687c478bd9Sstevel@tonic-gate phys_addr, max_addr);
5697c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS;
5707c478bd9Sstevel@tonic-gate
5717c478bd9Sstevel@tonic-gate rval = EINVAL;
5727c478bd9Sstevel@tonic-gate
5737c478bd9Sstevel@tonic-gate /* Alignment checking. */
5747c478bd9Sstevel@tonic-gate } else if (!IS_P2ALIGNED(phys_addr, size)) {
5757c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
5767c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_NOT_ALIGNED;
5777c478bd9Sstevel@tonic-gate
5787c478bd9Sstevel@tonic-gate rval = EINVAL;
5797c478bd9Sstevel@tonic-gate
5807c478bd9Sstevel@tonic-gate /* Made it through checks. Do the access. */
5817c478bd9Sstevel@tonic-gate } else if (write) {
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
5847851eb82Sschwartz "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
5857c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
5867c478bd9Sstevel@tonic-gate
5877851eb82Sschwartz if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
5887851eb82Sschwartz *data) != DDI_SUCCESS) {
5897c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
5907851eb82Sschwartz "%d byte %s pcitool_phys_poke at addr "
5917c478bd9Sstevel@tonic-gate "0x%llx failed\n",
5927c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
5937c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS;
5947c478bd9Sstevel@tonic-gate
5957c478bd9Sstevel@tonic-gate rval = EFAULT;
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate
5987851eb82Sschwartz } else { /* Read */
5997c478bd9Sstevel@tonic-gate
6007c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
6017851eb82Sschwartz "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
6027c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
6037c478bd9Sstevel@tonic-gate
6047851eb82Sschwartz if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
6057851eb82Sschwartz data) != DDI_SUCCESS) {
6067c478bd9Sstevel@tonic-gate DEBUG3(DBG_PHYS_ACC, dip,
6077851eb82Sschwartz "%d byte %s pcitool_phys_peek at addr "
6087c478bd9Sstevel@tonic-gate "0x%llx failed\n",
6097c478bd9Sstevel@tonic-gate size, (endian ? "BE" : "LE"), phys_addr);
6107c478bd9Sstevel@tonic-gate *pcitool_status = PCITOOL_INVALID_ADDRESS;
6117c478bd9Sstevel@tonic-gate
6127c478bd9Sstevel@tonic-gate rval = EFAULT;
6137c478bd9Sstevel@tonic-gate }
6147c478bd9Sstevel@tonic-gate }
6157c478bd9Sstevel@tonic-gate return (rval);
6167c478bd9Sstevel@tonic-gate }
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate /*
6197c478bd9Sstevel@tonic-gate * Perform register accesses on the nexus device itself.
6207c478bd9Sstevel@tonic-gate */
6217c478bd9Sstevel@tonic-gate int
pcitool_bus_reg_ops(dev_t dev,void * arg,int cmd,int mode)6227c478bd9Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
6237c478bd9Sstevel@tonic-gate {
6247c478bd9Sstevel@tonic-gate
6257c478bd9Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev);
6267c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
6277851eb82Sschwartz pci_nexus_regspec_t *pci_rp = NULL;
6287851eb82Sschwartz boolean_t write_flag = B_FALSE;
6297c478bd9Sstevel@tonic-gate pcitool_reg_t prg;
6307c478bd9Sstevel@tonic-gate uint64_t base_addr;
6317c478bd9Sstevel@tonic-gate uint64_t max_addr;
6327c478bd9Sstevel@tonic-gate uint32_t reglen;
6337c478bd9Sstevel@tonic-gate uint8_t size;
6347c478bd9Sstevel@tonic-gate uint32_t rval = 0;
6357c478bd9Sstevel@tonic-gate
6367851eb82Sschwartz if (cmd == PCITOOL_NEXUS_SET_REG)
6377c478bd9Sstevel@tonic-gate write_flag = B_TRUE;
6387c478bd9Sstevel@tonic-gate
6397851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
6407c478bd9Sstevel@tonic-gate
6417851eb82Sschwartz /* Read data from userland. */
6427851eb82Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
6437851eb82Sschwartz DDI_SUCCESS) {
6447851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
6457851eb82Sschwartz return (EFAULT);
6467851eb82Sschwartz }
6477c478bd9Sstevel@tonic-gate
6487851eb82Sschwartz /* Read reg property which contains starting addr and size of banks. */
6497851eb82Sschwartz if (ddi_prop_lookup_int_array(
6507851eb82Sschwartz DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6517851eb82Sschwartz "reg", (int **)&pci_rp, ®len) == DDI_SUCCESS) {
6527851eb82Sschwartz if (((reglen * sizeof (int)) %
6537851eb82Sschwartz sizeof (pci_nexus_regspec_t)) != 0) {
6547851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
6557851eb82Sschwartz prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
6567851eb82Sschwartz rval = EIO;
6577c478bd9Sstevel@tonic-gate goto done;
6587c478bd9Sstevel@tonic-gate }
6597851eb82Sschwartz }
6607c478bd9Sstevel@tonic-gate
6617851eb82Sschwartz /* Bounds check the bank number. */
6627851eb82Sschwartz if (prg.barnum >=
6637851eb82Sschwartz (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
6647851eb82Sschwartz prg.status = PCITOOL_OUT_OF_RANGE;
6657851eb82Sschwartz rval = EINVAL;
6667851eb82Sschwartz goto done;
6677851eb82Sschwartz }
6687c478bd9Sstevel@tonic-gate
6697851eb82Sschwartz size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
6707851eb82Sschwartz base_addr = pci_rp[prg.barnum].phys_addr;
6717851eb82Sschwartz max_addr = base_addr + pci_rp[prg.barnum].size;
6727851eb82Sschwartz prg.phys_addr = base_addr + prg.offset;
6737c478bd9Sstevel@tonic-gate
6747851eb82Sschwartz DEBUG4(DBG_TOOLS, dip,
6757851eb82Sschwartz "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
6767851eb82Sschwartz "addr:0x%llx, max_addr:0x%llx\n",
6777851eb82Sschwartz base_addr, prg.offset, prg.phys_addr, max_addr);
6787c478bd9Sstevel@tonic-gate
6797851eb82Sschwartz /* Access device. prg.status is modified. */
6807851eb82Sschwartz rval = pcitool_access(pci_p,
6817851eb82Sschwartz prg.phys_addr, max_addr, &prg.data, size, write_flag,
6827851eb82Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
6837c478bd9Sstevel@tonic-gate
6847c478bd9Sstevel@tonic-gate done:
6857851eb82Sschwartz if (pci_rp != NULL)
6867851eb82Sschwartz ddi_prop_free(pci_rp);
6877851eb82Sschwartz
6882917a9c9Sschwartz prg.drvr_version = PCITOOL_VERSION;
6897c478bd9Sstevel@tonic-gate if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
6907c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
6917c478bd9Sstevel@tonic-gate DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
6927c478bd9Sstevel@tonic-gate return (EFAULT);
6937c478bd9Sstevel@tonic-gate }
6947c478bd9Sstevel@tonic-gate
6957c478bd9Sstevel@tonic-gate return (rval);
6967c478bd9Sstevel@tonic-gate }
6977c478bd9Sstevel@tonic-gate
6987c478bd9Sstevel@tonic-gate
6997851eb82Sschwartz static int
pcitool_validate_barnum_bdf(pcitool_reg_t * prg)7007851eb82Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
7017851eb82Sschwartz {
7027851eb82Sschwartz int rval = SUCCESS;
7037851eb82Sschwartz
7047851eb82Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
7057851eb82Sschwartz prg->status = PCITOOL_OUT_OF_RANGE;
7067851eb82Sschwartz rval = EINVAL;
7077851eb82Sschwartz
7087851eb82Sschwartz /* Validate address arguments of bus / dev / func */
7097851eb82Sschwartz } else if (((prg->bus_no &
7107851eb82Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
7117851eb82Sschwartz ((prg->dev_no &
7127851eb82Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
7137851eb82Sschwartz ((prg->func_no &
7147851eb82Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
7157851eb82Sschwartz prg->status = PCITOOL_INVALID_ADDRESS;
7167851eb82Sschwartz rval = EINVAL;
7177851eb82Sschwartz }
7187851eb82Sschwartz
7197851eb82Sschwartz return (rval);
7207851eb82Sschwartz }
7217851eb82Sschwartz
7227851eb82Sschwartz static int
pcitool_get_bar(pci_t * pci_p,pcitool_reg_t * prg,uint64_t config_base_addr,uint64_t config_max_addr,uint64_t * bar,boolean_t * is_io_space)7237851eb82Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
7247851eb82Sschwartz uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
7257851eb82Sschwartz {
7267851eb82Sschwartz
7277851eb82Sschwartz uint8_t bar_offset;
7287851eb82Sschwartz int rval;
7297851eb82Sschwartz dev_info_t *dip = pci_p->pci_dip;
7307851eb82Sschwartz
7317851eb82Sschwartz *bar = 0;
7327851eb82Sschwartz *is_io_space = B_FALSE;
7337851eb82Sschwartz
7347851eb82Sschwartz /*
7357851eb82Sschwartz * Translate BAR number into offset of the BAR in
7367851eb82Sschwartz * the device's config space.
7377851eb82Sschwartz */
7387851eb82Sschwartz bar_offset = PCI_BAR_OFFSET((*prg));
7397851eb82Sschwartz
7407851eb82Sschwartz DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
7417851eb82Sschwartz prg->barnum, bar_offset);
7427851eb82Sschwartz
7437851eb82Sschwartz /*
7447851eb82Sschwartz * Get Bus Address Register (BAR) from config space.
7457851eb82Sschwartz * bar_offset is the offset into config space of the BAR desired.
7467851eb82Sschwartz * prg->status is modified on error.
7477851eb82Sschwartz */
7487851eb82Sschwartz rval = pcitool_access(pci_p, config_base_addr + bar_offset,
7497851eb82Sschwartz config_max_addr, bar,
7507851eb82Sschwartz 4, /* 4 bytes. */
7517851eb82Sschwartz B_FALSE, /* Read */
7527851eb82Sschwartz B_FALSE, /* Little endian. */
7537851eb82Sschwartz &prg->status);
7547851eb82Sschwartz if (rval != SUCCESS)
7557851eb82Sschwartz return (rval);
7567851eb82Sschwartz
7577851eb82Sschwartz DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
7587851eb82Sschwartz if (!(*bar)) {
7597851eb82Sschwartz prg->status = PCITOOL_INVALID_ADDRESS;
7607851eb82Sschwartz return (EINVAL);
7617851eb82Sschwartz }
7627851eb82Sschwartz
7637851eb82Sschwartz /*
7647851eb82Sschwartz * BAR has bits saying this space is IO space, unless
7657851eb82Sschwartz * this is the ROM address register.
7667851eb82Sschwartz */
7677851eb82Sschwartz if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
7687851eb82Sschwartz (bar_offset != PCI_CONF_ROM)) {
7697851eb82Sschwartz *is_io_space = B_TRUE;
7707851eb82Sschwartz *bar &= PCI_BASE_IO_ADDR_M;
7717851eb82Sschwartz
7727851eb82Sschwartz /*
7737851eb82Sschwartz * BAR has bits saying this space is 64 bit memory
7747851eb82Sschwartz * space, unless this is the ROM address register.
7757851eb82Sschwartz *
7767851eb82Sschwartz * The 64 bit address stored in two BAR cells is not necessarily
7777851eb82Sschwartz * aligned on an 8-byte boundary. Need to keep the first 4
7787851eb82Sschwartz * bytes read, and do a separate read of the high 4 bytes.
7797851eb82Sschwartz */
7807851eb82Sschwartz
7817851eb82Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
7827851eb82Sschwartz
7837851eb82Sschwartz uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
7847851eb82Sschwartz
7857851eb82Sschwartz /* Don't try to read past the end of BARs. */
7867851eb82Sschwartz if (bar_offset >= PCI_CONF_BASE5) {
7877851eb82Sschwartz prg->status = PCITOOL_OUT_OF_RANGE;
7887851eb82Sschwartz return (EIO);
7897851eb82Sschwartz }
7907851eb82Sschwartz
7917851eb82Sschwartz /* Access device. prg->status is modified on error. */
7927851eb82Sschwartz rval = pcitool_access(pci_p,
7937851eb82Sschwartz config_base_addr + bar_offset + 4, config_max_addr, bar,
7947851eb82Sschwartz 4, /* 4 bytes. */
7957851eb82Sschwartz B_FALSE, /* Read */
7967851eb82Sschwartz B_FALSE, /* Little endian. */
7977851eb82Sschwartz &prg->status);
7987851eb82Sschwartz if (rval != SUCCESS)
7997851eb82Sschwartz return (rval);
8007851eb82Sschwartz
8017851eb82Sschwartz *bar = (*bar << 32) + low_bytes;
8027851eb82Sschwartz }
8037851eb82Sschwartz
8047851eb82Sschwartz return (SUCCESS);
8057851eb82Sschwartz }
8067851eb82Sschwartz
8077851eb82Sschwartz
8087851eb82Sschwartz static int
pcitool_config_request(pci_t * pci_p,pcitool_reg_t * prg,uint64_t base_addr,uint64_t max_addr,uint8_t size,boolean_t write_flag)8097851eb82Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
8107851eb82Sschwartz uint64_t max_addr, uint8_t size, boolean_t write_flag)
8117851eb82Sschwartz {
8127851eb82Sschwartz int rval;
8137851eb82Sschwartz dev_info_t *dip = pci_p->pci_dip;
8147851eb82Sschwartz
8157851eb82Sschwartz /* Access config space and we're done. */
8167851eb82Sschwartz prg->phys_addr = base_addr + prg->offset;
8177851eb82Sschwartz
8187851eb82Sschwartz DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
8197851eb82Sschwartz "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
8207851eb82Sschwartz base_addr, prg->offset, prg->phys_addr,
8217851eb82Sschwartz (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
8227851eb82Sschwartz
8237851eb82Sschwartz /* Access device. pr.status is modified. */
8247851eb82Sschwartz rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
8257851eb82Sschwartz write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
8267851eb82Sschwartz
8277851eb82Sschwartz DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
8287851eb82Sschwartz
8297851eb82Sschwartz return (rval);
8307851eb82Sschwartz }
8317851eb82Sschwartz
8327c478bd9Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
8337c478bd9Sstevel@tonic-gate int
pcitool_dev_reg_ops(dev_t dev,void * arg,int cmd,int mode)8347c478bd9Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
8357c478bd9Sstevel@tonic-gate {
8367c478bd9Sstevel@tonic-gate pci_t *pci_p = DEV_TO_SOFTSTATE(dev);
8377c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip;
8387c478bd9Sstevel@tonic-gate pci_ranges_t *rp = pci_p->pci_ranges;
8397c478bd9Sstevel@tonic-gate pcitool_reg_t prg;
8407c478bd9Sstevel@tonic-gate uint64_t max_addr;
8417c478bd9Sstevel@tonic-gate uint64_t base_addr;
8427c478bd9Sstevel@tonic-gate uint64_t range_prop;
8437c478bd9Sstevel@tonic-gate uint64_t range_prop_size;
8447c478bd9Sstevel@tonic-gate uint64_t bar = 0;
8457c478bd9Sstevel@tonic-gate int rval = 0;
8467c478bd9Sstevel@tonic-gate boolean_t write_flag = B_FALSE;
8477851eb82Sschwartz boolean_t is_io_space = B_FALSE;
8487c478bd9Sstevel@tonic-gate uint8_t size;
8497c478bd9Sstevel@tonic-gate
8507851eb82Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG)
8517c478bd9Sstevel@tonic-gate write_flag = B_TRUE;
8527c478bd9Sstevel@tonic-gate
8537851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
8547851eb82Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
8557851eb82Sschwartz DDI_SUCCESS) {
8567851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
8577851eb82Sschwartz return (EFAULT);
8587851eb82Sschwartz }
8597c478bd9Sstevel@tonic-gate
8607851eb82Sschwartz DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
8617851eb82Sschwartz prg.bus_no, prg.dev_no, prg.func_no);
8627c478bd9Sstevel@tonic-gate
8637851eb82Sschwartz if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
8647851eb82Sschwartz goto done_reg;
8657c478bd9Sstevel@tonic-gate
8667851eb82Sschwartz size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
8677c478bd9Sstevel@tonic-gate
8687851eb82Sschwartz /* Get config space first. */
8697851eb82Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
8707851eb82Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
8717851eb82Sschwartz max_addr = range_prop + range_prop_size;
8727c478bd9Sstevel@tonic-gate
8737851eb82Sschwartz /*
8747851eb82Sschwartz * Build device address based on base addr from range prop, and bus,
8757851eb82Sschwartz * dev and func values passed in. This address is where config space
8767851eb82Sschwartz * begins.
8777851eb82Sschwartz */
8787851eb82Sschwartz base_addr = range_prop +
8797851eb82Sschwartz (prg.bus_no << PCI_REG_BUS_SHIFT) +
8807851eb82Sschwartz (prg.dev_no << PCI_REG_DEV_SHIFT) +
8817851eb82Sschwartz (prg.func_no << PCI_REG_FUNC_SHIFT);
8827c478bd9Sstevel@tonic-gate
8837851eb82Sschwartz if ((base_addr < range_prop) || (base_addr >= max_addr)) {
8847851eb82Sschwartz prg.status = PCITOOL_OUT_OF_RANGE;
8857851eb82Sschwartz rval = EINVAL;
8867851eb82Sschwartz goto done_reg;
8877851eb82Sschwartz }
8887c478bd9Sstevel@tonic-gate
8897851eb82Sschwartz DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
8907851eb82Sschwartz "func:0x%x, addr:0x%x\n", range_prop,
8917851eb82Sschwartz prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
8927851eb82Sschwartz prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
8937c478bd9Sstevel@tonic-gate
8947851eb82Sschwartz /* Proper config space desired. */
8957851eb82Sschwartz if (prg.barnum == 0) {
8967c478bd9Sstevel@tonic-gate
8977851eb82Sschwartz rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
8987851eb82Sschwartz size, write_flag);
8997c478bd9Sstevel@tonic-gate
9007851eb82Sschwartz } else { /* IO / MEM / MEM64 space. */
9017c478bd9Sstevel@tonic-gate
9027851eb82Sschwartz if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
9037851eb82Sschwartz &is_io_space) != SUCCESS)
9047851eb82Sschwartz goto done_reg;
9057c478bd9Sstevel@tonic-gate
9067851eb82Sschwartz /* IO space. */
9077851eb82Sschwartz if (is_io_space) {
9087c478bd9Sstevel@tonic-gate
9097851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "IO space\n");
9107c478bd9Sstevel@tonic-gate
9117851eb82Sschwartz /* Reposition to focus on IO space. */
9127851eb82Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
9137851eb82Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
9147851eb82Sschwartz PCI_IO_RANGE_BANK);
9157c478bd9Sstevel@tonic-gate
9167851eb82Sschwartz /* 64 bit memory space. */
9177851eb82Sschwartz } else if ((bar >> 32) != 0) {
9187851eb82Sschwartz
9197851eb82Sschwartz DEBUG1(DBG_TOOLS, dip,
9207851eb82Sschwartz "64 bit mem space. 64-bit bar is 0x%llx\n", bar);
9217851eb82Sschwartz
9227851eb82Sschwartz /* Reposition to MEM64 range space. */
9237851eb82Sschwartz range_prop = PCI_GET_RANGE_PROP(rp,
9247851eb82Sschwartz PCI_MEM64_RANGE_BANK);
9257851eb82Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
9267851eb82Sschwartz PCI_MEM64_RANGE_BANK);
9277c478bd9Sstevel@tonic-gate
9287851eb82Sschwartz } else { /* Mem32 space, including ROM */
9297c478bd9Sstevel@tonic-gate
9307851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
9317c478bd9Sstevel@tonic-gate
9327851eb82Sschwartz if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9337851eb82Sschwartz
9347851eb82Sschwartz DEBUG0(DBG_TOOLS, dip,
9357851eb82Sschwartz "Additional ROM checking\n");
9367851eb82Sschwartz
9377851eb82Sschwartz /* Can't write to ROM */
9387851eb82Sschwartz if (write_flag) {
9397851eb82Sschwartz prg.status = PCITOOL_ROM_WRITE;
9407c478bd9Sstevel@tonic-gate rval = EIO;
9417c478bd9Sstevel@tonic-gate goto done_reg;
9427c478bd9Sstevel@tonic-gate
9437851eb82Sschwartz /* ROM disabled for reading */
9447851eb82Sschwartz } else if (!(bar & 0x00000001)) {
9457851eb82Sschwartz prg.status = PCITOOL_ROM_DISABLED;
9467851eb82Sschwartz rval = EIO;
9477c478bd9Sstevel@tonic-gate goto done_reg;
9487c478bd9Sstevel@tonic-gate }
9497851eb82Sschwartz }
9507c478bd9Sstevel@tonic-gate
9517851eb82Sschwartz range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
9527851eb82Sschwartz range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
9537851eb82Sschwartz PCI_MEM_RANGE_BANK);
9547851eb82Sschwartz }
9557c478bd9Sstevel@tonic-gate
9567851eb82Sschwartz /* Common code for all IO/MEM range spaces. */
9577851eb82Sschwartz max_addr = range_prop + range_prop_size;
9587851eb82Sschwartz base_addr = range_prop + bar;
9597c478bd9Sstevel@tonic-gate
9607851eb82Sschwartz DEBUG3(DBG_TOOLS, dip,
9617851eb82Sschwartz "addr portion of bar is 0x%llx, base=0x%llx, "
9627851eb82Sschwartz "offset:0x%lx\n", bar, base_addr, prg.offset);
9637c478bd9Sstevel@tonic-gate
9647851eb82Sschwartz /*
9657851eb82Sschwartz * Use offset provided by caller to index into
9667851eb82Sschwartz * desired space, then access.
9677851eb82Sschwartz * Note that prg.status is modified on error.
9687851eb82Sschwartz */
9697851eb82Sschwartz prg.phys_addr = base_addr + prg.offset;
9707851eb82Sschwartz rval = pcitool_access(pci_p, prg.phys_addr,
9717851eb82Sschwartz max_addr, &prg.data, size, write_flag,
9727851eb82Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
9737851eb82Sschwartz }
9747c478bd9Sstevel@tonic-gate
9757851eb82Sschwartz done_reg:
9762917a9c9Sschwartz prg.drvr_version = PCITOOL_VERSION;
9777851eb82Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
9787851eb82Sschwartz DDI_SUCCESS) {
9797851eb82Sschwartz DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
9807851eb82Sschwartz rval = EFAULT;
9817851eb82Sschwartz }
9827851eb82Sschwartz return (rval);
9837851eb82Sschwartz }
9847c478bd9Sstevel@tonic-gate
9857851eb82Sschwartz int
pcitool_init(dev_info_t * dip)9867851eb82Sschwartz pcitool_init(dev_info_t *dip)
9877851eb82Sschwartz {
9887851eb82Sschwartz int instance = ddi_get_instance(dip);
9897c478bd9Sstevel@tonic-gate
9907851eb82Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
9917851eb82Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
9927851eb82Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS)
9937851eb82Sschwartz return (DDI_FAILURE);
9947c478bd9Sstevel@tonic-gate
9957851eb82Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
9967851eb82Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
9977851eb82Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
9987851eb82Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG);
9997851eb82Sschwartz return (DDI_FAILURE);
10007851eb82Sschwartz }
10017c478bd9Sstevel@tonic-gate
10027851eb82Sschwartz return (DDI_SUCCESS);
10037851eb82Sschwartz }
10047c478bd9Sstevel@tonic-gate
10057851eb82Sschwartz void
pcitool_uninit(dev_info_t * dip)10067851eb82Sschwartz pcitool_uninit(dev_info_t *dip)
10077851eb82Sschwartz {
10087851eb82Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG);
10097851eb82Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR);
10107c478bd9Sstevel@tonic-gate }
1011