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