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
_topo_init(topo_mod_t * mod,topo_version_t version)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
_topo_fini(topo_mod_t * mod)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
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)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
x86pi_enum_start(topo_mod_t * mod,x86pi_enum_t * x86pi)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
x86pi_enum_gentopo(topo_mod_t * mod,tnode_t * t_parent)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