xref: /illumos-gate/usr/src/uts/sun4/io/px/px_tools.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 2007 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 /*ARGSUSED*/
88 static int
89 pxtool_intr_info(dev_info_t *dip, void *arg, int mode)
90 {
91 	pcitool_intr_info_t intr_info;
92 	int rval = SUCCESS;
93 
94 	/* If we need user_version, and to ret same user version as passed in */
95 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
96 	    DDI_SUCCESS) {
97 		return (EFAULT);
98 	}
99 
100 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
101 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
102 	intr_info.num_intr = pxtool_num_inos;
103 
104 	intr_info.drvr_version = PCITOOL_VERSION;
105 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
106 	    DDI_SUCCESS) {
107 		rval = EFAULT;
108 	}
109 
110 	return (rval);
111 }
112 
113 
114 /*
115  * Get interrupt information for a given ino.
116  * Returns info only for inos mapped to devices.
117  *
118  * Returned info is valid only when iget.num_devs is returned > 0.
119  * If ino is not enabled or is not mapped to a device,
120  * iget.num_devs will be returned as = 0.
121  */
122 /*ARGSUSED*/
123 static int
124 pxtool_get_intr(dev_info_t *dip, void *arg, int mode)
125 {
126 	/* Array part isn't used here, but oh well... */
127 	pcitool_intr_get_t partial_iget;
128 	uint32_t ino;
129 	uint8_t num_devs_ret;
130 	int copyout_rval;
131 	sysino_t sysino;
132 	intr_valid_state_t intr_valid_state;
133 	cpuid_t old_cpu_id;
134 	px_t *px_p = DIP_TO_STATE(dip);
135 	pcitool_intr_get_t *iget = &partial_iget;
136 	size_t	iget_kmem_alloc_size = 0;
137 	int rval = SUCCESS;
138 
139 	/* Read in just the header part, no array section. */
140 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
141 	    DDI_SUCCESS)
142 		return (EFAULT);
143 
144 	ino = partial_iget.ino;
145 	num_devs_ret = partial_iget.num_devs_ret;
146 
147 	partial_iget.num_devs_ret = 0;		/* Assume error for now. */
148 	partial_iget.status = PCITOOL_INVALID_INO;
149 	rval = EINVAL;
150 
151 	/* Validate argument. */
152 	if (partial_iget.ino > pxtool_num_inos) {
153 		goto done_get_intr;
154 	}
155 
156 	/* Caller wants device information returned. */
157 	if (num_devs_ret > 0) {
158 
159 		/*
160 		 * Allocate room.
161 		 * Note if num_devs == 0 iget remains pointing to
162 		 * partial_iget.
163 		 */
164 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
165 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
166 
167 		/* Read in whole structure to verify there's room. */
168 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
169 		    SUCCESS) {
170 
171 			/* Be consistent and just return EFAULT here. */
172 			kmem_free(iget, iget_kmem_alloc_size);
173 
174 			return (EFAULT);
175 		}
176 	}
177 
178 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
179 	iget->ino = ino;
180 	iget->num_devs_ret = num_devs_ret;
181 
182 	/* Convert leaf-wide intr to system-wide intr */
183 	if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) ==
184 	    DDI_FAILURE) {
185 		iget->status = PCITOOL_IO_ERROR;
186 		rval = EIO;
187 		goto done_get_intr;
188 	}
189 
190 	/* Operate only on inos which are already enabled. */
191 	if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) ==
192 	    DDI_FAILURE) {
193 		iget->status = PCITOOL_IO_ERROR;
194 		rval = EIO;
195 		goto done_get_intr;
196 	}
197 
198 	/*
199 	 * Consider all valid inos: those mapped to the root complex itself
200 	 * as well as those mapped to devices.
201 	 */
202 	if (intr_valid_state == INTR_VALID) {
203 
204 		/*
205 		 * The following looks up the px_ino and returns
206 		 * info of devices mapped to this ino.
207 		 */
208 		iget->num_devs = pxtool_ib_get_ino_devs(
209 		    px_p, ino, &iget->num_devs_ret, iget->dev);
210 
211 		if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) ==
212 		    DDI_FAILURE) {
213 			iget->status = PCITOOL_IO_ERROR;
214 			rval = EIO;
215 			goto done_get_intr;
216 		}
217 		iget->cpu_id = old_cpu_id;
218 	}
219 
220 	iget->status = PCITOOL_SUCCESS;
221 	rval = SUCCESS;
222 
223 done_get_intr:
224 	iget->drvr_version = PCITOOL_VERSION;
225 	copyout_rval =
226 	    ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode);
227 
228 	if (iget_kmem_alloc_size > 0)
229 		kmem_free(iget, iget_kmem_alloc_size);
230 
231 	if (copyout_rval != DDI_SUCCESS)
232 		rval = EFAULT;
233 
234 	return (rval);
235 }
236 
237 
238 /*
239  * Associate a new CPU with a given ino.
240  *
241  * Operate only on inos which are already mapped to devices.
242  */
243 static int
244 pxtool_set_intr(dev_info_t *dip, void *arg, int mode)
245 {
246 	pcitool_intr_set_t iset;
247 	cpuid_t old_cpu_id;
248 	sysino_t sysino;
249 	px_t *px_p = DIP_TO_STATE(dip);
250 	px_ib_t *ib_p = px_p->px_ib_p;
251 	uint8_t zero = 0;
252 	int rval = SUCCESS;
253 	size_t copyinout_size;
254 
255 	bzero(&iset, sizeof (pcitool_intr_set_t));
256 
257 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
258 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
259 
260 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
261 		return (EFAULT);
262 
263 	switch (iset.user_version) {
264 	case PCITOOL_V1:
265 		break;
266 
267 	case PCITOOL_V2:
268 		copyinout_size = sizeof (pcitool_intr_set_t);
269 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
270 			return (EFAULT);
271 		break;
272 
273 	default:
274 		iset.status = PCITOOL_OUT_OF_RANGE;
275 		rval = ENOTSUP;
276 		goto done_set_intr;
277 	}
278 
279 	if (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP) {
280 		iset.status = PCITOOL_IO_ERROR;
281 		rval = ENOTSUP;
282 		goto done_set_intr;
283 	}
284 
285 	iset.status = PCITOOL_INVALID_INO;
286 	rval = EINVAL;
287 
288 	/* Validate input argument. */
289 	if (iset.ino > pxtool_num_inos)
290 		goto done_set_intr;
291 
292 	/* Validate that ino given belongs to a device. */
293 	if (pxtool_ib_get_ino_devs(px_p, iset.ino, &zero, NULL) == 0)
294 		goto done_set_intr;
295 
296 	/*
297 	 * Get lock, validate cpu and write new mapreg value.
298 	 * Return original cpu value to caller via iset.cpu.
299 	 */
300 	mutex_enter(&cpu_lock);
301 	if (pxtool_validate_cpuid(iset.cpu_id)) {
302 
303 		DBG(DBG_TOOLS, dip, "Enabling CPU %d\n", iset.cpu_id);
304 
305 		if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) ==
306 		    DDI_FAILURE)
307 			goto done_set_intr;
308 
309 		if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) ==
310 		    DDI_FAILURE)
311 			goto done_set_intr;
312 
313 		px_ib_intr_dist_en(dip, iset.cpu_id, iset.ino, B_TRUE);
314 
315 		px_ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino);
316 
317 		iset.cpu_id = old_cpu_id;
318 		iset.status = PCITOOL_SUCCESS;
319 		rval = SUCCESS;
320 
321 	} else {	/* Invalid cpu.  Restore original register image. */
322 
323 		DBG(DBG_TOOLS, dip,
324 		    "Invalid cpuid: writing orig mapreg value\n");
325 
326 		iset.status = PCITOOL_INVALID_CPUID;
327 		rval = EINVAL;
328 	}
329 	mutex_exit(&cpu_lock);
330 
331 done_set_intr:
332 	iset.drvr_version = PCITOOL_VERSION;
333 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
334 		rval = EFAULT;
335 
336 	return (rval);
337 }
338 
339 
340 /* Main function for handling interrupt CPU binding requests and queries. */
341 int
342 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
343 {
344 	int rval = SUCCESS;
345 
346 	switch (cmd) {
347 
348 	/* Get system interrupt information. */
349 	case PCITOOL_SYSTEM_INTR_INFO:
350 		rval = pxtool_intr_info(dip, arg, mode);
351 		break;
352 
353 	/* Get interrupt information for a given ino. */
354 	case PCITOOL_DEVICE_GET_INTR:
355 		rval = pxtool_get_intr(dip, arg, mode);
356 		break;
357 
358 	/* Associate a new CPU with a given ino. */
359 	case PCITOOL_DEVICE_SET_INTR:
360 		rval = pxtool_set_intr(dip, arg, mode);
361 		break;
362 
363 	default:
364 		rval = ENOTTY;
365 	}
366 
367 	return (rval);
368 }
369 
370 
371 static int
372 pxtool_validate_barnum_bdf(pcitool_reg_t *prg)
373 {
374 	int rval = SUCCESS;
375 
376 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
377 		prg->status = PCITOOL_OUT_OF_RANGE;
378 		rval = EINVAL;
379 
380 	/* Validate address arguments of bus / dev / func */
381 	} else if (((prg->bus_no &
382 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
383 	    ((prg->dev_no &
384 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
385 	    ((prg->func_no &
386 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
387 		prg->status = PCITOOL_INVALID_ADDRESS;
388 		rval = EINVAL;
389 	}
390 
391 	return (rval);
392 }
393 
394 /*
395  * px_p defines which leaf, space defines which space in that leaf, offset
396  * defines the offset within the specified space.
397  *
398  * This returns the physical address of the corresponding location.
399  */
400 static uintptr_t
401 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset)
402 {
403 	uint64_t range_base;
404 	int rval;
405 	pci_regspec_t dev_regspec;
406 	struct regspec xlated_regspec;
407 	dev_info_t *dip = px_p->px_dip;
408 
409 	/*
410 	 * Assume that requested entity is small enough to be on the same page.
411 	 * PCItool checks alignment so that this will be true for single
412 	 * accesses.
413 	 */
414 	dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT;
415 	if (space == PCI_CONFIG_SPACE) {
416 		dev_regspec.pci_phys_hi +=
417 		    (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M));
418 		dev_regspec.pci_phys_low = offset & PCI_REG_REG_M;
419 		dev_regspec.pci_phys_mid = 0;	/* Not used */
420 	} else {
421 		dev_regspec.pci_phys_mid = offset >> 32;
422 		dev_regspec.pci_phys_low = offset & 0xffffffff;
423 	}
424 	dev_regspec.pci_size_hi = 0;	/* Not used. */
425 
426 	/* Note: object is guaranteed to be within a page. */
427 	dev_regspec.pci_size_low = 4;
428 
429 	rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec);
430 
431 	DBG(DBG_TOOLS, dip,
432 	    "space:0x%d, offset:0x%" PRIx64 "\n", space, offset);
433 
434 	if (rval != DDI_SUCCESS)
435 		return (NULL);
436 
437 	/* Bustype here returns the high order address bits. */
438 	xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p);
439 
440 	range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) +
441 	    xlated_regspec.regspec_addr;
442 	DBG(DBG_TOOLS, dip,
443 	    "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n",
444 	    xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr,
445 	    xlated_regspec.regspec_size, range_base);
446 
447 	return ((uintptr_t)range_base);
448 }
449 
450 
451 static int
452 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p,
453     uint32_t *space_p)
454 {
455 	int rval;
456 	uint64_t off_in_space;
457 	pcitool_reg_t cfg_prg = *prg_p;	/* Make local copy. */
458 	dev_info_t *dip = px_p->px_dip;
459 
460 	*space_p = PCI_MEM32_SPACE;
461 	*bar_p = 0;
462 
463 	/*
464 	 * Translate BAR number into offset of the BAR in
465 	 * the device's config space.
466 	 */
467 	cfg_prg.acc_attr =
468 	    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
469 
470 	/*
471 	 * Note: sun4u acc function uses phys_addr which includes offset.
472 	 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset.
473 	 */
474 	cfg_prg.offset = PCI_BAR_OFFSET((*prg_p));
475 	off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset;
476 	cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE,
477 	    off_in_space);
478 
479 	DBG(DBG_TOOLS, dip,
480 	    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n",
481 	    off_in_space, cfg_prg.phys_addr, cfg_prg.barnum);
482 
483 	/*
484 	 * Get Bus Address Register (BAR) from config space.
485 	 * cfg_prg.offset is the offset into config space of the
486 	 * BAR desired.  prg_p->status is modified on error.
487 	 */
488 	rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
489 
490 	if (rval != SUCCESS) {
491 		prg_p->status = cfg_prg.status;
492 		return (rval);
493 	}
494 
495 	DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p);
496 
497 	/*
498 	 * BAR has bits saying this space is IO space, unless
499 	 * this is the ROM address register.
500 	 */
501 	if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) &&
502 	    (cfg_prg.offset != PCI_CONF_ROM)) {
503 		*space_p = PCI_IO_SPACE;
504 		*bar_p &= PCI_BASE_IO_ADDR_M;
505 
506 	/*
507 	 * BAR has bits saying this space is 64 bit memory
508 	 * space, unless this is the ROM address register.
509 	 *
510 	 * The 64 bit address stored in two BAR cells is not
511 	 * necessarily aligned on an 8-byte boundary.
512 	 * Need to keep the first 4 bytes read,
513 	 * and do a separate read of the high 4 bytes.
514 	 */
515 	} else if ((PCI_BASE_TYPE_ALL & *bar_p) &&
516 	    (cfg_prg.offset != PCI_CONF_ROM)) {
517 
518 		uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL);
519 
520 		/* Don't try to read the next 4 bytes past the end of BARs. */
521 		if (cfg_prg.offset >= PCI_CONF_BASE5) {
522 			prg_p->status = PCITOOL_OUT_OF_RANGE;
523 			return (EIO);
524 		}
525 
526 		/* Access device.  prg_p->status is modified on error. */
527 		cfg_prg.phys_addr += sizeof (uint32_t);
528 		cfg_prg.offset += sizeof (uint32_t);
529 
530 		rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
531 		if (rval != SUCCESS) {
532 			prg_p->status = cfg_prg.status;
533 			return (rval);
534 		}
535 
536 		/*
537 		 * Honor the 64 bit BAR as such, only when the upper 32 bits
538 		 * store a non-zero value.
539 		 */
540 		if (*bar_p) {
541 			*space_p = PCI_MEM64_SPACE;
542 			*bar_p = (*bar_p << 32) | low_bytes;
543 		} else
544 			*bar_p = low_bytes;
545 
546 	} else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */
547 
548 		/*
549 		 * ROM enabled. Filter ROM enable bit from the BAR.
550 		 * Treat as Mem32 henceforth.
551 		 */
552 		if (!(*bar_p & PCI_BASE_ROM_ENABLE))
553 			*bar_p ^= PCI_BASE_ROM_ENABLE;
554 
555 		else {	/* ROM disabled. */
556 			prg_p->status = PCITOOL_ROM_DISABLED;
557 			return (EIO);
558 		}
559 	}
560 
561 	/* Accept a bar of 0 only for IO space. */
562 	if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) {
563 		prg_p->status = PCITOOL_INVALID_ADDRESS;
564 		return (EINVAL);
565 	}
566 
567 	return (SUCCESS);
568 }
569 
570 
571 /* Perform register accesses on PCI leaf devices. */
572 int
573 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
574 {
575 	pcitool_reg_t	prg;
576 	uint64_t	bar;
577 	uint32_t	space;
578 	uint64_t	off_in_space;
579 	boolean_t	write_flag = B_FALSE;
580 	px_t		*px_p = DIP_TO_STATE(dip);
581 	int		rval = 0;
582 
583 	if (cmd == PCITOOL_DEVICE_SET_REG)
584 		write_flag = B_TRUE;
585 
586 	DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n");
587 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
588 	    mode) != DDI_SUCCESS) {
589 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
590 		return (EFAULT);
591 	}
592 
593 	if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) {
594 		goto done_reg;
595 	}
596 
597 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
598 	    prg.bus_no, prg.dev_no, prg.func_no);
599 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
600 	    prg.barnum, prg.offset, prg.acc_attr);
601 
602 	if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS)
603 		goto done_reg;
604 
605 	if (prg.barnum == 0) {	/* Proper config space desired. */
606 
607 		/* Enforce offset limits. */
608 		if (prg.offset >= DEV_CFG_SPACE_SIZE) {
609 			DBG(DBG_TOOLS, dip,
610 			    "Config space offset 0x%" PRIx64 " out of range\n",
611 			    prg.offset);
612 			prg.status = PCITOOL_OUT_OF_RANGE;
613 			rval = EINVAL;
614 			goto done_reg;
615 		}
616 
617 		/*
618 		 * For sun4v, config space base won't be known.
619 		 * pxtool_get_phys_addr will return zero.
620 		 * Note that for sun4v, phys_addr isn't
621 		 * used for making config space accesses.
622 		 *
623 		 * For sun4u, assume that phys_addr will come back valid.
624 		 */
625 		/*
626 		 * Accessed entity is assumed small enough to be on one page.
627 		 *
628 		 * Since config space is less than a page and is aligned to
629 		 * 0x1000, a device's entire config space will be on a single
630 		 * page.  Pass the device's base config space address here,
631 		 * then add the offset within that space later.  This works
632 		 * around an issue in px_xlate_reg (called by
633 		 * pxtool_get_phys_addr) which accepts only a 256 byte
634 		 * range within a device.
635 		 */
636 		off_in_space = PX_GET_BDF(&prg);
637 		prg.phys_addr =
638 		    pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space);
639 		prg.phys_addr += prg.offset;
640 
641 		DBG(DBG_TOOLS, dip,
642 		    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", "
643 		    "end:%s\n", off_in_space, prg.phys_addr,
644 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl");
645 
646 		/*
647 		 * Access device.  pr.status is modified.
648 		 * BDF is assumed valid at this point.
649 		 */
650 		rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag);
651 		goto done_reg;
652 	}
653 
654 	/* IO/ MEM/ MEM64 space. */
655 
656 	if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS)
657 		goto done_reg;
658 
659 	switch (space) {
660 	case PCI_MEM32_SPACE:
661 
662 		DBG(DBG_TOOLS, dip, "32 bit mem space\n");
663 
664 		/* Can't write to ROM */
665 		if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) {
666 			prg.status = PCITOOL_ROM_WRITE;
667 			rval = EIO;
668 			goto done_reg;
669 		}
670 		break;
671 
672 	case PCI_IO_SPACE:
673 		DBG(DBG_TOOLS, dip, "IO space\n");
674 		break;
675 
676 	case PCI_MEM64_SPACE:
677 		DBG(DBG_TOOLS, dip,
678 		    "64 bit mem space.  64-bit bar is 0x%" PRIx64 "\n", bar);
679 		break;
680 
681 	default:
682 		DBG(DBG_TOOLS, dip, "Unknown space!\n");
683 		prg.status = PCITOOL_IO_ERROR;
684 		rval = EIO;
685 		goto done_reg;
686 	}
687 
688 	/*
689 	 * Common code for all IO/MEM range spaces.
690 	 *
691 	 * Use offset provided by caller to index into desired space.
692 	 * Note that prg.status is modified on error.
693 	 */
694 	off_in_space = bar + prg.offset;
695 	prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space);
696 
697 	DBG(DBG_TOOLS, dip,
698 	    "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", "
699 	    "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr);
700 
701 	rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag);
702 
703 done_reg:
704 	prg.drvr_version = PCITOOL_VERSION;
705 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
706 	    mode) != DDI_SUCCESS) {
707 		DBG(DBG_TOOLS, dip, "Error returning arguments.\n");
708 		rval = EFAULT;
709 	}
710 	return (rval);
711 }
712 
713 
714 int
715 pxtool_init(dev_info_t *dip)
716 {
717 	int instance = ddi_get_instance(dip);
718 
719 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
720 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
721 	    DDI_NT_REGACC, 0) != DDI_SUCCESS) {
722 		return (DDI_FAILURE);
723 	}
724 
725 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
726 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
727 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
728 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
729 		return (DDI_FAILURE);
730 	}
731 
732 	return (DDI_SUCCESS);
733 }
734 
735 
736 void
737 pxtool_uninit(dev_info_t *dip)
738 {
739 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
740 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
741 }
742