xref: /illumos-gate/usr/src/boot/i386/libi386/biospci.c (revision 22028508fd28d36ff74dc02c5774a8ba1f0db045)
1 /*
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2016 Netflix, Inc
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 
30 /*
31  * PnP enumerator using the PCI BIOS.
32  */
33 
34 #include <stand.h>
35 #include <machine/stdarg.h>
36 #include <machine/cpufunc.h>
37 #include <bootstrap.h>
38 #include <isapnp.h>
39 #include <btxv86.h>
40 #include "libi386.h"
41 #include "ficl.h"
42 
43 /*
44  * Stupid PCI BIOS interface doesn't let you simply enumerate everything
45  * that's there, instead you have to ask it if it has something.
46  *
47  * So we have to scan by class code, subclass code and sometimes programming
48  * interface.
49  */
50 
51 struct pci_progif
52 {
53 	int		pi_code;
54 	const char	*pi_name;
55 };
56 
57 static struct pci_progif progif_null[] = {
58 	{0x0,	NULL},
59 	{-1,	NULL}
60 };
61 
62 static struct pci_progif progif_display[] = {
63 	{0x0,	"VGA"},
64 	{0x1,	"8514"},
65 	{-1,	NULL}
66 };
67 
68 static struct pci_progif progif_ide[] = {
69 	{0x00,	NULL},
70 	{0x01,	NULL},
71 	{0x02,	NULL},
72 	{0x03,	NULL},
73 	{0x04,	NULL},
74 	{0x05,	NULL},
75 	{0x06,	NULL},
76 	{0x07,	NULL},
77 	{0x08,	NULL},
78 	{0x09,	NULL},
79 	{0x0a,	NULL},
80 	{0x0b,	NULL},
81 	{0x0c,	NULL},
82 	{0x0d,	NULL},
83 	{0x0e,	NULL},
84 	{0x0f,	NULL},
85 	{0x80,	NULL},
86 	{0x81,	NULL},
87 	{0x82,	NULL},
88 	{0x83,	NULL},
89 	{0x84,	NULL},
90 	{0x85,	NULL},
91 	{0x86,	NULL},
92 	{0x87,	NULL},
93 	{0x88,	NULL},
94 	{0x89,	NULL},
95 	{0x8a,	NULL},
96 	{0x8b,	NULL},
97 	{0x8c,	NULL},
98 	{0x8d,	NULL},
99 	{0x8e,	NULL},
100 	{0x8f,	NULL},
101 	{-1,	NULL}
102 };
103 
104 static struct pci_progif progif_serial[] = {
105 	{0x0,	"8250"},
106 	{0x1,	"16450"},
107 	{0x2,	"16550"},
108 	{-1,	NULL}
109 };
110 
111 static struct pci_progif progif_parallel[] = {
112 	{0x0,	"Standard"},
113 	{0x1,	"Bidirectional"},
114 	{0x2,	"ECP"},
115 	{-1,	NULL}
116 };
117 
118 static struct pci_progif progif_firewire[] = {
119 	{0x10,	"OHCI"},
120 	{-1,	NULL}
121 };
122 
123 struct pci_subclass
124 {
125 	int			ps_subclass;
126 	const char		*ps_name;
127 	/* if set, use for programming interface value(s) */
128 	struct pci_progif	*ps_progif;
129 };
130 
131 static struct pci_subclass subclass_old[] = {
132 	{0x0,	"Old non-VGA",		progif_null},
133 	{0x1,	"Old VGA",		progif_null},
134 	{-1,	NULL,			NULL}
135 };
136 
137 static struct pci_subclass subclass_mass[] = {
138 	{0x0,	"SCSI",			progif_null},
139 	{0x1,	"IDE",			progif_ide},
140 	{0x2,	"Floppy disk",		progif_null},
141 	{0x3,	"IPI",			progif_null},
142 	{0x4,	"RAID",			progif_null},
143 	{0x80,	"mass storage",		progif_null},
144 	{-1,	NULL,			NULL}
145 };
146 
147 static struct pci_subclass subclass_net[] = {
148 	{0x0,	"Ethernet",		progif_null},
149 	{0x1,	"Token ring",		progif_null},
150 	{0x2,	"FDDI",			progif_null},
151 	{0x3,	"ATM",			progif_null},
152 	{0x80,	"network",		progif_null},
153 	{-1,	NULL,			NULL}
154 };
155 
156 static struct pci_subclass subclass_display[] = {
157 	{0x0,	NULL,			progif_display},
158 	{0x1,	"XGA",			progif_null},
159 	{0x80,	"other",		progif_null},
160 	{-1,	NULL,			NULL}
161 };
162 
163 static struct pci_subclass subclass_comms[] = {
164 	{0x0,	"serial",		progif_serial},
165 	{0x1,	"parallel",		progif_parallel},
166 	{0x80,	"communications",	progif_null},
167 	{-1,	NULL,			NULL}
168 };
169 
170 static struct pci_subclass subclass_serial[] = {
171 	{0x0,	"FireWire",		progif_firewire},
172 	{0x1,	"ACCESS.bus",		progif_null},
173 	{0x2,	"SSA",			progif_null},
174 	{0x3,	"USB",			progif_null},
175 	{0x4,	"Fibrechannel",		progif_null},
176 	{-1,	NULL,			NULL}
177 };
178 
179 static struct pci_class
180 {
181 	int			pc_class;
182 	const char		*pc_name;
183 	struct pci_subclass	*pc_subclass;
184 } pci_classes[] = {
185 	{0x0,	"device",	subclass_old},
186 	{0x1,	"controller",	subclass_mass},
187 	{0x2,	"controller",	subclass_net},
188 	{0x3,	"display",	subclass_display},
189 	{0x7,	"controller",	subclass_comms},
190 	{0xc,	"controller",	subclass_serial},
191 	{-1,	NULL,		NULL}
192 };
193 
194 
195 static void	biospci_enumerate(void);
196 static void	biospci_addinfo(int, struct pci_class *, struct pci_subclass *,
197     struct pci_progif *);
198 
199 struct pnphandler biospcihandler =
200 {
201 	"PCI BIOS",
202 	biospci_enumerate
203 };
204 
205 #define	PCI_BIOS_PRESENT	0xb101
206 #define	FIND_PCI_DEVICE		0xb102
207 #define	FIND_PCI_CLASS_CODE	0xb103
208 #define	GENERATE_SPECIAL_CYCLE	0xb106
209 #define	READ_CONFIG_BYTE	0xb108
210 #define	READ_CONFIG_WORD	0xb109
211 #define	READ_CONFIG_DWORD	0xb10a
212 #define	WRITE_CONFIG_BYTE	0xb10b
213 #define	WRITE_CONFIG_WORD	0xb10c
214 #define	WRITE_CONFIG_DWORD	0xb10d
215 #define	GET_IRQ_ROUTING_OPTIONS	0xb10e
216 #define	SET_PCI_IRQ		0xb10f
217 
218 #define	PCI_INT			0x1a
219 
220 #define	PCI_SIGNATURE		0x20494350	/* AKA "PCI " */
221 
222 void
biospci_detect(void)223 biospci_detect(void)
224 {
225 	uint16_t version, hwcap, maxbus;
226 	char buf[24];
227 
228 	/* Find the PCI BIOS */
229 	v86.ctl = V86_FLAGS;
230 	v86.addr = PCI_INT;
231 	v86.eax = PCI_BIOS_PRESENT;
232 	v86.edi = 0x0;
233 	v86int();
234 
235 	/* Check for OK response */
236 	if (V86_CY(v86.efl) || ((v86.eax & 0xff00) != 0) ||
237 	    (v86.edx != PCI_SIGNATURE))
238 		return;
239 
240 	version = v86.ebx & 0xffff;
241 	hwcap = v86.eax & 0xff;
242 	maxbus = v86.ecx & 0xff;
243 #if 0
244 	printf("PCI BIOS %d.%d%s%s maxbus %d\n",
245 	    bcd2bin((version >> 8) & 0xf), bcd2bin(version & 0xf),
246 	    (hwcap & 1) ? " config1" : "", (hwcap & 2) ? " config2" : "",
247 	    maxbus);
248 #endif
249 	sprintf(buf, "%d", bcd2bin((version >> 8) & 0xf));
250 	setenv("pcibios.major", buf, 1);
251 	sprintf(buf, "%d", bcd2bin(version & 0xf));
252 	setenv("pcibios.minor", buf, 1);
253 	sprintf(buf, "%d", !!(hwcap & 1));
254 	setenv("pcibios.config1", buf, 1);
255 	sprintf(buf, "%d", !!(hwcap & 2));
256 	setenv("pcibios.config2", buf, 1);
257 	sprintf(buf, "%d", maxbus);
258 	setenv("pcibios.maxbus", buf, 1);
259 }
260 
261 static void
biospci_enumerate(void)262 biospci_enumerate(void)
263 {
264 	int			device_index, err;
265 	uint32_t		locator, devid;
266 	struct pci_class	*pc;
267 	struct pci_subclass	*psc;
268 	struct pci_progif	*ppi;
269 
270 	/* Iterate over known classes */
271 	for (pc = pci_classes; pc->pc_class >= 0; pc++) {
272 		/* Iterate over subclasses */
273 		for (psc = pc->pc_subclass; psc->ps_subclass >= 0; psc++) {
274 			/* Iterate over programming interfaces */
275 			for (ppi = psc->ps_progif; ppi->pi_code >= 0; ppi++) {
276 
277 				/* Scan for matches */
278 				for (device_index = 0; ; device_index++) {
279 					/* Look for a match */
280 					err = biospci_find_devclass(
281 					    (pc->pc_class << 16) +
282 					    (psc->ps_subclass << 8) +
283 					    ppi->pi_code,
284 					    device_index, &locator);
285 					if (err != 0)
286 						break;
287 
288 					/*
289 					 * Read the device identifier from
290 					 * the nominated device
291 					 */
292 					err = biospci_read_config(locator,
293 					    0, 2, &devid);
294 					if (err != 0)
295 						break;
296 
297 					/*
298 					 * We have the device ID, create a PnP
299 					 * object and save everything
300 					 */
301 					biospci_addinfo(devid, pc, psc, ppi);
302 				}
303 			}
304 		}
305 	}
306 }
307 
308 static void
biospci_addinfo(int devid,struct pci_class * pc,struct pci_subclass * psc,struct pci_progif * ppi)309 biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc,
310     struct pci_progif *ppi)
311 {
312 	struct pnpinfo	*pi;
313 	char		desc[80];
314 
315 
316 	/* build the description */
317 	desc[0] = 0;
318 	if (ppi->pi_name != NULL) {
319 		strcat(desc, ppi->pi_name);
320 		strcat(desc, " ");
321 	}
322 	if (psc->ps_name != NULL) {
323 		strcat(desc, psc->ps_name);
324 		strcat(desc, " ");
325 	}
326 	if (pc->pc_name != NULL)
327 		strcat(desc, pc->pc_name);
328 
329 	pi = pnp_allocinfo();
330 	pi->pi_desc = strdup(desc);
331 	sprintf(desc, "0x%08x", devid);
332 	pnp_addident(pi, desc);
333 	pnp_addinfo(pi);
334 }
335 
336 int
biospci_find_devclass(uint32_t class,int index,uint32_t * locator)337 biospci_find_devclass(uint32_t class, int index, uint32_t *locator)
338 {
339 	v86.ctl = V86_FLAGS;
340 	v86.addr = PCI_INT;
341 	v86.eax = FIND_PCI_CLASS_CODE;
342 	v86.ecx = class;
343 	v86.esi = index;
344 	v86int();
345 
346 	/* error */
347 	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
348 		return (-1);
349 
350 	*locator = v86.ebx;
351 	return (0);
352 }
353 
354 static int
biospci_find_device(uint32_t devid,int index,uint32_t * locator)355 biospci_find_device(uint32_t devid, int index, uint32_t *locator)
356 {
357 	v86.ctl = V86_FLAGS;
358 	v86.addr = PCI_INT;
359 	v86.eax = FIND_PCI_DEVICE;
360 	v86.edx = devid & 0xffff;		/* EDX - Vendor ID */
361 	v86.ecx = (devid >> 16) & 0xffff;	/* ECX - Device ID */
362 	v86.esi = index;
363 	v86int();
364 
365 	/* error */
366 	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
367 		return (-1);
368 
369 	*locator = v86.ebx;
370 	return (0);
371 }
372 /*
373  * Configuration space access methods.
374  * width = 0(byte), 1(word) or 2(dword).
375  */
376 int
biospci_write_config(uint32_t locator,int offset,int width,uint32_t val)377 biospci_write_config(uint32_t locator, int offset, int width, uint32_t val)
378 {
379 	v86.ctl = V86_FLAGS;
380 	v86.addr = PCI_INT;
381 	v86.eax = WRITE_CONFIG_BYTE + width;
382 	v86.ebx = locator;
383 	v86.edi = offset;
384 	v86.ecx = val;
385 	v86int();
386 
387 	/* error */
388 	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
389 		return (-1);
390 
391 	return (0);
392 }
393 
394 int
biospci_read_config(uint32_t locator,int offset,int width,uint32_t * val)395 biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val)
396 {
397 	v86.ctl = V86_FLAGS;
398 	v86.addr = PCI_INT;
399 	v86.eax = READ_CONFIG_BYTE + width;
400 	v86.ebx = locator;
401 	v86.edi = offset;
402 	v86int();
403 
404 	/* error */
405 	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
406 		return (-1);
407 
408 	*val = v86.ecx;
409 	return (0);
410 }
411 
412 uint32_t
biospci_locator(int8_t bus,uint8_t device,uint8_t function)413 biospci_locator(int8_t bus, uint8_t device, uint8_t function)
414 {
415 
416 	return ((bus << 8) | ((device & 0x1f) << 3) | (function & 0x7));
417 }
418 
419 /*
420  * Counts the number of instances of devid we have in the system, as least as
421  * far as the PCI BIOS is able to tell.
422  */
423 static int
biospci_count_device_type(uint32_t devid)424 biospci_count_device_type(uint32_t devid)
425 {
426 	int i;
427 
428 	for (i = 0; 1; i++) {
429 		v86.ctl = V86_FLAGS;
430 		v86.addr = PCI_INT;
431 		v86.eax = FIND_PCI_DEVICE;
432 		v86.edx = devid & 0xffff;		/* EDX - Vendor ID */
433 		v86.ecx = (devid >> 16) & 0xffff;	/* ECX - Device ID */
434 		v86.esi = i;
435 		v86int();
436 		if (V86_CY(v86.efl) || (v86.eax & 0xff00))
437 			break;
438 
439 	}
440 	return (i);
441 }
442 
443 /*
444  * pcibios-device-count (devid -- count)
445  *
446  * Returns the PCI BIOS' count of how many devices matching devid are
447  * in the system. devid is the 32-bit vendor + device.
448  */
449 static void
ficlPciBiosCountDevices(ficlVm * pVM)450 ficlPciBiosCountDevices(ficlVm *pVM)
451 {
452 	uint32_t devid;
453 	int i;
454 
455 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 1, 1);
456 
457 	devid = ficlStackPopInteger(ficlVmGetDataStack(pVM));
458 
459 	i = biospci_count_device_type(devid);
460 
461 	ficlStackPushInteger(ficlVmGetDataStack(pVM), i);
462 }
463 
464 /*
465  * pcibios-write-config (locator offset width value -- )
466  *
467  * Writes the specified config register.
468  * Locator is bus << 8 | device << 3 | fuction
469  * offset is the pci config register
470  * width is 0 for byte, 1 for word, 2 for dword
471  * value is the value to write
472  */
473 static void
ficlPciBiosWriteConfig(ficlVm * pVM)474 ficlPciBiosWriteConfig(ficlVm *pVM)
475 {
476 	uint32_t value, width, offset, locator;
477 
478 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 4, 0);
479 
480 	value = ficlStackPopInteger(ficlVmGetDataStack(pVM));
481 	width = ficlStackPopInteger(ficlVmGetDataStack(pVM));
482 	offset = ficlStackPopInteger(ficlVmGetDataStack(pVM));
483 	locator = ficlStackPopInteger(ficlVmGetDataStack(pVM));
484 
485 	biospci_write_config(locator, offset, width, value);
486 }
487 
488 /*
489  * pcibios-read-config (locator offset width -- value)
490  *
491  * Reads the specified config register.
492  * Locator is bus << 8 | device << 3 | fuction
493  * offset is the pci config register
494  * width is 0 for byte, 1 for word, 2 for dword
495  * value is the value to read from the register
496  */
497 static void
ficlPciBiosReadConfig(ficlVm * pVM)498 ficlPciBiosReadConfig(ficlVm *pVM)
499 {
500 	uint32_t value, width, offset, locator;
501 
502 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 1);
503 
504 	width = ficlStackPopInteger(ficlVmGetDataStack(pVM));
505 	offset = ficlStackPopInteger(ficlVmGetDataStack(pVM));
506 	locator = ficlStackPopInteger(ficlVmGetDataStack(pVM));
507 
508 	value = 0;
509 	(void) biospci_read_config(locator, offset, width, &value);
510 
511 	ficlStackPushInteger(ficlVmGetDataStack(pVM), value);
512 }
513 
514 /*
515  * pcibios-find-devclass (class index -- locator)
516  *
517  * Finds the index'th instance of class in the pci tree.
518  * must be an exact match.
519  * class is the class to search for.
520  * index 0..N (set to 0, increment until error)
521  *
522  * Locator is bus << 8 | device << 3 | fuction (or -1 on error)
523  */
524 static void
ficlPciBiosFindDevclass(ficlVm * pVM)525 ficlPciBiosFindDevclass(ficlVm *pVM)
526 {
527 	uint32_t index, class, locator;
528 
529 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 1);
530 
531 	index = ficlStackPopInteger(ficlVmGetDataStack(pVM));
532 	class = ficlStackPopInteger(ficlVmGetDataStack(pVM));
533 
534 	if (biospci_find_devclass(class, index, &locator))
535 		locator = 0xffffffff;
536 
537 	ficlStackPushInteger(ficlVmGetDataStack(pVM), locator);
538 }
539 
540 /*
541  * pcibios-find-device(devid index -- locator)
542  *
543  * Finds the index'th instance of devid in the pci tree.
544  * must be an exact match.
545  * class is the class to search for.
546  * index 0..N (set to 0, increment until error)
547  *
548  * Locator is bus << 8 | device << 3 | fuction (or -1 on error)
549  */
550 static void
ficlPciBiosFindDevice(ficlVm * pVM)551 ficlPciBiosFindDevice(ficlVm *pVM)
552 {
553 	uint32_t index, devid, locator;
554 
555 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 1);
556 
557 	index = ficlStackPopInteger(ficlVmGetDataStack(pVM));
558 	devid = ficlStackPopInteger(ficlVmGetDataStack(pVM));
559 
560 	if (biospci_find_device(devid, index, &locator))
561 		locator = 0xffffffff;
562 
563 	ficlStackPushInteger(ficlVmGetDataStack(pVM), locator);
564 }
565 
566 /*
567  * pcibios-locator(bus device function -- locator)
568  *
569  * converts bus, device, function to locator.
570  *
571  * Locator is bus << 8 | device << 3 | fuction
572  */
573 static void
ficlPciBiosLocator(ficlVm * pVM)574 ficlPciBiosLocator(ficlVm *pVM)
575 {
576 	uint32_t bus, device, function, locator;
577 
578 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 3, 1);
579 
580 	function = ficlStackPopInteger(ficlVmGetDataStack(pVM));
581 	device = ficlStackPopInteger(ficlVmGetDataStack(pVM));
582 	bus = ficlStackPopInteger(ficlVmGetDataStack(pVM));
583 
584 	locator = biospci_locator(bus, device, function);
585 
586 	ficlStackPushInteger(ficlVmGetDataStack(pVM), locator);
587 }
588 
589 /*
590  * Glue function to add the appropriate forth words to access pci bios
591  * functionality.
592  */
593 static void
ficlCompilePciBios(ficlSystem * pSys)594 ficlCompilePciBios(ficlSystem *pSys)
595 {
596 	ficlDictionary *dp = ficlSystemGetDictionary(pSys);
597 
598 	FICL_SYSTEM_ASSERT(pSys, dp);
599 
600 	ficlDictionarySetPrimitive(dp, "pcibios-device-count",
601 	    ficlPciBiosCountDevices, FICL_WORD_DEFAULT);
602 	ficlDictionarySetPrimitive(dp, "pcibios-read-config",
603 	    ficlPciBiosReadConfig, FICL_WORD_DEFAULT);
604 	ficlDictionarySetPrimitive(dp, "pcibios-write-config",
605 	    ficlPciBiosWriteConfig, FICL_WORD_DEFAULT);
606 	ficlDictionarySetPrimitive(dp, "pcibios-find-devclass",
607 	    ficlPciBiosFindDevclass, FICL_WORD_DEFAULT);
608 	ficlDictionarySetPrimitive(dp, "pcibios-find-device",
609 	    ficlPciBiosFindDevice, FICL_WORD_DEFAULT);
610 	ficlDictionarySetPrimitive(dp, "pcibios-locator", ficlPciBiosLocator,
611 	    FICL_WORD_DEFAULT);
612 }
613 
614 FICL_COMPILE_SET(ficlCompilePciBios);
615