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, ®len) == 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