xref: /onnv-gate/usr/src/uts/sun4/io/px/px_tools.c (revision 624:8c5206bfd8f1)
1*624Sschwartz /*
2*624Sschwartz  * CDDL HEADER START
3*624Sschwartz  *
4*624Sschwartz  * The contents of this file are subject to the terms of the
5*624Sschwartz  * Common Development and Distribution License, Version 1.0 only
6*624Sschwartz  * (the "License").  You may not use this file except in compliance
7*624Sschwartz  * with the License.
8*624Sschwartz  *
9*624Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*624Sschwartz  * or http://www.opensolaris.org/os/licensing.
11*624Sschwartz  * See the License for the specific language governing permissions
12*624Sschwartz  * and limitations under the License.
13*624Sschwartz  *
14*624Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
15*624Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*624Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
17*624Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
18*624Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
19*624Sschwartz  *
20*624Sschwartz  * CDDL HEADER END
21*624Sschwartz  */
22*624Sschwartz /*
23*624Sschwartz  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*624Sschwartz  * Use is subject to license terms.
25*624Sschwartz  */
26*624Sschwartz 
27*624Sschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*624Sschwartz 
29*624Sschwartz #include <sys/types.h>
30*624Sschwartz #include <sys/stat.h>
31*624Sschwartz #include <sys/cpuvar.h>
32*624Sschwartz #include <sys/kmem.h>
33*624Sschwartz #include <sys/sunddi.h>
34*624Sschwartz #include <sys/hotplug/pci/pcihp.h>
35*624Sschwartz #include "px_obj.h"
36*624Sschwartz #include <sys/pci_tools.h>
37*624Sschwartz #include "px_tools.h"
38*624Sschwartz #include "px_tools_var.h"
39*624Sschwartz 
40*624Sschwartz /*
41*624Sschwartz  * PCI Space definitions.
42*624Sschwartz  */
43*624Sschwartz #define	PCI_CONFIG_SPACE	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
44*624Sschwartz #define	PCI_IO_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_IO))
45*624Sschwartz #define	PCI_MEM32_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
46*624Sschwartz #define	PCI_MEM64_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
47*624Sschwartz 
48*624Sschwartz /*
49*624Sschwartz  * Config space range for a device.  IEEE 1275 spec defines for PCI.
50*624Sschwartz  * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA
51*624Sschwartz  */
52*624Sschwartz #define	DEV_CFG_SPACE_SIZE	\
53*624Sschwartz 	(1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA))
54*624Sschwartz 
55*624Sschwartz /*
56*624Sschwartz  * Offsets of BARS in config space.  First entry of 0 means config space.
57*624Sschwartz  * Entries here correlate to pcitool_bars_t enumerated type.
58*624Sschwartz  */
59*624Sschwartz uint8_t pci_bars[] = {
60*624Sschwartz 	0x0,
61*624Sschwartz 	PCI_CONF_BASE0,
62*624Sschwartz 	PCI_CONF_BASE1,
63*624Sschwartz 	PCI_CONF_BASE2,
64*624Sschwartz 	PCI_CONF_BASE3,
65*624Sschwartz 	PCI_CONF_BASE4,
66*624Sschwartz 	PCI_CONF_BASE5,
67*624Sschwartz 	PCI_CONF_ROM
68*624Sschwartz };
69*624Sschwartz 
70*624Sschwartz int	pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]);
71*624Sschwartz 
72*624Sschwartz /*
73*624Sschwartz  * Validate the cpu_id passed in.
74*624Sschwartz  * A value of 1 will be returned for success and zero for failure.
75*624Sschwartz  */
76*624Sschwartz static int
77*624Sschwartz pxtool_validate_cpuid(uint32_t cpuid)
78*624Sschwartz {
79*624Sschwartz 	extern const int _ncpu;
80*624Sschwartz 	extern cpu_t	*cpu[];
81*624Sschwartz 
82*624Sschwartz 	ASSERT(mutex_owned(&cpu_lock));
83*624Sschwartz 
84*624Sschwartz 	return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid])));
85*624Sschwartz }
86*624Sschwartz 
87*624Sschwartz 
88*624Sschwartz static int
89*624Sschwartz pxtool_intr_get_max_ino(uint32_t *arg, int mode)
90*624Sschwartz {
91*624Sschwartz 	if (ddi_copyout(&pxtool_num_inos, arg, sizeof (uint32_t), mode) !=
92*624Sschwartz 	    DDI_SUCCESS)
93*624Sschwartz 		return (EFAULT);
94*624Sschwartz 	else
95*624Sschwartz 		return (SUCCESS);
96*624Sschwartz }
97*624Sschwartz /*
98*624Sschwartz  * Get interrupt information for a given ino.
99*624Sschwartz  * Returns info only for inos mapped to devices.
100*624Sschwartz  *
101*624Sschwartz  * Returned info is valid only when iget.num_devs is returned > 0.
102*624Sschwartz  * If ino is not enabled or is not mapped to a device,
103*624Sschwartz  * iget.num_devs will be returned as = 0.
104*624Sschwartz  */
105*624Sschwartz /*ARGSUSED*/
106*624Sschwartz static int
107*624Sschwartz pxtool_get_intr(dev_info_t *dip, void *arg, int mode)
108*624Sschwartz {
109*624Sschwartz 	/* Array part isn't used here, but oh well... */
110*624Sschwartz 	pcitool_intr_get_t partial_iget;
111*624Sschwartz 	uint32_t ino;
112*624Sschwartz 	uint8_t num_devs_ret;
113*624Sschwartz 	int copyout_rval;
114*624Sschwartz 	sysino_t sysino;
115*624Sschwartz 	intr_valid_state_t intr_valid_state;
116*624Sschwartz 	cpuid_t old_cpu_id;
117*624Sschwartz 	px_t *px_p = DIP_TO_STATE(dip);
118*624Sschwartz 	pcitool_intr_get_t *iget = &partial_iget;
119*624Sschwartz 	size_t	iget_kmem_alloc_size = 0;
120*624Sschwartz 	int rval = SUCCESS;
121*624Sschwartz 
122*624Sschwartz 	/* Read in just the header part, no array section. */
123*624Sschwartz 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
124*624Sschwartz 	    DDI_SUCCESS)
125*624Sschwartz 		return (EFAULT);
126*624Sschwartz 
127*624Sschwartz 	ino = partial_iget.ino;
128*624Sschwartz 	num_devs_ret = partial_iget.num_devs_ret;
129*624Sschwartz 
130*624Sschwartz 	partial_iget.num_devs_ret = 0;		/* Assume error for now. */
131*624Sschwartz 	partial_iget.status = PCITOOL_INVALID_INO;
132*624Sschwartz 	rval = EINVAL;
133*624Sschwartz 
134*624Sschwartz 	/* Validate argument. */
135*624Sschwartz 	if (partial_iget.ino > pxtool_num_inos) {
136*624Sschwartz 		goto done_get_intr;
137*624Sschwartz 	}
138*624Sschwartz 
139*624Sschwartz 	/* Caller wants device information returned. */
140*624Sschwartz 	if (num_devs_ret > 0) {
141*624Sschwartz 
142*624Sschwartz 		/*
143*624Sschwartz 		 * Allocate room.
144*624Sschwartz 		 * Note if num_devs == 0 iget remains pointing to
145*624Sschwartz 		 * partial_iget.
146*624Sschwartz 		 */
147*624Sschwartz 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
148*624Sschwartz 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
149*624Sschwartz 
150*624Sschwartz 		/* Read in whole structure to verify there's room. */
151*624Sschwartz 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
152*624Sschwartz 		    SUCCESS) {
153*624Sschwartz 
154*624Sschwartz 			/* Be consistent and just return EFAULT here. */
155*624Sschwartz 			kmem_free(iget, iget_kmem_alloc_size);
156*624Sschwartz 
157*624Sschwartz 			return (EFAULT);
158*624Sschwartz 		}
159*624Sschwartz 	}
160*624Sschwartz 
161*624Sschwartz 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
162*624Sschwartz 	iget->ino = ino;
163*624Sschwartz 	iget->num_devs_ret = num_devs_ret;
164*624Sschwartz 
165*624Sschwartz 	/* Convert leaf-wide intr to system-wide intr */
166*624Sschwartz 	if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) ==
167*624Sschwartz 	    DDI_FAILURE) {
168*624Sschwartz 		iget->status = PCITOOL_IO_ERROR;
169*624Sschwartz 		rval = EIO;
170*624Sschwartz 		goto done_get_intr;
171*624Sschwartz 	}
172*624Sschwartz 
173*624Sschwartz 	/* Operate only on inos which are already enabled. */
174*624Sschwartz 	if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) ==
175*624Sschwartz 	    DDI_FAILURE) {
176*624Sschwartz 		iget->status = PCITOOL_IO_ERROR;
177*624Sschwartz 		rval = EIO;
178*624Sschwartz 		goto done_get_intr;
179*624Sschwartz 	}
180*624Sschwartz 
181*624Sschwartz 	/*
182*624Sschwartz 	 * Consider all valid inos: those mapped to the root complex itself
183*624Sschwartz 	 * as well as those mapped to devices.
184*624Sschwartz 	 */
185*624Sschwartz 	if (intr_valid_state == INTR_VALID) {
186*624Sschwartz 
187*624Sschwartz 		/*
188*624Sschwartz 		 * The following looks up the px_ib_ino_info and returns
189*624Sschwartz 		 * info of devices mapped to this ino.
190*624Sschwartz 		 */
191*624Sschwartz 		iget->num_devs = pxtool_ib_get_ino_devs(
192*624Sschwartz 		    px_p, ino, &iget->num_devs_ret, iget->dev);
193*624Sschwartz 
194*624Sschwartz 		if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) ==
195*624Sschwartz 		    DDI_FAILURE) {
196*624Sschwartz 			iget->status = PCITOOL_IO_ERROR;
197*624Sschwartz 			rval = EIO;
198*624Sschwartz 			goto done_get_intr;
199*624Sschwartz 		}
200*624Sschwartz 		iget->cpu_id = old_cpu_id;
201*624Sschwartz 	}
202*624Sschwartz 
203*624Sschwartz 	iget->status = PCITOOL_SUCCESS;
204*624Sschwartz 	rval = SUCCESS;
205*624Sschwartz 
206*624Sschwartz done_get_intr:
207*624Sschwartz 	copyout_rval =
208*624Sschwartz 	    ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode);
209*624Sschwartz 
210*624Sschwartz 	if (iget_kmem_alloc_size > 0)
211*624Sschwartz 		kmem_free(iget, iget_kmem_alloc_size);
212*624Sschwartz 
213*624Sschwartz 	if (copyout_rval != DDI_SUCCESS)
214*624Sschwartz 		rval = EFAULT;
215*624Sschwartz 
216*624Sschwartz 	return (rval);
217*624Sschwartz }
218*624Sschwartz 
219*624Sschwartz 
220*624Sschwartz /*
221*624Sschwartz  * Associate a new CPU with a given ino.
222*624Sschwartz  *
223*624Sschwartz  * Operate only on inos which are already mapped to devices.
224*624Sschwartz  */
225*624Sschwartz static int
226*624Sschwartz pxtool_set_intr(dev_info_t *dip, void *arg, int mode)
227*624Sschwartz {
228*624Sschwartz 	pcitool_intr_set_t iset;
229*624Sschwartz 	cpuid_t old_cpu_id;
230*624Sschwartz 	sysino_t sysino;
231*624Sschwartz 	px_t *px_p = DIP_TO_STATE(dip);
232*624Sschwartz 	px_ib_t *ib_p = px_p->px_ib_p;
233*624Sschwartz 	uint8_t zero = 0;
234*624Sschwartz 	int rval = SUCCESS;
235*624Sschwartz 
236*624Sschwartz 	if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) !=
237*624Sschwartz 	    DDI_SUCCESS)
238*624Sschwartz 		return (EFAULT);
239*624Sschwartz 
240*624Sschwartz 	iset.status = PCITOOL_INVALID_INO;
241*624Sschwartz 	rval = EINVAL;
242*624Sschwartz 
243*624Sschwartz 	/* Validate input argument. */
244*624Sschwartz 	if (iset.ino > pxtool_num_inos)
245*624Sschwartz 		goto done_set_intr;
246*624Sschwartz 
247*624Sschwartz 	/* Validate that ino given belongs to a device. */
248*624Sschwartz 	if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0)
249*624Sschwartz 		goto done_set_intr;
250*624Sschwartz 
251*624Sschwartz 	/*
252*624Sschwartz 	 * Get lock, validate cpu and write new mapreg value.
253*624Sschwartz 	 * Return original cpu value to caller via iset.cpu.
254*624Sschwartz 	 */
255*624Sschwartz 	mutex_enter(&cpu_lock);
256*624Sschwartz 	if (pxtool_validate_cpuid(iset.cpu_id)) {
257*624Sschwartz 
258*624Sschwartz 		DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id);
259*624Sschwartz 
260*624Sschwartz 		if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) ==
261*624Sschwartz 		    DDI_FAILURE)
262*624Sschwartz 			goto done_set_intr;
263*624Sschwartz 
264*624Sschwartz 		if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) ==
265*624Sschwartz 		    DDI_FAILURE)
266*624Sschwartz 			goto done_set_intr;
267*624Sschwartz 
268*624Sschwartz 		px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE);
269*624Sschwartz 
270*624Sschwartz 		px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino);
271*624Sschwartz 
272*624Sschwartz 		iset.cpu_id = old_cpu_id;
273*624Sschwartz 		iset.status = PCITOOL_SUCCESS;
274*624Sschwartz 		rval = SUCCESS;
275*624Sschwartz 
276*624Sschwartz 	} else {	/* Invalid cpu.  Restore original register image. */
277*624Sschwartz 
278*624Sschwartz 		DBG(DBG_TOOLS, dip,
279*624Sschwartz 		    "Invalid cpuid: writing orig mapreg value\n");
280*624Sschwartz 
281*624Sschwartz 		iset.status = PCITOOL_INVALID_CPUID;
282*624Sschwartz 		rval = EINVAL;
283*624Sschwartz 	}
284*624Sschwartz 	mutex_exit(&cpu_lock);
285*624Sschwartz 
286*624Sschwartz done_set_intr:
287*624Sschwartz 	if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) !=
288*624Sschwartz 	    DDI_SUCCESS)
289*624Sschwartz 		rval = EFAULT;
290*624Sschwartz 
291*624Sschwartz 	return (rval);
292*624Sschwartz }
293*624Sschwartz 
294*624Sschwartz 
295*624Sschwartz /* Main function for handling interrupt CPU binding requests and queries. */
296*624Sschwartz int
297*624Sschwartz pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
298*624Sschwartz {
299*624Sschwartz 	int rval = SUCCESS;
300*624Sschwartz 
301*624Sschwartz 	switch (cmd) {
302*624Sschwartz 
303*624Sschwartz 	/* Return the number of interrupts supported by a PCI bus. */
304*624Sschwartz 	case PCITOOL_DEVICE_NUM_INTR:
305*624Sschwartz 		rval = pxtool_intr_get_max_ino(arg, mode);
306*624Sschwartz 		break;
307*624Sschwartz 
308*624Sschwartz 	/* Get interrupt information for a given ino. */
309*624Sschwartz 	case PCITOOL_DEVICE_GET_INTR:
310*624Sschwartz 		rval = pxtool_get_intr(dip, arg, mode);
311*624Sschwartz 		break;
312*624Sschwartz 
313*624Sschwartz 	/* Associate a new CPU with a given ino. */
314*624Sschwartz 	case PCITOOL_DEVICE_SET_INTR:
315*624Sschwartz 		rval = pxtool_set_intr(dip, arg, mode);
316*624Sschwartz 		break;
317*624Sschwartz 
318*624Sschwartz 	default:
319*624Sschwartz 		rval = ENOTTY;
320*624Sschwartz 	}
321*624Sschwartz 
322*624Sschwartz 	return (rval);
323*624Sschwartz }
324*624Sschwartz 
325*624Sschwartz 
326*624Sschwartz static int
327*624Sschwartz pxtool_validate_barnum_bdf(pcitool_reg_t *prg)
328*624Sschwartz {
329*624Sschwartz 	int rval = SUCCESS;
330*624Sschwartz 
331*624Sschwartz 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
332*624Sschwartz 		prg->status = PCITOOL_OUT_OF_RANGE;
333*624Sschwartz 		rval = EINVAL;
334*624Sschwartz 
335*624Sschwartz 	/* Validate address arguments of bus / dev / func */
336*624Sschwartz 	} else if (((prg->bus_no &
337*624Sschwartz 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
338*624Sschwartz 	    ((prg->dev_no &
339*624Sschwartz 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
340*624Sschwartz 	    ((prg->func_no &
341*624Sschwartz 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
342*624Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
343*624Sschwartz 		rval = EINVAL;
344*624Sschwartz 	}
345*624Sschwartz 
346*624Sschwartz 	return (rval);
347*624Sschwartz }
348*624Sschwartz 
349*624Sschwartz static int
350*624Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t max_addr,
351*624Sschwartz     uint64_t *bar_p, uint32_t *space_p)
352*624Sschwartz {
353*624Sschwartz 	int rval;
354*624Sschwartz 	pcitool_reg_t cfg_prg = *prg_p;	/* Make local copy. */
355*624Sschwartz 	dev_info_t *dip = px_p->px_dip;
356*624Sschwartz 
357*624Sschwartz 	*space_p = PCI_MEM32_SPACE;
358*624Sschwartz 	*bar_p = 0;
359*624Sschwartz 
360*624Sschwartz 	/*
361*624Sschwartz 	 * Translate BAR number into offset of the BAR in
362*624Sschwartz 	 * the device's config space.
363*624Sschwartz 	 */
364*624Sschwartz 	cfg_prg.offset = PCI_BAR_OFFSET((*prg_p));
365*624Sschwartz 	cfg_prg.phys_addr = prg_p->phys_addr + cfg_prg.offset;
366*624Sschwartz 	cfg_prg.acc_attr =
367*624Sschwartz 	    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
368*624Sschwartz 
369*624Sschwartz 	DBG(DBG_TOOLS, dip, "barnum:%d, phys_addr:0x%" PRIx64 "\n",
370*624Sschwartz 	    cfg_prg.barnum, cfg_prg.phys_addr);
371*624Sschwartz 
372*624Sschwartz 	/*
373*624Sschwartz 	 * Get Bus Address Register (BAR) from config space.
374*624Sschwartz 	 * cfg_prg.offset is the offset into config space of the
375*624Sschwartz 	 * BAR desired.  prg_p->status is modified on error.
376*624Sschwartz 	 */
377*624Sschwartz 	rval = pxtool_pcicfg_access(px_p, &cfg_prg, max_addr, bar_p, PX_ISREAD);
378*624Sschwartz 
379*624Sschwartz 	if (rval != SUCCESS) {
380*624Sschwartz 		prg_p->status = cfg_prg.status;
381*624Sschwartz 		return (rval);
382*624Sschwartz 	}
383*624Sschwartz 
384*624Sschwartz 	DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p);
385*624Sschwartz 
386*624Sschwartz 	/*
387*624Sschwartz 	 * BAR has bits saying this space is IO space, unless
388*624Sschwartz 	 * this is the ROM address register.
389*624Sschwartz 	 */
390*624Sschwartz 	if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) &&
391*624Sschwartz 	    (cfg_prg.offset != PCI_CONF_ROM)) {
392*624Sschwartz 		*space_p = PCI_IO_SPACE;
393*624Sschwartz 		*bar_p &= PCI_BASE_IO_ADDR_M;
394*624Sschwartz 
395*624Sschwartz 	/*
396*624Sschwartz 	 * BAR has bits saying this space is 64 bit memory
397*624Sschwartz 	 * space, unless this is the ROM address register.
398*624Sschwartz 	 *
399*624Sschwartz 	 * The 64 bit address stored in two BAR cells is not
400*624Sschwartz 	 * necessarily aligned on an 8-byte boundary.
401*624Sschwartz 	 * Need to keep the first 4 bytes read,
402*624Sschwartz 	 * and do a separate read of the high 4 bytes.
403*624Sschwartz 	 */
404*624Sschwartz 	} else if ((PCI_BASE_TYPE_ALL & *bar_p) &&
405*624Sschwartz 	    (cfg_prg.offset != PCI_CONF_ROM)) {
406*624Sschwartz 
407*624Sschwartz 		uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL);
408*624Sschwartz 
409*624Sschwartz 		/* Don't try to read the next 4 bytes past the end of BARs. */
410*624Sschwartz 		if (cfg_prg.offset >= PCI_CONF_BASE5) {
411*624Sschwartz 			prg_p->status = PCITOOL_OUT_OF_RANGE;
412*624Sschwartz 			return (EIO);
413*624Sschwartz 		}
414*624Sschwartz 
415*624Sschwartz 		/* Access device.  prg_p->status is modified on error. */
416*624Sschwartz 		cfg_prg.phys_addr += sizeof (uint32_t);
417*624Sschwartz 		cfg_prg.offset += sizeof (uint32_t);
418*624Sschwartz 
419*624Sschwartz 		rval = pxtool_pcicfg_access(px_p, &cfg_prg, max_addr, bar_p,
420*624Sschwartz 		    PX_ISREAD);
421*624Sschwartz 		if (rval != SUCCESS) {
422*624Sschwartz 			prg_p->status = cfg_prg.status;
423*624Sschwartz 			return (rval);
424*624Sschwartz 		}
425*624Sschwartz 
426*624Sschwartz 		/*
427*624Sschwartz 		 * Honor the 64 bit BAR as such, only when the upper 32 bits
428*624Sschwartz 		 * store a non-zero value.
429*624Sschwartz 		 */
430*624Sschwartz 		if (*bar_p) {
431*624Sschwartz 			*space_p = PCI_MEM64_SPACE;
432*624Sschwartz 			*bar_p = (*bar_p << 32) | low_bytes;
433*624Sschwartz 		} else
434*624Sschwartz 			*bar_p = low_bytes;
435*624Sschwartz 
436*624Sschwartz 	} else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */
437*624Sschwartz 
438*624Sschwartz 		/*
439*624Sschwartz 		 * ROM enabled. Filter ROM enable bit from the BAR.
440*624Sschwartz 		 * Treat as Mem32 henceforth.
441*624Sschwartz 		 */
442*624Sschwartz 		if (!(*bar_p & PCI_BASE_ROM_ENABLE))
443*624Sschwartz 			*bar_p ^= PCI_BASE_ROM_ENABLE;
444*624Sschwartz 
445*624Sschwartz 		else {	/* ROM disabled. */
446*624Sschwartz 			prg_p->status = PCITOOL_ROM_DISABLED;
447*624Sschwartz 			return (EIO);
448*624Sschwartz 		}
449*624Sschwartz 	}
450*624Sschwartz 
451*624Sschwartz 	/* Accept a bar of 0 only for IO space. */
452*624Sschwartz 	if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) {
453*624Sschwartz 		prg_p->status = PCITOOL_INVALID_ADDRESS;
454*624Sschwartz 		return (EINVAL);
455*624Sschwartz 	}
456*624Sschwartz 
457*624Sschwartz 	return (SUCCESS);
458*624Sschwartz }
459*624Sschwartz 
460*624Sschwartz 
461*624Sschwartz /* Perform register accesses on PCI leaf devices. */
462*624Sschwartz int
463*624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
464*624Sschwartz {
465*624Sschwartz 	pcitool_reg_t	prg;
466*624Sschwartz 	uint64_t	base_addr;
467*624Sschwartz 	uint64_t	range_base;
468*624Sschwartz 	uint64_t	range_base_size;
469*624Sschwartz 	uint64_t	max_addr;
470*624Sschwartz 	uint64_t	bar;
471*624Sschwartz 	uint32_t	space;
472*624Sschwartz 	boolean_t	write_flag = B_FALSE;
473*624Sschwartz 	px_t		*px_p = DIP_TO_STATE(dip);
474*624Sschwartz 	px_ranges_t	*rp = px_p->px_ranges_p;
475*624Sschwartz 	int		rval = 0;
476*624Sschwartz 
477*624Sschwartz 	if (cmd == PCITOOL_DEVICE_SET_REG)
478*624Sschwartz 		write_flag = B_TRUE;
479*624Sschwartz 
480*624Sschwartz 	DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n");
481*624Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
482*624Sschwartz 	    mode) != DDI_SUCCESS) {
483*624Sschwartz 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
484*624Sschwartz 		return (EFAULT);
485*624Sschwartz 	}
486*624Sschwartz 
487*624Sschwartz 	if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) {
488*624Sschwartz 		goto done_reg;
489*624Sschwartz 	}
490*624Sschwartz 
491*624Sschwartz 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
492*624Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
493*624Sschwartz 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
494*624Sschwartz 	    prg.barnum, prg.offset, prg.acc_attr);
495*624Sschwartz 
496*624Sschwartz 	if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS)
497*624Sschwartz 		goto done_reg;
498*624Sschwartz 
499*624Sschwartz 	range_base = PX_GET_RANGE_PROP(rp, PCI_CONFIG_SPACE);
500*624Sschwartz 	range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_SPACE);
501*624Sschwartz 
502*624Sschwartz 	base_addr = range_base + (PX_GET_BDF(&prg) << PX_PCI_BDF_OFFSET_DELTA);
503*624Sschwartz 	max_addr = base_addr + DEV_CFG_SPACE_SIZE - 1;
504*624Sschwartz 
505*624Sschwartz 	prg.phys_addr = base_addr;
506*624Sschwartz 
507*624Sschwartz 	if (prg.barnum == 0) {	/* Proper config space desired. */
508*624Sschwartz 
509*624Sschwartz 		/* Access config space and we're done. */
510*624Sschwartz 		prg.phys_addr = base_addr + prg.offset;
511*624Sschwartz 
512*624Sschwartz 		DBG(DBG_TOOLS, dip,
513*624Sschwartz 		    "config access: base:0x%" PRIx64 ", offset:0x%" PRIx64 ", "
514*624Sschwartz 		    "phys_addr:0x%" PRIx64 ", end:%s\n",
515*624Sschwartz 		    base_addr, prg.offset, prg.phys_addr,
516*624Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl");
517*624Sschwartz 
518*624Sschwartz 		/*
519*624Sschwartz 		 * Access device.  pr.status is modified.
520*624Sschwartz 		 * BDF is assumed valid at this point.
521*624Sschwartz 		 */
522*624Sschwartz 		rval = pxtool_pcicfg_access(px_p, &prg, max_addr, &prg.data,
523*624Sschwartz 		    write_flag);
524*624Sschwartz 		goto done_reg;
525*624Sschwartz 	}
526*624Sschwartz 
527*624Sschwartz 	/* IO/ MEM/ MEM64 space. */
528*624Sschwartz 
529*624Sschwartz 	if ((rval = pxtool_get_bar(px_p, &prg, max_addr, &bar, &space)) !=
530*624Sschwartz 	    SUCCESS)
531*624Sschwartz 		goto done_reg;
532*624Sschwartz 
533*624Sschwartz 	if (space == PCI_IO_SPACE) {	/* IO space. */
534*624Sschwartz 
535*624Sschwartz 		DBG(DBG_TOOLS, dip, "IO space\n");
536*624Sschwartz 
537*624Sschwartz 		/* Focus on IO space. */
538*624Sschwartz 		range_base = PX_GET_RANGE_PROP(rp, PCI_IO_SPACE);
539*624Sschwartz 		range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_IO_SPACE);
540*624Sschwartz 
541*624Sschwartz 	} else if (space == PCI_MEM64_SPACE) {	/* 64 bit memory space. */
542*624Sschwartz 
543*624Sschwartz 		DBG(DBG_TOOLS, dip,
544*624Sschwartz 		    "64 bit mem space.  64-bit bar is 0x%" PRIx64 "\n", bar);
545*624Sschwartz 
546*624Sschwartz 		/* Set focus to MEM64 range space. */
547*624Sschwartz 		range_base = PX_GET_RANGE_PROP(rp, PCI_MEM64_SPACE);
548*624Sschwartz 		range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_MEM64_SPACE);
549*624Sschwartz 
550*624Sschwartz 	} else {	/* Mem32 space, including ROM */
551*624Sschwartz 
552*624Sschwartz 		DBG(DBG_TOOLS, dip, "32 bit mem space\n");
553*624Sschwartz 
554*624Sschwartz 		/* Can't write to ROM */
555*624Sschwartz 		if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) {
556*624Sschwartz 			prg.status = PCITOOL_ROM_WRITE;
557*624Sschwartz 			rval = EIO;
558*624Sschwartz 			goto done_reg;
559*624Sschwartz 		}
560*624Sschwartz 
561*624Sschwartz 		range_base = PX_GET_RANGE_PROP(rp, PCI_MEM32_SPACE);
562*624Sschwartz 		range_base_size = PX_GET_RANGE_PROP_SIZE(rp, PCI_MEM32_SPACE);
563*624Sschwartz 	}
564*624Sschwartz 
565*624Sschwartz 	/* Common code for all IO/MEM range spaces. */
566*624Sschwartz 	max_addr = range_base + range_base_size;
567*624Sschwartz 	DBG(DBG_TOOLS, dip, "Range from 0x%" PRIx64 " to 0x%" PRIx64 "\n",
568*624Sschwartz 	    range_base, range_base + range_base_size);
569*624Sschwartz 
570*624Sschwartz 	base_addr = range_base + bar;
571*624Sschwartz 
572*624Sschwartz 	DBG(DBG_TOOLS, dip,
573*624Sschwartz 	    "addr portion of bar is 0x%" PRIx64 ", base=0x%" PRIx64 ", "
574*624Sschwartz 	    "offset:0x%" PRIx64 "\n", bar, base_addr, prg.offset);
575*624Sschwartz 
576*624Sschwartz 	/*
577*624Sschwartz 	 * Use offset provided by caller to index into desired space.
578*624Sschwartz 	 * Note that prg.status is modified on error.
579*624Sschwartz 	 */
580*624Sschwartz 	prg.phys_addr = base_addr + prg.offset;
581*624Sschwartz 
582*624Sschwartz 	rval = pxtool_pciiomem_access(px_p, &prg, max_addr, &prg.data,
583*624Sschwartz 	    write_flag);
584*624Sschwartz 
585*624Sschwartz done_reg:
586*624Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
587*624Sschwartz 	    mode) != DDI_SUCCESS) {
588*624Sschwartz 		DBG(DBG_TOOLS, dip, "Error returning arguments.\n");
589*624Sschwartz 		rval = EFAULT;
590*624Sschwartz 	}
591*624Sschwartz 	return (rval);
592*624Sschwartz }
593*624Sschwartz 
594*624Sschwartz 
595*624Sschwartz int
596*624Sschwartz pxtool_init(dev_info_t *dip)
597*624Sschwartz {
598*624Sschwartz 	int instance = ddi_get_instance(dip);
599*624Sschwartz 
600*624Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
601*624Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
602*624Sschwartz 	    DDI_NT_REGACC, 0) != DDI_SUCCESS) {
603*624Sschwartz 		return (DDI_FAILURE);
604*624Sschwartz 	}
605*624Sschwartz 
606*624Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
607*624Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
608*624Sschwartz 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
609*624Sschwartz 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
610*624Sschwartz 		return (DDI_FAILURE);
611*624Sschwartz 	}
612*624Sschwartz 
613*624Sschwartz 	return (DDI_SUCCESS);
614*624Sschwartz }
615*624Sschwartz 
616*624Sschwartz 
617*624Sschwartz void
618*624Sschwartz pxtool_uninit(dev_info_t *dip)
619*624Sschwartz {
620*624Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
621*624Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
622*624Sschwartz }
623