1*624Sschwartz /* 2*624Sschwartz * CDDL HEADER START 3*624Sschwartz * 4*624Sschwartz * The contents of this file are subject to the terms of the 5*624Sschwartz * Common Development and Distribution License, Version 1.0 only 6*624Sschwartz * (the "License"). You may not use this file except in compliance 7*624Sschwartz * with the License. 8*624Sschwartz * 9*624Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*624Sschwartz * or http://www.opensolaris.org/os/licensing. 11*624Sschwartz * See the License for the specific language governing permissions 12*624Sschwartz * and limitations under the License. 13*624Sschwartz * 14*624Sschwartz * When distributing Covered Code, include this CDDL HEADER in each 15*624Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*624Sschwartz * If applicable, add the following below this CDDL HEADER, with the 17*624Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying 18*624Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner] 19*624Sschwartz * 20*624Sschwartz * CDDL HEADER END 21*624Sschwartz */ 22*624Sschwartz /* 23*624Sschwartz * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*624Sschwartz * Use is subject to license terms. 25*624Sschwartz */ 26*624Sschwartz 27*624Sschwartz #pragma ident "%Z%%M% %I% %E% SMI" 28*624Sschwartz 29*624Sschwartz #include <sys/types.h> 30*624Sschwartz #include <sys/stat.h> 31*624Sschwartz #include <sys/cpuvar.h> 32*624Sschwartz #include <sys/kmem.h> 33*624Sschwartz #include <sys/sunddi.h> 34*624Sschwartz #include <sys/hotplug/pci/pcihp.h> 35*624Sschwartz #include "px_obj.h" 36*624Sschwartz #include <sys/pci_tools.h> 37*624Sschwartz #include "px_tools.h" 38*624Sschwartz #include "px_tools_var.h" 39*624Sschwartz 40*624Sschwartz /* 41*624Sschwartz * PCI Space definitions. 42*624Sschwartz */ 43*624Sschwartz #define PCI_CONFIG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 44*624Sschwartz #define PCI_IO_SPACE (PCI_REG_ADDR_G(PCI_ADDR_IO)) 45*624Sschwartz #define PCI_MEM32_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM32)) 46*624Sschwartz #define PCI_MEM64_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM64)) 47*624Sschwartz 48*624Sschwartz /* 49*624Sschwartz * Config space range for a device. IEEE 1275 spec defines for PCI. 50*624Sschwartz * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA 51*624Sschwartz */ 52*624Sschwartz #define DEV_CFG_SPACE_SIZE \ 53*624Sschwartz (1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA)) 54*624Sschwartz 55*624Sschwartz /* 56*624Sschwartz * Offsets of BARS in config space. First entry of 0 means config space. 57*624Sschwartz * Entries here correlate to pcitool_bars_t enumerated type. 58*624Sschwartz */ 59*624Sschwartz uint8_t pci_bars[] = { 60*624Sschwartz 0x0, 61*624Sschwartz PCI_CONF_BASE0, 62*624Sschwartz PCI_CONF_BASE1, 63*624Sschwartz PCI_CONF_BASE2, 64*624Sschwartz PCI_CONF_BASE3, 65*624Sschwartz PCI_CONF_BASE4, 66*624Sschwartz PCI_CONF_BASE5, 67*624Sschwartz PCI_CONF_ROM 68*624Sschwartz }; 69*624Sschwartz 70*624Sschwartz int pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]); 71*624Sschwartz 72*624Sschwartz /* 73*624Sschwartz * Validate the cpu_id passed in. 74*624Sschwartz * A value of 1 will be returned for success and zero for failure. 75*624Sschwartz */ 76*624Sschwartz static int 77*624Sschwartz pxtool_validate_cpuid(uint32_t cpuid) 78*624Sschwartz { 79*624Sschwartz extern const int _ncpu; 80*624Sschwartz extern cpu_t *cpu[]; 81*624Sschwartz 82*624Sschwartz ASSERT(mutex_owned(&cpu_lock)); 83*624Sschwartz 84*624Sschwartz return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid]))); 85*624Sschwartz } 86*624Sschwartz 87*624Sschwartz 88*624Sschwartz static int 89*624Sschwartz pxtool_intr_get_max_ino(uint32_t *arg, int mode) 90*624Sschwartz { 91*624Sschwartz if (ddi_copyout(&pxtool_num_inos, arg, sizeof (uint32_t), mode) != 92*624Sschwartz DDI_SUCCESS) 93*624Sschwartz return (EFAULT); 94*624Sschwartz else 95*624Sschwartz return (SUCCESS); 96*624Sschwartz } 97*624Sschwartz /* 98*624Sschwartz * Get interrupt information for a given ino. 99*624Sschwartz * Returns info only for inos mapped to devices. 100*624Sschwartz * 101*624Sschwartz * Returned info is valid only when iget.num_devs is returned > 0. 102*624Sschwartz * If ino is not enabled or is not mapped to a device, 103*624Sschwartz * iget.num_devs will be returned as = 0. 104*624Sschwartz */ 105*624Sschwartz /*ARGSUSED*/ 106*624Sschwartz static int 107*624Sschwartz pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 108*624Sschwartz { 109*624Sschwartz /* Array part isn't used here, but oh well... */ 110*624Sschwartz pcitool_intr_get_t partial_iget; 111*624Sschwartz uint32_t ino; 112*624Sschwartz uint8_t num_devs_ret; 113*624Sschwartz int copyout_rval; 114*624Sschwartz sysino_t sysino; 115*624Sschwartz intr_valid_state_t intr_valid_state; 116*624Sschwartz cpuid_t old_cpu_id; 117*624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 118*624Sschwartz pcitool_intr_get_t *iget = &partial_iget; 119*624Sschwartz size_t iget_kmem_alloc_size = 0; 120*624Sschwartz int rval = SUCCESS; 121*624Sschwartz 122*624Sschwartz /* Read in just the header part, no array section. */ 123*624Sschwartz if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 124*624Sschwartz DDI_SUCCESS) 125*624Sschwartz return (EFAULT); 126*624Sschwartz 127*624Sschwartz ino = partial_iget.ino; 128*624Sschwartz num_devs_ret = partial_iget.num_devs_ret; 129*624Sschwartz 130*624Sschwartz partial_iget.num_devs_ret = 0; /* Assume error for now. */ 131*624Sschwartz partial_iget.status = PCITOOL_INVALID_INO; 132*624Sschwartz rval = EINVAL; 133*624Sschwartz 134*624Sschwartz /* Validate argument. */ 135*624Sschwartz if (partial_iget.ino > pxtool_num_inos) { 136*624Sschwartz goto done_get_intr; 137*624Sschwartz } 138*624Sschwartz 139*624Sschwartz /* Caller wants device information returned. */ 140*624Sschwartz if (num_devs_ret > 0) { 141*624Sschwartz 142*624Sschwartz /* 143*624Sschwartz * Allocate room. 144*624Sschwartz * Note if num_devs == 0 iget remains pointing to 145*624Sschwartz * partial_iget. 146*624Sschwartz */ 147*624Sschwartz iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 148*624Sschwartz iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 149*624Sschwartz 150*624Sschwartz /* Read in whole structure to verify there's room. */ 151*624Sschwartz if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 152*624Sschwartz SUCCESS) { 153*624Sschwartz 154*624Sschwartz /* Be consistent and just return EFAULT here. */ 155*624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 156*624Sschwartz 157*624Sschwartz return (EFAULT); 158*624Sschwartz } 159*624Sschwartz } 160*624Sschwartz 161*624Sschwartz bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 162*624Sschwartz iget->ino = ino; 163*624Sschwartz iget->num_devs_ret = num_devs_ret; 164*624Sschwartz 165*624Sschwartz /* Convert leaf-wide intr to system-wide intr */ 166*624Sschwartz if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) == 167*624Sschwartz DDI_FAILURE) { 168*624Sschwartz iget->status = PCITOOL_IO_ERROR; 169*624Sschwartz rval = EIO; 170*624Sschwartz goto done_get_intr; 171*624Sschwartz } 172*624Sschwartz 173*624Sschwartz /* Operate only on inos which are already enabled. */ 174*624Sschwartz if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) == 175*624Sschwartz DDI_FAILURE) { 176*624Sschwartz iget->status = PCITOOL_IO_ERROR; 177*624Sschwartz rval = EIO; 178*624Sschwartz goto done_get_intr; 179*624Sschwartz } 180*624Sschwartz 181*624Sschwartz /* 182*624Sschwartz * Consider all valid inos: those mapped to the root complex itself 183*624Sschwartz * as well as those mapped to devices. 184*624Sschwartz */ 185*624Sschwartz if (intr_valid_state == INTR_VALID) { 186*624Sschwartz 187*624Sschwartz /* 188*624Sschwartz * The following looks up the px_ib_ino_info and returns 189*624Sschwartz * info of devices mapped to this ino. 190*624Sschwartz */ 191*624Sschwartz iget->num_devs = pxtool_ib_get_ino_devs( 192*624Sschwartz px_p, ino, &iget->num_devs_ret, iget->dev); 193*624Sschwartz 194*624Sschwartz if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 195*624Sschwartz DDI_FAILURE) { 196*624Sschwartz iget->status = PCITOOL_IO_ERROR; 197*624Sschwartz rval = EIO; 198*624Sschwartz goto done_get_intr; 199*624Sschwartz } 200*624Sschwartz iget->cpu_id = old_cpu_id; 201*624Sschwartz } 202*624Sschwartz 203*624Sschwartz iget->status = PCITOOL_SUCCESS; 204*624Sschwartz rval = SUCCESS; 205*624Sschwartz 206*624Sschwartz done_get_intr: 207*624Sschwartz copyout_rval = 208*624Sschwartz ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); 209*624Sschwartz 210*624Sschwartz if (iget_kmem_alloc_size > 0) 211*624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 212*624Sschwartz 213*624Sschwartz if (copyout_rval != DDI_SUCCESS) 214*624Sschwartz rval = EFAULT; 215*624Sschwartz 216*624Sschwartz return (rval); 217*624Sschwartz } 218*624Sschwartz 219*624Sschwartz 220*624Sschwartz /* 221*624Sschwartz * Associate a new CPU with a given ino. 222*624Sschwartz * 223*624Sschwartz * Operate only on inos which are already mapped to devices. 224*624Sschwartz */ 225*624Sschwartz static int 226*624Sschwartz pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 227*624Sschwartz { 228*624Sschwartz pcitool_intr_set_t iset; 229*624Sschwartz cpuid_t old_cpu_id; 230*624Sschwartz sysino_t sysino; 231*624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 232*624Sschwartz px_ib_t *ib_p = px_p->px_ib_p; 233*624Sschwartz uint8_t zero = 0; 234*624Sschwartz int rval = SUCCESS; 235*624Sschwartz 236*624Sschwartz if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != 237*624Sschwartz DDI_SUCCESS) 238*624Sschwartz return (EFAULT); 239*624Sschwartz 240*624Sschwartz iset.status = PCITOOL_INVALID_INO; 241*624Sschwartz rval = EINVAL; 242*624Sschwartz 243*624Sschwartz /* Validate input argument. */ 244*624Sschwartz if (iset.ino > pxtool_num_inos) 245*624Sschwartz goto done_set_intr; 246*624Sschwartz 247*624Sschwartz /* Validate that ino given belongs to a device. */ 248*624Sschwartz if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0) 249*624Sschwartz goto done_set_intr; 250*624Sschwartz 251*624Sschwartz /* 252*624Sschwartz * Get lock, validate cpu and write new mapreg value. 253*624Sschwartz * Return original cpu value to caller via iset.cpu. 254*624Sschwartz */ 255*624Sschwartz mutex_enter(&cpu_lock); 256*624Sschwartz if (pxtool_validate_cpuid(iset.cpu_id)) { 257*624Sschwartz 258*624Sschwartz DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id); 259*624Sschwartz 260*624Sschwartz if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) == 261*624Sschwartz DDI_FAILURE) 262*624Sschwartz goto done_set_intr; 263*624Sschwartz 264*624Sschwartz if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 265*624Sschwartz DDI_FAILURE) 266*624Sschwartz goto done_set_intr; 267*624Sschwartz 268*624Sschwartz px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE); 269*624Sschwartz 270*624Sschwartz px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino); 271*624Sschwartz 272*624Sschwartz iset.cpu_id = old_cpu_id; 273*624Sschwartz iset.status = PCITOOL_SUCCESS; 274*624Sschwartz rval = SUCCESS; 275*624Sschwartz 276*624Sschwartz } else { /* Invalid cpu. Restore original register image. */ 277*624Sschwartz 278*624Sschwartz DBG(DBG_TOOLS, dip, 279*624Sschwartz "Invalid cpuid: writing orig mapreg value\n"); 280*624Sschwartz 281*624Sschwartz iset.status = PCITOOL_INVALID_CPUID; 282*624Sschwartz rval = EINVAL; 283*624Sschwartz } 284*624Sschwartz mutex_exit(&cpu_lock); 285*624Sschwartz 286*624Sschwartz done_set_intr: 287*624Sschwartz if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != 288*624Sschwartz DDI_SUCCESS) 289*624Sschwartz rval = EFAULT; 290*624Sschwartz 291*624Sschwartz return (rval); 292*624Sschwartz } 293*624Sschwartz 294*624Sschwartz 295*624Sschwartz /* Main function for handling interrupt CPU binding requests and queries. */ 296*624Sschwartz int 297*624Sschwartz pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 298*624Sschwartz { 299*624Sschwartz int rval = SUCCESS; 300*624Sschwartz 301*624Sschwartz switch (cmd) { 302*624Sschwartz 303*624Sschwartz /* Return the number of interrupts supported by a PCI bus. */ 304*624Sschwartz case PCITOOL_DEVICE_NUM_INTR: 305*624Sschwartz rval = pxtool_intr_get_max_ino(arg, mode); 306*624Sschwartz break; 307*624Sschwartz 308*624Sschwartz /* Get interrupt information for a given ino. */ 309*624Sschwartz case PCITOOL_DEVICE_GET_INTR: 310*624Sschwartz rval = pxtool_get_intr(dip, arg, mode); 311*624Sschwartz break; 312*624Sschwartz 313*624Sschwartz /* Associate a new CPU with a given ino. */ 314*624Sschwartz case PCITOOL_DEVICE_SET_INTR: 315*624Sschwartz rval = pxtool_set_intr(dip, arg, mode); 316*624Sschwartz break; 317*624Sschwartz 318*624Sschwartz default: 319*624Sschwartz rval = ENOTTY; 320*624Sschwartz } 321*624Sschwartz 322*624Sschwartz return (rval); 323*624Sschwartz } 324*624Sschwartz 325*624Sschwartz 326*624Sschwartz static int 327*624Sschwartz pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 328*624Sschwartz { 329*624Sschwartz int rval = SUCCESS; 330*624Sschwartz 331*624Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 332*624Sschwartz prg->status = PCITOOL_OUT_OF_RANGE; 333*624Sschwartz rval = EINVAL; 334*624Sschwartz 335*624Sschwartz /* Validate address arguments of bus / dev / func */ 336*624Sschwartz } else if (((prg->bus_no & 337*624Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 338*624Sschwartz ((prg->dev_no & 339*624Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 340*624Sschwartz ((prg->func_no & 341*624Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 342*624Sschwartz prg->status = PCITOOL_INVALID_ADDRESS; 343*624Sschwartz rval = EINVAL; 344*624Sschwartz } 345*624Sschwartz 346*624Sschwartz return (rval); 347*624Sschwartz } 348*624Sschwartz 349*624Sschwartz static int 350*624Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t max_addr, 351*624Sschwartz uint64_t *bar_p, uint32_t *space_p) 352*624Sschwartz { 353*624Sschwartz int rval; 354*624Sschwartz pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 355*624Sschwartz dev_info_t *dip = px_p->px_dip; 356*624Sschwartz 357*624Sschwartz *space_p = PCI_MEM32_SPACE; 358*624Sschwartz *bar_p = 0; 359*624Sschwartz 360*624Sschwartz /* 361*624Sschwartz * Translate BAR number into offset of the BAR in 362*624Sschwartz * the device's config space. 363*624Sschwartz */ 364*624Sschwartz cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 365*624Sschwartz cfg_prg.phys_addr = prg_p->phys_addr + cfg_prg.offset; 366*624Sschwartz cfg_prg.acc_attr = 367*624Sschwartz PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 368*624Sschwartz 369*624Sschwartz DBG(DBG_TOOLS, dip, "barnum:%d, phys_addr:0x%" PRIx64 "\n", 370*624Sschwartz cfg_prg.barnum, cfg_prg.phys_addr); 371*624Sschwartz 372*624Sschwartz /* 373*624Sschwartz * Get Bus Address Register (BAR) from config space. 374*624Sschwartz * cfg_prg.offset is the offset into config space of the 375*624Sschwartz * BAR desired. prg_p->status is modified on error. 376*624Sschwartz */ 377*624Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, max_addr, bar_p, PX_ISREAD); 378*624Sschwartz 379*624Sschwartz if (rval != SUCCESS) { 380*624Sschwartz prg_p->status = cfg_prg.status; 381*624Sschwartz return (rval); 382*624Sschwartz } 383*624Sschwartz 384*624Sschwartz DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 385*624Sschwartz 386*624Sschwartz /* 387*624Sschwartz * BAR has bits saying this space is IO space, unless 388*624Sschwartz * this is the ROM address register. 389*624Sschwartz */ 390*624Sschwartz if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 391*624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 392*624Sschwartz *space_p = PCI_IO_SPACE; 393*624Sschwartz *bar_p &= PCI_BASE_IO_ADDR_M; 394*624Sschwartz 395*624Sschwartz /* 396*624Sschwartz * BAR has bits saying this space is 64 bit memory 397*624Sschwartz * space, unless this is the ROM address register. 398*624Sschwartz * 399*624Sschwartz * The 64 bit address stored in two BAR cells is not 400*624Sschwartz * necessarily aligned on an 8-byte boundary. 401*624Sschwartz * Need to keep the first 4 bytes read, 402*624Sschwartz * and do a separate read of the high 4 bytes. 403*624Sschwartz */ 404*624Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 405*624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 406*624Sschwartz 407*624Sschwartz uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 408*624Sschwartz 409*624Sschwartz /* Don't try to read the next 4 bytes past the end of BARs. */ 410*624Sschwartz if (cfg_prg.offset >= PCI_CONF_BASE5) { 411*624Sschwartz prg_p->status = PCITOOL_OUT_OF_RANGE; 412*624Sschwartz return (EIO); 413*624Sschwartz } 414*624Sschwartz 415*624Sschwartz /* Access device. prg_p->status is modified on error. */ 416*624Sschwartz cfg_prg.phys_addr += sizeof (uint32_t); 417*624Sschwartz cfg_prg.offset += sizeof (uint32_t); 418*624Sschwartz 419*624Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, max_addr, bar_p, 420*624Sschwartz PX_ISREAD); 421*624Sschwartz if (rval != SUCCESS) { 422*624Sschwartz prg_p->status = cfg_prg.status; 423*624Sschwartz return (rval); 424*624Sschwartz } 425*624Sschwartz 426*624Sschwartz /* 427*624Sschwartz * Honor the 64 bit BAR as such, only when the upper 32 bits 428*624Sschwartz * store a non-zero value. 429*624Sschwartz */ 430*624Sschwartz if (*bar_p) { 431*624Sschwartz *space_p = PCI_MEM64_SPACE; 432*624Sschwartz *bar_p = (*bar_p << 32) | low_bytes; 433*624Sschwartz } else 434*624Sschwartz *bar_p = low_bytes; 435*624Sschwartz 436*624Sschwartz } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 437*624Sschwartz 438*624Sschwartz /* 439*624Sschwartz * ROM enabled. Filter ROM enable bit from the BAR. 440*624Sschwartz * Treat as Mem32 henceforth. 441*624Sschwartz */ 442*624Sschwartz if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 443*624Sschwartz *bar_p ^= PCI_BASE_ROM_ENABLE; 444*624Sschwartz 445*624Sschwartz else { /* ROM disabled. */ 446*624Sschwartz prg_p->status = PCITOOL_ROM_DISABLED; 447*624Sschwartz return (EIO); 448*624Sschwartz } 449*624Sschwartz } 450*624Sschwartz 451*624Sschwartz /* Accept a bar of 0 only for IO space. */ 452*624Sschwartz if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 453*624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS; 454*624Sschwartz return (EINVAL); 455*624Sschwartz } 456*624Sschwartz 457*624Sschwartz return (SUCCESS); 458*624Sschwartz } 459*624Sschwartz 460*624Sschwartz 461*624Sschwartz /* Perform register accesses on PCI leaf devices. */ 462*624Sschwartz int 463*624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 464*624Sschwartz { 465*624Sschwartz pcitool_reg_t prg; 466*624Sschwartz uint64_t base_addr; 467*624Sschwartz uint64_t range_base; 468*624Sschwartz uint64_t range_base_size; 469*624Sschwartz uint64_t max_addr; 470*624Sschwartz uint64_t bar; 471*624Sschwartz uint32_t space; 472*624Sschwartz boolean_t write_flag = B_FALSE; 473*624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 474*624Sschwartz px_ranges_t *rp = px_p->px_ranges_p; 475*624Sschwartz int rval = 0; 476*624Sschwartz 477*624Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG) 478*624Sschwartz write_flag = B_TRUE; 479*624Sschwartz 480*624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 481*624Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 482*624Sschwartz mode) != DDI_SUCCESS) { 483*624Sschwartz DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 484*624Sschwartz return (EFAULT); 485*624Sschwartz } 486*624Sschwartz 487*624Sschwartz if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 488*624Sschwartz goto done_reg; 489*624Sschwartz } 490*624Sschwartz 491*624Sschwartz DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 492*624Sschwartz prg.bus_no, prg.dev_no, prg.func_no); 493*624Sschwartz DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 494*624Sschwartz prg.barnum, prg.offset, prg.acc_attr); 495*624Sschwartz 496*624Sschwartz if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 497*624Sschwartz goto done_reg; 498*624Sschwartz 499*624Sschwartz range_base = PX_GET_RANGE_PROP(rp, PCI_CONFIG_SPACE); 500*624Sschwartz range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_SPACE); 501*624Sschwartz 502*624Sschwartz base_addr = range_base + (PX_GET_BDF(&prg) << PX_PCI_BDF_OFFSET_DELTA); 503*624Sschwartz max_addr = base_addr + DEV_CFG_SPACE_SIZE - 1; 504*624Sschwartz 505*624Sschwartz prg.phys_addr = base_addr; 506*624Sschwartz 507*624Sschwartz if (prg.barnum == 0) { /* Proper config space desired. */ 508*624Sschwartz 509*624Sschwartz /* Access config space and we're done. */ 510*624Sschwartz prg.phys_addr = base_addr + prg.offset; 511*624Sschwartz 512*624Sschwartz DBG(DBG_TOOLS, dip, 513*624Sschwartz "config access: base:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 514*624Sschwartz "phys_addr:0x%" PRIx64 ", end:%s\n", 515*624Sschwartz base_addr, prg.offset, prg.phys_addr, 516*624Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 517*624Sschwartz 518*624Sschwartz /* 519*624Sschwartz * Access device. pr.status is modified. 520*624Sschwartz * BDF is assumed valid at this point. 521*624Sschwartz */ 522*624Sschwartz rval = pxtool_pcicfg_access(px_p, &prg, max_addr, &prg.data, 523*624Sschwartz write_flag); 524*624Sschwartz goto done_reg; 525*624Sschwartz } 526*624Sschwartz 527*624Sschwartz /* IO/ MEM/ MEM64 space. */ 528*624Sschwartz 529*624Sschwartz if ((rval = pxtool_get_bar(px_p, &prg, max_addr, &bar, &space)) != 530*624Sschwartz SUCCESS) 531*624Sschwartz goto done_reg; 532*624Sschwartz 533*624Sschwartz if (space == PCI_IO_SPACE) { /* IO space. */ 534*624Sschwartz 535*624Sschwartz DBG(DBG_TOOLS, dip, "IO space\n"); 536*624Sschwartz 537*624Sschwartz /* Focus on IO space. */ 538*624Sschwartz range_base = PX_GET_RANGE_PROP(rp, PCI_IO_SPACE); 539*624Sschwartz range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_IO_SPACE); 540*624Sschwartz 541*624Sschwartz } else if (space == PCI_MEM64_SPACE) { /* 64 bit memory space. */ 542*624Sschwartz 543*624Sschwartz DBG(DBG_TOOLS, dip, 544*624Sschwartz "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 545*624Sschwartz 546*624Sschwartz /* Set focus to MEM64 range space. */ 547*624Sschwartz range_base = PX_GET_RANGE_PROP(rp, PCI_MEM64_SPACE); 548*624Sschwartz range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_MEM64_SPACE); 549*624Sschwartz 550*624Sschwartz } else { /* Mem32 space, including ROM */ 551*624Sschwartz 552*624Sschwartz DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 553*624Sschwartz 554*624Sschwartz /* Can't write to ROM */ 555*624Sschwartz if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 556*624Sschwartz prg.status = PCITOOL_ROM_WRITE; 557*624Sschwartz rval = EIO; 558*624Sschwartz goto done_reg; 559*624Sschwartz } 560*624Sschwartz 561*624Sschwartz range_base = PX_GET_RANGE_PROP(rp, PCI_MEM32_SPACE); 562*624Sschwartz range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_MEM32_SPACE); 563*624Sschwartz } 564*624Sschwartz 565*624Sschwartz /* Common code for all IO/MEM range spaces. */ 566*624Sschwartz max_addr = range_base + range_base_size; 567*624Sschwartz DBG(DBG_TOOLS, dip, "Range from 0x%" PRIx64 " to 0x%" PRIx64 "\n", 568*624Sschwartz range_base, range_base + range_base_size); 569*624Sschwartz 570*624Sschwartz base_addr = range_base + bar; 571*624Sschwartz 572*624Sschwartz DBG(DBG_TOOLS, dip, 573*624Sschwartz "addr portion of bar is 0x%" PRIx64 ", base=0x%" PRIx64 ", " 574*624Sschwartz "offset:0x%" PRIx64 "\n", bar, base_addr, prg.offset); 575*624Sschwartz 576*624Sschwartz /* 577*624Sschwartz * Use offset provided by caller to index into desired space. 578*624Sschwartz * Note that prg.status is modified on error. 579*624Sschwartz */ 580*624Sschwartz prg.phys_addr = base_addr + prg.offset; 581*624Sschwartz 582*624Sschwartz rval = pxtool_pciiomem_access(px_p, &prg, max_addr, &prg.data, 583*624Sschwartz write_flag); 584*624Sschwartz 585*624Sschwartz done_reg: 586*624Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 587*624Sschwartz mode) != DDI_SUCCESS) { 588*624Sschwartz DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 589*624Sschwartz rval = EFAULT; 590*624Sschwartz } 591*624Sschwartz return (rval); 592*624Sschwartz } 593*624Sschwartz 594*624Sschwartz 595*624Sschwartz int 596*624Sschwartz pxtool_init(dev_info_t *dip) 597*624Sschwartz { 598*624Sschwartz int instance = ddi_get_instance(dip); 599*624Sschwartz 600*624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 601*624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 602*624Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) { 603*624Sschwartz return (DDI_FAILURE); 604*624Sschwartz } 605*624Sschwartz 606*624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 607*624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 608*624Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 609*624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 610*624Sschwartz return (DDI_FAILURE); 611*624Sschwartz } 612*624Sschwartz 613*624Sschwartz return (DDI_SUCCESS); 614*624Sschwartz } 615*624Sschwartz 616*624Sschwartz 617*624Sschwartz void 618*624Sschwartz pxtool_uninit(dev_info_t *dip) 619*624Sschwartz { 620*624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 621*624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 622*624Sschwartz } 623