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