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