xref: /illumos-gate/usr/src/uts/i86pc/os/pci_bios.c (revision f5cd957f512eb3fe078de1043418ae6acf52a4c6)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/sunndi.h>
31 #include <sys/pci.h>
32 #include <sys/pci_impl.h>
33 #include <sys/pci_cfgspace.h>
34 #include <sys/pci_cfgspace_impl.h>
35 #include <sys/memlist.h>
36 #include <sys/bootconf.h>
37 #include <sys/psw.h>
38 
39 /*
40  * pci irq routing information table
41  */
42 static int			pci_irq_nroutes;
43 static pci_irq_route_t		*pci_irq_routes;
44 
45 
46 static int pci_bios_get_irq_routing(pci_irq_route_t *, int, int *);
47 static void pci_get_irq_routing_table(void);
48 
49 
50 /*
51  * Retrieve information from the bios needed for system
52  * configuration early during startup.
53  */
54 void
55 startup_pci_bios(void)
56 {
57 	pci_get_irq_routing_table();
58 }
59 
60 
61 /*
62  * Issue the bios get irq routing information table interrupt
63  *
64  * Despite the name, the information in the table is only
65  * used to derive slot names for some named pci hot-plug slots.
66  *
67  * Returns the number of irq routing table entries returned
68  * by the bios, or 0 and optionally, the number of entries required.
69  */
70 static int
71 pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp)
72 {
73 	struct bop_regs regs;
74 	uchar_t		*hdrp;
75 	uchar_t		*bufp;
76 	int 		i, n;
77 	int		rval = 0;
78 
79 	if (nneededp)
80 		*nneededp = 0;
81 
82 	/*
83 	 * Set up irq routing header with the size and address
84 	 * of some useable low-memory data addresses.  Initalize
85 	 * data area to zero, avoiding memcpy/bzero.
86 	 */
87 	hdrp = (uchar_t *)BIOS_IRQ_ROUTING_HDR;
88 	bufp = (uchar_t *)BIOS_IRQ_ROUTING_DATA;
89 
90 	n = nroutes * sizeof (pci_irq_route_t);
91 	for (i = 0; i < n; i++)
92 		bufp[i] = 0;
93 	((pci_irq_route_hdr_t *)hdrp)->pir_size = n;
94 	((pci_irq_route_hdr_t *)hdrp)->pir_addr = (uint32_t)(uintptr_t)bufp;
95 
96 	bzero(&regs, sizeof (regs));
97 	regs.eax.word.ax = (PCI_FUNCTION_ID << 8) | PCI_GET_IRQ_ROUTING;
98 
99 	regs.ds = 0xf000;
100 	regs.es = FP_SEG((uint_t)(uintptr_t)hdrp);
101 	regs.edi.word.di = FP_OFF((uint_t)(uintptr_t)hdrp);
102 
103 	BOP_DOINT(bootops, 0x1a, &regs);
104 
105 	n = (int)(((pci_irq_route_hdr_t *)hdrp)->pir_size /
106 	    sizeof (pci_irq_route_t));
107 
108 	if ((regs.eflags & PS_C) != 0) {
109 		if (nneededp)
110 			*nneededp = n;
111 	} else {
112 		/*
113 		 * Copy resulting irq routing data from low memory up to
114 		 * the kernel address space, avoiding memcpy as usual.
115 		 */
116 		if (n <= nroutes) {
117 			for (i = 0; i < n * sizeof (pci_irq_route_t); i++)
118 				((uchar_t *)routes)[i] = bufp[i];
119 			rval = n;
120 		}
121 	}
122 	return (rval);
123 }
124 
125 static void
126 pci_get_irq_routing_table(void)
127 {
128 	pci_irq_route_t	*routes;
129 	int		n = N_PCI_IRQ_ROUTES;
130 	int		nneeded = 0;
131 	int		nroutes;
132 
133 	/*
134 	 * Get irq routing table information.
135 	 * Allocate a buffer for an initial default number of entries.
136 	 * If the bios indicates it needs a larger buffer, try it again.
137 	 * Drive on if it still won't cooperate and play nice after that.
138 	 */
139 	routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
140 	nroutes = pci_bios_get_irq_routing(routes, n, &nneeded);
141 	if (nroutes == 0 && nneeded > n) {
142 		kmem_free(routes, n * sizeof (pci_irq_route_t));
143 		if (nneeded > N_PCI_IRQ_ROUTES_MAX) {
144 			cmn_err(CE_CONT,
145 			    "pci: unable to get IRQ routing information, "
146 			    "required buffer space of %d entries exceeds max\n",
147 			    nneeded);
148 			return;
149 		}
150 		n = nneeded;
151 		routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
152 		nroutes = pci_bios_get_irq_routing(routes, n, NULL);
153 		if (nroutes == 0) {
154 			cmn_err(CE_CONT,
155 			    "pci: unable to get IRQ routing information, "
156 			    "required buffer space for %d entries\n", n);
157 			kmem_free(routes, n * sizeof (pci_irq_route_t));
158 		}
159 	}
160 
161 	if (nroutes > 0) {
162 		pci_irq_routes = routes;
163 		pci_irq_nroutes = nroutes;
164 	}
165 }
166 
167 /*
168  * Use the results of the PCI BIOS call that returned the routing tables
169  * to build the 1275 slot-names property for the indicated bus.
170  * Results are returned in buf.  Length is return value, -1 is returned on
171  * overflow and zero is returned if no data exists to build a property.
172  */
173 int
174 pci_slot_names_prop(int bus, char *buf, int len)
175 {
176 	uchar_t		dev;
177 	uchar_t		slot[N_PCI_IRQ_ROUTES_MAX+1];
178 	uint32_t	 mask;
179 	int		i, nnames, plen;
180 
181 	ASSERT(pci_irq_nroutes <= N_PCI_IRQ_ROUTES_MAX);
182 
183 	if (pci_irq_nroutes == 0)
184 		return (0);
185 	nnames = 0;
186 	mask = 0;
187 	for (i = 0; i < pci_irq_nroutes; i++)
188 		slot[i] = 0xff;
189 	for (i = 0; i < pci_irq_nroutes; i++) {
190 		if (pci_irq_routes[i].pir_bus != bus)
191 			continue;
192 		if (pci_irq_routes[i].pir_slot != 0) {
193 			dev = (pci_irq_routes[i].pir_dev & 0xf8) >> 3;
194 			slot[dev] = pci_irq_routes[i].pir_slot;
195 			mask |= (1 << dev);
196 			nnames++;
197 		}
198 	}
199 
200 	if (nnames == 0)
201 		return (0);
202 
203 	if (len < (4 + nnames * 8))
204 		return (-1);
205 	*(uint32_t *)buf = mask;
206 	plen = 4;
207 	for (i = 0; i < pci_irq_nroutes; i++) {
208 		if (slot[i] == 0xff)
209 			continue;
210 		(void) sprintf(buf + plen, "Slot%d", slot[i]);
211 		plen += strlen(buf+plen) + 1;
212 		*(buf + plen) = 0;
213 	}
214 	for (; plen % 4; plen++)
215 		*(buf + plen) = 0;
216 	return (plen);
217 }
218