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