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 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
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>
33 #include <sys/pci.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>
38
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)
43
44 /* patchable variables */
45 volatile boolean_t pci_cfgacc_force_io = B_FALSE;
46
47 extern uintptr_t alloc_vaddr(size_t, paddr_t);
48
49 void pci_cfgacc_acc(pci_cfgacc_req_t *);
50
51 boolean_t pci_cfgacc_find_workaround(uint16_t);
52 /*
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.
57 */
58 static caddr_t pci_cfgacc_virt_base = NULL;
59
60 static caddr_t
pci_cfgacc_map(paddr_t phys_addr)61 pci_cfgacc_map(paddr_t phys_addr)
62 {
63 #ifdef __xpv
64 phys_addr = pfn_to_pa(xen_assign_pfn(mmu_btop(phys_addr))) |
65 (phys_addr & MMU_PAGEOFFSET);
66 #endif
67 if (khat_running) {
68 pfn_t pfn = mmu_btop(phys_addr);
69 /*
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
74 * do that for us.
75 */
76 if (pci_cfgacc_virt_base < (caddr_t)kernelbase)
77 pci_cfgacc_virt_base = vmem_alloc(heap_arena,
78 MMU_PAGESIZE, VM_SLEEP);
79
80 hat_devload(kas.a_hat, pci_cfgacc_virt_base,
81 MMU_PAGESIZE, pfn, PROT_READ | PROT_WRITE |
82 HAT_STRICTORDER, HAT_LOAD_LOCK);
83 } else {
84 paddr_t pa_base = P2ALIGN(phys_addr, MMU_PAGESIZE);
85
86 if (pci_cfgacc_virt_base == NULL)
87 pci_cfgacc_virt_base =
88 (caddr_t)alloc_vaddr(MMU_PAGESIZE, MMU_PAGESIZE);
89
90 kbm_map((uintptr_t)pci_cfgacc_virt_base, pa_base, 0, 0);
91 }
92
93 return (pci_cfgacc_virt_base + (phys_addr & MMU_PAGEOFFSET));
94 }
95
96 static void
pci_cfgacc_unmap()97 pci_cfgacc_unmap()
98 {
99 if (khat_running)
100 hat_unload(kas.a_hat, pci_cfgacc_virt_base, MMU_PAGESIZE,
101 HAT_UNLOAD_UNLOCK);
102 }
103
104 static void
pci_cfgacc_io(pci_cfgacc_req_t * req)105 pci_cfgacc_io(pci_cfgacc_req_t *req)
106 {
107 uint8_t bus, dev, func;
108 uint16_t ioacc_offset; /* 4K config access with IO ECS */
109
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;
114
115 switch (req->size) {
116 case 1:
117 if (req->write)
118 (*pci_putb_func)(bus, dev, func,
119 ioacc_offset, VAL8(req));
120 else
121 VAL8(req) = (*pci_getb_func)(bus, dev, func,
122 ioacc_offset);
123 break;
124 case 2:
125 if (req->write)
126 (*pci_putw_func)(bus, dev, func,
127 ioacc_offset, VAL16(req));
128 else
129 VAL16(req) = (*pci_getw_func)(bus, dev, func,
130 ioacc_offset);
131 break;
132 case 4:
133 if (req->write)
134 (*pci_putl_func)(bus, dev, func,
135 ioacc_offset, VAL32(req));
136 else
137 VAL32(req) = (*pci_getl_func)(bus, dev, func,
138 ioacc_offset);
139 break;
140 case 8:
141 if (req->write) {
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);
146 } else {
147 VAL64(req) = (*pci_getl_func)(bus, dev, func,
148 ioacc_offset);
149 VAL64(req) |= (uint64_t)(*pci_getl_func)(bus, dev, func,
150 ioacc_offset + 4) << 32;
151 }
152 break;
153 }
154 }
155
156 static void
pci_cfgacc_mmio(pci_cfgacc_req_t * req)157 pci_cfgacc_mmio(pci_cfgacc_req_t *req)
158 {
159 caddr_t vaddr;
160 paddr_t paddr;
161
162 paddr = (paddr_t)req->bdf << 12;
163 paddr += mcfg_mem_base + req->offset;
164
165 mutex_enter(&pcicfg_mmio_mutex);
166 vaddr = pci_cfgacc_map(paddr);
167
168 switch (req->size) {
169 case 1:
170 if (req->write)
171 *((uint8_t *)vaddr) = VAL8(req);
172 else
173 VAL8(req) = *((uint8_t *)vaddr);
174 break;
175 case 2:
176 if (req->write)
177 *((uint16_t *)vaddr) = VAL16(req);
178 else
179 VAL16(req) = *((uint16_t *)vaddr);
180 break;
181 case 4:
182 if (req->write)
183 *((uint32_t *)vaddr) = VAL32(req);
184 else
185 VAL32(req) = *((uint32_t *)vaddr);
186 break;
187 case 8:
188 if (req->write)
189 *((uint64_t *)vaddr) = VAL64(req);
190 else
191 VAL64(req) = *((uint64_t *)vaddr);
192 break;
193 }
194 pci_cfgacc_unmap();
195 mutex_exit(&pcicfg_mmio_mutex);
196 }
197
198 static boolean_t
pci_cfgacc_valid(pci_cfgacc_req_t * req,uint16_t cfgspc_size)199 pci_cfgacc_valid(pci_cfgacc_req_t *req, uint16_t cfgspc_size)
200 {
201 int sz = req->size;
202
203 if (IS_P2ALIGNED(req->offset, sz) &&
204 (req->offset + sz - 1 < cfgspc_size) &&
205 ((sz & 0xf) && ISP2(sz)))
206 return (B_TRUE);
207
208 cmn_err(CE_WARN, "illegal PCI request: offset = %x, size = %d",
209 req->offset, sz);
210 return (B_FALSE);
211 }
212
213 void
pci_cfgacc_check_io(pci_cfgacc_req_t * req)214 pci_cfgacc_check_io(pci_cfgacc_req_t *req)
215 {
216 uint8_t bus;
217
218 bus = PCI_BDF_BUS(req->bdf);
219
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))
223 req->ioacc = B_TRUE;
224 }
225
226 void
pci_cfgacc_acc(pci_cfgacc_req_t * req)227 pci_cfgacc_acc(pci_cfgacc_req_t *req)
228 {
229 extern uint_t pci_iocfg_max_offset;
230
231 if (!req->write)
232 VAL64(req) = (uint64_t)-1;
233
234 pci_cfgacc_check_io(req);
235
236 if (req->ioacc) {
237 if (pci_cfgacc_valid(req, pci_iocfg_max_offset + 1))
238 pci_cfgacc_io(req);
239 } else {
240 if (pci_cfgacc_valid(req, PCIE_CFG_SPACE_SIZE))
241 pci_cfgacc_mmio(req);
242 }
243 }
244
245 typedef struct cfgacc_bus_range {
246 struct cfgacc_bus_range *next;
247 uint16_t bdf;
248 uchar_t secbus;
249 uchar_t subbus;
250 } cfgacc_bus_range_t;
251
252 cfgacc_bus_range_t *pci_cfgacc_bus_head = NULL;
253
254 #define BUS_INSERT(prev, el) \
255 el->next = *prev; \
256 *prev = el;
257
258 #define BUS_REMOVE(prev, el) \
259 *prev = el->next;
260
261 /*
262 * This function is only supposed to be called in device tree setup time,
263 * thus no lock is needed.
264 */
265 void
pci_cfgacc_add_workaround(uint16_t bdf,uchar_t secbus,uchar_t subbus)266 pci_cfgacc_add_workaround(uint16_t bdf, uchar_t secbus, uchar_t subbus)
267 {
268 cfgacc_bus_range_t *entry;
269
270 entry = kmem_zalloc(sizeof (cfgacc_bus_range_t), KM_SLEEP);
271 entry->bdf = bdf;
272 entry->secbus = secbus;
273 entry->subbus = subbus;
274 BUS_INSERT(&pci_cfgacc_bus_head, entry);
275 }
276
277 boolean_t
pci_cfgacc_find_workaround(uint16_t bdf)278 pci_cfgacc_find_workaround(uint16_t bdf)
279 {
280 cfgacc_bus_range_t *entry;
281 uchar_t bus;
282
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 */
287 return (B_TRUE);
288 }
289
290 bus = PCI_BDF_BUS(bdf);
291 if ((bus != 0) && (bus >= entry->secbus) &&
292 (bus <= entry->subbus)) {
293 /*
294 * found a device whose parent/grandparent is
295 * known to be broken.
296 */
297 return (B_TRUE);
298 }
299 }
300
301 return (B_FALSE);
302 }
303