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/types.h>
27 #include <sys/stat.h>
28 #include <sys/cpuvar.h>
29 #include <sys/kmem.h>
30 #include <sys/sunddi.h>
31 #include "px_obj.h"
32 #include <sys/pci_tools.h>
33 #include "px_tools_ext.h"
34 #include "px_tools_var.h"
35
36 /*
37 * PCI Space definitions.
38 */
39 #define PCI_CONFIG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
40 #define PCI_IO_SPACE (PCI_REG_ADDR_G(PCI_ADDR_IO))
41 #define PCI_MEM32_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM32))
42 #define PCI_MEM64_SPACE (PCI_REG_ADDR_G(PCI_ADDR_MEM64))
43
44 /*
45 * Config space range for a device. IEEE 1275 spec defines for PCI.
46 * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA
47 */
48 #define DEV_CFG_SPACE_SIZE \
49 (1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA))
50
51 /*
52 * Offsets of BARS in config space. First entry of 0 means config space.
53 * Entries here correlate to pcitool_bars_t enumerated type.
54 */
55 uint8_t pci_bars[] = {
56 0x0,
57 PCI_CONF_BASE0,
58 PCI_CONF_BASE1,
59 PCI_CONF_BASE2,
60 PCI_CONF_BASE3,
61 PCI_CONF_BASE4,
62 PCI_CONF_BASE5,
63 PCI_CONF_ROM
64 };
65
66 int pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]);
67
68
69 /*ARGSUSED*/
70 static int
pxtool_intr_info(dev_info_t * dip,void * arg,int mode)71 pxtool_intr_info(dev_info_t *dip, void *arg, int mode)
72 {
73 px_t *px_p = DIP_TO_STATE(dip);
74 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state;
75 pcitool_intr_info_t intr_info;
76 int rval = SUCCESS;
77
78 /* If we need user_version, and to ret same user version as passed in */
79 if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
80 DDI_SUCCESS) {
81 return (EFAULT);
82 }
83
84 intr_info.ctlr_version = 0; /* XXX how to get real version? */
85 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
86 if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
87 intr_info.num_intr = msi_state_p->msi_cnt;
88 else
89 intr_info.num_intr = pxtool_num_inos;
90
91 intr_info.drvr_version = PCITOOL_VERSION;
92 if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
93 DDI_SUCCESS) {
94 rval = EFAULT;
95 }
96
97 return (rval);
98 }
99
100
101 /*
102 * Get interrupt information for a given ino.
103 * Returns info only for inos mapped to devices.
104 *
105 * Returned info is valid only when iget.num_devs is returned > 0.
106 * If ino is not enabled or is not mapped to a device,
107 * iget.num_devs will be returned as = 0.
108 */
109 /*ARGSUSED*/
110 static int
pxtool_get_intr(dev_info_t * dip,void * arg,int mode)111 pxtool_get_intr(dev_info_t *dip, void *arg, int mode)
112 {
113 /* Array part isn't used here, but oh well... */
114 pcitool_intr_get_t partial_iget;
115 pcitool_intr_get_t *iget = &partial_iget;
116 int copyout_rval;
117 sysino_t sysino;
118 intr_valid_state_t intr_valid_state;
119 cpuid_t old_cpu_id;
120 px_t *px_p = DIP_TO_STATE(dip);
121 size_t iget_kmem_alloc_size = 0;
122 int rval = EIO;
123
124 /* Read in just the header part, no array section. */
125 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
126 DDI_SUCCESS)
127 return (EFAULT);
128
129 iget->status = PCITOOL_IO_ERROR;
130
131 if (iget->flags & PCITOOL_INTR_FLAG_GET_MSI) {
132 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state;
133 pci_msi_valid_state_t msi_state;
134 msiqid_t msiq_id;
135
136 if ((iget->msi < msi_state_p->msi_1st_msinum) ||
137 (iget->msi >= (msi_state_p->msi_1st_msinum +
138 msi_state_p->msi_cnt))) {
139 iget->status = PCITOOL_INVALID_MSI;
140 rval = EINVAL;
141 goto done_get_intr;
142 }
143
144 if ((px_lib_msi_getvalid(dip, iget->msi,
145 &msi_state) != DDI_SUCCESS) ||
146 (msi_state != PCI_MSI_VALID))
147 goto done_get_intr;
148
149 if (px_lib_msi_getmsiq(dip, iget->msi,
150 &msiq_id) != DDI_SUCCESS)
151 goto done_get_intr;
152
153 iget->ino = px_msiqid_to_devino(px_p, msiq_id);
154 } else {
155 iget->msi = (uint32_t)-1;
156 }
157
158 /* Validate argument. */
159 if (iget->ino > pxtool_num_inos) {
160 iget->status = PCITOOL_INVALID_INO;
161 rval = EINVAL;
162 goto done_get_intr;
163 }
164
165 /* Caller wants device information returned. */
166 if (iget->num_devs_ret > 0) {
167 /*
168 * Allocate room.
169 * Note if num_devs == 0 iget remains pointing to
170 * partial_iget.
171 */
172 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget->num_devs_ret);
173 iget = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP);
174
175 /* Read in whole structure to verify there's room. */
176 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
177 SUCCESS) {
178
179 /* Be consistent and just return EFAULT here. */
180 kmem_free(iget, iget_kmem_alloc_size);
181
182 return (EFAULT);
183 }
184 }
185
186 /* Convert leaf-wide intr to system-wide intr */
187 if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) !=
188 DDI_SUCCESS) {
189 iget->status = PCITOOL_IO_ERROR;
190 rval = EIO;
191 goto done_get_intr;
192 }
193
194 /* Operate only on inos which are already enabled. */
195 if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
196 DDI_SUCCESS) {
197 iget->status = PCITOOL_IO_ERROR;
198 rval = EIO;
199 goto done_get_intr;
200 }
201
202 /*
203 * Consider all valid inos: those mapped to the root complex itself
204 * as well as those mapped to devices.
205 */
206 if (intr_valid_state == INTR_VALID) {
207 /*
208 * The following looks up the px_ino and returns
209 * info of devices mapped to this ino.
210 */
211 iget->num_devs = pxtool_ib_get_ino_devs(px_p, iget->ino,
212 iget->msi, &iget->num_devs_ret, iget->dev);
213
214 if (px_ib_get_intr_target(px_p, iget->ino,
215 &old_cpu_id) != DDI_SUCCESS) {
216 iget->status = PCITOOL_IO_ERROR;
217 rval = EIO;
218 goto done_get_intr;
219 }
220
221 iget->cpu_id = old_cpu_id;
222 }
223
224 iget->status = PCITOOL_SUCCESS;
225 rval = SUCCESS;
226
227 done_get_intr:
228 iget->drvr_version = PCITOOL_VERSION;
229 copyout_rval =
230 ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(iget->num_devs_ret), mode);
231
232 if (iget_kmem_alloc_size > 0)
233 kmem_free(iget, iget_kmem_alloc_size);
234
235 if (copyout_rval != DDI_SUCCESS)
236 rval = EFAULT;
237
238 return (rval);
239 }
240
241
242 /*
243 * Associate a new CPU with a given ino.
244 *
245 * Operate only on inos which are already mapped to devices.
246 */
247 static int
pxtool_set_intr(dev_info_t * dip,void * arg,int mode)248 pxtool_set_intr(dev_info_t *dip, void *arg, int mode)
249 {
250 pcitool_intr_set_t iset;
251 cpuid_t old_cpu_id;
252 sysino_t sysino;
253 intr_valid_state_t intr_valid_state;
254 px_t *px_p = DIP_TO_STATE(dip);
255 msiqid_t msiq_id;
256 int rval = EIO;
257 int ret = DDI_SUCCESS;
258 size_t copyinout_size;
259
260 bzero(&iset, sizeof (pcitool_intr_set_t));
261
262 /* Version 1 of pcitool_intr_set_t doesn't have flags. */
263 copyinout_size = (size_t)&iset.flags - (size_t)&iset;
264
265 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
266 return (EFAULT);
267
268 switch (iset.user_version) {
269 case PCITOOL_V1:
270 break;
271
272 case PCITOOL_V2:
273 copyinout_size = sizeof (pcitool_intr_set_t);
274 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
275 return (EFAULT);
276 break;
277
278 default:
279 iset.status = PCITOOL_OUT_OF_RANGE;
280 rval = ENOTSUP;
281 goto done_set_intr;
282 }
283
284 if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) {
285 iset.status = PCITOOL_IO_ERROR;
286 rval = ENOTSUP;
287 goto done_set_intr;
288 }
289
290 iset.status = PCITOOL_IO_ERROR;
291
292 if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
293 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state;
294 pci_msi_valid_state_t msi_state;
295
296 if ((iset.msi < msi_state_p->msi_1st_msinum) ||
297 (iset.msi >= (msi_state_p->msi_1st_msinum +
298 msi_state_p->msi_cnt))) {
299 iset.status = PCITOOL_INVALID_MSI;
300 rval = EINVAL;
301 goto done_set_intr;
302 }
303
304 if ((px_lib_msi_getvalid(dip, iset.msi,
305 &msi_state) != DDI_SUCCESS) ||
306 (msi_state != PCI_MSI_VALID))
307 goto done_set_intr;
308
309 if (px_lib_msi_getmsiq(dip, iset.msi,
310 &msiq_id) != DDI_SUCCESS)
311 goto done_set_intr;
312
313 iset.ino = px_msiqid_to_devino(px_p, msiq_id);
314 } else {
315 iset.msi = (uint32_t)-1;
316 }
317
318 /* Validate input argument. */
319 if (iset.ino > pxtool_num_inos) {
320 iset.status = PCITOOL_INVALID_INO;
321 rval = EINVAL;
322 goto done_set_intr;
323 }
324
325 /* Convert leaf-wide intr to system-wide intr */
326 if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) !=
327 DDI_SUCCESS)
328 goto done_set_intr;
329
330 /* Operate only on inos which are already enabled. */
331 if ((px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
332 DDI_SUCCESS) || (intr_valid_state == INTR_NOTVALID))
333 goto done_set_intr;
334
335 /*
336 * Consider all valid inos: those mapped to the root complex itself
337 * as well as those mapped to devices.
338 */
339 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS)
340 goto done_set_intr;
341
342 if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
343 ddi_intr_handle_impl_t hdle;
344
345 bzero(&hdle, sizeof (ddi_intr_handle_impl_t));
346 if (pxtool_ib_get_msi_info(px_p, iset.ino, iset.msi,
347 &hdle) != DDI_SUCCESS) {
348 iset.status = PCITOOL_INVALID_MSI;
349 rval = EINVAL;
350 goto done_set_intr;
351 }
352
353 if ((ret = px_ib_set_msix_target(px_p, &hdle, iset.msi,
354 iset.cpu_id)) == DDI_SUCCESS) {
355 (void) px_lib_msi_getmsiq(dip, iset.msi, &msiq_id);
356 iset.ino = px_msiqid_to_devino(px_p, msiq_id);
357 iset.cpu_id = old_cpu_id;
358 iset.status = PCITOOL_SUCCESS;
359 rval = SUCCESS;
360 goto done_set_intr;
361 }
362 } else {
363 if ((ret = px_ib_set_intr_target(px_p, iset.ino,
364 iset.cpu_id)) == DDI_SUCCESS) {
365 iset.cpu_id = old_cpu_id;
366 iset.status = PCITOOL_SUCCESS;
367 rval = SUCCESS;
368 goto done_set_intr;
369 }
370 }
371
372 switch (ret) {
373 case DDI_EPENDING:
374 iset.status = PCITOOL_PENDING_INTRTIMEOUT;
375 rval = ETIME;
376 break;
377 case DDI_EINVAL:
378 iset.status = PCITOOL_INVALID_CPUID;
379 rval = EINVAL;
380 break;
381 default:
382 iset.status = PCITOOL_IO_ERROR;
383 rval = EIO;
384 break;
385 }
386
387 done_set_intr:
388 iset.drvr_version = PCITOOL_VERSION;
389 if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
390 rval = EFAULT;
391
392 return (rval);
393 }
394
395
396 /* Main function for handling interrupt CPU binding requests and queries. */
397 int
pxtool_intr(dev_info_t * dip,void * arg,int cmd,int mode)398 pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
399 {
400 int rval = SUCCESS;
401
402 switch (cmd) {
403
404 /* Get system interrupt information. */
405 case PCITOOL_SYSTEM_INTR_INFO:
406 rval = pxtool_intr_info(dip, arg, mode);
407 break;
408
409 /* Get interrupt information for a given ino. */
410 case PCITOOL_DEVICE_GET_INTR:
411 rval = pxtool_get_intr(dip, arg, mode);
412 break;
413
414 /* Associate a new CPU with a given ino. */
415 case PCITOOL_DEVICE_SET_INTR:
416 rval = pxtool_set_intr(dip, arg, mode);
417 break;
418
419 default:
420 rval = ENOTTY;
421 }
422
423 return (rval);
424 }
425
426
427 static int
pxtool_validate_barnum_bdf(pcitool_reg_t * prg)428 pxtool_validate_barnum_bdf(pcitool_reg_t *prg)
429 {
430 int rval = SUCCESS;
431
432 if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
433 prg->status = PCITOOL_OUT_OF_RANGE;
434 rval = EINVAL;
435
436 /* Validate address arguments of bus / dev / func */
437 } else if (((prg->bus_no &
438 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
439 ((prg->dev_no &
440 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
441 ((prg->func_no &
442 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
443 prg->status = PCITOOL_INVALID_ADDRESS;
444 rval = EINVAL;
445 }
446
447 return (rval);
448 }
449
450 /*
451 * px_p defines which leaf, space defines which space in that leaf, offset
452 * defines the offset within the specified space.
453 *
454 * This returns the physical address of the corresponding location.
455 */
456 static uintptr_t
pxtool_get_phys_addr(px_t * px_p,int space,uint64_t offset)457 pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset)
458 {
459 uint64_t range_base;
460 int rval;
461 pci_regspec_t dev_regspec;
462 struct regspec xlated_regspec;
463 dev_info_t *dip = px_p->px_dip;
464
465 /*
466 * Assume that requested entity is small enough to be on the same page.
467 * PCItool checks alignment so that this will be true for single
468 * accesses.
469 */
470 dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT;
471 if (space == PCI_CONFIG_SPACE) {
472 dev_regspec.pci_phys_hi +=
473 (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M));
474 dev_regspec.pci_phys_low = offset & PCI_REG_REG_M;
475 dev_regspec.pci_phys_mid = 0; /* Not used */
476 } else {
477 dev_regspec.pci_phys_mid = offset >> 32;
478 dev_regspec.pci_phys_low = offset & 0xffffffff;
479 }
480 dev_regspec.pci_size_hi = 0; /* Not used. */
481
482 /* Note: object is guaranteed to be within a page. */
483 dev_regspec.pci_size_low = 4;
484
485 rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec);
486
487 DBG(DBG_TOOLS, dip,
488 "space:0x%d, offset:0x%" PRIx64 "\n", space, offset);
489
490 if (rval != DDI_SUCCESS)
491 return ((uintptr_t)NULL);
492
493 /* Bustype here returns the high order address bits. */
494 xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p);
495
496 range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) +
497 xlated_regspec.regspec_addr;
498 DBG(DBG_TOOLS, dip,
499 "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n",
500 xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr,
501 xlated_regspec.regspec_size, range_base);
502
503 return ((uintptr_t)range_base);
504 }
505
506
507 static int
pxtool_get_bar(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * bar_p,uint32_t * space_p)508 pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p,
509 uint32_t *space_p)
510 {
511 int rval;
512 uint64_t off_in_space;
513 pcitool_reg_t cfg_prg = *prg_p; /* Make local copy. */
514 dev_info_t *dip = px_p->px_dip;
515
516 *space_p = PCI_MEM32_SPACE;
517 *bar_p = 0;
518
519 /*
520 * Translate BAR number into offset of the BAR in
521 * the device's config space.
522 */
523 cfg_prg.acc_attr =
524 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
525
526 /*
527 * Note: sun4u acc function uses phys_addr which includes offset.
528 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset.
529 */
530 cfg_prg.offset = PCI_BAR_OFFSET((*prg_p));
531 off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset;
532 cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE,
533 off_in_space);
534
535 DBG(DBG_TOOLS, dip,
536 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n",
537 off_in_space, cfg_prg.phys_addr, cfg_prg.barnum);
538
539 /*
540 * Get Bus Address Register (BAR) from config space.
541 * cfg_prg.offset is the offset into config space of the
542 * BAR desired. prg_p->status is modified on error.
543 */
544 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
545
546 if (rval != SUCCESS) {
547 prg_p->status = cfg_prg.status;
548 return (rval);
549 }
550
551 DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p);
552
553 /*
554 * BAR has bits saying this space is IO space, unless
555 * this is the ROM address register.
556 */
557 if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) &&
558 (cfg_prg.offset != PCI_CONF_ROM)) {
559 *space_p = PCI_IO_SPACE;
560 *bar_p &= PCI_BASE_IO_ADDR_M;
561
562 /*
563 * BAR has bits saying this space is 64 bit memory
564 * space, unless this is the ROM address register.
565 *
566 * The 64 bit address stored in two BAR cells is not
567 * necessarily aligned on an 8-byte boundary.
568 * Need to keep the first 4 bytes read,
569 * and do a separate read of the high 4 bytes.
570 */
571 } else if ((PCI_BASE_TYPE_ALL & *bar_p) &&
572 (cfg_prg.offset != PCI_CONF_ROM)) {
573
574 uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL);
575
576 /* Don't try to read the next 4 bytes past the end of BARs. */
577 if (cfg_prg.offset >= PCI_CONF_BASE5) {
578 prg_p->status = PCITOOL_OUT_OF_RANGE;
579 return (EIO);
580 }
581
582 /* Access device. prg_p->status is modified on error. */
583 cfg_prg.phys_addr += sizeof (uint32_t);
584 cfg_prg.offset += sizeof (uint32_t);
585
586 rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
587 if (rval != SUCCESS) {
588 prg_p->status = cfg_prg.status;
589 return (rval);
590 }
591
592 /*
593 * Honor the 64 bit BAR as such, only when the upper 32 bits
594 * store a non-zero value.
595 */
596 if (*bar_p) {
597 *space_p = PCI_MEM64_SPACE;
598 *bar_p = (*bar_p << 32) | low_bytes;
599 } else
600 *bar_p = low_bytes;
601
602 } else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */
603
604 /*
605 * ROM enabled. Filter ROM enable bit from the BAR.
606 * Treat as Mem32 henceforth.
607 */
608 if (!(*bar_p & PCI_BASE_ROM_ENABLE))
609 *bar_p ^= PCI_BASE_ROM_ENABLE;
610
611 else { /* ROM disabled. */
612 prg_p->status = PCITOOL_ROM_DISABLED;
613 return (EIO);
614 }
615 }
616
617 /* Accept a bar of 0 only for IO space. */
618 if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) {
619 prg_p->status = PCITOOL_INVALID_ADDRESS;
620 return (EINVAL);
621 }
622
623 return (SUCCESS);
624 }
625
626
627 /* Perform register accesses on PCI leaf devices. */
628 int
pxtool_dev_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)629 pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
630 {
631 pcitool_reg_t prg;
632 uint64_t bar;
633 uint32_t space;
634 uint64_t off_in_space;
635 boolean_t write_flag = B_FALSE;
636 px_t *px_p = DIP_TO_STATE(dip);
637 int rval = 0;
638
639 if (cmd == PCITOOL_DEVICE_SET_REG)
640 write_flag = B_TRUE;
641
642 DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n");
643 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
644 mode) != DDI_SUCCESS) {
645 DBG(DBG_TOOLS, dip, "Error reading arguments\n");
646 return (EFAULT);
647 }
648
649 if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) {
650 goto done_reg;
651 }
652
653 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
654 prg.bus_no, prg.dev_no, prg.func_no);
655 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
656 prg.barnum, prg.offset, prg.acc_attr);
657
658 if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS)
659 goto done_reg;
660
661 if (prg.barnum == 0) { /* Proper config space desired. */
662
663 /* Enforce offset limits. */
664 if (prg.offset >= DEV_CFG_SPACE_SIZE) {
665 DBG(DBG_TOOLS, dip,
666 "Config space offset 0x%" PRIx64 " out of range\n",
667 prg.offset);
668 prg.status = PCITOOL_OUT_OF_RANGE;
669 rval = EINVAL;
670 goto done_reg;
671 }
672
673 /*
674 * For sun4v, config space base won't be known.
675 * pxtool_get_phys_addr will return zero.
676 * Note that for sun4v, phys_addr isn't
677 * used for making config space accesses.
678 *
679 * For sun4u, assume that phys_addr will come back valid.
680 */
681 /*
682 * Accessed entity is assumed small enough to be on one page.
683 *
684 * Since config space is less than a page and is aligned to
685 * 0x1000, a device's entire config space will be on a single
686 * page. Pass the device's base config space address here,
687 * then add the offset within that space later. This works
688 * around an issue in px_xlate_reg (called by
689 * pxtool_get_phys_addr) which accepts only a 256 byte
690 * range within a device.
691 */
692 off_in_space = PX_GET_BDF(&prg);
693 prg.phys_addr =
694 pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space);
695 prg.phys_addr += prg.offset;
696
697 DBG(DBG_TOOLS, dip,
698 "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", "
699 "end:%s\n", off_in_space, prg.phys_addr,
700 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl");
701
702 /*
703 * Access device. pr.status is modified.
704 * BDF is assumed valid at this point.
705 */
706 rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag);
707 goto done_reg;
708 }
709
710 /* IO/ MEM/ MEM64 space. */
711
712 if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS)
713 goto done_reg;
714
715 switch (space) {
716 case PCI_MEM32_SPACE:
717
718 DBG(DBG_TOOLS, dip, "32 bit mem space\n");
719
720 /* Can't write to ROM */
721 if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) {
722 prg.status = PCITOOL_ROM_WRITE;
723 rval = EIO;
724 goto done_reg;
725 }
726 break;
727
728 case PCI_IO_SPACE:
729 DBG(DBG_TOOLS, dip, "IO space\n");
730 break;
731
732 case PCI_MEM64_SPACE:
733 DBG(DBG_TOOLS, dip,
734 "64 bit mem space. 64-bit bar is 0x%" PRIx64 "\n", bar);
735 break;
736
737 default:
738 DBG(DBG_TOOLS, dip, "Unknown space!\n");
739 prg.status = PCITOOL_IO_ERROR;
740 rval = EIO;
741 goto done_reg;
742 }
743
744 /*
745 * Common code for all IO/MEM range spaces.
746 *
747 * Use offset provided by caller to index into desired space.
748 * Note that prg.status is modified on error.
749 */
750 off_in_space = bar + prg.offset;
751 prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space);
752
753 DBG(DBG_TOOLS, dip,
754 "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", "
755 "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr);
756
757 rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag);
758
759 done_reg:
760 prg.drvr_version = PCITOOL_VERSION;
761 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
762 mode) != DDI_SUCCESS) {
763 DBG(DBG_TOOLS, dip, "Error returning arguments.\n");
764 rval = EFAULT;
765 }
766 return (rval);
767 }
768
769
770 int
pxtool_init(dev_info_t * dip)771 pxtool_init(dev_info_t *dip)
772 {
773 int instance = ddi_get_instance(dip);
774
775 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
776 PCI_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
777 DDI_NT_REGACC, 0) != DDI_SUCCESS) {
778 return (DDI_FAILURE);
779 }
780
781 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
782 PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
783 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
784 ddi_remove_minor_node(dip, PCI_MINOR_REG);
785 return (DDI_FAILURE);
786 }
787
788 return (DDI_SUCCESS);
789 }
790
791
792 void
pxtool_uninit(dev_info_t * dip)793 pxtool_uninit(dev_info_t *dip)
794 {
795 ddi_remove_minor_node(dip, PCI_MINOR_REG);
796 ddi_remove_minor_node(dip, PCI_MINOR_INTR);
797 }
798