xref: /illumos-gate/usr/src/uts/sun4u/io/px/px_tools_4u.c (revision d36395bd)
169cd775fSschwartz /*
269cd775fSschwartz  * CDDL HEADER START
369cd775fSschwartz  *
469cd775fSschwartz  * The contents of this file are subject to the terms of the
5f0a73f04Sschwartz  * Common Development and Distribution License (the "License").
6f0a73f04Sschwartz  * You may not use this file except in compliance with the License.
769cd775fSschwartz  *
869cd775fSschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
969cd775fSschwartz  * or http://www.opensolaris.org/os/licensing.
1069cd775fSschwartz  * See the License for the specific language governing permissions
1169cd775fSschwartz  * and limitations under the License.
1269cd775fSschwartz  *
1369cd775fSschwartz  * When distributing Covered Code, include this CDDL HEADER in each
1469cd775fSschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1569cd775fSschwartz  * If applicable, add the following below this CDDL HEADER, with the
1669cd775fSschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
1769cd775fSschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
1869cd775fSschwartz  *
1969cd775fSschwartz  * CDDL HEADER END
2069cd775fSschwartz  */
2169cd775fSschwartz /*
221578c521Sschwartz  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2369cd775fSschwartz  * Use is subject to license terms.
2469cd775fSschwartz  */
2569cd775fSschwartz 
2669cd775fSschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
2769cd775fSschwartz 
2869cd775fSschwartz #include <sys/sysmacros.h>
2969cd775fSschwartz #include <sys/machsystm.h>
3069cd775fSschwartz #include <sys/cpuvar.h>
3169cd775fSschwartz #include <sys/ddi_implfuncs.h>
3269cd775fSschwartz #include <px_csr.h>
3369cd775fSschwartz #include <px_regs.h>
3469cd775fSschwartz #include <px_obj.h>
3569cd775fSschwartz #include <sys/pci_tools.h>
3669cd775fSschwartz #include <px_tools_var.h>
3769cd775fSschwartz #include <px_asm_4u.h>
3869cd775fSschwartz #include <px_lib4u.h>
39d4476ccbSschwartz #include <px_tools_ext.h>
4069cd775fSschwartz 
4169cd775fSschwartz /*
4269cd775fSschwartz  * Delay needed to have a safe environment envelop any error which could
4369cd775fSschwartz  * surface.  The larger the number of bridges and switches, the larger the
4469cd775fSschwartz  * number needed here.
4569cd775fSschwartz  *
46dabea0dbSschwartz  * The way it works is as follows:
47dabea0dbSschwartz  *
48dabea0dbSschwartz  * An access is done which causes an error.  Fire errors are handled with
49dabea0dbSschwartz  * ontrap protection and usually come in first.  Fabric errors can come in
50dabea0dbSschwartz  * later.
51dabea0dbSschwartz  *
52dabea0dbSschwartz  * px_phys_peek_4u() disables interrupts.  Interrupts are reenabled at the end
53dabea0dbSschwartz  * of that function if no errors have been caught by the trap handler, or by
54dabea0dbSschwartz  * peek_fault() which executes when a fire error occurs.
55dabea0dbSschwartz  *
56dabea0dbSschwartz  * Fabric error messages get put on an event queue but are not processed until
57dabea0dbSschwartz  * interrupts are reenabled.
58dabea0dbSschwartz  *
59dabea0dbSschwartz  * The delay gives time for the fabric errors to be processed by FMA before
60dabea0dbSschwartz  * changing the fm error flag back to DDI_FM_ERR_UNEXPECTED.  If this isn't
61dabea0dbSschwartz  * done, then the fabric error which should be safe can panic the system.
62dabea0dbSschwartz  *
6369cd775fSschwartz  * Note: this is a workaround until a better solution is found.  While this
6469cd775fSschwartz  * number is high, given enough bridges and switches in the device path, this
6569cd775fSschwartz  * workaround can break.  Also, other PIL 15 interrupts besides the ones we are
6669cd775fSschwartz  * enveloping could delay processing of the interrupt we are trying to protect.
6769cd775fSschwartz  */
68*d36395bdSrameshc 
69*d36395bdSrameshc /*
70*d36395bdSrameshc  * Set delay to 10 ms
71*d36395bdSrameshc  */
72*d36395bdSrameshc int pxtool_delay_usec = 10000;
7369cd775fSschwartz 
7469cd775fSschwartz /* Number of inos per root complex. */
7569cd775fSschwartz int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
7669cd775fSschwartz 
7769cd775fSschwartz /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
7869cd775fSschwartz typedef union {
7969cd775fSschwartz 	uint64_t u64;
8069cd775fSschwartz 	uint32_t u32;
8169cd775fSschwartz 	uint16_t u16;
8269cd775fSschwartz 	uint8_t u8;
8369cd775fSschwartz } peek_poke_value_t;
8469cd775fSschwartz 
8569cd775fSschwartz /*
8669cd775fSschwartz  * Safe C wrapper around assy language routine px_phys_peek_4u
8769cd775fSschwartz  *
8869cd775fSschwartz  * Type is TRUE for big endian, FALSE for little endian.
8969cd775fSschwartz  * Size is 1, 2, 4 or 8 bytes.
9069cd775fSschwartz  * paddr is the physical address in IO space to access read.
9169cd775fSschwartz  * value_p is where the value is returned.
9269cd775fSschwartz  */
9369cd775fSschwartz static int
pxtool_safe_phys_peek(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)9469cd775fSschwartz pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
9569cd775fSschwartz     uint64_t *value_p)
9669cd775fSschwartz {
9769cd775fSschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
98f0a73f04Sschwartz 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
9969cd775fSschwartz 	on_trap_data_t otd;
10069cd775fSschwartz 	peek_poke_value_t peek_value;
10169cd775fSschwartz 	int err = DDI_SUCCESS;
10269cd775fSschwartz 
10369cd775fSschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
10469cd775fSschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
10569cd775fSschwartz 
106f0a73f04Sschwartz 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
107f0a73f04Sschwartz 
10869cd775fSschwartz 	/*
10969cd775fSschwartz 	 * Set up trap handling to make the access safe.
11069cd775fSschwartz 	 *
11169cd775fSschwartz 	 * on_trap works like setjmp.
11269cd775fSschwartz 	 * Set it up to not panic on data access error,
11369cd775fSschwartz 	 * but to call peek_fault instead.
11469cd775fSschwartz 	 * Call px_phys_peek_4u after trap handling is setup.
11569cd775fSschwartz 	 * When on_trap returns FALSE, it has been setup.
11669cd775fSschwartz 	 * When it returns TRUE, an it has caught an error.
11769cd775fSschwartz 	 */
11869cd775fSschwartz 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
11969cd775fSschwartz 		otd.ot_trampoline = (uintptr_t)&peek_fault;
12069cd775fSschwartz 		err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
12169cd775fSschwartz 	} else
12269cd775fSschwartz 		err = DDI_FAILURE;
12369cd775fSschwartz 
124dabea0dbSschwartz 	no_trap();
125dabea0dbSschwartz 
12669cd775fSschwartz 	/*
12769cd775fSschwartz 	 * Workaround: delay taking down safe access env.
128*d36395bdSrameshc 	 * For more info, see comments where pxtool_delay_usec is declared.
12969cd775fSschwartz 	 */
130*d36395bdSrameshc 	if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0))
131*d36395bdSrameshc 		delay(drv_usectohz(pxtool_delay_usec));
13269cd775fSschwartz 
13369cd775fSschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
134f0a73f04Sschwartz 	pxu_p->pcitool_addr = NULL;
13569cd775fSschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
13669cd775fSschwartz 
13769cd775fSschwartz 	if (err != DDI_FAILURE) {
13869cd775fSschwartz 		switch (size) {
13969cd775fSschwartz 		case 8:
14069cd775fSschwartz 			*value_p = peek_value.u64;
14169cd775fSschwartz 			break;
14269cd775fSschwartz 		case 4:
14369cd775fSschwartz 			*value_p = (uint64_t)peek_value.u32;
14469cd775fSschwartz 			break;
14569cd775fSschwartz 		case 2:
14669cd775fSschwartz 			*value_p = (uint64_t)peek_value.u16;
14769cd775fSschwartz 			break;
14869cd775fSschwartz 		case 1:
14969cd775fSschwartz 			*value_p = (uint64_t)peek_value.u8;
15069cd775fSschwartz 			break;
15169cd775fSschwartz 		default:
15269cd775fSschwartz 			err = DDI_FAILURE;
15369cd775fSschwartz 		}
15469cd775fSschwartz 	}
15569cd775fSschwartz 
15669cd775fSschwartz 	return (err);
15769cd775fSschwartz }
15869cd775fSschwartz 
15969cd775fSschwartz /*
16069cd775fSschwartz  * Safe C wrapper around assy language routine px_phys_poke_4u
16169cd775fSschwartz  *
16269cd775fSschwartz  * Type is TRUE for big endian, FALSE for little endian.
16369cd775fSschwartz  * Size is 1,2,4 or 8 bytes.
16469cd775fSschwartz  * paddr is the physical address in IO space to access read.
16569cd775fSschwartz  * value contains the value to be written.
16669cd775fSschwartz  */
16769cd775fSschwartz static int
pxtool_safe_phys_poke(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)16869cd775fSschwartz pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
16969cd775fSschwartz     uint64_t value)
17069cd775fSschwartz {
17169cd775fSschwartz 	on_trap_data_t otd;
172f0a73f04Sschwartz 	pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
17369cd775fSschwartz 	px_pec_t *pec_p = px_p->px_pec_p;
17469cd775fSschwartz 	peek_poke_value_t poke_value;
17569cd775fSschwartz 	int err = DDI_SUCCESS;
17669cd775fSschwartz 
17769cd775fSschwartz 	switch (size) {
17869cd775fSschwartz 	case 8:
17969cd775fSschwartz 		poke_value.u64 = value;
18069cd775fSschwartz 		break;
18169cd775fSschwartz 	case 4:
18269cd775fSschwartz 		poke_value.u32 = (uint32_t)value;
18369cd775fSschwartz 		break;
18469cd775fSschwartz 	case 2:
18569cd775fSschwartz 		poke_value.u16 = (uint16_t)value;
18669cd775fSschwartz 		break;
18769cd775fSschwartz 	case 1:
18869cd775fSschwartz 		poke_value.u8 = (uint8_t)value;
18969cd775fSschwartz 		break;
19069cd775fSschwartz 	default:
19169cd775fSschwartz 		return (DDI_FAILURE);
19269cd775fSschwartz 	}
19369cd775fSschwartz 
19469cd775fSschwartz 	mutex_enter(&pec_p->pec_pokefault_mutex);
19569cd775fSschwartz 	pec_p->pec_ontrap_data = &otd;
19669cd775fSschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
197f0a73f04Sschwartz 	pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
19869cd775fSschwartz 
19969cd775fSschwartz 	/*
20069cd775fSschwartz 	 * on_trap works like setjmp.
20169cd775fSschwartz 	 * Set it up to not panic on data access error,
20269cd775fSschwartz 	 * but to call poke_fault instead.
20369cd775fSschwartz 	 * Call px_phys_poke_4u after trap handling is setup.
20469cd775fSschwartz 	 * When on_trap returns FALSE, it has been setup.
20569cd775fSschwartz 	 * When it returns TRUE, an it has caught an error.
20669cd775fSschwartz 	 */
20769cd775fSschwartz 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
20869cd775fSschwartz 
20969cd775fSschwartz 		otd.ot_trampoline = (uintptr_t)&poke_fault;
21069cd775fSschwartz 		err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
21169cd775fSschwartz 	} else
21269cd775fSschwartz 		err = DDI_FAILURE;
21369cd775fSschwartz 
214bf8fc234Set 	px_lib_clr_errs(px_p, 0, paddr);
21569cd775fSschwartz 
21669cd775fSschwartz 	if (otd.ot_trap & OT_DATA_ACCESS)
21769cd775fSschwartz 		err = DDI_FAILURE;
21869cd775fSschwartz 
21969cd775fSschwartz 	/* Take down protected environment. */
22069cd775fSschwartz 	no_trap();
22169cd775fSschwartz 	pec_p->pec_ontrap_data = NULL;
222dabea0dbSschwartz 
223dabea0dbSschwartz 	/*
224dabea0dbSschwartz 	 * Workaround: delay taking down safe access env.
225*d36395bdSrameshc 	 * For more info, see comments where pxtool_delay_usec is declared.
226dabea0dbSschwartz 	 */
227*d36395bdSrameshc 	if (pxtool_delay_usec > 0)
228*d36395bdSrameshc 		delay(drv_usectohz(pxtool_delay_usec));
229dabea0dbSschwartz 
23069cd775fSschwartz 	pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
231f0a73f04Sschwartz 	pxu_p->pcitool_addr = NULL;
23269cd775fSschwartz 	mutex_exit(&pec_p->pec_pokefault_mutex);
23369cd775fSschwartz 
23469cd775fSschwartz 	return (err);
23569cd775fSschwartz }
23669cd775fSschwartz 
23769cd775fSschwartz 
23869cd775fSschwartz /*
23969cd775fSschwartz  * Wrapper around pxtool_safe_phys_peek/poke.
24069cd775fSschwartz  *
24169cd775fSschwartz  * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately.
24269cd775fSschwartz  *
24369cd775fSschwartz  * Dip is of the nexus,
24469cd775fSschwartz  * phys_addr is the address to write in physical space.
24569cd775fSschwartz  * pcitool_status returns more detailed status in addition to a more generic
24669cd775fSschwartz  * errno-style function return value.
24769cd775fSschwartz  * other args are self-explanatory.
24869cd775fSschwartz  *
249dabea0dbSschwartz  * This function assumes that offset, bdf, and acc_attr are current in
25069cd775fSschwartz  * prg_p.  It also assumes that prg_p->phys_addr is the final phys addr,
25169cd775fSschwartz  * including offset.
25269cd775fSschwartz  * This function modifies prg_p status and data.
25369cd775fSschwartz  */
25469cd775fSschwartz /*ARGSUSED*/
25569cd775fSschwartz static int
pxtool_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)256dabea0dbSschwartz pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
257dabea0dbSschwartz     boolean_t is_write)
25869cd775fSschwartz {
25969cd775fSschwartz 	dev_info_t *dip = px_p->px_dip;
26069cd775fSschwartz 	uint64_t phys_addr = prg_p->phys_addr;
26169cd775fSschwartz 	boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
26269cd775fSschwartz 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
26369cd775fSschwartz 	int rval = SUCCESS;
26469cd775fSschwartz 
26569cd775fSschwartz 	/* Alignment checking.  Assumes base address is 8-byte aligned. */
266dabea0dbSschwartz 	if (!IS_P2ALIGNED(phys_addr, size)) {
26769cd775fSschwartz 		DBG(DBG_TOOLS, dip, "not aligned.\n");
26869cd775fSschwartz 		prg_p->status = PCITOOL_NOT_ALIGNED;
26969cd775fSschwartz 
27069cd775fSschwartz 		rval = EINVAL;
27169cd775fSschwartz 
27269cd775fSschwartz 	} else if (is_write) {	/* Made it through checks.  Do the access. */
27369cd775fSschwartz 
27469cd775fSschwartz 		DBG(DBG_PHYS_ACC, dip,
27569cd775fSschwartz 		    "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
27669cd775fSschwartz 		    size, (endian ? "BE" : "LE"), phys_addr);
27769cd775fSschwartz 
27869cd775fSschwartz 		if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
27969cd775fSschwartz 		    *data_p) != DDI_SUCCESS) {
28069cd775fSschwartz 			DBG(DBG_PHYS_ACC, dip,
28169cd775fSschwartz 			    "%d byte %s pxtool_safe_phys_poke at addr "
28269cd775fSschwartz 			    "0x%" PRIx64 " failed\n",
28369cd775fSschwartz 			    size, (endian ? "BE" : "LE"), phys_addr);
28469cd775fSschwartz 			prg_p->status = PCITOOL_INVALID_ADDRESS;
28569cd775fSschwartz 
28669cd775fSschwartz 			rval = EFAULT;
28769cd775fSschwartz 		}
28869cd775fSschwartz 
28969cd775fSschwartz 	} else {	/* Read */
29069cd775fSschwartz 
29169cd775fSschwartz 		DBG(DBG_PHYS_ACC, dip,
29269cd775fSschwartz 		    "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
29369cd775fSschwartz 		    size, (endian ? "BE" : "LE"), phys_addr);
29469cd775fSschwartz 
29569cd775fSschwartz 		if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
29669cd775fSschwartz 		    data_p) != DDI_SUCCESS) {
29769cd775fSschwartz 			DBG(DBG_PHYS_ACC, dip,
29869cd775fSschwartz 			    "%d byte %s pxtool_safe_phys_peek at addr "
29969cd775fSschwartz 			    "0x%" PRIx64 " failed\n",
30069cd775fSschwartz 			    size, (endian ? "BE" : "LE"), phys_addr);
30169cd775fSschwartz 			prg_p->status = PCITOOL_INVALID_ADDRESS;
30269cd775fSschwartz 
30369cd775fSschwartz 			rval = EFAULT;
30469cd775fSschwartz 		}
30569cd775fSschwartz 	}
30669cd775fSschwartz 	return (rval);
30769cd775fSschwartz }
30869cd775fSschwartz 
30969cd775fSschwartz 
31069cd775fSschwartz int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)31169cd775fSschwartz pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
312dabea0dbSschwartz     uint64_t *data_p, boolean_t is_write)
31369cd775fSschwartz {
314dabea0dbSschwartz 	return (pxtool_access(px_p, prg_p, data_p, is_write));
31569cd775fSschwartz }
31669cd775fSschwartz 
31769cd775fSschwartz int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)318dabea0dbSschwartz pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
31969cd775fSschwartz     uint64_t *data_p, boolean_t is_write)
32069cd775fSschwartz {
321dabea0dbSschwartz 	return (pxtool_access(px_p, prg_p, data_p, is_write));
32269cd775fSschwartz }
32369cd775fSschwartz 
32469cd775fSschwartz int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)32569cd775fSschwartz pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
32669cd775fSschwartz {
32769cd775fSschwartz 	/*
32869cd775fSschwartz 	 * Guard against checking a root nexus which is empty.
32969cd775fSschwartz 	 * On some systems this will result in a Fatal Reset.
33069cd775fSschwartz 	 */
3311578c521Sschwartz 	if (ddi_get_child(dip) == NULL) {
33269cd775fSschwartz 		DBG(DBG_TOOLS, dip,
33369cd775fSschwartz 		    "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
33469cd775fSschwartz 		prg_p->status = PCITOOL_IO_ERROR;
33569cd775fSschwartz 		return (ENXIO);
33669cd775fSschwartz 	}
33769cd775fSschwartz 
33869cd775fSschwartz 	return (SUCCESS);
33969cd775fSschwartz }
34069cd775fSschwartz 
34169cd775fSschwartz /*
34269cd775fSschwartz  * Perform register accesses on the nexus device itself.
34369cd775fSschwartz  */
34469cd775fSschwartz int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)34569cd775fSschwartz pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
34669cd775fSschwartz {
34769cd775fSschwartz 	pcitool_reg_t		prg;
34869cd775fSschwartz 	uint64_t		base_addr;
34969cd775fSschwartz 	uint32_t		reglen;
35069cd775fSschwartz 	px_t			*px_p = DIP_TO_STATE(dip);
35169cd775fSschwartz 	px_nexus_regspec_t	*px_rp = NULL;
35269cd775fSschwartz 	uint32_t		numbanks = 0;
35369cd775fSschwartz 	boolean_t		write_flag = B_FALSE;
35469cd775fSschwartz 	uint32_t		rval = 0;
35569cd775fSschwartz 
35669cd775fSschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
35769cd775fSschwartz 		write_flag = B_TRUE;
35869cd775fSschwartz 
35969cd775fSschwartz 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
36069cd775fSschwartz 
36169cd775fSschwartz 	/* Read data from userland. */
36269cd775fSschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
36369cd775fSschwartz 	    DDI_SUCCESS) {
36469cd775fSschwartz 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
36569cd775fSschwartz 		return (EFAULT);
36669cd775fSschwartz 	}
36769cd775fSschwartz 
36869cd775fSschwartz 	/* Read reg property which contains starting addr and size of banks. */
36969cd775fSschwartz 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
37069cd775fSschwartz 	    "reg", (int **)&px_rp, &reglen) == DDI_SUCCESS) {
37169cd775fSschwartz 		if (((reglen * sizeof (int)) %
37269cd775fSschwartz 		    sizeof (px_nexus_regspec_t)) != 0) {
37369cd775fSschwartz 			DBG(DBG_TOOLS, dip, "reg prop not well-formed");
37469cd775fSschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
37569cd775fSschwartz 			rval = EIO;
37669cd775fSschwartz 			goto done;
37769cd775fSschwartz 		}
37869cd775fSschwartz 	}
37969cd775fSschwartz 
38069cd775fSschwartz 	numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
38169cd775fSschwartz 
38269cd775fSschwartz 	/* Bounds check the bank number. */
38369cd775fSschwartz 	if (prg.barnum >= numbanks) {
38469cd775fSschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
38569cd775fSschwartz 		rval = EINVAL;
38669cd775fSschwartz 		goto done;
38769cd775fSschwartz 	}
38869cd775fSschwartz 
38969cd775fSschwartz 	base_addr = px_rp[prg.barnum].phys_addr;
39069cd775fSschwartz 	prg.phys_addr = base_addr + prg.offset;
39169cd775fSschwartz 
39269cd775fSschwartz 	DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
393dabea0dbSschwartz 	    "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
394dabea0dbSschwartz 	    "0x%" PRIx64 "\n",
395dabea0dbSschwartz 	    base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
396dabea0dbSschwartz 
397dabea0dbSschwartz 	if (prg.offset >= px_rp[prg.barnum].size) {
398dabea0dbSschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
399dabea0dbSschwartz 		rval = EINVAL;
400dabea0dbSschwartz 		goto done;
401dabea0dbSschwartz 	}
40269cd775fSschwartz 
40369cd775fSschwartz 	/* Access device.  prg.status is modified. */
404dabea0dbSschwartz 	rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
40569cd775fSschwartz 
40669cd775fSschwartz done:
40769cd775fSschwartz 	if (px_rp != NULL)
40869cd775fSschwartz 		ddi_prop_free(px_rp);
40969cd775fSschwartz 
4102917a9c9Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
41169cd775fSschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
41269cd775fSschwartz 	    mode) != DDI_SUCCESS) {
41369cd775fSschwartz 		DBG(DBG_TOOLS, dip, "Copyout failed.\n");
41469cd775fSschwartz 		return (EFAULT);
41569cd775fSschwartz 	}
41669cd775fSschwartz 
41769cd775fSschwartz 	return (rval);
41869cd775fSschwartz }
419