xref: /illumos-gate/usr/src/lib/fm/topo/modules/i86pc/x86pi/x86pi_hostbridge.c (revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * i86pc Generic hostbridge/pciex/pci enumerator
29  *
30  * hostbridge/pciexrc/pcibus topo nodes are created per SMBIOS type 138
31  * (SUN_OEM_PCIEXRC) records.   Each type 138 record can either represent
32  * a hostbridge or a pciexrc/pcibus determined by whether it points to
33  * a baseboard record or another type 138 record.
34  *
35  * x86pi_gen_hbr() is called when a new hostbridge node needs to be created..
36  * It then searches all the type 138 records that connected to it.  For each
37  * of the records, bdf is compared to find a matching di_node.  If the
38  * di_node is a pciex root port, a pciexrc (bad name!) node will be created.
39  * When pciexrc creation is done, or the di_node is a pcibus, in either
40  * case the pcibus module will loaded to enumerate pciexbus/pcibus etc.
41  *
42  * The enumeration uses did routines heavily, which requires a did hash
43  * pointer stored in x86pi's module-specific area.
44  */
45 
46 #include <sys/types.h>
47 #include <strings.h>
48 #include <fm/topo_mod.h>
49 #include <fm/topo_hc.h>
50 #include <sys/systeminfo.h>
51 #include <sys/smbios_impl.h>
52 #include <sys/fm/protocol.h>
53 #include <x86pi_impl.h>
54 #include <did.h>
55 #include <did_impl.h>
56 #include <did_props.h>
57 #include <hostbridge.h>
58 
59 #define	PCI_ENUM	"pcibus"
60 #define	PCI_ENUMR_VERS	1
61 #define	MAX_HB_BUSES	255
62 
63 extern txprop_t RC_common_props[], HB_common_props[], ExHB_common_props[];
64 extern int RC_propcnt, HB_propcnt, ExHB_propcnt;
65 
66 static topo_mod_t *pcimp = NULL;
67 
68 int
69 x86pi_hbr_enum_init(topo_mod_t *mod)
70 {
71 	did_hash_t *tab = (did_hash_t *)topo_mod_getspecific(mod);
72 	const char *f = "x86pi_hbr_enum_init";
73 
74 	if (tab == NULL && did_hash_init(mod) < 0) {
75 		topo_mod_dprintf(mod, "%s: did_hash_init() failed.\n", f);
76 		return (-1);
77 	}
78 
79 	if (pcimp == NULL &&
80 	    (pcimp = topo_mod_load(mod, PCI_ENUM, PCI_ENUMR_VERS))
81 	    == NULL) {
82 		topo_mod_dprintf(mod,
83 		    "%s: %s enumerator could not load %s.\n",
84 		    f, HOSTBRIDGE, PCI_ENUM);
85 		did_hash_fini(mod);
86 		return (-1);
87 	}
88 
89 	return (0);
90 }
91 
92 void
93 x86pi_hbr_enum_fini(topo_mod_t *mod)
94 {
95 	did_hash_fini(mod);
96 	if (pcimp != NULL) {
97 		topo_mod_unload(pcimp);
98 		pcimp = NULL;
99 	}
100 }
101 
102 static uint16_t
103 x86pi_bdf(topo_mod_t *mod, di_node_t node)
104 {
105 	int *val;
106 
107 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &val) < 0) {
108 		topo_mod_dprintf(mod, "couldn't get \"reg\" prop: %s.\n",
109 		    strerror(errno));
110 		return ((uint16_t)-1);
111 	}
112 
113 	return (uint16_t)((*val & PCI_REG_BDFR_M) >> PCI_REG_FUNC_SHIFT);
114 }
115 
116 static int
117 pciex_process(topo_mod_t *mod, tnode_t *tn_hbr, di_node_t rcn,
118     topo_instance_t rci)
119 {
120 	did_t		*did;
121 	int		rv;
122 	tnode_t		*tn_rc;
123 	x86pi_hcfmri_t	hcfmri = {0};
124 	tnode_t		*tn_bb = topo_node_parent(tn_hbr);
125 	const char	*f = "pciexrc_process";
126 
127 	if ((did = did_create(mod, rcn, topo_node_instance(tn_bb),
128 	    topo_node_instance(tn_hbr), rci, TRUST_BDF)) == NULL)
129 		return (NULL);
130 
131 	did_markrc(did);
132 
133 	/*
134 	 * Let did set the hostbridge properties excluding FRU and label.
135 	 */
136 	(void) did_props_set(tn_hbr, did, ExHB_common_props, ExHB_propcnt - 2);
137 
138 	if (topo_node_range_create(mod, tn_hbr, PCIEX_ROOT, 0,
139 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
140 		topo_mod_dprintf(mod,
141 		    "%s: create child range for %s failed: %s\n",
142 		    f, PCIEX_ROOT, topo_mod_errmsg(mod));
143 		return (-1);
144 	}
145 
146 	hcfmri.hc_name = PCIEX_ROOT;
147 	hcfmri.instance = rci;
148 	rv = x86pi_enum_generic(mod, &hcfmri, tn_hbr, tn_hbr, &tn_rc, 0);
149 	if (rv != 0) {
150 		topo_mod_dprintf(mod, "%s: failed to create %s = %d\n",
151 		    f, PCIEX_ROOT, rci);
152 		return (-1);
153 	}
154 
155 	/*
156 	 * pcibus enumerator requires di_node_t be set in node specific
157 	 */
158 	topo_node_setspecific(tn_rc, rcn);
159 
160 	/*
161 	 * Let did set the RC properties excluding FRU, and label.
162 	 */
163 	if (did_props_set(tn_rc, did, RC_common_props, RC_propcnt - 2) < 0) {
164 		topo_mod_dprintf(mod, "%s: did_props_set failed for %s = %d\n",
165 		    f, PCIEX_ROOT, rci);
166 		topo_node_unbind(tn_rc);
167 		return (-1);
168 	}
169 
170 	if (topo_node_range_create(mod, tn_rc, PCIEX_BUS, 0,
171 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
172 		topo_mod_dprintf(mod,
173 		    "%s: create child range for %s failed: %s\n",
174 		    f, PCIEX_BUS, topo_mod_errmsg(mod));
175 		return (-1);
176 	}
177 
178 	return (topo_mod_enumerate(mod, tn_rc, PCI_BUS, PCIEX_BUS,
179 	    0, MAX_HB_BUSES, did));
180 }
181 
182 static int
183 pci_process(topo_mod_t *mod, tnode_t *tn_hbr, di_node_t bn)
184 {
185 	did_t *did;
186 	tnode_t *tn_bb = topo_node_parent(tn_hbr);
187 
188 	if ((did = did_create(mod, bn, topo_node_instance(tn_bb),
189 	    topo_node_instance(tn_hbr), NO_RC, TRUST_BDF)) == NULL)
190 		return (-1);
191 
192 	/*
193 	 * Let did set the hostbridge properties excluding FRU and label.
194 	 */
195 	(void) did_props_set(tn_hbr, did, HB_common_props, HB_propcnt - 2);
196 
197 	if (topo_node_range_create(mod, tn_hbr, PCI_BUS, 0,
198 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
199 		topo_mod_dprintf(mod, "create child range for %s failed: %s\n",
200 		    PCI_BUS, topo_mod_errmsg(mod));
201 		return (-1);
202 	}
203 
204 	return (topo_mod_enumerate(mod, tn_hbr, PCI_BUS, PCI_BUS,
205 	    0, MAX_HB_BUSES, did));
206 }
207 
208 static int
209 x86pi_gen_pci_pciexrc(topo_mod_t *mod, tnode_t *tn_hbr, uint16_t bdf,
210     topo_instance_t *rcip)
211 {
212 	di_node_t devtree, pnode, cnode;
213 
214 	topo_mod_dprintf(mod, "creating pci/pciexrc node bdf = %#x\n",
215 	    (int)bdf);
216 
217 	devtree = topo_mod_devinfo(mod);
218 	if (devtree == DI_NODE_NIL) {
219 		topo_mod_dprintf(mod, "devinfo init failed.\n");
220 		return (-1);
221 	}
222 
223 	for (pnode = di_drv_first_node(PCI, devtree);
224 	    pnode != DI_NODE_NIL; pnode = di_drv_next_node(pnode))
225 		if (x86pi_bdf(mod, pnode) == bdf)
226 			return (pci_process(mod, tn_hbr, pnode));
227 
228 	pnode = di_drv_first_node(NPE, devtree);
229 	while (pnode != DI_NODE_NIL) {
230 		for (cnode = di_child_node(pnode); cnode != DI_NODE_NIL;
231 		    cnode = di_sibling_node(cnode)) {
232 			if (di_driver_name(cnode) == NULL ||
233 			    x86pi_bdf(mod, cnode) != bdf)
234 				continue;
235 
236 			if (strcmp(di_driver_name(cnode), PCI_PCI) == 0)
237 				return (pci_process(mod, tn_hbr, cnode));
238 
239 			if (strcmp(di_driver_name(cnode), PCIEB) == 0)
240 				return (pciex_process(mod, tn_hbr,
241 				    cnode, (*rcip)++));
242 
243 			topo_mod_dprintf(mod, "no matching driver found: "
244 			    "bdf = %#x\n", (int)bdf);
245 		}
246 		pnode = di_drv_next_node(pnode);
247 	}
248 
249 	topo_mod_dprintf(mod, "no matching bdf found: bdf = %#x\n", (int)bdf);
250 
251 	return (0);
252 }
253 
254 int
255 x86pi_gen_hbr(topo_mod_t *mod, tnode_t *tn_bb, smbios_hdl_t *shp,
256     int hbr_smbid, topo_instance_t hbri, topo_instance_t *rcip)
257 {
258 	x86pi_hcfmri_t	hcfmri = {0};
259 	tnode_t		*tn_hbr;
260 	smbs_cnt_t	*smbc = &stypes[SUN_OEM_PCIEXRC];
261 	smbios_pciexrc_t smb_rc;
262 	int		i, rv, err = 0;
263 	const char	*f = "x86pi_gen_hbr";
264 
265 	hcfmri.hc_name = HOSTBRIDGE;
266 	hcfmri.instance = hbri;
267 
268 	/* create and bind the "hostbridge" node */
269 	rv = x86pi_enum_generic(mod, &hcfmri, tn_bb, tn_bb, &tn_hbr, 0);
270 	if (rv != 0) {
271 		topo_mod_dprintf(mod, "%s: failed to create %s = %d\n",
272 		    f, HOSTBRIDGE, hbri);
273 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
274 	}
275 
276 	/*
277 	 * Walk the smbios records and create the pci/pciexrc nodes
278 	 */
279 	for (i = 0; i < smbc->count; i++) {
280 		if (smbios_info_pciexrc(shp, smbc->ids[i].id, &smb_rc) != 0)
281 			topo_mod_dprintf(mod,
282 			    "%s: failed: id = %d\n", f, (int)smbc->ids[i].id);
283 		else if (smb_rc.smbpcie_bb == hbr_smbid &&
284 		    x86pi_gen_pci_pciexrc(mod, tn_hbr, smb_rc.smbpcie_bdf,
285 		    rcip) != 0)
286 			err++;
287 	}
288 
289 	return (err == 0 ? 0 : topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
290 }
291