xref: /illumos-gate/usr/src/uts/sun4u/io/px/px_tools_4u.c (revision 1578c5210cc5a6709ee11c9693bb0a24ed8d38f8)
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 int pxtool_delay_ticks = 1;
69 
70 /* Number of inos per root complex. */
71 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
72 
73 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
74 typedef union {
75 	uint64_t u64;
76 	uint32_t u32;
77 	uint16_t u16;
78 	uint8_t u8;
79 } peek_poke_value_t;
80 
81 /*
82  * Safe C wrapper around assy language routine px_phys_peek_4u
83  *
84  * Type is TRUE for big endian, FALSE for little endian.
85  * Size is 1, 2, 4 or 8 bytes.
86  * paddr is the physical address in IO space to access read.
87  * value_p is where the value is returned.
88  */
89 static int
90 pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
91     uint64_t *value_p)
92 {
93 	px_pec_t *pec_p = px_p->px_pec_p;
94 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
95 	on_trap_data_t otd;
96 	peek_poke_value_t peek_value;
97 	int err = DDI_SUCCESS;
98 
99 	mutex_enter(&pec_p->pec_pokefault_mutex);
100 	pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
101 
102 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
103 
104 	/*
105 	 * Set up trap handling to make the access safe.
106 	 *
107 	 * on_trap works like setjmp.
108 	 * Set it up to not panic on data access error,
109 	 * but to call peek_fault instead.
110 	 * Call px_phys_peek_4u after trap handling is setup.
111 	 * When on_trap returns FALSE, it has been setup.
112 	 * When it returns TRUE, an it has caught an error.
113 	 */
114 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
115 		otd.ot_trampoline = (uintptr_t)&peek_fault;
116 		err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
117 	} else
118 		err = DDI_FAILURE;
119 
120 	no_trap();
121 
122 	/*
123 	 * Workaround: delay taking down safe access env.
124 	 * For more info, see comments where pxtool_delay_ticks is declared.
125 	 */
126 	if (pxtool_delay_ticks > 0)
127 		delay(pxtool_delay_ticks);
128 
129 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
130 	pxu_p->pcitool_addr = NULL;
131 	mutex_exit(&pec_p->pec_pokefault_mutex);
132 
133 	if (err != DDI_FAILURE) {
134 		switch (size) {
135 		case 8:
136 			*value_p = peek_value.u64;
137 			break;
138 		case 4:
139 			*value_p = (uint64_t)peek_value.u32;
140 			break;
141 		case 2:
142 			*value_p = (uint64_t)peek_value.u16;
143 			break;
144 		case 1:
145 			*value_p = (uint64_t)peek_value.u8;
146 			break;
147 		default:
148 			err = DDI_FAILURE;
149 		}
150 	}
151 
152 	return (err);
153 }
154 
155 /*
156  * Safe C wrapper around assy language routine px_phys_poke_4u
157  *
158  * Type is TRUE for big endian, FALSE for little endian.
159  * Size is 1,2,4 or 8 bytes.
160  * paddr is the physical address in IO space to access read.
161  * value contains the value to be written.
162  */
163 static int
164 pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
165     uint64_t value)
166 {
167 	on_trap_data_t otd;
168 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
169 	px_pec_t *pec_p = px_p->px_pec_p;
170 	peek_poke_value_t poke_value;
171 	int err = DDI_SUCCESS;
172 
173 	switch (size) {
174 	case 8:
175 		poke_value.u64 = value;
176 		break;
177 	case 4:
178 		poke_value.u32 = (uint32_t)value;
179 		break;
180 	case 2:
181 		poke_value.u16 = (uint16_t)value;
182 		break;
183 	case 1:
184 		poke_value.u8 = (uint8_t)value;
185 		break;
186 	default:
187 		return (DDI_FAILURE);
188 	}
189 
190 	mutex_enter(&pec_p->pec_pokefault_mutex);
191 	pec_p->pec_ontrap_data = &otd;
192 	pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
193 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
194 
195 	/*
196 	 * on_trap works like setjmp.
197 	 * Set it up to not panic on data access error,
198 	 * but to call poke_fault instead.
199 	 * Call px_phys_poke_4u after trap handling is setup.
200 	 * When on_trap returns FALSE, it has been setup.
201 	 * When it returns TRUE, an it has caught an error.
202 	 */
203 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
204 
205 		otd.ot_trampoline = (uintptr_t)&poke_fault;
206 		err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
207 	} else
208 		err = DDI_FAILURE;
209 
210 	px_lib_clr_errs(px_p, 0, paddr);
211 
212 	if (otd.ot_trap & OT_DATA_ACCESS)
213 		err = DDI_FAILURE;
214 
215 	/* Take down protected environment. */
216 	no_trap();
217 	pec_p->pec_ontrap_data = NULL;
218 
219 	/*
220 	 * Workaround: delay taking down safe access env.
221 	 * For more info, see comments where pxtool_delay_ticks is declared.
222 	 */
223 	if (pxtool_delay_ticks > 0)
224 		delay(pxtool_delay_ticks);
225 
226 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
227 	pxu_p->pcitool_addr = NULL;
228 	mutex_exit(&pec_p->pec_pokefault_mutex);
229 
230 	return (err);
231 }
232 
233 
234 /*
235  * Wrapper around pxtool_safe_phys_peek/poke.
236  *
237  * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately.
238  *
239  * Dip is of the nexus,
240  * phys_addr is the address to write in physical space.
241  * pcitool_status returns more detailed status in addition to a more generic
242  * errno-style function return value.
243  * other args are self-explanatory.
244  *
245  * This function assumes that offset, bdf, and acc_attr are current in
246  * prg_p.  It also assumes that prg_p->phys_addr is the final phys addr,
247  * including offset.
248  * This function modifies prg_p status and data.
249  */
250 /*ARGSUSED*/
251 static int
252 pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
253     boolean_t is_write)
254 {
255 	dev_info_t *dip = px_p->px_dip;
256 	uint64_t phys_addr = prg_p->phys_addr;
257 	boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
258 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
259 	int rval = SUCCESS;
260 
261 	/* Alignment checking.  Assumes base address is 8-byte aligned. */
262 	if (!IS_P2ALIGNED(phys_addr, size)) {
263 		DBG(DBG_TOOLS, dip, "not aligned.\n");
264 		prg_p->status = PCITOOL_NOT_ALIGNED;
265 
266 		rval = EINVAL;
267 
268 	} else if (is_write) {	/* Made it through checks.  Do the access. */
269 
270 		DBG(DBG_PHYS_ACC, dip,
271 		    "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
272 		    size, (endian ? "BE" : "LE"), phys_addr);
273 
274 		if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
275 		    *data_p) != DDI_SUCCESS) {
276 			DBG(DBG_PHYS_ACC, dip,
277 			    "%d byte %s pxtool_safe_phys_poke at addr "
278 			    "0x%" PRIx64 " failed\n",
279 			    size, (endian ? "BE" : "LE"), phys_addr);
280 			prg_p->status = PCITOOL_INVALID_ADDRESS;
281 
282 			rval = EFAULT;
283 		}
284 
285 	} else {	/* Read */
286 
287 		DBG(DBG_PHYS_ACC, dip,
288 		    "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
289 		    size, (endian ? "BE" : "LE"), phys_addr);
290 
291 		if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
292 		    data_p) != DDI_SUCCESS) {
293 			DBG(DBG_PHYS_ACC, dip,
294 			    "%d byte %s pxtool_safe_phys_peek at addr "
295 			    "0x%" PRIx64 " failed\n",
296 			    size, (endian ? "BE" : "LE"), phys_addr);
297 			prg_p->status = PCITOOL_INVALID_ADDRESS;
298 
299 			rval = EFAULT;
300 		}
301 	}
302 	return (rval);
303 }
304 
305 
306 int
307 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
308     uint64_t *data_p, boolean_t is_write)
309 {
310 	return (pxtool_access(px_p, prg_p, data_p, is_write));
311 }
312 
313 int
314 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
315     uint64_t *data_p, boolean_t is_write)
316 {
317 	return (pxtool_access(px_p, prg_p, data_p, is_write));
318 }
319 
320 int
321 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
322 {
323 	/*
324 	 * Guard against checking a root nexus which is empty.
325 	 * On some systems this will result in a Fatal Reset.
326 	 */
327 	if (ddi_get_child(dip) == NULL) {
328 		DBG(DBG_TOOLS, dip,
329 		    "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
330 		prg_p->status = PCITOOL_IO_ERROR;
331 		return (ENXIO);
332 	}
333 
334 	return (SUCCESS);
335 }
336 
337 /*
338  * Perform register accesses on the nexus device itself.
339  */
340 int
341 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
342 {
343 	pcitool_reg_t		prg;
344 	uint64_t		base_addr;
345 	uint32_t		reglen;
346 	px_t			*px_p = DIP_TO_STATE(dip);
347 	px_nexus_regspec_t	*px_rp = NULL;
348 	uint32_t		numbanks = 0;
349 	boolean_t		write_flag = B_FALSE;
350 	uint32_t		rval = 0;
351 
352 	if (cmd == PCITOOL_NEXUS_SET_REG)
353 		write_flag = B_TRUE;
354 
355 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
356 
357 	/* Read data from userland. */
358 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
359 	    DDI_SUCCESS) {
360 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
361 		return (EFAULT);
362 	}
363 
364 	/* Read reg property which contains starting addr and size of banks. */
365 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
366 	    "reg", (int **)&px_rp, &reglen) == DDI_SUCCESS) {
367 		if (((reglen * sizeof (int)) %
368 		    sizeof (px_nexus_regspec_t)) != 0) {
369 			DBG(DBG_TOOLS, dip, "reg prop not well-formed");
370 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
371 			rval = EIO;
372 			goto done;
373 		}
374 	}
375 
376 	numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
377 
378 	/* Bounds check the bank number. */
379 	if (prg.barnum >= numbanks) {
380 		prg.status = PCITOOL_OUT_OF_RANGE;
381 		rval = EINVAL;
382 		goto done;
383 	}
384 
385 	base_addr = px_rp[prg.barnum].phys_addr;
386 	prg.phys_addr = base_addr + prg.offset;
387 
388 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
389 	    "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
390 	    "0x%" PRIx64 "\n",
391 	    base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
392 
393 	if (prg.offset >= px_rp[prg.barnum].size) {
394 		prg.status = PCITOOL_OUT_OF_RANGE;
395 		rval = EINVAL;
396 		goto done;
397 	}
398 
399 	/* Access device.  prg.status is modified. */
400 	rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
401 
402 done:
403 	if (px_rp != NULL)
404 		ddi_prop_free(px_rp);
405 
406 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
407 	    mode) != DDI_SUCCESS) {
408 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
409 		return (EFAULT);
410 	}
411 
412 	return (rval);
413 }
414