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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * x86 Generic FMA Topology Enumerator 29 */ 30 31 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <sys/types.h> 35 #include <strings.h> 36 #include <sys/fcntl.h> 37 #include <fm/topo_mod.h> 38 #include <fm/topo_hc.h> 39 #include <sys/systeminfo.h> 40 #include <sys/smbios.h> 41 #include <sys/smbios_impl.h> 42 #include <sys/fm/protocol.h> 43 #include <x86pi_impl.h> 44 45 46 static int x86pi_enum_start(topo_mod_t *, x86pi_enum_t *); 47 static int x86pi_enum_gentopo(topo_mod_t *, tnode_t *, smbios_hdl_t *); 48 49 /* 50 * Entry point called by libtopo when enumeration is required 51 */ 52 static topo_enum_f x86pi_enum; /* libtopo enumeration entry point */ 53 54 /* 55 * Declare the operations vector and information structure used during 56 * module registration 57 */ 58 static topo_modops_t x86pi_ops = 59 { x86pi_enum, NULL }; 60 61 static topo_modinfo_t x86pi_modinfo = 62 { X86PI_DESC, X86PI_SCHEME, X86PI_VERSION, &x86pi_ops }; 63 64 /* 65 * Used to pass SMBIOS' FM compatibility to the 66 * chip enumerator 67 */ 68 int x86pi_smbios = 0; 69 70 /* 71 * Called by libtopo when the topo module is loaded. 72 */ 73 int 74 _topo_init(topo_mod_t *mod, topo_version_t version) 75 { 76 int result; 77 char isa[MAXNAMELEN]; 78 79 if (getenv("TOPOX86PIDBG") != NULL) { 80 /* Debugging is requested for this module */ 81 topo_mod_setdebug(mod); 82 } 83 topo_mod_dprintf(mod, "module initializing.\n"); 84 85 if (version != TOPO_VERSION) { 86 (void) topo_mod_seterrno(mod, EMOD_VER_NEW); 87 topo_mod_dprintf(mod, "incompatible topo version %d\n", 88 version); 89 return (-1); 90 } 91 92 /* Verify that this is a i86pc architecture machine */ 93 (void) sysinfo(SI_MACHINE, isa, MAXNAMELEN); 94 if (strncmp(isa, "i86pc", MAXNAMELEN) != 0) { 95 topo_mod_dprintf(mod, "not i86pc architecture: %s\n", isa); 96 return (-1); 97 } 98 99 result = topo_mod_register(mod, &x86pi_modinfo, TOPO_VERSION); 100 if (result < 0) { 101 topo_mod_dprintf(mod, "registration failed: %s\n", 102 topo_mod_errmsg(mod)); 103 /* module errno already set */ 104 return (-1); 105 } 106 topo_mod_dprintf(mod, "module ready.\n"); 107 return (0); 108 } 109 110 111 /* 112 * Clean up any data used by the module before it is unloaded. 113 */ 114 void 115 _topo_fini(topo_mod_t *mod) 116 { 117 topo_mod_dprintf(mod, "module finishing.\n"); 118 119 x86pi_hbr_enum_fini(mod); 120 121 /* Unregister from libtopo */ 122 topo_mod_unregister(mod); 123 } 124 125 126 /* 127 * Enumeration entry point for the x86 Generic topology enumerator 128 */ 129 /* ARGSUSED */ 130 static int 131 x86pi_enum(topo_mod_t *mod, tnode_t *t_parent, const char *name, 132 topo_instance_t min, topo_instance_t max, void *pi_private, void *data) 133 { 134 int result; 135 hrtime_t starttime; 136 x86pi_enum_t x86pi; 137 138 /* Begin enumeration */ 139 starttime = gethrtime(); 140 topo_mod_dprintf(mod, "enumeration starting.\n"); 141 142 /* 143 * Let's do some enumeration. 144 */ 145 bzero(&x86pi, sizeof (x86pi_enum_t)); 146 x86pi.t_parent = t_parent; 147 result = x86pi_enum_start(mod, &x86pi); 148 if (result != 0) { 149 topo_mod_dprintf(mod, "Enumeration failed.\n"); 150 return (-1); 151 } 152 153 /* Complete enumeration */ 154 topo_mod_dprintf(mod, "enumeration complete in %lld ms.\n", 155 ((gethrtime() - starttime)/MICROSEC)); 156 157 /* All done */ 158 return (result); 159 } 160 161 static int 162 x86pi_enum_start(topo_mod_t *mod, x86pi_enum_t *x86pi) 163 { 164 int rv; 165 int complvl = 0; 166 smbios_hdl_t *shp; 167 char *f = "x86pi_enum_start"; 168 169 /* 170 * Verify BIOS compliance. 171 */ 172 shp = x86pi_smb_open(mod); 173 if (shp == NULL) { 174 topo_mod_dprintf(mod, "%s: failed to open SMBIOS\n", f); 175 complvl = X86PI_NONE; 176 } else { 177 complvl = x86pi_check_comp(mod, shp); 178 } 179 180 topo_mod_dprintf(mod, "%s: SMBIOS x86pi compliance: %s\n", f, 181 complvl == X86PI_FULL ? "FULL" : "NONE"); 182 183 if (complvl == X86PI_NONE) { 184 /* fall back to legacy enumeration */ 185 topo_mod_dprintf(mod, 186 "%s: Calling legacy enumeration\n", f); 187 188 return (topo_mod_enummap(mod, x86pi->t_parent, 189 "i86pc-legacy", FM_FMRI_SCHEME_HC)); 190 } 191 192 x86pi->priv = (void *)shp; 193 x86pi_smbios = complvl; 194 195 if (x86pi_hbr_enum_init(mod) < 0) { 196 topo_mod_dprintf(mod, "%s: x86pi_hbr_enum_init() failed.\n", f); 197 return (-1); 198 } 199 200 /* 201 * Create the topology. 202 */ 203 fac_done = 0; 204 rv = x86pi_enum_gentopo(mod, x86pi->t_parent, shp); 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 topo_instance_t hbri = 0, rci = 0; 257 smbios_pciexrc_t hbr; 258 char *f = "x86pi_enum_gentopo"; 259 260 if (t_parent == NULL) { 261 topo_mod_dprintf(mod, "%s: NULL parent\n", f); 262 return (-1); 263 } 264 265 /* 266 * "Chassis'" 267 */ 268 /* Type 3 structs */ 269 stypes[SMB_TYPE_CHASSIS].type = SMB_TYPE_CHASSIS; 270 x86pi_smb_strcnt(shp, &stypes[SMB_TYPE_CHASSIS]); 271 272 ch_count = stypes[SMB_TYPE_CHASSIS].count; 273 274 for (nch = 0; nch < ch_count; nch++) { 275 topo_mod_dprintf(mod, "%s: found %d chassis\n", f, 276 stypes[SMB_TYPE_CHASSIS].count); 277 278 ch_smbid = stypes[SMB_TYPE_CHASSIS].ids[nch].id; 279 280 /* 281 * Expect SMBIOS to set the first Chassis Structure to be the 282 * parent/mother of all chassis 283 */ 284 if (nch == 0) 285 motherchassis_node = chassis_node = 286 x86pi_gen_chassis(mod, t_parent, shp, 287 ch_smbid, ch_inst++); 288 else { 289 if (motherchassis_node != NULL) 290 chassis_node = x86pi_gen_chassis(mod, 291 motherchassis_node, shp, 292 ch_smbid, ch_inst++); 293 else 294 chassis_node = x86pi_gen_chassis(mod, 295 t_parent, shp, ch_smbid, ch_inst++); 296 } 297 298 if (chassis_node == NULL) { 299 topo_mod_dprintf(mod, 300 "%s: Failed to create chassis %d\n", f, nch); 301 continue; 302 } 303 stypes[SMB_TYPE_CHASSIS].ids[nch].node = chassis_node; 304 } 305 306 /* 307 * "Base Board" 308 */ 309 /* Type 2 structs */ 310 stypes[SMB_TYPE_BASEBOARD].type = SMB_TYPE_BASEBOARD; 311 x86pi_smb_strcnt(shp, &stypes[SMB_TYPE_BASEBOARD]); 312 bb_count = notvisited = stypes[SMB_TYPE_BASEBOARD].count; 313 314 for (nbb = 0; nbb < bb_count; nbb++) { 315 stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 0; 316 stypes[SMB_TYPE_BASEBOARD].ids[nbb].con_by_id = 0; 317 stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = NULL; 318 } 319 (void) x86pi_bb_contains(mod, shp); 320 321 min = 0; 322 nbb = 0; 323 do { 324 /* 325 * We have reached end of the array due to the 326 * parent-child relationship, without visiting all 327 * baseboards! so re-iterate.. 328 * (or) 329 * All baseboards are visited and their contained 330 * processors are enumerated 331 * (and/or) 332 * More baseboards pending a visit 333 */ 334 if (nbb > bb_count && notvisited) 335 nbb = 0; 336 else if (nbb > bb_count && !notvisited) 337 break; 338 if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited == 339 X86PI_VISITED) { 340 nbb++; 341 continue; 342 } 343 344 /* 345 * Get the Top-most Parent Baseboard, irrespective 346 * of its index in the array of Type-2s 347 * If this Baseboard has no Baseboard parents 348 * place it under the chassis that contains it 349 */ 350 bb_smbid = x86pi_bb_topparent(shp, nbb, &pnode, &psmbid); 351 if (bb_smbid == -1 || pnode == NULL) { 352 topo_mod_dprintf(mod, 353 "Failed to get BaseBoard node (%d): parent\n", 354 nbb); 355 return (-1); 356 } 357 358 if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].id != bb_smbid) { 359 for (int i = 0; i < bb_count; i++) { 360 if (bb_smbid == 361 stypes[SMB_TYPE_BASEBOARD].ids[i].id) { 362 stypes[SMB_TYPE_BASEBOARD].ids[i].\ 363 visited = 1; 364 notvisited--; 365 break; 366 } 367 } 368 } else { 369 stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 1; 370 notvisited--; 371 } 372 373 basebd_node = x86pi_gen_bboard(mod, pnode, shp, 374 bb_smbid, nbb, psmbid); 375 if (basebd_node == NULL) { 376 topo_mod_dprintf(mod, 377 "Failed to create BaseBoard node (%d)\n", nbb); 378 nbb++; 379 continue; 380 } 381 382 stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = basebd_node; 383 /* 384 * Look for contained handles here and if there are 385 * make sure the chip handle below is part of it. 386 */ 387 ncmp = x86pi_bb_getchips(mod, shp, nbb, bb_count); 388 if (ncmp > 0) { 389 max = min + ncmp - 1; 390 /* make sure the chip enum is loaded */ 391 topo_mod_dprintf(mod, "%s: loading chip enum\n", f); 392 393 if (topo_mod_load(mod, CHIP, TOPO_VERSION) == NULL) { 394 topo_mod_dprintf(mod, 395 "%s: Failed to load %s module: %s\n", f, 396 CHIP, topo_strerror(topo_mod_errno(mod))); 397 } else { 398 /* create node range */ 399 topo_mod_dprintf(mod, 400 "%s: chip range %d to %d\n", 401 f, min, max); 402 rv = topo_node_range_create(mod, basebd_node, 403 CHIP, min, max); 404 if (rv != 0) { 405 topo_mod_dprintf(mod, 406 "%s: Failed to create node range: " 407 "%s\n", f, 408 topo_strerror(topo_mod_errno(mod))); 409 } else { 410 /* call the chip enumerator */ 411 topo_mod_dprintf(mod, "%s: calling" 412 " chip enum\n", f); 413 rv = 414 topo_mod_enumerate(mod, basebd_node, 415 CHIP, CHIP, min, max, 416 &x86pi_smbios); 417 min = max + 1; 418 if (rv != 0) 419 topo_mod_dprintf(mod, "%s:%s" 420 "enumeration failed: \n", 421 f, CHIP); 422 } 423 } 424 } 425 426 /* enumerate the hostbridge node */ 427 rv = topo_node_range_create(mod, basebd_node, HOSTBRIDGE, 428 0, 255); 429 if (rv != 0) { 430 topo_mod_dprintf(mod, 431 "%s: Failed to create %s range: %s\n", 432 f, HOSTBRIDGE, topo_mod_errmsg(mod)); 433 continue; 434 } 435 436 smbc = &stypes[SUN_OEM_PCIEXRC]; 437 smbc->type = SUN_OEM_PCIEXRC; 438 x86pi_smb_strcnt(shp, smbc); 439 for (i = 0; i < smbc->count; i++) { 440 if (smbios_info_pciexrc(shp, smbc->ids[i].id, 441 &hbr) != 0) { 442 topo_mod_dprintf(mod, 443 "smbios_info_pciexrc failed: " 444 "id = %d\n", (int)smbc->ids[i].id); 445 continue; 446 } 447 448 if (hbr.smbpcie_bb != bb_smbid) 449 continue; 450 rv = x86pi_gen_hbr(mod, basebd_node, shp, 451 smbc->ids[i].id, hbri, &rci); 452 if (rv != 0) 453 topo_mod_dprintf(mod, 454 "couldn't create hostbridge=%d\n", hbri); 455 hbri++; 456 } 457 nbb++; 458 459 } while (notvisited); 460 461 return (0); 462 } 463