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