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