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.
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.
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]
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/sysmacros.h>
26 #include <sys/types.h>
27 #include <sys/mkdev.h>
29 #include <sys/sunddi.h>
30 #include <vm/seg_kmem.h>
31 #include <sys/machparam.h>
32 #include <sys/sunndi.h>
33 #include <sys/ontrap.h>
36 #include <sys/pci_cfgspace.h>
37 #include <sys/pci_tools.h>
38 #include <io/pci/pci_tools_ext.h>
41 #include <io/pci/pci_var.h>
42 #include <sys/pci_impl.h>
43 #include <sys/promif.h>
44 #include <sys/x86_archext.h>
45 #include <sys/cpuvar.h>
46 #include <sys/pci_cfgacc.h>
49 #define PCIEX_BDF_OFFSET_DELTA 4
50 #define PCIEX_REG_FUNC_SHIFT (PCI_REG_FUNC_SHIFT + PCIEX_BDF_OFFSET_DELTA)
51 #define PCIEX_REG_DEV_SHIFT (PCI_REG_DEV_SHIFT + PCIEX_BDF_OFFSET_DELTA)
52 #define PCIEX_REG_BUS_SHIFT (PCI_REG_BUS_SHIFT + PCIEX_BDF_OFFSET_DELTA)
56 extern uint64_t mcfg_mem_base
;
57 int pcitool_debug
= 0;
60 * Offsets of BARS in config space. First entry of 0 means config space.
61 * Entries here correlate to pcitool_bars_t enumerated type.
63 static uint8_t pci_bars
[] = {
74 /* Max offset allowed into config space for a particular device. */
75 static uint64_t max_cfg_size
= PCI_CONF_HDR_SIZE
;
77 static uint64_t pcitool_swap_endian(uint64_t data
, int size
);
78 static int pcitool_cfg_access(pcitool_reg_t
*prg
, boolean_t write_flag
,
80 static int pcitool_io_access(pcitool_reg_t
*prg
, boolean_t write_flag
);
81 static int pcitool_mem_access(pcitool_reg_t
*prg
, uint64_t virt_addr
,
82 boolean_t write_flag
);
83 static uint64_t pcitool_map(uint64_t phys_addr
, size_t size
, size_t *num_pages
);
84 static void pcitool_unmap(uint64_t virt_addr
, size_t num_pages
);
86 /* Extern declarations */
87 extern int (*psm_intr_ops
)(dev_info_t
*, ddi_intr_handle_impl_t
*,
88 psm_intr_op_t
, int *);
91 pcitool_init(dev_info_t
*dip
, boolean_t is_pciex
)
93 int instance
= ddi_get_instance(dip
);
95 /* Create pcitool nodes for register access and interrupt routing. */
97 if (ddi_create_minor_node(dip
, PCI_MINOR_REG
, S_IFCHR
,
98 PCI_MINOR_NUM(instance
, PCI_TOOL_REG_MINOR_NUM
),
99 DDI_NT_REGACC
, 0) != DDI_SUCCESS
) {
100 return (DDI_FAILURE
);
103 if (ddi_create_minor_node(dip
, PCI_MINOR_INTR
, S_IFCHR
,
104 PCI_MINOR_NUM(instance
, PCI_TOOL_INTR_MINOR_NUM
),
105 DDI_NT_INTRCTL
, 0) != DDI_SUCCESS
) {
106 ddi_remove_minor_node(dip
, PCI_MINOR_REG
);
107 return (DDI_FAILURE
);
111 max_cfg_size
= PCIE_CONF_HDR_SIZE
;
113 return (DDI_SUCCESS
);
117 pcitool_uninit(dev_info_t
*dip
)
119 ddi_remove_minor_node(dip
, PCI_MINOR_INTR
);
120 ddi_remove_minor_node(dip
, PCI_MINOR_REG
);
125 pcitool_set_intr(dev_info_t
*dip
, void *arg
, int mode
)
127 ddi_intr_handle_impl_t info_hdl
;
128 pcitool_intr_set_t iset
;
131 size_t copyinout_size
;
133 apic_get_type_t type_info
;
135 /* Version 1 of pcitool_intr_set_t doesn't have flags. */
136 copyinout_size
= (size_t)&iset
.flags
- (size_t)&iset
;
138 if (ddi_copyin(arg
, &iset
, copyinout_size
, mode
) != DDI_SUCCESS
)
141 switch (iset
.user_version
) {
146 copyinout_size
= sizeof (pcitool_intr_set_t
);
147 if (ddi_copyin(arg
, &iset
, copyinout_size
, mode
) != DDI_SUCCESS
)
152 iset
.status
= PCITOOL_OUT_OF_RANGE
;
157 if (iset
.flags
& PCITOOL_INTR_FLAG_SET_MSI
) {
159 iset
.status
= PCITOOL_IO_ERROR
;
163 info_hdl
.ih_private
= &type_info
;
165 if ((*psm_intr_ops
)(NULL
, &info_hdl
,
166 PSM_INTR_OP_APIC_TYPE
, NULL
) != PSM_SUCCESS
) {
168 iset
.status
= PCITOOL_IO_ERROR
;
172 if (strcmp(type_info
.avgi_type
, APIC_APIX_NAME
) == 0) {
173 if (iset
.old_cpu
> type_info
.avgi_num_cpu
) {
175 iset
.status
= PCITOOL_INVALID_CPUID
;
178 old_cpu
= iset
.old_cpu
;
181 pci_get_cpu_from_vecirq(iset
.ino
, IS_VEC
)) == -1) {
182 iset
.status
= PCITOOL_IO_ERROR
;
188 if (iset
.ino
> type_info
.avgi_num_intr
) {
190 iset
.status
= PCITOOL_INVALID_INO
;
194 iset
.status
= PCITOOL_SUCCESS
;
196 old_cpu
&= ~PSMGI_CPU_USER_BOUND
;
199 * For this locally-declared and used handle, ih_private will contain a
200 * CPU value, not an ihdl_plat_t as used for global interrupt handling.
202 if (strcmp(type_info
.avgi_type
, APIC_APIX_NAME
) == 0) {
203 info_hdl
.ih_vector
= APIX_VIRTVECTOR(old_cpu
, iset
.ino
);
205 info_hdl
.ih_vector
= iset
.ino
;
207 info_hdl
.ih_private
= (void *)(uintptr_t)iset
.cpu_id
;
208 info_hdl
.ih_flags
= PSMGI_INTRBY_VEC
;
210 prom_printf("user version:%d, flags:0x%x\n",
211 iset
.user_version
, iset
.flags
);
214 if ((iset
.user_version
>= PCITOOL_V2
) &&
215 (iset
.flags
& PCITOOL_INTR_FLAG_SET_GROUP
)) {
216 ret
= (*psm_intr_ops
)(NULL
, &info_hdl
, PSM_INTR_OP_GRP_SET_CPU
,
219 ret
= (*psm_intr_ops
)(NULL
, &info_hdl
, PSM_INTR_OP_SET_CPU
,
223 if (ret
!= PSM_SUCCESS
) {
225 case EIO
: /* Error making the change */
227 iset
.status
= PCITOOL_IO_ERROR
;
229 case ENXIO
: /* Couldn't convert vector to irq */
231 iset
.status
= PCITOOL_INVALID_INO
;
233 case EINVAL
: /* CPU out of range */
235 iset
.status
= PCITOOL_INVALID_CPUID
;
237 case ENOTSUP
: /* Requested PSM intr ops missing */
239 iset
.status
= PCITOOL_IO_ERROR
;
244 /* Return original CPU. */
245 iset
.cpu_id
= old_cpu
;
247 /* Return new vector */
248 if (strcmp(type_info
.avgi_type
, APIC_APIX_NAME
) == 0) {
249 iset
.ino
= APIX_VIRTVEC_VECTOR(info_hdl
.ih_vector
);
253 iset
.drvr_version
= PCITOOL_VERSION
;
254 if (ddi_copyout(&iset
, arg
, copyinout_size
, mode
) != DDI_SUCCESS
)
260 /* It is assumed that dip != NULL */
262 pcitool_get_intr_dev_info(dev_info_t
*dip
, pcitool_intr_dev_t
*devs
)
264 (void) strncpy(devs
->driver_name
,
265 ddi_driver_name(dip
), MAXMODCONFNAME
-2);
266 devs
->driver_name
[MAXMODCONFNAME
-1] = '\0';
267 (void) ddi_pathname(dip
, devs
->path
);
268 devs
->dev_inst
= ddi_get_instance(dip
);
272 pcitool_get_intr(dev_info_t
*dip
, void *arg
, int mode
)
274 /* Array part isn't used here, but oh well... */
275 pcitool_intr_get_t partial_iget
;
276 pcitool_intr_get_t
*iget
= &partial_iget
;
277 size_t iget_kmem_alloc_size
= 0;
278 uint8_t num_devs_ret
;
284 ddi_intr_handle_impl_t info_hdl
;
285 apic_get_intr_t intr_info
;
286 apic_get_type_t type_info
;
288 /* Read in just the header part, no array section. */
289 if (ddi_copyin(arg
, &partial_iget
, PCITOOL_IGET_SIZE(0), mode
) !=
293 if (partial_iget
.flags
& PCITOOL_INTR_FLAG_GET_MSI
) {
294 partial_iget
.status
= PCITOOL_IO_ERROR
;
295 partial_iget
.num_devs_ret
= 0;
300 info_hdl
.ih_private
= &type_info
;
302 if ((*psm_intr_ops
)(NULL
, &info_hdl
,
303 PSM_INTR_OP_APIC_TYPE
, NULL
) != PSM_SUCCESS
) {
304 iget
->status
= PCITOOL_IO_ERROR
;
305 iget
->num_devs_ret
= 0;
310 if (strcmp(type_info
.avgi_type
, APIC_APIX_NAME
) == 0) {
311 if (partial_iget
.cpu_id
> type_info
.avgi_num_cpu
) {
312 partial_iget
.status
= PCITOOL_INVALID_CPUID
;
313 partial_iget
.num_devs_ret
= 0;
319 /* Validate argument. */
320 if ((partial_iget
.ino
& APIX_VIRTVEC_VECMASK
) >
321 type_info
.avgi_num_intr
) {
322 partial_iget
.status
= PCITOOL_INVALID_INO
;
323 partial_iget
.num_devs_ret
= 0;
328 num_devs_ret
= partial_iget
.num_devs_ret
;
329 intr_info
.avgi_dip_list
= NULL
;
330 intr_info
.avgi_req_flags
=
331 PSMGI_REQ_CPUID
| PSMGI_REQ_NUM_DEVS
| PSMGI_INTRBY_VEC
;
333 * For this locally-declared and used handle, ih_private will contain a
334 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for
335 * global interrupt handling.
337 info_hdl
.ih_private
= &intr_info
;
339 if (strcmp(type_info
.avgi_type
, APIC_APIX_NAME
) == 0) {
341 APIX_VIRTVECTOR(partial_iget
.cpu_id
, partial_iget
.ino
);
343 info_hdl
.ih_vector
= partial_iget
.ino
;
346 /* Caller wants device information returned. */
347 if (num_devs_ret
> 0) {
349 intr_info
.avgi_req_flags
|= PSMGI_REQ_GET_DEVS
;
353 * If num_devs_ret == 0 iget remains pointing to partial_iget.
355 iget_kmem_alloc_size
= PCITOOL_IGET_SIZE(num_devs_ret
);
356 iget
= kmem_alloc(iget_kmem_alloc_size
, KM_SLEEP
);
358 /* Read in whole structure to verify there's room. */
359 if (ddi_copyin(arg
, iget
, iget_kmem_alloc_size
, mode
) !=
362 /* Be consistent and just return EFAULT here. */
363 kmem_free(iget
, iget_kmem_alloc_size
);
369 bzero(iget
, PCITOOL_IGET_SIZE(num_devs_ret
));
370 iget
->ino
= info_hdl
.ih_vector
;
373 * Lock device tree branch from the pci root nexus on down if info will
374 * be extracted from dips returned from the tree.
376 if (intr_info
.avgi_req_flags
& PSMGI_REQ_GET_DEVS
) {
377 ndi_devi_enter(dip
, &circ
);
380 /* Call psm_intr_ops(PSM_INTR_OP_GET_INTR) to get information. */
381 if ((rval
= (*psm_intr_ops
)(NULL
, &info_hdl
,
382 PSM_INTR_OP_GET_INTR
, NULL
)) != PSM_SUCCESS
) {
383 iget
->status
= PCITOOL_IO_ERROR
;
384 iget
->num_devs_ret
= 0;
390 * Fill in the pcitool_intr_get_t to be returned,
391 * with the CPU, num_devs_ret and num_devs.
393 if (intr_info
.avgi_cpu_id
== IRQ_UNBOUND
||
394 intr_info
.avgi_cpu_id
== IRQ_UNINIT
)
397 iget
->cpu_id
= intr_info
.avgi_cpu_id
& ~PSMGI_CPU_USER_BOUND
;
399 /* Number of devices returned by apic. */
400 iget
->num_devs
= intr_info
.avgi_num_devs
;
402 /* Device info was returned. */
403 if (intr_info
.avgi_req_flags
& PSMGI_REQ_GET_DEVS
) {
406 * num devs returned is num devs ret by apic,
409 iget
->num_devs_ret
= min(num_devs_ret
, intr_info
.avgi_num_devs
);
412 * Loop thru list of dips and extract driver, name and instance.
413 * Fill in the pcitool_intr_dev_t's with this info.
415 for (i
= 0; i
< iget
->num_devs_ret
; i
++)
416 pcitool_get_intr_dev_info(intr_info
.avgi_dip_list
[i
],
419 /* Free kmem_alloc'ed memory of the apic_get_intr_t */
420 kmem_free(intr_info
.avgi_dip_list
,
421 intr_info
.avgi_num_devs
* sizeof (dev_info_t
*));
426 if (intr_info
.avgi_req_flags
& PSMGI_REQ_GET_DEVS
) {
427 ndi_devi_exit(dip
, circ
);
430 iget
->drvr_version
= PCITOOL_VERSION
;
431 copyout_rval
= ddi_copyout(iget
, arg
,
432 PCITOOL_IGET_SIZE(num_devs_ret
), mode
);
434 if (iget_kmem_alloc_size
> 0)
435 kmem_free(iget
, iget_kmem_alloc_size
);
437 if (copyout_rval
!= DDI_SUCCESS
)
445 pcitool_intr_info(dev_info_t
*dip
, void *arg
, int mode
)
447 pcitool_intr_info_t intr_info
;
448 ddi_intr_handle_impl_t info_hdl
;
450 apic_get_type_t type_info
;
452 /* If we need user_version, and to ret same user version as passed in */
453 if (ddi_copyin(arg
, &intr_info
, sizeof (pcitool_intr_info_t
), mode
) !=
456 prom_printf("Error reading arguments\n");
460 if (intr_info
.flags
& PCITOOL_INTR_FLAG_GET_MSI
)
463 info_hdl
.ih_private
= &type_info
;
465 /* For UPPC systems, psm_intr_ops has no entry for APIC_TYPE. */
466 if ((rval
= (*psm_intr_ops
)(NULL
, &info_hdl
,
467 PSM_INTR_OP_APIC_TYPE
, NULL
)) != PSM_SUCCESS
) {
468 intr_info
.ctlr_type
= PCITOOL_CTLR_TYPE_UPPC
;
469 intr_info
.ctlr_version
= 0;
470 intr_info
.num_intr
= APIC_MAX_VECTOR
;
472 intr_info
.ctlr_version
= (uint32_t)info_hdl
.ih_ver
;
473 intr_info
.num_cpu
= type_info
.avgi_num_cpu
;
474 if (strcmp(type_info
.avgi_type
,
475 APIC_PCPLUSMP_NAME
) == 0) {
476 intr_info
.ctlr_type
= PCITOOL_CTLR_TYPE_PCPLUSMP
;
477 intr_info
.num_intr
= type_info
.avgi_num_intr
;
478 } else if (strcmp(type_info
.avgi_type
,
479 APIC_APIX_NAME
) == 0) {
480 intr_info
.ctlr_type
= PCITOOL_CTLR_TYPE_APIX
;
481 intr_info
.num_intr
= type_info
.avgi_num_intr
;
483 intr_info
.ctlr_type
= PCITOOL_CTLR_TYPE_UNKNOWN
;
484 intr_info
.num_intr
= APIC_MAX_VECTOR
;
488 intr_info
.drvr_version
= PCITOOL_VERSION
;
489 if (ddi_copyout(&intr_info
, arg
, sizeof (pcitool_intr_info_t
), mode
) !=
492 prom_printf("Error returning arguments.\n");
502 * Main function for handling interrupt CPU binding requests and queries.
503 * Need to implement later
506 pcitool_intr_admn(dev_info_t
*dip
, void *arg
, int cmd
, int mode
)
512 /* Associate a new CPU with a given vector */
513 case PCITOOL_DEVICE_SET_INTR
:
514 rval
= pcitool_set_intr(dip
, arg
, mode
);
517 case PCITOOL_DEVICE_GET_INTR
:
518 rval
= pcitool_get_intr(dip
, arg
, mode
);
521 case PCITOOL_SYSTEM_INTR_INFO
:
522 rval
= pcitool_intr_info(dip
, arg
, mode
);
533 * Perform register accesses on the nexus device itself.
534 * No explicit PCI nexus device for X86, so not applicable.
539 pcitool_bus_reg_ops(dev_info_t
*dip
, void *arg
, int cmd
, int mode
)
544 /* Swap endianness. */
546 pcitool_swap_endian(uint64_t data
, int size
)
553 data_split_t orig_data
;
554 data_split_t returned_data
;
557 orig_data
.data64
= data
;
558 returned_data
.data64
= 0;
560 for (i
= 0; i
< size
; i
++) {
561 returned_data
.data8
[i
] = orig_data
.data8
[size
- 1 - i
];
564 return (returned_data
.data64
);
568 * A note about ontrap handling:
570 * X86 systems on which this module was tested return FFs instead of bus errors
571 * when accessing devices with invalid addresses. Ontrap handling, which
572 * gracefully handles kernel bus errors, is installed anyway for I/O and mem
573 * space accessing (not for pci config space), in case future X86 platforms
577 /* Access device. prg is modified. */
579 pcitool_cfg_access(pcitool_reg_t
*prg
, boolean_t write_flag
,
582 int size
= PCITOOL_ACC_ATTR_SIZE(prg
->acc_attr
);
583 boolean_t big_endian
= PCITOOL_ACC_IS_BIG_ENDIAN(prg
->acc_attr
);
586 pci_cfgacc_req_t req
;
589 if ((size
<= 0) || (size
> 8) || !ISP2(size
)) {
590 prg
->status
= PCITOOL_INVALID_SIZE
;
595 * NOTE: there is no way to verify whether or not the address is
596 * valid other than that it is within the maximum offset. The
597 * put functions return void and the get functions return -1 on error.
604 if (prg
->offset
+ size
- 1 > max_offset
) {
605 prg
->status
= PCITOOL_INVALID_ADDRESS
;
609 prg
->status
= PCITOOL_SUCCESS
;
612 req
.bdf
= PCI_GETBDF(prg
->bus_no
, prg
->dev_no
, prg
->func_no
);
613 req
.offset
= prg
->offset
;
615 req
.write
= write_flag
;
616 req
.ioacc
= io_access
;
619 local_data
= pcitool_swap_endian(prg
->data
, size
);
621 local_data
= prg
->data
;
623 VAL64(&req
) = local_data
;
624 pci_cfgacc_acc(&req
);
626 pci_cfgacc_acc(&req
);
629 local_data
= VAL8(&req
);
632 local_data
= VAL16(&req
);
635 local_data
= VAL32(&req
);
638 local_data
= VAL64(&req
);
643 pcitool_swap_endian(local_data
, size
);
645 prg
->data
= local_data
;
649 * Check if legacy IO config access is used, in which case
650 * only first 256 bytes are valid.
652 if (req
.ioacc
&& (prg
->offset
+ size
- 1 > 0xFF)) {
653 prg
->status
= PCITOOL_INVALID_ADDRESS
;
657 /* Set phys_addr only if MMIO is used */
659 if (!req
.ioacc
&& mcfg_mem_base
!= 0) {
660 prg
->phys_addr
= mcfg_mem_base
+ prg
->offset
+
661 ((prg
->bus_no
<< PCIEX_REG_BUS_SHIFT
) |
662 (prg
->dev_no
<< PCIEX_REG_DEV_SHIFT
) |
663 (prg
->func_no
<< PCIEX_REG_FUNC_SHIFT
));
670 pcitool_io_access(pcitool_reg_t
*prg
, boolean_t write_flag
)
672 int port
= (int)prg
->phys_addr
;
673 size_t size
= PCITOOL_ACC_ATTR_SIZE(prg
->acc_attr
);
674 boolean_t big_endian
= PCITOOL_ACC_IS_BIG_ENDIAN(prg
->acc_attr
);
681 * on_trap works like setjmp.
683 * A non-zero return here means on_trap has returned from an error.
685 * A zero return here means that on_trap has just returned from setup.
687 if (on_trap(&otd
, OT_DATA_ACCESS
)) {
691 "pcitool_io_access: on_trap caught an error...\n");
692 prg
->status
= PCITOOL_INVALID_ADDRESS
;
699 local_data
= pcitool_swap_endian(prg
->data
, size
);
701 local_data
= prg
->data
;
705 prom_printf("Writing %ld byte(s) to port 0x%x\n",
710 outb(port
, (uint8_t)local_data
);
713 outw(port
, (uint16_t)local_data
);
716 outl(port
, (uint32_t)local_data
);
720 prg
->status
= PCITOOL_INVALID_SIZE
;
725 prom_printf("Reading %ld byte(s) from port 0x%x\n",
730 local_data
= inb(port
);
733 local_data
= inw(port
);
736 local_data
= inl(port
);
740 prg
->status
= PCITOOL_INVALID_SIZE
;
744 if (rval
== SUCCESS
) {
747 pcitool_swap_endian(local_data
, size
);
749 prg
->data
= local_data
;
759 pcitool_mem_access(pcitool_reg_t
*prg
, uint64_t virt_addr
, boolean_t write_flag
)
761 size_t size
= PCITOOL_ACC_ATTR_SIZE(prg
->acc_attr
);
762 boolean_t big_endian
= PCITOOL_ACC_IS_BIG_ENDIAN(prg
->acc_attr
);
763 int rval
= DDI_SUCCESS
;
768 * on_trap works like setjmp.
770 * A non-zero return here means on_trap has returned from an error.
772 * A zero return here means that on_trap has just returned from setup.
774 if (on_trap(&otd
, OT_DATA_ACCESS
)) {
778 "pcitool_mem_access: on_trap caught an error...\n");
779 prg
->status
= PCITOOL_INVALID_ADDRESS
;
786 local_data
= pcitool_swap_endian(prg
->data
, size
);
788 local_data
= prg
->data
;
793 *((uint8_t *)(uintptr_t)virt_addr
) = local_data
;
796 *((uint16_t *)(uintptr_t)virt_addr
) = local_data
;
799 *((uint32_t *)(uintptr_t)virt_addr
) = local_data
;
802 *((uint64_t *)(uintptr_t)virt_addr
) = local_data
;
806 prg
->status
= PCITOOL_INVALID_SIZE
;
812 local_data
= *((uint8_t *)(uintptr_t)virt_addr
);
815 local_data
= *((uint16_t *)(uintptr_t)virt_addr
);
818 local_data
= *((uint32_t *)(uintptr_t)virt_addr
);
821 local_data
= *((uint64_t *)(uintptr_t)virt_addr
);
825 prg
->status
= PCITOOL_INVALID_SIZE
;
829 if (rval
== SUCCESS
) {
832 pcitool_swap_endian(local_data
, size
);
834 prg
->data
= local_data
;
844 * Map up to 2 pages which contain the address we want to access.
846 * Mapping should span no more than 8 bytes. With X86 it is possible for an
847 * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary.
848 * We'll never have to map more than two pages.
852 pcitool_map(uint64_t phys_addr
, size_t size
, size_t *num_pages
)
855 uint64_t page_base
= phys_addr
& ~MMU_PAGEOFFSET
;
856 uint64_t offset
= phys_addr
& MMU_PAGEOFFSET
;
858 uint64_t returned_addr
;
862 prom_printf("pcitool_map: Called with PA:0x%p\n",
863 (void *)(uintptr_t)phys_addr
);
867 /* Desired mapping would span more than two pages. */
868 if ((offset
+ size
) > (MMU_PAGESIZE
* 2)) {
870 prom_printf("boundary violation: "
871 "offset:0x%" PRIx64
", size:%ld, pagesize:0x%lx\n",
872 offset
, (uintptr_t)size
, (uintptr_t)MMU_PAGESIZE
);
875 } else if ((offset
+ size
) > MMU_PAGESIZE
) {
879 /* Get page(s) of virtual space. */
880 virt_base
= vmem_alloc(heap_arena
, ptob(*num_pages
), VM_NOSLEEP
);
881 if (virt_base
== NULL
) {
883 prom_printf("Couldn't get virtual base address.\n");
888 prom_printf("Got base virtual address:0x%p\n", virt_base
);
890 pfn
= btop(page_base
);
892 /* Now map the allocated virtual space to the physical address. */
893 hat_devload(kas
.a_hat
, virt_base
, mmu_ptob(*num_pages
), pfn
,
894 PROT_READ
| PROT_WRITE
| HAT_STRICTORDER
,
897 returned_addr
= ((uintptr_t)(virt_base
)) + offset
;
900 prom_printf("pcitool_map: returning VA:0x%p\n",
901 (void *)(uintptr_t)returned_addr
);
903 return (returned_addr
);
906 /* Unmap the mapped page(s). */
908 pcitool_unmap(uint64_t virt_addr
, size_t num_pages
)
910 void *base_virt_addr
= (void *)(uintptr_t)(virt_addr
& ~MMU_PAGEOFFSET
);
912 hat_unload(kas
.a_hat
, base_virt_addr
, ptob(num_pages
),
914 vmem_free(heap_arena
, base_virt_addr
, ptob(num_pages
));
918 /* Perform register accesses on PCI leaf devices. */
921 pcitool_dev_reg_ops(dev_info_t
*dip
, void *arg
, int cmd
, int mode
)
923 boolean_t write_flag
= B_FALSE
;
924 boolean_t io_access
= B_TRUE
;
931 size_t num_virt_pages
;
934 case (PCITOOL_DEVICE_SET_REG
):
938 case (PCITOOL_DEVICE_GET_REG
):
940 prom_printf("pci_dev_reg_ops set/get reg\n");
941 if (ddi_copyin(arg
, &prg
, sizeof (pcitool_reg_t
), mode
) !=
944 prom_printf("Error reading arguments\n");
948 if (prg
.barnum
>= (sizeof (pci_bars
) / sizeof (pci_bars
[0]))) {
949 prg
.status
= PCITOOL_OUT_OF_RANGE
;
955 prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n",
956 prg
.bus_no
, prg
.dev_no
, prg
.func_no
);
957 /* Validate address arguments of bus / dev / func */
959 (PCI_REG_BUS_M
>> PCI_REG_BUS_SHIFT
)) !=
962 (PCI_REG_DEV_M
>> PCI_REG_DEV_SHIFT
)) !=
965 (PCI_REG_FUNC_M
>> PCI_REG_FUNC_SHIFT
)) !=
967 prg
.status
= PCITOOL_INVALID_ADDRESS
;
972 size
= PCITOOL_ACC_ATTR_SIZE(prg
.acc_attr
);
974 /* Proper config space desired. */
975 if (prg
.barnum
== 0) {
979 "config access: offset:0x%" PRIx64
", "
980 "phys_addr:0x%" PRIx64
"\n",
981 prg
.offset
, prg
.phys_addr
);
983 if (prg
.offset
>= max_cfg_size
) {
984 prg
.status
= PCITOOL_OUT_OF_RANGE
;
988 if (max_cfg_size
== PCIE_CONF_HDR_SIZE
)
991 rval
= pcitool_cfg_access(&prg
, write_flag
, io_access
);
994 "config access: data:0x%" PRIx64
"\n",
997 /* IO/ MEM/ MEM64 space. */
1001 bcopy(&prg
, &prg2
, sizeof (pcitool_reg_t
));
1004 * Translate BAR number into offset of the BAR in
1005 * the device's config space.
1007 prg2
.offset
= pci_bars
[prg2
.barnum
];
1009 PCITOOL_ACC_ATTR_SIZE_4
| PCITOOL_ACC_ATTR_ENDN_LTL
;
1013 "barnum:%d, bar_offset:0x%" PRIx64
"\n",
1014 prg2
.barnum
, prg2
.offset
);
1016 * Get Bus Address Register (BAR) from config space.
1017 * prg2.offset is the offset into config space of the
1018 * BAR desired. prg.status is modified on error.
1020 rval
= pcitool_cfg_access(&prg2
, B_FALSE
, B_TRUE
);
1021 if (rval
!= SUCCESS
) {
1023 prom_printf("BAR access failed\n");
1024 prg
.status
= prg2
.status
;
1028 * Reference proper PCI space based on the BAR.
1029 * If 64 bit MEM space, need to load other half of the
1034 prom_printf("bar returned is 0x%" PRIx64
"\n",
1038 prom_printf("BAR data == 0\n");
1040 prg
.status
= PCITOOL_INVALID_ADDRESS
;
1043 if (prg2
.data
== 0xffffffff) {
1045 prom_printf("BAR data == -1\n");
1047 prg
.status
= PCITOOL_INVALID_ADDRESS
;
1052 * BAR has bits saying this space is IO space, unless
1053 * this is the ROM address register.
1055 if (((PCI_BASE_SPACE_M
& prg2
.data
) ==
1056 PCI_BASE_SPACE_IO
) &&
1057 (prg2
.offset
!= PCI_CONF_ROM
)) {
1059 prom_printf("IO space\n");
1061 prg2
.data
&= PCI_BASE_IO_ADDR_M
;
1062 prg
.phys_addr
= prg2
.data
+ prg
.offset
;
1064 rval
= pcitool_io_access(&prg
, write_flag
);
1065 if ((rval
!= SUCCESS
) && (pcitool_debug
))
1066 prom_printf("IO access failed\n");
1072 * BAR has bits saying this space is 64 bit memory
1073 * space, unless this is the ROM address register.
1075 * The 64 bit address stored in two BAR cells is not
1076 * necessarily aligned on an 8-byte boundary.
1077 * Need to keep the first 4 bytes read,
1078 * and do a separate read of the high 4 bytes.
1081 } else if ((PCI_BASE_TYPE_ALL
& prg2
.data
) &&
1082 (prg2
.offset
!= PCI_CONF_ROM
)) {
1084 uint32_t low_bytes
=
1085 (uint32_t)(prg2
.data
& ~PCI_BASE_TYPE_ALL
);
1088 * Don't try to read the next 4 bytes
1089 * past the end of BARs.
1091 if (prg2
.offset
>= PCI_CONF_BASE5
) {
1092 prg
.status
= PCITOOL_OUT_OF_RANGE
;
1099 * prg2.status is modified on error.
1102 rval
= pcitool_cfg_access(&prg2
,
1104 if (rval
!= SUCCESS
) {
1105 prg
.status
= prg2
.status
;
1109 if (prg2
.data
== 0xffffffff) {
1110 prg
.status
= PCITOOL_INVALID_ADDRESS
;
1111 prg
.status
= EFAULT
;
1115 prg2
.data
= (prg2
.data
<< 32) + low_bytes
;
1118 "64 bit mem space. "
1119 "64-bit bar is 0x%" PRIx64
"\n",
1122 /* Mem32 space, including ROM */
1125 if (prg2
.offset
== PCI_CONF_ROM
) {
1130 /* Can't write to ROM */
1132 prg
.status
= PCITOOL_ROM_WRITE
;
1136 /* ROM disabled for reading */
1137 } else if (!(prg2
.data
& 0x00000001)) {
1139 PCITOOL_ROM_DISABLED
;
1146 prom_printf("32 bit mem space\n");
1149 /* Common code for all IO/MEM range spaces. */
1151 base_addr
= prg2
.data
;
1154 "addr portion of bar is 0x%" PRIx64
", "
1155 "base=0x%" PRIx64
", "
1156 "offset:0x%" PRIx64
"\n",
1157 prg2
.data
, base_addr
, prg
.offset
);
1159 * Use offset provided by caller to index into
1160 * desired space, then access.
1161 * Note that prg.status is modified on error.
1163 prg
.phys_addr
= base_addr
+ prg
.offset
;
1165 virt_addr
= pcitool_map(prg
.phys_addr
, size
,
1167 if (virt_addr
== (uintptr_t)NULL
) {
1168 prg
.status
= PCITOOL_IO_ERROR
;
1173 rval
= pcitool_mem_access(&prg
, virt_addr
, write_flag
);
1174 pcitool_unmap(virt_addr
, num_virt_pages
);
1177 prg
.drvr_version
= PCITOOL_VERSION
;
1178 if (ddi_copyout(&prg
, arg
, sizeof (pcitool_reg_t
), mode
) !=
1181 prom_printf("Error returning arguments.\n");