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