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