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