Home | History | Annotate | Download | only in pci
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/stat.h>
     27 #include <sys/sunddi.h>
     28 #include <sys/param.h>
     29 
     30 #include <sys/sysmacros.h>
     31 #include <sys/machsystm.h>
     32 #include <sys/promif.h>
     33 #include <sys/cpuvar.h>
     34 
     35 #include <sys/pci/pci_obj.h>
     36 #include <sys/hotplug/pci/pcihp.h>
     37 
     38 #include <sys/pci_tools.h>
     39 #include <sys/pci/pci_tools_ext.h>
     40 
     41 /*
     42  * Number of interrupts supported per PCI bus.
     43  */
     44 #define	PCI_MAX_INO		0x3f
     45 
     46 /*
     47  * PCI Space definitions.
     48  */
     49 #define	PCI_CONFIG_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
     50 #define	PCI_IO_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_IO))
     51 #define	PCI_MEM_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
     52 #define	PCI_MEM64_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
     53 
     54 /*
     55  * Extract 64 bit parent or size values from 32 bit cells of
     56  * pci_ranges_t property.
     57  *
     58  * Only bits 42-32 are relevant in parent_high.
     59  */
     60 #define	PCI_GET_RANGE_PROP(ranges, bank) \
     61 	((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
     62 	ranges[bank].parent_low)
     63 
     64 #define	PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
     65 	((((uint64_t)(ranges[bank].size_high)) << 32) | \
     66 	ranges[bank].size_low)
     67 
     68 #define	PCI_BAR_OFFSET(x)	(pci_bars[x.barnum])
     69 
     70 /* Big and little endian as boolean values. */
     71 #define	BE B_TRUE
     72 #define	LE B_FALSE
     73 
     74 #define	SUCCESS	0
     75 
     76 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
     77 typedef union {
     78 	uint64_t u64;
     79 	uint32_t u32;
     80 	uint16_t u16;
     81 	uint8_t u8;
     82 } peek_poke_value_t;
     83 
     84 /*
     85  * Offsets of BARS in config space.  First entry of 0 means config space.
     86  * Entries here correlate to pcitool_bars_t enumerated type.
     87  */
     88 static uint8_t pci_bars[] = {
     89 	0x0,
     90 	PCI_CONF_BASE0,
     91 	PCI_CONF_BASE1,
     92 	PCI_CONF_BASE2,
     93 	PCI_CONF_BASE3,
     94 	PCI_CONF_BASE4,
     95 	PCI_CONF_BASE5,
     96 	PCI_CONF_ROM
     97 };
     98 
     99 /*LINTLIBRARY*/
    100 
    101 static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
    102     uint64_t paddr, uint64_t *value_p);
    103 static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
    104     uint64_t paddr, uint64_t value);
    105 static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
    106     uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
    107     uint32_t *pcitool_status);
    108 static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
    109 static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
    110     uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
    111     boolean_t *is_io_space);
    112 static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
    113     uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
    114 static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
    115 static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
    116 static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
    117 
    118 extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
    119 extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
    120 
    121 /*
    122  * Safe C wrapper around assy language routine pci_do_phys_peek
    123  *
    124  * Type is TRUE for big endian, FALSE for little endian.
    125  * Size is 1, 2, 4 or 8 bytes.
    126  * paddr is the physical address in IO space to access read.
    127  * value_p is where the value is returned.
    128  */
    129 static int
    130 pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
    131 	uint64_t paddr, uint64_t *value_p)
    132 {
    133 	on_trap_data_t otd;
    134 	int err = DDI_SUCCESS;
    135 	peek_poke_value_t peek_value;
    136 
    137 	pbm_t *pbm_p = pci_p->pci_pbm_p;
    138 
    139 	pbm_p->pbm_ontrap_data = &otd;
    140 
    141 	/* Set up trap handling to make the access safe. */
    142 
    143 	/*
    144 	 * on_trap works like setjmp.
    145 	 * Set it up to not panic on data access error,
    146 	 * but to call peek_fault instead.
    147 	 * Call pci_do_phys_peek after trap handling is setup.
    148 	 * When on_trap returns FALSE, it has been setup.
    149 	 * When it returns TRUE, an it has caught an error.
    150 	 */
    151 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
    152 		otd.ot_trampoline = (uintptr_t)&peek_fault;
    153 		err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
    154 	} else {
    155 		err = DDI_FAILURE;
    156 	}
    157 
    158 	pbm_p->pbm_ontrap_data = NULL;
    159 	no_trap();
    160 
    161 	if (err != DDI_FAILURE) {
    162 		switch (size) {
    163 		case 8:
    164 			*value_p = (uint64_t)peek_value.u64;
    165 			break;
    166 		case 4:
    167 			*value_p = (uint64_t)peek_value.u32;
    168 			break;
    169 		case 2:
    170 			*value_p = (uint64_t)peek_value.u16;
    171 			break;
    172 		case 1:
    173 			*value_p = (uint64_t)peek_value.u8;
    174 			break;
    175 		default:
    176 			err = DDI_FAILURE;
    177 		}
    178 	}
    179 
    180 	return (err);
    181 }
    182 
    183 /*
    184  * Safe C wrapper around assy language routine pci_do_phys_poke
    185  *
    186  * Type is TRUE for big endian, FALSE for little endian.
    187  * Size is 1,2,4 or 8 bytes.
    188  * paddr is the physical address in IO space to access read.
    189  * value contains the value to be written.
    190  */
    191 static int
    192 pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
    193 	uint64_t paddr, uint64_t value)
    194 {
    195 	on_trap_data_t otd;
    196 	int err = DDI_SUCCESS;
    197 	peek_poke_value_t poke_value;
    198 
    199 	pbm_t *pbm_p = pci_p->pci_pbm_p;
    200 
    201 	switch (size) {
    202 	case 8:
    203 		poke_value.u64 = value;
    204 		break;
    205 	case 4:
    206 		poke_value.u32 = (uint32_t)value;
    207 		break;
    208 	case 2:
    209 		poke_value.u16 = (uint16_t)value;
    210 		break;
    211 	case 1:
    212 		poke_value.u8 = (uint8_t)value;
    213 		break;
    214 	default:
    215 		return (DDI_FAILURE);
    216 	}
    217 
    218 	mutex_enter(&pbm_p->pbm_pokefault_mutex);
    219 
    220 	pbm_p->pbm_ontrap_data = &otd;
    221 
    222 	/*
    223 	 * on_trap works like setjmp.
    224 	 * Set it up to not panic on data access error,
    225 	 * but to call poke_fault instead.
    226 	 * Call pci_do_phys_poke after trap handling is setup.
    227 	 * When on_trap returns FALSE, it has been setup.
    228 	 * When it returns TRUE, an it has caught an error.
    229 	 */
    230 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
    231 		otd.ot_trampoline = (uintptr_t)&poke_fault;
    232 		err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
    233 	}
    234 
    235 	/* Let the dust settle and errors occur if they will. */
    236 	pbm_clear_error(pbm_p);
    237 
    238 	/* Check for an error. */
    239 	if (otd.ot_trap == OT_DATA_ACCESS) {
    240 		err = DDI_FAILURE;
    241 	}
    242 
    243 	pbm_p->pbm_ontrap_data = NULL;
    244 	mutex_exit(&pbm_p->pbm_pokefault_mutex);
    245 
    246 	no_trap();
    247 	return (err);
    248 }
    249 
    250 
    251 /*ARGSUSED*/
    252 static int
    253 pcitool_intr_info(dev_info_t *dip, void *arg, int mode)
    254 {
    255 	pcitool_intr_info_t intr_info;
    256 	int rval = SUCCESS;
    257 
    258 	/* If we need user_version, and to ret same user version as passed in */
    259 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
    260 	    DDI_SUCCESS) {
    261 		return (EFAULT);
    262 	}
    263 
    264 	if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
    265 		return (ENOTSUP);
    266 
    267 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
    268 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
    269 	intr_info.num_intr = PCI_MAX_INO;
    270 
    271 	intr_info.drvr_version = PCITOOL_VERSION;
    272 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
    273 	    DDI_SUCCESS) {
    274 		rval = EFAULT;
    275 	}
    276 
    277 	return (rval);
    278 }
    279 
    280 
    281 /*
    282  * Get interrupt information for a given ino.
    283  * Returns info only for inos mapped to devices.
    284  *
    285  * Returned info is valid only when iget.num_devs is returned > 0.
    286  * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
    287  */
    288 /*ARGSUSED*/
    289 static int
    290 pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
    291 {
    292 	/* Array part isn't used here, but oh well... */
    293 	pcitool_intr_get_t partial_iget;
    294 	pcitool_intr_get_t *iget = &partial_iget;
    295 	size_t	iget_kmem_alloc_size = 0;
    296 	ib_t *ib_p = pci_p->pci_ib_p;
    297 	volatile uint64_t *imregp;
    298 	uint64_t imregval;
    299 	uint32_t ino;
    300 	uint8_t num_devs_ret;
    301 	int cpu_id;
    302 	int copyout_rval;
    303 	int rval = SUCCESS;
    304 
    305 	/* Read in just the header part, no array section. */
    306 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
    307 	    DDI_SUCCESS) {
    308 
    309 		return (EFAULT);
    310 	}
    311 
    312 	if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) {
    313 		partial_iget.status = PCITOOL_IO_ERROR;
    314 		partial_iget.num_devs_ret = 0;
    315 		rval = ENOTSUP;
    316 		goto done_get_intr;
    317 	}
    318 
    319 	ino = partial_iget.ino;
    320 	num_devs_ret = partial_iget.num_devs_ret;
    321 
    322 	/* Validate argument. */
    323 	if (ino > PCI_MAX_INO) {
    324 		partial_iget.status = PCITOOL_INVALID_INO;
    325 		partial_iget.num_devs_ret = 0;
    326 		rval = EINVAL;
    327 		goto done_get_intr;
    328 	}
    329 
    330 	/* Caller wants device information returned. */
    331 	if (num_devs_ret > 0) {
    332 
    333 		/*
    334 		 * Allocate room.
    335 		 * Note if num_devs_ret == 0 iget remains pointing to
    336 		 * partial_iget.
    337 		 */
    338 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
    339 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
    340 
    341 		/* Read in whole structure to verify there's room. */
    342 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
    343 		    SUCCESS) {
    344 
    345 			/* Be consistent and just return EFAULT here. */
    346 			kmem_free(iget, iget_kmem_alloc_size);
    347 
    348 			return (EFAULT);
    349 		}
    350 	}
    351 
    352 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
    353 	iget->ino = ino;
    354 	iget->num_devs_ret = num_devs_ret;
    355 
    356 	imregp = ib_intr_map_reg_addr(ib_p, ino);
    357 	imregval = *imregp;
    358 
    359 	/*
    360 	 * Read "valid" bit.  If set, interrupts are enabled.
    361 	 * This bit happens to be the same on Fire and Tomatillo.
    362 	 */
    363 	if (imregval & COMMON_INTR_MAP_REG_VALID) {
    364 		/*
    365 		 * The following looks up the ib_ino_info and returns
    366 		 * info of devices mapped to this ino.
    367 		 */
    368 		iget->num_devs = ib_get_ino_devs(
    369 		    ib_p, ino, &iget->num_devs_ret, iget->dev);
    370 
    371 		if (ib_get_intr_target(pci_p, ino, &cpu_id) != DDI_SUCCESS) {
    372 			iget->status = PCITOOL_IO_ERROR;
    373 			rval = EIO;
    374 			goto done_get_intr;
    375 		}
    376 
    377 		/*
    378 		 * Consider only inos mapped to devices (as opposed to
    379 		 * inos mapped to the bridge itself.
    380 		 */
    381 		if (iget->num_devs > 0) {
    382 			/*
    383 			 * These 2 items are platform specific,
    384 			 * extracted from the bridge.
    385 			 */
    386 			iget->ctlr = 0;
    387 			iget->cpu_id = cpu_id;
    388 		}
    389 	}
    390 done_get_intr:
    391 	iget->drvr_version = PCITOOL_VERSION;
    392 	copyout_rval = ddi_copyout(iget, arg,
    393 	    PCITOOL_IGET_SIZE(num_devs_ret), mode);
    394 
    395 	if (iget_kmem_alloc_size > 0) {
    396 		kmem_free(iget, iget_kmem_alloc_size);
    397 	}
    398 
    399 	if (copyout_rval != DDI_SUCCESS) {
    400 		rval = EFAULT;
    401 	}
    402 
    403 	return (rval);
    404 }
    405 
    406 /*
    407  * Associate a new CPU with a given ino.
    408  *
    409  * Operate only on inos which are already mapped to devices.
    410  */
    411 static int
    412 pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
    413 {
    414 	ib_t *ib_p = pci_p->pci_ib_p;
    415 	int rval = SUCCESS;
    416 	int ret = DDI_SUCCESS;
    417 	uint8_t zero = 0;
    418 	pcitool_intr_set_t iset;
    419 	volatile uint64_t *imregp;
    420 	uint64_t imregval;
    421 
    422 	size_t copyinout_size;
    423 	int old_cpu_id;
    424 
    425 	bzero(&iset, sizeof (pcitool_intr_set_t));
    426 
    427 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
    428 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
    429 
    430 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
    431 		return (EFAULT);
    432 
    433 	switch (iset.user_version) {
    434 	case PCITOOL_V1:
    435 		break;
    436 
    437 	case PCITOOL_V2:
    438 		copyinout_size = sizeof (pcitool_intr_set_t);
    439 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
    440 			return (EFAULT);
    441 		break;
    442 
    443 	default:
    444 		iset.status = PCITOOL_OUT_OF_RANGE;
    445 		rval = ENOTSUP;
    446 		goto done_set_intr;
    447 	}
    448 
    449 	if ((iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) ||
    450 	    (iset.flags & PCITOOL_INTR_FLAG_SET_MSI)) {
    451 		iset.status = PCITOOL_IO_ERROR;
    452 		rval = ENOTSUP;
    453 		goto done_set_intr;
    454 	}
    455 
    456 	/* Validate input argument and that ino given belongs to a device. */
    457 	if ((iset.ino > PCI_MAX_INO) ||
    458 	    (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
    459 		iset.status = PCITOOL_INVALID_INO;
    460 		rval = EINVAL;
    461 		goto done_set_intr;
    462 	}
    463 
    464 	imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
    465 	imregval = *imregp;
    466 
    467 	/* Operate only on inos which are already enabled. */
    468 	if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
    469 		iset.status = PCITOOL_INVALID_INO;
    470 		rval = EINVAL;
    471 		goto done_set_intr;
    472 	}
    473 
    474 	if (ib_get_intr_target(pci_p, iset.ino, &old_cpu_id) != DDI_SUCCESS) {
    475 		iset.status = PCITOOL_INVALID_INO;
    476 		rval = EINVAL;
    477 		goto done_set_intr;
    478 	}
    479 
    480 	if ((ret = ib_set_intr_target(pci_p, iset.ino,
    481 	    iset.cpu_id)) == DDI_SUCCESS) {
    482 		iset.cpu_id = old_cpu_id;
    483 		iset.status = PCITOOL_SUCCESS;
    484 		goto done_set_intr;
    485 	}
    486 
    487 	switch (ret) {
    488 	case DDI_EPENDING:
    489 		iset.status = PCITOOL_PENDING_INTRTIMEOUT;
    490 		rval = ETIME;
    491 		break;
    492 	case DDI_EINVAL:
    493 		iset.status = PCITOOL_INVALID_CPUID;
    494 		rval = EINVAL;
    495 		break;
    496 	default:
    497 		iset.status = PCITOOL_INVALID_INO;
    498 		rval = EINVAL;
    499 		break;
    500 	}
    501 done_set_intr:
    502 	iset.drvr_version = PCITOOL_VERSION;
    503 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
    504 		rval = EFAULT;
    505 
    506 	return (rval);
    507 }
    508 
    509 
    510 /* Main function for handling interrupt CPU binding requests and queries. */
    511 int
    512 pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
    513 {
    514 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
    515 	dev_info_t	*dip = pci_p->pci_dip;
    516 	int		rval = SUCCESS;
    517 
    518 	switch (cmd) {
    519 
    520 	/* Get system interrupt information. */
    521 	case PCITOOL_SYSTEM_INTR_INFO:
    522 		rval = pcitool_intr_info(dip, arg, mode);
    523 		break;
    524 
    525 	/* Get interrupt information for a given ino. */
    526 	case PCITOOL_DEVICE_GET_INTR:
    527 		rval = pcitool_get_intr(dip, arg, mode, pci_p);
    528 		break;
    529 
    530 	/* Associate a new CPU with a given ino. */
    531 	case PCITOOL_DEVICE_SET_INTR:
    532 		rval = pcitool_set_intr(dip, arg, mode, pci_p);
    533 		break;
    534 
    535 	default:
    536 		rval = ENOTTY;
    537 	}
    538 
    539 	return (rval);
    540 }
    541 
    542 
    543 /*
    544  * Wrapper around pcitool_phys_peek/poke.
    545  *
    546  * Validates arguments and calls pcitool_phys_peek/poke appropriately.
    547  *
    548  * Dip is of the nexus,
    549  * phys_addr is the address to write in physical space,
    550  * max_addr is the upper bound on the physical space used for bounds checking,
    551  * pcitool_status returns more detailed status in addition to a more generic
    552  * errno-style function return value.
    553  * other args are self-explanatory.
    554  */
    555 static int
    556 pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
    557 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
    558 	uint32_t *pcitool_status)
    559 {
    560 
    561 	int rval = SUCCESS;
    562 	dev_info_t *dip = pci_p->pci_dip;
    563 
    564 	/* Upper bounds checking. */
    565 	if (phys_addr > max_addr) {
    566 		DEBUG2(DBG_TOOLS, dip,
    567 		    "Phys addr 0x%llx out of range (max 0x%llx).\n",
    568 		    phys_addr, max_addr);
    569 		*pcitool_status = PCITOOL_INVALID_ADDRESS;
    570 
    571 		rval = EINVAL;
    572 
    573 	/* Alignment checking. */
    574 	} else if (!IS_P2ALIGNED(phys_addr, size)) {
    575 		DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
    576 		*pcitool_status = PCITOOL_NOT_ALIGNED;
    577 
    578 		rval = EINVAL;
    579 
    580 	/* Made it through checks.  Do the access. */
    581 	} else if (write) {
    582 
    583 		DEBUG3(DBG_PHYS_ACC, dip,
    584 		    "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
    585 		    size, (endian ? "BE" : "LE"), phys_addr);
    586 
    587 		if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
    588 		    *data) != DDI_SUCCESS) {
    589 			DEBUG3(DBG_PHYS_ACC, dip,
    590 			    "%d byte %s pcitool_phys_poke at addr "
    591 			    "0x%llx failed\n",
    592 			    size, (endian ? "BE" : "LE"), phys_addr);
    593 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
    594 
    595 			rval = EFAULT;
    596 		}
    597 
    598 	} else {	/* Read */
    599 
    600 		DEBUG3(DBG_PHYS_ACC, dip,
    601 		    "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
    602 		    size, (endian ? "BE" : "LE"), phys_addr);
    603 
    604 		if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
    605 		    data) != DDI_SUCCESS) {
    606 			DEBUG3(DBG_PHYS_ACC, dip,
    607 			    "%d byte %s pcitool_phys_peek at addr "
    608 			    "0x%llx failed\n",
    609 			    size, (endian ? "BE" : "LE"), phys_addr);
    610 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
    611 
    612 			rval = EFAULT;
    613 		}
    614 	}
    615 	return (rval);
    616 }
    617 
    618 /*
    619  * Perform register accesses on the nexus device itself.
    620  */
    621 int
    622 pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
    623 {
    624 
    625 	pci_t			*pci_p = DEV_TO_SOFTSTATE(dev);
    626 	dev_info_t		*dip = pci_p->pci_dip;
    627 	pci_nexus_regspec_t	*pci_rp = NULL;
    628 	boolean_t		write_flag = B_FALSE;
    629 	pcitool_reg_t		prg;
    630 	uint64_t		base_addr;
    631 	uint64_t		max_addr;
    632 	uint32_t		reglen;
    633 	uint8_t			size;
    634 	uint32_t		rval = 0;
    635 
    636 	if (cmd == PCITOOL_NEXUS_SET_REG)
    637 		write_flag = B_TRUE;
    638 
    639 	DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
    640 
    641 	/* Read data from userland. */
    642 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
    643 	    DDI_SUCCESS) {
    644 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
    645 		return (EFAULT);
    646 	}
    647 
    648 	/* Read reg property which contains starting addr and size of banks. */
    649 	if (ddi_prop_lookup_int_array(
    650 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
    651 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
    652 		if (((reglen * sizeof (int)) %
    653 		    sizeof (pci_nexus_regspec_t)) != 0) {
    654 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
    655 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
    656 			rval = EIO;
    657 			goto done;
    658 		}
    659 	}
    660 
    661 	/* Bounds check the bank number. */
    662 	if (prg.barnum >=
    663 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
    664 		prg.status = PCITOOL_OUT_OF_RANGE;
    665 		rval = EINVAL;
    666 		goto done;
    667 	}
    668 
    669 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
    670 	base_addr = pci_rp[prg.barnum].phys_addr;
    671 	max_addr = base_addr + pci_rp[prg.barnum].size;
    672 	prg.phys_addr = base_addr + prg.offset;
    673 
    674 	DEBUG4(DBG_TOOLS, dip,
    675 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
    676 	    "addr:0x%llx, max_addr:0x%llx\n",
    677 	    base_addr, prg.offset, prg.phys_addr, max_addr);
    678 
    679 	/* Access device.  prg.status is modified. */
    680 	rval = pcitool_access(pci_p,
    681 	    prg.phys_addr, max_addr, &prg.data, size, write_flag,
    682 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
    683 
    684 done:
    685 	if (pci_rp != NULL)
    686 		ddi_prop_free(pci_rp);
    687 
    688 	prg.drvr_version = PCITOOL_VERSION;
    689 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
    690 	    DDI_SUCCESS) {
    691 		DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
    692 		return (EFAULT);
    693 	}
    694 
    695 	return (rval);
    696 }
    697 
    698 
    699 static int
    700 pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
    701 {
    702 	int rval = SUCCESS;
    703 
    704 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
    705 		prg->status = PCITOOL_OUT_OF_RANGE;
    706 		rval = EINVAL;
    707 
    708 	/* Validate address arguments of bus / dev / func */
    709 	} else if (((prg->bus_no &
    710 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
    711 	    ((prg->dev_no &
    712 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
    713 	    ((prg->func_no &
    714 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
    715 		prg->status = PCITOOL_INVALID_ADDRESS;
    716 		rval = EINVAL;
    717 	}
    718 
    719 	return (rval);
    720 }
    721 
    722 static int
    723 pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
    724 	uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
    725 {
    726 
    727 	uint8_t bar_offset;
    728 	int rval;
    729 	dev_info_t *dip = pci_p->pci_dip;
    730 
    731 	*bar = 0;
    732 	*is_io_space = B_FALSE;
    733 
    734 	/*
    735 	 * Translate BAR number into offset of the BAR in
    736 	 * the device's config space.
    737 	 */
    738 	bar_offset = PCI_BAR_OFFSET((*prg));
    739 
    740 	DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
    741 	    prg->barnum, bar_offset);
    742 
    743 	/*
    744 	 * Get Bus Address Register (BAR) from config space.
    745 	 * bar_offset is the offset into config space of the BAR desired.
    746 	 * prg->status is modified on error.
    747 	 */
    748 	rval = pcitool_access(pci_p, config_base_addr + bar_offset,
    749 	    config_max_addr, bar,
    750 	    4,		/* 4 bytes. */
    751 	    B_FALSE,	/* Read */
    752 	    B_FALSE, 	/* Little endian. */
    753 	    &prg->status);
    754 	if (rval != SUCCESS)
    755 		return (rval);
    756 
    757 	DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
    758 	if (!(*bar)) {
    759 		prg->status = PCITOOL_INVALID_ADDRESS;
    760 		return (EINVAL);
    761 	}
    762 
    763 	/*
    764 	 * BAR has bits saying this space is IO space, unless
    765 	 * this is the ROM address register.
    766 	 */
    767 	if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
    768 	    (bar_offset != PCI_CONF_ROM)) {
    769 		*is_io_space = B_TRUE;
    770 		*bar &= PCI_BASE_IO_ADDR_M;
    771 
    772 	/*
    773 	 * BAR has bits saying this space is 64 bit memory
    774 	 * space, unless this is the ROM address register.
    775 	 *
    776 	 * The 64 bit address stored in two BAR cells is not necessarily
    777 	 * aligned on an 8-byte boundary.  Need to keep the first 4
    778 	 * bytes read, and do a separate read of the high 4 bytes.
    779 	 */
    780 
    781 	} else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
    782 
    783 		uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
    784 
    785 		/* Don't try to read past the end of BARs. */
    786 		if (bar_offset >= PCI_CONF_BASE5) {
    787 			prg->status = PCITOOL_OUT_OF_RANGE;
    788 			return (EIO);
    789 		}
    790 
    791 		/* Access device.  prg->status is modified on error. */
    792 		rval = pcitool_access(pci_p,
    793 		    config_base_addr + bar_offset + 4, config_max_addr, bar,
    794 		    4,		/* 4 bytes. */
    795 		    B_FALSE,	/* Read */
    796 		    B_FALSE, 	/* Little endian. */
    797 		    &prg->status);
    798 		if (rval != SUCCESS)
    799 			return (rval);
    800 
    801 		*bar = (*bar << 32) + low_bytes;
    802 	}
    803 
    804 	return (SUCCESS);
    805 }
    806 
    807 
    808 static int
    809 pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
    810 	uint64_t max_addr, uint8_t size, boolean_t write_flag)
    811 {
    812 	int rval;
    813 	dev_info_t *dip = pci_p->pci_dip;
    814 
    815 	/* Access config space and we're done. */
    816 	prg->phys_addr = base_addr + prg->offset;
    817 
    818 	DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
    819 	    "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
    820 	    base_addr, prg->offset, prg->phys_addr,
    821 	    (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
    822 
    823 	/* Access device.  pr.status is modified. */
    824 	rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
    825 	    write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
    826 
    827 	DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
    828 
    829 	return (rval);
    830 }
    831 
    832 /* Perform register accesses on PCI leaf devices. */
    833 int
    834 pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
    835 {
    836 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
    837 	dev_info_t	*dip = pci_p->pci_dip;
    838 	pci_ranges_t	*rp = pci_p->pci_ranges;
    839 	pcitool_reg_t	prg;
    840 	uint64_t	max_addr;
    841 	uint64_t	base_addr;
    842 	uint64_t	range_prop;
    843 	uint64_t	range_prop_size;
    844 	uint64_t	bar = 0;
    845 	int		rval = 0;
    846 	boolean_t	write_flag = B_FALSE;
    847 	boolean_t	is_io_space = B_FALSE;
    848 	uint8_t		size;
    849 
    850 	if (cmd == PCITOOL_DEVICE_SET_REG)
    851 		write_flag = B_TRUE;
    852 
    853 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
    854 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
    855 	    DDI_SUCCESS) {
    856 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
    857 		return (EFAULT);
    858 	}
    859 
    860 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
    861 	    prg.bus_no, prg.dev_no, prg.func_no);
    862 
    863 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
    864 		goto done_reg;
    865 
    866 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
    867 
    868 	/* Get config space first. */
    869 	range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
    870 	range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
    871 	max_addr = range_prop + range_prop_size;
    872 
    873 	/*
    874 	 * Build device address based on base addr from range prop, and bus,
    875 	 * dev and func values passed in.  This address is where config space
    876 	 * begins.
    877 	 */
    878 	base_addr = range_prop +
    879 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
    880 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
    881 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
    882 
    883 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
    884 		prg.status = PCITOOL_OUT_OF_RANGE;
    885 		rval = EINVAL;
    886 		goto done_reg;
    887 	}
    888 
    889 	DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
    890 	    "func:0x%x, addr:0x%x\n", range_prop,
    891 	    prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
    892 	    prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
    893 
    894 	/* Proper config space desired. */
    895 	if (prg.barnum == 0) {
    896 
    897 		rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
    898 		    size, write_flag);
    899 
    900 	} else {	/* IO / MEM / MEM64 space. */
    901 
    902 		if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
    903 		    &is_io_space) != SUCCESS)
    904 			goto done_reg;
    905 
    906 		/* IO space. */
    907 		if (is_io_space) {
    908 
    909 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
    910 
    911 			/* Reposition to focus on IO space. */
    912 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
    913 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
    914 			    PCI_IO_RANGE_BANK);
    915 
    916 		/* 64 bit memory space. */
    917 		} else if ((bar >> 32) != 0) {
    918 
    919 			DEBUG1(DBG_TOOLS, dip,
    920 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
    921 
    922 			/* Reposition to MEM64 range space. */
    923 			range_prop = PCI_GET_RANGE_PROP(rp,
    924 			    PCI_MEM64_RANGE_BANK);
    925 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
    926 			    PCI_MEM64_RANGE_BANK);
    927 
    928 		} else {	/* Mem32 space, including ROM */
    929 
    930 			DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
    931 
    932 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
    933 
    934 				DEBUG0(DBG_TOOLS, dip,
    935 				    "Additional ROM checking\n");
    936 
    937 				/* Can't write to ROM */
    938 				if (write_flag) {
    939 					prg.status = PCITOOL_ROM_WRITE;
    940 					rval = EIO;
    941 					goto done_reg;
    942 
    943 				/* ROM disabled for reading */
    944 				} else if (!(bar & 0x00000001)) {
    945 					prg.status = PCITOOL_ROM_DISABLED;
    946 					rval = EIO;
    947 					goto done_reg;
    948 				}
    949 			}
    950 
    951 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
    952 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
    953 			    PCI_MEM_RANGE_BANK);
    954 		}
    955 
    956 		/* Common code for all IO/MEM range spaces. */
    957 		max_addr = range_prop + range_prop_size;
    958 		base_addr = range_prop + bar;
    959 
    960 		DEBUG3(DBG_TOOLS, dip,
    961 		    "addr portion of bar is 0x%llx, base=0x%llx, "
    962 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
    963 
    964 		/*
    965 		 * Use offset provided by caller to index into
    966 		 * desired space, then access.
    967 		 * Note that prg.status is modified on error.
    968 		 */
    969 		prg.phys_addr = base_addr + prg.offset;
    970 		rval = pcitool_access(pci_p, prg.phys_addr,
    971 		    max_addr, &prg.data, size, write_flag,
    972 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
    973 	}
    974 
    975 done_reg:
    976 	prg.drvr_version = PCITOOL_VERSION;
    977 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
    978 	    DDI_SUCCESS) {
    979 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
    980 		rval = EFAULT;
    981 	}
    982 	return (rval);
    983 }
    984 
    985 int
    986 pcitool_init(dev_info_t *dip)
    987 {
    988 	int instance = ddi_get_instance(dip);
    989 
    990 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
    991 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
    992 	    DDI_NT_REGACC, 0) != DDI_SUCCESS)
    993 		return (DDI_FAILURE);
    994 
    995 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
    996 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
    997 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
    998 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
    999 		return (DDI_FAILURE);
   1000 	}
   1001 
   1002 	return (DDI_SUCCESS);
   1003 }
   1004 
   1005 void
   1006 pcitool_uninit(dev_info_t *dip)
   1007 {
   1008 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
   1009 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
   1010 }
   1011