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 /* 221772Sjl139090 * Copyright 2006 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 87624Sschwartz static int 88624Sschwartz pxtool_intr_get_max_ino(uint32_t *arg, int mode) 89624Sschwartz { 90624Sschwartz if (ddi_copyout(&pxtool_num_inos, arg, sizeof (uint32_t), mode) != 91624Sschwartz DDI_SUCCESS) 92624Sschwartz return (EFAULT); 93624Sschwartz else 94624Sschwartz return (SUCCESS); 95624Sschwartz } 96624Sschwartz /* 97624Sschwartz * Get interrupt information for a given ino. 98624Sschwartz * Returns info only for inos mapped to devices. 99624Sschwartz * 100624Sschwartz * Returned info is valid only when iget.num_devs is returned > 0. 101624Sschwartz * If ino is not enabled or is not mapped to a device, 102624Sschwartz * iget.num_devs will be returned as = 0. 103624Sschwartz */ 104624Sschwartz /*ARGSUSED*/ 105624Sschwartz static int 106624Sschwartz pxtool_get_intr(dev_info_t *dip, void *arg, int mode) 107624Sschwartz { 108624Sschwartz /* Array part isn't used here, but oh well... */ 109624Sschwartz pcitool_intr_get_t partial_iget; 110624Sschwartz uint32_t ino; 111624Sschwartz uint8_t num_devs_ret; 112624Sschwartz int copyout_rval; 113624Sschwartz sysino_t sysino; 114624Sschwartz intr_valid_state_t intr_valid_state; 115624Sschwartz cpuid_t old_cpu_id; 116624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 117624Sschwartz pcitool_intr_get_t *iget = &partial_iget; 118624Sschwartz size_t iget_kmem_alloc_size = 0; 119624Sschwartz int rval = SUCCESS; 120624Sschwartz 121624Sschwartz /* Read in just the header part, no array section. */ 122624Sschwartz if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 123624Sschwartz DDI_SUCCESS) 124624Sschwartz return (EFAULT); 125624Sschwartz 126624Sschwartz ino = partial_iget.ino; 127624Sschwartz num_devs_ret = partial_iget.num_devs_ret; 128624Sschwartz 129624Sschwartz partial_iget.num_devs_ret = 0; /* Assume error for now. */ 130624Sschwartz partial_iget.status = PCITOOL_INVALID_INO; 131624Sschwartz rval = EINVAL; 132624Sschwartz 133624Sschwartz /* Validate argument. */ 134624Sschwartz if (partial_iget.ino > pxtool_num_inos) { 135624Sschwartz goto done_get_intr; 136624Sschwartz } 137624Sschwartz 138624Sschwartz /* Caller wants device information returned. */ 139624Sschwartz if (num_devs_ret > 0) { 140624Sschwartz 141624Sschwartz /* 142624Sschwartz * Allocate room. 143624Sschwartz * Note if num_devs == 0 iget remains pointing to 144624Sschwartz * partial_iget. 145624Sschwartz */ 146624Sschwartz iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 147624Sschwartz iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 148624Sschwartz 149624Sschwartz /* Read in whole structure to verify there's room. */ 150624Sschwartz if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 151624Sschwartz SUCCESS) { 152624Sschwartz 153624Sschwartz /* Be consistent and just return EFAULT here. */ 154624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 155624Sschwartz 156624Sschwartz return (EFAULT); 157624Sschwartz } 158624Sschwartz } 159624Sschwartz 160624Sschwartz bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 161624Sschwartz iget->ino = ino; 162624Sschwartz iget->num_devs_ret = num_devs_ret; 163624Sschwartz 164624Sschwartz /* Convert leaf-wide intr to system-wide intr */ 165624Sschwartz if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) == 166624Sschwartz DDI_FAILURE) { 167624Sschwartz iget->status = PCITOOL_IO_ERROR; 168624Sschwartz rval = EIO; 169624Sschwartz goto done_get_intr; 170624Sschwartz } 171624Sschwartz 172624Sschwartz /* Operate only on inos which are already enabled. */ 173624Sschwartz if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) == 174624Sschwartz DDI_FAILURE) { 175624Sschwartz iget->status = PCITOOL_IO_ERROR; 176624Sschwartz rval = EIO; 177624Sschwartz goto done_get_intr; 178624Sschwartz } 179624Sschwartz 180624Sschwartz /* 181624Sschwartz * Consider all valid inos: those mapped to the root complex itself 182624Sschwartz * as well as those mapped to devices. 183624Sschwartz */ 184624Sschwartz if (intr_valid_state == INTR_VALID) { 185624Sschwartz 186624Sschwartz /* 187624Sschwartz * The following looks up the px_ib_ino_info and returns 188624Sschwartz * info of devices mapped to this ino. 189624Sschwartz */ 190624Sschwartz iget->num_devs = pxtool_ib_get_ino_devs( 191624Sschwartz px_p, ino, &iget->num_devs_ret, iget->dev); 192624Sschwartz 193624Sschwartz if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 194624Sschwartz DDI_FAILURE) { 195624Sschwartz iget->status = PCITOOL_IO_ERROR; 196624Sschwartz rval = EIO; 197624Sschwartz goto done_get_intr; 198624Sschwartz } 199624Sschwartz iget->cpu_id = old_cpu_id; 200624Sschwartz } 201624Sschwartz 202624Sschwartz iget->status = PCITOOL_SUCCESS; 203624Sschwartz rval = SUCCESS; 204624Sschwartz 205624Sschwartz done_get_intr: 206624Sschwartz copyout_rval = 207624Sschwartz ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); 208624Sschwartz 209624Sschwartz if (iget_kmem_alloc_size > 0) 210624Sschwartz kmem_free(iget, iget_kmem_alloc_size); 211624Sschwartz 212624Sschwartz if (copyout_rval != DDI_SUCCESS) 213624Sschwartz rval = EFAULT; 214624Sschwartz 215624Sschwartz return (rval); 216624Sschwartz } 217624Sschwartz 218624Sschwartz 219624Sschwartz /* 220624Sschwartz * Associate a new CPU with a given ino. 221624Sschwartz * 222624Sschwartz * Operate only on inos which are already mapped to devices. 223624Sschwartz */ 224624Sschwartz static int 225624Sschwartz pxtool_set_intr(dev_info_t *dip, void *arg, int mode) 226624Sschwartz { 227624Sschwartz pcitool_intr_set_t iset; 228624Sschwartz cpuid_t old_cpu_id; 229624Sschwartz sysino_t sysino; 230624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 231624Sschwartz px_ib_t *ib_p = px_p->px_ib_p; 232624Sschwartz uint8_t zero = 0; 233624Sschwartz int rval = SUCCESS; 234624Sschwartz 235624Sschwartz if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != 236624Sschwartz DDI_SUCCESS) 237624Sschwartz return (EFAULT); 238624Sschwartz 239624Sschwartz iset.status = PCITOOL_INVALID_INO; 240624Sschwartz rval = EINVAL; 241624Sschwartz 242624Sschwartz /* Validate input argument. */ 243624Sschwartz if (iset.ino > pxtool_num_inos) 244624Sschwartz goto done_set_intr; 245624Sschwartz 246624Sschwartz /* Validate that ino given belongs to a device. */ 247624Sschwartz if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0) 248624Sschwartz goto done_set_intr; 249624Sschwartz 250624Sschwartz /* 251624Sschwartz * Get lock, validate cpu and write new mapreg value. 252624Sschwartz * Return original cpu value to caller via iset.cpu. 253624Sschwartz */ 254624Sschwartz mutex_enter(&cpu_lock); 255624Sschwartz if (pxtool_validate_cpuid(iset.cpu_id)) { 256624Sschwartz 257624Sschwartz DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id); 258624Sschwartz 259624Sschwartz if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) == 260624Sschwartz DDI_FAILURE) 261624Sschwartz goto done_set_intr; 262624Sschwartz 263624Sschwartz if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) == 264624Sschwartz DDI_FAILURE) 265624Sschwartz goto done_set_intr; 266624Sschwartz 267624Sschwartz px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE); 268624Sschwartz 269624Sschwartz px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino); 270624Sschwartz 271624Sschwartz iset.cpu_id = old_cpu_id; 272624Sschwartz iset.status = PCITOOL_SUCCESS; 273624Sschwartz rval = SUCCESS; 274624Sschwartz 275624Sschwartz } else { /* Invalid cpu. Restore original register image. */ 276624Sschwartz 277624Sschwartz DBG(DBG_TOOLS, dip, 278624Sschwartz "Invalid cpuid: writing orig mapreg value\n"); 279624Sschwartz 280624Sschwartz iset.status = PCITOOL_INVALID_CPUID; 281624Sschwartz rval = EINVAL; 282624Sschwartz } 283624Sschwartz mutex_exit(&cpu_lock); 284624Sschwartz 285624Sschwartz done_set_intr: 286624Sschwartz if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != 287624Sschwartz DDI_SUCCESS) 288624Sschwartz rval = EFAULT; 289624Sschwartz 290624Sschwartz return (rval); 291624Sschwartz } 292624Sschwartz 293624Sschwartz 294624Sschwartz /* Main function for handling interrupt CPU binding requests and queries. */ 295624Sschwartz int 296624Sschwartz pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) 297624Sschwartz { 298624Sschwartz int rval = SUCCESS; 299624Sschwartz 300624Sschwartz switch (cmd) { 301624Sschwartz 302624Sschwartz /* Return the number of interrupts supported by a PCI bus. */ 303624Sschwartz case PCITOOL_DEVICE_NUM_INTR: 304624Sschwartz rval = pxtool_intr_get_max_ino(arg, mode); 305624Sschwartz break; 306624Sschwartz 307624Sschwartz /* Get interrupt information for a given ino. */ 308624Sschwartz case PCITOOL_DEVICE_GET_INTR: 309624Sschwartz rval = pxtool_get_intr(dip, arg, mode); 310624Sschwartz break; 311624Sschwartz 312624Sschwartz /* Associate a new CPU with a given ino. */ 313624Sschwartz case PCITOOL_DEVICE_SET_INTR: 314624Sschwartz rval = pxtool_set_intr(dip, arg, mode); 315624Sschwartz break; 316624Sschwartz 317624Sschwartz default: 318624Sschwartz rval = ENOTTY; 319624Sschwartz } 320624Sschwartz 321624Sschwartz return (rval); 322624Sschwartz } 323624Sschwartz 324624Sschwartz 325624Sschwartz static int 326624Sschwartz pxtool_validate_barnum_bdf(pcitool_reg_t *prg) 327624Sschwartz { 328624Sschwartz int rval = SUCCESS; 329624Sschwartz 330624Sschwartz if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 331624Sschwartz prg->status = PCITOOL_OUT_OF_RANGE; 332624Sschwartz rval = EINVAL; 333624Sschwartz 334624Sschwartz /* Validate address arguments of bus / dev / func */ 335624Sschwartz } else if (((prg->bus_no & 336624Sschwartz (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) || 337624Sschwartz ((prg->dev_no & 338624Sschwartz (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) || 339624Sschwartz ((prg->func_no & 340624Sschwartz (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) { 341624Sschwartz prg->status = PCITOOL_INVALID_ADDRESS; 342624Sschwartz rval = EINVAL; 343624Sschwartz } 344624Sschwartz 345624Sschwartz return (rval); 346624Sschwartz } 347624Sschwartz 3481064Sschwartz /* 3491064Sschwartz * px_p defines which leaf, space defines which space in that leaf, offset 3501064Sschwartz * defines the offset within the specified space. 3511064Sschwartz * 3521064Sschwartz * This returns the physical address of the corresponding location. 3531064Sschwartz */ 3541064Sschwartz static uintptr_t 3551064Sschwartz pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 3561064Sschwartz { 3571064Sschwartz uint64_t range_base; 3581064Sschwartz int rval; 359*2053Sschwartz pci_regspec_t dev_regspec; 360*2053Sschwartz struct regspec xlated_regspec; 3611064Sschwartz dev_info_t *dip = px_p->px_dip; 3621064Sschwartz 3631064Sschwartz /* 3641064Sschwartz * Assume that requested entity is small enough to be on the same page. 3651064Sschwartz * PCItool checks alignment so that this will be true for single 3661064Sschwartz * accesses. 3671064Sschwartz */ 368*2053Sschwartz dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT; 369*2053Sschwartz if (space == PCI_CONFIG_SPACE) { 370*2053Sschwartz dev_regspec.pci_phys_hi += 371*2053Sschwartz (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M)); 372*2053Sschwartz dev_regspec.pci_phys_low = offset & PCI_REG_REG_M; 373*2053Sschwartz } else { 374*2053Sschwartz dev_regspec.pci_phys_mid = offset >> 32; 375*2053Sschwartz dev_regspec.pci_phys_low = offset & 0xffffffff; 376*2053Sschwartz } 377*2053Sschwartz dev_regspec.pci_size_hi = 0; /* Not used. */ 378*2053Sschwartz 379*2053Sschwartz /* Note: object is guaranteed to be within a page. */ 380*2053Sschwartz dev_regspec.pci_size_low = 4; 381*2053Sschwartz 382*2053Sschwartz rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec); 383*2053Sschwartz 3841064Sschwartz DBG(DBG_TOOLS, dip, 385*2053Sschwartz "space:0x%d, offset:0x%" PRIx64 "\n", space, offset); 3861064Sschwartz 3871064Sschwartz if (rval != DDI_SUCCESS) 3881064Sschwartz return (NULL); 389*2053Sschwartz 390*2053Sschwartz /* Bustype here returns the high order address bits. */ 391*2053Sschwartz xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p); 392*2053Sschwartz 393*2053Sschwartz range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) + 394*2053Sschwartz xlated_regspec.regspec_addr; 395*2053Sschwartz DBG(DBG_TOOLS, dip, 396*2053Sschwartz "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n", 397*2053Sschwartz xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr, 398*2053Sschwartz xlated_regspec.regspec_size, range_base); 399*2053Sschwartz 400*2053Sschwartz return ((uintptr_t)range_base); 4011064Sschwartz } 4021064Sschwartz 4031064Sschwartz 404624Sschwartz static int 4051064Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 4061064Sschwartz uint32_t *space_p) 407624Sschwartz { 408624Sschwartz int rval; 4091064Sschwartz uint64_t off_in_space; 410624Sschwartz pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 411624Sschwartz dev_info_t *dip = px_p->px_dip; 412624Sschwartz 413624Sschwartz *space_p = PCI_MEM32_SPACE; 414624Sschwartz *bar_p = 0; 415624Sschwartz 416624Sschwartz /* 417624Sschwartz * Translate BAR number into offset of the BAR in 418624Sschwartz * the device's config space. 419624Sschwartz */ 420624Sschwartz cfg_prg.acc_attr = 421624Sschwartz PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 422624Sschwartz 4231064Sschwartz /* 4241064Sschwartz * Note: sun4u acc function uses phys_addr which includes offset. 4251064Sschwartz * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 4261064Sschwartz */ 4271064Sschwartz cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 428*2053Sschwartz off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset; 4291064Sschwartz cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 4301064Sschwartz off_in_space); 4311064Sschwartz 4321064Sschwartz DBG(DBG_TOOLS, dip, 4331064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 4341064Sschwartz off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 435624Sschwartz 436624Sschwartz /* 437624Sschwartz * Get Bus Address Register (BAR) from config space. 438624Sschwartz * cfg_prg.offset is the offset into config space of the 439624Sschwartz * BAR desired. prg_p->status is modified on error. 440624Sschwartz */ 4411064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 442624Sschwartz 443624Sschwartz if (rval != SUCCESS) { 444624Sschwartz prg_p->status = cfg_prg.status; 445624Sschwartz return (rval); 446624Sschwartz } 447624Sschwartz 448624Sschwartz DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 449624Sschwartz 450624Sschwartz /* 451624Sschwartz * BAR has bits saying this space is IO space, unless 452624Sschwartz * this is the ROM address register. 453624Sschwartz */ 454624Sschwartz if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 455624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 456624Sschwartz *space_p = PCI_IO_SPACE; 457624Sschwartz *bar_p &= PCI_BASE_IO_ADDR_M; 458624Sschwartz 459624Sschwartz /* 460624Sschwartz * BAR has bits saying this space is 64 bit memory 461624Sschwartz * space, unless this is the ROM address register. 462624Sschwartz * 463624Sschwartz * The 64 bit address stored in two BAR cells is not 464624Sschwartz * necessarily aligned on an 8-byte boundary. 465624Sschwartz * Need to keep the first 4 bytes read, 466624Sschwartz * and do a separate read of the high 4 bytes. 467624Sschwartz */ 468624Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 469624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 470624Sschwartz 471624Sschwartz uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 472624Sschwartz 473624Sschwartz /* Don't try to read the next 4 bytes past the end of BARs. */ 474624Sschwartz if (cfg_prg.offset >= PCI_CONF_BASE5) { 475624Sschwartz prg_p->status = PCITOOL_OUT_OF_RANGE; 476624Sschwartz return (EIO); 477624Sschwartz } 478624Sschwartz 479624Sschwartz /* Access device. prg_p->status is modified on error. */ 480624Sschwartz cfg_prg.phys_addr += sizeof (uint32_t); 481624Sschwartz cfg_prg.offset += sizeof (uint32_t); 482624Sschwartz 4831064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 484624Sschwartz if (rval != SUCCESS) { 485624Sschwartz prg_p->status = cfg_prg.status; 486624Sschwartz return (rval); 487624Sschwartz } 488624Sschwartz 489624Sschwartz /* 490624Sschwartz * Honor the 64 bit BAR as such, only when the upper 32 bits 491624Sschwartz * store a non-zero value. 492624Sschwartz */ 493624Sschwartz if (*bar_p) { 494624Sschwartz *space_p = PCI_MEM64_SPACE; 495624Sschwartz *bar_p = (*bar_p << 32) | low_bytes; 496624Sschwartz } else 497624Sschwartz *bar_p = low_bytes; 498624Sschwartz 499624Sschwartz } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 500624Sschwartz 501624Sschwartz /* 502624Sschwartz * ROM enabled. Filter ROM enable bit from the BAR. 503624Sschwartz * Treat as Mem32 henceforth. 504624Sschwartz */ 505624Sschwartz if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 506624Sschwartz *bar_p ^= PCI_BASE_ROM_ENABLE; 507624Sschwartz 508624Sschwartz else { /* ROM disabled. */ 509624Sschwartz prg_p->status = PCITOOL_ROM_DISABLED; 510624Sschwartz return (EIO); 511624Sschwartz } 512624Sschwartz } 513624Sschwartz 514624Sschwartz /* Accept a bar of 0 only for IO space. */ 515624Sschwartz if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 516624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS; 517624Sschwartz return (EINVAL); 518624Sschwartz } 519624Sschwartz 520624Sschwartz return (SUCCESS); 521624Sschwartz } 522624Sschwartz 523624Sschwartz 524624Sschwartz /* Perform register accesses on PCI leaf devices. */ 525624Sschwartz int 526624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 527624Sschwartz { 528624Sschwartz pcitool_reg_t prg; 529624Sschwartz uint64_t bar; 530624Sschwartz uint32_t space; 5311064Sschwartz uint64_t off_in_space; 532624Sschwartz boolean_t write_flag = B_FALSE; 533624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 534624Sschwartz int rval = 0; 535624Sschwartz 536624Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG) 537624Sschwartz write_flag = B_TRUE; 538624Sschwartz 539624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 540624Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 541624Sschwartz mode) != DDI_SUCCESS) { 542624Sschwartz DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 543624Sschwartz return (EFAULT); 544624Sschwartz } 545624Sschwartz 546624Sschwartz if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 547624Sschwartz goto done_reg; 548624Sschwartz } 549624Sschwartz 550624Sschwartz DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 551624Sschwartz prg.bus_no, prg.dev_no, prg.func_no); 552624Sschwartz DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 553624Sschwartz prg.barnum, prg.offset, prg.acc_attr); 554624Sschwartz 555624Sschwartz if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 556624Sschwartz goto done_reg; 557624Sschwartz 558624Sschwartz if (prg.barnum == 0) { /* Proper config space desired. */ 559624Sschwartz 5601064Sschwartz /* Enforce offset limits. */ 5611064Sschwartz if (prg.offset >= DEV_CFG_SPACE_SIZE) { 5621064Sschwartz DBG(DBG_TOOLS, dip, 5631064Sschwartz "Config space offset 0x%" PRIx64 " out of range\n", 5641064Sschwartz prg.offset); 5651064Sschwartz prg.status = PCITOOL_OUT_OF_RANGE; 5661064Sschwartz rval = EINVAL; 5671064Sschwartz goto done_reg; 5681064Sschwartz } 5691064Sschwartz 5701064Sschwartz /* 5711064Sschwartz * For sun4v, config space base won't be known. 5721064Sschwartz * pxtool_get_phys_addr will return zero. 5731064Sschwartz * Note that for sun4v, phys_addr isn't 5741064Sschwartz * used for making config space accesses. 5751064Sschwartz * 5761064Sschwartz * For sun4u, assume that phys_addr will come back valid. 5771064Sschwartz */ 578*2053Sschwartz /* 579*2053Sschwartz * Accessed entity is assumed small enough to be on one page. 580*2053Sschwartz * 581*2053Sschwartz * Since config space is less than a page and is aligned to 582*2053Sschwartz * 0x1000, a device's entire config space will be on a single 583*2053Sschwartz * page. Pass the device's base config space address here, 584*2053Sschwartz * then add the offset within that space later. This works 585*2053Sschwartz * around an issue in px_xlate_reg (called by 586*2053Sschwartz * pxtool_get_phys_addr) which accepts only a 256 byte 587*2053Sschwartz * range within a device. 588*2053Sschwartz */ 589*2053Sschwartz off_in_space = PX_GET_BDF(&prg); 590*2053Sschwartz prg.phys_addr = 591*2053Sschwartz pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space); 592*2053Sschwartz prg.phys_addr += prg.offset; 593624Sschwartz 594624Sschwartz DBG(DBG_TOOLS, dip, 5951064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 5961064Sschwartz "end:%s\n", off_in_space, prg.phys_addr, 597624Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 598624Sschwartz 599624Sschwartz /* 600624Sschwartz * Access device. pr.status is modified. 601624Sschwartz * BDF is assumed valid at this point. 602624Sschwartz */ 6031064Sschwartz rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 604624Sschwartz goto done_reg; 605624Sschwartz } 606624Sschwartz 607624Sschwartz /* IO/ MEM/ MEM64 space. */ 608624Sschwartz 6091064Sschwartz if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 610624Sschwartz goto done_reg; 611624Sschwartz 6121064Sschwartz switch (space) { 6131064Sschwartz case PCI_MEM32_SPACE: 614624Sschwartz 615624Sschwartz DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 616624Sschwartz 617624Sschwartz /* Can't write to ROM */ 618624Sschwartz if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 619624Sschwartz prg.status = PCITOOL_ROM_WRITE; 620624Sschwartz rval = EIO; 621624Sschwartz goto done_reg; 622624Sschwartz } 6231064Sschwartz break; 624624Sschwartz 6251064Sschwartz case PCI_IO_SPACE: 6261064Sschwartz DBG(DBG_TOOLS, dip, "IO space\n"); 6271064Sschwartz break; 6281064Sschwartz 6291064Sschwartz case PCI_MEM64_SPACE: 6301064Sschwartz DBG(DBG_TOOLS, dip, 6311064Sschwartz "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 6321064Sschwartz break; 6331064Sschwartz 6341064Sschwartz default: 6351064Sschwartz DBG(DBG_TOOLS, dip, "Unknown space!\n"); 6361064Sschwartz prg.status = PCITOOL_IO_ERROR; 6371064Sschwartz rval = EIO; 6381064Sschwartz goto done_reg; 639624Sschwartz } 640624Sschwartz 641624Sschwartz /* 6421064Sschwartz * Common code for all IO/MEM range spaces. 6431064Sschwartz * 644624Sschwartz * Use offset provided by caller to index into desired space. 645624Sschwartz * Note that prg.status is modified on error. 646624Sschwartz */ 6471064Sschwartz off_in_space = bar + prg.offset; 6481064Sschwartz prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 649624Sschwartz 6501064Sschwartz DBG(DBG_TOOLS, dip, 6511064Sschwartz "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 6521064Sschwartz "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 6531064Sschwartz 6541064Sschwartz rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 655624Sschwartz 656624Sschwartz done_reg: 657624Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 658624Sschwartz mode) != DDI_SUCCESS) { 659624Sschwartz DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 660624Sschwartz rval = EFAULT; 661624Sschwartz } 662624Sschwartz return (rval); 663624Sschwartz } 664624Sschwartz 665624Sschwartz 666624Sschwartz int 667624Sschwartz pxtool_init(dev_info_t *dip) 668624Sschwartz { 669624Sschwartz int instance = ddi_get_instance(dip); 670624Sschwartz 671624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 672624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 673624Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) { 674624Sschwartz return (DDI_FAILURE); 675624Sschwartz } 676624Sschwartz 677624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 678624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 679624Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 680624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 681624Sschwartz return (DDI_FAILURE); 682624Sschwartz } 683624Sschwartz 684624Sschwartz return (DDI_SUCCESS); 685624Sschwartz } 686624Sschwartz 687624Sschwartz 688624Sschwartz void 689624Sschwartz pxtool_uninit(dev_info_t *dip) 690624Sschwartz { 691624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 692624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 693624Sschwartz } 694