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