xref: /illumos-gate/usr/src/uts/sun4v/io/px/px_tools_4v.c (revision 29e362da24db33a6650152985ef5626b4e6a810f)
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 2006 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 <sys/hypervisor_api.h>
33 #include <px_obj.h>
34 #include <sys/pci_tools.h>
35 #include <px_tools_var.h>
36 #include "px_lib4v.h"
37 #include <px_tools_ext.h>
38 
39 /*
40  * Delay needed to have a safe environment envelop any error which could
41  * surface.  The larger the number of bridges and switches, the larger the
42  * number needed here.
43  *
44  * Note: this is a workaround until a better solution is found.  While this
45  * number is high, given enough bridges and switches in the device path, this
46  * workaround can break.  Also, other PIL 15 interrupts besides the ones we are
47  * enveloping could delay processing of the interrupt we are trying to protect.
48  */
49 int pxtool_cfg_delay_usec = 2500;
50 int pxtool_iomem_delay_usec = 25000;
51 
52 /* Currently there is no way of getting this info from hypervisor. */
53 #define	INTERRUPT_MAPPING_ENTRIES	64
54 
55 /* Number of inos per root complex. */
56 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
57 
58 /* Swap endianness. */
59 static uint64_t
60 pxtool_swap_endian(uint64_t data, int size)
61 {
62 	typedef union {
63 		uint64_t data64;
64 		uint8_t data8[8];
65 	} data_split_t;
66 
67 	data_split_t orig_data;
68 	data_split_t returned_data;
69 	int i;
70 
71 	orig_data.data64 = data;
72 	returned_data.data64 = 0;
73 
74 	for (i = 0; i < size; i++) {
75 		returned_data.data8[7 - i] = orig_data.data8[8 - size + i];
76 	}
77 
78 	return (returned_data.data64);
79 }
80 
81 static int
82 pxtool_phys_access(px_t *px_p, uintptr_t dev_addr,
83     uint64_t *data_p, boolean_t is_big_endian, boolean_t is_write)
84 {
85 	uint64_t rfunc, pfunc;
86 	uint64_t rdata_addr, pdata_addr;
87 	uint64_t to_addr, from_addr;
88 	uint64_t local_data;
89 	int rval;
90 	dev_info_t *dip = px_p->px_dip;
91 
92 	DBG(DBG_TOOLS, dip,
93 	    "pxtool_phys_access: dev_addr:0x%" PRIx64 "\n", dev_addr);
94 	DBG(DBG_TOOLS, dip, "    data_addr:0x%" PRIx64 ", is_write:%s\n",
95 	    data_p, (is_write ? "yes" : "no"));
96 
97 	if ((rfunc = va_to_pa((void *)px_phys_acc_4v))  == (uint64_t)-1) {
98 		DBG(DBG_TOOLS, dip, "Error getting real addr for function\n");
99 		return (EIO);
100 	}
101 
102 	if ((pfunc = hv_ra2pa(rfunc)) == -1) {
103 		DBG(DBG_TOOLS, dip, "Error getting phys addr for function\n");
104 		return (EIO);
105 	}
106 
107 	if ((rdata_addr = va_to_pa((void *)&local_data))  == (uint64_t)-1) {
108 		DBG(DBG_TOOLS, dip, "Error getting real addr for data_p\n");
109 		return (EIO);
110 	}
111 
112 	if ((pdata_addr = hv_ra2pa(rdata_addr)) == -1) {
113 		DBG(DBG_TOOLS, dip, "Error getting phys addr for data ptr\n");
114 		return (EIO);
115 	}
116 
117 	if (is_write) {
118 		to_addr = dev_addr;
119 		from_addr = pdata_addr;
120 
121 		if (is_big_endian)
122 			local_data = *data_p;
123 		else
124 			local_data =
125 			    pxtool_swap_endian(*data_p, sizeof (uint64_t));
126 	} else {
127 		to_addr = pdata_addr;
128 		from_addr = dev_addr;
129 	}
130 
131 	rval = hv_hpriv((void *)pfunc, from_addr, to_addr, NULL);
132 	switch (rval) {
133 	case H_ENOACCESS:	/* Returned by non-debug hypervisor. */
134 		rval = ENOTSUP;
135 		break;
136 	case H_EOK:
137 		rval = SUCCESS;
138 		break;
139 	default:
140 		rval = EIO;
141 		break;
142 	}
143 
144 	if ((rval == SUCCESS) && (!is_write)) {
145 		if (is_big_endian)
146 			*data_p = local_data;
147 		else
148 			*data_p =
149 			    pxtool_swap_endian(local_data, sizeof (uint64_t));
150 	}
151 
152 	return (rval);
153 }
154 
155 /*
156  * This function is for PCI config space access.
157  * It assumes that offset, bdf, acc_attr are valid in prg_p.
158  * This function modifies prg_p status and data.
159  *
160  * prg_p->phys_addr isn't used.
161  */
162 
163 /*ARGSUSED*/
164 int
165 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
166     uint64_t *data_p, boolean_t is_write)
167 {
168 	pci_cfg_data_t data;
169 	on_trap_data_t otd;
170 	dev_info_t *dip = px_p->px_dip;
171 	px_pec_t *pec_p = px_p->px_pec_p;
172 	pci_device_t bdf = PX_GET_BDF(prg_p);
173 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
174 	int rval = 0;
175 
176 	/* Alignment checking. */
177 	if (!IS_P2ALIGNED(prg_p->offset, size)) {
178 		DBG(DBG_TOOLS, dip, "not aligned.\n");
179 		prg_p->status = PCITOOL_NOT_ALIGNED;
180 		return (EINVAL);
181 	}
182 
183 	mutex_enter(&pec_p->pec_pokefault_mutex);
184 	pec_p->pec_ontrap_data = &otd;
185 
186 	if (is_write) {
187 
188 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
189 			data.qw = pxtool_swap_endian(*data_p, size);
190 		else
191 			data.qw = *data_p;
192 
193 		switch (size) {
194 			case sizeof (uint8_t):
195 				data.b = (uint8_t)data.qw;
196 				break;
197 			case sizeof (uint16_t):
198 				data.w = (uint16_t)data.qw;
199 				break;
200 			case sizeof (uint32_t):
201 				data.dw = (uint32_t)data.qw;
202 				break;
203 			case sizeof (uint64_t):
204 				break;
205 		}
206 
207 		DBG(DBG_TOOLS, dip, "put: bdf:0x%x, off:0x%" PRIx64 ", size:"
208 		    "0x%" PRIx64 ", data:0x%" PRIx64 "\n",
209 		    bdf, prg_p->offset, size, data.qw);
210 
211 		pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
212 
213 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
214 			otd.ot_trampoline = (uintptr_t)&poke_fault;
215 			rval = hvio_config_put(px_p->px_dev_hdl, bdf,
216 			    prg_p->offset, size, data);
217 		} else
218 			rval = H_EIO;
219 
220 		if (otd.ot_trap & OT_DATA_ACCESS)
221 			rval = H_EIO;
222 
223 	} else {
224 
225 		data.qw = 0;
226 
227 		pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
228 
229 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
230 			otd.ot_trampoline = (uintptr_t)&peek_fault;
231 			rval = hvio_config_get(px_p->px_dev_hdl, bdf,
232 			    prg_p->offset, size, &data);
233 		} else
234 			rval = H_EIO;
235 
236 		DBG(DBG_TOOLS, dip, "get: bdf:0x%x, off:0x%" PRIx64 ", size:"
237 		    "0x%" PRIx64 ", data:0x%" PRIx64 "\n",
238 		    bdf, prg_p->offset, size, data.qw);
239 
240 		switch (size) {
241 			case sizeof (uint8_t):
242 				*data_p = data.b;
243 				break;
244 			case sizeof (uint16_t):
245 				*data_p = data.w;
246 				break;
247 			case sizeof (uint32_t):
248 				*data_p = data.dw;
249 				break;
250 			case sizeof (uint64_t):
251 				*data_p = data.qw;
252 				break;
253 			default:
254 				DBG(DBG_TOOLS, dip,
255 				    "bad size:0x%" PRIx64 "\n", size);
256 				break;
257 		}
258 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
259 			*data_p = pxtool_swap_endian(*data_p, size);
260 	}
261 
262 	/*
263 	 * Workaround: delay taking down safe access env.
264 	 * For more info, see comments where pxtool_cfg_delay_usec is declared.
265 	 */
266 	if (pxtool_cfg_delay_usec > 0)
267 		drv_usecwait(pxtool_cfg_delay_usec);
268 
269 	no_trap();
270 	pec_p->pec_ontrap_data = NULL;
271 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
272 	mutex_exit(&pec_p->pec_pokefault_mutex);
273 
274 	if (rval != SUCCESS) {
275 		prg_p->status = PCITOOL_INVALID_ADDRESS;
276 		rval = EINVAL;
277 	} else
278 		prg_p->status = PCITOOL_SUCCESS;
279 
280 	return (rval);
281 }
282 
283 
284 /*
285  * This function is for PCI IO space and memory space access.
286  * It assumes that offset, bdf, acc_attr are current in prg_p.
287  * It assumes that prg_p->phys_addr is the final phys addr (including offset).
288  * This function modifies prg_p status and data.
289  */
290 int
291 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
292     uint64_t *data_p, boolean_t is_write)
293 {
294 	on_trap_data_t otd;
295 	uint32_t io_stat = 0;
296 	dev_info_t *dip = px_p->px_dip;
297 	px_pec_t *pec_p = px_p->px_pec_p;
298 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
299 	int rval = 0;
300 
301 	/* Alignment checking. */
302 	if (!IS_P2ALIGNED(prg_p->offset, size)) {
303 		DBG(DBG_TOOLS, dip, "not aligned.\n");
304 		prg_p->status = PCITOOL_NOT_ALIGNED;
305 		return (EINVAL);
306 	}
307 
308 	mutex_enter(&pec_p->pec_pokefault_mutex);
309 	pec_p->pec_ontrap_data = &otd;
310 
311 	if (is_write) {
312 		pci_device_t bdf = PX_GET_BDF(prg_p);
313 
314 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
315 			*data_p = pxtool_swap_endian(*data_p, size);
316 
317 		pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
318 
319 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
320 			otd.ot_trampoline = (uintptr_t)&poke_fault;
321 			rval = hvio_poke(px_p->px_dev_hdl, prg_p->phys_addr,
322 			    size, *data_p, bdf, &io_stat);
323 		} else
324 			rval = H_EIO;
325 
326 		if (otd.ot_trap & OT_DATA_ACCESS)
327 			rval = H_EIO;
328 
329 		DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", bdf:0x%x, "
330 		    "rval:%d, io_stat:%d\n", prg_p->phys_addr, bdf,
331 		    rval, io_stat);
332 	} else {
333 
334 		*data_p = 0;
335 
336 		pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
337 
338 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
339 			otd.ot_trampoline = (uintptr_t)&peek_fault;
340 			rval = hvio_peek(px_p->px_dev_hdl, prg_p->phys_addr,
341 			    size, &io_stat, data_p);
342 		} else
343 			rval = H_EIO;
344 
345 		DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", "
346 		    "size:0x%" PRIx64 ", hdl:0x%" PRIx64 ", "
347 		    "rval:%d, io_stat:%d\n", prg_p->phys_addr,
348 		    size, px_p->px_dev_hdl, rval, io_stat);
349 		DBG(DBG_TOOLS, dip, "read data:0x%" PRIx64 "\n", *data_p);
350 
351 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
352 			*data_p = pxtool_swap_endian(*data_p, size);
353 	}
354 
355 	/*
356 	 * Workaround: delay taking down safe access env.
357 	 * For more info, see comment where pxtool_iomem_delay_usec is declared.
358 	 */
359 	if (pxtool_iomem_delay_usec > 0)
360 		delay(drv_usectohz(pxtool_iomem_delay_usec));
361 
362 	no_trap();
363 	pec_p->pec_ontrap_data = NULL;
364 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
365 	mutex_exit(&pec_p->pec_pokefault_mutex);
366 
367 	if (rval != SUCCESS) {
368 		prg_p->status = PCITOOL_INVALID_ADDRESS;
369 		rval = EINVAL;
370 	} else if (io_stat != SUCCESS) {
371 		prg_p->status = PCITOOL_IO_ERROR;
372 		rval = EIO;
373 	} else
374 		prg_p->status = PCITOOL_SUCCESS;
375 
376 	return (rval);
377 }
378 
379 
380 /*ARGSUSED*/
381 int
382 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
383 {
384 	return (SUCCESS);
385 }
386 
387 
388 /*
389  * Perform register accesses on the nexus device itself.
390  */
391 int
392 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
393 {
394 
395 	pcitool_reg_t		prg;
396 	size_t			size;
397 	px_t			*px_p = DIP_TO_STATE(dip);
398 	boolean_t		is_write = B_FALSE;
399 	uint32_t		rval = 0;
400 
401 	if (cmd == PCITOOL_NEXUS_SET_REG)
402 		is_write = B_TRUE;
403 
404 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
405 
406 	/* Read data from userland. */
407 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
408 	    mode) != DDI_SUCCESS) {
409 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
410 		return (EFAULT);
411 	}
412 
413 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
414 
415 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
416 	    prg.bus_no, prg.dev_no, prg.func_no);
417 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
418 	    prg.barnum, prg.offset, prg.acc_attr);
419 	DBG(DBG_TOOLS, dip, "data:0x%" PRIx64 ", phys_addr:0x%" PRIx64 "\n",
420 	    prg.data, prg.phys_addr);
421 
422 	/*
423 	 * If bank num == ff, base phys addr passed in from userland.
424 	 *
425 	 * Normal bank specification is invalid, as there is no OBP property to
426 	 * back it up.
427 	 */
428 	if (prg.barnum != PCITOOL_BASE) {
429 		prg.status = PCITOOL_OUT_OF_RANGE;
430 		rval = EINVAL;
431 		goto done;
432 	}
433 
434 	/* Allow only size of 8-bytes. */
435 	if (size != sizeof (uint64_t)) {
436 		prg.status = PCITOOL_INVALID_SIZE;
437 		rval = EINVAL;
438 		goto done;
439 	}
440 
441 	/* Alignment checking. */
442 	if (!IS_P2ALIGNED(prg.offset, size)) {
443 		DBG(DBG_TOOLS, dip, "not aligned.\n");
444 		prg.status = PCITOOL_NOT_ALIGNED;
445 		rval = EINVAL;
446 		goto done;
447 	}
448 
449 	prg.phys_addr += prg.offset;
450 
451 	/*  XXX do some kind of checking here? */
452 
453 	/* Access device.  prg.status is modified. */
454 	rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data,
455 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write);
456 done:
457 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
458 	    mode) != DDI_SUCCESS) {
459 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
460 		return (EFAULT);
461 	}
462 
463 	return (rval);
464 }
465