xref: /illumos-gate/usr/src/uts/sun4u/io/px/px_tools_4u.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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