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