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