1624Sschwartz /* 2624Sschwartz * CDDL HEADER START 3624Sschwartz * 4624Sschwartz * The contents of this file are subject to the terms of the 5*1772Sjl139090 * Common Development and Distribution License (the "License"). 6*1772Sjl139090 * 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*1772Sjl139090 * 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 /* 3501064Sschwartz * px_p defines which leaf, space defines which space in that leaf, offset 3511064Sschwartz * defines the offset within the specified space. 3521064Sschwartz * 3531064Sschwartz * This returns the physical address of the corresponding location. 3541064Sschwartz */ 3551064Sschwartz static uintptr_t 3561064Sschwartz pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset) 3571064Sschwartz { 3581064Sschwartz px_ranges_t *rp; 3591064Sschwartz uint64_t range_base; 3601064Sschwartz int rval; 3611064Sschwartz dev_info_t *dip = px_p->px_dip; 3621064Sschwartz uint32_t base_offset = 0; 363*1772Sjl139090 extern uint64_t px_get_range_prop(px_t *, px_ranges_t *, int); 3641064Sschwartz 3651064Sschwartz /* 3661064Sschwartz * Assume that requested entity is small enough to be on the same page. 3671064Sschwartz * (Same starting and ending value "offset" passed to px_search_ranges.) 3681064Sschwartz * PCItool checks alignment so that this will be true for single 3691064Sschwartz * accesses. 3701064Sschwartz * 3711064Sschwartz * Base_offset is the offset from the specified address, where the 3721064Sschwartz * current range begins. This is nonzero when a PCI space is split and 3731064Sschwartz * the address is inside the second or subsequent range. 3741064Sschwartz * 3751064Sschwartz * NOTE: offset is a uint64_t but px_search_ranges takes a uint32_t. 3761064Sschwartz * px_search_ranges should also take a uint64_t for base_offset. 3771064Sschwartz * RFE is to have px_search_ranges handle a uint64_t offset. 3781064Sschwartz */ 3791064Sschwartz rval = px_search_ranges(px_p, space, offset, offset, &rp, 3801064Sschwartz (uint32_t *)&base_offset); 3811064Sschwartz DBG(DBG_TOOLS, dip, 3821064Sschwartz "space:0x%d, offset:0x%" PRIx64 ", base_offset:0x%" PRIx64 "\n", 3831064Sschwartz space, offset, base_offset); 3841064Sschwartz 3851064Sschwartz if (rval != DDI_SUCCESS) 3861064Sschwartz return (NULL); 3871064Sschwartz else { 388*1772Sjl139090 range_base = px_get_range_prop(px_p, rp, 0); 3891064Sschwartz DBG(DBG_TOOLS, dip, "range base:0x%" PRIx64 "\n", range_base); 3901064Sschwartz return (base_offset + range_base); 3911064Sschwartz } 3921064Sschwartz } 3931064Sschwartz 3941064Sschwartz 395624Sschwartz static int 3961064Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p, 3971064Sschwartz uint32_t *space_p) 398624Sschwartz { 399624Sschwartz int rval; 4001064Sschwartz uint64_t off_in_space; 401624Sschwartz pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */ 402624Sschwartz dev_info_t *dip = px_p->px_dip; 403624Sschwartz 404624Sschwartz *space_p = PCI_MEM32_SPACE; 405624Sschwartz *bar_p = 0; 406624Sschwartz 407624Sschwartz /* 408624Sschwartz * Translate BAR number into offset of the BAR in 409624Sschwartz * the device's config space. 410624Sschwartz */ 411624Sschwartz cfg_prg.acc_attr = 412624Sschwartz PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 413624Sschwartz 4141064Sschwartz /* 4151064Sschwartz * Note: sun4u acc function uses phys_addr which includes offset. 4161064Sschwartz * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset. 4171064Sschwartz */ 4181064Sschwartz cfg_prg.offset = PCI_BAR_OFFSET((*prg_p)); 4191064Sschwartz off_in_space = (PX_GET_BDF(prg_p) << PX_PCI_BDF_OFFSET_DELTA) + 4201064Sschwartz cfg_prg.offset; 4211064Sschwartz cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 4221064Sschwartz off_in_space); 4231064Sschwartz 4241064Sschwartz DBG(DBG_TOOLS, dip, 4251064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n", 4261064Sschwartz off_in_space, cfg_prg.phys_addr, cfg_prg.barnum); 427624Sschwartz 428624Sschwartz /* 429624Sschwartz * Get Bus Address Register (BAR) from config space. 430624Sschwartz * cfg_prg.offset is the offset into config space of the 431624Sschwartz * BAR desired. prg_p->status is modified on error. 432624Sschwartz */ 4331064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 434624Sschwartz 435624Sschwartz if (rval != SUCCESS) { 436624Sschwartz prg_p->status = cfg_prg.status; 437624Sschwartz return (rval); 438624Sschwartz } 439624Sschwartz 440624Sschwartz DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p); 441624Sschwartz 442624Sschwartz /* 443624Sschwartz * BAR has bits saying this space is IO space, unless 444624Sschwartz * this is the ROM address register. 445624Sschwartz */ 446624Sschwartz if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) && 447624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 448624Sschwartz *space_p = PCI_IO_SPACE; 449624Sschwartz *bar_p &= PCI_BASE_IO_ADDR_M; 450624Sschwartz 451624Sschwartz /* 452624Sschwartz * BAR has bits saying this space is 64 bit memory 453624Sschwartz * space, unless this is the ROM address register. 454624Sschwartz * 455624Sschwartz * The 64 bit address stored in two BAR cells is not 456624Sschwartz * necessarily aligned on an 8-byte boundary. 457624Sschwartz * Need to keep the first 4 bytes read, 458624Sschwartz * and do a separate read of the high 4 bytes. 459624Sschwartz */ 460624Sschwartz } else if ((PCI_BASE_TYPE_ALL & *bar_p) && 461624Sschwartz (cfg_prg.offset != PCI_CONF_ROM)) { 462624Sschwartz 463624Sschwartz uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL); 464624Sschwartz 465624Sschwartz /* Don't try to read the next 4 bytes past the end of BARs. */ 466624Sschwartz if (cfg_prg.offset >= PCI_CONF_BASE5) { 467624Sschwartz prg_p->status = PCITOOL_OUT_OF_RANGE; 468624Sschwartz return (EIO); 469624Sschwartz } 470624Sschwartz 471624Sschwartz /* Access device. prg_p->status is modified on error. */ 472624Sschwartz cfg_prg.phys_addr += sizeof (uint32_t); 473624Sschwartz cfg_prg.offset += sizeof (uint32_t); 474624Sschwartz 4751064Sschwartz rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD); 476624Sschwartz if (rval != SUCCESS) { 477624Sschwartz prg_p->status = cfg_prg.status; 478624Sschwartz return (rval); 479624Sschwartz } 480624Sschwartz 481624Sschwartz /* 482624Sschwartz * Honor the 64 bit BAR as such, only when the upper 32 bits 483624Sschwartz * store a non-zero value. 484624Sschwartz */ 485624Sschwartz if (*bar_p) { 486624Sschwartz *space_p = PCI_MEM64_SPACE; 487624Sschwartz *bar_p = (*bar_p << 32) | low_bytes; 488624Sschwartz } else 489624Sschwartz *bar_p = low_bytes; 490624Sschwartz 491624Sschwartz } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */ 492624Sschwartz 493624Sschwartz /* 494624Sschwartz * ROM enabled. Filter ROM enable bit from the BAR. 495624Sschwartz * Treat as Mem32 henceforth. 496624Sschwartz */ 497624Sschwartz if (!(*bar_p & PCI_BASE_ROM_ENABLE)) 498624Sschwartz *bar_p ^= PCI_BASE_ROM_ENABLE; 499624Sschwartz 500624Sschwartz else { /* ROM disabled. */ 501624Sschwartz prg_p->status = PCITOOL_ROM_DISABLED; 502624Sschwartz return (EIO); 503624Sschwartz } 504624Sschwartz } 505624Sschwartz 506624Sschwartz /* Accept a bar of 0 only for IO space. */ 507624Sschwartz if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) { 508624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS; 509624Sschwartz return (EINVAL); 510624Sschwartz } 511624Sschwartz 512624Sschwartz return (SUCCESS); 513624Sschwartz } 514624Sschwartz 515624Sschwartz 516624Sschwartz /* Perform register accesses on PCI leaf devices. */ 517624Sschwartz int 518624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 519624Sschwartz { 520624Sschwartz pcitool_reg_t prg; 521624Sschwartz uint64_t bar; 522624Sschwartz uint32_t space; 5231064Sschwartz uint64_t off_in_space; 524624Sschwartz boolean_t write_flag = B_FALSE; 525624Sschwartz px_t *px_p = DIP_TO_STATE(dip); 526624Sschwartz int rval = 0; 527624Sschwartz 528624Sschwartz if (cmd == PCITOOL_DEVICE_SET_REG) 529624Sschwartz write_flag = B_TRUE; 530624Sschwartz 531624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n"); 532624Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 533624Sschwartz mode) != DDI_SUCCESS) { 534624Sschwartz DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 535624Sschwartz return (EFAULT); 536624Sschwartz } 537624Sschwartz 538624Sschwartz if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) { 539624Sschwartz goto done_reg; 540624Sschwartz } 541624Sschwartz 542624Sschwartz DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 543624Sschwartz prg.bus_no, prg.dev_no, prg.func_no); 544624Sschwartz DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 545624Sschwartz prg.barnum, prg.offset, prg.acc_attr); 546624Sschwartz 547624Sschwartz if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS) 548624Sschwartz goto done_reg; 549624Sschwartz 550624Sschwartz if (prg.barnum == 0) { /* Proper config space desired. */ 551624Sschwartz 5521064Sschwartz /* Enforce offset limits. */ 5531064Sschwartz if (prg.offset >= DEV_CFG_SPACE_SIZE) { 5541064Sschwartz DBG(DBG_TOOLS, dip, 5551064Sschwartz "Config space offset 0x%" PRIx64 " out of range\n", 5561064Sschwartz prg.offset); 5571064Sschwartz prg.status = PCITOOL_OUT_OF_RANGE; 5581064Sschwartz rval = EINVAL; 5591064Sschwartz goto done_reg; 5601064Sschwartz } 5611064Sschwartz 5621064Sschwartz off_in_space = (PX_GET_BDF(&prg) << PX_PCI_BDF_OFFSET_DELTA) + 5631064Sschwartz (uint32_t)prg.offset; 5641064Sschwartz 5651064Sschwartz /* 5661064Sschwartz * For sun4v, config space base won't be known. 5671064Sschwartz * pxtool_get_phys_addr will return zero. 5681064Sschwartz * Note that for sun4v, phys_addr isn't 5691064Sschwartz * used for making config space accesses. 5701064Sschwartz * 5711064Sschwartz * For sun4u, assume that phys_addr will come back valid. 5721064Sschwartz */ 5731064Sschwartz 5741064Sschwartz /* Accessed entity is assumed small enough to be on one page. */ 5751064Sschwartz prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, 5761064Sschwartz off_in_space); 577624Sschwartz 578624Sschwartz DBG(DBG_TOOLS, dip, 5791064Sschwartz "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", " 5801064Sschwartz "end:%s\n", off_in_space, prg.phys_addr, 581624Sschwartz PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl"); 582624Sschwartz 583624Sschwartz /* 584624Sschwartz * Access device. pr.status is modified. 585624Sschwartz * BDF is assumed valid at this point. 586624Sschwartz */ 5871064Sschwartz rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag); 588624Sschwartz goto done_reg; 589624Sschwartz } 590624Sschwartz 591624Sschwartz /* IO/ MEM/ MEM64 space. */ 592624Sschwartz 5931064Sschwartz if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS) 594624Sschwartz goto done_reg; 595624Sschwartz 5961064Sschwartz switch (space) { 5971064Sschwartz case PCI_MEM32_SPACE: 598624Sschwartz 599624Sschwartz DBG(DBG_TOOLS, dip, "32 bit mem space\n"); 600624Sschwartz 601624Sschwartz /* Can't write to ROM */ 602624Sschwartz if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) { 603624Sschwartz prg.status = PCITOOL_ROM_WRITE; 604624Sschwartz rval = EIO; 605624Sschwartz goto done_reg; 606624Sschwartz } 6071064Sschwartz break; 608624Sschwartz 6091064Sschwartz case PCI_IO_SPACE: 6101064Sschwartz DBG(DBG_TOOLS, dip, "IO space\n"); 6111064Sschwartz break; 6121064Sschwartz 6131064Sschwartz case PCI_MEM64_SPACE: 6141064Sschwartz DBG(DBG_TOOLS, dip, 6151064Sschwartz "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar); 6161064Sschwartz break; 6171064Sschwartz 6181064Sschwartz default: 6191064Sschwartz DBG(DBG_TOOLS, dip, "Unknown space!\n"); 6201064Sschwartz prg.status = PCITOOL_IO_ERROR; 6211064Sschwartz rval = EIO; 6221064Sschwartz goto done_reg; 623624Sschwartz } 624624Sschwartz 625624Sschwartz /* 6261064Sschwartz * Common code for all IO/MEM range spaces. 6271064Sschwartz * 628624Sschwartz * Use offset provided by caller to index into desired space. 629624Sschwartz * Note that prg.status is modified on error. 630624Sschwartz */ 6311064Sschwartz off_in_space = bar + prg.offset; 6321064Sschwartz prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space); 633624Sschwartz 6341064Sschwartz DBG(DBG_TOOLS, dip, 6351064Sschwartz "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", " 6361064Sschwartz "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr); 6371064Sschwartz 6381064Sschwartz rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); 639624Sschwartz 640624Sschwartz done_reg: 641624Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 642624Sschwartz mode) != DDI_SUCCESS) { 643624Sschwartz DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); 644624Sschwartz rval = EFAULT; 645624Sschwartz } 646624Sschwartz return (rval); 647624Sschwartz } 648624Sschwartz 649624Sschwartz 650624Sschwartz int 651624Sschwartz pxtool_init(dev_info_t *dip) 652624Sschwartz { 653624Sschwartz int instance = ddi_get_instance(dip); 654624Sschwartz 655624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 656624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 657624Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) { 658624Sschwartz return (DDI_FAILURE); 659624Sschwartz } 660624Sschwartz 661624Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 662624Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 663624Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 664624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 665624Sschwartz return (DDI_FAILURE); 666624Sschwartz } 667624Sschwartz 668624Sschwartz return (DDI_SUCCESS); 669624Sschwartz } 670624Sschwartz 671624Sschwartz 672624Sschwartz void 673624Sschwartz pxtool_uninit(dev_info_t *dip) 674624Sschwartz { 675624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 676624Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 677624Sschwartz } 678