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 *, smbios_hdl_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, shp); 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, shp); 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, smbios_hdl_t *shp) 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 262 if (t_parent == NULL) { 263 topo_mod_dprintf(mod, "%s: NULL parent\n", f); 264 return (-1); 265 } 266 267 /* 268 * "Chassis'" 269 */ 270 /* Type 3 structs */ 271 stypes[SMB_TYPE_CHASSIS].type = SMB_TYPE_CHASSIS; 272 x86pi_smb_strcnt(shp, &stypes[SMB_TYPE_CHASSIS]); 273 274 ch_count = stypes[SMB_TYPE_CHASSIS].count; 275 276 for (nch = 0; nch < ch_count; nch++) { 277 topo_mod_dprintf(mod, "%s: found %d chassis\n", f, 278 stypes[SMB_TYPE_CHASSIS].count); 279 280 ch_smbid = stypes[SMB_TYPE_CHASSIS].ids[nch].id; 281 282 /* 283 * Expect SMBIOS to set the first Chassis Structure to be the 284 * parent/mother of all chassis 285 */ 286 if (nch == 0) 287 motherchassis_node = chassis_node = 288 x86pi_gen_chassis(mod, t_parent, shp, 289 ch_smbid, ch_inst++); 290 else { 291 if (motherchassis_node != NULL) 292 chassis_node = x86pi_gen_chassis(mod, 293 motherchassis_node, shp, 294 ch_smbid, ch_inst++); 295 else 296 chassis_node = x86pi_gen_chassis(mod, 297 t_parent, shp, ch_smbid, ch_inst++); 298 } 299 300 if (chassis_node == NULL) { 301 topo_mod_dprintf(mod, 302 "%s: Failed to create chassis %d\n", f, nch); 303 continue; 304 } 305 stypes[SMB_TYPE_CHASSIS].ids[nch].node = chassis_node; 306 307 /* count SMBIOS extended port connector structures */ 308 smbc = &stypes[SUN_OEM_EXT_PORT]; 309 smbc->type = SUN_OEM_EXT_PORT; 310 x86pi_smb_strcnt(shp, smbc); 311 312 /* 313 * enumerate direct attached SATA disks if we found a 314 * SUN_OEM_EXT_PORT record. 315 */ 316 if (smbc->count > 0) { 317 rv = topo_node_range_create(mod, chassis_node, BAY, 0, 318 smbc->count + 1); 319 if (rv != 0) { 320 topo_mod_dprintf(mod, 321 "%s: Failed to create %s range: %s\n", 322 f, BAY, topo_mod_errmsg(mod)); 323 continue; 324 } 325 } else { 326 topo_mod_dprintf(mod, 327 "Skipping disk bay enumeration\n"); 328 } 329 330 for (i = 0; i < smbc->count; i++) { 331 if (smbios_info_extport(shp, smbc->ids[i].id, 332 &export) != 0) { 333 topo_mod_dprintf(mod, 334 "smbios_info_export failed: id = %d\n", 335 (int)smbc->ids[i].id); 336 continue; 337 } 338 if (export.smbporte_chassis != ch_smbid) 339 continue; 340 341 /* 342 * x86pi_gen_bay: 343 * create "bay" node 344 * call "disk" enum passing in "bay" node 345 */ 346 rv = x86pi_gen_bay(mod, chassis_node, shp, 347 &export, disk_inst); 348 if (rv != 0) 349 topo_mod_dprintf(mod, 350 "Failed to create disk %d\n", i); 351 disk_inst++; 352 } 353 } 354 355 /* 356 * "Base Board" 357 */ 358 /* Type 2 structs */ 359 stypes[SMB_TYPE_BASEBOARD].type = SMB_TYPE_BASEBOARD; 360 x86pi_smb_strcnt(shp, &stypes[SMB_TYPE_BASEBOARD]); 361 bb_count = notvisited = stypes[SMB_TYPE_BASEBOARD].count; 362 363 for (nbb = 0; nbb < bb_count; nbb++) { 364 stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 0; 365 stypes[SMB_TYPE_BASEBOARD].ids[nbb].con_by_id = 0; 366 stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = NULL; 367 } 368 (void) x86pi_bb_contains(mod, shp); 369 370 min = 0; 371 nbb = 0; 372 do { 373 /* 374 * We have reached end of the array due to the 375 * parent-child relationship, without visiting all 376 * baseboards! so re-iterate.. 377 * (or) 378 * All baseboards are visited and their contained 379 * processors are enumerated 380 * (and/or) 381 * More baseboards pending a visit 382 */ 383 if (nbb > bb_count && notvisited) 384 nbb = 0; 385 else if (nbb > bb_count && !notvisited) 386 break; 387 if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited == 388 X86PI_VISITED) { 389 nbb++; 390 continue; 391 } 392 393 /* 394 * Get the Top-most Parent Baseboard, irrespective 395 * of its index in the array of Type-2s 396 * If this Baseboard has no Baseboard parents 397 * place it under the chassis that contains it 398 */ 399 bb_smbid = x86pi_bb_topparent(shp, nbb, &pnode, &psmbid); 400 if (bb_smbid == -1 || pnode == NULL) { 401 topo_mod_dprintf(mod, 402 "Failed to get BaseBoard node (%d): parent\n", 403 nbb); 404 return (-1); 405 } 406 407 if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].id != bb_smbid) { 408 for (int i = 0; i < bb_count; i++) { 409 if (bb_smbid == 410 stypes[SMB_TYPE_BASEBOARD].ids[i].id) { 411 stypes[SMB_TYPE_BASEBOARD].ids[i].\ 412 visited = 1; 413 notvisited--; 414 break; 415 } 416 } 417 } else { 418 stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 1; 419 notvisited--; 420 } 421 422 basebd_node = x86pi_gen_bboard(mod, pnode, shp, 423 bb_smbid, nbb, psmbid); 424 if (basebd_node == NULL) { 425 topo_mod_dprintf(mod, 426 "Failed to create BaseBoard node (%d)\n", nbb); 427 nbb++; 428 continue; 429 } 430 431 stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = basebd_node; 432 /* 433 * Look for contained handles here and if there are 434 * make sure the chip handle below is part of it. 435 */ 436 ncmp = x86pi_bb_getchips(mod, shp, nbb, bb_count); 437 if (ncmp > 0) { 438 max = min + ncmp - 1; 439 /* make sure the chip enum is loaded */ 440 topo_mod_dprintf(mod, "%s: loading chip enum\n", f); 441 442 if (topo_mod_load(mod, CHIP, TOPO_VERSION) == NULL) { 443 topo_mod_dprintf(mod, 444 "%s: Failed to load %s module: %s\n", f, 445 CHIP, topo_strerror(topo_mod_errno(mod))); 446 } else { 447 /* create node range */ 448 topo_mod_dprintf(mod, 449 "%s: chip range %d to %d\n", 450 f, min, max); 451 rv = topo_node_range_create(mod, basebd_node, 452 CHIP, min, max); 453 if (rv != 0) { 454 topo_mod_dprintf(mod, 455 "%s: Failed to create node range: " 456 "%s\n", f, 457 topo_strerror(topo_mod_errno(mod))); 458 } else { 459 /* call the chip enumerator */ 460 topo_mod_dprintf(mod, "%s: calling" 461 " chip enum\n", f); 462 rv = 463 topo_mod_enumerate(mod, basebd_node, 464 CHIP, CHIP, min, max, 465 &x86pi_smbios); 466 min = max + 1; 467 if (rv != 0) 468 topo_mod_dprintf(mod, "%s:%s" 469 "enumeration failed: \n", 470 f, CHIP); 471 } 472 } 473 } 474 475 /* enumerate the hostbridge node */ 476 rv = topo_node_range_create(mod, basebd_node, HOSTBRIDGE, 477 0, 255); 478 if (rv != 0) { 479 topo_mod_dprintf(mod, 480 "%s: Failed to create %s range: %s\n", 481 f, HOSTBRIDGE, topo_mod_errmsg(mod)); 482 continue; 483 } 484 485 smbc = &stypes[SUN_OEM_PCIEXRC]; 486 smbc->type = SUN_OEM_PCIEXRC; 487 x86pi_smb_strcnt(shp, smbc); 488 for (i = 0; i < smbc->count; i++) { 489 if (smbios_info_pciexrc(shp, smbc->ids[i].id, 490 &hbr) != 0) { 491 topo_mod_dprintf(mod, 492 "smbios_info_pciexrc failed: " 493 "id = %d\n", (int)smbc->ids[i].id); 494 continue; 495 } 496 497 if (hbr.smbpcie_bb != bb_smbid) 498 continue; 499 rv = x86pi_gen_hbr(mod, basebd_node, shp, 500 smbc->ids[i].id, hbri, &rci); 501 if (rv != 0) 502 topo_mod_dprintf(mod, 503 "couldn't create hostbridge=%d\n", hbri); 504 hbri++; 505 } 506 nbb++; 507 508 } while (notvisited); 509 510 return (0); 511 } 512