10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 230Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate #include <sys/mkdev.h> 31117Sschwartz #include <sys/stat.h> 320Sstevel@tonic-gate #include <sys/sunddi.h> 330Sstevel@tonic-gate #include <vm/seg_kmem.h> 340Sstevel@tonic-gate #include <sys/machparam.h> 35*916Sschwartz #include <sys/sunndi.h> 360Sstevel@tonic-gate #include <sys/ontrap.h> 37*916Sschwartz #include <sys/psm.h> 38881Sjohnny #include <sys/pcie.h> 390Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h> 400Sstevel@tonic-gate #include <sys/pci_cfgspace.h> 410Sstevel@tonic-gate #include <sys/pci_tools.h> 42777Sschwartz #include "pci_tools_ext.h" 43*916Sschwartz #include <io/pcplusmp/apic.h> 44*916Sschwartz #include <io/pci/pci_var.h> 450Sstevel@tonic-gate #include <sys/promif.h> 460Sstevel@tonic-gate 47777Sschwartz #define PCIEX_BDF_OFFSET_DELTA 4 48777Sschwartz #define PCIEX_REG_FUNC_SHIFT (PCI_REG_FUNC_SHIFT + PCIEX_BDF_OFFSET_DELTA) 49777Sschwartz #define PCIEX_REG_DEV_SHIFT (PCI_REG_DEV_SHIFT + PCIEX_BDF_OFFSET_DELTA) 50777Sschwartz #define PCIEX_REG_BUS_SHIFT (PCI_REG_BUS_SHIFT + PCIEX_BDF_OFFSET_DELTA) 51777Sschwartz 520Sstevel@tonic-gate #define SUCCESS 0 530Sstevel@tonic-gate 540Sstevel@tonic-gate int pcitool_debug = 0; 550Sstevel@tonic-gate 560Sstevel@tonic-gate /* 570Sstevel@tonic-gate * Offsets of BARS in config space. First entry of 0 means config space. 580Sstevel@tonic-gate * Entries here correlate to pcitool_bars_t enumerated type. 590Sstevel@tonic-gate */ 600Sstevel@tonic-gate static uint8_t pci_bars[] = { 610Sstevel@tonic-gate 0x0, 620Sstevel@tonic-gate PCI_CONF_BASE0, 630Sstevel@tonic-gate PCI_CONF_BASE1, 640Sstevel@tonic-gate PCI_CONF_BASE2, 650Sstevel@tonic-gate PCI_CONF_BASE3, 660Sstevel@tonic-gate PCI_CONF_BASE4, 670Sstevel@tonic-gate PCI_CONF_BASE5, 680Sstevel@tonic-gate PCI_CONF_ROM 690Sstevel@tonic-gate }; 700Sstevel@tonic-gate 71777Sschwartz /* Max offset allowed into config space for a particular device. */ 72777Sschwartz static uint64_t max_cfg_size = PCI_CONF_HDR_SIZE; 73777Sschwartz 740Sstevel@tonic-gate static uint64_t pcitool_swap_endian(uint64_t data, int size); 75777Sschwartz static int pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 76777Sschwartz boolean_t write_flag); 770Sstevel@tonic-gate static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 780Sstevel@tonic-gate boolean_t write_flag); 790Sstevel@tonic-gate static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, 800Sstevel@tonic-gate boolean_t write_flag); 810Sstevel@tonic-gate static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, 820Sstevel@tonic-gate uint64_t virt_addr, boolean_t write_flag); 830Sstevel@tonic-gate static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages); 840Sstevel@tonic-gate static void pcitool_unmap(uint64_t virt_addr, size_t num_pages); 850Sstevel@tonic-gate 86*916Sschwartz /* Extern decalrations */ 87*916Sschwartz extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 88*916Sschwartz psm_intr_op_t, int *); 89*916Sschwartz 90117Sschwartz int 91777Sschwartz pcitool_init(dev_info_t *dip, boolean_t is_pciex) 92117Sschwartz { 93117Sschwartz int instance = ddi_get_instance(dip); 94117Sschwartz 95117Sschwartz /* Create pcitool nodes for register access and interrupt routing. */ 96117Sschwartz 97117Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR, 98117Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM), 99117Sschwartz DDI_NT_REGACC, 0) != DDI_SUCCESS) { 100117Sschwartz return (DDI_FAILURE); 101117Sschwartz } 102117Sschwartz 103117Sschwartz if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR, 104117Sschwartz PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM), 105117Sschwartz DDI_NT_INTRCTL, 0) != DDI_SUCCESS) { 106117Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 107117Sschwartz return (DDI_FAILURE); 108117Sschwartz } 109117Sschwartz 110777Sschwartz if (is_pciex) 111777Sschwartz max_cfg_size = PCIE_CONF_HDR_SIZE; 112777Sschwartz 113117Sschwartz return (DDI_SUCCESS); 114117Sschwartz } 115117Sschwartz 116117Sschwartz void 117117Sschwartz pcitool_uninit(dev_info_t *dip) 118117Sschwartz { 119117Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_INTR); 120117Sschwartz ddi_remove_minor_node(dip, PCI_MINOR_REG); 121117Sschwartz } 122117Sschwartz 123117Sschwartz 124*916Sschwartz /* Return the number of interrupts on a pci bus. */ 125*916Sschwartz static int 126*916Sschwartz pcitool_intr_get_max_ino(uint32_t *arg, int mode) 127*916Sschwartz { 128*916Sschwartz uint32_t num_intr = APIC_MAX_VECTOR; 129*916Sschwartz 130*916Sschwartz if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) != 131*916Sschwartz DDI_SUCCESS) 132*916Sschwartz return (EFAULT); 133*916Sschwartz else 134*916Sschwartz return (SUCCESS); 135*916Sschwartz } 136*916Sschwartz 137*916Sschwartz 138*916Sschwartz /*ARGSUSED*/ 139*916Sschwartz static int 140*916Sschwartz pcitool_set_intr(dev_info_t *dip, void *arg, int mode) 141*916Sschwartz { 142*916Sschwartz ddi_intr_handle_impl_t info_hdl; 143*916Sschwartz pcitool_intr_set_t iset; 144*916Sschwartz uint32_t old_cpu; 145*916Sschwartz int ret, result; 146*916Sschwartz int rval = SUCCESS; 147*916Sschwartz 148*916Sschwartz if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != 149*916Sschwartz DDI_SUCCESS) 150*916Sschwartz return (EFAULT); 151*916Sschwartz 152*916Sschwartz if (iset.ino > APIC_MAX_VECTOR) { 153*916Sschwartz rval = EINVAL; 154*916Sschwartz iset.status = PCITOOL_INVALID_INO; 155*916Sschwartz goto done_set_intr; 156*916Sschwartz } 157*916Sschwartz 158*916Sschwartz iset.status = PCITOOL_SUCCESS; 159*916Sschwartz 160*916Sschwartz if ((old_cpu = pci_get_cpu_from_vecirq(iset.ino, IS_VEC)) == -1) { 161*916Sschwartz iset.status = PCITOOL_IO_ERROR; 162*916Sschwartz rval = EINVAL; 163*916Sschwartz goto done_set_intr; 164*916Sschwartz } 165*916Sschwartz 166*916Sschwartz old_cpu &= ~PSMGI_CPU_USER_BOUND; 167*916Sschwartz 168*916Sschwartz /* 169*916Sschwartz * For this locally-declared and used handle, ih_private will contain a 170*916Sschwartz * CPU value, not an ihdl_plat_t as used for global interrupt handling. 171*916Sschwartz */ 172*916Sschwartz info_hdl.ih_vector = iset.ino; 173*916Sschwartz info_hdl.ih_private = (void *)(uintptr_t)iset.cpu_id; 174*916Sschwartz ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_SET_CPU, &result); 175*916Sschwartz 176*916Sschwartz iset.drvr_version = PCITOOL_DRVR_VERSION; 177*916Sschwartz if (ret != PSM_SUCCESS) { 178*916Sschwartz switch (result) { 179*916Sschwartz case EIO: /* Error making the change */ 180*916Sschwartz rval = EIO; 181*916Sschwartz iset.status = PCITOOL_IO_ERROR; 182*916Sschwartz break; 183*916Sschwartz case ENXIO: /* Couldn't convert vector to irq */ 184*916Sschwartz rval = EINVAL; 185*916Sschwartz iset.status = PCITOOL_INVALID_INO; 186*916Sschwartz break; 187*916Sschwartz case EINVAL: /* CPU out of range */ 188*916Sschwartz rval = EINVAL; 189*916Sschwartz iset.status = PCITOOL_INVALID_CPUID; 190*916Sschwartz break; 191*916Sschwartz } 192*916Sschwartz } 193*916Sschwartz 194*916Sschwartz /* Return original CPU. */ 195*916Sschwartz iset.cpu_id = old_cpu; 196*916Sschwartz 197*916Sschwartz done_set_intr: 198*916Sschwartz if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != 199*916Sschwartz DDI_SUCCESS) 200*916Sschwartz rval = EFAULT; 201*916Sschwartz return (rval); 202*916Sschwartz } 203*916Sschwartz 204*916Sschwartz 205*916Sschwartz /* It is assumed that dip != NULL */ 206*916Sschwartz static void 207*916Sschwartz pcitool_get_intr_dev_info(dev_info_t *dip, pcitool_intr_dev_t *devs) 208*916Sschwartz { 209*916Sschwartz (void) strncpy(devs->driver_name, 210*916Sschwartz ddi_driver_name(dip), MAXMODCONFNAME-1); 211*916Sschwartz devs->driver_name[MAXMODCONFNAME] = '\0'; 212*916Sschwartz (void) ddi_pathname(dip, devs->path); 213*916Sschwartz devs->dev_inst = ddi_get_instance(dip); 214*916Sschwartz } 215*916Sschwartz 216*916Sschwartz 217*916Sschwartz /*ARGSUSED*/ 218*916Sschwartz static int 219*916Sschwartz pcitool_get_intr(dev_info_t *dip, void *arg, int mode) 220*916Sschwartz { 221*916Sschwartz /* Array part isn't used here, but oh well... */ 222*916Sschwartz pcitool_intr_get_t partial_iget; 223*916Sschwartz pcitool_intr_get_t *iget = &partial_iget; 224*916Sschwartz size_t iget_kmem_alloc_size = 0; 225*916Sschwartz uint8_t num_devs_ret; 226*916Sschwartz int copyout_rval; 227*916Sschwartz int rval = SUCCESS; 228*916Sschwartz int circ; 229*916Sschwartz int i; 230*916Sschwartz 231*916Sschwartz ddi_intr_handle_impl_t info_hdl; 232*916Sschwartz apic_get_intr_t intr_info; 233*916Sschwartz 234*916Sschwartz /* Read in just the header part, no array section. */ 235*916Sschwartz if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) != 236*916Sschwartz DDI_SUCCESS) 237*916Sschwartz return (EFAULT); 238*916Sschwartz 239*916Sschwartz /* Validate argument. */ 240*916Sschwartz if (partial_iget.ino > APIC_MAX_VECTOR) { 241*916Sschwartz partial_iget.status = PCITOOL_INVALID_INO; 242*916Sschwartz partial_iget.num_devs_ret = 0; 243*916Sschwartz rval = EINVAL; 244*916Sschwartz goto done_get_intr; 245*916Sschwartz } 246*916Sschwartz 247*916Sschwartz num_devs_ret = partial_iget.num_devs_ret; 248*916Sschwartz intr_info.avgi_dip_list = NULL; 249*916Sschwartz intr_info.avgi_req_flags = 250*916Sschwartz PSMGI_REQ_CPUID | PSMGI_REQ_NUM_DEVS | PSMGI_INTRBY_VEC; 251*916Sschwartz /* 252*916Sschwartz * For this locally-declared and used handle, ih_private will contain a 253*916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 254*916Sschwartz * global interrupt handling. 255*916Sschwartz */ 256*916Sschwartz info_hdl.ih_private = &intr_info; 257*916Sschwartz info_hdl.ih_vector = partial_iget.ino; 258*916Sschwartz 259*916Sschwartz /* Caller wants device information returned. */ 260*916Sschwartz if (num_devs_ret > 0) { 261*916Sschwartz 262*916Sschwartz intr_info.avgi_req_flags |= PSMGI_REQ_GET_DEVS; 263*916Sschwartz 264*916Sschwartz /* 265*916Sschwartz * Allocate room. 266*916Sschwartz * If num_devs_ret == 0 iget remains pointing to partial_iget. 267*916Sschwartz */ 268*916Sschwartz iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret); 269*916Sschwartz iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP); 270*916Sschwartz 271*916Sschwartz /* Read in whole structure to verify there's room. */ 272*916Sschwartz if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) != 273*916Sschwartz SUCCESS) { 274*916Sschwartz 275*916Sschwartz /* Be consistent and just return EFAULT here. */ 276*916Sschwartz kmem_free(iget, iget_kmem_alloc_size); 277*916Sschwartz 278*916Sschwartz return (EFAULT); 279*916Sschwartz } 280*916Sschwartz } 281*916Sschwartz 282*916Sschwartz bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret)); 283*916Sschwartz iget->ino = info_hdl.ih_vector; 284*916Sschwartz 285*916Sschwartz /* 286*916Sschwartz * Lock device tree branch from the pci root nexus on down if info will 287*916Sschwartz * be extracted from dips returned from the tree. 288*916Sschwartz */ 289*916Sschwartz if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) { 290*916Sschwartz ndi_devi_enter(dip, &circ); 291*916Sschwartz } 292*916Sschwartz 293*916Sschwartz /* Call psm_intr_ops(PSM_INTR_OP_GET_INTR) to get information. */ 294*916Sschwartz if ((rval = (*psm_intr_ops)(NULL, &info_hdl, 295*916Sschwartz PSM_INTR_OP_GET_INTR, NULL)) != PSM_SUCCESS) { 296*916Sschwartz iget->status = PCITOOL_IO_ERROR; 297*916Sschwartz iget->num_devs_ret = 0; 298*916Sschwartz rval = EINVAL; 299*916Sschwartz goto done_get_intr; 300*916Sschwartz } 301*916Sschwartz 302*916Sschwartz /* 303*916Sschwartz * Fill in the pcitool_intr_get_t to be returned, 304*916Sschwartz * with the CPU, num_devs_ret and num_devs. 305*916Sschwartz */ 306*916Sschwartz iget->cpu_id = intr_info.avgi_cpu_id & ~PSMGI_CPU_USER_BOUND; 307*916Sschwartz 308*916Sschwartz /* Number of devices returned by apic. */ 309*916Sschwartz iget->num_devs = intr_info.avgi_num_devs; 310*916Sschwartz 311*916Sschwartz /* Device info was returned. */ 312*916Sschwartz if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) { 313*916Sschwartz 314*916Sschwartz /* 315*916Sschwartz * num devs returned is num devs ret by apic, 316*916Sschwartz * space permitting. 317*916Sschwartz */ 318*916Sschwartz iget->num_devs_ret = min(num_devs_ret, intr_info.avgi_num_devs); 319*916Sschwartz 320*916Sschwartz /* 321*916Sschwartz * Loop thru list of dips and extract driver, name and instance. 322*916Sschwartz * Fill in the pcitool_intr_dev_t's with this info. 323*916Sschwartz */ 324*916Sschwartz for (i = 0; i < iget->num_devs_ret; i++) 325*916Sschwartz pcitool_get_intr_dev_info(intr_info.avgi_dip_list[i], 326*916Sschwartz &iget->dev[i]); 327*916Sschwartz 328*916Sschwartz /* Free kmem_alloc'ed memory of the apic_get_intr_t */ 329*916Sschwartz kmem_free(intr_info.avgi_dip_list, 330*916Sschwartz intr_info.avgi_num_devs * sizeof (dev_info_t *)); 331*916Sschwartz } 332*916Sschwartz 333*916Sschwartz done_get_intr: 334*916Sschwartz 335*916Sschwartz if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) { 336*916Sschwartz ndi_devi_exit(dip, circ); 337*916Sschwartz } 338*916Sschwartz 339*916Sschwartz iget->drvr_version = PCITOOL_DRVR_VERSION; 340*916Sschwartz copyout_rval = ddi_copyout(iget, arg, 341*916Sschwartz PCITOOL_IGET_SIZE(num_devs_ret), mode); 342*916Sschwartz 343*916Sschwartz if (iget_kmem_alloc_size > 0) 344*916Sschwartz kmem_free(iget, iget_kmem_alloc_size); 345*916Sschwartz 346*916Sschwartz if (copyout_rval != DDI_SUCCESS) 347*916Sschwartz rval = EFAULT; 348*916Sschwartz 349*916Sschwartz return (rval); 350*916Sschwartz } 351*916Sschwartz 352*916Sschwartz 353*916Sschwartz /* 354*916Sschwartz * Main function for handling interrupt CPU binding requests and queries. 355*916Sschwartz * Need to implement later 356*916Sschwartz */ 357*916Sschwartz /*ARGSUSED*/ 358*916Sschwartz int 359*916Sschwartz pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode) 360*916Sschwartz { 361*916Sschwartz int rval; 362*916Sschwartz 363*916Sschwartz switch (cmd) { 364*916Sschwartz 365*916Sschwartz /* Associate a new CPU with a given vector */ 366*916Sschwartz case PCITOOL_DEVICE_SET_INTR: 367*916Sschwartz rval = pcitool_set_intr(dip, arg, mode); 368*916Sschwartz break; 369*916Sschwartz 370*916Sschwartz case PCITOOL_DEVICE_GET_INTR: 371*916Sschwartz rval = pcitool_get_intr(dip, arg, mode); 372*916Sschwartz break; 373*916Sschwartz 374*916Sschwartz case PCITOOL_DEVICE_NUM_INTR: 375*916Sschwartz rval = pcitool_intr_get_max_ino(arg, mode); 376*916Sschwartz break; 377*916Sschwartz 378*916Sschwartz default: 379*916Sschwartz rval = ENOTSUP; 380*916Sschwartz } 381*916Sschwartz 382*916Sschwartz return (rval); 383*916Sschwartz } 384*916Sschwartz 385*916Sschwartz 3860Sstevel@tonic-gate /* 3870Sstevel@tonic-gate * A note about ontrap handling: 3880Sstevel@tonic-gate * 3890Sstevel@tonic-gate * X86 systems on which this module was tested return FFs instead of bus errors 3900Sstevel@tonic-gate * when accessing devices with invalid addresses. Ontrap handling, which 3910Sstevel@tonic-gate * gracefully handles kernel bus errors, is installed anyway, in case future 3920Sstevel@tonic-gate * X86 platforms require it. 3930Sstevel@tonic-gate */ 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate /* 3960Sstevel@tonic-gate * Perform register accesses on the nexus device itself. 3970Sstevel@tonic-gate * No explicit PCI nexus device for X86, so not applicable. 3980Sstevel@tonic-gate */ 399*916Sschwartz 4000Sstevel@tonic-gate /*ARGSUSED*/ 4010Sstevel@tonic-gate int 402777Sschwartz pcitool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 4030Sstevel@tonic-gate { 4040Sstevel@tonic-gate return (ENOTSUP); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /* Swap endianness. */ 4080Sstevel@tonic-gate static uint64_t 4090Sstevel@tonic-gate pcitool_swap_endian(uint64_t data, int size) 4100Sstevel@tonic-gate { 4110Sstevel@tonic-gate typedef union { 4120Sstevel@tonic-gate uint64_t data64; 4130Sstevel@tonic-gate uint8_t data8[8]; 4140Sstevel@tonic-gate } data_split_t; 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate data_split_t orig_data; 4170Sstevel@tonic-gate data_split_t returned_data; 4180Sstevel@tonic-gate int i; 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate orig_data.data64 = data; 4210Sstevel@tonic-gate returned_data.data64 = 0; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate for (i = 0; i < size; i++) { 4240Sstevel@tonic-gate returned_data.data8[i] = orig_data.data8[size - 1 - i]; 4250Sstevel@tonic-gate } 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate return (returned_data.data64); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate 431777Sschwartz /* 432777Sschwartz * Access device. prg is modified. 433777Sschwartz * 434777Sschwartz * Extended config space is available only through memory-mapped access. 435777Sschwartz * Standard config space on pci express devices is available either way, 436777Sschwartz * so do it memory-mapped here too, for simplicity. 437777Sschwartz */ 438777Sschwartz /*ARGSUSED*/ 439777Sschwartz static int 440777Sschwartz pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 441777Sschwartz boolean_t write_flag) 442777Sschwartz { 443777Sschwartz int rval = SUCCESS; 444777Sschwartz uint64_t virt_addr; 445777Sschwartz size_t num_virt_pages; 446777Sschwartz 447777Sschwartz prg->status = PCITOOL_SUCCESS; 448777Sschwartz 449777Sschwartz prg->phys_addr = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0, 450881Sjohnny "ecfga-base-address", 0); 451777Sschwartz if (prg->phys_addr == 0) { 452777Sschwartz prg->status = PCITOOL_IO_ERROR; 453777Sschwartz return (EIO); 454777Sschwartz } 455777Sschwartz 456777Sschwartz prg->phys_addr += prg->offset + 457777Sschwartz ((prg->bus_no << PCIEX_REG_BUS_SHIFT) | 458777Sschwartz (prg->dev_no << PCIEX_REG_DEV_SHIFT) | 459777Sschwartz (prg->func_no << PCIEX_REG_FUNC_SHIFT)); 460777Sschwartz 461777Sschwartz virt_addr = pcitool_map(prg->phys_addr, 462777Sschwartz PCITOOL_ACC_ATTR_SIZE(prg->acc_attr), &num_virt_pages); 463777Sschwartz if (virt_addr == NULL) { 464777Sschwartz prg->status = PCITOOL_IO_ERROR; 465777Sschwartz return (EIO); 466777Sschwartz } 467777Sschwartz 468777Sschwartz rval = pcitool_mem_access(dip, prg, virt_addr, write_flag); 469777Sschwartz pcitool_unmap(virt_addr, num_virt_pages); 470777Sschwartz return (rval); 471777Sschwartz } 472777Sschwartz 4730Sstevel@tonic-gate /* Access device. prg is modified. */ 4740Sstevel@tonic-gate /*ARGSUSED*/ 4750Sstevel@tonic-gate static int 4760Sstevel@tonic-gate pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 4770Sstevel@tonic-gate { 4780Sstevel@tonic-gate int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 4790Sstevel@tonic-gate boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 4800Sstevel@tonic-gate int rval = SUCCESS; 4810Sstevel@tonic-gate uint64_t local_data; 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate /* 4840Sstevel@tonic-gate * NOTE: there is no way to verify whether or not the address is valid. 4850Sstevel@tonic-gate * The put functions return void and the get functions return ff on 4860Sstevel@tonic-gate * error. 4870Sstevel@tonic-gate */ 4880Sstevel@tonic-gate prg->status = PCITOOL_SUCCESS; 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate if (write_flag) { 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate if (big_endian) { 4930Sstevel@tonic-gate local_data = pcitool_swap_endian(prg->data, size); 4940Sstevel@tonic-gate } else { 4950Sstevel@tonic-gate local_data = prg->data; 4960Sstevel@tonic-gate } 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate switch (size) { 4990Sstevel@tonic-gate case 1: 5000Sstevel@tonic-gate (*pci_putb_func)(prg->bus_no, prg->dev_no, 5010Sstevel@tonic-gate prg->func_no, prg->offset, local_data); 5020Sstevel@tonic-gate break; 5030Sstevel@tonic-gate case 2: 5040Sstevel@tonic-gate (*pci_putw_func)(prg->bus_no, prg->dev_no, 5050Sstevel@tonic-gate prg->func_no, prg->offset, local_data); 5060Sstevel@tonic-gate break; 5070Sstevel@tonic-gate case 4: 5080Sstevel@tonic-gate (*pci_putl_func)(prg->bus_no, prg->dev_no, 5090Sstevel@tonic-gate prg->func_no, prg->offset, local_data); 5100Sstevel@tonic-gate break; 5110Sstevel@tonic-gate default: 5120Sstevel@tonic-gate rval = ENOTSUP; 5130Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 5140Sstevel@tonic-gate break; 5150Sstevel@tonic-gate } 5160Sstevel@tonic-gate } else { 5170Sstevel@tonic-gate switch (size) { 5180Sstevel@tonic-gate case 1: 5190Sstevel@tonic-gate local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no, 5200Sstevel@tonic-gate prg->func_no, prg->offset); 5210Sstevel@tonic-gate break; 5220Sstevel@tonic-gate case 2: 5230Sstevel@tonic-gate local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no, 5240Sstevel@tonic-gate prg->func_no, prg->offset); 5250Sstevel@tonic-gate break; 5260Sstevel@tonic-gate case 4: 5270Sstevel@tonic-gate local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no, 5280Sstevel@tonic-gate prg->func_no, prg->offset); 5290Sstevel@tonic-gate break; 5300Sstevel@tonic-gate default: 5310Sstevel@tonic-gate rval = ENOTSUP; 5320Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 5330Sstevel@tonic-gate break; 5340Sstevel@tonic-gate } 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate if (rval == SUCCESS) { 5370Sstevel@tonic-gate if (big_endian) { 5380Sstevel@tonic-gate prg->data = 5390Sstevel@tonic-gate pcitool_swap_endian(local_data, size); 5400Sstevel@tonic-gate } else { 5410Sstevel@tonic-gate prg->data = local_data; 5420Sstevel@tonic-gate } 5430Sstevel@tonic-gate } 5440Sstevel@tonic-gate } 5450Sstevel@tonic-gate prg->phys_addr = 0; /* Config space is not memory mapped on X86. */ 5460Sstevel@tonic-gate return (rval); 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate /*ARGSUSED*/ 5510Sstevel@tonic-gate static int 5520Sstevel@tonic-gate pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 5530Sstevel@tonic-gate { 5540Sstevel@tonic-gate int port = (int)prg->phys_addr; 5550Sstevel@tonic-gate size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 5560Sstevel@tonic-gate boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 5570Sstevel@tonic-gate int rval = SUCCESS; 5580Sstevel@tonic-gate on_trap_data_t otd; 5590Sstevel@tonic-gate uint64_t local_data; 5600Sstevel@tonic-gate 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate /* 5630Sstevel@tonic-gate * on_trap works like setjmp. 5640Sstevel@tonic-gate * 5650Sstevel@tonic-gate * A non-zero return here means on_trap has returned from an error. 5660Sstevel@tonic-gate * 5670Sstevel@tonic-gate * A zero return here means that on_trap has just returned from setup. 5680Sstevel@tonic-gate */ 5690Sstevel@tonic-gate if (on_trap(&otd, OT_DATA_ACCESS)) { 5700Sstevel@tonic-gate no_trap(); 5710Sstevel@tonic-gate if (pcitool_debug) 5720Sstevel@tonic-gate prom_printf( 5730Sstevel@tonic-gate "pcitool_mem_access: on_trap caught an error...\n"); 5740Sstevel@tonic-gate prg->status = PCITOOL_INVALID_ADDRESS; 5750Sstevel@tonic-gate return (EFAULT); 5760Sstevel@tonic-gate } 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate if (write_flag) { 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate if (big_endian) { 5810Sstevel@tonic-gate local_data = pcitool_swap_endian(prg->data, size); 5820Sstevel@tonic-gate } else { 5830Sstevel@tonic-gate local_data = prg->data; 5840Sstevel@tonic-gate } 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate if (pcitool_debug) 5870Sstevel@tonic-gate prom_printf("Writing %ld byte(s) to port 0x%x\n", 5880Sstevel@tonic-gate size, port); 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate switch (size) { 5910Sstevel@tonic-gate case 1: 5920Sstevel@tonic-gate outb(port, (uint8_t)local_data); 5930Sstevel@tonic-gate break; 5940Sstevel@tonic-gate case 2: 5950Sstevel@tonic-gate outw(port, (uint16_t)local_data); 5960Sstevel@tonic-gate break; 5970Sstevel@tonic-gate case 4: 5980Sstevel@tonic-gate outl(port, (uint32_t)local_data); 5990Sstevel@tonic-gate break; 6000Sstevel@tonic-gate default: 6010Sstevel@tonic-gate rval = ENOTSUP; 6020Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 6030Sstevel@tonic-gate break; 6040Sstevel@tonic-gate } 6050Sstevel@tonic-gate } else { 6060Sstevel@tonic-gate if (pcitool_debug) 6070Sstevel@tonic-gate prom_printf("Reading %ld byte(s) from port 0x%x\n", 6080Sstevel@tonic-gate size, port); 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate switch (size) { 6110Sstevel@tonic-gate case 1: 6120Sstevel@tonic-gate local_data = inb(port); 6130Sstevel@tonic-gate break; 6140Sstevel@tonic-gate case 2: 6150Sstevel@tonic-gate local_data = inw(port); 6160Sstevel@tonic-gate break; 6170Sstevel@tonic-gate case 4: 6180Sstevel@tonic-gate local_data = inl(port); 6190Sstevel@tonic-gate break; 6200Sstevel@tonic-gate default: 6210Sstevel@tonic-gate rval = ENOTSUP; 6220Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 6230Sstevel@tonic-gate break; 6240Sstevel@tonic-gate } 6250Sstevel@tonic-gate 6260Sstevel@tonic-gate if (rval == SUCCESS) { 6270Sstevel@tonic-gate if (big_endian) { 6280Sstevel@tonic-gate prg->data = 6290Sstevel@tonic-gate pcitool_swap_endian(local_data, size); 6300Sstevel@tonic-gate } else { 6310Sstevel@tonic-gate prg->data = local_data; 6320Sstevel@tonic-gate } 6330Sstevel@tonic-gate } 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate 6360Sstevel@tonic-gate no_trap(); 6370Sstevel@tonic-gate return (rval); 6380Sstevel@tonic-gate } 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate /*ARGSUSED*/ 6410Sstevel@tonic-gate static int 6420Sstevel@tonic-gate pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr, 643117Sschwartz boolean_t write_flag) 6440Sstevel@tonic-gate { 6450Sstevel@tonic-gate size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 6460Sstevel@tonic-gate boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 6470Sstevel@tonic-gate int rval = DDI_SUCCESS; 6480Sstevel@tonic-gate on_trap_data_t otd; 6490Sstevel@tonic-gate uint64_t local_data; 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate /* 6520Sstevel@tonic-gate * on_trap works like setjmp. 6530Sstevel@tonic-gate * 6540Sstevel@tonic-gate * A non-zero return here means on_trap has returned from an error. 6550Sstevel@tonic-gate * 6560Sstevel@tonic-gate * A zero return here means that on_trap has just returned from setup. 6570Sstevel@tonic-gate */ 6580Sstevel@tonic-gate if (on_trap(&otd, OT_DATA_ACCESS)) { 6590Sstevel@tonic-gate no_trap(); 6600Sstevel@tonic-gate if (pcitool_debug) 6610Sstevel@tonic-gate prom_printf( 6620Sstevel@tonic-gate "pcitool_mem_access: on_trap caught an error...\n"); 6630Sstevel@tonic-gate prg->status = PCITOOL_INVALID_ADDRESS; 6640Sstevel@tonic-gate return (EFAULT); 6650Sstevel@tonic-gate } 6660Sstevel@tonic-gate 6670Sstevel@tonic-gate if (write_flag) { 6680Sstevel@tonic-gate 6690Sstevel@tonic-gate if (big_endian) { 6700Sstevel@tonic-gate local_data = pcitool_swap_endian(prg->data, size); 6710Sstevel@tonic-gate } else { 6720Sstevel@tonic-gate local_data = prg->data; 6730Sstevel@tonic-gate } 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate switch (size) { 6760Sstevel@tonic-gate case 1: 6770Sstevel@tonic-gate *((uint8_t *)(uintptr_t)virt_addr) = local_data; 6780Sstevel@tonic-gate break; 6790Sstevel@tonic-gate case 2: 6800Sstevel@tonic-gate *((uint16_t *)(uintptr_t)virt_addr) = local_data; 6810Sstevel@tonic-gate break; 6820Sstevel@tonic-gate case 4: 6830Sstevel@tonic-gate *((uint32_t *)(uintptr_t)virt_addr) = local_data; 6840Sstevel@tonic-gate break; 6850Sstevel@tonic-gate case 8: 6860Sstevel@tonic-gate *((uint64_t *)(uintptr_t)virt_addr) = local_data; 6870Sstevel@tonic-gate break; 6880Sstevel@tonic-gate default: 6890Sstevel@tonic-gate rval = ENOTSUP; 6900Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 6910Sstevel@tonic-gate break; 6920Sstevel@tonic-gate } 6930Sstevel@tonic-gate } else { 6940Sstevel@tonic-gate switch (size) { 6950Sstevel@tonic-gate case 1: 6960Sstevel@tonic-gate local_data = *((uint8_t *)(uintptr_t)virt_addr); 6970Sstevel@tonic-gate break; 6980Sstevel@tonic-gate case 2: 6990Sstevel@tonic-gate local_data = *((uint16_t *)(uintptr_t)virt_addr); 7000Sstevel@tonic-gate break; 7010Sstevel@tonic-gate case 4: 7020Sstevel@tonic-gate local_data = *((uint32_t *)(uintptr_t)virt_addr); 7030Sstevel@tonic-gate break; 7040Sstevel@tonic-gate case 8: 7050Sstevel@tonic-gate local_data = *((uint64_t *)(uintptr_t)virt_addr); 7060Sstevel@tonic-gate break; 7070Sstevel@tonic-gate default: 7080Sstevel@tonic-gate rval = ENOTSUP; 7090Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 7100Sstevel@tonic-gate break; 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate if (rval == SUCCESS) { 7140Sstevel@tonic-gate if (big_endian) { 7150Sstevel@tonic-gate prg->data = 7160Sstevel@tonic-gate pcitool_swap_endian(local_data, size); 7170Sstevel@tonic-gate } else { 7180Sstevel@tonic-gate prg->data = local_data; 7190Sstevel@tonic-gate } 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate } 7220Sstevel@tonic-gate 7230Sstevel@tonic-gate no_trap(); 7240Sstevel@tonic-gate return (rval); 7250Sstevel@tonic-gate } 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate /* 7280Sstevel@tonic-gate * Map up to 2 pages which contain the address we want to access. 7290Sstevel@tonic-gate * 7300Sstevel@tonic-gate * Mapping should span no more than 8 bytes. With X86 it is possible for an 7310Sstevel@tonic-gate * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary. 7320Sstevel@tonic-gate * We'll never have to map more than two pages. 7330Sstevel@tonic-gate */ 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate static uint64_t 7360Sstevel@tonic-gate pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages) 7370Sstevel@tonic-gate { 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET; 7400Sstevel@tonic-gate uint64_t offset = phys_addr & MMU_PAGEOFFSET; 7410Sstevel@tonic-gate void *virt_base; 7420Sstevel@tonic-gate uint64_t returned_addr; 7430Sstevel@tonic-gate 7440Sstevel@tonic-gate if (pcitool_debug) 7450Sstevel@tonic-gate prom_printf("pcitool_map: Called with PA:0x%p\n", 7460Sstevel@tonic-gate (uint8_t *)(uintptr_t)phys_addr); 7470Sstevel@tonic-gate 7480Sstevel@tonic-gate *num_pages = 1; 7490Sstevel@tonic-gate 7500Sstevel@tonic-gate /* Desired mapping would span more than two pages. */ 7510Sstevel@tonic-gate if ((offset + size) > (MMU_PAGESIZE * 2)) { 7520Sstevel@tonic-gate if (pcitool_debug) 7530Sstevel@tonic-gate prom_printf("boundary violation: " 754777Sschwartz "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%lx\n", 755777Sschwartz offset, (uintptr_t)size, (uintptr_t)MMU_PAGESIZE); 7560Sstevel@tonic-gate return (NULL); 7570Sstevel@tonic-gate 7580Sstevel@tonic-gate } else if ((offset + size) > MMU_PAGESIZE) { 7590Sstevel@tonic-gate (*num_pages)++; 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* Get page(s) of virtual space. */ 7630Sstevel@tonic-gate virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP); 7640Sstevel@tonic-gate if (virt_base == NULL) { 7650Sstevel@tonic-gate if (pcitool_debug) 7660Sstevel@tonic-gate prom_printf("Couldn't get virtual base address.\n"); 7670Sstevel@tonic-gate return (NULL); 7680Sstevel@tonic-gate } 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate if (pcitool_debug) 7710Sstevel@tonic-gate prom_printf("Got base virtual address:0x%p\n", virt_base); 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate /* Now map the allocated virtual space to the physical address. */ 7740Sstevel@tonic-gate hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages), 7750Sstevel@tonic-gate mmu_btop(page_base), PROT_READ | PROT_WRITE | HAT_STRICTORDER, 7760Sstevel@tonic-gate HAT_LOAD_LOCK); 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate returned_addr = ((uintptr_t)(virt_base)) + offset; 7790Sstevel@tonic-gate 7800Sstevel@tonic-gate if (pcitool_debug) 7810Sstevel@tonic-gate prom_printf("pcitool_map: returning VA:0x%p\n", 7820Sstevel@tonic-gate (void *)(uintptr_t)returned_addr); 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate return (returned_addr); 7850Sstevel@tonic-gate } 7860Sstevel@tonic-gate 7870Sstevel@tonic-gate /* Unmap the mapped page(s). */ 7880Sstevel@tonic-gate static void 7890Sstevel@tonic-gate pcitool_unmap(uint64_t virt_addr, size_t num_pages) 7900Sstevel@tonic-gate { 7910Sstevel@tonic-gate void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET); 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages), 7940Sstevel@tonic-gate HAT_UNLOAD_UNLOCK); 7950Sstevel@tonic-gate vmem_free(heap_arena, base_virt_addr, ptob(num_pages)); 7960Sstevel@tonic-gate } 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */ 8000Sstevel@tonic-gate int 801777Sschwartz pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 8020Sstevel@tonic-gate { 8030Sstevel@tonic-gate boolean_t write_flag = B_FALSE; 8040Sstevel@tonic-gate int rval = 0; 8050Sstevel@tonic-gate pcitool_reg_t prg; 8060Sstevel@tonic-gate uint8_t size; 8070Sstevel@tonic-gate 8080Sstevel@tonic-gate uint64_t base_addr; 8090Sstevel@tonic-gate uint64_t virt_addr; 8100Sstevel@tonic-gate size_t num_virt_pages; 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate switch (cmd) { 8130Sstevel@tonic-gate case (PCITOOL_DEVICE_SET_REG): 8140Sstevel@tonic-gate write_flag = B_TRUE; 8150Sstevel@tonic-gate 8160Sstevel@tonic-gate /*FALLTHRU*/ 8170Sstevel@tonic-gate case (PCITOOL_DEVICE_GET_REG): 8180Sstevel@tonic-gate if (pcitool_debug) 8190Sstevel@tonic-gate prom_printf("pci_dev_reg_ops set/get reg\n"); 8200Sstevel@tonic-gate if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 8210Sstevel@tonic-gate DDI_SUCCESS) { 8220Sstevel@tonic-gate if (pcitool_debug) 8230Sstevel@tonic-gate prom_printf("Error reading arguments\n"); 8240Sstevel@tonic-gate return (EFAULT); 8250Sstevel@tonic-gate } 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 8280Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 8290Sstevel@tonic-gate rval = EINVAL; 8300Sstevel@tonic-gate goto done_reg; 8310Sstevel@tonic-gate } 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate if (pcitool_debug) 8340Sstevel@tonic-gate prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n", 8350Sstevel@tonic-gate prg.bus_no, prg.dev_no, prg.func_no); 8360Sstevel@tonic-gate /* Validate address arguments of bus / dev / func */ 8370Sstevel@tonic-gate if (((prg.bus_no & 8380Sstevel@tonic-gate (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != 8390Sstevel@tonic-gate prg.bus_no) || 8400Sstevel@tonic-gate ((prg.dev_no & 8410Sstevel@tonic-gate (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != 8420Sstevel@tonic-gate prg.dev_no) || 8430Sstevel@tonic-gate ((prg.func_no & 8440Sstevel@tonic-gate (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != 8450Sstevel@tonic-gate prg.func_no)) { 8460Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 8470Sstevel@tonic-gate rval = EINVAL; 8480Sstevel@tonic-gate goto done_reg; 8490Sstevel@tonic-gate } 8500Sstevel@tonic-gate 8510Sstevel@tonic-gate size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 8520Sstevel@tonic-gate 8530Sstevel@tonic-gate /* Proper config space desired. */ 8540Sstevel@tonic-gate if (prg.barnum == 0) { 8550Sstevel@tonic-gate 856777Sschwartz if (pcitool_debug) 857777Sschwartz prom_printf( 858777Sschwartz "config access: offset:0x%" PRIx64 ", " 859777Sschwartz "phys_addr:0x%" PRIx64 "\n", 860777Sschwartz prg.offset, prg.phys_addr); 861777Sschwartz 862777Sschwartz if (prg.offset >= max_cfg_size) { 8630Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 8640Sstevel@tonic-gate rval = EINVAL; 8650Sstevel@tonic-gate goto done_reg; 8660Sstevel@tonic-gate } 8670Sstevel@tonic-gate 8680Sstevel@tonic-gate /* Access device. prg is modified. */ 869777Sschwartz if (max_cfg_size == PCIE_CONF_HDR_SIZE) 870777Sschwartz rval = pcitool_pciex_cfg_access(dip, &prg, 871777Sschwartz write_flag); 872777Sschwartz else 873777Sschwartz rval = pcitool_cfg_access(dip, &prg, 874777Sschwartz write_flag); 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate if (pcitool_debug) 8770Sstevel@tonic-gate prom_printf( 8780Sstevel@tonic-gate "config access: data:0x%" PRIx64 "\n", 8790Sstevel@tonic-gate prg.data); 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate /* IO/ MEM/ MEM64 space. */ 8820Sstevel@tonic-gate } else { 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate pcitool_reg_t prg2; 8850Sstevel@tonic-gate bcopy(&prg, &prg2, sizeof (pcitool_reg_t)); 8860Sstevel@tonic-gate 8870Sstevel@tonic-gate /* 8880Sstevel@tonic-gate * Translate BAR number into offset of the BAR in 8890Sstevel@tonic-gate * the device's config space. 8900Sstevel@tonic-gate */ 8910Sstevel@tonic-gate prg2.offset = pci_bars[prg2.barnum]; 8920Sstevel@tonic-gate prg2.acc_attr = 8930Sstevel@tonic-gate PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 8940Sstevel@tonic-gate 8950Sstevel@tonic-gate if (pcitool_debug) 8960Sstevel@tonic-gate prom_printf( 8970Sstevel@tonic-gate "barnum:%d, bar_offset:0x%" PRIx64 "\n", 8980Sstevel@tonic-gate prg2.barnum, prg2.offset); 8990Sstevel@tonic-gate /* 9000Sstevel@tonic-gate * Get Bus Address Register (BAR) from config space. 9010Sstevel@tonic-gate * prg2.offset is the offset into config space of the 9020Sstevel@tonic-gate * BAR desired. prg.status is modified on error. 9030Sstevel@tonic-gate */ 9040Sstevel@tonic-gate rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 9050Sstevel@tonic-gate if (rval != SUCCESS) { 9060Sstevel@tonic-gate if (pcitool_debug) 9070Sstevel@tonic-gate prom_printf("BAR access failed\n"); 9080Sstevel@tonic-gate prg.status = prg2.status; 9090Sstevel@tonic-gate goto done_reg; 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate /* 9120Sstevel@tonic-gate * Reference proper PCI space based on the BAR. 9130Sstevel@tonic-gate * If 64 bit MEM space, need to load other half of the 9140Sstevel@tonic-gate * BAR first. 9150Sstevel@tonic-gate */ 9160Sstevel@tonic-gate 9170Sstevel@tonic-gate if (pcitool_debug) 9180Sstevel@tonic-gate prom_printf("bar returned is 0x%" PRIx64 "\n", 9190Sstevel@tonic-gate prg2.data); 9200Sstevel@tonic-gate if (!prg2.data) { 9210Sstevel@tonic-gate if (pcitool_debug) 9220Sstevel@tonic-gate prom_printf("BAR data == 0\n"); 9230Sstevel@tonic-gate rval = EINVAL; 9240Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 9250Sstevel@tonic-gate goto done_reg; 9260Sstevel@tonic-gate } 9270Sstevel@tonic-gate if (prg2.data == 0xffffffff) { 9280Sstevel@tonic-gate if (pcitool_debug) 9290Sstevel@tonic-gate prom_printf("BAR data == -1\n"); 9300Sstevel@tonic-gate rval = EINVAL; 9310Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 9320Sstevel@tonic-gate goto done_reg; 9330Sstevel@tonic-gate } 9340Sstevel@tonic-gate 9350Sstevel@tonic-gate /* 9360Sstevel@tonic-gate * BAR has bits saying this space is IO space, unless 9370Sstevel@tonic-gate * this is the ROM address register. 9380Sstevel@tonic-gate */ 9390Sstevel@tonic-gate if (((PCI_BASE_SPACE_M & prg2.data) == 9400Sstevel@tonic-gate PCI_BASE_SPACE_IO) && 9410Sstevel@tonic-gate (prg2.offset != PCI_CONF_ROM)) { 9420Sstevel@tonic-gate if (pcitool_debug) 9430Sstevel@tonic-gate prom_printf("IO space\n"); 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate prg2.data &= PCI_BASE_IO_ADDR_M; 9460Sstevel@tonic-gate prg.phys_addr = prg2.data + prg.offset; 9470Sstevel@tonic-gate 9480Sstevel@tonic-gate rval = pcitool_io_access(dip, &prg, write_flag); 9490Sstevel@tonic-gate if ((rval != SUCCESS) && (pcitool_debug)) 9500Sstevel@tonic-gate prom_printf("IO access failed\n"); 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate goto done_reg; 9530Sstevel@tonic-gate 9540Sstevel@tonic-gate 9550Sstevel@tonic-gate /* 9560Sstevel@tonic-gate * BAR has bits saying this space is 64 bit memory 9570Sstevel@tonic-gate * space, unless this is the ROM address register. 9580Sstevel@tonic-gate * 9590Sstevel@tonic-gate * The 64 bit address stored in two BAR cells is not 9600Sstevel@tonic-gate * necessarily aligned on an 8-byte boundary. 9610Sstevel@tonic-gate * Need to keep the first 4 bytes read, 9620Sstevel@tonic-gate * and do a separate read of the high 4 bytes. 9630Sstevel@tonic-gate */ 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate } else if ((PCI_BASE_TYPE_ALL & prg2.data) && 9660Sstevel@tonic-gate (prg2.offset != PCI_CONF_ROM)) { 9670Sstevel@tonic-gate 9680Sstevel@tonic-gate uint32_t low_bytes = 9690Sstevel@tonic-gate (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL); 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate /* 9720Sstevel@tonic-gate * Don't try to read the next 4 bytes 9730Sstevel@tonic-gate * past the end of BARs. 9740Sstevel@tonic-gate */ 9750Sstevel@tonic-gate if (prg2.offset >= PCI_CONF_BASE5) { 9760Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 9770Sstevel@tonic-gate rval = EIO; 9780Sstevel@tonic-gate goto done_reg; 9790Sstevel@tonic-gate } 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate /* 9820Sstevel@tonic-gate * Access device. 9830Sstevel@tonic-gate * prg2.status is modified on error. 9840Sstevel@tonic-gate */ 9850Sstevel@tonic-gate prg2.offset += 4; 9860Sstevel@tonic-gate rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 9870Sstevel@tonic-gate if (rval != SUCCESS) { 9880Sstevel@tonic-gate prg.status = prg2.status; 9890Sstevel@tonic-gate goto done_reg; 9900Sstevel@tonic-gate } 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate if (prg2.data == 0xffffffff) { 9930Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 9940Sstevel@tonic-gate prg.status = EFAULT; 9950Sstevel@tonic-gate goto done_reg; 9960Sstevel@tonic-gate } 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate prg2.data = (prg2.data << 32) + low_bytes; 9990Sstevel@tonic-gate if (pcitool_debug) 10000Sstevel@tonic-gate prom_printf( 10010Sstevel@tonic-gate "64 bit mem space. " 10020Sstevel@tonic-gate "64-bit bar is 0x%" PRIx64 "\n", 10030Sstevel@tonic-gate prg2.data); 10040Sstevel@tonic-gate 10050Sstevel@tonic-gate /* Mem32 space, including ROM */ 10060Sstevel@tonic-gate } else { 10070Sstevel@tonic-gate 10080Sstevel@tonic-gate if (prg2.offset == PCI_CONF_ROM) { 10090Sstevel@tonic-gate if (pcitool_debug) 10100Sstevel@tonic-gate prom_printf( 10110Sstevel@tonic-gate "Additional ROM " 10120Sstevel@tonic-gate "checking\n"); 10130Sstevel@tonic-gate /* Can't write to ROM */ 10140Sstevel@tonic-gate if (write_flag) { 10150Sstevel@tonic-gate prg.status = PCITOOL_ROM_WRITE; 10160Sstevel@tonic-gate rval = EIO; 10170Sstevel@tonic-gate goto done_reg; 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate /* ROM disabled for reading */ 10200Sstevel@tonic-gate } else if (!(prg2.data & 0x00000001)) { 10210Sstevel@tonic-gate prg.status = 10220Sstevel@tonic-gate PCITOOL_ROM_DISABLED; 10230Sstevel@tonic-gate rval = EIO; 10240Sstevel@tonic-gate goto done_reg; 10250Sstevel@tonic-gate } 10260Sstevel@tonic-gate } 10270Sstevel@tonic-gate 10280Sstevel@tonic-gate if (pcitool_debug) 10290Sstevel@tonic-gate prom_printf("32 bit mem space\n"); 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate /* Common code for all IO/MEM range spaces. */ 10330Sstevel@tonic-gate 10340Sstevel@tonic-gate base_addr = prg2.data; 10350Sstevel@tonic-gate if (pcitool_debug) 10360Sstevel@tonic-gate prom_printf( 10370Sstevel@tonic-gate "addr portion of bar is 0x%" PRIx64 ", " 10380Sstevel@tonic-gate "base=0x%" PRIx64 ", " 10390Sstevel@tonic-gate "offset:0x%" PRIx64 "\n", 10400Sstevel@tonic-gate prg2.data, base_addr, prg.offset); 10410Sstevel@tonic-gate /* 10420Sstevel@tonic-gate * Use offset provided by caller to index into 10430Sstevel@tonic-gate * desired space, then access. 10440Sstevel@tonic-gate * Note that prg.status is modified on error. 10450Sstevel@tonic-gate */ 10460Sstevel@tonic-gate prg.phys_addr = base_addr + prg.offset; 10470Sstevel@tonic-gate 10480Sstevel@tonic-gate virt_addr = pcitool_map(prg.phys_addr, size, 10490Sstevel@tonic-gate &num_virt_pages); 10500Sstevel@tonic-gate if (virt_addr == NULL) { 10510Sstevel@tonic-gate prg.status = PCITOOL_IO_ERROR; 10520Sstevel@tonic-gate rval = EIO; 10530Sstevel@tonic-gate goto done_reg; 10540Sstevel@tonic-gate } 10550Sstevel@tonic-gate 10560Sstevel@tonic-gate rval = pcitool_mem_access(dip, &prg, virt_addr, 10570Sstevel@tonic-gate write_flag); 10580Sstevel@tonic-gate pcitool_unmap(virt_addr, num_virt_pages); 10590Sstevel@tonic-gate } 10600Sstevel@tonic-gate done_reg: 10610Sstevel@tonic-gate if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 10620Sstevel@tonic-gate DDI_SUCCESS) { 10630Sstevel@tonic-gate if (pcitool_debug) 10640Sstevel@tonic-gate prom_printf("Error returning arguments.\n"); 10650Sstevel@tonic-gate rval = EFAULT; 10660Sstevel@tonic-gate } 10670Sstevel@tonic-gate break; 10680Sstevel@tonic-gate default: 10690Sstevel@tonic-gate rval = ENOTTY; 10700Sstevel@tonic-gate break; 10710Sstevel@tonic-gate } 10720Sstevel@tonic-gate return (rval); 10730Sstevel@tonic-gate } 1074