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