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 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <sys/sysmacros.h>
     29 #include <sys/machsystm.h>
     30 #include <sys/cpuvar.h>
     31 #include <sys/ddi_implfuncs.h>
     32 #include <px_csr.h>
     33 #include <px_regs.h>
     34 #include <px_obj.h>
     35 #include <sys/pci_tools.h>
     36 #include <px_tools_var.h>
     37 #include <px_asm_4u.h>
     38 #include <px_lib4u.h>
     39 #include <px_tools_ext.h>
     40 
     41 /*
     42  * Delay needed to have a safe environment envelop any error which could
     43  * surface.  The larger the number of bridges and switches, the larger the
     44  * number needed here.
     45  *
     46  * The way it works is as follows:
     47  *
     48  * An access is done which causes an error.  Fire errors are handled with
     49  * ontrap protection and usually come in first.  Fabric errors can come in
     50  * later.
     51  *
     52  * px_phys_peek_4u() disables interrupts.  Interrupts are reenabled at the end
     53  * of that function if no errors have been caught by the trap handler, or by
     54  * peek_fault() which executes when a fire error occurs.
     55  *
     56  * Fabric error messages get put on an event queue but are not processed until
     57  * interrupts are reenabled.
     58  *
     59  * The delay gives time for the fabric errors to be processed by FMA before
     60  * changing the fm error flag back to DDI_FM_ERR_UNEXPECTED.  If this isn't
     61  * done, then the fabric error which should be safe can panic the system.
     62  *
     63  * Note: this is a workaround until a better solution is found.  While this
     64  * number is high, given enough bridges and switches in the device path, this
     65  * workaround can break.  Also, other PIL 15 interrupts besides the ones we are
     66  * enveloping could delay processing of the interrupt we are trying to protect.
     67  */
     68 
     69 /*
     70  * Set delay to 10 ms
     71  */
     72 int pxtool_delay_usec = 10000;
     73 
     74 /* Number of inos per root complex. */
     75 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
     76 
     77 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
     78 typedef union {
     79 	uint64_t u64;
     80 	uint32_t u32;
     81 	uint16_t u16;
     82 	uint8_t u8;
     83 } peek_poke_value_t;
     84 
     85 /*
     86  * Safe C wrapper around assy language routine px_phys_peek_4u
     87  *
     88  * Type is TRUE for big endian, FALSE for little endian.
     89  * Size is 1, 2, 4 or 8 bytes.
     90  * paddr is the physical address in IO space to access read.
     91  * value_p is where the value is returned.
     92  */
     93 static int
     94 pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
     95     uint64_t *value_p)
     96 {
     97 	px_pec_t *pec_p = px_p->px_pec_p;
     98 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
     99 	on_trap_data_t otd;
    100 	peek_poke_value_t peek_value;
    101 	int err = DDI_SUCCESS;
    102 
    103 	mutex_enter(&pec_p->pec_pokefault_mutex);
    104 	pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
    105 
    106 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
    107 
    108 	/*
    109 	 * Set up trap handling to make the access safe.
    110 	 *
    111 	 * on_trap works like setjmp.
    112 	 * Set it up to not panic on data access error,
    113 	 * but to call peek_fault instead.
    114 	 * Call px_phys_peek_4u after trap handling is setup.
    115 	 * When on_trap returns FALSE, it has been setup.
    116 	 * When it returns TRUE, an it has caught an error.
    117 	 */
    118 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
    119 		otd.ot_trampoline = (uintptr_t)&peek_fault;
    120 		err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
    121 	} else
    122 		err = DDI_FAILURE;
    123 
    124 	no_trap();
    125 
    126 	/*
    127 	 * Workaround: delay taking down safe access env.
    128 	 * For more info, see comments where pxtool_delay_usec is declared.
    129 	 */
    130 	if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0))
    131 		delay(drv_usectohz(pxtool_delay_usec));
    132 
    133 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
    134 	pxu_p->pcitool_addr = NULL;
    135 	mutex_exit(&pec_p->pec_pokefault_mutex);
    136 
    137 	if (err != DDI_FAILURE) {
    138 		switch (size) {
    139 		case 8:
    140 			*value_p = peek_value.u64;
    141 			break;
    142 		case 4:
    143 			*value_p = (uint64_t)peek_value.u32;
    144 			break;
    145 		case 2:
    146 			*value_p = (uint64_t)peek_value.u16;
    147 			break;
    148 		case 1:
    149 			*value_p = (uint64_t)peek_value.u8;
    150 			break;
    151 		default:
    152 			err = DDI_FAILURE;
    153 		}
    154 	}
    155 
    156 	return (err);
    157 }
    158 
    159 /*
    160  * Safe C wrapper around assy language routine px_phys_poke_4u
    161  *
    162  * Type is TRUE for big endian, FALSE for little endian.
    163  * Size is 1,2,4 or 8 bytes.
    164  * paddr is the physical address in IO space to access read.
    165  * value contains the value to be written.
    166  */
    167 static int
    168 pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
    169     uint64_t value)
    170 {
    171 	on_trap_data_t otd;
    172 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
    173 	px_pec_t *pec_p = px_p->px_pec_p;
    174 	peek_poke_value_t poke_value;
    175 	int err = DDI_SUCCESS;
    176 
    177 	switch (size) {
    178 	case 8:
    179 		poke_value.u64 = value;
    180 		break;
    181 	case 4:
    182 		poke_value.u32 = (uint32_t)value;
    183 		break;
    184 	case 2:
    185 		poke_value.u16 = (uint16_t)value;
    186 		break;
    187 	case 1:
    188 		poke_value.u8 = (uint8_t)value;
    189 		break;
    190 	default:
    191 		return (DDI_FAILURE);
    192 	}
    193 
    194 	mutex_enter(&pec_p->pec_pokefault_mutex);
    195 	pec_p->pec_ontrap_data = &otd;
    196 	pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
    197 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
    198 
    199 	/*
    200 	 * on_trap works like setjmp.
    201 	 * Set it up to not panic on data access error,
    202 	 * but to call poke_fault instead.
    203 	 * Call px_phys_poke_4u after trap handling is setup.
    204 	 * When on_trap returns FALSE, it has been setup.
    205 	 * When it returns TRUE, an it has caught an error.
    206 	 */
    207 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
    208 
    209 		otd.ot_trampoline = (uintptr_t)&poke_fault;
    210 		err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
    211 	} else
    212 		err = DDI_FAILURE;
    213 
    214 	px_lib_clr_errs(px_p, 0, paddr);
    215 
    216 	if (otd.ot_trap & OT_DATA_ACCESS)
    217 		err = DDI_FAILURE;
    218 
    219 	/* Take down protected environment. */
    220 	no_trap();
    221 	pec_p->pec_ontrap_data = NULL;
    222 
    223 	/*
    224 	 * Workaround: delay taking down safe access env.
    225 	 * For more info, see comments where pxtool_delay_usec is declared.
    226 	 */
    227 	if (pxtool_delay_usec > 0)
    228 		delay(drv_usectohz(pxtool_delay_usec));
    229 
    230 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
    231 	pxu_p->pcitool_addr = NULL;
    232 	mutex_exit(&pec_p->pec_pokefault_mutex);
    233 
    234 	return (err);
    235 }
    236 
    237 
    238 /*
    239  * Wrapper around pxtool_safe_phys_peek/poke.
    240  *
    241  * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately.
    242  *
    243  * Dip is of the nexus,
    244  * phys_addr is the address to write in physical space.
    245  * pcitool_status returns more detailed status in addition to a more generic
    246  * errno-style function return value.
    247  * other args are self-explanatory.
    248  *
    249  * This function assumes that offset, bdf, and acc_attr are current in
    250  * prg_p.  It also assumes that prg_p->phys_addr is the final phys addr,
    251  * including offset.
    252  * This function modifies prg_p status and data.
    253  */
    254 /*ARGSUSED*/
    255 static int
    256 pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
    257     boolean_t is_write)
    258 {
    259 	dev_info_t *dip = px_p->px_dip;
    260 	uint64_t phys_addr = prg_p->phys_addr;
    261 	boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
    262 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
    263 	int rval = SUCCESS;
    264 
    265 	/* Alignment checking.  Assumes base address is 8-byte aligned. */
    266 	if (!IS_P2ALIGNED(phys_addr, size)) {
    267 		DBG(DBG_TOOLS, dip, "not aligned.\n");
    268 		prg_p->status = PCITOOL_NOT_ALIGNED;
    269 
    270 		rval = EINVAL;
    271 
    272 	} else if (is_write) {	/* Made it through checks.  Do the access. */
    273 
    274 		DBG(DBG_PHYS_ACC, dip,
    275 		    "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
    276 		    size, (endian ? "BE" : "LE"), phys_addr);
    277 
    278 		if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
    279 		    *data_p) != DDI_SUCCESS) {
    280 			DBG(DBG_PHYS_ACC, dip,
    281 			    "%d byte %s pxtool_safe_phys_poke at addr "
    282 			    "0x%" PRIx64 " failed\n",
    283 			    size, (endian ? "BE" : "LE"), phys_addr);
    284 			prg_p->status = PCITOOL_INVALID_ADDRESS;
    285 
    286 			rval = EFAULT;
    287 		}
    288 
    289 	} else {	/* Read */
    290 
    291 		DBG(DBG_PHYS_ACC, dip,
    292 		    "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
    293 		    size, (endian ? "BE" : "LE"), phys_addr);
    294 
    295 		if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
    296 		    data_p) != DDI_SUCCESS) {
    297 			DBG(DBG_PHYS_ACC, dip,
    298 			    "%d byte %s pxtool_safe_phys_peek at addr "
    299 			    "0x%" PRIx64 " failed\n",
    300 			    size, (endian ? "BE" : "LE"), phys_addr);
    301 			prg_p->status = PCITOOL_INVALID_ADDRESS;
    302 
    303 			rval = EFAULT;
    304 		}
    305 	}
    306 	return (rval);
    307 }
    308 
    309 
    310 int
    311 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
    312     uint64_t *data_p, boolean_t is_write)
    313 {
    314 	return (pxtool_access(px_p, prg_p, data_p, is_write));
    315 }
    316 
    317 int
    318 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
    319     uint64_t *data_p, boolean_t is_write)
    320 {
    321 	return (pxtool_access(px_p, prg_p, data_p, is_write));
    322 }
    323 
    324 int
    325 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
    326 {
    327 	/*
    328 	 * Guard against checking a root nexus which is empty.
    329 	 * On some systems this will result in a Fatal Reset.
    330 	 */
    331 	if (ddi_get_child(dip) == NULL) {
    332 		DBG(DBG_TOOLS, dip,
    333 		    "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
    334 		prg_p->status = PCITOOL_IO_ERROR;
    335 		return (ENXIO);
    336 	}
    337 
    338 	return (SUCCESS);
    339 }
    340 
    341 /*
    342  * Perform register accesses on the nexus device itself.
    343  */
    344 int
    345 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
    346 {
    347 	pcitool_reg_t		prg;
    348 	uint64_t		base_addr;
    349 	uint32_t		reglen;
    350 	px_t			*px_p = DIP_TO_STATE(dip);
    351 	px_nexus_regspec_t	*px_rp = NULL;
    352 	uint32_t		numbanks = 0;
    353 	boolean_t		write_flag = B_FALSE;
    354 	uint32_t		rval = 0;
    355 
    356 	if (cmd == PCITOOL_NEXUS_SET_REG)
    357 		write_flag = B_TRUE;
    358 
    359 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
    360 
    361 	/* Read data from userland. */
    362 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
    363 	    DDI_SUCCESS) {
    364 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
    365 		return (EFAULT);
    366 	}
    367 
    368 	/* Read reg property which contains starting addr and size of banks. */
    369 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
    370 	    "reg", (int **)&px_rp, &reglen) == DDI_SUCCESS) {
    371 		if (((reglen * sizeof (int)) %
    372 		    sizeof (px_nexus_regspec_t)) != 0) {
    373 			DBG(DBG_TOOLS, dip, "reg prop not well-formed");
    374 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
    375 			rval = EIO;
    376 			goto done;
    377 		}
    378 	}
    379 
    380 	numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
    381 
    382 	/* Bounds check the bank number. */
    383 	if (prg.barnum >= numbanks) {
    384 		prg.status = PCITOOL_OUT_OF_RANGE;
    385 		rval = EINVAL;
    386 		goto done;
    387 	}
    388 
    389 	base_addr = px_rp[prg.barnum].phys_addr;
    390 	prg.phys_addr = base_addr + prg.offset;
    391 
    392 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
    393 	    "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
    394 	    "0x%" PRIx64 "\n",
    395 	    base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
    396 
    397 	if (prg.offset >= px_rp[prg.barnum].size) {
    398 		prg.status = PCITOOL_OUT_OF_RANGE;
    399 		rval = EINVAL;
    400 		goto done;
    401 	}
    402 
    403 	/* Access device.  prg.status is modified. */
    404 	rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
    405 
    406 done:
    407 	if (px_rp != NULL)
    408 		ddi_prop_free(px_rp);
    409 
    410 	prg.drvr_version = PCITOOL_VERSION;
    411 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
    412 	    mode) != DDI_SUCCESS) {
    413 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
    414 		return (EFAULT);
    415 	}
    416 
    417 	return (rval);
    418 }
    419