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 #include <unistd.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdarg.h> 31 #include <string.h> 32 #include <strings.h> 33 #include <limits.h> 34 #include <alloca.h> 35 #include <kstat.h> 36 #include <fcntl.h> 37 #include <errno.h> 38 #include <libnvpair.h> 39 #include <sys/types.h> 40 #include <sys/bitmap.h> 41 #include <sys/processor.h> 42 #include <sys/param.h> 43 #include <sys/fm/protocol.h> 44 #include <sys/systeminfo.h> 45 #include <sys/mc.h> 46 #include <sys/mc_amd.h> 47 #include <sys/mc_intel.h> 48 #include <fm/topo_mod.h> 49 50 #include "chip.h" 51 52 #ifndef MAX 53 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 54 #endif 55 56 static const topo_pgroup_info_t dimm_channel_pgroup = 57 { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 58 static const topo_pgroup_info_t dimm_pgroup = 59 { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 60 static const topo_pgroup_info_t rank_pgroup = 61 { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 62 static const topo_pgroup_info_t mc_pgroup = 63 { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 64 65 static const topo_method_t dimm_methods[] = { 66 { SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, 67 simple_dimm_label}, 68 { SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL, 69 simple_dimm_label_mp}, 70 { SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, 71 seq_dimm_label}, 72 { NULL } 73 }; 74 75 extern const topo_method_t rank_methods[]; 76 extern const topo_method_t ntv_page_retire_methods[]; 77 78 static int mc_fd; 79 80 int 81 mc_offchip_open() 82 { 83 mc_fd = open("/dev/mc/mc", O_RDONLY); 84 return (mc_fd != -1); 85 } 86 87 static int 88 mc_onchip(topo_instance_t id) 89 { 90 char path[64]; 91 92 (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); 93 mc_fd = open(path, O_RDONLY); 94 return (mc_fd != -1); 95 } 96 97 static void 98 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm, 99 nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part, 100 char *rev, int maxranks) 101 { 102 int i; 103 int rank; 104 tnode_t *rnode; 105 nvpair_t *nvp; 106 nvlist_t *fmri; 107 int err = 0; 108 109 /* 110 * If start_rank is defined, it is assigned to the first rank of this 111 * dimm. 112 */ 113 rank = start_rank >= 0 ? start_rank : dimm * maxranks; 114 if (topo_node_range_create(mod, dnode, RANK, rank, 115 rank + nranks - 1) < 0) { 116 whinge(mod, NULL, "mc_add_ranks: node range create failed" 117 " for rank\n"); 118 return; 119 } 120 for (i = 0; i < nranks; i++) { 121 fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION, 122 RANK, rank, NULL, auth, part, rev, serial); 123 if (fmri == NULL) { 124 whinge(mod, NULL, 125 "mc_add_ranks: topo_mod_hcfmri failed\n"); 126 return; 127 } 128 if ((rnode = topo_node_bind(mod, dnode, RANK, rank, 129 fmri)) == NULL) { 130 nvlist_free(fmri); 131 whinge(mod, NULL, "mc_add_ranks: node bind failed" 132 " for ranks\n"); 133 return; 134 } 135 (void) topo_node_fru_set(rnode, NULL, 0, &err); 136 137 if (topo_method_register(mod, rnode, rank_methods) < 0) 138 whinge(mod, &err, "rank_create: " 139 "topo_method_register failed"); 140 141 if (! is_xpv() && topo_method_register(mod, rnode, 142 ntv_page_retire_methods) < 0) 143 whinge(mod, &err, "mc_add_ranks: " 144 "topo_method_register failed"); 145 146 (void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err); 147 148 if (FM_AWARE_SMBIOS(mod)) 149 (void) topo_node_label_set(rnode, NULL, &err); 150 151 nvlist_free(fmri); 152 153 (void) topo_pgroup_create(rnode, &rank_pgroup, &err); 154 for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL; 155 nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) { 156 (void) nvprop_add(mod, nvp, PGNAME(RANK), rnode); 157 } 158 rank++; 159 } 160 } 161 162 static void 163 mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 164 nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks) 165 { 166 int i; 167 nvlist_t *fmri; 168 tnode_t *dnode; 169 nvpair_t *nvp; 170 int err; 171 nvlist_t **ranks_nvp; 172 int32_t start_rank = -1; 173 uint_t nranks = 0; 174 uint32_t dimm_number; 175 char *serial = NULL; 176 char *part = NULL; 177 char *rev = NULL; 178 char *label = NULL; 179 char *name; 180 id_t smbid; 181 182 if (topo_node_range_create(mod, pnode, DIMM, 0, 183 maxdimms ? maxdimms-1 : ndimms-1) < 0) { 184 whinge(mod, NULL, 185 "mc_add_dimms: node range create failed\n"); 186 return; 187 } 188 for (i = 0; i < ndimms; i++) { 189 dimm_number = i; 190 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 191 nvp = nvlist_next_nvpair(nvl[i], nvp)) { 192 name = nvpair_name(nvp); 193 if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) { 194 (void) nvpair_value_nvlist_array(nvp, 195 &ranks_nvp, &nranks); 196 } else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) { 197 (void) nvpair_value_int32(nvp, &start_rank); 198 } else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) { 199 (void) nvpair_value_string(nvp, &serial); 200 } else if (strcmp(name, FM_FMRI_HC_PART) == 0) { 201 (void) nvpair_value_string(nvp, &part); 202 } else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) { 203 (void) nvpair_value_string(nvp, &rev); 204 } else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) { 205 (void) nvpair_value_string(nvp, &label); 206 } else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) { 207 (void) nvpair_value_uint32(nvp, &dimm_number); 208 } 209 } 210 fmri = NULL; 211 212 if (FM_AWARE_SMBIOS(mod)) { 213 int channum; 214 215 channum = topo_node_instance(pnode); 216 smbid = memnode_to_smbiosid(chip_smbid, DIMM_NODE_NAME, 217 i, &channum); 218 if (serial == NULL) 219 serial = (char *)chip_serial_smbios_get(mod, 220 smbid); 221 if (part == NULL) 222 part = (char *)chip_part_smbios_get(mod, 223 smbid); 224 if (rev == NULL) 225 rev = (char *)chip_rev_smbios_get(mod, 226 smbid); 227 } 228 229 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 230 DIMM, dimm_number, NULL, auth, part, rev, serial); 231 if (fmri == NULL) { 232 whinge(mod, NULL, 233 "mc_add_dimms: topo_mod_hcfmri failed\n"); 234 return; 235 } 236 if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number, 237 fmri)) == NULL) { 238 nvlist_free(fmri); 239 whinge(mod, NULL, "mc_add_dimms: node bind failed" 240 " for dimm\n"); 241 return; 242 } 243 244 if (!FM_AWARE_SMBIOS(mod)) 245 if (topo_method_register(mod, dnode, dimm_methods) < 0) 246 whinge(mod, NULL, "mc_add_dimms: " 247 "topo_method_register failed"); 248 249 (void) topo_pgroup_create(dnode, &dimm_pgroup, &err); 250 251 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 252 nvp = nvlist_next_nvpair(nvl[i], nvp)) { 253 name = nvpair_name(nvp); 254 if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 && 255 strcmp(name, FM_FAULT_FRU_LABEL) != 0 && 256 strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) { 257 (void) nvprop_add(mod, nvp, PGNAME(DIMM), 258 dnode); 259 } 260 } 261 262 if (FM_AWARE_SMBIOS(mod)) { 263 nvlist_free(fmri); 264 (void) topo_node_resource(dnode, &fmri, &err); 265 /* 266 * We will use a full absolute parent/child label 267 */ 268 label = (char *)chip_label_smbios_get(mod, 269 pnode, smbid, label); 270 } 271 272 (void) topo_node_label_set(dnode, label, &err); 273 274 if (FM_AWARE_SMBIOS(mod)) 275 topo_mod_strfree(mod, label); 276 277 (void) topo_node_fru_set(dnode, fmri, 0, &err); 278 (void) topo_node_asru_set(dnode, fmri, 0, &err); 279 nvlist_free(fmri); 280 281 if (nranks) { 282 mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp, 283 start_rank, nranks, serial, part, rev, maxranks); 284 } 285 } 286 } 287 288 static int 289 mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 290 int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks) 291 { 292 tnode_t *mc_channel; 293 nvlist_t *fmri; 294 nvlist_t **dimm_nvl; 295 nvpair_t *nvp; 296 char *name; 297 uint_t ndimms; 298 int err; 299 300 if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) { 301 whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n"); 302 return (-1); 303 } 304 if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel, 305 fmri)) == NULL) { 306 whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n", 307 DRAMCHANNEL); 308 nvlist_free(fmri); 309 return (-1); 310 } 311 (void) topo_node_fru_set(mc_channel, NULL, 0, &err); 312 nvlist_free(fmri); 313 (void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err); 314 315 if (FM_AWARE_SMBIOS(mod)) 316 (void) topo_node_label_set(mc_channel, NULL, &err); 317 318 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl, 319 &ndimms) == 0) { 320 mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl, 321 ndimms, maxdimms, maxranks); 322 } 323 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 324 nvp = nvlist_next_nvpair(nvl, nvp)) { 325 name = nvpair_name(nvp); 326 if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) { 327 (void) nvprop_add(mod, nvp, PGNAME(CHAN), 328 mc_channel); 329 } 330 } 331 332 return (0); 333 } 334 335 static int 336 mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 337 const char *name, nvlist_t *auth, nvlist_t *nvl) 338 { 339 int err; 340 int i, j; 341 int channel; 342 uint8_t nmc; 343 uint8_t maxranks; 344 uint8_t maxdimms; 345 tnode_t *mcnode; 346 nvlist_t *fmri; 347 nvlist_t **channel_nvl; 348 nvpair_t *nvp; 349 char *pname; 350 uint_t nchannels; 351 352 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl, 353 &nchannels) != 0) { 354 whinge(mod, NULL, 355 "mc_nb_create: failed to find channel information\n"); 356 return (-1); 357 } 358 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) { 359 /* 360 * Assume channels are evenly divided among the controllers. 361 * Convert nchannels to channels per controller 362 */ 363 nchannels = nchannels / nmc; 364 } else { 365 /* 366 * if number of memory controllers is not specified then there 367 * are two channels per controller and the nchannels is total 368 * we will set up nmc as number of controllers and convert 369 * nchannels to channels per controller 370 */ 371 nmc = nchannels / 2; 372 nchannels = nchannels / nmc; 373 } 374 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0) 375 maxranks = 2; 376 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0) 377 maxdimms = 0; 378 if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) { 379 whinge(mod, NULL, 380 "mc_nb_create: node range create failed\n"); 381 return (-1); 382 } 383 channel = 0; 384 for (i = 0; i < nmc; i++) { 385 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { 386 whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n"); 387 return (-1); 388 } 389 if ((mcnode = topo_node_bind(mod, pnode, name, i, 390 fmri)) == NULL) { 391 whinge(mod, NULL, "mc_nb_create: node bind failed" 392 " for memory-controller\n"); 393 nvlist_free(fmri); 394 return (-1); 395 } 396 397 (void) topo_node_fru_set(mcnode, NULL, 0, &err); 398 nvlist_free(fmri); 399 (void) topo_pgroup_create(mcnode, &mc_pgroup, &err); 400 401 if (FM_AWARE_SMBIOS(mod)) 402 (void) topo_node_label_set(mcnode, NULL, &err); 403 404 if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel, 405 channel + nchannels - 1) < 0) { 406 whinge(mod, NULL, 407 "mc_nb_create: channel node range create failed\n"); 408 return (-1); 409 } 410 for (j = 0; j < nchannels; j++) { 411 if (mc_add_channel(mod, chip_smbid, mcnode, channel, 412 auth, channel_nvl[channel], maxdimms, 413 maxranks) < 0) { 414 return (-1); 415 } 416 channel++; 417 } 418 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 419 nvp = nvlist_next_nvpair(nvl, nvp)) { 420 pname = nvpair_name(nvp); 421 if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 && 422 strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 && 423 strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 && 424 strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 && 425 strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 && 426 strcmp(pname, MCINTEL_NVLIST_MEM) != 0) { 427 (void) nvprop_add(mod, nvp, PGNAME(MCT), 428 mcnode); 429 } 430 } 431 } 432 433 return (NULL); 434 } 435 436 int 437 mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 438 const char *name, nvlist_t *auth) 439 { 440 mc_snapshot_info_t mcs; 441 void *buf = NULL; 442 nvlist_t *nvl; 443 uint8_t ver; 444 int rc; 445 446 if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 447 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 448 ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) { 449 450 whinge(mod, NULL, "mc failed to snapshot %s\n", 451 strerror(errno)); 452 453 free(buf); 454 (void) close(mc_fd); 455 return (NULL); 456 } 457 (void) close(mc_fd); 458 (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 459 topo_mod_free(mod, buf, mcs.mcs_size); 460 461 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) { 462 whinge(mod, NULL, "mc nvlist is not versioned\n"); 463 nvlist_free(nvl); 464 return (NULL); 465 } else if (ver != MCINTEL_NVLIST_VERS0) { 466 whinge(mod, NULL, "mc nvlist version mismatch\n"); 467 nvlist_free(nvl); 468 return (NULL); 469 } 470 471 rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl); 472 473 nvlist_free(nvl); 474 return (rc); 475 } 476 477 void 478 onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 479 const char *name, nvlist_t *auth) 480 { 481 if (mc_onchip(topo_node_instance(pnode))) 482 (void) mc_node_create(mod, chip_smbid, pnode, name, auth); 483 } 484 485 int 486 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 487 nvlist_t *auth) 488 { 489 return (mc_node_create(mod, IGNORE_ID, pnode, name, auth)); 490 } 491