xref: /onnv-gate/usr/src/uts/sun4v/io/px/px_tools_4v.c (revision 11245:28613b254aad)
1777Sschwartz /*
2777Sschwartz  * CDDL HEADER START
3777Sschwartz  *
4777Sschwartz  * The contents of this file are subject to the terms of the
51617Sgovinda  * Common Development and Distribution License (the "License").
61617Sgovinda  * You may not use this file except in compliance with the License.
7777Sschwartz  *
8777Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9777Sschwartz  * or http://www.opensolaris.org/os/licensing.
10777Sschwartz  * See the License for the specific language governing permissions
11777Sschwartz  * and limitations under the License.
12777Sschwartz  *
13777Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
14777Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15777Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
16777Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
17777Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
18777Sschwartz  *
19777Sschwartz  * CDDL HEADER END
20777Sschwartz  */
21624Sschwartz /*
2210595SShesha.Sreenivasamurthy@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23624Sschwartz  * Use is subject to license terms.
24624Sschwartz  */
25624Sschwartz 
26624Sschwartz #include <sys/sysmacros.h>
27624Sschwartz #include <sys/machsystm.h>
28624Sschwartz #include <sys/cpuvar.h>
29624Sschwartz #include <sys/ddi_implfuncs.h>
30624Sschwartz #include <sys/hypervisor_api.h>
311691Sschwartz #include <sys/hsvc.h>
32624Sschwartz #include <px_obj.h>
33624Sschwartz #include <sys/pci_tools.h>
34*11245SZhijun.Fu@Sun.COM #include <sys/pci_cfgacc.h>
35624Sschwartz #include <px_tools_var.h>
36624Sschwartz #include "px_lib4v.h"
37777Sschwartz #include <px_tools_ext.h>
38624Sschwartz 
39624Sschwartz /*
40624Sschwartz  * Delay needed to have a safe environment envelop any error which could
41624Sschwartz  * surface.  The larger the number of bridges and switches, the larger the
42624Sschwartz  * number needed here.
43624Sschwartz  *
44624Sschwartz  * Note: this is a workaround until a better solution is found.  While this
45624Sschwartz  * number is high, given enough bridges and switches in the device path, this
46624Sschwartz  * workaround can break.  Also, other PIL 15 interrupts besides the ones we are
47624Sschwartz  * enveloping could delay processing of the interrupt we are trying to protect.
48624Sschwartz  */
49624Sschwartz int pxtool_cfg_delay_usec = 2500;
50624Sschwartz int pxtool_iomem_delay_usec = 25000;
51624Sschwartz 
52624Sschwartz /* Currently there is no way of getting this info from hypervisor. */
53624Sschwartz #define	INTERRUPT_MAPPING_ENTRIES	64
54624Sschwartz 
55624Sschwartz /* Number of inos per root complex. */
56624Sschwartz int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
57624Sschwartz 
581691Sschwartz /* Verify hypervisor version for DIAG functions ra2pa and hpriv. */
591691Sschwartz #define	PXTOOL_HYP_VER_UNINIT	0
601691Sschwartz #define	PXTOOL_HYP_VER_BAD	1
611691Sschwartz #define	PXTOOL_HYP_VER_OK	2
621691Sschwartz 
631691Sschwartz static int pxtool_hyp_version = PXTOOL_HYP_VER_UNINIT;
641691Sschwartz 
65624Sschwartz /* Swap endianness. */
66624Sschwartz static uint64_t
pxtool_swap_endian(uint64_t data,int size)67624Sschwartz pxtool_swap_endian(uint64_t data, int size)
68624Sschwartz {
69624Sschwartz 	typedef union {
70624Sschwartz 		uint64_t data64;
71624Sschwartz 		uint8_t data8[8];
72624Sschwartz 	} data_split_t;
73624Sschwartz 
74624Sschwartz 	data_split_t orig_data;
75624Sschwartz 	data_split_t returned_data;
76624Sschwartz 	int i;
77624Sschwartz 
78624Sschwartz 	orig_data.data64 = data;
79624Sschwartz 	returned_data.data64 = 0;
80624Sschwartz 
81624Sschwartz 	for (i = 0; i < size; i++) {
82624Sschwartz 		returned_data.data8[7 - i] = orig_data.data8[8 - size + i];
83624Sschwartz 	}
84624Sschwartz 
85624Sschwartz 	return (returned_data.data64);
86624Sschwartz }
87624Sschwartz 
881691Sschwartz static void
pxtool_validate_diag_hyp_svc(dev_info_t * dip,int * diag_svc_status_p)891691Sschwartz pxtool_validate_diag_hyp_svc(dev_info_t *dip, int *diag_svc_status_p)
901691Sschwartz {
911691Sschwartz 	uint64_t pxtool_diag_maj_ver;
921691Sschwartz 	uint64_t pxtool_diag_min_ver;
931691Sschwartz 	int ret;
941691Sschwartz 
951691Sschwartz 	if (*diag_svc_status_p == PXTOOL_HYP_VER_UNINIT) {
961691Sschwartz 
971691Sschwartz 		*diag_svc_status_p = PXTOOL_HYP_VER_BAD;
981691Sschwartz 
991691Sschwartz 		/*
1001691Sschwartz 		 * Verify that hypervisor DIAG API has been
1011691Sschwartz 		 * negotiated (by unix).
1021691Sschwartz 		 */
1031691Sschwartz 		if ((ret = hsvc_version(HSVC_GROUP_DIAG,
1041691Sschwartz 		    &pxtool_diag_maj_ver, &pxtool_diag_min_ver)) != 0) {
1051691Sschwartz 			DBG(DBG_TOOLS, dip,
1061691Sschwartz 			    "diag hypervisor svc not negotiated: "
1071691Sschwartz 			    "grp:0x%lx, errno:%d\n", HSVC_GROUP_DIAG, ret);
1081691Sschwartz 
1091691Sschwartz 		} else if (pxtool_diag_maj_ver == 1) {
1101691Sschwartz 			/*
1111691Sschwartz 			 * Major version 1 is OK.
1121691Sschwartz 			 *
1131691Sschwartz 			 * Code maintainers: if the version changes, check for
1141691Sschwartz 			 * API changes in hv_ra2pa() and hv_hpriv() before
1151691Sschwartz 			 * accepting the new version.
1161691Sschwartz 			 */
1171691Sschwartz 			*diag_svc_status_p = PXTOOL_HYP_VER_OK;
1181691Sschwartz 
1191691Sschwartz 		} else {
1201691Sschwartz 			DBG(DBG_TOOLS, dip,
1211691Sschwartz 			    "diag hypervisor svc: bad major number: "
1221691Sschwartz 			    "grp:0x%lx, maj:0x%lx, min:0x%lx\n",
1231691Sschwartz 			    HSVC_GROUP_DIAG, pxtool_diag_maj_ver,
1241691Sschwartz 			    pxtool_diag_min_ver);
1251691Sschwartz 		}
1261691Sschwartz 	}
1271691Sschwartz }
1281691Sschwartz 
1291064Sschwartz static int
pxtool_phys_access(px_t * px_p,uintptr_t dev_addr,uint64_t * data_p,boolean_t is_big_endian,boolean_t is_write)1301064Sschwartz pxtool_phys_access(px_t *px_p, uintptr_t dev_addr,
1311064Sschwartz     uint64_t *data_p, boolean_t is_big_endian, boolean_t is_write)
1321064Sschwartz {
1331064Sschwartz 	uint64_t rfunc, pfunc;
1341064Sschwartz 	uint64_t rdata_addr, pdata_addr;
1351064Sschwartz 	uint64_t to_addr, from_addr;
1361064Sschwartz 	uint64_t local_data;
1371064Sschwartz 	int rval;
1381064Sschwartz 	dev_info_t *dip = px_p->px_dip;
1391064Sschwartz 
1401064Sschwartz 	DBG(DBG_TOOLS, dip,
1411064Sschwartz 	    "pxtool_phys_access: dev_addr:0x%" PRIx64 "\n", dev_addr);
1421064Sschwartz 	DBG(DBG_TOOLS, dip, "    data_addr:0x%" PRIx64 ", is_write:%s\n",
1431064Sschwartz 	    data_p, (is_write ? "yes" : "no"));
1441064Sschwartz 
1451691Sschwartz 	if (pxtool_hyp_version != PXTOOL_HYP_VER_OK) {
1461691Sschwartz 		pxtool_validate_diag_hyp_svc(dip, &pxtool_hyp_version);
1471691Sschwartz 		if (pxtool_hyp_version != PXTOOL_HYP_VER_OK) {
1481691Sschwartz 			DBG(DBG_TOOLS, dip, "Couldn't validate diag hyp svc\n");
1491691Sschwartz 			return (EPERM);
1501691Sschwartz 		}
1511691Sschwartz 	}
1521691Sschwartz 
1531064Sschwartz 	if ((rfunc = va_to_pa((void *)px_phys_acc_4v))  == (uint64_t)-1) {
1541064Sschwartz 		DBG(DBG_TOOLS, dip, "Error getting real addr for function\n");
1551064Sschwartz 		return (EIO);
1561064Sschwartz 	}
1571064Sschwartz 
1581064Sschwartz 	if ((pfunc = hv_ra2pa(rfunc)) == -1) {
1591064Sschwartz 		DBG(DBG_TOOLS, dip, "Error getting phys addr for function\n");
1601064Sschwartz 		return (EIO);
1611064Sschwartz 	}
1621064Sschwartz 
1631064Sschwartz 	if ((rdata_addr = va_to_pa((void *)&local_data))  == (uint64_t)-1) {
1641064Sschwartz 		DBG(DBG_TOOLS, dip, "Error getting real addr for data_p\n");
1651064Sschwartz 		return (EIO);
1661064Sschwartz 	}
1671064Sschwartz 
1681064Sschwartz 	if ((pdata_addr = hv_ra2pa(rdata_addr)) == -1) {
1691064Sschwartz 		DBG(DBG_TOOLS, dip, "Error getting phys addr for data ptr\n");
1701064Sschwartz 		return (EIO);
1711064Sschwartz 	}
1721064Sschwartz 
1731064Sschwartz 	if (is_write) {
1741064Sschwartz 		to_addr = dev_addr;
1751064Sschwartz 		from_addr = pdata_addr;
1761064Sschwartz 
1771064Sschwartz 		if (is_big_endian)
1781064Sschwartz 			local_data = *data_p;
1791064Sschwartz 		else
1801064Sschwartz 			local_data =
1811064Sschwartz 			    pxtool_swap_endian(*data_p, sizeof (uint64_t));
1821064Sschwartz 	} else {
1831064Sschwartz 		to_addr = pdata_addr;
1841064Sschwartz 		from_addr = dev_addr;
1851064Sschwartz 	}
1861064Sschwartz 
1871064Sschwartz 	rval = hv_hpriv((void *)pfunc, from_addr, to_addr, NULL);
1881064Sschwartz 	switch (rval) {
1891064Sschwartz 	case H_ENOACCESS:	/* Returned by non-debug hypervisor. */
1901064Sschwartz 		rval = ENOTSUP;
1911064Sschwartz 		break;
1921064Sschwartz 	case H_EOK:
1931064Sschwartz 		rval = SUCCESS;
1941064Sschwartz 		break;
1951064Sschwartz 	default:
1961064Sschwartz 		rval = EIO;
1971064Sschwartz 		break;
1981064Sschwartz 	}
1991064Sschwartz 
2001064Sschwartz 	if ((rval == SUCCESS) && (!is_write)) {
2011064Sschwartz 		if (is_big_endian)
2021064Sschwartz 			*data_p = local_data;
2031064Sschwartz 		else
2041064Sschwartz 			*data_p =
2051064Sschwartz 			    pxtool_swap_endian(local_data, sizeof (uint64_t));
2061064Sschwartz 	}
2071064Sschwartz 
2081064Sschwartz 	return (rval);
2091064Sschwartz }
2101064Sschwartz 
211624Sschwartz /*
212624Sschwartz  * This function is for PCI config space access.
213624Sschwartz  * It assumes that offset, bdf, acc_attr are valid in prg_p.
214624Sschwartz  * This function modifies prg_p status and data.
2151064Sschwartz  *
2161064Sschwartz  * prg_p->phys_addr isn't used.
217624Sschwartz  */
218624Sschwartz 
219624Sschwartz int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)220624Sschwartz pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
2211064Sschwartz     uint64_t *data_p, boolean_t is_write)
222624Sschwartz {
223624Sschwartz 	pci_cfg_data_t data;
224624Sschwartz 	on_trap_data_t otd;
225624Sschwartz 	dev_info_t *dip = px_p->px_dip;
226624Sschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
227624Sschwartz 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
228624Sschwartz 	int rval = 0;
229*11245SZhijun.Fu@Sun.COM 	pci_cfgacc_req_t req;
230*11245SZhijun.Fu@Sun.COM 
231*11245SZhijun.Fu@Sun.COM 	if ((size <= 0) || (size > 8)) {
232*11245SZhijun.Fu@Sun.COM 		DBG(DBG_TOOLS, dip, "not supported size.\n");
233*11245SZhijun.Fu@Sun.COM 		prg_p->status = PCITOOL_INVALID_SIZE;
234*11245SZhijun.Fu@Sun.COM 		return (ENOTSUP);
235*11245SZhijun.Fu@Sun.COM 	}
236624Sschwartz 
237624Sschwartz 	/* Alignment checking. */
238624Sschwartz 	if (!IS_P2ALIGNED(prg_p->offset, size)) {
239624Sschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
240624Sschwartz 		prg_p->status = PCITOOL_NOT_ALIGNED;
241624Sschwartz 		return (EINVAL);
242624Sschwartz 	}
243624Sschwartz 
244624Sschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
245624Sschwartz 	pec_p->pec_ontrap_data = &otd;
246624Sschwartz 
247*11245SZhijun.Fu@Sun.COM 	req.rcdip = dip;
248*11245SZhijun.Fu@Sun.COM 	req.bdf = PCI_GETBDF(prg_p->bus_no, prg_p->dev_no, prg_p->func_no);
249*11245SZhijun.Fu@Sun.COM 	req.offset = prg_p->offset;
250*11245SZhijun.Fu@Sun.COM 	req.size = size;
251*11245SZhijun.Fu@Sun.COM 	req.write = is_write;
252624Sschwartz 	if (is_write) {
253624Sschwartz 
254624Sschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
255624Sschwartz 			data.qw = pxtool_swap_endian(*data_p, size);
256624Sschwartz 		else
257624Sschwartz 			data.qw = *data_p;
258624Sschwartz 
259624Sschwartz 		switch (size) {
260624Sschwartz 			case sizeof (uint8_t):
261624Sschwartz 				data.b = (uint8_t)data.qw;
262624Sschwartz 				break;
263624Sschwartz 			case sizeof (uint16_t):
264624Sschwartz 				data.w = (uint16_t)data.qw;
265624Sschwartz 				break;
266624Sschwartz 			case sizeof (uint32_t):
267624Sschwartz 				data.dw = (uint32_t)data.qw;
268624Sschwartz 				break;
269624Sschwartz 			case sizeof (uint64_t):
270624Sschwartz 				break;
271624Sschwartz 		}
272624Sschwartz 
273*11245SZhijun.Fu@Sun.COM 		DBG(DBG_TOOLS, dip, "put: bdf:%d,%d,%d, off:0x%"PRIx64", size:"
274*11245SZhijun.Fu@Sun.COM 		    "0x%"PRIx64", data:0x%"PRIx64"\n",
275*11245SZhijun.Fu@Sun.COM 		    prg_p->bus_no, prg_p->dev_no, prg_p->func_no,
276*11245SZhijun.Fu@Sun.COM 		    prg_p->offset, size, data.qw);
277624Sschwartz 
278624Sschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
279624Sschwartz 
280624Sschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
281624Sschwartz 			otd.ot_trampoline = (uintptr_t)&poke_fault;
282*11245SZhijun.Fu@Sun.COM 			VAL64(&req) = data.qw;
283*11245SZhijun.Fu@Sun.COM 			pci_cfgacc_acc(&req);
284624Sschwartz 		} else
285624Sschwartz 			rval = H_EIO;
286624Sschwartz 
287624Sschwartz 		if (otd.ot_trap & OT_DATA_ACCESS)
288624Sschwartz 			rval = H_EIO;
289624Sschwartz 
290624Sschwartz 	} else {
291624Sschwartz 
292624Sschwartz 		data.qw = 0;
293624Sschwartz 
294624Sschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
295624Sschwartz 
296624Sschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
297624Sschwartz 			otd.ot_trampoline = (uintptr_t)&peek_fault;
298*11245SZhijun.Fu@Sun.COM 			pci_cfgacc_acc(&req);
299*11245SZhijun.Fu@Sun.COM 			data.qw = VAL64(&req);
300624Sschwartz 		} else
301624Sschwartz 			rval = H_EIO;
302624Sschwartz 
303*11245SZhijun.Fu@Sun.COM 		switch (size) {
304*11245SZhijun.Fu@Sun.COM 			case sizeof (uint8_t):
305*11245SZhijun.Fu@Sun.COM 				data.qw = (uint64_t)data.b;
306*11245SZhijun.Fu@Sun.COM 				break;
307*11245SZhijun.Fu@Sun.COM 			case sizeof (uint16_t):
308*11245SZhijun.Fu@Sun.COM 				data.qw = (uint64_t)data.w;
309*11245SZhijun.Fu@Sun.COM 				break;
310*11245SZhijun.Fu@Sun.COM 			case sizeof (uint32_t):
311*11245SZhijun.Fu@Sun.COM 				data.qw = (uint64_t)data.dw;
312*11245SZhijun.Fu@Sun.COM 				break;
313*11245SZhijun.Fu@Sun.COM 			case sizeof (uint64_t):
314*11245SZhijun.Fu@Sun.COM 				break;
315*11245SZhijun.Fu@Sun.COM 		}
316*11245SZhijun.Fu@Sun.COM 
317*11245SZhijun.Fu@Sun.COM 		DBG(DBG_TOOLS, dip, "get: bdf:%d,%d,%d, off:0x%"PRIx64", size:"
318*11245SZhijun.Fu@Sun.COM 		    "0x%"PRIx64", data:0x%"PRIx64"\n",
319*11245SZhijun.Fu@Sun.COM 		    prg_p->bus_no, prg_p->dev_no, prg_p->func_no,
320*11245SZhijun.Fu@Sun.COM 		    prg_p->offset, size, data.qw);
32110595SShesha.Sreenivasamurthy@Sun.COM 		*data_p = data.qw;
322624Sschwartz 
323624Sschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
324624Sschwartz 			*data_p = pxtool_swap_endian(*data_p, size);
325624Sschwartz 	}
326624Sschwartz 
327624Sschwartz 	/*
328624Sschwartz 	 * Workaround: delay taking down safe access env.
329624Sschwartz 	 * For more info, see comments where pxtool_cfg_delay_usec is declared.
330624Sschwartz 	 */
331624Sschwartz 	if (pxtool_cfg_delay_usec > 0)
332624Sschwartz 		drv_usecwait(pxtool_cfg_delay_usec);
333624Sschwartz 
334624Sschwartz 	no_trap();
335624Sschwartz 	pec_p->pec_ontrap_data = NULL;
336624Sschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
337624Sschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
338624Sschwartz 
339624Sschwartz 	if (rval != SUCCESS) {
340624Sschwartz 		prg_p->status = PCITOOL_INVALID_ADDRESS;
341624Sschwartz 		rval = EINVAL;
342624Sschwartz 	} else
343624Sschwartz 		prg_p->status = PCITOOL_SUCCESS;
344624Sschwartz 
345624Sschwartz 	return (rval);
346624Sschwartz }
347624Sschwartz 
348624Sschwartz 
349624Sschwartz /*
350624Sschwartz  * This function is for PCI IO space and memory space access.
351624Sschwartz  * It assumes that offset, bdf, acc_attr are current in prg_p.
352624Sschwartz  * It assumes that prg_p->phys_addr is the final phys addr (including offset).
353624Sschwartz  * This function modifies prg_p status and data.
354624Sschwartz  */
355624Sschwartz int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)3561064Sschwartz pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
357624Sschwartz     uint64_t *data_p, boolean_t is_write)
358624Sschwartz {
359624Sschwartz 	on_trap_data_t otd;
360624Sschwartz 	uint32_t io_stat = 0;
361624Sschwartz 	dev_info_t *dip = px_p->px_dip;
362624Sschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
363624Sschwartz 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
364624Sschwartz 	int rval = 0;
365624Sschwartz 
366624Sschwartz 	/* Alignment checking. */
367624Sschwartz 	if (!IS_P2ALIGNED(prg_p->offset, size)) {
368624Sschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
369624Sschwartz 		prg_p->status = PCITOOL_NOT_ALIGNED;
370624Sschwartz 		return (EINVAL);
371624Sschwartz 	}
372624Sschwartz 
373624Sschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
374624Sschwartz 	pec_p->pec_ontrap_data = &otd;
375624Sschwartz 
376624Sschwartz 	if (is_write) {
377624Sschwartz 		pci_device_t bdf = PX_GET_BDF(prg_p);
378624Sschwartz 
379624Sschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
380624Sschwartz 			*data_p = pxtool_swap_endian(*data_p, size);
381624Sschwartz 
382624Sschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
383624Sschwartz 
384624Sschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
385624Sschwartz 			otd.ot_trampoline = (uintptr_t)&poke_fault;
386624Sschwartz 			rval = hvio_poke(px_p->px_dev_hdl, prg_p->phys_addr,
387624Sschwartz 			    size, *data_p, bdf, &io_stat);
388624Sschwartz 		} else
389624Sschwartz 			rval = H_EIO;
390624Sschwartz 
391624Sschwartz 		if (otd.ot_trap & OT_DATA_ACCESS)
392624Sschwartz 			rval = H_EIO;
393624Sschwartz 
394624Sschwartz 		DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", bdf:0x%x, "
395624Sschwartz 		    "rval:%d, io_stat:%d\n", prg_p->phys_addr, bdf,
396624Sschwartz 		    rval, io_stat);
397624Sschwartz 	} else {
398624Sschwartz 
399624Sschwartz 		*data_p = 0;
400624Sschwartz 
401624Sschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
402624Sschwartz 
403624Sschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
404624Sschwartz 			otd.ot_trampoline = (uintptr_t)&peek_fault;
405624Sschwartz 			rval = hvio_peek(px_p->px_dev_hdl, prg_p->phys_addr,
406624Sschwartz 			    size, &io_stat, data_p);
407624Sschwartz 		} else
408624Sschwartz 			rval = H_EIO;
409624Sschwartz 
410624Sschwartz 		DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", "
411624Sschwartz 		    "size:0x%" PRIx64 ", hdl:0x%" PRIx64 ", "
412624Sschwartz 		    "rval:%d, io_stat:%d\n", prg_p->phys_addr,
413624Sschwartz 		    size, px_p->px_dev_hdl, rval, io_stat);
414624Sschwartz 		DBG(DBG_TOOLS, dip, "read data:0x%" PRIx64 "\n", *data_p);
415624Sschwartz 
416624Sschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
417624Sschwartz 			*data_p = pxtool_swap_endian(*data_p, size);
418624Sschwartz 	}
419624Sschwartz 
420624Sschwartz 	/*
421624Sschwartz 	 * Workaround: delay taking down safe access env.
422624Sschwartz 	 * For more info, see comment where pxtool_iomem_delay_usec is declared.
423624Sschwartz 	 */
424624Sschwartz 	if (pxtool_iomem_delay_usec > 0)
425624Sschwartz 		delay(drv_usectohz(pxtool_iomem_delay_usec));
426624Sschwartz 
427624Sschwartz 	no_trap();
428624Sschwartz 	pec_p->pec_ontrap_data = NULL;
429624Sschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
430624Sschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
431624Sschwartz 
432624Sschwartz 	if (rval != SUCCESS) {
433624Sschwartz 		prg_p->status = PCITOOL_INVALID_ADDRESS;
434624Sschwartz 		rval = EINVAL;
435624Sschwartz 	} else if (io_stat != SUCCESS) {
436624Sschwartz 		prg_p->status = PCITOOL_IO_ERROR;
437624Sschwartz 		rval = EIO;
438624Sschwartz 	} else
439624Sschwartz 		prg_p->status = PCITOOL_SUCCESS;
440624Sschwartz 
441624Sschwartz 	return (rval);
442624Sschwartz }
443624Sschwartz 
444624Sschwartz 
445624Sschwartz /*ARGSUSED*/
446624Sschwartz int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)447624Sschwartz pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
448624Sschwartz {
449624Sschwartz 	return (SUCCESS);
450624Sschwartz }
451624Sschwartz 
452624Sschwartz 
453624Sschwartz /*
454624Sschwartz  * Perform register accesses on the nexus device itself.
455624Sschwartz  */
456624Sschwartz int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)457624Sschwartz pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
458624Sschwartz {
459624Sschwartz 
460624Sschwartz 	pcitool_reg_t		prg;
461624Sschwartz 	size_t			size;
462624Sschwartz 	px_t			*px_p = DIP_TO_STATE(dip);
463624Sschwartz 	boolean_t		is_write = B_FALSE;
464624Sschwartz 	uint32_t		rval = 0;
465624Sschwartz 
466624Sschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
467624Sschwartz 		is_write = B_TRUE;
468624Sschwartz 
469624Sschwartz 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
470624Sschwartz 
471624Sschwartz 	/* Read data from userland. */
472624Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
473624Sschwartz 	    mode) != DDI_SUCCESS) {
474624Sschwartz 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
475624Sschwartz 		return (EFAULT);
476624Sschwartz 	}
477624Sschwartz 
478624Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
479624Sschwartz 
480624Sschwartz 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
481624Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
482624Sschwartz 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
483624Sschwartz 	    prg.barnum, prg.offset, prg.acc_attr);
484624Sschwartz 	DBG(DBG_TOOLS, dip, "data:0x%" PRIx64 ", phys_addr:0x%" PRIx64 "\n",
485624Sschwartz 	    prg.data, prg.phys_addr);
486624Sschwartz 
487624Sschwartz 	/*
488624Sschwartz 	 * If bank num == ff, base phys addr passed in from userland.
489624Sschwartz 	 *
490624Sschwartz 	 * Normal bank specification is invalid, as there is no OBP property to
491624Sschwartz 	 * back it up.
492624Sschwartz 	 */
493624Sschwartz 	if (prg.barnum != PCITOOL_BASE) {
494624Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
495624Sschwartz 		rval = EINVAL;
496624Sschwartz 		goto done;
497624Sschwartz 	}
498624Sschwartz 
499624Sschwartz 	/* Allow only size of 8-bytes. */
500624Sschwartz 	if (size != sizeof (uint64_t)) {
501624Sschwartz 		prg.status = PCITOOL_INVALID_SIZE;
502624Sschwartz 		rval = EINVAL;
503624Sschwartz 		goto done;
504624Sschwartz 	}
505624Sschwartz 
506624Sschwartz 	/* Alignment checking. */
507624Sschwartz 	if (!IS_P2ALIGNED(prg.offset, size)) {
508624Sschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
509624Sschwartz 		prg.status = PCITOOL_NOT_ALIGNED;
510624Sschwartz 		rval = EINVAL;
511624Sschwartz 		goto done;
512624Sschwartz 	}
513624Sschwartz 
514624Sschwartz 	prg.phys_addr += prg.offset;
515624Sschwartz 
5162276Sschwartz 	/*
5172276Sschwartz 	 * Only the hypervisor can access nexus registers.  As a result, there
5182276Sschwartz 	 * can be no error recovery in the OS.  If there is an error, the
5192276Sschwartz 	 * system will go down, but with a trap type 7f.  The OS cannot
5202276Sschwartz 	 * intervene with this kind of trap.
5212276Sschwartz 	 */
522624Sschwartz 
523624Sschwartz 	/* Access device.  prg.status is modified. */
524624Sschwartz 	rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data,
525624Sschwartz 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write);
526624Sschwartz done:
5274397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
528624Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
529624Sschwartz 	    mode) != DDI_SUCCESS) {
530624Sschwartz 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
531624Sschwartz 		return (EFAULT);
532624Sschwartz 	}
533624Sschwartz 
534624Sschwartz 	return (rval);
535624Sschwartz }
536