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