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