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