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