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