Home | History | Annotate | Download | only in px
      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/types.h>
     27 #include <sys/stat.h>
     28 #include <sys/cpuvar.h>
     29 #include <sys/kmem.h>
     30 #include <sys/sunddi.h>
     31 #include "px_obj.h"
     32 #include <sys/pci_tools.h>
     33 #include "px_tools_ext.h"
     34 #include "px_tools_var.h"
     35 
     36 /*
     37  * PCI Space definitions.
     38  */
     39 #define	PCI_CONFIG_SPACE	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
     40 #define	PCI_IO_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_IO))
     41 #define	PCI_MEM32_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
     42 #define	PCI_MEM64_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
     43 
     44 /*
     45  * Config space range for a device.  IEEE 1275 spec defines for PCI.
     46  * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA
     47  */
     48 #define	DEV_CFG_SPACE_SIZE	\
     49 	(1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA))
     50 
     51 /*
     52  * Offsets of BARS in config space.  First entry of 0 means config space.
     53  * Entries here correlate to pcitool_bars_t enumerated type.
     54  */
     55 uint8_t pci_bars[] = {
     56 	0x0,
     57 	PCI_CONF_BASE0,
     58 	PCI_CONF_BASE1,
     59 	PCI_CONF_BASE2,
     60 	PCI_CONF_BASE3,
     61 	PCI_CONF_BASE4,
     62 	PCI_CONF_BASE5,
     63 	PCI_CONF_ROM
     64 };
     65 
     66 int	pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]);
     67 
     68 
     69 /*ARGSUSED*/
     70 static int
     71 pxtool_intr_info(dev_info_t *dip, void *arg, int mode)
     72 {
     73 	px_t *px_p = DIP_TO_STATE(dip);
     74 	px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
     75 	pcitool_intr_info_t intr_info;
     76 	int rval = SUCCESS;
     77 
     78 	/* If we need user_version, and to ret same user version as passed in */
     79 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
     80 	    DDI_SUCCESS) {
     81 		return (EFAULT);
     82 	}
     83 
     84 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
     85 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
     86 	if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
     87 		intr_info.num_intr = msi_state_p->msi_cnt;
     88 	else
     89 		intr_info.num_intr = pxtool_num_inos;
     90 
     91 	intr_info.drvr_version = PCITOOL_VERSION;
     92 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
     93 	    DDI_SUCCESS) {
     94 		rval = EFAULT;
     95 	}
     96 
     97 	return (rval);
     98 }
     99 
    100 
    101 /*
    102  * Get interrupt information for a given ino.
    103  * Returns info only for inos mapped to devices.
    104  *
    105  * Returned info is valid only when iget.num_devs is returned > 0.
    106  * If ino is not enabled or is not mapped to a device,
    107  * iget.num_devs will be returned as = 0.
    108  */
    109 /*ARGSUSED*/
    110 static int
    111 pxtool_get_intr(dev_info_t *dip, void *arg, int mode)
    112 {
    113 	/* Array part isn't used here, but oh well... */
    114 	pcitool_intr_get_t partial_iget;
    115 	pcitool_intr_get_t *iget = &partial_iget;
    116 	int copyout_rval;
    117 	sysino_t sysino;
    118 	intr_valid_state_t intr_valid_state;
    119 	cpuid_t old_cpu_id;
    120 	px_t *px_p = DIP_TO_STATE(dip);
    121 	size_t	iget_kmem_alloc_size = 0;
    122 	int rval = EIO;
    123 
    124 	/* Read in just the header part, no array section. */
    125 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
    126 	    DDI_SUCCESS)
    127 		return (EFAULT);
    128 
    129 	iget->status = PCITOOL_IO_ERROR;
    130 
    131 	if (iget->flags & PCITOOL_INTR_FLAG_GET_MSI) {
    132 		px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
    133 		pci_msi_valid_state_t	msi_state;
    134 		msiqid_t	msiq_id;
    135 
    136 		if ((iget->msi < msi_state_p->msi_1st_msinum) ||
    137 		    (iget->msi >= (msi_state_p->msi_1st_msinum +
    138 		    msi_state_p->msi_cnt))) {
    139 			iget->status = PCITOOL_INVALID_MSI;
    140 			rval = EINVAL;
    141 			goto done_get_intr;
    142 		}
    143 
    144 		if ((px_lib_msi_getvalid(dip, iget->msi,
    145 		    &msi_state) != DDI_SUCCESS) ||
    146 		    (msi_state != PCI_MSI_VALID))
    147 			goto done_get_intr;
    148 
    149 		if (px_lib_msi_getmsiq(dip, iget->msi,
    150 		    &msiq_id) != DDI_SUCCESS)
    151 			goto done_get_intr;
    152 
    153 		iget->ino = px_msiqid_to_devino(px_p, msiq_id);
    154 	} else {
    155 		iget->msi = (uint32_t)-1;
    156 	}
    157 
    158 	/* Validate argument. */
    159 	if (iget->ino > pxtool_num_inos) {
    160 		iget->status = PCITOOL_INVALID_INO;
    161 		rval = EINVAL;
    162 		goto done_get_intr;
    163 	}
    164 
    165 	/* Caller wants device information returned. */
    166 	if (iget->num_devs_ret > 0) {
    167 		/*
    168 		 * Allocate room.
    169 		 * Note if num_devs == 0 iget remains pointing to
    170 		 * partial_iget.
    171 		 */
    172 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget->num_devs_ret);
    173 		iget = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP);
    174 
    175 		/* Read in whole structure to verify there's room. */
    176 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
    177 		    SUCCESS) {
    178 
    179 			/* Be consistent and just return EFAULT here. */
    180 			kmem_free(iget, iget_kmem_alloc_size);
    181 
    182 			return (EFAULT);
    183 		}
    184 	}
    185 
    186 	/* Convert leaf-wide intr to system-wide intr */
    187 	if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) !=
    188 	    DDI_SUCCESS) {
    189 		iget->status = PCITOOL_IO_ERROR;
    190 		rval = EIO;
    191 		goto done_get_intr;
    192 	}
    193 
    194 	/* Operate only on inos which are already enabled. */
    195 	if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
    196 	    DDI_SUCCESS) {
    197 		iget->status = PCITOOL_IO_ERROR;
    198 		rval = EIO;
    199 		goto done_get_intr;
    200 	}
    201 
    202 	/*
    203 	 * Consider all valid inos: those mapped to the root complex itself
    204 	 * as well as those mapped to devices.
    205 	 */
    206 	if (intr_valid_state == INTR_VALID) {
    207 		/*
    208 		 * The following looks up the px_ino and returns
    209 		 * info of devices mapped to this ino.
    210 		 */
    211 		iget->num_devs = pxtool_ib_get_ino_devs(px_p, iget->ino,
    212 		    iget->msi, &iget->num_devs_ret, iget->dev);
    213 
    214 		if (px_ib_get_intr_target(px_p, iget->ino,
    215 		    &old_cpu_id) != DDI_SUCCESS) {
    216 			iget->status = PCITOOL_IO_ERROR;
    217 			rval = EIO;
    218 			goto done_get_intr;
    219 		}
    220 
    221 		iget->cpu_id = old_cpu_id;
    222 	}
    223 
    224 	iget->status = PCITOOL_SUCCESS;
    225 	rval = SUCCESS;
    226 
    227 done_get_intr:
    228 	iget->drvr_version = PCITOOL_VERSION;
    229 	copyout_rval =
    230 	    ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(iget->num_devs_ret), mode);
    231 
    232 	if (iget_kmem_alloc_size > 0)
    233 		kmem_free(iget, iget_kmem_alloc_size);
    234 
    235 	if (copyout_rval != DDI_SUCCESS)
    236 		rval = EFAULT;
    237 
    238 	return (rval);
    239 }
    240 
    241 
    242 /*
    243  * Associate a new CPU with a given ino.
    244  *
    245  * Operate only on inos which are already mapped to devices.
    246  */
    247 static int
    248 pxtool_set_intr(dev_info_t *dip, void *arg, int mode)
    249 {
    250 	pcitool_intr_set_t iset;
    251 	cpuid_t old_cpu_id;
    252 	sysino_t sysino;
    253 	intr_valid_state_t intr_valid_state;
    254 	px_t *px_p = DIP_TO_STATE(dip);
    255 	msiqid_t msiq_id;
    256 	int rval = EIO;
    257 	int ret = DDI_SUCCESS;
    258 	size_t copyinout_size;
    259 
    260 	bzero(&iset, sizeof (pcitool_intr_set_t));
    261 
    262 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
    263 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
    264 
    265 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
    266 		return (EFAULT);
    267 
    268 	switch (iset.user_version) {
    269 	case PCITOOL_V1:
    270 		break;
    271 
    272 	case PCITOOL_V2:
    273 		copyinout_size = sizeof (pcitool_intr_set_t);
    274 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
    275 			return (EFAULT);
    276 		break;
    277 
    278 	default:
    279 		iset.status = PCITOOL_OUT_OF_RANGE;
    280 		rval = ENOTSUP;
    281 		goto done_set_intr;
    282 	}
    283 
    284 	if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) {
    285 		iset.status = PCITOOL_IO_ERROR;
    286 		rval = ENOTSUP;
    287 		goto done_set_intr;
    288 	}
    289 
    290 	iset.status = PCITOOL_IO_ERROR;
    291 
    292 	if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
    293 		px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
    294 		pci_msi_valid_state_t	msi_state;
    295 
    296 		if ((iset.msi < msi_state_p->msi_1st_msinum) ||
    297 		    (iset.msi >= (msi_state_p->msi_1st_msinum +
    298 		    msi_state_p->msi_cnt))) {
    299 			iset.status = PCITOOL_INVALID_MSI;
    300 			rval = EINVAL;
    301 			goto done_set_intr;
    302 		}
    303 
    304 		if ((px_lib_msi_getvalid(dip, iset.msi,
    305 		    &msi_state) != DDI_SUCCESS) ||
    306 		    (msi_state != PCI_MSI_VALID))
    307 			goto done_set_intr;
    308 
    309 		if (px_lib_msi_getmsiq(dip, iset.msi,
    310 		    &msiq_id) != DDI_SUCCESS)
    311 			goto done_set_intr;
    312 
    313 		iset.ino = px_msiqid_to_devino(px_p, msiq_id);
    314 	} else {
    315 		iset.msi = (uint32_t)-1;
    316 	}
    317 
    318 	/* Validate input argument. */
    319 	if (iset.ino > pxtool_num_inos) {
    320 		iset.status = PCITOOL_INVALID_INO;
    321 		rval = EINVAL;
    322 		goto done_set_intr;
    323 	}
    324 
    325 	/* Convert leaf-wide intr to system-wide intr */
    326 	if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) !=
    327 	    DDI_SUCCESS)
    328 		goto done_set_intr;
    329 
    330 	/* Operate only on inos which are already enabled. */
    331 	if ((px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
    332 	    DDI_SUCCESS) || (intr_valid_state == INTR_NOTVALID))
    333 		goto done_set_intr;
    334 
    335 	/*
    336 	 * Consider all valid inos: those mapped to the root complex itself
    337 	 * as well as those mapped to devices.
    338 	 */
    339 	if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS)
    340 		goto done_set_intr;
    341 
    342 	if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
    343 		ddi_intr_handle_impl_t	hdle;
    344 
    345 		bzero(&hdle, sizeof (ddi_intr_handle_impl_t));
    346 		if (pxtool_ib_get_msi_info(px_p, iset.ino, iset.msi,
    347 		    &hdle) != DDI_SUCCESS) {
    348 			iset.status = PCITOOL_INVALID_MSI;
    349 			rval = EINVAL;
    350 			goto done_set_intr;
    351 		}
    352 
    353 		if ((ret = px_ib_set_msix_target(px_p, &hdle, iset.msi,
    354 		    iset.cpu_id)) == DDI_SUCCESS) {
    355 			(void) px_lib_msi_getmsiq(dip, iset.msi, &msiq_id);
    356 			iset.ino = px_msiqid_to_devino(px_p, msiq_id);
    357 			iset.cpu_id = old_cpu_id;
    358 			iset.status = PCITOOL_SUCCESS;
    359 			rval = SUCCESS;
    360 			goto done_set_intr;
    361 		}
    362 	} else {
    363 		if ((ret = px_ib_set_intr_target(px_p, iset.ino,
    364 		    iset.cpu_id)) == DDI_SUCCESS) {
    365 			iset.cpu_id = old_cpu_id;
    366 			iset.status = PCITOOL_SUCCESS;
    367 			rval = SUCCESS;
    368 			goto done_set_intr;
    369 		}
    370 	}
    371 
    372 	switch (ret) {
    373 	case DDI_EPENDING:
    374 		iset.status = PCITOOL_PENDING_INTRTIMEOUT;
    375 		rval = ETIME;
    376 		break;
    377 	case DDI_EINVAL:
    378 		iset.status = PCITOOL_INVALID_CPUID;
    379 		rval = EINVAL;
    380 		break;
    381 	default:
    382 		iset.status = PCITOOL_IO_ERROR;
    383 		rval = EIO;
    384 		break;
    385 	}
    386 
    387 done_set_intr:
    388 	iset.drvr_version = PCITOOL_VERSION;
    389 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
    390 		rval = EFAULT;
    391 
    392 	return (rval);
    393 }
    394 
    395 
    396 /* Main function for handling interrupt CPU binding requests and queries. */
    397 int
    398 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
    399 {
    400 	int rval = SUCCESS;
    401 
    402 	switch (cmd) {
    403 
    404 	/* Get system interrupt information. */
    405 	case PCITOOL_SYSTEM_INTR_INFO:
    406 		rval = pxtool_intr_info(dip, arg, mode);
    407 		break;
    408 
    409 	/* Get interrupt information for a given ino. */
    410 	case PCITOOL_DEVICE_GET_INTR:
    411 		rval = pxtool_get_intr(dip, arg, mode);
    412 		break;
    413 
    414 	/* Associate a new CPU with a given ino. */
    415 	case PCITOOL_DEVICE_SET_INTR:
    416 		rval = pxtool_set_intr(dip, arg, mode);
    417 		break;
    418 
    419 	default:
    420 		rval = ENOTTY;
    421 	}
    422 
    423 	return (rval);
    424 }
    425 
    426 
    427 static int
    428 pxtool_validate_barnum_bdf(pcitool_reg_t *prg)
    429 {
    430 	int rval = SUCCESS;
    431 
    432 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
    433 		prg->status = PCITOOL_OUT_OF_RANGE;
    434 		rval = EINVAL;
    435 
    436 	/* Validate address arguments of bus / dev / func */
    437 	} else if (((prg->bus_no &
    438 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
    439 	    ((prg->dev_no &
    440 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
    441 	    ((prg->func_no &
    442 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
    443 		prg->status = PCITOOL_INVALID_ADDRESS;
    444 		rval = EINVAL;
    445 	}
    446 
    447 	return (rval);
    448 }
    449 
    450 /*
    451  * px_p defines which leaf, space defines which space in that leaf, offset
    452  * defines the offset within the specified space.
    453  *
    454  * This returns the physical address of the corresponding location.
    455  */
    456 static uintptr_t
    457 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset)
    458 {
    459 	uint64_t range_base;
    460 	int rval;
    461 	pci_regspec_t dev_regspec;
    462 	struct regspec xlated_regspec;
    463 	dev_info_t *dip = px_p->px_dip;
    464 
    465 	/*
    466 	 * Assume that requested entity is small enough to be on the same page.
    467 	 * PCItool checks alignment so that this will be true for single
    468 	 * accesses.
    469 	 */
    470 	dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT;
    471 	if (space == PCI_CONFIG_SPACE) {
    472 		dev_regspec.pci_phys_hi +=
    473 		    (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M));
    474 		dev_regspec.pci_phys_low = offset & PCI_REG_REG_M;
    475 		dev_regspec.pci_phys_mid = 0;	/* Not used */
    476 	} else {
    477 		dev_regspec.pci_phys_mid = offset >> 32;
    478 		dev_regspec.pci_phys_low = offset & 0xffffffff;
    479 	}
    480 	dev_regspec.pci_size_hi = 0;	/* Not used. */
    481 
    482 	/* Note: object is guaranteed to be within a page. */
    483 	dev_regspec.pci_size_low = 4;
    484 
    485 	rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec);
    486 
    487 	DBG(DBG_TOOLS, dip,
    488 	    "space:0x%d, offset:0x%" PRIx64 "\n", space, offset);
    489 
    490 	if (rval != DDI_SUCCESS)
    491 		return (NULL);
    492 
    493 	/* Bustype here returns the high order address bits. */
    494 	xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p);
    495 
    496 	range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) +
    497 	    xlated_regspec.regspec_addr;
    498 	DBG(DBG_TOOLS, dip,
    499 	    "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n",
    500 	    xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr,
    501 	    xlated_regspec.regspec_size, range_base);
    502 
    503 	return ((uintptr_t)range_base);
    504 }
    505 
    506 
    507 static int
    508 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p,
    509     uint32_t *space_p)
    510 {
    511 	int rval;
    512 	uint64_t off_in_space;
    513 	pcitool_reg_t cfg_prg = *prg_p;	/* Make local copy. */
    514 	dev_info_t *dip = px_p->px_dip;
    515 
    516 	*space_p = PCI_MEM32_SPACE;
    517 	*bar_p = 0;
    518 
    519 	/*
    520 	 * Translate BAR number into offset of the BAR in
    521 	 * the device's config space.
    522 	 */
    523 	cfg_prg.acc_attr =
    524 	    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
    525 
    526 	/*
    527 	 * Note: sun4u acc function uses phys_addr which includes offset.
    528 	 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset.
    529 	 */
    530 	cfg_prg.offset = PCI_BAR_OFFSET((*prg_p));
    531 	off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset;
    532 	cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE,
    533 	    off_in_space);
    534 
    535 	DBG(DBG_TOOLS, dip,
    536 	    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n",
    537 	    off_in_space, cfg_prg.phys_addr, cfg_prg.barnum);
    538 
    539 	/*
    540 	 * Get Bus Address Register (BAR) from config space.
    541 	 * cfg_prg.offset is the offset into config space of the
    542 	 * BAR desired.  prg_p->status is modified on error.
    543 	 */
    544 	rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
    545 
    546 	if (rval != SUCCESS) {
    547 		prg_p->status = cfg_prg.status;
    548 		return (rval);
    549 	}
    550 
    551 	DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p);
    552 
    553 	/*
    554 	 * BAR has bits saying this space is IO space, unless
    555 	 * this is the ROM address register.
    556 	 */
    557 	if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) &&
    558 	    (cfg_prg.offset != PCI_CONF_ROM)) {
    559 		*space_p = PCI_IO_SPACE;
    560 		*bar_p &= PCI_BASE_IO_ADDR_M;
    561 
    562 	/*
    563 	 * BAR has bits saying this space is 64 bit memory
    564 	 * space, unless this is the ROM address register.
    565 	 *
    566 	 * The 64 bit address stored in two BAR cells is not
    567 	 * necessarily aligned on an 8-byte boundary.
    568 	 * Need to keep the first 4 bytes read,
    569 	 * and do a separate read of the high 4 bytes.
    570 	 */
    571 	} else if ((PCI_BASE_TYPE_ALL & *bar_p) &&
    572 	    (cfg_prg.offset != PCI_CONF_ROM)) {
    573 
    574 		uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL);
    575 
    576 		/* Don't try to read the next 4 bytes past the end of BARs. */
    577 		if (cfg_prg.offset >= PCI_CONF_BASE5) {
    578 			prg_p->status = PCITOOL_OUT_OF_RANGE;
    579 			return (EIO);
    580 		}
    581 
    582 		/* Access device.  prg_p->status is modified on error. */
    583 		cfg_prg.phys_addr += sizeof (uint32_t);
    584 		cfg_prg.offset += sizeof (uint32_t);
    585 
    586 		rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
    587 		if (rval != SUCCESS) {
    588 			prg_p->status = cfg_prg.status;
    589 			return (rval);
    590 		}
    591 
    592 		/*
    593 		 * Honor the 64 bit BAR as such, only when the upper 32 bits
    594 		 * store a non-zero value.
    595 		 */
    596 		if (*bar_p) {
    597 			*space_p = PCI_MEM64_SPACE;
    598 			*bar_p = (*bar_p << 32) | low_bytes;
    599 		} else
    600 			*bar_p = low_bytes;
    601 
    602 	} else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */
    603 
    604 		/*
    605 		 * ROM enabled. Filter ROM enable bit from the BAR.
    606 		 * Treat as Mem32 henceforth.
    607 		 */
    608 		if (!(*bar_p & PCI_BASE_ROM_ENABLE))
    609 			*bar_p ^= PCI_BASE_ROM_ENABLE;
    610 
    611 		else {	/* ROM disabled. */
    612 			prg_p->status = PCITOOL_ROM_DISABLED;
    613 			return (EIO);
    614 		}
    615 	}
    616 
    617 	/* Accept a bar of 0 only for IO space. */
    618 	if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) {
    619 		prg_p->status = PCITOOL_INVALID_ADDRESS;
    620 		return (EINVAL);
    621 	}
    622 
    623 	return (SUCCESS);
    624 }
    625 
    626 
    627 /* Perform register accesses on PCI leaf devices. */
    628 int
    629 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
    630 {
    631 	pcitool_reg_t	prg;
    632 	uint64_t	bar;
    633 	uint32_t	space;
    634 	uint64_t	off_in_space;
    635 	boolean_t	write_flag = B_FALSE;
    636 	px_t		*px_p = DIP_TO_STATE(dip);
    637 	int		rval = 0;
    638 
    639 	if (cmd == PCITOOL_DEVICE_SET_REG)
    640 		write_flag = B_TRUE;
    641 
    642 	DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n");
    643 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
    644 	    mode) != DDI_SUCCESS) {
    645 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
    646 		return (EFAULT);
    647 	}
    648 
    649 	if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) {
    650 		goto done_reg;
    651 	}
    652 
    653 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
    654 	    prg.bus_no, prg.dev_no, prg.func_no);
    655 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
    656 	    prg.barnum, prg.offset, prg.acc_attr);
    657 
    658 	if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS)
    659 		goto done_reg;
    660 
    661 	if (prg.barnum == 0) {	/* Proper config space desired. */
    662 
    663 		/* Enforce offset limits. */
    664 		if (prg.offset >= DEV_CFG_SPACE_SIZE) {
    665 			DBG(DBG_TOOLS, dip,
    666 			    "Config space offset 0x%" PRIx64 " out of range\n",
    667 			    prg.offset);
    668 			prg.status = PCITOOL_OUT_OF_RANGE;
    669 			rval = EINVAL;
    670 			goto done_reg;
    671 		}
    672 
    673 		/*
    674 		 * For sun4v, config space base won't be known.
    675 		 * pxtool_get_phys_addr will return zero.
    676 		 * Note that for sun4v, phys_addr isn't
    677 		 * used for making config space accesses.
    678 		 *
    679 		 * For sun4u, assume that phys_addr will come back valid.
    680 		 */
    681 		/*
    682 		 * Accessed entity is assumed small enough to be on one page.
    683 		 *
    684 		 * Since config space is less than a page and is aligned to
    685 		 * 0x1000, a device's entire config space will be on a single
    686 		 * page.  Pass the device's base config space address here,
    687 		 * then add the offset within that space later.  This works
    688 		 * around an issue in px_xlate_reg (called by
    689 		 * pxtool_get_phys_addr) which accepts only a 256 byte
    690 		 * range within a device.
    691 		 */
    692 		off_in_space = PX_GET_BDF(&prg);
    693 		prg.phys_addr =
    694 		    pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space);
    695 		prg.phys_addr += prg.offset;
    696 
    697 		DBG(DBG_TOOLS, dip,
    698 		    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", "
    699 		    "end:%s\n", off_in_space, prg.phys_addr,
    700 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl");
    701 
    702 		/*
    703 		 * Access device.  pr.status is modified.
    704 		 * BDF is assumed valid at this point.
    705 		 */
    706 		rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag);
    707 		goto done_reg;
    708 	}
    709 
    710 	/* IO/ MEM/ MEM64 space. */
    711 
    712 	if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS)
    713 		goto done_reg;
    714 
    715 	switch (space) {
    716 	case PCI_MEM32_SPACE:
    717 
    718 		DBG(DBG_TOOLS, dip, "32 bit mem space\n");
    719 
    720 		/* Can't write to ROM */
    721 		if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) {
    722 			prg.status = PCITOOL_ROM_WRITE;
    723 			rval = EIO;
    724 			goto done_reg;
    725 		}
    726 		break;
    727 
    728 	case PCI_IO_SPACE:
    729 		DBG(DBG_TOOLS, dip, "IO space\n");
    730 		break;
    731 
    732 	case PCI_MEM64_SPACE:
    733 		DBG(DBG_TOOLS, dip,
    734 		    "64 bit mem space.  64-bit bar is 0x%" PRIx64 "\n", bar);
    735 		break;
    736 
    737 	default:
    738 		DBG(DBG_TOOLS, dip, "Unknown space!\n");
    739 		prg.status = PCITOOL_IO_ERROR;
    740 		rval = EIO;
    741 		goto done_reg;
    742 	}
    743 
    744 	/*
    745 	 * Common code for all IO/MEM range spaces.
    746 	 *
    747 	 * Use offset provided by caller to index into desired space.
    748 	 * Note that prg.status is modified on error.
    749 	 */
    750 	off_in_space = bar + prg.offset;
    751 	prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space);
    752 
    753 	DBG(DBG_TOOLS, dip,
    754 	    "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", "
    755 	    "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr);
    756 
    757 	rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag);
    758 
    759 done_reg:
    760 	prg.drvr_version = PCITOOL_VERSION;
    761 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
    762 	    mode) != DDI_SUCCESS) {
    763 		DBG(DBG_TOOLS, dip, "Error returning arguments.\n");
    764 		rval = EFAULT;
    765 	}
    766 	return (rval);
    767 }
    768 
    769 
    770 int
    771 pxtool_init(dev_info_t *dip)
    772 {
    773 	int instance = ddi_get_instance(dip);
    774 
    775 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
    776 	    PCI_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
    777 	    DDI_NT_REGACC, 0) != DDI_SUCCESS) {
    778 		return (DDI_FAILURE);
    779 	}
    780 
    781 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
    782 	    PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
    783 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
    784 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
    785 		return (DDI_FAILURE);
    786 	}
    787 
    788 	return (DDI_SUCCESS);
    789 }
    790 
    791 
    792 void
    793 pxtool_uninit(dev_info_t *dip)
    794 {
    795 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
    796 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
    797 }
    798