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