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