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