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