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