xref: /onnv-gate/usr/src/uts/sun4u/io/px/px_tools_4u.c (revision 5132:74c6615ada57)
1624Sschwartz /*
2624Sschwartz  * CDDL HEADER START
3624Sschwartz  *
4624Sschwartz  * The contents of this file are subject to the terms of the
52276Sschwartz  * Common Development and Distribution License (the "License").
62276Sschwartz  * You may not use this file except in compliance with the License.
7624Sschwartz  *
8624Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9624Sschwartz  * or http://www.opensolaris.org/os/licensing.
10624Sschwartz  * See the License for the specific language governing permissions
11624Sschwartz  * and limitations under the License.
12624Sschwartz  *
13624Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
14624Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15624Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
16624Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
17624Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
18624Sschwartz  *
19624Sschwartz  * CDDL HEADER END
20624Sschwartz  */
21624Sschwartz /*
223720Sschwartz  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23624Sschwartz  * Use is subject to license terms.
24624Sschwartz  */
25624Sschwartz 
26624Sschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
27624Sschwartz 
28624Sschwartz #include <sys/sysmacros.h>
29624Sschwartz #include <sys/machsystm.h>
30624Sschwartz #include <sys/cpuvar.h>
31624Sschwartz #include <sys/ddi_implfuncs.h>
32624Sschwartz #include <px_csr.h>
33624Sschwartz #include <px_regs.h>
34624Sschwartz #include <px_obj.h>
35624Sschwartz #include <sys/pci_tools.h>
36624Sschwartz #include <px_tools_var.h>
37624Sschwartz #include <px_asm_4u.h>
38624Sschwartz #include <px_lib4u.h>
39777Sschwartz #include <px_tools_ext.h>
40624Sschwartz 
41624Sschwartz /*
42624Sschwartz  * Delay needed to have a safe environment envelop any error which could
43624Sschwartz  * surface.  The larger the number of bridges and switches, the larger the
44624Sschwartz  * number needed here.
45624Sschwartz  *
461064Sschwartz  * The way it works is as follows:
471064Sschwartz  *
481064Sschwartz  * An access is done which causes an error.  Fire errors are handled with
491064Sschwartz  * ontrap protection and usually come in first.  Fabric errors can come in
501064Sschwartz  * later.
511064Sschwartz  *
521064Sschwartz  * px_phys_peek_4u() disables interrupts.  Interrupts are reenabled at the end
531064Sschwartz  * of that function if no errors have been caught by the trap handler, or by
541064Sschwartz  * peek_fault() which executes when a fire error occurs.
551064Sschwartz  *
561064Sschwartz  * Fabric error messages get put on an event queue but are not processed until
571064Sschwartz  * interrupts are reenabled.
581064Sschwartz  *
591064Sschwartz  * The delay gives time for the fabric errors to be processed by FMA before
601064Sschwartz  * changing the fm error flag back to DDI_FM_ERR_UNEXPECTED.  If this isn't
611064Sschwartz  * done, then the fabric error which should be safe can panic the system.
621064Sschwartz  *
63624Sschwartz  * Note: this is a workaround until a better solution is found.  While this
64624Sschwartz  * number is high, given enough bridges and switches in the device path, this
65624Sschwartz  * workaround can break.  Also, other PIL 15 interrupts besides the ones we are
66624Sschwartz  * enveloping could delay processing of the interrupt we are trying to protect.
67624Sschwartz  */
68*5132Srameshc 
69*5132Srameshc /*
70*5132Srameshc  * Set delay to 10 ms
71*5132Srameshc  */
72*5132Srameshc int pxtool_delay_usec = 10000;
73624Sschwartz 
74624Sschwartz /* Number of inos per root complex. */
75624Sschwartz int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
76624Sschwartz 
77624Sschwartz /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
78624Sschwartz typedef union {
79624Sschwartz 	uint64_t u64;
80624Sschwartz 	uint32_t u32;
81624Sschwartz 	uint16_t u16;
82624Sschwartz 	uint8_t u8;
83624Sschwartz } peek_poke_value_t;
84624Sschwartz 
85624Sschwartz /*
86624Sschwartz  * Safe C wrapper around assy language routine px_phys_peek_4u
87624Sschwartz  *
88624Sschwartz  * Type is TRUE for big endian, FALSE for little endian.
89624Sschwartz  * Size is 1, 2, 4 or 8 bytes.
90624Sschwartz  * paddr is the physical address in IO space to access read.
91624Sschwartz  * value_p is where the value is returned.
92624Sschwartz  */
93624Sschwartz static int
pxtool_safe_phys_peek(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)94624Sschwartz pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
95624Sschwartz     uint64_t *value_p)
96624Sschwartz {
97624Sschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
982276Sschwartz 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
99624Sschwartz 	on_trap_data_t otd;
100624Sschwartz 	peek_poke_value_t peek_value;
101624Sschwartz 	int err = DDI_SUCCESS;
102624Sschwartz 
103624Sschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
104624Sschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
105624Sschwartz 
1062276Sschwartz 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
1072276Sschwartz 
108624Sschwartz 	/*
109624Sschwartz 	 * Set up trap handling to make the access safe.
110624Sschwartz 	 *
111624Sschwartz 	 * on_trap works like setjmp.
112624Sschwartz 	 * Set it up to not panic on data access error,
113624Sschwartz 	 * but to call peek_fault instead.
114624Sschwartz 	 * Call px_phys_peek_4u after trap handling is setup.
115624Sschwartz 	 * When on_trap returns FALSE, it has been setup.
116624Sschwartz 	 * When it returns TRUE, an it has caught an error.
117624Sschwartz 	 */
118624Sschwartz 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
119624Sschwartz 		otd.ot_trampoline = (uintptr_t)&peek_fault;
120624Sschwartz 		err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
121624Sschwartz 	} else
122624Sschwartz 		err = DDI_FAILURE;
123624Sschwartz 
1241064Sschwartz 	no_trap();
1251064Sschwartz 
126624Sschwartz 	/*
127624Sschwartz 	 * Workaround: delay taking down safe access env.
128*5132Srameshc 	 * For more info, see comments where pxtool_delay_usec is declared.
129624Sschwartz 	 */
130*5132Srameshc 	if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0))
131*5132Srameshc 		delay(drv_usectohz(pxtool_delay_usec));
132624Sschwartz 
133624Sschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
1342276Sschwartz 	pxu_p->pcitool_addr = NULL;
135624Sschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
136624Sschwartz 
137624Sschwartz 	if (err != DDI_FAILURE) {
138624Sschwartz 		switch (size) {
139624Sschwartz 		case 8:
140624Sschwartz 			*value_p = peek_value.u64;
141624Sschwartz 			break;
142624Sschwartz 		case 4:
143624Sschwartz 			*value_p = (uint64_t)peek_value.u32;
144624Sschwartz 			break;
145624Sschwartz 		case 2:
146624Sschwartz 			*value_p = (uint64_t)peek_value.u16;
147624Sschwartz 			break;
148624Sschwartz 		case 1:
149624Sschwartz 			*value_p = (uint64_t)peek_value.u8;
150624Sschwartz 			break;
151624Sschwartz 		default:
152624Sschwartz 			err = DDI_FAILURE;
153624Sschwartz 		}
154624Sschwartz 	}
155624Sschwartz 
156624Sschwartz 	return (err);
157624Sschwartz }
158624Sschwartz 
159624Sschwartz /*
160624Sschwartz  * Safe C wrapper around assy language routine px_phys_poke_4u
161624Sschwartz  *
162624Sschwartz  * Type is TRUE for big endian, FALSE for little endian.
163624Sschwartz  * Size is 1,2,4 or 8 bytes.
164624Sschwartz  * paddr is the physical address in IO space to access read.
165624Sschwartz  * value contains the value to be written.
166624Sschwartz  */
167624Sschwartz static int
pxtool_safe_phys_poke(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)168624Sschwartz pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
169624Sschwartz     uint64_t value)
170624Sschwartz {
171624Sschwartz 	on_trap_data_t otd;
1722276Sschwartz 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
173624Sschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
174624Sschwartz 	peek_poke_value_t poke_value;
175624Sschwartz 	int err = DDI_SUCCESS;
176624Sschwartz 
177624Sschwartz 	switch (size) {
178624Sschwartz 	case 8:
179624Sschwartz 		poke_value.u64 = value;
180624Sschwartz 		break;
181624Sschwartz 	case 4:
182624Sschwartz 		poke_value.u32 = (uint32_t)value;
183624Sschwartz 		break;
184624Sschwartz 	case 2:
185624Sschwartz 		poke_value.u16 = (uint16_t)value;
186624Sschwartz 		break;
187624Sschwartz 	case 1:
188624Sschwartz 		poke_value.u8 = (uint8_t)value;
189624Sschwartz 		break;
190624Sschwartz 	default:
191624Sschwartz 		return (DDI_FAILURE);
192624Sschwartz 	}
193624Sschwartz 
194624Sschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
195624Sschwartz 	pec_p->pec_ontrap_data = &otd;
196624Sschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
1972276Sschwartz 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
198624Sschwartz 
199624Sschwartz 	/*
200624Sschwartz 	 * on_trap works like setjmp.
201624Sschwartz 	 * Set it up to not panic on data access error,
202624Sschwartz 	 * but to call poke_fault instead.
203624Sschwartz 	 * Call px_phys_poke_4u after trap handling is setup.
204624Sschwartz 	 * When on_trap returns FALSE, it has been setup.
205624Sschwartz 	 * When it returns TRUE, an it has caught an error.
206624Sschwartz 	 */
207624Sschwartz 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
208624Sschwartz 
209624Sschwartz 		otd.ot_trampoline = (uintptr_t)&poke_fault;
210624Sschwartz 		err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
211624Sschwartz 	} else
212624Sschwartz 		err = DDI_FAILURE;
213624Sschwartz 
2143274Set142600 	px_lib_clr_errs(px_p, 0, paddr);
215624Sschwartz 
216624Sschwartz 	if (otd.ot_trap & OT_DATA_ACCESS)
217624Sschwartz 		err = DDI_FAILURE;
218624Sschwartz 
219624Sschwartz 	/* Take down protected environment. */
220624Sschwartz 	no_trap();
2211064Sschwartz 	pec_p->pec_ontrap_data = NULL;
222624Sschwartz 
2231064Sschwartz 	/*
2241064Sschwartz 	 * Workaround: delay taking down safe access env.
225*5132Srameshc 	 * For more info, see comments where pxtool_delay_usec is declared.
2261064Sschwartz 	 */
227*5132Srameshc 	if (pxtool_delay_usec > 0)
228*5132Srameshc 		delay(drv_usectohz(pxtool_delay_usec));
2291064Sschwartz 
230624Sschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
2312276Sschwartz 	pxu_p->pcitool_addr = NULL;
232624Sschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
233624Sschwartz 
234624Sschwartz 	return (err);
235624Sschwartz }
236624Sschwartz 
237624Sschwartz 
238624Sschwartz /*
239624Sschwartz  * Wrapper around pxtool_safe_phys_peek/poke.
240624Sschwartz  *
241624Sschwartz  * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately.
242624Sschwartz  *
243624Sschwartz  * Dip is of the nexus,
244624Sschwartz  * phys_addr is the address to write in physical space.
245624Sschwartz  * pcitool_status returns more detailed status in addition to a more generic
246624Sschwartz  * errno-style function return value.
247624Sschwartz  * other args are self-explanatory.
248624Sschwartz  *
2491064Sschwartz  * This function assumes that offset, bdf, and acc_attr are current in
250624Sschwartz  * prg_p.  It also assumes that prg_p->phys_addr is the final phys addr,
251624Sschwartz  * including offset.
252624Sschwartz  * This function modifies prg_p status and data.
253624Sschwartz  */
254624Sschwartz /*ARGSUSED*/
255624Sschwartz static int
pxtool_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)2561064Sschwartz pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
2571064Sschwartz     boolean_t is_write)
258624Sschwartz {
259624Sschwartz 	dev_info_t *dip = px_p->px_dip;
260624Sschwartz 	uint64_t phys_addr = prg_p->phys_addr;
261624Sschwartz 	boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
262624Sschwartz 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
263624Sschwartz 	int rval = SUCCESS;
264624Sschwartz 
265624Sschwartz 	/* Alignment checking.  Assumes base address is 8-byte aligned. */
2661064Sschwartz 	if (!IS_P2ALIGNED(phys_addr, size)) {
267624Sschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
268624Sschwartz 		prg_p->status = PCITOOL_NOT_ALIGNED;
269624Sschwartz 
270624Sschwartz 		rval = EINVAL;
271624Sschwartz 
272624Sschwartz 	} else if (is_write) {	/* Made it through checks.  Do the access. */
273624Sschwartz 
274624Sschwartz 		DBG(DBG_PHYS_ACC, dip,
275624Sschwartz 		    "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
276624Sschwartz 		    size, (endian ? "BE" : "LE"), phys_addr);
277624Sschwartz 
278624Sschwartz 		if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
279624Sschwartz 		    *data_p) != DDI_SUCCESS) {
280624Sschwartz 			DBG(DBG_PHYS_ACC, dip,
281624Sschwartz 			    "%d byte %s pxtool_safe_phys_poke at addr "
282624Sschwartz 			    "0x%" PRIx64 " failed\n",
283624Sschwartz 			    size, (endian ? "BE" : "LE"), phys_addr);
284624Sschwartz 			prg_p->status = PCITOOL_INVALID_ADDRESS;
285624Sschwartz 
286624Sschwartz 			rval = EFAULT;
287624Sschwartz 		}
288624Sschwartz 
289624Sschwartz 	} else {	/* Read */
290624Sschwartz 
291624Sschwartz 		DBG(DBG_PHYS_ACC, dip,
292624Sschwartz 		    "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
293624Sschwartz 		    size, (endian ? "BE" : "LE"), phys_addr);
294624Sschwartz 
295624Sschwartz 		if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
296624Sschwartz 		    data_p) != DDI_SUCCESS) {
297624Sschwartz 			DBG(DBG_PHYS_ACC, dip,
298624Sschwartz 			    "%d byte %s pxtool_safe_phys_peek at addr "
299624Sschwartz 			    "0x%" PRIx64 " failed\n",
300624Sschwartz 			    size, (endian ? "BE" : "LE"), phys_addr);
301624Sschwartz 			prg_p->status = PCITOOL_INVALID_ADDRESS;
302624Sschwartz 
303624Sschwartz 			rval = EFAULT;
304624Sschwartz 		}
305624Sschwartz 	}
306624Sschwartz 	return (rval);
307624Sschwartz }
308624Sschwartz 
309624Sschwartz 
310624Sschwartz int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)311624Sschwartz pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
3121064Sschwartz     uint64_t *data_p, boolean_t is_write)
313624Sschwartz {
3141064Sschwartz 	return (pxtool_access(px_p, prg_p, data_p, is_write));
315624Sschwartz }
316624Sschwartz 
317624Sschwartz int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)3181064Sschwartz pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
319624Sschwartz     uint64_t *data_p, boolean_t is_write)
320624Sschwartz {
3211064Sschwartz 	return (pxtool_access(px_p, prg_p, data_p, is_write));
322624Sschwartz }
323624Sschwartz 
324624Sschwartz int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)325624Sschwartz pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
326624Sschwartz {
327624Sschwartz 	/*
328624Sschwartz 	 * Guard against checking a root nexus which is empty.
329624Sschwartz 	 * On some systems this will result in a Fatal Reset.
330624Sschwartz 	 */
3313720Sschwartz 	if (ddi_get_child(dip) == NULL) {
332624Sschwartz 		DBG(DBG_TOOLS, dip,
333624Sschwartz 		    "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
334624Sschwartz 		prg_p->status = PCITOOL_IO_ERROR;
335624Sschwartz 		return (ENXIO);
336624Sschwartz 	}
337624Sschwartz 
338624Sschwartz 	return (SUCCESS);
339624Sschwartz }
340624Sschwartz 
341624Sschwartz /*
342624Sschwartz  * Perform register accesses on the nexus device itself.
343624Sschwartz  */
344624Sschwartz int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)345624Sschwartz pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
346624Sschwartz {
347624Sschwartz 	pcitool_reg_t		prg;
348624Sschwartz 	uint64_t		base_addr;
349624Sschwartz 	uint32_t		reglen;
350624Sschwartz 	px_t			*px_p = DIP_TO_STATE(dip);
351624Sschwartz 	px_nexus_regspec_t	*px_rp = NULL;
352624Sschwartz 	uint32_t		numbanks = 0;
353624Sschwartz 	boolean_t		write_flag = B_FALSE;
354624Sschwartz 	uint32_t		rval = 0;
355624Sschwartz 
356624Sschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
357624Sschwartz 		write_flag = B_TRUE;
358624Sschwartz 
359624Sschwartz 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
360624Sschwartz 
361624Sschwartz 	/* Read data from userland. */
362624Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
363624Sschwartz 	    DDI_SUCCESS) {
364624Sschwartz 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
365624Sschwartz 		return (EFAULT);
366624Sschwartz 	}
367624Sschwartz 
368624Sschwartz 	/* Read reg property which contains starting addr and size of banks. */
369624Sschwartz 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
370624Sschwartz 	    "reg", (int **)&px_rp, &reglen) == DDI_SUCCESS) {
371624Sschwartz 		if (((reglen * sizeof (int)) %
372624Sschwartz 		    sizeof (px_nexus_regspec_t)) != 0) {
373624Sschwartz 			DBG(DBG_TOOLS, dip, "reg prop not well-formed");
374624Sschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
375624Sschwartz 			rval = EIO;
376624Sschwartz 			goto done;
377624Sschwartz 		}
378624Sschwartz 	}
379624Sschwartz 
380624Sschwartz 	numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
381624Sschwartz 
382624Sschwartz 	/* Bounds check the bank number. */
383624Sschwartz 	if (prg.barnum >= numbanks) {
384624Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
385624Sschwartz 		rval = EINVAL;
386624Sschwartz 		goto done;
387624Sschwartz 	}
388624Sschwartz 
389624Sschwartz 	base_addr = px_rp[prg.barnum].phys_addr;
390624Sschwartz 	prg.phys_addr = base_addr + prg.offset;
391624Sschwartz 
392624Sschwartz 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
3931064Sschwartz 	    "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
3941064Sschwartz 	    "0x%" PRIx64 "\n",
3951064Sschwartz 	    base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
3961064Sschwartz 
3971064Sschwartz 	if (prg.offset >= px_rp[prg.barnum].size) {
3981064Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
3991064Sschwartz 		rval = EINVAL;
4001064Sschwartz 		goto done;
4011064Sschwartz 	}
402624Sschwartz 
403624Sschwartz 	/* Access device.  prg.status is modified. */
4041064Sschwartz 	rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
405624Sschwartz 
406624Sschwartz done:
407624Sschwartz 	if (px_rp != NULL)
408624Sschwartz 		ddi_prop_free(px_rp);
409624Sschwartz 
4104397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
411624Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
412624Sschwartz 	    mode) != DDI_SUCCESS) {
413624Sschwartz 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
414624Sschwartz 		return (EFAULT);
415624Sschwartz 	}
416624Sschwartz 
417624Sschwartz 	return (rval);
418624Sschwartz }
419