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]
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/systm.h>
27 #include <sys/pci_cfgacc.h>
28 #include <sys/pci_cfgspace.h>
29 #include <sys/pci_cfgspace_impl.h>
30 #include <sys/sunddi.h>
31 #include <sys/sysmacros.h>
32 #include <sys/x86_archext.h>
34 #include <sys/cmn_err.h>
35 #include <vm/hat_i86.h>
36 #include <vm/seg_kmem.h>
37 #include <vm/kboot_mmu.h>
39 #define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4)
40 #define PCI_BDF_BUS(bdf) ((((uint16_t)bdf) & 0xff00) >> 8)
41 #define PCI_BDF_DEV(bdf) ((((uint16_t)bdf) & 0xf8) >> 3)
42 #define PCI_BDF_FUNC(bdf) (((uint16_t)bdf) & 0x7)
44 /* patchable variables */
45 volatile boolean_t pci_cfgacc_force_io
= B_FALSE
;
47 extern uintptr_t alloc_vaddr(size_t, paddr_t
);
49 void pci_cfgacc_acc(pci_cfgacc_req_t
*);
51 boolean_t
pci_cfgacc_find_workaround(uint16_t);
53 * IS_P2ALIGNED() is used to make sure offset is 'size'-aligned, so
54 * it's guaranteed that the access will not cross 4k page boundary.
55 * Thus only 1 page is allocated for all config space access, and the
56 * virtual address of that page is cached in pci_cfgacc_virt_base.
58 static caddr_t pci_cfgacc_virt_base
= NULL
;
61 pci_cfgacc_map(paddr_t phys_addr
)
64 phys_addr
= pfn_to_pa(xen_assign_pfn(mmu_btop(phys_addr
))) |
65 (phys_addr
& MMU_PAGEOFFSET
);
68 pfn_t pfn
= mmu_btop(phys_addr
);
70 * pci_cfgacc_virt_base may hold address left from early
71 * boot, which points to low mem. Realloc virtual address
72 * in kernel space since it's already late in boot now.
73 * Note: no need to unmap first, clear_boot_mappings() will
76 if (pci_cfgacc_virt_base
< (caddr_t
)kernelbase
)
77 pci_cfgacc_virt_base
= vmem_alloc(heap_arena
,
78 MMU_PAGESIZE
, VM_SLEEP
);
80 hat_devload(kas
.a_hat
, pci_cfgacc_virt_base
,
81 MMU_PAGESIZE
, pfn
, PROT_READ
| PROT_WRITE
|
82 HAT_STRICTORDER
, HAT_LOAD_LOCK
);
84 paddr_t pa_base
= P2ALIGN(phys_addr
, MMU_PAGESIZE
);
86 if (pci_cfgacc_virt_base
== NULL
)
87 pci_cfgacc_virt_base
=
88 (caddr_t
)alloc_vaddr(MMU_PAGESIZE
, MMU_PAGESIZE
);
90 kbm_map((uintptr_t)pci_cfgacc_virt_base
, pa_base
, 0, 0);
93 return (pci_cfgacc_virt_base
+ (phys_addr
& MMU_PAGEOFFSET
));
100 hat_unload(kas
.a_hat
, pci_cfgacc_virt_base
, MMU_PAGESIZE
,
105 pci_cfgacc_io(pci_cfgacc_req_t
*req
)
107 uint8_t bus
, dev
, func
;
108 uint16_t ioacc_offset
; /* 4K config access with IO ECS */
110 bus
= PCI_BDF_BUS(req
->bdf
);
111 dev
= PCI_BDF_DEV(req
->bdf
);
112 func
= PCI_BDF_FUNC(req
->bdf
);
113 ioacc_offset
= req
->offset
;
118 (*pci_putb_func
)(bus
, dev
, func
,
119 ioacc_offset
, VAL8(req
));
121 VAL8(req
) = (*pci_getb_func
)(bus
, dev
, func
,
126 (*pci_putw_func
)(bus
, dev
, func
,
127 ioacc_offset
, VAL16(req
));
129 VAL16(req
) = (*pci_getw_func
)(bus
, dev
, func
,
134 (*pci_putl_func
)(bus
, dev
, func
,
135 ioacc_offset
, VAL32(req
));
137 VAL32(req
) = (*pci_getl_func
)(bus
, dev
, func
,
142 (*pci_putl_func
)(bus
, dev
, func
,
143 ioacc_offset
, VAL64(req
) & 0xffffffff);
144 (*pci_putl_func
)(bus
, dev
, func
,
145 ioacc_offset
+ 4, VAL64(req
) >> 32);
147 VAL64(req
) = (*pci_getl_func
)(bus
, dev
, func
,
149 VAL64(req
) |= (uint64_t)(*pci_getl_func
)(bus
, dev
, func
,
150 ioacc_offset
+ 4) << 32;
157 pci_cfgacc_mmio(pci_cfgacc_req_t
*req
)
162 paddr
= (paddr_t
)req
->bdf
<< 12;
163 paddr
+= mcfg_mem_base
+ req
->offset
;
165 mutex_enter(&pcicfg_mmio_mutex
);
166 vaddr
= pci_cfgacc_map(paddr
);
171 *((uint8_t *)vaddr
) = VAL8(req
);
173 VAL8(req
) = *((uint8_t *)vaddr
);
177 *((uint16_t *)vaddr
) = VAL16(req
);
179 VAL16(req
) = *((uint16_t *)vaddr
);
183 *((uint32_t *)vaddr
) = VAL32(req
);
185 VAL32(req
) = *((uint32_t *)vaddr
);
189 *((uint64_t *)vaddr
) = VAL64(req
);
191 VAL64(req
) = *((uint64_t *)vaddr
);
195 mutex_exit(&pcicfg_mmio_mutex
);
199 pci_cfgacc_valid(pci_cfgacc_req_t
*req
, uint16_t cfgspc_size
)
203 if (IS_P2ALIGNED(req
->offset
, sz
) &&
204 (req
->offset
+ sz
- 1 < cfgspc_size
) &&
205 ((sz
& 0xf) && ISP2(sz
)))
208 cmn_err(CE_WARN
, "illegal PCI request: offset = %x, size = %d",
214 pci_cfgacc_check_io(pci_cfgacc_req_t
*req
)
218 bus
= PCI_BDF_BUS(req
->bdf
);
220 if (pci_cfgacc_force_io
|| (mcfg_mem_base
== NULL
) ||
221 (bus
< mcfg_bus_start
) || (bus
> mcfg_bus_end
) ||
222 pci_cfgacc_find_workaround(req
->bdf
))
227 pci_cfgacc_acc(pci_cfgacc_req_t
*req
)
229 extern uint_t pci_iocfg_max_offset
;
232 VAL64(req
) = (uint64_t)-1;
234 pci_cfgacc_check_io(req
);
237 if (pci_cfgacc_valid(req
, pci_iocfg_max_offset
+ 1))
240 if (pci_cfgacc_valid(req
, PCIE_CFG_SPACE_SIZE
))
241 pci_cfgacc_mmio(req
);
245 typedef struct cfgacc_bus_range
{
246 struct cfgacc_bus_range
*next
;
250 } cfgacc_bus_range_t
;
252 cfgacc_bus_range_t
*pci_cfgacc_bus_head
= NULL
;
254 #define BUS_INSERT(prev, el) \
258 #define BUS_REMOVE(prev, el) \
262 * This function is only supposed to be called in device tree setup time,
263 * thus no lock is needed.
266 pci_cfgacc_add_workaround(uint16_t bdf
, uchar_t secbus
, uchar_t subbus
)
268 cfgacc_bus_range_t
*entry
;
270 entry
= kmem_zalloc(sizeof (cfgacc_bus_range_t
), KM_SLEEP
);
272 entry
->secbus
= secbus
;
273 entry
->subbus
= subbus
;
274 BUS_INSERT(&pci_cfgacc_bus_head
, entry
);
278 pci_cfgacc_find_workaround(uint16_t bdf
)
280 cfgacc_bus_range_t
*entry
;
283 for (entry
= pci_cfgacc_bus_head
; entry
!= NULL
;
284 entry
= entry
->next
) {
285 if (bdf
== entry
->bdf
) {
286 /* found a device which is known to be broken */
290 bus
= PCI_BDF_BUS(bdf
);
291 if ((bus
!= 0) && (bus
>= entry
->secbus
) &&
292 (bus
<= entry
->subbus
)) {
294 * found a device whose parent/grandparent is
295 * known to be broken.