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