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 /* 22*10053SEvan.Yan@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23624Sschwartz * Use is subject to license terms. 24624Sschwartz */ 25624Sschwartz 26624Sschwartz #include <sys/types.h> 27624Sschwartz #include <sys/stat.h> 28624Sschwartz #include <sys/cpuvar.h> 29624Sschwartz #include <sys/kmem.h> 30624Sschwartz #include <sys/sunddi.h> 31624Sschwartz #include <sys/hotplug/pci/pcihp.h> 32624Sschwartz #include "px_obj.h" 33624Sschwartz #include <sys/pci_tools.h> 34777Sschwartz #include "px_tools_ext.h" 35624Sschwartz #include "px_tools_var.h" 36624Sschwartz 37624Sschwartz /* 38624Sschwartz * PCI Space definitions. 39624Sschwartz */ 40624Sschwartz #define PCI_CONFIG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 41624Sschwartz #define PCI_IO_SPACE (PCI_REG_ADDR_G(PCI_ADDR_IO)) 42624Sschwartz #define PCI_MEM32_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 43624Sschwartz #define PCI_MEM64_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 44624Sschwartz 45624Sschwartz /* 46624Sschwartz * Config space range for a device. IEEE 1275 spec defines for PCI. 47624Sschwartz * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA 48624Sschwartz */ 49624Sschwartz #define DEV_CFG_SPACE_SIZE \ 50624Sschwartz (1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA)) 51624Sschwartz 52624Sschwartz /* 53624Sschwartz * Offsets of BARS in config space. First entry of 0 means config space. 54624Sschwartz * Entries here correlate to pcitool_bars_t enumerated type. 55624Sschwartz */ 56624Sschwartz uint8_t pci_bars[] = { 57624Sschwartz 0x0, 58624Sschwartz PCI_CONF_BASE0, 59624Sschwartz PCI_CONF_BASE1, 60624Sschwartz PCI_CONF_BASE2, 61624Sschwartz PCI_CONF_BASE3, 62624Sschwartz PCI_CONF_BASE4, 63624Sschwartz PCI_CONF_BASE5, 64624Sschwartz PCI_CONF_ROM 65624Sschwartz }; 66624Sschwartz 67624Sschwartz int pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]); 68624Sschwartz 69624Sschwartz 704397Sschwartz /*ARGSUSED*/ 71624Sschwartz static int 724397Sschwartz pxtool_intr_info(dev_info_t *dip, void *arg, int mode) 73624Sschwartz { 74*10053SEvan.Yan@Sun.COM px_t *px_p = DIP_TO_STATE(dip); 75*10053SEvan.Yan@Sun.COM px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 764397Sschwartz pcitool_intr_info_t intr_info; 774397Sschwartz int rval = SUCCESS; 784397Sschwartz 794397Sschwartz /* If we need user_version, and to ret same user version as passed in */ 804397Sschwartz if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != 814397Sschwartz DDI_SUCCESS) { 82624Sschwartz return (EFAULT); 834397Sschwartz } 844397Sschwartz 854397Sschwartz intr_info.ctlr_version = 0; /* XXX how to get real version? */ 864397Sschwartz intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; 87*10053SEvan.Yan@Sun.COM if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI) 88*10053SEvan.Yan@Sun.COM intr_info.num_intr = msi_state_p->msi_cnt; 89*10053SEvan.Yan@Sun.COM else 90*10053SEvan.Yan@Sun.COM intr_info.num_intr = pxtool_num_inos; 914397Sschwartz 924397Sschwartz intr_info.drvr_version = PCITOOL_VERSION; 934397Sschwartz if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != 944397Sschwartz DDI_SUCCESS) { 954397Sschwartz rval = EFAULT; 964397Sschwartz } 974397Sschwartz 984397Sschwartz return (rval); 99624Sschwartz } 1004397Sschwartz 1014397Sschwartz 102624Sschwartz /* 103624Sschwartz * Get interrupt information for a given ino. 104624Sschwartz * Returns info only for inos mapped to devices. 105624Sschwartz * 106624Sschwartz * Returned info is valid only when iget.num_devs is returned > 0. 107624Sschwartz * If ino is not enabled or is not mapped to a device, 108624Sschwartz * iget.num_devs will be returned as = 0. 109624Sschwartz */ 110624Sschwartz /*ARGSUSED*/ 111624Sschwartz static int 112624Sschwartz pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 113624Sschwartz { 114624Sschwartz /* Array part isn't used here, but oh well... */ 115624Sschwartz pcitool_intr_get_t partial_iget; 116*10053SEvan.Yan@Sun.COM pcitool_intr_get_t *iget = &partial_iget; 117624Sschwartz int copyout_rval; 118624Sschwartz sysino_t sysino; 119624Sschwartz intr_valid_state_t intr_valid_state; 120624Sschwartz cpuid_t old_cpu_id; 121624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 122624Sschwartz size_t iget_kmem_alloc_size = 0; 123*10053SEvan.Yan@Sun.COM int rval = EIO; 124624Sschwartz 125624Sschwartz /* Read in just the header part, no array section. */ 126624Sschwartz if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 127624Sschwartz DDI_SUCCESS) 128624Sschwartz return (EFAULT); 129624Sschwartz 130*10053SEvan.Yan@Sun.COM iget->status = PCITOOL_IO_ERROR; 131*10053SEvan.Yan@Sun.COM 132*10053SEvan.Yan@Sun.COM if (iget->flags & PCITOOL_INTR_FLAG_GET_MSI) { 133*10053SEvan.Yan@Sun.COM px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 134*10053SEvan.Yan@Sun.COM pci_msi_valid_state_t msi_state; 135*10053SEvan.Yan@Sun.COM msiqid_t msiq_id; 136624Sschwartz 137*10053SEvan.Yan@Sun.COM if ((iget->msi < msi_state_p->msi_1st_msinum) || 138*10053SEvan.Yan@Sun.COM (iget->msi >= (msi_state_p->msi_1st_msinum + 139*10053SEvan.Yan@Sun.COM msi_state_p->msi_cnt))) { 140*10053SEvan.Yan@Sun.COM iget->status = PCITOOL_INVALID_MSI; 141*10053SEvan.Yan@Sun.COM rval = EINVAL; 142*10053SEvan.Yan@Sun.COM goto done_get_intr; 143*10053SEvan.Yan@Sun.COM } 144*10053SEvan.Yan@Sun.COM 145*10053SEvan.Yan@Sun.COM if ((px_lib_msi_getvalid(dip, iget->msi, 146*10053SEvan.Yan@Sun.COM &msi_state) != DDI_SUCCESS) || 147*10053SEvan.Yan@Sun.COM (msi_state != PCI_MSI_VALID)) 148*10053SEvan.Yan@Sun.COM goto done_get_intr; 149*10053SEvan.Yan@Sun.COM 150*10053SEvan.Yan@Sun.COM if (px_lib_msi_getmsiq(dip, iget->msi, 151*10053SEvan.Yan@Sun.COM &msiq_id) != DDI_SUCCESS) 152*10053SEvan.Yan@Sun.COM goto done_get_intr; 153*10053SEvan.Yan@Sun.COM 154*10053SEvan.Yan@Sun.COM iget->ino = px_msiqid_to_devino(px_p, msiq_id); 155*10053SEvan.Yan@Sun.COM } else { 156*10053SEvan.Yan@Sun.COM iget->msi = (uint32_t)-1; 157*10053SEvan.Yan@Sun.COM } 158624Sschwartz 159624Sschwartz /* Validate argument. */ 160*10053SEvan.Yan@Sun.COM if (iget->ino > pxtool_num_inos) { 161*10053SEvan.Yan@Sun.COM iget->status = PCITOOL_INVALID_INO; 162*10053SEvan.Yan@Sun.COM rval = EINVAL; 163624Sschwartz goto done_get_intr; 164624Sschwartz } 165624Sschwartz 166624Sschwartz /* Caller wants device information returned. */ 167*10053SEvan.Yan@Sun.COM if (iget->num_devs_ret > 0) { 168624Sschwartz /* 169624Sschwartz * Allocate room. 170624Sschwartz * Note if num_devs == 0 iget remains pointing to 171624Sschwartz * partial_iget. 172624Sschwartz */ 173*10053SEvan.Yan@Sun.COM iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget->num_devs_ret); 174*10053SEvan.Yan@Sun.COM iget = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP); 175624Sschwartz 176624Sschwartz /* Read in whole structure to verify there's room. */ 177624Sschwartz if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 178624Sschwartz SUCCESS) { 179624Sschwartz 180624Sschwartz /* Be consistent and just return EFAULT here. */ 181624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 182624Sschwartz 183624Sschwartz return (EFAULT); 184624Sschwartz } 185624Sschwartz } 186624Sschwartz 187624Sschwartz /* Convert leaf-wide intr to system-wide intr */ 188*10053SEvan.Yan@Sun.COM if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) != 189*10053SEvan.Yan@Sun.COM DDI_SUCCESS) { 190624Sschwartz iget->status = PCITOOL_IO_ERROR; 191624Sschwartz rval = EIO; 192624Sschwartz goto done_get_intr; 193624Sschwartz } 194624Sschwartz 195624Sschwartz /* Operate only on inos which are already enabled. */ 196*10053SEvan.Yan@Sun.COM if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) != 197*10053SEvan.Yan@Sun.COM DDI_SUCCESS) { 198624Sschwartz iget->status = PCITOOL_IO_ERROR; 199624Sschwartz rval = EIO; 200624Sschwartz goto done_get_intr; 201624Sschwartz } 202624Sschwartz 203624Sschwartz /* 204624Sschwartz * Consider all valid inos: those mapped to the root complex itself 205624Sschwartz * as well as those mapped to devices. 206624Sschwartz */ 207624Sschwartz if (intr_valid_state == INTR_VALID) { 208624Sschwartz /* 2092973Sgovinda * The following looks up the px_ino and returns 210624Sschwartz * info of devices mapped to this ino. 211624Sschwartz */ 212*10053SEvan.Yan@Sun.COM iget->num_devs = pxtool_ib_get_ino_devs(px_p, iget->ino, 213*10053SEvan.Yan@Sun.COM iget->msi, &iget->num_devs_ret, iget->dev); 214624Sschwartz 215*10053SEvan.Yan@Sun.COM if (px_ib_get_intr_target(px_p, iget->ino, 216*10053SEvan.Yan@Sun.COM &old_cpu_id) != DDI_SUCCESS) { 217624Sschwartz iget->status = PCITOOL_IO_ERROR; 218624Sschwartz rval = EIO; 219624Sschwartz goto done_get_intr; 220624Sschwartz } 221*10053SEvan.Yan@Sun.COM 222624Sschwartz iget->cpu_id = old_cpu_id; 223624Sschwartz } 224624Sschwartz 225624Sschwartz iget->status = PCITOOL_SUCCESS; 226624Sschwartz rval = SUCCESS; 227624Sschwartz 228624Sschwartz done_get_intr: 2294397Sschwartz iget->drvr_version = PCITOOL_VERSION; 230624Sschwartz copyout_rval = 231*10053SEvan.Yan@Sun.COM ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(iget->num_devs_ret), mode); 232624Sschwartz 233624Sschwartz if (iget_kmem_alloc_size > 0) 234624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 235624Sschwartz 236624Sschwartz if (copyout_rval != DDI_SUCCESS) 237624Sschwartz rval = EFAULT; 238624Sschwartz 239624Sschwartz return (rval); 240624Sschwartz } 241624Sschwartz 242624Sschwartz 243624Sschwartz /* 244624Sschwartz * Associate a new CPU with a given ino. 245624Sschwartz * 246624Sschwartz * Operate only on inos which are already mapped to devices. 247624Sschwartz */ 248624Sschwartz static int 249624Sschwartz pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 250624Sschwartz { 251624Sschwartz pcitool_intr_set_t iset; 252624Sschwartz cpuid_t old_cpu_id; 253624Sschwartz sysino_t sysino; 254*10053SEvan.Yan@Sun.COM intr_valid_state_t intr_valid_state; 255624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 256*10053SEvan.Yan@Sun.COM msiqid_t msiq_id; 257*10053SEvan.Yan@Sun.COM int rval = EIO; 258*10053SEvan.Yan@Sun.COM int ret = DDI_SUCCESS; 2594397Sschwartz size_t copyinout_size; 260624Sschwartz 2614397Sschwartz bzero(&iset, sizeof (pcitool_intr_set_t)); 2624397Sschwartz 2634397Sschwartz /* Version 1 of pcitool_intr_set_t doesn't have flags. */ 2644397Sschwartz copyinout_size = (size_t)&iset.flags - (size_t)&iset; 2654397Sschwartz 2664397Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 267624Sschwartz return (EFAULT); 268624Sschwartz 2694397Sschwartz switch (iset.user_version) { 2704397Sschwartz case PCITOOL_V1: 2714397Sschwartz break; 2724397Sschwartz 2734397Sschwartz case PCITOOL_V2: 2744397Sschwartz copyinout_size = sizeof (pcitool_intr_set_t); 2754397Sschwartz if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) 2764397Sschwartz return (EFAULT); 2774397Sschwartz break; 2784397Sschwartz 2794397Sschwartz default: 2804397Sschwartz iset.status = PCITOOL_OUT_OF_RANGE; 2814397Sschwartz rval = ENOTSUP; 2824397Sschwartz goto done_set_intr; 2834397Sschwartz } 2844397Sschwartz 285*10053SEvan.Yan@Sun.COM if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) { 2864397Sschwartz iset.status = PCITOOL_IO_ERROR; 2874397Sschwartz rval = ENOTSUP; 2884397Sschwartz goto done_set_intr; 2894397Sschwartz } 2904397Sschwartz 291*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_IO_ERROR; 292*10053SEvan.Yan@Sun.COM 293*10053SEvan.Yan@Sun.COM if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) { 294*10053SEvan.Yan@Sun.COM px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 295*10053SEvan.Yan@Sun.COM pci_msi_valid_state_t msi_state; 296*10053SEvan.Yan@Sun.COM 297*10053SEvan.Yan@Sun.COM if ((iset.msi < msi_state_p->msi_1st_msinum) || 298*10053SEvan.Yan@Sun.COM (iset.msi >= (msi_state_p->msi_1st_msinum + 299*10053SEvan.Yan@Sun.COM msi_state_p->msi_cnt))) { 300*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_INVALID_MSI; 301*10053SEvan.Yan@Sun.COM rval = EINVAL; 302*10053SEvan.Yan@Sun.COM goto done_set_intr; 303*10053SEvan.Yan@Sun.COM } 304*10053SEvan.Yan@Sun.COM 305*10053SEvan.Yan@Sun.COM if ((px_lib_msi_getvalid(dip, iset.msi, 306*10053SEvan.Yan@Sun.COM &msi_state) != DDI_SUCCESS) || 307*10053SEvan.Yan@Sun.COM (msi_state != PCI_MSI_VALID)) 308*10053SEvan.Yan@Sun.COM goto done_set_intr; 309*10053SEvan.Yan@Sun.COM 310*10053SEvan.Yan@Sun.COM if (px_lib_msi_getmsiq(dip, iset.msi, 311*10053SEvan.Yan@Sun.COM &msiq_id) != DDI_SUCCESS) 312*10053SEvan.Yan@Sun.COM goto done_set_intr; 313*10053SEvan.Yan@Sun.COM 314*10053SEvan.Yan@Sun.COM iset.ino = px_msiqid_to_devino(px_p, msiq_id); 315*10053SEvan.Yan@Sun.COM } else { 316*10053SEvan.Yan@Sun.COM iset.msi = (uint32_t)-1; 317*10053SEvan.Yan@Sun.COM } 318624Sschwartz 319624Sschwartz /* Validate input argument. */ 320*10053SEvan.Yan@Sun.COM if (iset.ino > pxtool_num_inos) { 321*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_INVALID_INO; 322*10053SEvan.Yan@Sun.COM rval = EINVAL; 323*10053SEvan.Yan@Sun.COM goto done_set_intr; 324*10053SEvan.Yan@Sun.COM } 325*10053SEvan.Yan@Sun.COM 326*10053SEvan.Yan@Sun.COM /* Convert leaf-wide intr to system-wide intr */ 327*10053SEvan.Yan@Sun.COM if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) != 328*10053SEvan.Yan@Sun.COM DDI_SUCCESS) 329624Sschwartz goto done_set_intr; 330624Sschwartz 331*10053SEvan.Yan@Sun.COM /* Operate only on inos which are already enabled. */ 332*10053SEvan.Yan@Sun.COM if ((px_lib_intr_getvalid(dip, sysino, &intr_valid_state) != 333*10053SEvan.Yan@Sun.COM DDI_SUCCESS) || (intr_valid_state == INTR_NOTVALID)) 334624Sschwartz goto done_set_intr; 335624Sschwartz 336624Sschwartz /* 337*10053SEvan.Yan@Sun.COM * Consider all valid inos: those mapped to the root complex itself 338*10053SEvan.Yan@Sun.COM * as well as those mapped to devices. 339624Sschwartz */ 340*10053SEvan.Yan@Sun.COM if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) 341*10053SEvan.Yan@Sun.COM goto done_set_intr; 342624Sschwartz 343*10053SEvan.Yan@Sun.COM if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) { 344*10053SEvan.Yan@Sun.COM ddi_intr_handle_impl_t hdle; 345624Sschwartz 346*10053SEvan.Yan@Sun.COM bzero(&hdle, sizeof (ddi_intr_handle_impl_t)); 347*10053SEvan.Yan@Sun.COM if (pxtool_ib_get_msi_info(px_p, iset.ino, iset.msi, 348*10053SEvan.Yan@Sun.COM &hdle) != DDI_SUCCESS) { 349*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_INVALID_MSI; 350*10053SEvan.Yan@Sun.COM rval = EINVAL; 351624Sschwartz goto done_set_intr; 352*10053SEvan.Yan@Sun.COM } 353624Sschwartz 354*10053SEvan.Yan@Sun.COM if ((ret = px_ib_set_msix_target(px_p, &hdle, iset.msi, 355*10053SEvan.Yan@Sun.COM iset.cpu_id)) == DDI_SUCCESS) { 356*10053SEvan.Yan@Sun.COM (void) px_lib_msi_getmsiq(dip, iset.msi, &msiq_id); 357*10053SEvan.Yan@Sun.COM iset.ino = px_msiqid_to_devino(px_p, msiq_id); 358*10053SEvan.Yan@Sun.COM iset.cpu_id = old_cpu_id; 359*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_SUCCESS; 360*10053SEvan.Yan@Sun.COM rval = SUCCESS; 361*10053SEvan.Yan@Sun.COM goto done_set_intr; 362*10053SEvan.Yan@Sun.COM } 363*10053SEvan.Yan@Sun.COM } else { 364*10053SEvan.Yan@Sun.COM if ((ret = px_ib_set_intr_target(px_p, iset.ino, 365*10053SEvan.Yan@Sun.COM iset.cpu_id)) == DDI_SUCCESS) { 366*10053SEvan.Yan@Sun.COM iset.cpu_id = old_cpu_id; 367*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_SUCCESS; 368*10053SEvan.Yan@Sun.COM rval = SUCCESS; 369*10053SEvan.Yan@Sun.COM goto done_set_intr; 370*10053SEvan.Yan@Sun.COM } 371*10053SEvan.Yan@Sun.COM } 372624Sschwartz 373*10053SEvan.Yan@Sun.COM switch (ret) { 374*10053SEvan.Yan@Sun.COM case DDI_EPENDING: 375*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_PENDING_INTRTIMEOUT; 376*10053SEvan.Yan@Sun.COM rval = ETIME; 377*10053SEvan.Yan@Sun.COM break; 378*10053SEvan.Yan@Sun.COM case DDI_EINVAL: 379624Sschwartz iset.status = PCITOOL_INVALID_CPUID; 380624Sschwartz rval = EINVAL; 381*10053SEvan.Yan@Sun.COM break; 382*10053SEvan.Yan@Sun.COM default: 383*10053SEvan.Yan@Sun.COM iset.status = PCITOOL_IO_ERROR; 384*10053SEvan.Yan@Sun.COM rval = EIO; 385*10053SEvan.Yan@Sun.COM break; 386624Sschwartz } 387624Sschwartz 388624Sschwartz done_set_intr: 3894397Sschwartz iset.drvr_version = PCITOOL_VERSION; 3904397Sschwartz if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) 391624Sschwartz rval = EFAULT; 392624Sschwartz 393624Sschwartz return (rval); 394624Sschwartz } 395624Sschwartz 396624Sschwartz 397624Sschwartz /* Main function for handling interrupt CPU binding requests and queries. */ 398624Sschwartz int 399624Sschwartz pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 400624Sschwartz { 401624Sschwartz int rval = SUCCESS; 402624Sschwartz 403624Sschwartz switch (cmd) { 404624Sschwartz 4054397Sschwartz /* Get system interrupt information. */ 4064397Sschwartz case PCITOOL_SYSTEM_INTR_INFO: 4074397Sschwartz rval = pxtool_intr_info(dip, arg, mode); 408624Sschwartz break; 409624Sschwartz 410624Sschwartz /* Get interrupt information for a given ino. */ 411624Sschwartz case PCITOOL_DEVICE_GET_INTR: 412624Sschwartz rval = pxtool_get_intr(dip, arg, mode); 413624Sschwartz break; 414624Sschwartz 415624Sschwartz /* Associate a new CPU with a given ino. */ 416624Sschwartz case PCITOOL_DEVICE_SET_INTR: 417624Sschwartz rval = pxtool_set_intr(dip, arg, mode); 418624Sschwartz break; 419624Sschwartz 420624Sschwartz default: 421624Sschwartz rval = ENOTTY; 422624Sschwartz } 423624Sschwartz 424624Sschwartz return (rval); 425624Sschwartz } 426624Sschwartz 427624Sschwartz 428624Sschwartz static int 429624Sschwartz pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 430624Sschwartz { 431624Sschwartz int rval = SUCCESS; 432624Sschwartz 433624Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 434624Sschwartz prg->status = PCITOOL_OUT_OF_RANGE; 435624Sschwartz rval = EINVAL; 436624Sschwartz 437624Sschwartz /* Validate address arguments of bus / dev / func */ 438624Sschwartz } else if (((prg->bus_no & 439624Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 440624Sschwartz ((prg->dev_no & 441624Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 442624Sschwartz ((prg->func_no & 443624Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 444624Sschwartz prg->status = PCITOOL_INVALID_ADDRESS; 445624Sschwartz rval = EINVAL; 446624Sschwartz } 447624Sschwartz 448624Sschwartz return (rval); 449624Sschwartz } 450624Sschwartz 4511064Sschwartz /* 4521064Sschwartz * px_p defines which leaf, space defines which space in that leaf, offset 4531064Sschwartz * defines the offset within the specified space. 4541064Sschwartz * 4551064Sschwartz * This returns the physical address of the corresponding location. 4561064Sschwartz */ 4571064Sschwartz static uintptr_t 4581064Sschwartz pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 4591064Sschwartz { 4601064Sschwartz uint64_t range_base; 4611064Sschwartz int rval; 4622053Sschwartz pci_regspec_t dev_regspec; 4632053Sschwartz struct regspec xlated_regspec; 4641064Sschwartz dev_info_t *dip = px_p->px_dip; 4651064Sschwartz 4661064Sschwartz /* 4671064Sschwartz * Assume that requested entity is small enough to be on the same page. 4681064Sschwartz * PCItool checks alignment so that this will be true for single 4691064Sschwartz * accesses. 4701064Sschwartz */ 4712053Sschwartz dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT; 4722053Sschwartz if (space == PCI_CONFIG_SPACE) { 4732053Sschwartz dev_regspec.pci_phys_hi += 4742053Sschwartz (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M)); 4752053Sschwartz dev_regspec.pci_phys_low = offset & PCI_REG_REG_M; 4763625Segillett dev_regspec.pci_phys_mid = 0; /* Not used */ 4772053Sschwartz } else { 4782053Sschwartz dev_regspec.pci_phys_mid = offset >> 32; 4792053Sschwartz dev_regspec.pci_phys_low = offset & 0xffffffff; 4802053Sschwartz } 4812053Sschwartz dev_regspec.pci_size_hi = 0; /* Not used. */ 4822053Sschwartz 4832053Sschwartz /* Note: object is guaranteed to be within a page. */ 4842053Sschwartz dev_regspec.pci_size_low = 4; 4852053Sschwartz 4862053Sschwartz rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec); 4872053Sschwartz 4881064Sschwartz DBG(DBG_TOOLS, dip, 4892053Sschwartz "space:0x%d, offset:0x%" PRIx64 "\n", space, offset); 4901064Sschwartz 4911064Sschwartz if (rval != DDI_SUCCESS) 4921064Sschwartz return (NULL); 4932053Sschwartz 4942053Sschwartz /* Bustype here returns the high order address bits. */ 4952053Sschwartz xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p); 4962053Sschwartz 4972053Sschwartz range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) + 4982053Sschwartz xlated_regspec.regspec_addr; 4992053Sschwartz DBG(DBG_TOOLS, dip, 5002053Sschwartz "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n", 5012053Sschwartz xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr, 5022053Sschwartz xlated_regspec.regspec_size, range_base); 5032053Sschwartz 5042053Sschwartz return ((uintptr_t)range_base); 5051064Sschwartz } 5061064Sschwartz 5071064Sschwartz 508624Sschwartz static int 5091064Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 5101064Sschwartz uint32_t *space_p) 511624Sschwartz { 512624Sschwartz int rval; 5131064Sschwartz uint64_t off_in_space; 514624Sschwartz pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 515624Sschwartz dev_info_t *dip = px_p->px_dip; 516624Sschwartz 517624Sschwartz *space_p = PCI_MEM32_SPACE; 518624Sschwartz *bar_p = 0; 519624Sschwartz 520624Sschwartz /* 521624Sschwartz * Translate BAR number into offset of the BAR in 522624Sschwartz * the device's config space. 523624Sschwartz */ 524624Sschwartz cfg_prg.acc_attr = 525624Sschwartz PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 526624Sschwartz 5271064Sschwartz /* 5281064Sschwartz * Note: sun4u acc function uses phys_addr which includes offset. 5291064Sschwartz * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 5301064Sschwartz */ 5311064Sschwartz cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 5322053Sschwartz off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset; 5331064Sschwartz cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 5341064Sschwartz off_in_space); 5351064Sschwartz 5361064Sschwartz DBG(DBG_TOOLS, dip, 5371064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 5381064Sschwartz off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 539624Sschwartz 540624Sschwartz /* 541624Sschwartz * Get Bus Address Register (BAR) from config space. 542624Sschwartz * cfg_prg.offset is the offset into config space of the 543624Sschwartz * BAR desired. prg_p->status is modified on error. 544624Sschwartz */ 5451064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 546624Sschwartz 547624Sschwartz if (rval != SUCCESS) { 548624Sschwartz prg_p->status = cfg_prg.status; 549624Sschwartz return (rval); 550624Sschwartz } 551624Sschwartz 552624Sschwartz DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 553624Sschwartz 554624Sschwartz /* 555624Sschwartz * BAR has bits saying this space is IO space, unless 556624Sschwartz * this is the ROM address register. 557624Sschwartz */ 558624Sschwartz if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 559624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 560624Sschwartz *space_p = PCI_IO_SPACE; 561624Sschwartz *bar_p &= PCI_BASE_IO_ADDR_M; 562624Sschwartz 563624Sschwartz /* 564624Sschwartz * BAR has bits saying this space is 64 bit memory 565624Sschwartz * space, unless this is the ROM address register. 566624Sschwartz * 567624Sschwartz * The 64 bit address stored in two BAR cells is not 568624Sschwartz * necessarily aligned on an 8-byte boundary. 569624Sschwartz * Need to keep the first 4 bytes read, 570624Sschwartz * and do a separate read of the high 4 bytes. 571624Sschwartz */ 572624Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 573624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 574624Sschwartz 575624Sschwartz uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 576624Sschwartz 577624Sschwartz /* Don't try to read the next 4 bytes past the end of BARs. */ 578624Sschwartz if (cfg_prg.offset >= PCI_CONF_BASE5) { 579624Sschwartz prg_p->status = PCITOOL_OUT_OF_RANGE; 580624Sschwartz return (EIO); 581624Sschwartz } 582624Sschwartz 583624Sschwartz /* Access device. prg_p->status is modified on error. */ 584624Sschwartz cfg_prg.phys_addr += sizeof (uint32_t); 585624Sschwartz cfg_prg.offset += sizeof (uint32_t); 586624Sschwartz 5871064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 588624Sschwartz if (rval != SUCCESS) { 589624Sschwartz prg_p->status = cfg_prg.status; 590624Sschwartz return (rval); 591624Sschwartz } 592624Sschwartz 593624Sschwartz /* 594624Sschwartz * Honor the 64 bit BAR as such, only when the upper 32 bits 595624Sschwartz * store a non-zero value. 596624Sschwartz */ 597624Sschwartz if (*bar_p) { 598624Sschwartz *space_p = PCI_MEM64_SPACE; 599624Sschwartz *bar_p = (*bar_p << 32) | low_bytes; 600624Sschwartz } else 601624Sschwartz *bar_p = low_bytes; 602624Sschwartz 603624Sschwartz } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 604624Sschwartz 605624Sschwartz /* 606624Sschwartz * ROM enabled. Filter ROM enable bit from the BAR. 607624Sschwartz * Treat as Mem32 henceforth. 608624Sschwartz */ 609624Sschwartz if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 610624Sschwartz *bar_p ^= PCI_BASE_ROM_ENABLE; 611624Sschwartz 612624Sschwartz else { /* ROM disabled. */ 613624Sschwartz prg_p->status = PCITOOL_ROM_DISABLED; 614624Sschwartz return (EIO); 615624Sschwartz } 616624Sschwartz } 617624Sschwartz 618624Sschwartz /* Accept a bar of 0 only for IO space. */ 619624Sschwartz if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 620624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS; 621624Sschwartz return (EINVAL); 622624Sschwartz } 623624Sschwartz 624624Sschwartz return (SUCCESS); 625624Sschwartz } 626624Sschwartz 627624Sschwartz 628624Sschwartz /* Perform register accesses on PCI leaf devices. */ 629624Sschwartz int 630624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 631624Sschwartz { 632624Sschwartz pcitool_reg_t prg; 633624Sschwartz uint64_t bar; 634624Sschwartz uint32_t space; 6351064Sschwartz uint64_t off_in_space; 636624Sschwartz boolean_t write_flag = B_FALSE; 637624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 638624Sschwartz int rval = 0; 639624Sschwartz 640624Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG) 641624Sschwartz write_flag = B_TRUE; 642624Sschwartz 643624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 644624Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 645624Sschwartz mode) != DDI_SUCCESS) { 646624Sschwartz DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 647624Sschwartz return (EFAULT); 648624Sschwartz } 649624Sschwartz 650624Sschwartz if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 651624Sschwartz goto done_reg; 652624Sschwartz } 653624Sschwartz 654624Sschwartz DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 655624Sschwartz prg.bus_no, prg.dev_no, prg.func_no); 656624Sschwartz DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 657624Sschwartz prg.barnum, prg.offset, prg.acc_attr); 658624Sschwartz 659624Sschwartz if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 660624Sschwartz goto done_reg; 661624Sschwartz 662624Sschwartz if (prg.barnum == 0) { /* Proper config space desired. */ 663624Sschwartz 6641064Sschwartz /* Enforce offset limits. */ 6651064Sschwartz if (prg.offset >= DEV_CFG_SPACE_SIZE) { 6661064Sschwartz DBG(DBG_TOOLS, dip, 6671064Sschwartz "Config space offset 0x%" PRIx64 " out of range\n", 6681064Sschwartz prg.offset); 6691064Sschwartz prg.status = PCITOOL_OUT_OF_RANGE; 6701064Sschwartz rval = EINVAL; 6711064Sschwartz goto done_reg; 6721064Sschwartz } 6731064Sschwartz 6741064Sschwartz /* 6751064Sschwartz * For sun4v, config space base won't be known. 6761064Sschwartz * pxtool_get_phys_addr will return zero. 6771064Sschwartz * Note that for sun4v, phys_addr isn't 6781064Sschwartz * used for making config space accesses. 6791064Sschwartz * 6801064Sschwartz * For sun4u, assume that phys_addr will come back valid. 6811064Sschwartz */ 6822053Sschwartz /* 6832053Sschwartz * Accessed entity is assumed small enough to be on one page. 6842053Sschwartz * 6852053Sschwartz * Since config space is less than a page and is aligned to 6862053Sschwartz * 0x1000, a device's entire config space will be on a single 6872053Sschwartz * page. Pass the device's base config space address here, 6882053Sschwartz * then add the offset within that space later. This works 6892053Sschwartz * around an issue in px_xlate_reg (called by 6902053Sschwartz * pxtool_get_phys_addr) which accepts only a 256 byte 6912053Sschwartz * range within a device. 6922053Sschwartz */ 6932053Sschwartz off_in_space = PX_GET_BDF(&prg); 6942053Sschwartz prg.phys_addr = 6952053Sschwartz pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space); 6962053Sschwartz prg.phys_addr += prg.offset; 697624Sschwartz 698624Sschwartz DBG(DBG_TOOLS, dip, 6991064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 7001064Sschwartz "end:%s\n", off_in_space, prg.phys_addr, 701624Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 702624Sschwartz 703624Sschwartz /* 704624Sschwartz * Access device. pr.status is modified. 705624Sschwartz * BDF is assumed valid at this point. 706624Sschwartz */ 7071064Sschwartz rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 708624Sschwartz goto done_reg; 709624Sschwartz } 710624Sschwartz 711624Sschwartz /* IO/ MEM/ MEM64 space. */ 712624Sschwartz 7131064Sschwartz if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 714624Sschwartz goto done_reg; 715624Sschwartz 7161064Sschwartz switch (space) { 7171064Sschwartz case PCI_MEM32_SPACE: 718624Sschwartz 719624Sschwartz DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 720624Sschwartz 721624Sschwartz /* Can't write to ROM */ 722624Sschwartz if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 723624Sschwartz prg.status = PCITOOL_ROM_WRITE; 724624Sschwartz rval = EIO; 725624Sschwartz goto done_reg; 726624Sschwartz } 7271064Sschwartz break; 728624Sschwartz 7291064Sschwartz case PCI_IO_SPACE: 7301064Sschwartz DBG(DBG_TOOLS, dip, "IO space\n"); 7311064Sschwartz break; 7321064Sschwartz 7331064Sschwartz case PCI_MEM64_SPACE: 7341064Sschwartz DBG(DBG_TOOLS, dip, 7351064Sschwartz "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 7361064Sschwartz break; 7371064Sschwartz 7381064Sschwartz default: 7391064Sschwartz DBG(DBG_TOOLS, dip, "Unknown space!\n"); 7401064Sschwartz prg.status = PCITOOL_IO_ERROR; 7411064Sschwartz rval = EIO; 7421064Sschwartz goto done_reg; 743624Sschwartz } 744624Sschwartz 745624Sschwartz /* 7461064Sschwartz * Common code for all IO/MEM range spaces. 7471064Sschwartz * 748624Sschwartz * Use offset provided by caller to index into desired space. 749624Sschwartz * Note that prg.status is modified on error. 750624Sschwartz */ 7511064Sschwartz off_in_space = bar + prg.offset; 7521064Sschwartz prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 753624Sschwartz 7541064Sschwartz DBG(DBG_TOOLS, dip, 7551064Sschwartz "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 7561064Sschwartz "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 7571064Sschwartz 7581064Sschwartz rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 759624Sschwartz 760624Sschwartz done_reg: 7614397Sschwartz prg.drvr_version = PCITOOL_VERSION; 762624Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 763624Sschwartz mode) != DDI_SUCCESS) { 764624Sschwartz DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 765624Sschwartz rval = EFAULT; 766624Sschwartz } 767624Sschwartz return (rval); 768624Sschwartz } 769624Sschwartz 770624Sschwartz 771624Sschwartz int 772624Sschwartz pxtool_init(dev_info_t *dip) 773624Sschwartz { 774624Sschwartz int instance = ddi_get_instance(dip); 775624Sschwartz 776624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 777624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 778624Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) { 779624Sschwartz return (DDI_FAILURE); 780624Sschwartz } 781624Sschwartz 782624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 783624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 784624Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 785624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 786624Sschwartz return (DDI_FAILURE); 787624Sschwartz } 788624Sschwartz 789624Sschwartz return (DDI_SUCCESS); 790624Sschwartz } 791624Sschwartz 792624Sschwartz 793624Sschwartz void 794624Sschwartz pxtool_uninit(dev_info_t *dip) 795624Sschwartz { 796624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 797624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 798624Sschwartz } 799