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