xref: /openbsd/sys/arch/i386/pci/pci_addr_fixup.c (revision f4e70637)
1 /*	$OpenBSD: pci_addr_fixup.c,v 1.26 2023/01/30 10:49:05 jsg Exp $	*/
2 /*	$NetBSD: pci_addr_fixup.c,v 1.7 2000/08/03 20:10:45 nathanw Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 UCHIYAMA Yasushi.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 #include <sys/extent.h>
34 
35 #include <uvm/uvm_extern.h>
36 
37 #include <machine/bus.h>
38 
39 #include <dev/pci/pcireg.h>
40 #include <dev/pci/pcivar.h>
41 
42 #include <i386/pci/pcibiosvar.h>
43 
44 typedef int (*pciaddr_resource_manage_func_t)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t, int,
45 	struct extent *, int, bus_addr_t *, bus_size_t);
46 void	pciaddr_resource_manage(struct pcibios_softc *,
47     pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t);
48 void	pciaddr_resource_reserve(struct pcibios_softc *,
49     pci_chipset_tag_t, pcitag_t);
50 void	pciaddr_resource_reserve_disabled(struct pcibios_softc *,
51     pci_chipset_tag_t, pcitag_t);
52 int	pciaddr_do_resource_reserve(struct pcibios_softc *,
53     pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
54     bus_addr_t *, bus_size_t);
55 int	pciaddr_do_resource_reserve_disabled(struct pcibios_softc *,
56     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, u_long *,
57     bus_size_t);
58 void	pciaddr_resource_allocate(struct pcibios_softc *,
59     pci_chipset_tag_t, pcitag_t);
60 int	pciaddr_do_resource_allocate(struct pcibios_softc *,
61     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
62     bus_size_t);
63 bus_addr_t pciaddr_ioaddr(u_int32_t);
64 void	pciaddr_print_devid(pci_chipset_tag_t, pcitag_t);
65 
66 int	pciaddr_device_is_agp(pci_chipset_tag_t, pcitag_t);
67 
68 #define PCIADDR_MEM_START	0x0
69 #define PCIADDR_MEM_END		0xffffffff
70 #define PCIADDR_PORT_START	0x0
71 #define PCIADDR_PORT_END	0xffff
72 
73 /* for ISA devices */
74 #define PCIADDR_ISAPORT_RESERVE	0x5800 /* empirical value */
75 #define PCIADDR_ISAMEM_RESERVE	(16 * 1024 * 1024)
76 
77 void
pci_addr_fixup(struct pcibios_softc * sc,pci_chipset_tag_t pc,int maxbus)78 pci_addr_fixup(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus)
79 {
80 	extern paddr_t avail_end;
81 	const char *verbose_header =
82 		"[%s]-----------------------\n"
83 		"  device vendor product\n"
84 		"  register space address    size\n"
85 		"--------------------------------------------\n";
86 	const char *verbose_footer =
87 		"--------------------------[%3d devices bogus]\n";
88 
89 	const struct {
90 		bus_addr_t start;
91 		bus_size_t size;
92 		char *name;
93 	} system_reserve [] = {
94 		{ 0xfec00000, 0x100000, "I/O APIC" },
95 		{ 0xfee00000, 0x100000, "Local APIC" },
96 		{ 0xfffe0000, 0x20000, "BIOS PROM" },
97 		{ 0, 0, 0 }, /* terminator */
98 	}, *srp;
99 	paddr_t start;
100 	int error;
101 
102 	sc->extent_mem = extent_create("PCI I/O memory space",
103 	    PCIADDR_MEM_START, PCIADDR_MEM_END, M_DEVBUF, 0, 0, EX_NOWAIT);
104 	KASSERT(sc->extent_mem);
105 	sc->extent_port = extent_create("PCI I/O port space",
106 	    PCIADDR_PORT_START, PCIADDR_PORT_END, M_DEVBUF, 0, 0, EX_NOWAIT);
107 	KASSERT(sc->extent_port);
108 
109 	/*
110 	 * 1. check & reserve system BIOS setting.
111 	 */
112 	PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
113 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
114 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve_disabled);
115 	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
116 
117 	/*
118 	 * 2. reserve non-PCI area.
119 	 */
120 	for (srp = system_reserve; srp->size; srp++) {
121 		error = extent_alloc_region(sc->extent_mem, srp->start,
122 		    srp->size, EX_NOWAIT| EX_MALLOCOK);
123 		if (error != 0)
124 			printf("WARNING: can't reserve area for %s.\n",
125 			       srp->name);
126 	}
127 
128 	/*
129 	 * 3. determine allocation space
130 	 */
131 	start = round_page(avail_end + 1);
132 	if (start < PCIADDR_ISAMEM_RESERVE)
133 		start = PCIADDR_ISAMEM_RESERVE;
134 	sc->mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
135 	sc->port_alloc_start = PCIADDR_ISAPORT_RESERVE;
136 	PCIBIOS_PRINTV((" Physical memory end: 0x%08lx\n PCI memory mapped I/O "
137 	    "space start: 0x%08lx\n", avail_end, sc->mem_alloc_start));
138 
139 	/*
140 	 * 4. do fixup
141 	 */
142 	PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
143 	sc->nbogus = 0;
144 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
145 	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
146 
147 }
148 
149 void
pciaddr_resource_reserve(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag)150 pciaddr_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
151     pcitag_t tag)
152 {
153 	if (pcibios_flags & PCIBIOS_VERBOSE)
154 		pciaddr_print_devid(pc, tag);
155 	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
156 }
157 
158 void
pciaddr_resource_reserve_disabled(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag)159 pciaddr_resource_reserve_disabled(struct pcibios_softc *sc,
160     pci_chipset_tag_t pc, pcitag_t tag)
161 {
162 	if (pcibios_flags & PCIBIOS_VERBOSE)
163 		pciaddr_print_devid(pc, tag);
164 	pciaddr_resource_manage(sc, pc, tag,
165 	    pciaddr_do_resource_reserve_disabled);
166 }
167 
168 void
pciaddr_resource_allocate(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag)169 pciaddr_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
170     pcitag_t tag)
171 {
172 	if (pcibios_flags & PCIBIOS_VERBOSE)
173 		pciaddr_print_devid(pc, tag);
174 	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
175 }
176 
177 void
pciaddr_resource_manage(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag,pciaddr_resource_manage_func_t func)178 pciaddr_resource_manage(struct pcibios_softc *sc, pci_chipset_tag_t pc,
179     pcitag_t tag, pciaddr_resource_manage_func_t func)
180 {
181 	struct extent *ex;
182 	pcireg_t val, mask;
183 	bus_addr_t addr;
184 	bus_size_t size;
185 	int error, mapreg, type, reg_start, reg_end, width;
186 
187 	val = pci_conf_read(pc, tag, PCI_BHLC_REG);
188 	switch (PCI_HDRTYPE_TYPE(val)) {
189 	default:
190 		printf("WARNING: unknown PCI device header 0x%x.\n",
191 		    PCI_HDRTYPE_TYPE(val));
192 		sc->nbogus++;
193 		return;
194 	case 0:
195 		reg_start = PCI_MAPREG_START;
196 		reg_end   = PCI_MAPREG_END;
197 		break;
198 	case 1: /* PCI-PCI bridge */
199 		reg_start = PCI_MAPREG_START;
200 		reg_end   = PCI_MAPREG_PPB_END;
201 		break;
202 	case 2: /* PCI-CardBus bridge */
203 		reg_start = PCI_MAPREG_START;
204 		reg_end   = PCI_MAPREG_PCB_END;
205 		break;
206 	}
207 	error = 0;
208 
209 	for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
210 		/* inquire PCI device bus space requirement */
211 		val = pci_conf_read(pc, tag, mapreg);
212 		pci_conf_write(pc, tag, mapreg, ~0);
213 
214 		mask = pci_conf_read(pc, tag, mapreg);
215 		pci_conf_write(pc, tag, mapreg, val);
216 
217 		type = PCI_MAPREG_TYPE(val);
218 		width = 4;
219 		if (type == PCI_MAPREG_TYPE_MEM) {
220 			if (PCI_MAPREG_MEM_TYPE(val) ==
221 			    PCI_MAPREG_MEM_TYPE_64BIT) {
222 				/* XXX We could examine the upper 32 bits
223 				 * XXX of the BAR here, but we are totally
224 				 * XXX unprepared to handle a non-zero value,
225 				 * XXX either here or anywhere else in
226 				 * XXX i386-land.
227 				 * XXX So just arrange to not look at the
228 				 * XXX upper 32 bits, lest we misinterpret
229 				 * XXX it as a 32-bit BAR set to zero.
230 				 */
231 			    width = 8;
232 			}
233 			addr = PCI_MAPREG_MEM_ADDR(val);
234 			size = PCI_MAPREG_MEM_SIZE(mask);
235 			ex = sc->extent_mem;
236 		} else {
237 			/* XXX some devices give 32bit value */
238 			addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
239 			size = PCI_MAPREG_IO_SIZE(mask);
240 			ex = sc->extent_port;
241 		}
242 
243 		if (!size) /* unused register */
244 			continue;
245 
246 		/* reservation/allocation phase */
247 		error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);
248 
249 		PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n",
250 				mapreg, type ? "port" : "mem ",
251 				(unsigned int)addr, (unsigned int)size));
252 	}
253 
254 	if (error)
255 		sc->nbogus++;
256 
257 	PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
258 }
259 
260 int
pciaddr_do_resource_allocate(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag,int mapreg,struct extent * ex,int type,bus_addr_t * addr,bus_size_t size)261 pciaddr_do_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
262     pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
263     bus_size_t size)
264 {
265 	bus_addr_t start;
266 	int error;
267 
268 	if (*addr) /* no need to allocate */
269 		return (0);
270 
271 	/* XXX Don't allocate if device is AGP device to avoid conflict. */
272 	if (pciaddr_device_is_agp(pc, tag))
273 		return (0);
274 
275 	start = (type == PCI_MAPREG_TYPE_MEM ? sc->mem_alloc_start
276 		: sc->port_alloc_start);
277 	if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
278 		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
279 		return (1);
280 	}
281 	error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
282 	    EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
283 	if (error) {
284 		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
285 		return (1);
286 	}
287 
288 	/* write new address to PCI device configuration header */
289 	pci_conf_write(pc, tag, mapreg, *addr);
290 	/* check */
291 	if (pcibios_flags & PCIBIOS_VERBOSE) {
292 		printf("pci_addr_fixup: ");
293 		pciaddr_print_devid(pc, tag);
294 	}
295 
296 	if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
297 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
298 		PCIBIOS_PRINTV(("fixup failed. (new address=%#lx)\n", *addr));
299 		return (1);
300 	}
301 	PCIBIOS_PRINTV(("new address 0x%08lx\n", *addr));
302 
303 	return (0);
304 }
305 
306 int
pciaddr_do_resource_reserve(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag,int mapreg,struct extent * ex,int type,bus_addr_t * addr,bus_size_t size)307 pciaddr_do_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
308     pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
309     bus_size_t size)
310 {
311 	pcireg_t val;
312 	int error;
313 
314 	if (*addr == 0)
315 		return (0);
316 
317 	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
318 	if (type == PCI_MAPREG_TYPE_MEM &&
319 	    (val & PCI_COMMAND_MEM_ENABLE) != PCI_COMMAND_MEM_ENABLE)
320 		return (0);
321 	if (type == PCI_MAPREG_TYPE_IO &&
322 	    (val & PCI_COMMAND_IO_ENABLE) != PCI_COMMAND_IO_ENABLE)
323 		return (0);
324 
325 	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
326 	if (error) {
327 		PCIBIOS_PRINTV(("Resource conflict.\n"));
328 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
329 		return (1);
330 	}
331 
332 	return (0);
333 }
334 
335 int
pciaddr_do_resource_reserve_disabled(struct pcibios_softc * sc,pci_chipset_tag_t pc,pcitag_t tag,int mapreg,struct extent * ex,int type,u_long * addr,bus_size_t size)336 pciaddr_do_resource_reserve_disabled(struct pcibios_softc *sc,
337     pci_chipset_tag_t pc, pcitag_t tag, int mapreg, struct extent *ex, int type,
338     u_long *addr, bus_size_t size)
339 {
340 	pcireg_t val;
341 	int error;
342 
343 	if (*addr == 0)
344 		return (0);
345 
346 	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
347 	if (type == PCI_MAPREG_TYPE_MEM &&
348 	    (val & PCI_COMMAND_MEM_ENABLE) == PCI_COMMAND_MEM_ENABLE)
349 		return (0);
350 	if (type == PCI_MAPREG_TYPE_IO &&
351 	    (val & PCI_COMMAND_IO_ENABLE) == PCI_COMMAND_IO_ENABLE)
352 		return (0);
353 
354 	PCIBIOS_PRINTV(("disabled %s space at addr 0x%lx size 0x%lx\n",
355 	    type == PCI_MAPREG_TYPE_MEM ? "mem" : "io", *addr, size));
356 
357 	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
358 	if (error) {
359 		PCIBIOS_PRINTV(("Resource conflict.\n"));
360 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
361 		return (1);
362 	}
363 
364 	return (0);
365 }
366 
367 bus_addr_t
pciaddr_ioaddr(u_int32_t val)368 pciaddr_ioaddr(u_int32_t val)
369 {
370 	return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
371 		? PCI_MAPREG_MEM_ADDR(val)
372 		: (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
373 }
374 
375 void
pciaddr_print_devid(pci_chipset_tag_t pc,pcitag_t tag)376 pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
377 {
378 	int bus, device, function;
379 	pcireg_t id;
380 
381 	id = pci_conf_read(pc, tag, PCI_ID_REG);
382 	pci_decompose_tag(pc, tag, &bus, &device, &function);
383 	printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
384 	       PCI_VENDOR(id), PCI_PRODUCT(id));
385 }
386 
387 int
pciaddr_device_is_agp(pci_chipset_tag_t pc,pcitag_t tag)388 pciaddr_device_is_agp(pci_chipset_tag_t pc, pcitag_t tag)
389 {
390 	pcireg_t class, status, rval;
391 	int off;
392 
393 	/* Check AGP device. */
394 	class = pci_conf_read(pc, tag, PCI_CLASS_REG);
395 	if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
396 		status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
397 		if (status & PCI_STATUS_CAPLIST_SUPPORT) {
398 			rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
399 			for (off = PCI_CAPLIST_PTR(rval);
400 			    off != 0;
401 			    off = PCI_CAPLIST_NEXT(rval) ) {
402 				rval = pci_conf_read(pc, tag, off);
403 				if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP)
404 					return (1);
405 			}
406 		}
407 	}
408 	return (0);
409 }
410