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 /*
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 int
103 pciex_process(topo_mod_t *mod, tnode_t *tn_hbr, di_node_t rcn,
104     topo_instance_t rci)
105 {
106 	did_t		*did;
107 	int		rv;
108 	tnode_t		*tn_rc;
109 	x86pi_hcfmri_t	hcfmri = {0};
110 	tnode_t		*tn_bb = topo_node_parent(tn_hbr);
111 	const char	*f = "pciexrc_process";
112 
113 	if ((did = did_create(mod, rcn, topo_node_instance(tn_bb),
114 	    topo_node_instance(tn_hbr), rci, TRUST_BDF)) == NULL)
115 		return (NULL);
116 
117 	did_markrc(did);
118 
119 	/*
120 	 * Let did set the hostbridge properties excluding FRU and label.
121 	 */
122 	(void) did_props_set(tn_hbr, did, ExHB_common_props, ExHB_propcnt - 2);
123 
124 	if (topo_node_range_create(mod, tn_hbr, PCIEX_ROOT, 0,
125 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
126 		topo_mod_dprintf(mod,
127 		    "%s: create child range for %s failed: %s\n",
128 		    f, PCIEX_ROOT, topo_mod_errmsg(mod));
129 		return (-1);
130 	}
131 
132 	hcfmri.hc_name = PCIEX_ROOT;
133 	hcfmri.instance = rci;
134 	rv = x86pi_enum_generic(mod, &hcfmri, tn_hbr, tn_hbr, &tn_rc, 0);
135 	if (rv != 0) {
136 		topo_mod_dprintf(mod, "%s: failed to create %s = %d\n",
137 		    f, PCIEX_ROOT, rci);
138 		return (-1);
139 	}
140 
141 	/*
142 	 * pcibus enumerator requires di_node_t be set in node specific
143 	 */
144 	topo_node_setspecific(tn_rc, rcn);
145 
146 	/*
147 	 * Let did set the RC properties excluding FRU, and label.
148 	 */
149 	if (did_props_set(tn_rc, did, RC_common_props, RC_propcnt - 2) < 0) {
150 		topo_mod_dprintf(mod, "%s: did_props_set failed for %s = %d\n",
151 		    f, PCIEX_ROOT, rci);
152 		topo_node_unbind(tn_rc);
153 		return (-1);
154 	}
155 
156 	if (topo_node_range_create(mod, tn_rc, PCIEX_BUS, 0,
157 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
158 		topo_mod_dprintf(mod,
159 		    "%s: create child range for %s failed: %s\n",
160 		    f, PCIEX_BUS, topo_mod_errmsg(mod));
161 		return (-1);
162 	}
163 
164 	return (topo_mod_enumerate(mod, tn_rc, PCI_BUS, PCIEX_BUS,
165 	    0, MAX_HB_BUSES, did));
166 }
167 
168 static int
169 pci_process(topo_mod_t *mod, tnode_t *tn_hbr, di_node_t bn)
170 {
171 	did_t *did;
172 	tnode_t *tn_bb = topo_node_parent(tn_hbr);
173 
174 	if ((did = did_create(mod, bn, topo_node_instance(tn_bb),
175 	    topo_node_instance(tn_hbr), NO_RC, TRUST_BDF)) == NULL)
176 		return (-1);
177 
178 	/*
179 	 * Let did set the hostbridge properties excluding FRU and label.
180 	 */
181 	(void) did_props_set(tn_hbr, did, HB_common_props, HB_propcnt - 2);
182 
183 	if (topo_node_range_create(mod, tn_hbr, PCI_BUS, 0,
184 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
185 		topo_mod_dprintf(mod, "create child range for %s failed: %s\n",
186 		    PCI_BUS, topo_mod_errmsg(mod));
187 		return (-1);
188 	}
189 
190 	return (topo_mod_enumerate(mod, tn_hbr, PCI_BUS, PCI_BUS,
191 	    0, MAX_HB_BUSES, did));
192 }
193 
194 static int
195 x86pi_gen_pci_pciexrc(topo_mod_t *mod, tnode_t *tn_hbr, uint16_t bdf,
196     topo_instance_t *rcip)
197 {
198 	di_node_t devtree, pnode, cnode;
199 
200 	topo_mod_dprintf(mod, "creating pci/pciexrc node bdf = %#x\n",
201 	    (int)bdf);
202 
203 	devtree = topo_mod_devinfo(mod);
204 	if (devtree == DI_NODE_NIL) {
205 		topo_mod_dprintf(mod, "devinfo init failed.\n");
206 		return (-1);
207 	}
208 
209 	for (pnode = di_drv_first_node(PCI, devtree);
210 	    pnode != DI_NODE_NIL; pnode = di_drv_next_node(pnode))
211 		if (x86pi_bdf(mod, pnode) == bdf)
212 			return (pci_process(mod, tn_hbr, pnode));
213 
214 	pnode = di_drv_first_node(NPE, devtree);
215 	while (pnode != DI_NODE_NIL) {
216 		for (cnode = di_child_node(pnode); cnode != DI_NODE_NIL;
217 		    cnode = di_sibling_node(cnode)) {
218 			if (di_driver_name(cnode) == NULL ||
219 			    x86pi_bdf(mod, cnode) != bdf)
220 				continue;
221 
222 			if (strcmp(di_driver_name(cnode), PCI_PCI) == 0)
223 				return (pci_process(mod, tn_hbr, cnode));
224 
225 			if (strcmp(di_driver_name(cnode), PCIEB) == 0)
226 				return (pciex_process(mod, tn_hbr,
227 				    cnode, (*rcip)++));
228 
229 			topo_mod_dprintf(mod, "no matching driver found: "
230 			    "bdf = %#x\n", (int)bdf);
231 		}
232 		pnode = di_drv_next_node(pnode);
233 	}
234 
235 	topo_mod_dprintf(mod, "no matching bdf found: bdf = %#x\n", (int)bdf);
236 
237 	return (0);
238 }
239 
240 int
241 x86pi_gen_hbr(topo_mod_t *mod, tnode_t *tn_bb, smbios_hdl_t *shp,
242     int hbr_smbid, topo_instance_t hbri, topo_instance_t *rcip)
243 {
244 	x86pi_hcfmri_t	hcfmri = {0};
245 	tnode_t		*tn_hbr;
246 	smbs_cnt_t	*smbc = &stypes[SUN_OEM_PCIEXRC];
247 	smbios_pciexrc_t smb_rc;
248 	int		i, rv, err = 0;
249 	const char	*f = "x86pi_gen_hbr";
250 
251 	hcfmri.hc_name = HOSTBRIDGE;
252 	hcfmri.instance = hbri;
253 
254 	/* create and bind the "hostbridge" node */
255 	rv = x86pi_enum_generic(mod, &hcfmri, tn_bb, tn_bb, &tn_hbr, 0);
256 	if (rv != 0) {
257 		topo_mod_dprintf(mod, "%s: failed to create %s = %d\n",
258 		    f, HOSTBRIDGE, hbri);
259 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
260 	}
261 
262 	/*
263 	 * Walk the smbios records and create the pci/pciexrc nodes
264 	 */
265 	for (i = 0; i < smbc->count; i++) {
266 		if (smbios_info_pciexrc(shp, smbc->ids[i].id, &smb_rc) != 0)
267 			topo_mod_dprintf(mod,
268 			    "%s: failed: id = %d\n", f, (int)smbc->ids[i].id);
269 		else if (smb_rc.smbpcie_bb == hbr_smbid &&
270 		    x86pi_gen_pci_pciexrc(mod, tn_hbr, smb_rc.smbpcie_bdf,
271 		    rcip) != 0)
272 			err++;
273 	}
274 
275 	return (err == 0 ? 0 : topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
276 }
277