xref: /illumos-gate/usr/src/lib/fm/topo/modules/i86pc/x86pi/x86pi.c (revision 56f33205c9ed776c3c909e07d52e94610a675740)
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  * x86 Generic FMA Topology Enumerator
29  */
30 
31 
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <strings.h>
36 #include <sys/fcntl.h>
37 #include <fm/topo_mod.h>
38 #include <fm/topo_hc.h>
39 #include <sys/systeminfo.h>
40 #include <sys/smbios.h>
41 #include <sys/smbios_impl.h>
42 #include <sys/fm/protocol.h>
43 #include <x86pi_impl.h>
44 
45 
46 static int x86pi_enum_start(topo_mod_t *, x86pi_enum_t *);
47 static int x86pi_enum_gentopo(topo_mod_t *, tnode_t *, smbios_hdl_t *);
48 
49 /*
50  * Entry point called by libtopo when enumeration is required
51  */
52 static topo_enum_f x86pi_enum;	/* libtopo enumeration entry point */
53 
54 /*
55  * Declare the operations vector and information structure used during
56  * module registration
57  */
58 static topo_modops_t x86pi_ops =
59 	{ x86pi_enum, NULL };
60 
61 static topo_modinfo_t	x86pi_modinfo =
62 	{ X86PI_DESC, X86PI_SCHEME, X86PI_VERSION, &x86pi_ops };
63 
64 /*
65  * Used to pass SMBIOS' FM compatibility to the
66  * chip enumerator
67  */
68 int x86pi_smbios = 0;
69 
70 /*
71  * Called by libtopo when the topo module is loaded.
72  */
73 int
74 _topo_init(topo_mod_t *mod, topo_version_t version)
75 {
76 	int	result;
77 	char	isa[MAXNAMELEN];
78 
79 	if (getenv("TOPOX86PIDBG") != NULL) {
80 		/* Debugging is requested for this module */
81 		topo_mod_setdebug(mod);
82 	}
83 	topo_mod_dprintf(mod, "module initializing.\n");
84 
85 	if (version != TOPO_VERSION) {
86 		(void) topo_mod_seterrno(mod, EMOD_VER_NEW);
87 		topo_mod_dprintf(mod, "incompatible topo version %d\n",
88 		    version);
89 		return (-1);
90 	}
91 
92 	/* Verify that this is a i86pc architecture machine */
93 	(void) sysinfo(SI_MACHINE, isa, MAXNAMELEN);
94 	if (strncmp(isa, "i86pc", MAXNAMELEN) != 0) {
95 		topo_mod_dprintf(mod, "not i86pc architecture: %s\n", isa);
96 		return (-1);
97 	}
98 
99 	result = topo_mod_register(mod, &x86pi_modinfo, TOPO_VERSION);
100 	if (result < 0) {
101 		topo_mod_dprintf(mod, "registration failed: %s\n",
102 		    topo_mod_errmsg(mod));
103 		/* module errno already set */
104 		return (-1);
105 	}
106 	topo_mod_dprintf(mod, "module ready.\n");
107 	return (0);
108 }
109 
110 
111 /*
112  * Clean up any data used by the module before it is unloaded.
113  */
114 void
115 _topo_fini(topo_mod_t *mod)
116 {
117 	topo_mod_dprintf(mod, "module finishing.\n");
118 
119 	x86pi_hbr_enum_fini(mod);
120 
121 	/* Unregister from libtopo */
122 	topo_mod_unregister(mod);
123 }
124 
125 
126 /*
127  * Enumeration entry point for the x86 Generic topology enumerator
128  */
129 /* ARGSUSED */
130 static int
131 x86pi_enum(topo_mod_t *mod, tnode_t *t_parent, const char *name,
132     topo_instance_t min, topo_instance_t max, void *pi_private, void *data)
133 {
134 	int		result;
135 	hrtime_t	starttime;
136 	x86pi_enum_t	x86pi;
137 
138 	/* Begin enumeration */
139 	starttime = gethrtime();
140 	topo_mod_dprintf(mod, "enumeration starting.\n");
141 
142 	/*
143 	 * Let's do some enumeration.
144 	 */
145 	bzero(&x86pi, sizeof (x86pi_enum_t));
146 	x86pi.t_parent = t_parent;
147 	result = x86pi_enum_start(mod, &x86pi);
148 	if (result != 0) {
149 		topo_mod_dprintf(mod, "Enumeration failed.\n");
150 		return (-1);
151 	}
152 
153 	/* Complete enumeration */
154 	topo_mod_dprintf(mod, "enumeration complete in %lld ms.\n",
155 	    ((gethrtime() - starttime)/MICROSEC));
156 
157 	/* All done */
158 	return (result);
159 }
160 
161 static int
162 x86pi_enum_start(topo_mod_t *mod, x86pi_enum_t *x86pi)
163 {
164 	int		rv;
165 	int		complvl = 0;
166 	smbios_hdl_t	*shp;
167 	char		*f = "x86pi_enum_start";
168 
169 	/*
170 	 * Verify BIOS compliance.
171 	 */
172 	shp = x86pi_smb_open(mod);
173 	if (shp == NULL) {
174 		topo_mod_dprintf(mod, "%s: failed to open SMBIOS\n", f);
175 		complvl = X86PI_NONE;
176 	} else {
177 		complvl = x86pi_check_comp(mod, shp);
178 	}
179 
180 	topo_mod_dprintf(mod, "%s: SMBIOS x86pi compliance: %s\n", f,
181 	    complvl == X86PI_FULL ? "FULL" : "NONE");
182 
183 	if (complvl == X86PI_NONE) {
184 		/* fall back to legacy enumeration */
185 		topo_mod_dprintf(mod,
186 		    "%s: Calling legacy enumeration\n", f);
187 
188 		return (topo_mod_enummap(mod, x86pi->t_parent,
189 		    "i86pc-legacy", FM_FMRI_SCHEME_HC));
190 	}
191 
192 	x86pi->priv = (void *)shp;
193 	x86pi_smbios = complvl;
194 
195 	if (x86pi_hbr_enum_init(mod) < 0) {
196 		topo_mod_dprintf(mod, "%s: x86pi_hbr_enum_init() failed.\n", f);
197 		return (-1);
198 	}
199 
200 	/*
201 	 * Create the topology.
202 	 */
203 	fac_done = 0;
204 	rv = x86pi_enum_gentopo(mod, x86pi->t_parent, shp);
205 	if (rv != 0) {
206 		return (-1);
207 	}
208 	x86pi->mod = mod;
209 
210 	if (fac_done == 0) {
211 		(void) topo_mod_enummap(mod, x86pi->t_parent, "chassis",
212 		    FM_FMRI_SCHEME_HC);
213 		(void) topo_mod_enummap(mod, x86pi->t_parent, "fan",
214 		    FM_FMRI_SCHEME_HC);
215 		(void) topo_mod_enummap(mod, x86pi->t_parent, "psu",
216 		    FM_FMRI_SCHEME_HC);
217 	}
218 
219 	/* All done */
220 	topo_mod_dprintf(mod, "%s: done.\n", f);
221 	return (rv);
222 }
223 
224 /*
225  * Create the i86pc topology
226  *
227  * If either Type 2 or Type 3 structures have contained elements/handles,
228  * walk them creating the topo.
229  *
230  * If there are no contained elements/handles, build this topo:
231  *
232  *    Main Chassis
233  *      Motherboard
234  *        CMP Chip/Core/Strands
235  *          Memory Controllers/Memory Devices (DIMMs)
236  *        PCIE HostBrige
237  *          PCIE Root Complex
238  *
239  */
240 static int
241 x86pi_enum_gentopo(topo_mod_t *mod, tnode_t *t_parent, smbios_hdl_t *shp)
242 {
243 	int		rv;
244 	int		nch, nbb, ncmp, i;
245 	int		ch_smbid, bb_smbid;
246 	tnode_t		*chassis_node = NULL;
247 	tnode_t		*basebd_node = NULL;
248 	smbs_cnt_t	*smbc;
249 	tnode_t		*motherchassis_node = NULL;
250 	tnode_t		*pnode = NULL;
251 	id_t		psmbid;
252 	int		notvisited;
253 	int		bb_count, ch_count;
254 	int		min, max;
255 	int		ch_inst = 0;
256 	topo_instance_t	 hbri = 0, rci = 0;
257 	smbios_pciexrc_t hbr;
258 	char		*f = "x86pi_enum_gentopo";
259 
260 	if (t_parent == NULL) {
261 		topo_mod_dprintf(mod, "%s: NULL parent\n", f);
262 		return (-1);
263 	}
264 
265 	/*
266 	 * "Chassis'"
267 	 */
268 	/* Type 3 structs */
269 	stypes[SMB_TYPE_CHASSIS].type = SMB_TYPE_CHASSIS;
270 	x86pi_smb_strcnt(shp, &stypes[SMB_TYPE_CHASSIS]);
271 
272 	ch_count = stypes[SMB_TYPE_CHASSIS].count;
273 
274 	for (nch = 0; nch < ch_count; nch++) {
275 		topo_mod_dprintf(mod, "%s: found %d chassis\n", f,
276 		    stypes[SMB_TYPE_CHASSIS].count);
277 
278 		ch_smbid = stypes[SMB_TYPE_CHASSIS].ids[nch].id;
279 
280 		/*
281 		 * Expect SMBIOS to set the first Chassis Structure to be the
282 		 * parent/mother of all chassis
283 		 */
284 		if (nch == 0)
285 			motherchassis_node = chassis_node =
286 			    x86pi_gen_chassis(mod, t_parent, shp,
287 			    ch_smbid, ch_inst++);
288 		else {
289 			if (motherchassis_node != NULL)
290 				chassis_node = x86pi_gen_chassis(mod,
291 				    motherchassis_node, shp,
292 				    ch_smbid, ch_inst++);
293 			else
294 				chassis_node = x86pi_gen_chassis(mod,
295 				    t_parent, shp, ch_smbid, ch_inst++);
296 		}
297 
298 		if (chassis_node == NULL) {
299 			topo_mod_dprintf(mod,
300 			    "%s: Failed to create chassis %d\n", f, nch);
301 			continue;
302 		}
303 		stypes[SMB_TYPE_CHASSIS].ids[nch].node = chassis_node;
304 	}
305 
306 	/*
307 	 * "Base Board"
308 	 */
309 	/* Type 2 structs */
310 	stypes[SMB_TYPE_BASEBOARD].type = SMB_TYPE_BASEBOARD;
311 	x86pi_smb_strcnt(shp, &stypes[SMB_TYPE_BASEBOARD]);
312 	bb_count = notvisited = stypes[SMB_TYPE_BASEBOARD].count;
313 
314 	for (nbb = 0; nbb < bb_count; nbb++) {
315 		stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 0;
316 		stypes[SMB_TYPE_BASEBOARD].ids[nbb].con_by_id = 0;
317 		stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = NULL;
318 	}
319 	(void) x86pi_bb_contains(mod, shp);
320 
321 	min = 0;
322 	nbb = 0;
323 	do {
324 		/*
325 		 * We have reached end of the array due to the
326 		 * parent-child relationship, without visiting all
327 		 * baseboards! so re-iterate..
328 		 * (or)
329 		 * All baseboards are visited and their contained
330 		 * processors are enumerated
331 		 * (and/or)
332 		 * More baseboards pending a visit
333 		 */
334 		if (nbb > bb_count && notvisited)
335 			nbb = 0;
336 		else if (nbb > bb_count && !notvisited)
337 			break;
338 		if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited ==
339 		    X86PI_VISITED) {
340 			nbb++;
341 			continue;
342 		}
343 
344 		/*
345 		 * Get the Top-most Parent Baseboard, irrespective
346 		 * of its index in the array of Type-2s
347 		 * If this Baseboard has no Baseboard parents
348 		 * place it under the chassis that contains it
349 		 */
350 		bb_smbid = x86pi_bb_topparent(shp, nbb, &pnode, &psmbid);
351 		if (bb_smbid == -1 || pnode == NULL) {
352 			topo_mod_dprintf(mod,
353 			    "Failed to get BaseBoard node (%d): parent\n",
354 			    nbb);
355 			return (-1);
356 		}
357 
358 		if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].id != bb_smbid) {
359 			for (int i = 0; i < bb_count; i++) {
360 				if (bb_smbid ==
361 				    stypes[SMB_TYPE_BASEBOARD].ids[i].id) {
362 					stypes[SMB_TYPE_BASEBOARD].ids[i].\
363 					    visited = 1;
364 					notvisited--;
365 					break;
366 				}
367 			}
368 		} else {
369 			stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 1;
370 			notvisited--;
371 		}
372 
373 		basebd_node = x86pi_gen_bboard(mod, pnode, shp,
374 		    bb_smbid, nbb, psmbid);
375 		if (basebd_node == NULL) {
376 			topo_mod_dprintf(mod,
377 			    "Failed to create BaseBoard node (%d)\n", nbb);
378 			nbb++;
379 			continue;
380 		}
381 
382 		stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = basebd_node;
383 		/*
384 		 * Look for contained handles here and if there are
385 		 * make sure the chip handle below is part of it.
386 		 */
387 		ncmp = x86pi_bb_getchips(mod, shp, nbb, bb_count);
388 		if (ncmp > 0) {
389 			max = min + ncmp - 1;
390 			/* make sure the chip enum is loaded */
391 			topo_mod_dprintf(mod, "%s: loading chip enum\n", f);
392 
393 			if (topo_mod_load(mod, CHIP, TOPO_VERSION) == NULL) {
394 				topo_mod_dprintf(mod,
395 				    "%s: Failed to load %s module: %s\n", f,
396 				    CHIP, topo_strerror(topo_mod_errno(mod)));
397 			} else {
398 				/* create node range */
399 				topo_mod_dprintf(mod,
400 				    "%s: chip range %d to %d\n",
401 				    f, min, max);
402 				rv = topo_node_range_create(mod, basebd_node,
403 				    CHIP, min, max);
404 				if (rv != 0) {
405 					topo_mod_dprintf(mod,
406 					    "%s: Failed to create node range: "
407 					    "%s\n", f,
408 					    topo_strerror(topo_mod_errno(mod)));
409 				} else {
410 					/* call the chip enumerator */
411 					topo_mod_dprintf(mod, "%s: calling"
412 					    " chip enum\n", f);
413 					rv =
414 					    topo_mod_enumerate(mod, basebd_node,
415 					    CHIP, CHIP, min, max,
416 					    &x86pi_smbios);
417 					min = max + 1;
418 					if (rv != 0)
419 						topo_mod_dprintf(mod, "%s:%s"
420 						    "enumeration failed: \n",
421 						    f, CHIP);
422 				}
423 			}
424 		}
425 
426 		/* enumerate the hostbridge node */
427 		rv = topo_node_range_create(mod, basebd_node, HOSTBRIDGE,
428 		    0, 255);
429 		if (rv != 0) {
430 			topo_mod_dprintf(mod,
431 			    "%s: Failed to create %s range: %s\n",
432 			    f, HOSTBRIDGE, topo_mod_errmsg(mod));
433 			continue;
434 		}
435 
436 		smbc = &stypes[SUN_OEM_PCIEXRC];
437 		smbc->type = SUN_OEM_PCIEXRC;
438 		x86pi_smb_strcnt(shp, smbc);
439 		for (i = 0; i < smbc->count; i++) {
440 			if (smbios_info_pciexrc(shp, smbc->ids[i].id,
441 			    &hbr) != 0) {
442 				topo_mod_dprintf(mod,
443 				    "smbios_info_pciexrc failed: "
444 				    "id = %d\n", (int)smbc->ids[i].id);
445 				continue;
446 			}
447 
448 			if (hbr.smbpcie_bb != bb_smbid)
449 				continue;
450 			rv = x86pi_gen_hbr(mod, basebd_node, shp,
451 			    smbc->ids[i].id, hbri, &rci);
452 			if (rv != 0)
453 				topo_mod_dprintf(mod,
454 				    "couldn't create hostbridge=%d\n", hbri);
455 			hbri++;
456 		}
457 		nbb++;
458 
459 	} while (notvisited);
460 
461 	return (0);
462 }
463