1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <sys/mkdev.h> 31*0Sstevel@tonic-gate #include <sys/ddi.h> 32*0Sstevel@tonic-gate #include <sys/sunddi.h> 33*0Sstevel@tonic-gate #include <vm/seg_kmem.h> 34*0Sstevel@tonic-gate #include <sys/machparam.h> 35*0Sstevel@tonic-gate #include <sys/ontrap.h> 36*0Sstevel@tonic-gate #include <sys/pci.h> 37*0Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h> 38*0Sstevel@tonic-gate #include <sys/pci_cfgspace.h> 39*0Sstevel@tonic-gate #include <sys/pci_tools.h> 40*0Sstevel@tonic-gate #include <sys/pci_tools_var.h> 41*0Sstevel@tonic-gate #include "pci_var.h" 42*0Sstevel@tonic-gate #include <sys/promif.h> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate #define SUCCESS 0 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate int pcitool_debug = 0; 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate /* 49*0Sstevel@tonic-gate * Offsets of BARS in config space. First entry of 0 means config space. 50*0Sstevel@tonic-gate * Entries here correlate to pcitool_bars_t enumerated type. 51*0Sstevel@tonic-gate */ 52*0Sstevel@tonic-gate static uint8_t pci_bars[] = { 53*0Sstevel@tonic-gate 0x0, 54*0Sstevel@tonic-gate PCI_CONF_BASE0, 55*0Sstevel@tonic-gate PCI_CONF_BASE1, 56*0Sstevel@tonic-gate PCI_CONF_BASE2, 57*0Sstevel@tonic-gate PCI_CONF_BASE3, 58*0Sstevel@tonic-gate PCI_CONF_BASE4, 59*0Sstevel@tonic-gate PCI_CONF_BASE5, 60*0Sstevel@tonic-gate PCI_CONF_ROM 61*0Sstevel@tonic-gate }; 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate static uint64_t pcitool_swap_endian(uint64_t data, int size); 64*0Sstevel@tonic-gate static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, 65*0Sstevel@tonic-gate boolean_t write_flag); 66*0Sstevel@tonic-gate static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, 67*0Sstevel@tonic-gate boolean_t write_flag); 68*0Sstevel@tonic-gate static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, 69*0Sstevel@tonic-gate uint64_t virt_addr, boolean_t write_flag); 70*0Sstevel@tonic-gate static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages); 71*0Sstevel@tonic-gate static void pcitool_unmap(uint64_t virt_addr, size_t num_pages); 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate /* 74*0Sstevel@tonic-gate * A note about ontrap handling: 75*0Sstevel@tonic-gate * 76*0Sstevel@tonic-gate * X86 systems on which this module was tested return FFs instead of bus errors 77*0Sstevel@tonic-gate * when accessing devices with invalid addresses. Ontrap handling, which 78*0Sstevel@tonic-gate * gracefully handles kernel bus errors, is installed anyway, in case future 79*0Sstevel@tonic-gate * X86 platforms require it. 80*0Sstevel@tonic-gate */ 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate /* 83*0Sstevel@tonic-gate * Main function for handling interrupt CPU binding requests and queries. 84*0Sstevel@tonic-gate * Need to implement later 85*0Sstevel@tonic-gate */ 86*0Sstevel@tonic-gate /*ARGSUSED*/ 87*0Sstevel@tonic-gate int 88*0Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode) 89*0Sstevel@tonic-gate { 90*0Sstevel@tonic-gate return (ENOTSUP); 91*0Sstevel@tonic-gate } 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate /* 95*0Sstevel@tonic-gate * Perform register accesses on the nexus device itself. 96*0Sstevel@tonic-gate * No explicit PCI nexus device for X86, so not applicable. 97*0Sstevel@tonic-gate */ 98*0Sstevel@tonic-gate /*ARGSUSED*/ 99*0Sstevel@tonic-gate int 100*0Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode) 101*0Sstevel@tonic-gate { 102*0Sstevel@tonic-gate return (ENOTSUP); 103*0Sstevel@tonic-gate } 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate /* Swap endianness. */ 106*0Sstevel@tonic-gate static uint64_t 107*0Sstevel@tonic-gate pcitool_swap_endian(uint64_t data, int size) 108*0Sstevel@tonic-gate { 109*0Sstevel@tonic-gate typedef union { 110*0Sstevel@tonic-gate uint64_t data64; 111*0Sstevel@tonic-gate uint8_t data8[8]; 112*0Sstevel@tonic-gate } data_split_t; 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate data_split_t orig_data; 115*0Sstevel@tonic-gate data_split_t returned_data; 116*0Sstevel@tonic-gate int i; 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate orig_data.data64 = data; 119*0Sstevel@tonic-gate returned_data.data64 = 0; 120*0Sstevel@tonic-gate 121*0Sstevel@tonic-gate for (i = 0; i < size; i++) { 122*0Sstevel@tonic-gate returned_data.data8[i] = orig_data.data8[size - 1 - i]; 123*0Sstevel@tonic-gate } 124*0Sstevel@tonic-gate 125*0Sstevel@tonic-gate return (returned_data.data64); 126*0Sstevel@tonic-gate } 127*0Sstevel@tonic-gate 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate /* Access device. prg is modified. */ 130*0Sstevel@tonic-gate /*ARGSUSED*/ 131*0Sstevel@tonic-gate static int 132*0Sstevel@tonic-gate pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 133*0Sstevel@tonic-gate { 134*0Sstevel@tonic-gate int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 135*0Sstevel@tonic-gate boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 136*0Sstevel@tonic-gate int rval = SUCCESS; 137*0Sstevel@tonic-gate uint64_t local_data; 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate /* 140*0Sstevel@tonic-gate * NOTE: there is no way to verify whether or not the address is valid. 141*0Sstevel@tonic-gate * The put functions return void and the get functions return ff on 142*0Sstevel@tonic-gate * error. 143*0Sstevel@tonic-gate */ 144*0Sstevel@tonic-gate prg->status = PCITOOL_SUCCESS; 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate if (write_flag) { 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate if (big_endian) { 149*0Sstevel@tonic-gate local_data = pcitool_swap_endian(prg->data, size); 150*0Sstevel@tonic-gate } else { 151*0Sstevel@tonic-gate local_data = prg->data; 152*0Sstevel@tonic-gate } 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate switch (size) { 155*0Sstevel@tonic-gate case 1: 156*0Sstevel@tonic-gate (*pci_putb_func)(prg->bus_no, prg->dev_no, 157*0Sstevel@tonic-gate prg->func_no, prg->offset, local_data); 158*0Sstevel@tonic-gate break; 159*0Sstevel@tonic-gate case 2: 160*0Sstevel@tonic-gate (*pci_putw_func)(prg->bus_no, prg->dev_no, 161*0Sstevel@tonic-gate prg->func_no, prg->offset, local_data); 162*0Sstevel@tonic-gate break; 163*0Sstevel@tonic-gate case 4: 164*0Sstevel@tonic-gate (*pci_putl_func)(prg->bus_no, prg->dev_no, 165*0Sstevel@tonic-gate prg->func_no, prg->offset, local_data); 166*0Sstevel@tonic-gate break; 167*0Sstevel@tonic-gate default: 168*0Sstevel@tonic-gate rval = ENOTSUP; 169*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 170*0Sstevel@tonic-gate break; 171*0Sstevel@tonic-gate } 172*0Sstevel@tonic-gate } else { 173*0Sstevel@tonic-gate switch (size) { 174*0Sstevel@tonic-gate case 1: 175*0Sstevel@tonic-gate local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no, 176*0Sstevel@tonic-gate prg->func_no, prg->offset); 177*0Sstevel@tonic-gate break; 178*0Sstevel@tonic-gate case 2: 179*0Sstevel@tonic-gate local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no, 180*0Sstevel@tonic-gate prg->func_no, prg->offset); 181*0Sstevel@tonic-gate break; 182*0Sstevel@tonic-gate case 4: 183*0Sstevel@tonic-gate local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no, 184*0Sstevel@tonic-gate prg->func_no, prg->offset); 185*0Sstevel@tonic-gate break; 186*0Sstevel@tonic-gate default: 187*0Sstevel@tonic-gate rval = ENOTSUP; 188*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 189*0Sstevel@tonic-gate break; 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate if (rval == SUCCESS) { 193*0Sstevel@tonic-gate if (big_endian) { 194*0Sstevel@tonic-gate prg->data = 195*0Sstevel@tonic-gate pcitool_swap_endian(local_data, size); 196*0Sstevel@tonic-gate } else { 197*0Sstevel@tonic-gate prg->data = local_data; 198*0Sstevel@tonic-gate } 199*0Sstevel@tonic-gate } 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate prg->phys_addr = 0; /* Config space is not memory mapped on X86. */ 202*0Sstevel@tonic-gate return (rval); 203*0Sstevel@tonic-gate } 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate /*ARGSUSED*/ 207*0Sstevel@tonic-gate static int 208*0Sstevel@tonic-gate pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) 209*0Sstevel@tonic-gate { 210*0Sstevel@tonic-gate int port = (int)prg->phys_addr; 211*0Sstevel@tonic-gate size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 212*0Sstevel@tonic-gate boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 213*0Sstevel@tonic-gate int rval = SUCCESS; 214*0Sstevel@tonic-gate on_trap_data_t otd; 215*0Sstevel@tonic-gate uint64_t local_data; 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate /* 219*0Sstevel@tonic-gate * on_trap works like setjmp. 220*0Sstevel@tonic-gate * 221*0Sstevel@tonic-gate * A non-zero return here means on_trap has returned from an error. 222*0Sstevel@tonic-gate * 223*0Sstevel@tonic-gate * A zero return here means that on_trap has just returned from setup. 224*0Sstevel@tonic-gate */ 225*0Sstevel@tonic-gate if (on_trap(&otd, OT_DATA_ACCESS)) { 226*0Sstevel@tonic-gate no_trap(); 227*0Sstevel@tonic-gate if (pcitool_debug) 228*0Sstevel@tonic-gate prom_printf( 229*0Sstevel@tonic-gate "pcitool_mem_access: on_trap caught an error...\n"); 230*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_ADDRESS; 231*0Sstevel@tonic-gate return (EFAULT); 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate if (write_flag) { 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate if (big_endian) { 237*0Sstevel@tonic-gate local_data = pcitool_swap_endian(prg->data, size); 238*0Sstevel@tonic-gate } else { 239*0Sstevel@tonic-gate local_data = prg->data; 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate if (pcitool_debug) 243*0Sstevel@tonic-gate prom_printf("Writing %ld byte(s) to port 0x%x\n", 244*0Sstevel@tonic-gate size, port); 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate switch (size) { 247*0Sstevel@tonic-gate case 1: 248*0Sstevel@tonic-gate outb(port, (uint8_t)local_data); 249*0Sstevel@tonic-gate break; 250*0Sstevel@tonic-gate case 2: 251*0Sstevel@tonic-gate outw(port, (uint16_t)local_data); 252*0Sstevel@tonic-gate break; 253*0Sstevel@tonic-gate case 4: 254*0Sstevel@tonic-gate outl(port, (uint32_t)local_data); 255*0Sstevel@tonic-gate break; 256*0Sstevel@tonic-gate default: 257*0Sstevel@tonic-gate rval = ENOTSUP; 258*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 259*0Sstevel@tonic-gate break; 260*0Sstevel@tonic-gate } 261*0Sstevel@tonic-gate } else { 262*0Sstevel@tonic-gate if (pcitool_debug) 263*0Sstevel@tonic-gate prom_printf("Reading %ld byte(s) from port 0x%x\n", 264*0Sstevel@tonic-gate size, port); 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate switch (size) { 267*0Sstevel@tonic-gate case 1: 268*0Sstevel@tonic-gate local_data = inb(port); 269*0Sstevel@tonic-gate break; 270*0Sstevel@tonic-gate case 2: 271*0Sstevel@tonic-gate local_data = inw(port); 272*0Sstevel@tonic-gate break; 273*0Sstevel@tonic-gate case 4: 274*0Sstevel@tonic-gate local_data = inl(port); 275*0Sstevel@tonic-gate break; 276*0Sstevel@tonic-gate default: 277*0Sstevel@tonic-gate rval = ENOTSUP; 278*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 279*0Sstevel@tonic-gate break; 280*0Sstevel@tonic-gate } 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate if (rval == SUCCESS) { 283*0Sstevel@tonic-gate if (big_endian) { 284*0Sstevel@tonic-gate prg->data = 285*0Sstevel@tonic-gate pcitool_swap_endian(local_data, size); 286*0Sstevel@tonic-gate } else { 287*0Sstevel@tonic-gate prg->data = local_data; 288*0Sstevel@tonic-gate } 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate no_trap(); 293*0Sstevel@tonic-gate return (rval); 294*0Sstevel@tonic-gate } 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate /*ARGSUSED*/ 297*0Sstevel@tonic-gate static int 298*0Sstevel@tonic-gate pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr, 299*0Sstevel@tonic-gate boolean_t write_flag) 300*0Sstevel@tonic-gate { 301*0Sstevel@tonic-gate size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); 302*0Sstevel@tonic-gate boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); 303*0Sstevel@tonic-gate int rval = DDI_SUCCESS; 304*0Sstevel@tonic-gate on_trap_data_t otd; 305*0Sstevel@tonic-gate uint64_t local_data; 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate /* 308*0Sstevel@tonic-gate * on_trap works like setjmp. 309*0Sstevel@tonic-gate * 310*0Sstevel@tonic-gate * A non-zero return here means on_trap has returned from an error. 311*0Sstevel@tonic-gate * 312*0Sstevel@tonic-gate * A zero return here means that on_trap has just returned from setup. 313*0Sstevel@tonic-gate */ 314*0Sstevel@tonic-gate if (on_trap(&otd, OT_DATA_ACCESS)) { 315*0Sstevel@tonic-gate no_trap(); 316*0Sstevel@tonic-gate if (pcitool_debug) 317*0Sstevel@tonic-gate prom_printf( 318*0Sstevel@tonic-gate "pcitool_mem_access: on_trap caught an error...\n"); 319*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_ADDRESS; 320*0Sstevel@tonic-gate return (EFAULT); 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate if (write_flag) { 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate if (big_endian) { 326*0Sstevel@tonic-gate local_data = pcitool_swap_endian(prg->data, size); 327*0Sstevel@tonic-gate } else { 328*0Sstevel@tonic-gate local_data = prg->data; 329*0Sstevel@tonic-gate } 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate switch (size) { 332*0Sstevel@tonic-gate case 1: 333*0Sstevel@tonic-gate *((uint8_t *)(uintptr_t)virt_addr) = local_data; 334*0Sstevel@tonic-gate break; 335*0Sstevel@tonic-gate case 2: 336*0Sstevel@tonic-gate *((uint16_t *)(uintptr_t)virt_addr) = local_data; 337*0Sstevel@tonic-gate break; 338*0Sstevel@tonic-gate case 4: 339*0Sstevel@tonic-gate *((uint32_t *)(uintptr_t)virt_addr) = local_data; 340*0Sstevel@tonic-gate break; 341*0Sstevel@tonic-gate case 8: 342*0Sstevel@tonic-gate *((uint64_t *)(uintptr_t)virt_addr) = local_data; 343*0Sstevel@tonic-gate break; 344*0Sstevel@tonic-gate default: 345*0Sstevel@tonic-gate rval = ENOTSUP; 346*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 347*0Sstevel@tonic-gate break; 348*0Sstevel@tonic-gate } 349*0Sstevel@tonic-gate } else { 350*0Sstevel@tonic-gate switch (size) { 351*0Sstevel@tonic-gate case 1: 352*0Sstevel@tonic-gate local_data = *((uint8_t *)(uintptr_t)virt_addr); 353*0Sstevel@tonic-gate break; 354*0Sstevel@tonic-gate case 2: 355*0Sstevel@tonic-gate local_data = *((uint16_t *)(uintptr_t)virt_addr); 356*0Sstevel@tonic-gate break; 357*0Sstevel@tonic-gate case 4: 358*0Sstevel@tonic-gate local_data = *((uint32_t *)(uintptr_t)virt_addr); 359*0Sstevel@tonic-gate break; 360*0Sstevel@tonic-gate case 8: 361*0Sstevel@tonic-gate local_data = *((uint64_t *)(uintptr_t)virt_addr); 362*0Sstevel@tonic-gate break; 363*0Sstevel@tonic-gate default: 364*0Sstevel@tonic-gate rval = ENOTSUP; 365*0Sstevel@tonic-gate prg->status = PCITOOL_INVALID_SIZE; 366*0Sstevel@tonic-gate break; 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate if (rval == SUCCESS) { 370*0Sstevel@tonic-gate if (big_endian) { 371*0Sstevel@tonic-gate prg->data = 372*0Sstevel@tonic-gate pcitool_swap_endian(local_data, size); 373*0Sstevel@tonic-gate } else { 374*0Sstevel@tonic-gate prg->data = local_data; 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate } 377*0Sstevel@tonic-gate } 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate no_trap(); 380*0Sstevel@tonic-gate return (rval); 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate /* 384*0Sstevel@tonic-gate * Map up to 2 pages which contain the address we want to access. 385*0Sstevel@tonic-gate * 386*0Sstevel@tonic-gate * Mapping should span no more than 8 bytes. With X86 it is possible for an 387*0Sstevel@tonic-gate * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary. 388*0Sstevel@tonic-gate * We'll never have to map more than two pages. 389*0Sstevel@tonic-gate */ 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate static uint64_t 392*0Sstevel@tonic-gate pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages) 393*0Sstevel@tonic-gate { 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET; 396*0Sstevel@tonic-gate uint64_t offset = phys_addr & MMU_PAGEOFFSET; 397*0Sstevel@tonic-gate void *virt_base; 398*0Sstevel@tonic-gate uint64_t returned_addr; 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate if (pcitool_debug) 401*0Sstevel@tonic-gate prom_printf("pcitool_map: Called with PA:0x%p\n", 402*0Sstevel@tonic-gate (uint8_t *)(uintptr_t)phys_addr); 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate *num_pages = 1; 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate /* Desired mapping would span more than two pages. */ 407*0Sstevel@tonic-gate if ((offset + size) > (MMU_PAGESIZE * 2)) { 408*0Sstevel@tonic-gate if (pcitool_debug) 409*0Sstevel@tonic-gate prom_printf("boundary violation: " 410*0Sstevel@tonic-gate "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%x\n", 411*0Sstevel@tonic-gate offset, size, MMU_PAGESIZE); 412*0Sstevel@tonic-gate return (NULL); 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gate } else if ((offset + size) > MMU_PAGESIZE) { 415*0Sstevel@tonic-gate (*num_pages)++; 416*0Sstevel@tonic-gate } 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate /* Get page(s) of virtual space. */ 419*0Sstevel@tonic-gate virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP); 420*0Sstevel@tonic-gate if (virt_base == NULL) { 421*0Sstevel@tonic-gate if (pcitool_debug) 422*0Sstevel@tonic-gate prom_printf("Couldn't get virtual base address.\n"); 423*0Sstevel@tonic-gate return (NULL); 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate if (pcitool_debug) 427*0Sstevel@tonic-gate prom_printf("Got base virtual address:0x%p\n", virt_base); 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate /* Now map the allocated virtual space to the physical address. */ 430*0Sstevel@tonic-gate hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages), 431*0Sstevel@tonic-gate mmu_btop(page_base), PROT_READ | PROT_WRITE | HAT_STRICTORDER, 432*0Sstevel@tonic-gate HAT_LOAD_LOCK); 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate returned_addr = ((uintptr_t)(virt_base)) + offset; 435*0Sstevel@tonic-gate 436*0Sstevel@tonic-gate if (pcitool_debug) 437*0Sstevel@tonic-gate prom_printf("pcitool_map: returning VA:0x%p\n", 438*0Sstevel@tonic-gate (void *)(uintptr_t)returned_addr); 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate return (returned_addr); 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gate /* Unmap the mapped page(s). */ 444*0Sstevel@tonic-gate static void 445*0Sstevel@tonic-gate pcitool_unmap(uint64_t virt_addr, size_t num_pages) 446*0Sstevel@tonic-gate { 447*0Sstevel@tonic-gate void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET); 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages), 450*0Sstevel@tonic-gate HAT_UNLOAD_UNLOCK); 451*0Sstevel@tonic-gate vmem_free(heap_arena, base_virt_addr, ptob(num_pages)); 452*0Sstevel@tonic-gate } 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */ 456*0Sstevel@tonic-gate int 457*0Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode) 458*0Sstevel@tonic-gate { 459*0Sstevel@tonic-gate pci_state_t *pci_p = PCI_DEV_TO_STATE(dev); 460*0Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 461*0Sstevel@tonic-gate boolean_t write_flag = B_FALSE; 462*0Sstevel@tonic-gate int rval = 0; 463*0Sstevel@tonic-gate pcitool_reg_t prg; 464*0Sstevel@tonic-gate uint8_t size; 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate uint64_t base_addr; 467*0Sstevel@tonic-gate uint64_t virt_addr; 468*0Sstevel@tonic-gate size_t num_virt_pages; 469*0Sstevel@tonic-gate 470*0Sstevel@tonic-gate switch (cmd) { 471*0Sstevel@tonic-gate case (PCITOOL_DEVICE_SET_REG): 472*0Sstevel@tonic-gate write_flag = B_TRUE; 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate /*FALLTHRU*/ 475*0Sstevel@tonic-gate case (PCITOOL_DEVICE_GET_REG): 476*0Sstevel@tonic-gate if (pcitool_debug) 477*0Sstevel@tonic-gate prom_printf("pci_dev_reg_ops set/get reg\n"); 478*0Sstevel@tonic-gate if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 479*0Sstevel@tonic-gate DDI_SUCCESS) { 480*0Sstevel@tonic-gate if (pcitool_debug) 481*0Sstevel@tonic-gate prom_printf("Error reading arguments\n"); 482*0Sstevel@tonic-gate return (EFAULT); 483*0Sstevel@tonic-gate } 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) { 486*0Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 487*0Sstevel@tonic-gate rval = EINVAL; 488*0Sstevel@tonic-gate goto done_reg; 489*0Sstevel@tonic-gate } 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate if (pcitool_debug) 492*0Sstevel@tonic-gate prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n", 493*0Sstevel@tonic-gate prg.bus_no, prg.dev_no, prg.func_no); 494*0Sstevel@tonic-gate /* Validate address arguments of bus / dev / func */ 495*0Sstevel@tonic-gate if (((prg.bus_no & 496*0Sstevel@tonic-gate (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != 497*0Sstevel@tonic-gate prg.bus_no) || 498*0Sstevel@tonic-gate ((prg.dev_no & 499*0Sstevel@tonic-gate (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != 500*0Sstevel@tonic-gate prg.dev_no) || 501*0Sstevel@tonic-gate ((prg.func_no & 502*0Sstevel@tonic-gate (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != 503*0Sstevel@tonic-gate prg.func_no)) { 504*0Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 505*0Sstevel@tonic-gate rval = EINVAL; 506*0Sstevel@tonic-gate goto done_reg; 507*0Sstevel@tonic-gate } 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate /* Proper config space desired. */ 512*0Sstevel@tonic-gate if (prg.barnum == 0) { 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate if (prg.offset > 0xFF) { 515*0Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 516*0Sstevel@tonic-gate rval = EINVAL; 517*0Sstevel@tonic-gate goto done_reg; 518*0Sstevel@tonic-gate } 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate if (pcitool_debug) 521*0Sstevel@tonic-gate prom_printf( 522*0Sstevel@tonic-gate "config access: offset:0x%" PRIx64 ", " 523*0Sstevel@tonic-gate "phys_addr:0x%" PRIx64 "\n", 524*0Sstevel@tonic-gate prg.offset, prg.phys_addr); 525*0Sstevel@tonic-gate /* Access device. prg is modified. */ 526*0Sstevel@tonic-gate rval = pcitool_cfg_access(dip, &prg, write_flag); 527*0Sstevel@tonic-gate 528*0Sstevel@tonic-gate if (pcitool_debug) 529*0Sstevel@tonic-gate prom_printf( 530*0Sstevel@tonic-gate "config access: data:0x%" PRIx64 "\n", 531*0Sstevel@tonic-gate prg.data); 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate /* IO/ MEM/ MEM64 space. */ 534*0Sstevel@tonic-gate } else { 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate pcitool_reg_t prg2; 537*0Sstevel@tonic-gate bcopy(&prg, &prg2, sizeof (pcitool_reg_t)); 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate /* 540*0Sstevel@tonic-gate * Translate BAR number into offset of the BAR in 541*0Sstevel@tonic-gate * the device's config space. 542*0Sstevel@tonic-gate */ 543*0Sstevel@tonic-gate prg2.offset = pci_bars[prg2.barnum]; 544*0Sstevel@tonic-gate prg2.acc_attr = 545*0Sstevel@tonic-gate PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL; 546*0Sstevel@tonic-gate 547*0Sstevel@tonic-gate if (pcitool_debug) 548*0Sstevel@tonic-gate prom_printf( 549*0Sstevel@tonic-gate "barnum:%d, bar_offset:0x%" PRIx64 "\n", 550*0Sstevel@tonic-gate prg2.barnum, prg2.offset); 551*0Sstevel@tonic-gate /* 552*0Sstevel@tonic-gate * Get Bus Address Register (BAR) from config space. 553*0Sstevel@tonic-gate * prg2.offset is the offset into config space of the 554*0Sstevel@tonic-gate * BAR desired. prg.status is modified on error. 555*0Sstevel@tonic-gate */ 556*0Sstevel@tonic-gate rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 557*0Sstevel@tonic-gate if (rval != SUCCESS) { 558*0Sstevel@tonic-gate if (pcitool_debug) 559*0Sstevel@tonic-gate prom_printf("BAR access failed\n"); 560*0Sstevel@tonic-gate prg.status = prg2.status; 561*0Sstevel@tonic-gate goto done_reg; 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate /* 564*0Sstevel@tonic-gate * Reference proper PCI space based on the BAR. 565*0Sstevel@tonic-gate * If 64 bit MEM space, need to load other half of the 566*0Sstevel@tonic-gate * BAR first. 567*0Sstevel@tonic-gate */ 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gate if (pcitool_debug) 570*0Sstevel@tonic-gate prom_printf("bar returned is 0x%" PRIx64 "\n", 571*0Sstevel@tonic-gate prg2.data); 572*0Sstevel@tonic-gate if (!prg2.data) { 573*0Sstevel@tonic-gate if (pcitool_debug) 574*0Sstevel@tonic-gate prom_printf("BAR data == 0\n"); 575*0Sstevel@tonic-gate rval = EINVAL; 576*0Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 577*0Sstevel@tonic-gate goto done_reg; 578*0Sstevel@tonic-gate } 579*0Sstevel@tonic-gate if (prg2.data == 0xffffffff) { 580*0Sstevel@tonic-gate if (pcitool_debug) 581*0Sstevel@tonic-gate prom_printf("BAR data == -1\n"); 582*0Sstevel@tonic-gate rval = EINVAL; 583*0Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 584*0Sstevel@tonic-gate goto done_reg; 585*0Sstevel@tonic-gate } 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate /* 588*0Sstevel@tonic-gate * BAR has bits saying this space is IO space, unless 589*0Sstevel@tonic-gate * this is the ROM address register. 590*0Sstevel@tonic-gate */ 591*0Sstevel@tonic-gate if (((PCI_BASE_SPACE_M & prg2.data) == 592*0Sstevel@tonic-gate PCI_BASE_SPACE_IO) && 593*0Sstevel@tonic-gate (prg2.offset != PCI_CONF_ROM)) { 594*0Sstevel@tonic-gate if (pcitool_debug) 595*0Sstevel@tonic-gate prom_printf("IO space\n"); 596*0Sstevel@tonic-gate 597*0Sstevel@tonic-gate prg2.data &= PCI_BASE_IO_ADDR_M; 598*0Sstevel@tonic-gate prg.phys_addr = prg2.data + prg.offset; 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate rval = pcitool_io_access(dip, &prg, write_flag); 601*0Sstevel@tonic-gate if ((rval != SUCCESS) && (pcitool_debug)) 602*0Sstevel@tonic-gate prom_printf("IO access failed\n"); 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate goto done_reg; 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate /* 608*0Sstevel@tonic-gate * BAR has bits saying this space is 64 bit memory 609*0Sstevel@tonic-gate * space, unless this is the ROM address register. 610*0Sstevel@tonic-gate * 611*0Sstevel@tonic-gate * The 64 bit address stored in two BAR cells is not 612*0Sstevel@tonic-gate * necessarily aligned on an 8-byte boundary. 613*0Sstevel@tonic-gate * Need to keep the first 4 bytes read, 614*0Sstevel@tonic-gate * and do a separate read of the high 4 bytes. 615*0Sstevel@tonic-gate */ 616*0Sstevel@tonic-gate 617*0Sstevel@tonic-gate } else if ((PCI_BASE_TYPE_ALL & prg2.data) && 618*0Sstevel@tonic-gate (prg2.offset != PCI_CONF_ROM)) { 619*0Sstevel@tonic-gate 620*0Sstevel@tonic-gate uint32_t low_bytes = 621*0Sstevel@tonic-gate (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL); 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate /* 624*0Sstevel@tonic-gate * Don't try to read the next 4 bytes 625*0Sstevel@tonic-gate * past the end of BARs. 626*0Sstevel@tonic-gate */ 627*0Sstevel@tonic-gate if (prg2.offset >= PCI_CONF_BASE5) { 628*0Sstevel@tonic-gate prg.status = PCITOOL_OUT_OF_RANGE; 629*0Sstevel@tonic-gate rval = EIO; 630*0Sstevel@tonic-gate goto done_reg; 631*0Sstevel@tonic-gate } 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate /* 634*0Sstevel@tonic-gate * Access device. 635*0Sstevel@tonic-gate * prg2.status is modified on error. 636*0Sstevel@tonic-gate */ 637*0Sstevel@tonic-gate prg2.offset += 4; 638*0Sstevel@tonic-gate rval = pcitool_cfg_access(dip, &prg2, B_FALSE); 639*0Sstevel@tonic-gate if (rval != SUCCESS) { 640*0Sstevel@tonic-gate prg.status = prg2.status; 641*0Sstevel@tonic-gate goto done_reg; 642*0Sstevel@tonic-gate } 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate if (prg2.data == 0xffffffff) { 645*0Sstevel@tonic-gate prg.status = PCITOOL_INVALID_ADDRESS; 646*0Sstevel@tonic-gate prg.status = EFAULT; 647*0Sstevel@tonic-gate goto done_reg; 648*0Sstevel@tonic-gate } 649*0Sstevel@tonic-gate 650*0Sstevel@tonic-gate prg2.data = (prg2.data << 32) + low_bytes; 651*0Sstevel@tonic-gate if (pcitool_debug) 652*0Sstevel@tonic-gate prom_printf( 653*0Sstevel@tonic-gate "64 bit mem space. " 654*0Sstevel@tonic-gate "64-bit bar is 0x%" PRIx64 "\n", 655*0Sstevel@tonic-gate prg2.data); 656*0Sstevel@tonic-gate 657*0Sstevel@tonic-gate /* Mem32 space, including ROM */ 658*0Sstevel@tonic-gate } else { 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate if (prg2.offset == PCI_CONF_ROM) { 661*0Sstevel@tonic-gate if (pcitool_debug) 662*0Sstevel@tonic-gate prom_printf( 663*0Sstevel@tonic-gate "Additional ROM " 664*0Sstevel@tonic-gate "checking\n"); 665*0Sstevel@tonic-gate /* Can't write to ROM */ 666*0Sstevel@tonic-gate if (write_flag) { 667*0Sstevel@tonic-gate prg.status = PCITOOL_ROM_WRITE; 668*0Sstevel@tonic-gate rval = EIO; 669*0Sstevel@tonic-gate goto done_reg; 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate /* ROM disabled for reading */ 672*0Sstevel@tonic-gate } else if (!(prg2.data & 0x00000001)) { 673*0Sstevel@tonic-gate prg.status = 674*0Sstevel@tonic-gate PCITOOL_ROM_DISABLED; 675*0Sstevel@tonic-gate rval = EIO; 676*0Sstevel@tonic-gate goto done_reg; 677*0Sstevel@tonic-gate } 678*0Sstevel@tonic-gate } 679*0Sstevel@tonic-gate 680*0Sstevel@tonic-gate if (pcitool_debug) 681*0Sstevel@tonic-gate prom_printf("32 bit mem space\n"); 682*0Sstevel@tonic-gate } 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate /* Common code for all IO/MEM range spaces. */ 685*0Sstevel@tonic-gate 686*0Sstevel@tonic-gate base_addr = prg2.data; 687*0Sstevel@tonic-gate if (pcitool_debug) 688*0Sstevel@tonic-gate prom_printf( 689*0Sstevel@tonic-gate "addr portion of bar is 0x%" PRIx64 ", " 690*0Sstevel@tonic-gate "base=0x%" PRIx64 ", " 691*0Sstevel@tonic-gate "offset:0x%" PRIx64 "\n", 692*0Sstevel@tonic-gate prg2.data, base_addr, prg.offset); 693*0Sstevel@tonic-gate /* 694*0Sstevel@tonic-gate * Use offset provided by caller to index into 695*0Sstevel@tonic-gate * desired space, then access. 696*0Sstevel@tonic-gate * Note that prg.status is modified on error. 697*0Sstevel@tonic-gate */ 698*0Sstevel@tonic-gate prg.phys_addr = base_addr + prg.offset; 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gate virt_addr = pcitool_map(prg.phys_addr, size, 701*0Sstevel@tonic-gate &num_virt_pages); 702*0Sstevel@tonic-gate if (virt_addr == NULL) { 703*0Sstevel@tonic-gate prg.status = PCITOOL_IO_ERROR; 704*0Sstevel@tonic-gate rval = EIO; 705*0Sstevel@tonic-gate goto done_reg; 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate rval = pcitool_mem_access(dip, &prg, virt_addr, 709*0Sstevel@tonic-gate write_flag); 710*0Sstevel@tonic-gate pcitool_unmap(virt_addr, num_virt_pages); 711*0Sstevel@tonic-gate } 712*0Sstevel@tonic-gate done_reg: 713*0Sstevel@tonic-gate if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != 714*0Sstevel@tonic-gate DDI_SUCCESS) { 715*0Sstevel@tonic-gate if (pcitool_debug) 716*0Sstevel@tonic-gate prom_printf("Error returning arguments.\n"); 717*0Sstevel@tonic-gate rval = EFAULT; 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate break; 720*0Sstevel@tonic-gate default: 721*0Sstevel@tonic-gate rval = ENOTTY; 722*0Sstevel@tonic-gate break; 723*0Sstevel@tonic-gate } 724*0Sstevel@tonic-gate return (rval); 725*0Sstevel@tonic-gate } 726