xref: /illumos-gate/usr/src/uts/sun4v/io/px/px_tools_4v.c (revision e214b19eaa16fec1fa60a97227778103f598336f)
169cd775fSschwartz /*
2d4476ccbSschwartz  * CDDL HEADER START
3d4476ccbSschwartz  *
4d4476ccbSschwartz  * The contents of this file are subject to the terms of the
544bb982bSgovinda  * Common Development and Distribution License (the "License").
644bb982bSgovinda  * You may not use this file except in compliance with the License.
7d4476ccbSschwartz  *
8d4476ccbSschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d4476ccbSschwartz  * or http://www.opensolaris.org/os/licensing.
10d4476ccbSschwartz  * See the License for the specific language governing permissions
11d4476ccbSschwartz  * and limitations under the License.
12d4476ccbSschwartz  *
13d4476ccbSschwartz  * When distributing Covered Code, include this CDDL HEADER in each
14d4476ccbSschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d4476ccbSschwartz  * If applicable, add the following below this CDDL HEADER, with the
16d4476ccbSschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
17d4476ccbSschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
18d4476ccbSschwartz  *
19d4476ccbSschwartz  * CDDL HEADER END
20d4476ccbSschwartz  */
21d4476ccbSschwartz /*
22abdf5d9aSShesha Sreenivasamurthy  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2369cd775fSschwartz  * Use is subject to license terms.
2469cd775fSschwartz  */
2569cd775fSschwartz 
2669cd775fSschwartz #include <sys/sysmacros.h>
2769cd775fSschwartz #include <sys/machsystm.h>
2869cd775fSschwartz #include <sys/cpuvar.h>
2969cd775fSschwartz #include <sys/ddi_implfuncs.h>
3069cd775fSschwartz #include <sys/hypervisor_api.h>
310ad689d6Sschwartz #include <sys/hsvc.h>
3269cd775fSschwartz #include <px_obj.h>
3369cd775fSschwartz #include <sys/pci_tools.h>
34c0da6274SZhi-Jun Robin Fu #include <sys/pci_cfgacc.h>
3569cd775fSschwartz #include <px_tools_var.h>
3669cd775fSschwartz #include "px_lib4v.h"
37d4476ccbSschwartz #include <px_tools_ext.h>
3869cd775fSschwartz 
3969cd775fSschwartz /*
4069cd775fSschwartz  * Delay needed to have a safe environment envelop any error which could
4169cd775fSschwartz  * surface.  The larger the number of bridges and switches, the larger the
4269cd775fSschwartz  * number needed here.
4369cd775fSschwartz  *
4469cd775fSschwartz  * Note: this is a workaround until a better solution is found.  While this
4569cd775fSschwartz  * number is high, given enough bridges and switches in the device path, this
4669cd775fSschwartz  * workaround can break.  Also, other PIL 15 interrupts besides the ones we are
4769cd775fSschwartz  * enveloping could delay processing of the interrupt we are trying to protect.
4869cd775fSschwartz  */
4969cd775fSschwartz int pxtool_cfg_delay_usec = 2500;
5069cd775fSschwartz int pxtool_iomem_delay_usec = 25000;
5169cd775fSschwartz 
5269cd775fSschwartz /* Currently there is no way of getting this info from hypervisor. */
5369cd775fSschwartz #define	INTERRUPT_MAPPING_ENTRIES	64
5469cd775fSschwartz 
5569cd775fSschwartz /* Number of inos per root complex. */
5669cd775fSschwartz int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
5769cd775fSschwartz 
580ad689d6Sschwartz /* Verify hypervisor version for DIAG functions ra2pa and hpriv. */
590ad689d6Sschwartz #define	PXTOOL_HYP_VER_UNINIT	0
600ad689d6Sschwartz #define	PXTOOL_HYP_VER_BAD	1
610ad689d6Sschwartz #define	PXTOOL_HYP_VER_OK	2
620ad689d6Sschwartz 
630ad689d6Sschwartz static int pxtool_hyp_version = PXTOOL_HYP_VER_UNINIT;
640ad689d6Sschwartz 
6569cd775fSschwartz /* Swap endianness. */
6669cd775fSschwartz static uint64_t
pxtool_swap_endian(uint64_t data,int size)6769cd775fSschwartz pxtool_swap_endian(uint64_t data, int size)
6869cd775fSschwartz {
6969cd775fSschwartz 	typedef union {
7069cd775fSschwartz 		uint64_t data64;
7169cd775fSschwartz 		uint8_t data8[8];
7269cd775fSschwartz 	} data_split_t;
7369cd775fSschwartz 
7469cd775fSschwartz 	data_split_t orig_data;
7569cd775fSschwartz 	data_split_t returned_data;
7669cd775fSschwartz 	int i;
7769cd775fSschwartz 
7869cd775fSschwartz 	orig_data.data64 = data;
7969cd775fSschwartz 	returned_data.data64 = 0;
8069cd775fSschwartz 
8169cd775fSschwartz 	for (i = 0; i < size; i++) {
8269cd775fSschwartz 		returned_data.data8[7 - i] = orig_data.data8[8 - size + i];
8369cd775fSschwartz 	}
8469cd775fSschwartz 
8569cd775fSschwartz 	return (returned_data.data64);
8669cd775fSschwartz }
8769cd775fSschwartz 
880ad689d6Sschwartz static void
pxtool_validate_diag_hyp_svc(dev_info_t * dip,int * diag_svc_status_p)890ad689d6Sschwartz pxtool_validate_diag_hyp_svc(dev_info_t *dip, int *diag_svc_status_p)
900ad689d6Sschwartz {
910ad689d6Sschwartz 	uint64_t pxtool_diag_maj_ver;
920ad689d6Sschwartz 	uint64_t pxtool_diag_min_ver;
930ad689d6Sschwartz 	int ret;
940ad689d6Sschwartz 
950ad689d6Sschwartz 	if (*diag_svc_status_p == PXTOOL_HYP_VER_UNINIT) {
960ad689d6Sschwartz 
970ad689d6Sschwartz 		*diag_svc_status_p = PXTOOL_HYP_VER_BAD;
980ad689d6Sschwartz 
990ad689d6Sschwartz 		/*
1000ad689d6Sschwartz 		 * Verify that hypervisor DIAG API has been
1010ad689d6Sschwartz 		 * negotiated (by unix).
1020ad689d6Sschwartz 		 */
1030ad689d6Sschwartz 		if ((ret = hsvc_version(HSVC_GROUP_DIAG,
1040ad689d6Sschwartz 		    &pxtool_diag_maj_ver, &pxtool_diag_min_ver)) != 0) {
1050ad689d6Sschwartz 			DBG(DBG_TOOLS, dip,
1060ad689d6Sschwartz 			    "diag hypervisor svc not negotiated: "
1070ad689d6Sschwartz 			    "grp:0x%lx, errno:%d\n", HSVC_GROUP_DIAG, ret);
1080ad689d6Sschwartz 
1090ad689d6Sschwartz 		} else if (pxtool_diag_maj_ver == 1) {
1100ad689d6Sschwartz 			/*
1110ad689d6Sschwartz 			 * Major version 1 is OK.
1120ad689d6Sschwartz 			 *
1130ad689d6Sschwartz 			 * Code maintainers: if the version changes, check for
1140ad689d6Sschwartz 			 * API changes in hv_ra2pa() and hv_hpriv() before
1150ad689d6Sschwartz 			 * accepting the new version.
1160ad689d6Sschwartz 			 */
1170ad689d6Sschwartz 			*diag_svc_status_p = PXTOOL_HYP_VER_OK;
1180ad689d6Sschwartz 
1190ad689d6Sschwartz 		} else {
1200ad689d6Sschwartz 			DBG(DBG_TOOLS, dip,
1210ad689d6Sschwartz 			    "diag hypervisor svc: bad major number: "
1220ad689d6Sschwartz 			    "grp:0x%lx, maj:0x%lx, min:0x%lx\n",
1230ad689d6Sschwartz 			    HSVC_GROUP_DIAG, pxtool_diag_maj_ver,
1240ad689d6Sschwartz 			    pxtool_diag_min_ver);
1250ad689d6Sschwartz 		}
1260ad689d6Sschwartz 	}
1270ad689d6Sschwartz }
1280ad689d6Sschwartz 
129dabea0dbSschwartz 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)130dabea0dbSschwartz pxtool_phys_access(px_t *px_p, uintptr_t dev_addr,
131dabea0dbSschwartz     uint64_t *data_p, boolean_t is_big_endian, boolean_t is_write)
132dabea0dbSschwartz {
133dabea0dbSschwartz 	uint64_t rfunc, pfunc;
134dabea0dbSschwartz 	uint64_t rdata_addr, pdata_addr;
135dabea0dbSschwartz 	uint64_t to_addr, from_addr;
136dabea0dbSschwartz 	uint64_t local_data;
137dabea0dbSschwartz 	int rval;
138dabea0dbSschwartz 	dev_info_t *dip = px_p->px_dip;
139dabea0dbSschwartz 
140dabea0dbSschwartz 	DBG(DBG_TOOLS, dip,
141dabea0dbSschwartz 	    "pxtool_phys_access: dev_addr:0x%" PRIx64 "\n", dev_addr);
142dabea0dbSschwartz 	DBG(DBG_TOOLS, dip, "    data_addr:0x%" PRIx64 ", is_write:%s\n",
143dabea0dbSschwartz 	    data_p, (is_write ? "yes" : "no"));
144dabea0dbSschwartz 
1450ad689d6Sschwartz 	if (pxtool_hyp_version != PXTOOL_HYP_VER_OK) {
1460ad689d6Sschwartz 		pxtool_validate_diag_hyp_svc(dip, &pxtool_hyp_version);
1470ad689d6Sschwartz 		if (pxtool_hyp_version != PXTOOL_HYP_VER_OK) {
1480ad689d6Sschwartz 			DBG(DBG_TOOLS, dip, "Couldn't validate diag hyp svc\n");
1490ad689d6Sschwartz 			return (EPERM);
1500ad689d6Sschwartz 		}
1510ad689d6Sschwartz 	}
1520ad689d6Sschwartz 
153dabea0dbSschwartz 	if ((rfunc = va_to_pa((void *)px_phys_acc_4v))  == (uint64_t)-1) {
154dabea0dbSschwartz 		DBG(DBG_TOOLS, dip, "Error getting real addr for function\n");
155dabea0dbSschwartz 		return (EIO);
156dabea0dbSschwartz 	}
157dabea0dbSschwartz 
158dabea0dbSschwartz 	if ((pfunc = hv_ra2pa(rfunc)) == -1) {
159dabea0dbSschwartz 		DBG(DBG_TOOLS, dip, "Error getting phys addr for function\n");
160dabea0dbSschwartz 		return (EIO);
161dabea0dbSschwartz 	}
162dabea0dbSschwartz 
163dabea0dbSschwartz 	if ((rdata_addr = va_to_pa((void *)&local_data))  == (uint64_t)-1) {
164dabea0dbSschwartz 		DBG(DBG_TOOLS, dip, "Error getting real addr for data_p\n");
165dabea0dbSschwartz 		return (EIO);
166dabea0dbSschwartz 	}
167dabea0dbSschwartz 
168dabea0dbSschwartz 	if ((pdata_addr = hv_ra2pa(rdata_addr)) == -1) {
169dabea0dbSschwartz 		DBG(DBG_TOOLS, dip, "Error getting phys addr for data ptr\n");
170dabea0dbSschwartz 		return (EIO);
171dabea0dbSschwartz 	}
172dabea0dbSschwartz 
173dabea0dbSschwartz 	if (is_write) {
174dabea0dbSschwartz 		to_addr = dev_addr;
175dabea0dbSschwartz 		from_addr = pdata_addr;
176dabea0dbSschwartz 
177dabea0dbSschwartz 		if (is_big_endian)
178dabea0dbSschwartz 			local_data = *data_p;
179dabea0dbSschwartz 		else
180dabea0dbSschwartz 			local_data =
181dabea0dbSschwartz 			    pxtool_swap_endian(*data_p, sizeof (uint64_t));
182dabea0dbSschwartz 	} else {
183dabea0dbSschwartz 		to_addr = pdata_addr;
184dabea0dbSschwartz 		from_addr = dev_addr;
185dabea0dbSschwartz 	}
186dabea0dbSschwartz 
187*e214b19eSToomas Soome 	rval = hv_hpriv((void *)pfunc, from_addr, to_addr, 0);
188dabea0dbSschwartz 	switch (rval) {
189dabea0dbSschwartz 	case H_ENOACCESS:	/* Returned by non-debug hypervisor. */
190dabea0dbSschwartz 		rval = ENOTSUP;
191dabea0dbSschwartz 		break;
192dabea0dbSschwartz 	case H_EOK:
193dabea0dbSschwartz 		rval = SUCCESS;
194dabea0dbSschwartz 		break;
195dabea0dbSschwartz 	default:
196dabea0dbSschwartz 		rval = EIO;
197dabea0dbSschwartz 		break;
198dabea0dbSschwartz 	}
199dabea0dbSschwartz 
200dabea0dbSschwartz 	if ((rval == SUCCESS) && (!is_write)) {
201dabea0dbSschwartz 		if (is_big_endian)
202dabea0dbSschwartz 			*data_p = local_data;
203dabea0dbSschwartz 		else
204dabea0dbSschwartz 			*data_p =
205dabea0dbSschwartz 			    pxtool_swap_endian(local_data, sizeof (uint64_t));
206dabea0dbSschwartz 	}
207dabea0dbSschwartz 
208dabea0dbSschwartz 	return (rval);
209dabea0dbSschwartz }
210dabea0dbSschwartz 
21169cd775fSschwartz /*
21269cd775fSschwartz  * This function is for PCI config space access.
21369cd775fSschwartz  * It assumes that offset, bdf, acc_attr are valid in prg_p.
21469cd775fSschwartz  * This function modifies prg_p status and data.
215dabea0dbSschwartz  *
216dabea0dbSschwartz  * prg_p->phys_addr isn't used.
21769cd775fSschwartz  */
21869cd775fSschwartz 
21969cd775fSschwartz int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)22069cd775fSschwartz pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
221dabea0dbSschwartz     uint64_t *data_p, boolean_t is_write)
22269cd775fSschwartz {
22369cd775fSschwartz 	pci_cfg_data_t data;
22469cd775fSschwartz 	on_trap_data_t otd;
22569cd775fSschwartz 	dev_info_t *dip = px_p->px_dip;
22669cd775fSschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
22769cd775fSschwartz 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
22869cd775fSschwartz 	int rval = 0;
229c0da6274SZhi-Jun Robin Fu 	pci_cfgacc_req_t req;
230c0da6274SZhi-Jun Robin Fu 
231c0da6274SZhi-Jun Robin Fu 	if ((size <= 0) || (size > 8)) {
232c0da6274SZhi-Jun Robin Fu 		DBG(DBG_TOOLS, dip, "not supported size.\n");
233c0da6274SZhi-Jun Robin Fu 		prg_p->status = PCITOOL_INVALID_SIZE;
234c0da6274SZhi-Jun Robin Fu 		return (ENOTSUP);
235c0da6274SZhi-Jun Robin Fu 	}
23669cd775fSschwartz 
23769cd775fSschwartz 	/* Alignment checking. */
23869cd775fSschwartz 	if (!IS_P2ALIGNED(prg_p->offset, size)) {
23969cd775fSschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
24069cd775fSschwartz 		prg_p->status = PCITOOL_NOT_ALIGNED;
24169cd775fSschwartz 		return (EINVAL);
24269cd775fSschwartz 	}
24369cd775fSschwartz 
24469cd775fSschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
24569cd775fSschwartz 	pec_p->pec_ontrap_data = &otd;
24669cd775fSschwartz 
247c0da6274SZhi-Jun Robin Fu 	req.rcdip = dip;
248c0da6274SZhi-Jun Robin Fu 	req.bdf = PCI_GETBDF(prg_p->bus_no, prg_p->dev_no, prg_p->func_no);
249c0da6274SZhi-Jun Robin Fu 	req.offset = prg_p->offset;
250c0da6274SZhi-Jun Robin Fu 	req.size = size;
251c0da6274SZhi-Jun Robin Fu 	req.write = is_write;
25269cd775fSschwartz 	if (is_write) {
25369cd775fSschwartz 
25469cd775fSschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
25569cd775fSschwartz 			data.qw = pxtool_swap_endian(*data_p, size);
25669cd775fSschwartz 		else
25769cd775fSschwartz 			data.qw = *data_p;
25869cd775fSschwartz 
25969cd775fSschwartz 		switch (size) {
26069cd775fSschwartz 			case sizeof (uint8_t):
26169cd775fSschwartz 				data.b = (uint8_t)data.qw;
26269cd775fSschwartz 				break;
26369cd775fSschwartz 			case sizeof (uint16_t):
26469cd775fSschwartz 				data.w = (uint16_t)data.qw;
26569cd775fSschwartz 				break;
26669cd775fSschwartz 			case sizeof (uint32_t):
26769cd775fSschwartz 				data.dw = (uint32_t)data.qw;
26869cd775fSschwartz 				break;
26969cd775fSschwartz 			case sizeof (uint64_t):
27069cd775fSschwartz 				break;
27169cd775fSschwartz 		}
27269cd775fSschwartz 
273c0da6274SZhi-Jun Robin Fu 		DBG(DBG_TOOLS, dip, "put: bdf:%d,%d,%d, off:0x%"PRIx64", size:"
27469cd775fSschwartz 		    "0x%"PRIx64", data:0x%"PRIx64"\n",
275c0da6274SZhi-Jun Robin Fu 		    prg_p->bus_no, prg_p->dev_no, prg_p->func_no,
276c0da6274SZhi-Jun Robin Fu 		    prg_p->offset, size, data.qw);
27769cd775fSschwartz 
27869cd775fSschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
27969cd775fSschwartz 
28069cd775fSschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
28169cd775fSschwartz 			otd.ot_trampoline = (uintptr_t)&poke_fault;
282c0da6274SZhi-Jun Robin Fu 			VAL64(&req) = data.qw;
283c0da6274SZhi-Jun Robin Fu 			pci_cfgacc_acc(&req);
28469cd775fSschwartz 		} else
28569cd775fSschwartz 			rval = H_EIO;
28669cd775fSschwartz 
28769cd775fSschwartz 		if (otd.ot_trap & OT_DATA_ACCESS)
28869cd775fSschwartz 			rval = H_EIO;
28969cd775fSschwartz 
29069cd775fSschwartz 	} else {
29169cd775fSschwartz 
29269cd775fSschwartz 		data.qw = 0;
29369cd775fSschwartz 
29469cd775fSschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
29569cd775fSschwartz 
29669cd775fSschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
29769cd775fSschwartz 			otd.ot_trampoline = (uintptr_t)&peek_fault;
298c0da6274SZhi-Jun Robin Fu 			pci_cfgacc_acc(&req);
299c0da6274SZhi-Jun Robin Fu 			data.qw = VAL64(&req);
30069cd775fSschwartz 		} else
30169cd775fSschwartz 			rval = H_EIO;
30269cd775fSschwartz 
303c0da6274SZhi-Jun Robin Fu 		switch (size) {
304c0da6274SZhi-Jun Robin Fu 			case sizeof (uint8_t):
305c0da6274SZhi-Jun Robin Fu 				data.qw = (uint64_t)data.b;
306c0da6274SZhi-Jun Robin Fu 				break;
307c0da6274SZhi-Jun Robin Fu 			case sizeof (uint16_t):
308c0da6274SZhi-Jun Robin Fu 				data.qw = (uint64_t)data.w;
309c0da6274SZhi-Jun Robin Fu 				break;
310c0da6274SZhi-Jun Robin Fu 			case sizeof (uint32_t):
311c0da6274SZhi-Jun Robin Fu 				data.qw = (uint64_t)data.dw;
312c0da6274SZhi-Jun Robin Fu 				break;
313c0da6274SZhi-Jun Robin Fu 			case sizeof (uint64_t):
314c0da6274SZhi-Jun Robin Fu 				break;
315c0da6274SZhi-Jun Robin Fu 		}
316c0da6274SZhi-Jun Robin Fu 
317c0da6274SZhi-Jun Robin Fu 		DBG(DBG_TOOLS, dip, "get: bdf:%d,%d,%d, off:0x%"PRIx64", size:"
31869cd775fSschwartz 		    "0x%"PRIx64", data:0x%"PRIx64"\n",
319c0da6274SZhi-Jun Robin Fu 		    prg_p->bus_no, prg_p->dev_no, prg_p->func_no,
320c0da6274SZhi-Jun Robin Fu 		    prg_p->offset, size, data.qw);
32169cd775fSschwartz 		*data_p = data.qw;
322abdf5d9aSShesha Sreenivasamurthy 
32369cd775fSschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
32469cd775fSschwartz 			*data_p = pxtool_swap_endian(*data_p, size);
32569cd775fSschwartz 	}
32669cd775fSschwartz 
32769cd775fSschwartz 	/*
32869cd775fSschwartz 	 * Workaround: delay taking down safe access env.
32969cd775fSschwartz 	 * For more info, see comments where pxtool_cfg_delay_usec is declared.
33069cd775fSschwartz 	 */
33169cd775fSschwartz 	if (pxtool_cfg_delay_usec > 0)
33269cd775fSschwartz 		drv_usecwait(pxtool_cfg_delay_usec);
33369cd775fSschwartz 
33469cd775fSschwartz 	no_trap();
33569cd775fSschwartz 	pec_p->pec_ontrap_data = NULL;
33669cd775fSschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
33769cd775fSschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
33869cd775fSschwartz 
33969cd775fSschwartz 	if (rval != SUCCESS) {
34069cd775fSschwartz 		prg_p->status = PCITOOL_INVALID_ADDRESS;
34169cd775fSschwartz 		rval = EINVAL;
34269cd775fSschwartz 	} else
34369cd775fSschwartz 		prg_p->status = PCITOOL_SUCCESS;
34469cd775fSschwartz 
34569cd775fSschwartz 	return (rval);
34669cd775fSschwartz }
34769cd775fSschwartz 
34869cd775fSschwartz 
34969cd775fSschwartz /*
35069cd775fSschwartz  * This function is for PCI IO space and memory space access.
35169cd775fSschwartz  * It assumes that offset, bdf, acc_attr are current in prg_p.
35269cd775fSschwartz  * It assumes that prg_p->phys_addr is the final phys addr (including offset).
35369cd775fSschwartz  * This function modifies prg_p status and data.
35469cd775fSschwartz  */
35569cd775fSschwartz int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)356dabea0dbSschwartz pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
35769cd775fSschwartz     uint64_t *data_p, boolean_t is_write)
35869cd775fSschwartz {
35969cd775fSschwartz 	on_trap_data_t otd;
36069cd775fSschwartz 	uint32_t io_stat = 0;
36169cd775fSschwartz 	dev_info_t *dip = px_p->px_dip;
36269cd775fSschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
36369cd775fSschwartz 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
36469cd775fSschwartz 	int rval = 0;
36569cd775fSschwartz 
36669cd775fSschwartz 	/* Alignment checking. */
36769cd775fSschwartz 	if (!IS_P2ALIGNED(prg_p->offset, size)) {
36869cd775fSschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
36969cd775fSschwartz 		prg_p->status = PCITOOL_NOT_ALIGNED;
37069cd775fSschwartz 		return (EINVAL);
37169cd775fSschwartz 	}
37269cd775fSschwartz 
37369cd775fSschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
37469cd775fSschwartz 	pec_p->pec_ontrap_data = &otd;
37569cd775fSschwartz 
37669cd775fSschwartz 	if (is_write) {
37769cd775fSschwartz 		pci_device_t bdf = PX_GET_BDF(prg_p);
37869cd775fSschwartz 
37969cd775fSschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
38069cd775fSschwartz 			*data_p = pxtool_swap_endian(*data_p, size);
38169cd775fSschwartz 
38269cd775fSschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
38369cd775fSschwartz 
38469cd775fSschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
38569cd775fSschwartz 			otd.ot_trampoline = (uintptr_t)&poke_fault;
38669cd775fSschwartz 			rval = hvio_poke(px_p->px_dev_hdl, prg_p->phys_addr,
38769cd775fSschwartz 			    size, *data_p, bdf, &io_stat);
38869cd775fSschwartz 		} else
38969cd775fSschwartz 			rval = H_EIO;
39069cd775fSschwartz 
39169cd775fSschwartz 		if (otd.ot_trap & OT_DATA_ACCESS)
39269cd775fSschwartz 			rval = H_EIO;
39369cd775fSschwartz 
39469cd775fSschwartz 		DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", bdf:0x%x, "
39569cd775fSschwartz 		    "rval:%d, io_stat:%d\n", prg_p->phys_addr, bdf,
39669cd775fSschwartz 		    rval, io_stat);
39769cd775fSschwartz 	} else {
39869cd775fSschwartz 
39969cd775fSschwartz 		*data_p = 0;
40069cd775fSschwartz 
40169cd775fSschwartz 		pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
40269cd775fSschwartz 
40369cd775fSschwartz 		if (!on_trap(&otd, OT_DATA_ACCESS)) {
40469cd775fSschwartz 			otd.ot_trampoline = (uintptr_t)&peek_fault;
40569cd775fSschwartz 			rval = hvio_peek(px_p->px_dev_hdl, prg_p->phys_addr,
40669cd775fSschwartz 			    size, &io_stat, data_p);
40769cd775fSschwartz 		} else
40869cd775fSschwartz 			rval = H_EIO;
40969cd775fSschwartz 
41069cd775fSschwartz 		DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", "
41169cd775fSschwartz 		    "size:0x%" PRIx64 ", hdl:0x%" PRIx64 ", "
41269cd775fSschwartz 		    "rval:%d, io_stat:%d\n", prg_p->phys_addr,
41369cd775fSschwartz 		    size, px_p->px_dev_hdl, rval, io_stat);
41469cd775fSschwartz 		DBG(DBG_TOOLS, dip, "read data:0x%" PRIx64 "\n", *data_p);
41569cd775fSschwartz 
41669cd775fSschwartz 		if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
41769cd775fSschwartz 			*data_p = pxtool_swap_endian(*data_p, size);
41869cd775fSschwartz 	}
41969cd775fSschwartz 
42069cd775fSschwartz 	/*
42169cd775fSschwartz 	 * Workaround: delay taking down safe access env.
42269cd775fSschwartz 	 * For more info, see comment where pxtool_iomem_delay_usec is declared.
42369cd775fSschwartz 	 */
42469cd775fSschwartz 	if (pxtool_iomem_delay_usec > 0)
42569cd775fSschwartz 		delay(drv_usectohz(pxtool_iomem_delay_usec));
42669cd775fSschwartz 
42769cd775fSschwartz 	no_trap();
42869cd775fSschwartz 	pec_p->pec_ontrap_data = NULL;
42969cd775fSschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
43069cd775fSschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
43169cd775fSschwartz 
43269cd775fSschwartz 	if (rval != SUCCESS) {
43369cd775fSschwartz 		prg_p->status = PCITOOL_INVALID_ADDRESS;
43469cd775fSschwartz 		rval = EINVAL;
43569cd775fSschwartz 	} else if (io_stat != SUCCESS) {
43669cd775fSschwartz 		prg_p->status = PCITOOL_IO_ERROR;
43769cd775fSschwartz 		rval = EIO;
43869cd775fSschwartz 	} else
43969cd775fSschwartz 		prg_p->status = PCITOOL_SUCCESS;
44069cd775fSschwartz 
44169cd775fSschwartz 	return (rval);
44269cd775fSschwartz }
44369cd775fSschwartz 
44469cd775fSschwartz 
44569cd775fSschwartz /*ARGSUSED*/
44669cd775fSschwartz int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)44769cd775fSschwartz pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
44869cd775fSschwartz {
44969cd775fSschwartz 	return (SUCCESS);
45069cd775fSschwartz }
45169cd775fSschwartz 
45269cd775fSschwartz 
45369cd775fSschwartz /*
45469cd775fSschwartz  * Perform register accesses on the nexus device itself.
45569cd775fSschwartz  */
45669cd775fSschwartz int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)45769cd775fSschwartz pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
45869cd775fSschwartz {
45969cd775fSschwartz 
46069cd775fSschwartz 	pcitool_reg_t		prg;
46169cd775fSschwartz 	size_t			size;
46269cd775fSschwartz 	px_t			*px_p = DIP_TO_STATE(dip);
46369cd775fSschwartz 	boolean_t		is_write = B_FALSE;
46469cd775fSschwartz 	uint32_t		rval = 0;
46569cd775fSschwartz 
46669cd775fSschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
46769cd775fSschwartz 		is_write = B_TRUE;
46869cd775fSschwartz 
46969cd775fSschwartz 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
47069cd775fSschwartz 
47169cd775fSschwartz 	/* Read data from userland. */
47269cd775fSschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
47369cd775fSschwartz 	    mode) != DDI_SUCCESS) {
47469cd775fSschwartz 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
47569cd775fSschwartz 		return (EFAULT);
47669cd775fSschwartz 	}
47769cd775fSschwartz 
47869cd775fSschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
47969cd775fSschwartz 
48069cd775fSschwartz 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
48169cd775fSschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
48269cd775fSschwartz 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
48369cd775fSschwartz 	    prg.barnum, prg.offset, prg.acc_attr);
48469cd775fSschwartz 	DBG(DBG_TOOLS, dip, "data:0x%" PRIx64 ", phys_addr:0x%" PRIx64 "\n",
48569cd775fSschwartz 	    prg.data, prg.phys_addr);
48669cd775fSschwartz 
48769cd775fSschwartz 	/*
48869cd775fSschwartz 	 * If bank num == ff, base phys addr passed in from userland.
48969cd775fSschwartz 	 *
49069cd775fSschwartz 	 * Normal bank specification is invalid, as there is no OBP property to
49169cd775fSschwartz 	 * back it up.
49269cd775fSschwartz 	 */
49369cd775fSschwartz 	if (prg.barnum != PCITOOL_BASE) {
49469cd775fSschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
49569cd775fSschwartz 		rval = EINVAL;
49669cd775fSschwartz 		goto done;
49769cd775fSschwartz 	}
49869cd775fSschwartz 
49969cd775fSschwartz 	/* Allow only size of 8-bytes. */
50069cd775fSschwartz 	if (size != sizeof (uint64_t)) {
50169cd775fSschwartz 		prg.status = PCITOOL_INVALID_SIZE;
50269cd775fSschwartz 		rval = EINVAL;
50369cd775fSschwartz 		goto done;
50469cd775fSschwartz 	}
50569cd775fSschwartz 
50669cd775fSschwartz 	/* Alignment checking. */
50769cd775fSschwartz 	if (!IS_P2ALIGNED(prg.offset, size)) {
50869cd775fSschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
50969cd775fSschwartz 		prg.status = PCITOOL_NOT_ALIGNED;
51069cd775fSschwartz 		rval = EINVAL;
51169cd775fSschwartz 		goto done;
51269cd775fSschwartz 	}
51369cd775fSschwartz 
51469cd775fSschwartz 	prg.phys_addr += prg.offset;
51569cd775fSschwartz 
516f0a73f04Sschwartz 	/*
517f0a73f04Sschwartz 	 * Only the hypervisor can access nexus registers.  As a result, there
518f0a73f04Sschwartz 	 * can be no error recovery in the OS.  If there is an error, the
519f0a73f04Sschwartz 	 * system will go down, but with a trap type 7f.  The OS cannot
520f0a73f04Sschwartz 	 * intervene with this kind of trap.
521f0a73f04Sschwartz 	 */
52269cd775fSschwartz 
52369cd775fSschwartz 	/* Access device.  prg.status is modified. */
52469cd775fSschwartz 	rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data,
52569cd775fSschwartz 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write);
52669cd775fSschwartz done:
5272917a9c9Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
52869cd775fSschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
52969cd775fSschwartz 	    mode) != DDI_SUCCESS) {
53069cd775fSschwartz 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
53169cd775fSschwartz 		return (EFAULT);
53269cd775fSschwartz 	}
53369cd775fSschwartz 
53469cd775fSschwartz 	return (rval);
53569cd775fSschwartz }
536