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