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 nvlist_free(fmri); 149 150 (void) topo_pgroup_create(rnode, &rank_pgroup, &err); 151 for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL; 152 nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) { 153 (void) nvprop_add(mod, nvp, PGNAME(RANK), rnode); 154 } 155 rank++; 156 } 157 } 158 159 static void 160 mc_add_dimms(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 161 nvlist_t **nvl, uint_t ndimms, int maxranks) 162 { 163 int i; 164 nvlist_t *fmri; 165 tnode_t *dnode; 166 nvpair_t *nvp; 167 int err; 168 nvlist_t **ranks_nvp; 169 int32_t start_rank = -1; 170 uint_t nranks = 0; 171 char *serial = NULL; 172 char *part = NULL; 173 char *rev = NULL; 174 char *label = NULL; 175 char *name; 176 177 if (topo_node_range_create(mod, pnode, DIMM, 0, ndimms-1) < 0) { 178 whinge(mod, NULL, 179 "mc_add_dimms: node range create failed\n"); 180 return; 181 } 182 for (i = 0; i < ndimms; i++) { 183 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 184 nvp = nvlist_next_nvpair(nvl[i], nvp)) { 185 name = nvpair_name(nvp); 186 if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) { 187 (void) nvpair_value_nvlist_array(nvp, 188 &ranks_nvp, &nranks); 189 } else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) { 190 (void) nvpair_value_int32(nvp, &start_rank); 191 } else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) { 192 (void) nvpair_value_string(nvp, &serial); 193 } else if (strcmp(name, FM_FMRI_HC_PART) == 0) { 194 (void) nvpair_value_string(nvp, &part); 195 } else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) { 196 (void) nvpair_value_string(nvp, &rev); 197 } else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) { 198 (void) nvpair_value_string(nvp, &label); 199 } 200 } 201 fmri = NULL; 202 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 203 DIMM, i, NULL, auth, part, rev, serial); 204 if (fmri == NULL) { 205 whinge(mod, NULL, 206 "mc_add_dimms: topo_mod_hcfmri failed\n"); 207 return; 208 } 209 if ((dnode = topo_node_bind(mod, pnode, DIMM, i, 210 fmri)) == NULL) { 211 nvlist_free(fmri); 212 whinge(mod, NULL, "mc_add_dimms: node bind failed" 213 " for dimm\n"); 214 return; 215 } 216 217 if (topo_method_register(mod, dnode, dimm_methods) < 0) 218 whinge(mod, NULL, "mc_add_dimms: " 219 "topo_method_register failed"); 220 221 (void) topo_node_fru_set(dnode, fmri, 0, &err); 222 nvlist_free(fmri); 223 (void) topo_pgroup_create(dnode, &dimm_pgroup, &err); 224 225 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 226 nvp = nvlist_next_nvpair(nvl[i], nvp)) { 227 name = nvpair_name(nvp); 228 if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 && 229 strcmp(name, FM_FAULT_FRU_LABEL) != 0 && 230 strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) { 231 (void) nvprop_add(mod, nvp, PGNAME(DIMM), 232 dnode); 233 } 234 } 235 if (label) 236 (void) topo_node_label_set(dnode, label, &err); 237 238 if (nranks) { 239 mc_add_ranks(mod, dnode, auth, i, ranks_nvp, start_rank, 240 nranks, serial, part, rev, maxranks); 241 } 242 } 243 } 244 245 static int 246 mc_add_channel(topo_mod_t *mod, tnode_t *pnode, int channel, nvlist_t *auth, 247 nvlist_t *nvl, int maxranks) 248 { 249 tnode_t *mc_channel; 250 nvlist_t *fmri; 251 nvlist_t **dimm_nvl; 252 nvpair_t *nvp; 253 char *name; 254 uint_t ndimms; 255 int err; 256 257 if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) { 258 whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n"); 259 return (-1); 260 } 261 if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel, 262 fmri)) == NULL) { 263 whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n", 264 DRAMCHANNEL); 265 nvlist_free(fmri); 266 return (-1); 267 } 268 (void) topo_node_fru_set(mc_channel, NULL, 0, &err); 269 nvlist_free(fmri); 270 (void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err); 271 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl, 272 &ndimms) == 0) { 273 mc_add_dimms(mod, mc_channel, auth, dimm_nvl, ndimms, maxranks); 274 } 275 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 276 nvp = nvlist_next_nvpair(nvl, nvp)) { 277 name = nvpair_name(nvp); 278 if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) { 279 (void) nvprop_add(mod, nvp, PGNAME(CHAN), 280 mc_channel); 281 } 282 } 283 return (0); 284 } 285 286 static int 287 mc_nb_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth, 288 nvlist_t *nvl) 289 { 290 int err; 291 int i, j; 292 int channel; 293 uint8_t nmc; 294 uint8_t maxranks; 295 tnode_t *mcnode; 296 nvlist_t *fmri; 297 nvlist_t **channel_nvl; 298 nvpair_t *nvp; 299 char *pname; 300 uint_t nchannels; 301 302 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl, 303 &nchannels) != 0) { 304 whinge(mod, NULL, 305 "mc_nb_create: failed to find channel information\n"); 306 return (-1); 307 } 308 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) { 309 /* 310 * Assume channels are evenly divided among the controllers. 311 * Convert nchannels to channels per controller 312 */ 313 nchannels = nchannels / nmc; 314 } else { 315 /* 316 * if number of memory controllers is not specified then there 317 * are two channels per controller and the nchannels is total 318 * we will set up nmc as number of controllers and convert 319 * nchannels to channels per controller 320 */ 321 nmc = nchannels / 2; 322 nchannels = nchannels / nmc; 323 } 324 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0) 325 maxranks = 2; 326 if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) { 327 whinge(mod, NULL, 328 "mc_nb_create: node range create failed\n"); 329 return (-1); 330 } 331 channel = 0; 332 for (i = 0; i < nmc; i++) { 333 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { 334 whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n"); 335 return (-1); 336 } 337 if ((mcnode = topo_node_bind(mod, pnode, name, i, 338 fmri)) == NULL) { 339 whinge(mod, NULL, "mc_nb_create: node bind failed" 340 " for memory-controller\n"); 341 nvlist_free(fmri); 342 return (-1); 343 } 344 345 (void) topo_node_fru_set(mcnode, NULL, 0, &err); 346 nvlist_free(fmri); 347 (void) topo_pgroup_create(mcnode, &mc_pgroup, &err); 348 349 if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel, 350 channel + nchannels - 1) < 0) { 351 whinge(mod, NULL, 352 "mc_nb_create: channel node range create failed\n"); 353 return (-1); 354 } 355 for (j = 0; j < nchannels; j++) { 356 if (mc_add_channel(mod, mcnode, channel, auth, 357 channel_nvl[channel], maxranks) < 0) { 358 return (-1); 359 } 360 channel++; 361 } 362 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 363 nvp = nvlist_next_nvpair(nvl, nvp)) { 364 pname = nvpair_name(nvp); 365 if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 && 366 strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 && 367 strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 && 368 strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 && 369 strcmp(pname, MCINTEL_NVLIST_MEM) != 0) { 370 (void) nvprop_add(mod, nvp, PGNAME(MCT), 371 mcnode); 372 } 373 } 374 } 375 376 return (NULL); 377 } 378 379 int 380 mc_node_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 381 nvlist_t *auth) 382 { 383 mc_snapshot_info_t mcs; 384 void *buf = NULL; 385 nvlist_t *nvl; 386 uint8_t ver; 387 int rc; 388 389 if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 390 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 391 ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) { 392 393 whinge(mod, NULL, "mc failed to snapshot %s\n", 394 strerror(errno)); 395 396 free(buf); 397 (void) close(mc_fd); 398 return (NULL); 399 } 400 (void) close(mc_fd); 401 (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 402 topo_mod_free(mod, buf, mcs.mcs_size); 403 404 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) { 405 whinge(mod, NULL, "mc nvlist is not versioned\n"); 406 nvlist_free(nvl); 407 return (NULL); 408 } else if (ver != MCINTEL_NVLIST_VERS0) { 409 whinge(mod, NULL, "mc nvlist version mismatch\n"); 410 nvlist_free(nvl); 411 return (NULL); 412 } 413 414 rc = mc_nb_create(mod, pnode, name, auth, nvl); 415 416 nvlist_free(nvl); 417 return (rc); 418 } 419 420 void 421 onchip_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 422 nvlist_t *auth) 423 { 424 if (mc_onchip(topo_node_instance(pnode))) 425 (void) mc_node_create(mod, pnode, name, auth); 426 } 427 428 int 429 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 430 nvlist_t *auth) 431 { 432 return (mc_node_create(mod, pnode, name, auth)); 433 } 434