1624Sschwartz /* 2624Sschwartz * CDDL HEADER START 3624Sschwartz * 4624Sschwartz * The contents of this file are subject to the terms of the 51772Sjl139090 * Common Development and Distribution License (the "License"). 61772Sjl139090 * You may not use this file except in compliance with the License. 7624Sschwartz * 8624Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9624Sschwartz * or http://www.opensolaris.org/os/licensing. 10624Sschwartz * See the License for the specific language governing permissions 11624Sschwartz * and limitations under the License. 12624Sschwartz * 13624Sschwartz * When distributing Covered Code, include this CDDL HEADER in each 14624Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15624Sschwartz * If applicable, add the following below this CDDL HEADER, with the 16624Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying 17624Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner] 18624Sschwartz * 19624Sschwartz * CDDL HEADER END 20624Sschwartz */ 21624Sschwartz /* 223625Segillett * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23624Sschwartz * Use is subject to license terms. 24624Sschwartz */ 25624Sschwartz 26624Sschwartz #pragma ident "%Z%%M% %I% %E% SMI" 27624Sschwartz 28624Sschwartz #include <sys/types.h> 29624Sschwartz #include <sys/stat.h> 30624Sschwartz #include <sys/cpuvar.h> 31624Sschwartz #include <sys/kmem.h> 32624Sschwartz #include <sys/sunddi.h> 33624Sschwartz #include <sys/hotplug/pci/pcihp.h> 34624Sschwartz #include "px_obj.h" 35624Sschwartz #include <sys/pci_tools.h> 36777Sschwartz #include "px_tools_ext.h" 37624Sschwartz #include "px_tools_var.h" 38624Sschwartz 39624Sschwartz /* 40624Sschwartz * PCI Space definitions. 41624Sschwartz */ 42624Sschwartz #define PCI_CONFIG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 43624Sschwartz #define PCI_IO_SPACE (PCI_REG_ADDR_G(PCI_ADDR_IO)) 44624Sschwartz #define PCI_MEM32_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 45624Sschwartz #define PCI_MEM64_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 46624Sschwartz 47624Sschwartz /* 48624Sschwartz * Config space range for a device. IEEE 1275 spec defines for PCI. 49624Sschwartz * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA 50624Sschwartz */ 51624Sschwartz #define DEV_CFG_SPACE_SIZE \ 52624Sschwartz (1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA)) 53624Sschwartz 54624Sschwartz /* 55624Sschwartz * Offsets of BARS in config space. First entry of 0 means config space. 56624Sschwartz * Entries here correlate to pcitool_bars_t enumerated type. 57624Sschwartz */ 58624Sschwartz uint8_t pci_bars[] = { 59624Sschwartz 0x0, 60624Sschwartz PCI_CONF_BASE0, 61624Sschwartz PCI_CONF_BASE1, 62624Sschwartz PCI_CONF_BASE2, 63624Sschwartz PCI_CONF_BASE3, 64624Sschwartz PCI_CONF_BASE4, 65624Sschwartz PCI_CONF_BASE5, 66624Sschwartz PCI_CONF_ROM 67624Sschwartz }; 68624Sschwartz 69624Sschwartz int pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]); 70624Sschwartz 71624Sschwartz /* 72624Sschwartz * Validate the cpu_id passed in. 73624Sschwartz * A value of 1 will be returned for success and zero for failure. 74624Sschwartz */ 75624Sschwartz static int 76624Sschwartz pxtool_validate_cpuid(uint32_t cpuid) 77624Sschwartz { 78624Sschwartz extern const int _ncpu; 79624Sschwartz extern cpu_t *cpu[]; 80624Sschwartz 81624Sschwartz ASSERT(mutex_owned(&cpu_lock)); 82624Sschwartz 83624Sschwartz return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid]))); 84624Sschwartz } 85624Sschwartz 86624Sschwartz 87*4397Sschwartz /*ARGSUSED*/ 88624Sschwartz static int 89*4397Sschwartz pxtool_intr_info(dev_info_t *dip, void *arg, int mode) 90624Sschwartz { 91*4397Sschwartz pcitool_intr_info_t intr_info; 92*4397Sschwartz int rval = SUCCESS; 93*4397Sschwartz 94*4397Sschwartz /* If we need user_version, and to ret same user version as passed in */ 95*4397Sschwartz if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != 96*4397Sschwartz DDI_SUCCESS) { 97624Sschwartz return (EFAULT); 98*4397Sschwartz } 99*4397Sschwartz 100*4397Sschwartz intr_info.ctlr_version = 0; /* XXX how to get real version? */ 101*4397Sschwartz intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; 102*4397Sschwartz intr_info.num_intr = pxtool_num_inos; 103*4397Sschwartz 104*4397Sschwartz intr_info.drvr_version = PCITOOL_VERSION; 105*4397Sschwartz if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != 106*4397Sschwartz DDI_SUCCESS) { 107*4397Sschwartz rval = EFAULT; 108*4397Sschwartz } 109*4397Sschwartz 110*4397Sschwartz return (rval); 111624Sschwartz } 112*4397Sschwartz 113*4397Sschwartz 114624Sschwartz /* 115624Sschwartz * Get interrupt information for a given ino. 116624Sschwartz * Returns info only for inos mapped to devices. 117624Sschwartz * 118624Sschwartz * Returned info is valid only when iget.num_devs is returned > 0. 119624Sschwartz * If ino is not enabled or is not mapped to a device, 120624Sschwartz * iget.num_devs will be returned as = 0. 121624Sschwartz */ 122624Sschwartz /*ARGSUSED*/ 123624Sschwartz static int 124624Sschwartz pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 125624Sschwartz { 126624Sschwartz /* Array part isn't used here, but oh well... */ 127624Sschwartz pcitool_intr_get_t partial_iget; 128624Sschwartz uint32_t ino; 129624Sschwartz uint8_t num_devs_ret; 130624Sschwartz int copyout_rval; 131624Sschwartz sysino_t sysino; 132624Sschwartz intr_valid_state_t intr_valid_state; 133624Sschwartz cpuid_t old_cpu_id; 134624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 135624Sschwartz pcitool_intr_get_t *iget = &partial_iget; 136624Sschwartz size_t iget_kmem_alloc_size = 0; 137624Sschwartz int rval = SUCCESS; 138624Sschwartz 139624Sschwartz /* Read in just the header part, no array section. */ 140624Sschwartz if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 141624Sschwartz DDI_SUCCESS) 142624Sschwartz return (EFAULT); 143624Sschwartz 144624Sschwartz ino = partial_iget.ino; 145624Sschwartz num_devs_ret = partial_iget.num_devs_ret; 146624Sschwartz 147624Sschwartz partial_iget.num_devs_ret = 0; /* Assume error for now. */ 148624Sschwartz partial_iget.status = PCITOOL_INVALID_INO; 149624Sschwartz rval = EINVAL; 150624Sschwartz 151624Sschwartz /* Validate argument. */ 152624Sschwartz if (partial_iget.ino > pxtool_num_inos) { 153624Sschwartz goto done_get_intr; 154624Sschwartz } 155624Sschwartz 156624Sschwartz /* Caller wants device information returned. */ 157624Sschwartz if (num_devs_ret > 0) { 158624Sschwartz 159624Sschwartz /* 160624Sschwartz * Allocate room. 161624Sschwartz * Note if num_devs == 0 iget remains pointing to 162624Sschwartz * partial_iget. 163624Sschwartz */ 164624Sschwartz iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 165624Sschwartz iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 166624Sschwartz 167624Sschwartz /* Read in whole structure to verify there's room. */ 168624Sschwartz if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 169624Sschwartz SUCCESS) { 170624Sschwartz 171624Sschwartz /* Be consistent and just return EFAULT here. */ 172624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 173624Sschwartz 174624Sschwartz return (EFAULT); 175624Sschwartz } 176624Sschwartz } 177624Sschwartz 178624Sschwartz bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 179624Sschwartz iget->ino = ino; 180624Sschwartz iget->num_devs_ret = num_devs_ret; 181624Sschwartz 182624Sschwartz /* Convert leaf-wide intr to system-wide intr */ 183624Sschwartz if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) == 184624Sschwartz DDI_FAILURE) { 185624Sschwartz iget->status = PCITOOL_IO_ERROR; 186624Sschwartz rval = EIO; 187624Sschwartz goto done_get_intr; 188624Sschwartz } 189624Sschwartz 190624Sschwartz /* Operate only on inos which are already enabled. */ 191624Sschwartz if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) == 192624Sschwartz DDI_FAILURE) { 193624Sschwartz iget->status = PCITOOL_IO_ERROR; 194624Sschwartz rval = EIO; 195624Sschwartz goto done_get_intr; 196624Sschwartz } 197624Sschwartz 198624Sschwartz /* 199624Sschwartz * Consider all valid inos: those mapped to the root complex itself 200624Sschwartz * as well as those mapped to devices. 201624Sschwartz */ 202624Sschwartz if (intr_valid_state == INTR_VALID) { 203624Sschwartz 204624Sschwartz /* 2052973Sgovinda * The following looks up the px_ino and returns 206624Sschwartz * info of devices mapped to this ino. 207624Sschwartz */ 208624Sschwartz iget->num_devs = pxtool_ib_get_ino_devs( 209624Sschwartz px_p, ino, &iget->num_devs_ret, iget->dev); 210624Sschwartz 211624Sschwartz if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 212624Sschwartz DDI_FAILURE) { 213624Sschwartz iget->status = PCITOOL_IO_ERROR; 214624Sschwartz rval = EIO; 215624Sschwartz goto done_get_intr; 216624Sschwartz } 217624Sschwartz iget->cpu_id = old_cpu_id; 218624Sschwartz } 219624Sschwartz 220624Sschwartz iget->status = PCITOOL_SUCCESS; 221624Sschwartz rval = SUCCESS; 222624Sschwartz 223624Sschwartz done_get_intr: 224*4397Sschwartz iget->drvr_version = PCITOOL_VERSION; 225624Sschwartz copyout_rval = 226624Sschwartz ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); 227624Sschwartz 228624Sschwartz if (iget_kmem_alloc_size > 0) 229624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 230624Sschwartz 231624Sschwartz if (copyout_rval != DDI_SUCCESS) 232624Sschwartz rval = EFAULT; 233624Sschwartz 234624Sschwartz return (rval); 235624Sschwartz } 236624Sschwartz 237624Sschwartz 238624Sschwartz /* 239624Sschwartz * Associate a new CPU with a given ino. 240624Sschwartz * 241624Sschwartz * Operate only on inos which are already mapped to devices. 242624Sschwartz */ 243624Sschwartz static int 244624Sschwartz pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 245624Sschwartz { 246624Sschwartz pcitool_intr_set_t iset; 247624Sschwartz cpuid_t old_cpu_id; 248624Sschwartz sysino_t sysino; 249624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 250624Sschwartz px_ib_t *ib_p = px_p->px_ib_p; 251624Sschwartz uint8_t zero = 0; 252624Sschwartz int rval = SUCCESS; 253*4397Sschwartz size_t copyinout_size; 254624Sschwartz 255*4397Sschwartz bzero(&iset, sizeof (pcitool_intr_set_t)); 256*4397Sschwartz 257*4397Sschwartz /* Version 1 of pcitool_intr_set_t doesn't have flags. */ 258*4397Sschwartz copyinout_size = (size_t)&iset.flags - (size_t)&iset; 259*4397Sschwartz 260*4397Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 261624Sschwartz return (EFAULT); 262624Sschwartz 263*4397Sschwartz switch (iset.user_version) { 264*4397Sschwartz case PCITOOL_V1: 265*4397Sschwartz break; 266*4397Sschwartz 267*4397Sschwartz case PCITOOL_V2: 268*4397Sschwartz copyinout_size = sizeof (pcitool_intr_set_t); 269*4397Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 270*4397Sschwartz return (EFAULT); 271*4397Sschwartz break; 272*4397Sschwartz 273*4397Sschwartz default: 274*4397Sschwartz iset.status = PCITOOL_OUT_OF_RANGE; 275*4397Sschwartz rval = ENOTSUP; 276*4397Sschwartz goto done_set_intr; 277*4397Sschwartz } 278*4397Sschwartz 279*4397Sschwartz if (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP) { 280*4397Sschwartz iset.status = PCITOOL_IO_ERROR; 281*4397Sschwartz rval = ENOTSUP; 282*4397Sschwartz goto done_set_intr; 283*4397Sschwartz } 284*4397Sschwartz 285624Sschwartz iset.status = PCITOOL_INVALID_INO; 286624Sschwartz rval = EINVAL; 287624Sschwartz 288624Sschwartz /* Validate input argument. */ 289624Sschwartz if (iset.ino > pxtool_num_inos) 290624Sschwartz goto done_set_intr; 291624Sschwartz 292624Sschwartz /* Validate that ino given belongs to a device. */ 293624Sschwartz if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0) 294624Sschwartz goto done_set_intr; 295624Sschwartz 296624Sschwartz /* 297624Sschwartz * Get lock, validate cpu and write new mapreg value. 298624Sschwartz * Return original cpu value to caller via iset.cpu. 299624Sschwartz */ 300624Sschwartz mutex_enter(&cpu_lock); 301624Sschwartz if (pxtool_validate_cpuid(iset.cpu_id)) { 302624Sschwartz 303624Sschwartz DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id); 304624Sschwartz 305624Sschwartz if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) == 306624Sschwartz DDI_FAILURE) 307624Sschwartz goto done_set_intr; 308624Sschwartz 309624Sschwartz if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 310624Sschwartz DDI_FAILURE) 311624Sschwartz goto done_set_intr; 312624Sschwartz 313624Sschwartz px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE); 314624Sschwartz 315624Sschwartz px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino); 316624Sschwartz 317624Sschwartz iset.cpu_id = old_cpu_id; 318624Sschwartz iset.status = PCITOOL_SUCCESS; 319624Sschwartz rval = SUCCESS; 320624Sschwartz 321624Sschwartz } else { /* Invalid cpu. Restore original register image. */ 322624Sschwartz 323624Sschwartz DBG(DBG_TOOLS, dip, 324624Sschwartz "Invalid cpuid: writing orig mapreg value\n"); 325624Sschwartz 326624Sschwartz iset.status = PCITOOL_INVALID_CPUID; 327624Sschwartz rval = EINVAL; 328624Sschwartz } 329624Sschwartz mutex_exit(&cpu_lock); 330624Sschwartz 331624Sschwartz done_set_intr: 332*4397Sschwartz iset.drvr_version = PCITOOL_VERSION; 333*4397Sschwartz if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) 334624Sschwartz rval = EFAULT; 335624Sschwartz 336624Sschwartz return (rval); 337624Sschwartz } 338624Sschwartz 339624Sschwartz 340624Sschwartz /* Main function for handling interrupt CPU binding requests and queries. */ 341624Sschwartz int 342624Sschwartz pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 343624Sschwartz { 344624Sschwartz int rval = SUCCESS; 345624Sschwartz 346624Sschwartz switch (cmd) { 347624Sschwartz 348*4397Sschwartz /* Get system interrupt information. */ 349*4397Sschwartz case PCITOOL_SYSTEM_INTR_INFO: 350*4397Sschwartz rval = pxtool_intr_info(dip, arg, mode); 351624Sschwartz break; 352624Sschwartz 353624Sschwartz /* Get interrupt information for a given ino. */ 354624Sschwartz case PCITOOL_DEVICE_GET_INTR: 355624Sschwartz rval = pxtool_get_intr(dip, arg, mode); 356624Sschwartz break; 357624Sschwartz 358624Sschwartz /* Associate a new CPU with a given ino. */ 359624Sschwartz case PCITOOL_DEVICE_SET_INTR: 360624Sschwartz rval = pxtool_set_intr(dip, arg, mode); 361624Sschwartz break; 362624Sschwartz 363624Sschwartz default: 364624Sschwartz rval = ENOTTY; 365624Sschwartz } 366624Sschwartz 367624Sschwartz return (rval); 368624Sschwartz } 369624Sschwartz 370624Sschwartz 371624Sschwartz static int 372624Sschwartz pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 373624Sschwartz { 374624Sschwartz int rval = SUCCESS; 375624Sschwartz 376624Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 377624Sschwartz prg->status = PCITOOL_OUT_OF_RANGE; 378624Sschwartz rval = EINVAL; 379624Sschwartz 380624Sschwartz /* Validate address arguments of bus / dev / func */ 381624Sschwartz } else if (((prg->bus_no & 382624Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 383624Sschwartz ((prg->dev_no & 384624Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 385624Sschwartz ((prg->func_no & 386624Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 387624Sschwartz prg->status = PCITOOL_INVALID_ADDRESS; 388624Sschwartz rval = EINVAL; 389624Sschwartz } 390624Sschwartz 391624Sschwartz return (rval); 392624Sschwartz } 393624Sschwartz 3941064Sschwartz /* 3951064Sschwartz * px_p defines which leaf, space defines which space in that leaf, offset 3961064Sschwartz * defines the offset within the specified space. 3971064Sschwartz * 3981064Sschwartz * This returns the physical address of the corresponding location. 3991064Sschwartz */ 4001064Sschwartz static uintptr_t 4011064Sschwartz pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 4021064Sschwartz { 4031064Sschwartz uint64_t range_base; 4041064Sschwartz int rval; 4052053Sschwartz pci_regspec_t dev_regspec; 4062053Sschwartz struct regspec xlated_regspec; 4071064Sschwartz dev_info_t *dip = px_p->px_dip; 4081064Sschwartz 4091064Sschwartz /* 4101064Sschwartz * Assume that requested entity is small enough to be on the same page. 4111064Sschwartz * PCItool checks alignment so that this will be true for single 4121064Sschwartz * accesses. 4131064Sschwartz */ 4142053Sschwartz dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT; 4152053Sschwartz if (space == PCI_CONFIG_SPACE) { 4162053Sschwartz dev_regspec.pci_phys_hi += 4172053Sschwartz (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M)); 4182053Sschwartz dev_regspec.pci_phys_low = offset & PCI_REG_REG_M; 4193625Segillett dev_regspec.pci_phys_mid = 0; /* Not used */ 4202053Sschwartz } else { 4212053Sschwartz dev_regspec.pci_phys_mid = offset >> 32; 4222053Sschwartz dev_regspec.pci_phys_low = offset & 0xffffffff; 4232053Sschwartz } 4242053Sschwartz dev_regspec.pci_size_hi = 0; /* Not used. */ 4252053Sschwartz 4262053Sschwartz /* Note: object is guaranteed to be within a page. */ 4272053Sschwartz dev_regspec.pci_size_low = 4; 4282053Sschwartz 4292053Sschwartz rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec); 4302053Sschwartz 4311064Sschwartz DBG(DBG_TOOLS, dip, 4322053Sschwartz "space:0x%d, offset:0x%" PRIx64 "\n", space, offset); 4331064Sschwartz 4341064Sschwartz if (rval != DDI_SUCCESS) 4351064Sschwartz return (NULL); 4362053Sschwartz 4372053Sschwartz /* Bustype here returns the high order address bits. */ 4382053Sschwartz xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p); 4392053Sschwartz 4402053Sschwartz range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) + 4412053Sschwartz xlated_regspec.regspec_addr; 4422053Sschwartz DBG(DBG_TOOLS, dip, 4432053Sschwartz "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n", 4442053Sschwartz xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr, 4452053Sschwartz xlated_regspec.regspec_size, range_base); 4462053Sschwartz 4472053Sschwartz return ((uintptr_t)range_base); 4481064Sschwartz } 4491064Sschwartz 4501064Sschwartz 451624Sschwartz static int 4521064Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 4531064Sschwartz uint32_t *space_p) 454624Sschwartz { 455624Sschwartz int rval; 4561064Sschwartz uint64_t off_in_space; 457624Sschwartz pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 458624Sschwartz dev_info_t *dip = px_p->px_dip; 459624Sschwartz 460624Sschwartz *space_p = PCI_MEM32_SPACE; 461624Sschwartz *bar_p = 0; 462624Sschwartz 463624Sschwartz /* 464624Sschwartz * Translate BAR number into offset of the BAR in 465624Sschwartz * the device's config space. 466624Sschwartz */ 467624Sschwartz cfg_prg.acc_attr = 468624Sschwartz PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 469624Sschwartz 4701064Sschwartz /* 4711064Sschwartz * Note: sun4u acc function uses phys_addr which includes offset. 4721064Sschwartz * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 4731064Sschwartz */ 4741064Sschwartz cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 4752053Sschwartz off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset; 4761064Sschwartz cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 4771064Sschwartz off_in_space); 4781064Sschwartz 4791064Sschwartz DBG(DBG_TOOLS, dip, 4801064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 4811064Sschwartz off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 482624Sschwartz 483624Sschwartz /* 484624Sschwartz * Get Bus Address Register (BAR) from config space. 485624Sschwartz * cfg_prg.offset is the offset into config space of the 486624Sschwartz * BAR desired. prg_p->status is modified on error. 487624Sschwartz */ 4881064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 489624Sschwartz 490624Sschwartz if (rval != SUCCESS) { 491624Sschwartz prg_p->status = cfg_prg.status; 492624Sschwartz return (rval); 493624Sschwartz } 494624Sschwartz 495624Sschwartz DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 496624Sschwartz 497624Sschwartz /* 498624Sschwartz * BAR has bits saying this space is IO space, unless 499624Sschwartz * this is the ROM address register. 500624Sschwartz */ 501624Sschwartz if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 502624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 503624Sschwartz *space_p = PCI_IO_SPACE; 504624Sschwartz *bar_p &= PCI_BASE_IO_ADDR_M; 505624Sschwartz 506624Sschwartz /* 507624Sschwartz * BAR has bits saying this space is 64 bit memory 508624Sschwartz * space, unless this is the ROM address register. 509624Sschwartz * 510624Sschwartz * The 64 bit address stored in two BAR cells is not 511624Sschwartz * necessarily aligned on an 8-byte boundary. 512624Sschwartz * Need to keep the first 4 bytes read, 513624Sschwartz * and do a separate read of the high 4 bytes. 514624Sschwartz */ 515624Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 516624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 517624Sschwartz 518624Sschwartz uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 519624Sschwartz 520624Sschwartz /* Don't try to read the next 4 bytes past the end of BARs. */ 521624Sschwartz if (cfg_prg.offset >= PCI_CONF_BASE5) { 522624Sschwartz prg_p->status = PCITOOL_OUT_OF_RANGE; 523624Sschwartz return (EIO); 524624Sschwartz } 525624Sschwartz 526624Sschwartz /* Access device. prg_p->status is modified on error. */ 527624Sschwartz cfg_prg.phys_addr += sizeof (uint32_t); 528624Sschwartz cfg_prg.offset += sizeof (uint32_t); 529624Sschwartz 5301064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 531624Sschwartz if (rval != SUCCESS) { 532624Sschwartz prg_p->status = cfg_prg.status; 533624Sschwartz return (rval); 534624Sschwartz } 535624Sschwartz 536624Sschwartz /* 537624Sschwartz * Honor the 64 bit BAR as such, only when the upper 32 bits 538624Sschwartz * store a non-zero value. 539624Sschwartz */ 540624Sschwartz if (*bar_p) { 541624Sschwartz *space_p = PCI_MEM64_SPACE; 542624Sschwartz *bar_p = (*bar_p << 32) | low_bytes; 543624Sschwartz } else 544624Sschwartz *bar_p = low_bytes; 545624Sschwartz 546624Sschwartz } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 547624Sschwartz 548624Sschwartz /* 549624Sschwartz * ROM enabled. Filter ROM enable bit from the BAR. 550624Sschwartz * Treat as Mem32 henceforth. 551624Sschwartz */ 552624Sschwartz if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 553624Sschwartz *bar_p ^= PCI_BASE_ROM_ENABLE; 554624Sschwartz 555624Sschwartz else { /* ROM disabled. */ 556624Sschwartz prg_p->status = PCITOOL_ROM_DISABLED; 557624Sschwartz return (EIO); 558624Sschwartz } 559624Sschwartz } 560624Sschwartz 561624Sschwartz /* Accept a bar of 0 only for IO space. */ 562624Sschwartz if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 563624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS; 564624Sschwartz return (EINVAL); 565624Sschwartz } 566624Sschwartz 567624Sschwartz return (SUCCESS); 568624Sschwartz } 569624Sschwartz 570624Sschwartz 571624Sschwartz /* Perform register accesses on PCI leaf devices. */ 572624Sschwartz int 573624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 574624Sschwartz { 575624Sschwartz pcitool_reg_t prg; 576624Sschwartz uint64_t bar; 577624Sschwartz uint32_t space; 5781064Sschwartz uint64_t off_in_space; 579624Sschwartz boolean_t write_flag = B_FALSE; 580624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 581624Sschwartz int rval = 0; 582624Sschwartz 583624Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG) 584624Sschwartz write_flag = B_TRUE; 585624Sschwartz 586624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 587624Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 588624Sschwartz mode) != DDI_SUCCESS) { 589624Sschwartz DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 590624Sschwartz return (EFAULT); 591624Sschwartz } 592624Sschwartz 593624Sschwartz if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 594624Sschwartz goto done_reg; 595624Sschwartz } 596624Sschwartz 597624Sschwartz DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 598624Sschwartz prg.bus_no, prg.dev_no, prg.func_no); 599624Sschwartz DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 600624Sschwartz prg.barnum, prg.offset, prg.acc_attr); 601624Sschwartz 602624Sschwartz if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 603624Sschwartz goto done_reg; 604624Sschwartz 605624Sschwartz if (prg.barnum == 0) { /* Proper config space desired. */ 606624Sschwartz 6071064Sschwartz /* Enforce offset limits. */ 6081064Sschwartz if (prg.offset >= DEV_CFG_SPACE_SIZE) { 6091064Sschwartz DBG(DBG_TOOLS, dip, 6101064Sschwartz "Config space offset 0x%" PRIx64 " out of range\n", 6111064Sschwartz prg.offset); 6121064Sschwartz prg.status = PCITOOL_OUT_OF_RANGE; 6131064Sschwartz rval = EINVAL; 6141064Sschwartz goto done_reg; 6151064Sschwartz } 6161064Sschwartz 6171064Sschwartz /* 6181064Sschwartz * For sun4v, config space base won't be known. 6191064Sschwartz * pxtool_get_phys_addr will return zero. 6201064Sschwartz * Note that for sun4v, phys_addr isn't 6211064Sschwartz * used for making config space accesses. 6221064Sschwartz * 6231064Sschwartz * For sun4u, assume that phys_addr will come back valid. 6241064Sschwartz */ 6252053Sschwartz /* 6262053Sschwartz * Accessed entity is assumed small enough to be on one page. 6272053Sschwartz * 6282053Sschwartz * Since config space is less than a page and is aligned to 6292053Sschwartz * 0x1000, a device's entire config space will be on a single 6302053Sschwartz * page. Pass the device's base config space address here, 6312053Sschwartz * then add the offset within that space later. This works 6322053Sschwartz * around an issue in px_xlate_reg (called by 6332053Sschwartz * pxtool_get_phys_addr) which accepts only a 256 byte 6342053Sschwartz * range within a device. 6352053Sschwartz */ 6362053Sschwartz off_in_space = PX_GET_BDF(&prg); 6372053Sschwartz prg.phys_addr = 6382053Sschwartz pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space); 6392053Sschwartz prg.phys_addr += prg.offset; 640624Sschwartz 641624Sschwartz DBG(DBG_TOOLS, dip, 6421064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 6431064Sschwartz "end:%s\n", off_in_space, prg.phys_addr, 644624Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 645624Sschwartz 646624Sschwartz /* 647624Sschwartz * Access device. pr.status is modified. 648624Sschwartz * BDF is assumed valid at this point. 649624Sschwartz */ 6501064Sschwartz rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 651624Sschwartz goto done_reg; 652624Sschwartz } 653624Sschwartz 654624Sschwartz /* IO/ MEM/ MEM64 space. */ 655624Sschwartz 6561064Sschwartz if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 657624Sschwartz goto done_reg; 658624Sschwartz 6591064Sschwartz switch (space) { 6601064Sschwartz case PCI_MEM32_SPACE: 661624Sschwartz 662624Sschwartz DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 663624Sschwartz 664624Sschwartz /* Can't write to ROM */ 665624Sschwartz if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 666624Sschwartz prg.status = PCITOOL_ROM_WRITE; 667624Sschwartz rval = EIO; 668624Sschwartz goto done_reg; 669624Sschwartz } 6701064Sschwartz break; 671624Sschwartz 6721064Sschwartz case PCI_IO_SPACE: 6731064Sschwartz DBG(DBG_TOOLS, dip, "IO space\n"); 6741064Sschwartz break; 6751064Sschwartz 6761064Sschwartz case PCI_MEM64_SPACE: 6771064Sschwartz DBG(DBG_TOOLS, dip, 6781064Sschwartz "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 6791064Sschwartz break; 6801064Sschwartz 6811064Sschwartz default: 6821064Sschwartz DBG(DBG_TOOLS, dip, "Unknown space!\n"); 6831064Sschwartz prg.status = PCITOOL_IO_ERROR; 6841064Sschwartz rval = EIO; 6851064Sschwartz goto done_reg; 686624Sschwartz } 687624Sschwartz 688624Sschwartz /* 6891064Sschwartz * Common code for all IO/MEM range spaces. 6901064Sschwartz * 691624Sschwartz * Use offset provided by caller to index into desired space. 692624Sschwartz * Note that prg.status is modified on error. 693624Sschwartz */ 6941064Sschwartz off_in_space = bar + prg.offset; 6951064Sschwartz prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 696624Sschwartz 6971064Sschwartz DBG(DBG_TOOLS, dip, 6981064Sschwartz "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 6991064Sschwartz "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 7001064Sschwartz 7011064Sschwartz rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 702624Sschwartz 703624Sschwartz done_reg: 704*4397Sschwartz prg.drvr_version = PCITOOL_VERSION; 705624Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 706624Sschwartz mode) != DDI_SUCCESS) { 707624Sschwartz DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 708624Sschwartz rval = EFAULT; 709624Sschwartz } 710624Sschwartz return (rval); 711624Sschwartz } 712624Sschwartz 713624Sschwartz 714624Sschwartz int 715624Sschwartz pxtool_init(dev_info_t *dip) 716624Sschwartz { 717624Sschwartz int instance = ddi_get_instance(dip); 718624Sschwartz 719624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 720624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 721624Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) { 722624Sschwartz return (DDI_FAILURE); 723624Sschwartz } 724624Sschwartz 725624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 726624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 727624Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 728624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 729624Sschwartz return (DDI_FAILURE); 730624Sschwartz } 731624Sschwartz 732624Sschwartz return (DDI_SUCCESS); 733624Sschwartz } 734624Sschwartz 735624Sschwartz 736624Sschwartz void 737624Sschwartz pxtool_uninit(dev_info_t *dip) 738624Sschwartz { 739624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 740624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 741624Sschwartz } 742