xref: /titanic_44/usr/src/uts/i86pc/os/pci_cfgacc_x86.c (revision 6e91bba0d6c6bdabbba62cefae583715a4a58e2a)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/systm.h>
28 #include <sys/pci_cfgacc.h>
29 #include <sys/pci_cfgspace.h>
30 #include <sys/pci_cfgspace_impl.h>
31 #include <sys/sunddi.h>
32 #include <sys/sysmacros.h>
33 #include <sys/x86_archext.h>
34 #include <sys/pci.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
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 			if ((pci_cfgacc_virt_base = vmem_alloc(heap_arena,
78 			    MMU_PAGESIZE, VM_NOSLEEP)) == NULL)
79 				return (NULL);
80 		}
81 		hat_devload(kas.a_hat, pci_cfgacc_virt_base,
82 		    MMU_PAGESIZE, pfn, PROT_READ | PROT_WRITE |
83 		    HAT_STRICTORDER, HAT_LOAD_LOCK);
84 	} else {
85 		paddr_t	pa_base = P2ALIGN(phys_addr, MMU_PAGESIZE);
86 
87 		if (pci_cfgacc_virt_base == NULL) {
88 			if ((pci_cfgacc_virt_base = (caddr_t)
89 			    alloc_vaddr(MMU_PAGESIZE, MMU_PAGESIZE)) == NULL)
90 				return (NULL);
91 		}
92 		kbm_map((uintptr_t)pci_cfgacc_virt_base, pa_base, 0, 0);
93 	}
94 
95 	return (pci_cfgacc_virt_base + (phys_addr & MMU_PAGEOFFSET));
96 }
97 
98 static void
99 pci_cfgacc_unmap()
100 {
101 	if (khat_running)
102 		hat_unload(kas.a_hat, pci_cfgacc_virt_base, MMU_PAGESIZE,
103 		    HAT_UNLOAD_UNLOCK);
104 }
105 
106 static void
107 pci_cfgacc_io(pci_cfgacc_req_t *req)
108 {
109 	uint8_t bus, dev, func, ioacc_offset;
110 
111 	if (req->offset > 0xff) {
112 		if (!req->write)
113 			VAL64(req) = (uint64_t)-1;
114 		return;
115 	}
116 
117 	bus = PCI_BDF_BUS(req->bdf);
118 	dev = PCI_BDF_DEV(req->bdf);
119 	func = PCI_BDF_FUNC(req->bdf);
120 	ioacc_offset = req->offset;
121 	switch (req->size) {
122 	case 1:
123 		if (req->write)
124 			(*pci_putb_func)(bus, dev, func,
125 			    ioacc_offset, VAL8(req));
126 		else
127 			VAL8(req) = (*pci_getb_func)(bus, dev, func,
128 			    ioacc_offset);
129 		break;
130 	case 2:
131 		if (req->write)
132 			(*pci_putw_func)(bus, dev, func,
133 			    ioacc_offset, VAL16(req));
134 		else
135 			VAL16(req) = (*pci_getw_func)(bus, dev, func,
136 			    ioacc_offset);
137 		break;
138 	case 4:
139 		if (req->write)
140 			(*pci_putl_func)(bus, dev, func,
141 			    ioacc_offset, VAL32(req));
142 		else
143 			VAL32(req) = (*pci_getl_func)(bus, dev, func,
144 			    ioacc_offset);
145 		break;
146 	default:
147 		return;
148 	}
149 }
150 
151 static int
152 pci_cfgacc_mmio(pci_cfgacc_req_t *req)
153 {
154 	caddr_t vaddr;
155 	paddr_t paddr;
156 	int rval = DDI_SUCCESS;
157 
158 	paddr = (paddr_t)req->bdf << 12;
159 	paddr += mcfg_mem_base + req->offset;
160 
161 	mutex_enter(&pcicfg_mmio_mutex);
162 	if ((vaddr = pci_cfgacc_map(paddr)) == NULL) {
163 		mutex_exit(&pcicfg_mmio_mutex);
164 		return (DDI_FAILURE);
165 	}
166 
167 	switch (req->size) {
168 	case 1:
169 		if (req->write)
170 			*((uint8_t *)vaddr) = VAL8(req);
171 		else
172 			VAL8(req) = *((uint8_t *)vaddr);
173 		break;
174 	case 2:
175 		if (req->write)
176 			*((uint16_t *)vaddr) = VAL16(req);
177 		else
178 			VAL16(req) = *((uint16_t *)vaddr);
179 		break;
180 	case 4:
181 		if (req->write)
182 			*((uint32_t *)vaddr) = VAL32(req);
183 		else
184 			VAL32(req) = *((uint32_t *)vaddr);
185 		break;
186 	case 8:
187 		if (req->write)
188 			*((uint64_t *)vaddr) = VAL64(req);
189 		else
190 			VAL64(req) = *((uint64_t *)vaddr);
191 		break;
192 	default:
193 		rval = DDI_FAILURE;
194 	}
195 	pci_cfgacc_unmap();
196 	mutex_exit(&pcicfg_mmio_mutex);
197 
198 	return (rval);
199 }
200 
201 static boolean_t
202 pci_cfgacc_valid(pci_cfgacc_req_t *req)
203 {
204 	return (IS_P2ALIGNED(req->offset, req->size) &&
205 	    (req->offset < PCIE_CFG_SPACE_SIZE));
206 }
207 
208 void
209 pci_cfgacc_acc(pci_cfgacc_req_t *req)
210 {
211 	uint8_t bus;
212 
213 	if (!req->write)
214 		VAL64(req) = 0;
215 
216 	if (!pci_cfgacc_valid(req)) {
217 		if (!req->write)
218 			VAL64(req) = (uint64_t)-1;
219 		return;
220 	}
221 
222 	bus = PCI_BDF_BUS(req->bdf);
223 	if (pci_cfgacc_force_io || (mcfg_mem_base == NULL) ||
224 	    (bus < mcfg_bus_start) || (bus > mcfg_bus_end))
225 		goto ioacc;
226 
227 	if (req->ioacc)
228 		goto ioacc;
229 
230 	/* check if workaround is needed */
231 	if (pci_cfgacc_find_workaround(req->bdf))
232 		goto ioacc;
233 
234 	if (pci_cfgacc_mmio(req) != DDI_SUCCESS)
235 		goto ioacc;
236 
237 	return;
238 
239 ioacc:
240 	pci_cfgacc_io(req);
241 	req->ioacc = B_TRUE;
242 }
243 
244 typedef	struct cfgacc_bus_range {
245 	struct cfgacc_bus_range *next;
246 	uint16_t bdf;
247 	uchar_t	secbus;
248 	uchar_t	subbus;
249 } cfgacc_bus_range_t;
250 
251 cfgacc_bus_range_t *pci_cfgacc_bus_head = NULL;
252 
253 #define	BUS_INSERT(prev, el) \
254 	el->next = *prev; \
255 	*prev = el;
256 
257 #define	BUS_REMOVE(prev, el) \
258 	*prev = el->next;
259 
260 /*
261  * This function is only supposed to be called in device tree setup time,
262  * thus no lock is needed.
263  */
264 void
265 pci_cfgacc_add_workaround(uint16_t bdf, uchar_t secbus, uchar_t subbus)
266 {
267 	cfgacc_bus_range_t	*entry;
268 
269 	entry = kmem_zalloc(sizeof (cfgacc_bus_range_t), KM_SLEEP);
270 	entry->bdf = bdf;
271 	entry->secbus = secbus;
272 	entry->subbus = subbus;
273 	BUS_INSERT(&pci_cfgacc_bus_head, entry);
274 }
275 
276 boolean_t
277 pci_cfgacc_find_workaround(uint16_t bdf)
278 {
279 	cfgacc_bus_range_t	*entry;
280 	uchar_t			bus;
281 
282 	for (entry = pci_cfgacc_bus_head; entry != NULL;
283 	    entry = entry->next) {
284 		if (bdf == entry->bdf) {
285 			/* found a device which is known to be broken */
286 			return (B_TRUE);
287 		}
288 
289 		bus = PCI_BDF_BUS(bdf);
290 		if ((bus != 0) && (bus >= entry->secbus) &&
291 		    (bus <= entry->subbus)) {
292 			/*
293 			 * found a device whose parent/grandparent is
294 			 * known to be broken.
295 			 */
296 			return (B_TRUE);
297 		}
298 	}
299 
300 	return (B_FALSE);
301 }
302