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 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 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 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 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 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