xref: /illumos-gate/usr/src/uts/sun4u/io/pci/pci_tools.c (revision d4476ccb08e9498c2013971c4212dc6362fcec46)
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/stat.h>
30 #include <sys/sunddi.h>
31 #include <sys/param.h>
32 
33 #include <sys/sysmacros.h>
34 #include <sys/machsystm.h>
35 #include <sys/promif.h>
36 #include <sys/cpuvar.h>
37 
38 #include <sys/pci/pci_obj.h>
39 #include <sys/hotplug/pci/pcihp.h>
40 
41 #include <sys/pci_tools.h>
42 #include <sys/pci/pci_tools_ext.h>
43 
44 /*
45  * Number of interrupts supported per PCI bus.
46  */
47 #define	PCI_MAX_INO		0x3f
48 
49 /*
50  * PCI Space definitions.
51  */
52 #define	PCI_CONFIG_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
53 #define	PCI_IO_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_IO))
54 #define	PCI_MEM_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
55 #define	PCI_MEM64_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
56 
57 /*
58  * Extract 64 bit parent or size values from 32 bit cells of
59  * pci_ranges_t property.
60  *
61  * Only bits 42-32 are relevant in parent_high.
62  */
63 #define	PCI_GET_RANGE_PROP(ranges, bank) \
64 	((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
65 	ranges[bank].parent_low)
66 
67 #define	PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
68 	((((uint64_t)(ranges[bank].size_high)) << 32) | \
69 	ranges[bank].size_low)
70 
71 #define	PCI_BAR_OFFSET(x)	(pci_bars[x.barnum])
72 
73 /* Big and little endian as boolean values. */
74 #define	BE B_TRUE
75 #define	LE B_FALSE
76 
77 #define	SUCCESS	0
78 
79 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
80 typedef union {
81 	uint64_t u64;
82 	uint32_t u32;
83 	uint16_t u16;
84 	uint8_t u8;
85 } peek_poke_value_t;
86 
87 /*
88  * Offsets of BARS in config space.  First entry of 0 means config space.
89  * Entries here correlate to pcitool_bars_t enumerated type.
90  */
91 static uint8_t pci_bars[] = {
92 	0x0,
93 	PCI_CONF_BASE0,
94 	PCI_CONF_BASE1,
95 	PCI_CONF_BASE2,
96 	PCI_CONF_BASE3,
97 	PCI_CONF_BASE4,
98 	PCI_CONF_BASE5,
99 	PCI_CONF_ROM
100 };
101 
102 /*LINTLIBRARY*/
103 
104 static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
105     uint64_t paddr, uint64_t *value_p);
106 static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
107     uint64_t paddr, uint64_t value);
108 static boolean_t pcitool_validate_cpuid(uint32_t cpu_id);
109 static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
110     uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
111     uint32_t *pcitool_status);
112 static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
113 static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
114     uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
115     boolean_t *is_io_space);
116 static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
117     uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
118 static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
119 static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
120 static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
121 
122 extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
123 extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
124 
125 /*
126  * Safe C wrapper around assy language routine pci_do_phys_peek
127  *
128  * Type is TRUE for big endian, FALSE for little endian.
129  * Size is 1, 2, 4 or 8 bytes.
130  * paddr is the physical address in IO space to access read.
131  * value_p is where the value is returned.
132  */
133 static int
134 pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
135 	uint64_t paddr, uint64_t *value_p)
136 {
137 	on_trap_data_t otd;
138 	int err = DDI_SUCCESS;
139 	peek_poke_value_t peek_value;
140 
141 	pbm_t *pbm_p = pci_p->pci_pbm_p;
142 
143 	pbm_p->pbm_ontrap_data = &otd;
144 
145 	/* Set up trap handling to make the access safe. */
146 
147 	/*
148 	 * on_trap works like setjmp.
149 	 * Set it up to not panic on data access error,
150 	 * but to call peek_fault instead.
151 	 * Call pci_do_phys_peek after trap handling is setup.
152 	 * When on_trap returns FALSE, it has been setup.
153 	 * When it returns TRUE, an it has caught an error.
154 	 */
155 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
156 		otd.ot_trampoline = (uintptr_t)&peek_fault;
157 		err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
158 	} else {
159 		err = DDI_FAILURE;
160 	}
161 
162 	pbm_p->pbm_ontrap_data = NULL;
163 	no_trap();
164 
165 	if (err != DDI_FAILURE) {
166 		switch (size) {
167 		case 8:
168 			*value_p = (uint64_t)peek_value.u64;
169 			break;
170 		case 4:
171 			*value_p = (uint64_t)peek_value.u32;
172 			break;
173 		case 2:
174 			*value_p = (uint64_t)peek_value.u16;
175 			break;
176 		case 1:
177 			*value_p = (uint64_t)peek_value.u8;
178 			break;
179 		default:
180 			err = DDI_FAILURE;
181 		}
182 	}
183 
184 	return (err);
185 }
186 
187 /*
188  * Safe C wrapper around assy language routine pci_do_phys_poke
189  *
190  * Type is TRUE for big endian, FALSE for little endian.
191  * Size is 1,2,4 or 8 bytes.
192  * paddr is the physical address in IO space to access read.
193  * value contains the value to be written.
194  */
195 static int
196 pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
197 	uint64_t paddr, uint64_t value)
198 {
199 	on_trap_data_t otd;
200 	int err = DDI_SUCCESS;
201 	peek_poke_value_t poke_value;
202 
203 	pbm_t *pbm_p = pci_p->pci_pbm_p;
204 
205 	switch (size) {
206 	case 8:
207 		poke_value.u64 = value;
208 		break;
209 	case 4:
210 		poke_value.u32 = (uint32_t)value;
211 		break;
212 	case 2:
213 		poke_value.u16 = (uint16_t)value;
214 		break;
215 	case 1:
216 		poke_value.u8 = (uint8_t)value;
217 		break;
218 	default:
219 		return (DDI_FAILURE);
220 	}
221 
222 	mutex_enter(&pbm_p->pbm_pokefault_mutex);
223 
224 	pbm_p->pbm_ontrap_data = &otd;
225 
226 	/*
227 	 * on_trap works like setjmp.
228 	 * Set it up to not panic on data access error,
229 	 * but to call poke_fault instead.
230 	 * Call pci_do_phys_poke after trap handling is setup.
231 	 * When on_trap returns FALSE, it has been setup.
232 	 * When it returns TRUE, an it has caught an error.
233 	 */
234 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
235 		otd.ot_trampoline = (uintptr_t)&poke_fault;
236 		err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
237 	}
238 
239 	/* Let the dust settle and errors occur if they will. */
240 	pbm_clear_error(pbm_p);
241 
242 	/* Check for an error. */
243 	if (otd.ot_trap == OT_DATA_ACCESS) {
244 		err = DDI_FAILURE;
245 	}
246 
247 	pbm_p->pbm_ontrap_data = NULL;
248 	mutex_exit(&pbm_p->pbm_pokefault_mutex);
249 
250 	no_trap();
251 	return (err);
252 }
253 
254 
255 /*
256  * Validate the cpu_id passed in.
257  * A value of B_TRUE will be returned for success.
258  */
259 static boolean_t
260 pcitool_validate_cpuid(uint32_t cpuid)
261 {
262 	extern const int _ncpu;
263 	extern cpu_t	*cpu[];
264 
265 	ASSERT(mutex_owned(&cpu_lock));
266 
267 	return ((cpuid < _ncpu) && (cpu[cpuid] && cpu_is_online(cpu[cpuid])));
268 }
269 
270 
271 /* Return the number of interrupts on a pci bus. */
272 static int
273 pcitool_intr_get_max_ino(uint32_t *arg, int mode)
274 {
275 	uint32_t num_intr = PCI_MAX_INO;
276 
277 	if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) !=
278 	    DDI_SUCCESS) {
279 		return (EFAULT);
280 	} else {
281 		return (SUCCESS);
282 	}
283 }
284 
285 
286 /*
287  * Get interrupt information for a given ino.
288  * Returns info only for inos mapped to devices.
289  *
290  * Returned info is valid only when iget.num_devs is returned > 0.
291  * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
292  */
293 /*ARGSUSED*/
294 static int
295 pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
296 {
297 	/* Array part isn't used here, but oh well... */
298 	pcitool_intr_get_t partial_iget;
299 	pcitool_intr_get_t *iget = &partial_iget;
300 	size_t	iget_kmem_alloc_size = 0;
301 	ib_t *ib_p = pci_p->pci_ib_p;
302 	volatile uint64_t *imregp;
303 	uint64_t imregval;
304 	uint32_t ino;
305 	uint8_t num_devs_ret;
306 	int copyout_rval;
307 	int rval = SUCCESS;
308 
309 	/* Read in just the header part, no array section. */
310 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
311 	    DDI_SUCCESS) {
312 
313 		return (EFAULT);
314 	}
315 
316 	ino = partial_iget.ino;
317 	num_devs_ret = partial_iget.num_devs_ret;
318 
319 	/* Validate argument. */
320 	if (ino > PCI_MAX_INO) {
321 		partial_iget.status = PCITOOL_INVALID_INO;
322 		partial_iget.num_devs_ret = 0;
323 		rval = EINVAL;
324 		goto done_get_intr;
325 	}
326 
327 	/* Caller wants device information returned. */
328 	if (num_devs_ret > 0) {
329 
330 		/*
331 		 * Allocate room.
332 		 * Note if num_devs_ret == 0 iget remains pointing to
333 		 * partial_iget.
334 		 */
335 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
336 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
337 
338 		/* Read in whole structure to verify there's room. */
339 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
340 		    SUCCESS) {
341 
342 			/* Be consistent and just return EFAULT here. */
343 			kmem_free(iget, iget_kmem_alloc_size);
344 
345 			return (EFAULT);
346 		}
347 	}
348 
349 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
350 	iget->ino = ino;
351 	iget->num_devs_ret = num_devs_ret;
352 
353 	imregp = ib_intr_map_reg_addr(ib_p, ino);
354 	imregval = *imregp;
355 
356 	/*
357 	 * Read "valid" bit.  If set, interrupts are enabled.
358 	 * This bit happens to be the same on Fire and Tomatillo.
359 	 */
360 	if (imregval & COMMON_INTR_MAP_REG_VALID) {
361 
362 		/*
363 		 * The following looks up the ib_ino_info and returns
364 		 * info of devices mapped to this ino.
365 		 */
366 		iget->num_devs = ib_get_ino_devs(
367 		    ib_p, ino, &iget->num_devs_ret, iget->dev);
368 
369 		/*
370 		 * Consider only inos mapped to devices (as opposed to
371 		 * inos mapped to the bridge itself.
372 		 */
373 		if (iget->num_devs > 0) {
374 
375 			/*
376 			 * These 2 items are platform specific,
377 			 * extracted from the bridge.
378 			 */
379 			iget->ctlr = 0;
380 			iget->cpu_id = ib_map_reg_get_cpu(imregval);
381 		}
382 	}
383 done_get_intr:
384 	iget->drvr_version = PCITOOL_DRVR_VERSION;
385 	copyout_rval = ddi_copyout(iget, arg,
386 	    PCITOOL_IGET_SIZE(num_devs_ret), mode);
387 
388 	if (iget_kmem_alloc_size > 0) {
389 		kmem_free(iget, iget_kmem_alloc_size);
390 	}
391 
392 	if (copyout_rval != DDI_SUCCESS) {
393 		rval = EFAULT;
394 	}
395 
396 	return (rval);
397 }
398 
399 /*
400  * Associate a new CPU with a given ino.
401  *
402  * Operate only on inos which are already mapped to devices.
403  */
404 static int
405 pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
406 {
407 	ib_t *ib_p = pci_p->pci_ib_p;
408 	int rval = SUCCESS;
409 
410 	uint8_t zero = 0;
411 	pcitool_intr_set_t iset;
412 	uint32_t old_cpu_id;
413 	hrtime_t start_time;
414 	uint64_t imregval;
415 	uint64_t new_imregval;
416 	volatile uint64_t *imregp;
417 	volatile uint64_t *idregp;
418 
419 	if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) !=
420 	    DDI_SUCCESS)
421 		return (EFAULT);
422 
423 	/* Validate input argument and that ino given belongs to a device. */
424 	if ((iset.ino > PCI_MAX_INO) ||
425 	    (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
426 		iset.status = PCITOOL_INVALID_INO;
427 		rval = EINVAL;
428 		goto done_set_intr;
429 	}
430 
431 	imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
432 	idregp = IB_INO_INTR_STATE_REG(ib_p, iset.ino);
433 
434 	DEBUG4(DBG_TOOLS, dip, "set_intr: cpu:%d, ino:0x%x, mapreg @ "
435 	    "0x%llx, intr_stat @ 0x%llx\n",
436 	    iset.cpu_id, iset.ino, imregp, idregp);
437 
438 	/* Save original mapreg value. */
439 	imregval = *imregp;
440 	DEBUG1(DBG_TOOLS, dip, "orig mapreg value: 0x%llx\n", imregval);
441 
442 	/* Is this request a noop? */
443 	if ((old_cpu_id = ib_map_reg_get_cpu(imregval)) == iset.cpu_id) {
444 		iset.status = PCITOOL_SUCCESS;
445 		goto done_set_intr;
446 	}
447 
448 	/* Operate only on inos which are already enabled. */
449 	if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
450 		iset.status = PCITOOL_INVALID_INO;
451 		rval = EINVAL;
452 		goto done_set_intr;
453 	}
454 
455 	/* Clear the interrupt valid/enable bit for particular ino. */
456 	DEBUG0(DBG_TOOLS, dip, "Clearing intr_enabled...\n");
457 	*imregp = imregval & ~COMMON_INTR_MAP_REG_VALID;
458 
459 	/* Wait until there are no more pending interrupts. */
460 	start_time = gethrtime();
461 
462 	DEBUG0(DBG_TOOLS, dip, "About to check for pending interrupts...\n");
463 
464 	while (IB_INO_INTR_PENDING(idregp, iset.ino)) {
465 
466 		DEBUG0(DBG_TOOLS, dip, "Waiting for pending ints to clear\n");
467 		if ((gethrtime() - start_time) < pci_intrpend_timeout)
468 			continue;
469 
470 		else {	/* Timed out waiting. */
471 			iset.status = PCITOOL_PENDING_INTRTIMEOUT;
472 			rval = ETIME;
473 			goto done_set_intr;
474 		}
475 	}
476 
477 	new_imregval = *imregp;
478 
479 	DEBUG1(DBG_TOOLS, dip,
480 	    "after disabling intr, mapreg value: 0x%llx\n", new_imregval);
481 
482 	/*
483 	 * Get lock, validate cpu and write new mapreg value.
484 	 * Return original cpu value to caller via iset.cpu_id.
485 	 */
486 	mutex_enter(&cpu_lock);
487 	if (pcitool_validate_cpuid(iset.cpu_id)) {
488 
489 		/* Prepare new mapreg value with intr enabled and new cpu_id. */
490 		new_imregval &=
491 		    COMMON_INTR_MAP_REG_IGN | COMMON_INTR_MAP_REG_INO;
492 		new_imregval = ib_get_map_reg(new_imregval, iset.cpu_id);
493 
494 		DEBUG1(DBG_TOOLS, dip, "Writing new mapreg value:0x%llx\n",
495 		    new_imregval);
496 
497 		*imregp = new_imregval;
498 
499 		ib_log_new_cpu(ib_p, old_cpu_id, iset.cpu_id, iset.ino);
500 
501 		mutex_exit(&cpu_lock);
502 
503 		iset.cpu_id = old_cpu_id;
504 		iset.status = PCITOOL_SUCCESS;
505 
506 	} else {	/* Invalid cpu.  Restore original register image. */
507 
508 		DEBUG0(DBG_TOOLS, dip,
509 		    "Invalid cpuid: writing orig mapreg value\n");
510 
511 		*imregp = imregval;
512 		mutex_exit(&cpu_lock);
513 		iset.status = PCITOOL_INVALID_CPUID;
514 		rval = EINVAL;
515 	}
516 done_set_intr:
517 	iset.drvr_version = PCITOOL_DRVR_VERSION;
518 	if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) !=
519 	    DDI_SUCCESS)
520 		rval = EFAULT;
521 
522 	return (rval);
523 }
524 
525 
526 /* Main function for handling interrupt CPU binding requests and queries. */
527 int
528 pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
529 {
530 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
531 	dev_info_t	*dip = pci_p->pci_dip;
532 	int		rval = SUCCESS;
533 
534 	switch (cmd) {
535 
536 	/* Return the number of interrupts supported by a PCI bus. */
537 	case PCITOOL_DEVICE_NUM_INTR:
538 		rval = pcitool_intr_get_max_ino(arg, mode);
539 		break;
540 
541 	/* Get interrupt information for a given ino. */
542 	case PCITOOL_DEVICE_GET_INTR:
543 		rval = pcitool_get_intr(dip, arg, mode, pci_p);
544 		break;
545 
546 	/* Associate a new CPU with a given ino. */
547 	case PCITOOL_DEVICE_SET_INTR:
548 		rval = pcitool_set_intr(dip, arg, mode, pci_p);
549 		break;
550 
551 	default:
552 		rval = ENOTTY;
553 	}
554 
555 	return (rval);
556 }
557 
558 
559 /*
560  * Wrapper around pcitool_phys_peek/poke.
561  *
562  * Validates arguments and calls pcitool_phys_peek/poke appropriately.
563  *
564  * Dip is of the nexus,
565  * phys_addr is the address to write in physical space,
566  * max_addr is the upper bound on the physical space used for bounds checking,
567  * pcitool_status returns more detailed status in addition to a more generic
568  * errno-style function return value.
569  * other args are self-explanatory.
570  */
571 static int
572 pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
573 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
574 	uint32_t *pcitool_status)
575 {
576 
577 	int rval = SUCCESS;
578 	dev_info_t *dip = pci_p->pci_dip;
579 
580 	/* Upper bounds checking. */
581 	if (phys_addr > max_addr) {
582 		DEBUG2(DBG_TOOLS, dip,
583 		    "Phys addr 0x%llx out of range (max 0x%llx).\n",
584 		    phys_addr, max_addr);
585 		*pcitool_status = PCITOOL_INVALID_ADDRESS;
586 
587 		rval = EINVAL;
588 
589 	/* Alignment checking. */
590 	} else if (!IS_P2ALIGNED(phys_addr, size)) {
591 		DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
592 		*pcitool_status = PCITOOL_NOT_ALIGNED;
593 
594 		rval = EINVAL;
595 
596 	/* Made it through checks.  Do the access. */
597 	} else if (write) {
598 
599 		DEBUG3(DBG_PHYS_ACC, dip,
600 		    "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
601 		    size, (endian ? "BE" : "LE"), phys_addr);
602 
603 		if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
604 		    *data) != DDI_SUCCESS) {
605 			DEBUG3(DBG_PHYS_ACC, dip,
606 			    "%d byte %s pcitool_phys_poke at addr "
607 			    "0x%llx failed\n",
608 			    size, (endian ? "BE" : "LE"), phys_addr);
609 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
610 
611 			rval = EFAULT;
612 		}
613 
614 	} else {	/* Read */
615 
616 		DEBUG3(DBG_PHYS_ACC, dip,
617 		    "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
618 		    size, (endian ? "BE" : "LE"), phys_addr);
619 
620 		if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
621 		    data) != DDI_SUCCESS) {
622 			DEBUG3(DBG_PHYS_ACC, dip,
623 			    "%d byte %s pcitool_phys_peek at addr "
624 			    "0x%llx failed\n",
625 			    size, (endian ? "BE" : "LE"), phys_addr);
626 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
627 
628 			rval = EFAULT;
629 		}
630 	}
631 	return (rval);
632 }
633 
634 /*
635  * Perform register accesses on the nexus device itself.
636  */
637 int
638 pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
639 {
640 
641 	pci_t			*pci_p = DEV_TO_SOFTSTATE(dev);
642 	dev_info_t		*dip = pci_p->pci_dip;
643 	pci_nexus_regspec_t	*pci_rp = NULL;
644 	boolean_t		write_flag = B_FALSE;
645 	pcitool_reg_t		prg;
646 	uint64_t		base_addr;
647 	uint64_t		max_addr;
648 	uint32_t		reglen;
649 	uint8_t			size;
650 	uint32_t		rval = 0;
651 
652 	if (cmd == PCITOOL_NEXUS_SET_REG)
653 		write_flag = B_TRUE;
654 
655 	DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
656 
657 	/* Read data from userland. */
658 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
659 	    DDI_SUCCESS) {
660 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
661 		return (EFAULT);
662 	}
663 
664 	/* Read reg property which contains starting addr and size of banks. */
665 	if (ddi_prop_lookup_int_array(
666 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
667 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
668 		if (((reglen * sizeof (int)) %
669 		    sizeof (pci_nexus_regspec_t)) != 0) {
670 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
671 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
672 			rval = EIO;
673 			goto done;
674 		}
675 	}
676 
677 	/* Bounds check the bank number. */
678 	if (prg.barnum >=
679 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
680 		prg.status = PCITOOL_OUT_OF_RANGE;
681 		rval = EINVAL;
682 		goto done;
683 	}
684 
685 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
686 	base_addr = pci_rp[prg.barnum].phys_addr;
687 	max_addr = base_addr + pci_rp[prg.barnum].size;
688 	prg.phys_addr = base_addr + prg.offset;
689 
690 	DEBUG4(DBG_TOOLS, dip,
691 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
692 	    "addr:0x%llx, max_addr:0x%llx\n",
693 	    base_addr, prg.offset, prg.phys_addr, max_addr);
694 
695 	/* Access device.  prg.status is modified. */
696 	rval = pcitool_access(pci_p,
697 	    prg.phys_addr, max_addr, &prg.data, size, write_flag,
698 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
699 
700 done:
701 	if (pci_rp != NULL)
702 		ddi_prop_free(pci_rp);
703 
704 	prg.drvr_version = PCITOOL_DRVR_VERSION;
705 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
706 	    DDI_SUCCESS) {
707 		DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
708 		return (EFAULT);
709 	}
710 
711 	return (rval);
712 }
713 
714 
715 static int
716 pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
717 {
718 	int rval = SUCCESS;
719 
720 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
721 		prg->status = PCITOOL_OUT_OF_RANGE;
722 		rval = EINVAL;
723 
724 	/* Validate address arguments of bus / dev / func */
725 	} else if (((prg->bus_no &
726 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
727 	    ((prg->dev_no &
728 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
729 	    ((prg->func_no &
730 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
731 		prg->status = PCITOOL_INVALID_ADDRESS;
732 		rval = EINVAL;
733 	}
734 
735 	return (rval);
736 }
737 
738 static int
739 pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
740 	uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
741 {
742 
743 	uint8_t bar_offset;
744 	int rval;
745 	dev_info_t *dip = pci_p->pci_dip;
746 
747 	*bar = 0;
748 	*is_io_space = B_FALSE;
749 
750 	/*
751 	 * Translate BAR number into offset of the BAR in
752 	 * the device's config space.
753 	 */
754 	bar_offset = PCI_BAR_OFFSET((*prg));
755 
756 	DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
757 	    prg->barnum, bar_offset);
758 
759 	/*
760 	 * Get Bus Address Register (BAR) from config space.
761 	 * bar_offset is the offset into config space of the BAR desired.
762 	 * prg->status is modified on error.
763 	 */
764 	rval = pcitool_access(pci_p, config_base_addr + bar_offset,
765 	    config_max_addr, bar,
766 	    4,		/* 4 bytes. */
767 	    B_FALSE,	/* Read */
768 	    B_FALSE, 	/* Little endian. */
769 	    &prg->status);
770 	if (rval != SUCCESS)
771 		return (rval);
772 
773 	DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
774 	if (!(*bar)) {
775 		prg->status = PCITOOL_INVALID_ADDRESS;
776 		return (EINVAL);
777 	}
778 
779 	/*
780 	 * BAR has bits saying this space is IO space, unless
781 	 * this is the ROM address register.
782 	 */
783 	if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
784 	    (bar_offset != PCI_CONF_ROM)) {
785 		*is_io_space = B_TRUE;
786 		*bar &= PCI_BASE_IO_ADDR_M;
787 
788 	/*
789 	 * BAR has bits saying this space is 64 bit memory
790 	 * space, unless this is the ROM address register.
791 	 *
792 	 * The 64 bit address stored in two BAR cells is not necessarily
793 	 * aligned on an 8-byte boundary.  Need to keep the first 4
794 	 * bytes read, and do a separate read of the high 4 bytes.
795 	 */
796 
797 	} else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
798 
799 		uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
800 
801 		/* Don't try to read past the end of BARs. */
802 		if (bar_offset >= PCI_CONF_BASE5) {
803 			prg->status = PCITOOL_OUT_OF_RANGE;
804 			return (EIO);
805 		}
806 
807 		/* Access device.  prg->status is modified on error. */
808 		rval = pcitool_access(pci_p,
809 		    config_base_addr + bar_offset + 4, config_max_addr, bar,
810 		    4,		/* 4 bytes. */
811 		    B_FALSE,	/* Read */
812 		    B_FALSE, 	/* Little endian. */
813 		    &prg->status);
814 		if (rval != SUCCESS)
815 			return (rval);
816 
817 		*bar = (*bar << 32) + low_bytes;
818 	}
819 
820 	return (SUCCESS);
821 }
822 
823 
824 static int
825 pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
826 	uint64_t max_addr, uint8_t size, boolean_t write_flag)
827 {
828 	int rval;
829 	dev_info_t *dip = pci_p->pci_dip;
830 
831 	/* Access config space and we're done. */
832 	prg->phys_addr = base_addr + prg->offset;
833 
834 	DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
835 	    "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
836 	    base_addr, prg->offset, prg->phys_addr,
837 	    (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
838 
839 	/* Access device.  pr.status is modified. */
840 	rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
841 	    write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
842 
843 	DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
844 
845 	return (rval);
846 }
847 
848 /* Perform register accesses on PCI leaf devices. */
849 int
850 pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
851 {
852 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
853 	dev_info_t	*dip = pci_p->pci_dip;
854 	pci_ranges_t	*rp = pci_p->pci_ranges;
855 	pcitool_reg_t	prg;
856 	uint64_t	max_addr;
857 	uint64_t	base_addr;
858 	uint64_t	range_prop;
859 	uint64_t	range_prop_size;
860 	uint64_t	bar = 0;
861 	int		rval = 0;
862 	boolean_t	write_flag = B_FALSE;
863 	boolean_t	is_io_space = B_FALSE;
864 	uint8_t		size;
865 
866 	if (cmd == PCITOOL_DEVICE_SET_REG)
867 		write_flag = B_TRUE;
868 
869 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
870 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
871 	    DDI_SUCCESS) {
872 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
873 		return (EFAULT);
874 	}
875 
876 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
877 	    prg.bus_no, prg.dev_no, prg.func_no);
878 
879 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
880 		goto done_reg;
881 
882 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
883 
884 	/* Get config space first. */
885 	range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
886 	range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
887 	max_addr = range_prop + range_prop_size;
888 
889 	/*
890 	 * Build device address based on base addr from range prop, and bus,
891 	 * dev and func values passed in.  This address is where config space
892 	 * begins.
893 	 */
894 	base_addr = range_prop +
895 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
896 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
897 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
898 
899 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
900 		prg.status = PCITOOL_OUT_OF_RANGE;
901 		rval = EINVAL;
902 		goto done_reg;
903 	}
904 
905 	DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
906 	    "func:0x%x, addr:0x%x\n", range_prop,
907 	    prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
908 	    prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
909 
910 	/* Proper config space desired. */
911 	if (prg.barnum == 0) {
912 
913 		rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
914 		    size, write_flag);
915 
916 	} else {	/* IO / MEM / MEM64 space. */
917 
918 		if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
919 		    &is_io_space) != SUCCESS)
920 			goto done_reg;
921 
922 		/* IO space. */
923 		if (is_io_space) {
924 
925 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
926 
927 			/* Reposition to focus on IO space. */
928 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
929 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
930 			    PCI_IO_RANGE_BANK);
931 
932 		/* 64 bit memory space. */
933 		} else if ((bar >> 32) != 0) {
934 
935 			DEBUG1(DBG_TOOLS, dip,
936 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
937 
938 			/* Reposition to MEM64 range space. */
939 			range_prop = PCI_GET_RANGE_PROP(rp,
940 			    PCI_MEM64_RANGE_BANK);
941 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
942 			    PCI_MEM64_RANGE_BANK);
943 
944 		} else {	/* Mem32 space, including ROM */
945 
946 			DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
947 
948 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
949 
950 				DEBUG0(DBG_TOOLS, dip,
951 				    "Additional ROM checking\n");
952 
953 				/* Can't write to ROM */
954 				if (write_flag) {
955 					prg.status = PCITOOL_ROM_WRITE;
956 					rval = EIO;
957 					goto done_reg;
958 
959 				/* ROM disabled for reading */
960 				} else if (!(bar & 0x00000001)) {
961 					prg.status = PCITOOL_ROM_DISABLED;
962 					rval = EIO;
963 					goto done_reg;
964 				}
965 			}
966 
967 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
968 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
969 			    PCI_MEM_RANGE_BANK);
970 		}
971 
972 		/* Common code for all IO/MEM range spaces. */
973 		max_addr = range_prop + range_prop_size;
974 		base_addr = range_prop + bar;
975 
976 		DEBUG3(DBG_TOOLS, dip,
977 		    "addr portion of bar is 0x%llx, base=0x%llx, "
978 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
979 
980 		/*
981 		 * Use offset provided by caller to index into
982 		 * desired space, then access.
983 		 * Note that prg.status is modified on error.
984 		 */
985 		prg.phys_addr = base_addr + prg.offset;
986 		rval = pcitool_access(pci_p, prg.phys_addr,
987 		    max_addr, &prg.data, size, write_flag,
988 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
989 	}
990 
991 done_reg:
992 	prg.drvr_version = PCITOOL_DRVR_VERSION;
993 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
994 	    DDI_SUCCESS) {
995 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
996 		rval = EFAULT;
997 	}
998 	return (rval);
999 }
1000 
1001 int
1002 pcitool_init(dev_info_t *dip)
1003 {
1004 	int instance = ddi_get_instance(dip);
1005 
1006 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
1007 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
1008 	    DDI_NT_REGACC, 0) != DDI_SUCCESS)
1009 		return (DDI_FAILURE);
1010 
1011 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
1012 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
1013 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
1014 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
1015 		return (DDI_FAILURE);
1016 	}
1017 
1018 	return (DDI_SUCCESS);
1019 }
1020 
1021 void
1022 pcitool_uninit(dev_info_t *dip)
1023 {
1024 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
1025 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
1026 }
1027