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