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