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 * Copyright 2019 Joyent, Inc. 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 /* 57 * These are names that we use for properties. In the v0 scheme we try to pull 58 * these directly from the memory controller (which also causes a lot more 59 * variation). In the v1 scheme we translate to the name that topo wants to use, 60 * regardless of what the parent is called. 61 */ 62 #define MC_PROP_ECC "memory-ecc" 63 #define MC_PROP_POLICY "memory-policy" 64 #define CHAN_PROP_MODE "channel-mode" 65 #define DIMM_SIZE "size" 66 #define DIMM_STRING_SIZE "dimm-size" 67 #define DIMM_COL "ncolumn" 68 #define DIMM_ROW "nrow" 69 #define DIMM_DENSITY "density" 70 #define DIMM_WIDTH "width" 71 #define DIMM_RANKS "ranks" 72 #define DIMM_BANKS "nbanks" 73 #define DIMM_HDRL "hdrl-enabled" 74 #define DIMM_HDRL_PARITY "hdrl-parity" 75 #define DIMM_3DRANK "3d-subranks" 76 #define RANK_STATUS "dimm-rank-status" 77 #define RANK_SIZE "dimm-rank-size" 78 79 static const topo_pgroup_info_t dimm_channel_pgroup = 80 { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 81 static const topo_pgroup_info_t dimm_pgroup = 82 { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 83 static const topo_pgroup_info_t rank_pgroup = 84 { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 85 static const topo_pgroup_info_t mc_pgroup = 86 { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 87 88 static const topo_method_t dimm_methods[] = { 89 { SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, 90 simple_dimm_label}, 91 { SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL, 92 simple_dimm_label_mp}, 93 { SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, 94 seq_dimm_label}, 95 { NULL } 96 }; 97 98 extern const topo_method_t rank_methods[]; 99 extern const topo_method_t ntv_page_retire_methods[]; 100 101 static int mc_fd; 102 103 int 104 mc_offchip_open() 105 { 106 mc_fd = open("/dev/mc/mc", O_RDONLY); 107 return (mc_fd != -1); 108 } 109 110 static int 111 mc_onchip(topo_instance_t id) 112 { 113 char path[64]; 114 115 (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); 116 mc_fd = open(path, O_RDONLY); 117 return (mc_fd != -1); 118 } 119 120 static void 121 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm, 122 nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part, 123 char *rev, int maxranks) 124 { 125 int i; 126 int rank; 127 tnode_t *rnode; 128 nvpair_t *nvp; 129 nvlist_t *fmri; 130 int err = 0; 131 132 /* 133 * If start_rank is defined, it is assigned to the first rank of this 134 * dimm. 135 */ 136 rank = start_rank >= 0 ? start_rank : dimm * maxranks; 137 if (topo_node_range_create(mod, dnode, RANK, rank, 138 rank + nranks - 1) < 0) { 139 whinge(mod, NULL, "mc_add_ranks: node range create failed" 140 " for rank\n"); 141 return; 142 } 143 for (i = 0; i < nranks; i++) { 144 fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION, 145 RANK, rank, NULL, auth, part, rev, serial); 146 if (fmri == NULL) { 147 whinge(mod, NULL, 148 "mc_add_ranks: topo_mod_hcfmri failed\n"); 149 return; 150 } 151 if ((rnode = topo_node_bind(mod, dnode, RANK, rank, 152 fmri)) == NULL) { 153 nvlist_free(fmri); 154 whinge(mod, NULL, "mc_add_ranks: node bind failed" 155 " for ranks\n"); 156 return; 157 } 158 (void) topo_node_fru_set(rnode, NULL, 0, &err); 159 160 if (topo_method_register(mod, rnode, rank_methods) < 0) 161 whinge(mod, &err, "mc_add_ranks: " 162 "topo_method_register failed"); 163 164 if (! is_xpv() && topo_method_register(mod, rnode, 165 ntv_page_retire_methods) < 0) 166 whinge(mod, &err, "mc_add_ranks: " 167 "topo_method_register failed"); 168 169 (void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err); 170 171 if (FM_AWARE_SMBIOS(mod)) 172 (void) topo_node_label_set(rnode, NULL, &err); 173 174 nvlist_free(fmri); 175 176 (void) topo_pgroup_create(rnode, &rank_pgroup, &err); 177 for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL; 178 nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) { 179 (void) nvprop_add(mod, nvp, PGNAME(RANK), rnode); 180 } 181 rank++; 182 } 183 } 184 185 static void 186 mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 187 nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks) 188 { 189 int i; 190 nvlist_t *fmri; 191 tnode_t *dnode; 192 nvpair_t *nvp; 193 int err; 194 nvlist_t **ranks_nvp; 195 int32_t start_rank = -1; 196 uint_t nranks = 0; 197 uint32_t dimm_number; 198 char *serial = NULL; 199 char *part = NULL; 200 char *rev = NULL; 201 char *label = NULL; 202 char *name; 203 id_t smbid = -1; 204 205 if (topo_node_range_create(mod, pnode, DIMM, 0, 206 maxdimms ? maxdimms-1 : ndimms-1) < 0) { 207 whinge(mod, NULL, 208 "mc_add_dimms: node range create failed\n"); 209 return; 210 } 211 for (i = 0; i < ndimms; i++) { 212 dimm_number = i; 213 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 214 nvp = nvlist_next_nvpair(nvl[i], nvp)) { 215 name = nvpair_name(nvp); 216 if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) { 217 (void) nvpair_value_nvlist_array(nvp, 218 &ranks_nvp, &nranks); 219 } else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) { 220 (void) nvpair_value_int32(nvp, &start_rank); 221 } else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) { 222 (void) nvpair_value_string(nvp, &serial); 223 } else if (strcmp(name, FM_FMRI_HC_PART) == 0) { 224 (void) nvpair_value_string(nvp, &part); 225 } else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) { 226 (void) nvpair_value_string(nvp, &rev); 227 } else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) { 228 (void) nvpair_value_string(nvp, &label); 229 } else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) { 230 (void) nvpair_value_uint32(nvp, &dimm_number); 231 } 232 } 233 fmri = NULL; 234 235 if (FM_AWARE_SMBIOS(mod)) { 236 int channum; 237 238 channum = topo_node_instance(pnode); 239 smbid = memnode_to_smbiosid(mod, chip_smbid, 240 DIMM_NODE_NAME, i, &channum); 241 if (serial == NULL) 242 serial = (char *)chip_serial_smbios_get(mod, 243 smbid); 244 if (part == NULL) 245 part = (char *)chip_part_smbios_get(mod, 246 smbid); 247 if (rev == NULL) 248 rev = (char *)chip_rev_smbios_get(mod, 249 smbid); 250 } 251 252 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 253 DIMM, dimm_number, NULL, auth, part, rev, serial); 254 if (fmri == NULL) { 255 whinge(mod, NULL, 256 "mc_add_dimms: topo_mod_hcfmri failed\n"); 257 return; 258 } 259 if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number, 260 fmri)) == NULL) { 261 nvlist_free(fmri); 262 whinge(mod, NULL, "mc_add_dimms: node bind failed" 263 " for dimm\n"); 264 return; 265 } 266 267 if (!FM_AWARE_SMBIOS(mod)) 268 if (topo_method_register(mod, dnode, dimm_methods) < 0) 269 whinge(mod, NULL, "mc_add_dimms: " 270 "topo_method_register failed"); 271 272 (void) topo_pgroup_create(dnode, &dimm_pgroup, &err); 273 274 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 275 nvp = nvlist_next_nvpair(nvl[i], nvp)) { 276 name = nvpair_name(nvp); 277 if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 && 278 strcmp(name, FM_FAULT_FRU_LABEL) != 0 && 279 strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) { 280 (void) nvprop_add(mod, nvp, PGNAME(DIMM), 281 dnode); 282 } 283 } 284 285 if (FM_AWARE_SMBIOS(mod)) { 286 nvlist_free(fmri); 287 (void) topo_node_resource(dnode, &fmri, &err); 288 /* 289 * We will use a full absolute parent/child label 290 */ 291 label = (char *)chip_label_smbios_get(mod, 292 pnode, smbid, label); 293 } 294 295 (void) topo_node_label_set(dnode, label, &err); 296 297 if (FM_AWARE_SMBIOS(mod)) 298 topo_mod_strfree(mod, label); 299 300 (void) topo_node_fru_set(dnode, fmri, 0, &err); 301 (void) topo_node_asru_set(dnode, fmri, 0, &err); 302 nvlist_free(fmri); 303 304 if (nranks) { 305 mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp, 306 start_rank, nranks, serial, part, rev, maxranks); 307 } 308 } 309 } 310 311 static int 312 mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 313 int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks) 314 { 315 tnode_t *mc_channel; 316 nvlist_t *fmri; 317 nvlist_t **dimm_nvl; 318 nvpair_t *nvp; 319 char *name; 320 uint_t ndimms; 321 int err; 322 323 if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) { 324 whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n"); 325 return (-1); 326 } 327 if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel, 328 fmri)) == NULL) { 329 whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n", 330 DRAMCHANNEL); 331 nvlist_free(fmri); 332 return (-1); 333 } 334 (void) topo_node_fru_set(mc_channel, NULL, 0, &err); 335 nvlist_free(fmri); 336 (void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err); 337 338 if (FM_AWARE_SMBIOS(mod)) 339 (void) topo_node_label_set(mc_channel, NULL, &err); 340 341 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl, 342 &ndimms) == 0) { 343 mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl, 344 ndimms, maxdimms, maxranks); 345 } 346 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 347 nvp = nvlist_next_nvpair(nvl, nvp)) { 348 name = nvpair_name(nvp); 349 if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) { 350 (void) nvprop_add(mod, nvp, PGNAME(CHAN), 351 mc_channel); 352 } 353 } 354 355 return (0); 356 } 357 358 static int 359 mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 360 const char *name, nvlist_t *auth, nvlist_t *nvl) 361 { 362 int err; 363 int i, j; 364 int channel; 365 uint8_t nmc; 366 uint8_t maxranks; 367 uint8_t maxdimms; 368 tnode_t *mcnode; 369 nvlist_t *fmri; 370 nvlist_t **channel_nvl; 371 nvpair_t *nvp; 372 char *pname; 373 uint_t nchannels; 374 375 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl, 376 &nchannels) != 0) { 377 whinge(mod, NULL, 378 "mc_nb_create: failed to find channel information\n"); 379 return (-1); 380 } 381 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) { 382 /* 383 * Assume channels are evenly divided among the controllers. 384 * Convert nchannels to channels per controller 385 */ 386 nchannels = nchannels / nmc; 387 } else { 388 /* 389 * if number of memory controllers is not specified then there 390 * are two channels per controller and the nchannels is total 391 * we will set up nmc as number of controllers and convert 392 * nchannels to channels per controller 393 */ 394 nmc = nchannels / 2; 395 nchannels = nchannels / nmc; 396 } 397 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0) 398 maxranks = 2; 399 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0) 400 maxdimms = 0; 401 if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) { 402 whinge(mod, NULL, 403 "mc_nb_create: node range create failed\n"); 404 return (-1); 405 } 406 channel = 0; 407 for (i = 0; i < nmc; i++) { 408 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { 409 whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n"); 410 return (-1); 411 } 412 if ((mcnode = topo_node_bind(mod, pnode, name, i, 413 fmri)) == NULL) { 414 whinge(mod, NULL, "mc_nb_create: node bind failed" 415 " for memory-controller\n"); 416 nvlist_free(fmri); 417 return (-1); 418 } 419 420 (void) topo_node_fru_set(mcnode, NULL, 0, &err); 421 nvlist_free(fmri); 422 (void) topo_pgroup_create(mcnode, &mc_pgroup, &err); 423 424 if (FM_AWARE_SMBIOS(mod)) 425 (void) topo_node_label_set(mcnode, NULL, &err); 426 427 if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel, 428 channel + nchannels - 1) < 0) { 429 whinge(mod, NULL, 430 "mc_nb_create: channel node range create failed\n"); 431 return (-1); 432 } 433 for (j = 0; j < nchannels; j++) { 434 if (mc_add_channel(mod, chip_smbid, mcnode, channel, 435 auth, channel_nvl[channel], maxdimms, 436 maxranks) < 0) { 437 return (-1); 438 } 439 channel++; 440 } 441 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 442 nvp = nvlist_next_nvpair(nvl, nvp)) { 443 pname = nvpair_name(nvp); 444 if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 && 445 strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 && 446 strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 && 447 strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 && 448 strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 && 449 strcmp(pname, MCINTEL_NVLIST_MEM) != 0) { 450 (void) nvprop_add(mod, nvp, PGNAME(MCT), 451 mcnode); 452 } 453 } 454 } 455 456 return (0); 457 } 458 459 static int 460 mc_rank_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 461 nvlist_t *dimm_nvl, uint64_t rsize, uint32_t id) 462 { 463 nvlist_t *fmri; 464 tnode_t *rank; 465 int err; 466 boolean_t *disabled; 467 uint_t ndisabled; 468 const char *status; 469 470 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, RANK, id, 471 NULL, auth, NULL, NULL, NULL); 472 if (fmri == NULL) { 473 whinge(mod, NULL, "mc_rank_create_v1: topo_mod_hcfmri " 474 "failed\n"); 475 return (-1); 476 } 477 478 if ((rank = topo_node_bind(mod, pnode, RANK, id, fmri)) == NULL) { 479 whinge(mod, NULL, "mc_rank_create_v1: node bind failed for " 480 "DIMM\n"); 481 nvlist_free(fmri); 482 return (-1); 483 } 484 485 if (topo_method_register(mod, rank, rank_methods) < 0) { 486 whinge(mod, NULL, "mc_rank_create_v1: topo_method_register " 487 "failed for rank_methods: %d", topo_mod_errno(mod)); 488 } 489 490 if (!is_xpv() && topo_method_register(mod, rank, 491 ntv_page_retire_methods) != 0) { 492 whinge(mod, NULL, "mc_rank_create_v1: topo_method_register " 493 "failed for page retire: %d", topo_mod_errno(mod)); 494 } 495 496 if (topo_node_asru_set(rank, fmri, TOPO_ASRU_COMPUTE, &err) != 0) { 497 whinge(mod, NULL, "mc_rank_create_v1: failed to set asru: %d", 498 err); 499 nvlist_free(fmri); 500 return (topo_mod_seterrno(mod, err)); 501 } 502 503 if (topo_node_fru_set(rank, NULL, 0, &err) != 0) { 504 whinge(mod, NULL, "mc_rank_create_v1: fru set failed: " 505 "%d\n", err); 506 nvlist_free(fmri); 507 return (topo_mod_seterrno(mod, err)); 508 } 509 nvlist_free(fmri); 510 511 if (topo_pgroup_create(rank, &rank_pgroup, &err) != 0) { 512 whinge(mod, NULL, "mc_rank_create_v1: failed to create " 513 "property group: %d\n", err); 514 return (topo_mod_seterrno(mod, err)); 515 } 516 517 /* 518 * The traditional northbridge driver broke down each rank into the 519 * interleave targets that led to it. At this time, the imc driver (the 520 * only v1 provider) does not supply that information and therefore we 521 * cannot set that. Instead we just set basic properties on this, the 522 * size of the rank and whether or not it is disabled. 523 */ 524 if (rsize != 0 && topo_prop_set_uint64(rank, PGNAME(RANK), RANK_SIZE, 525 TOPO_PROP_IMMUTABLE, rsize, &err) != 0) { 526 whinge(mod, NULL, "mc_rank_create_v1: failed to set %s " 527 "property: %d", RANK_SIZE, err); 528 return (topo_mod_seterrno(mod, err)); 529 } 530 531 if (nvlist_lookup_boolean_array(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RDIS, 532 &disabled, &ndisabled) != 0) { 533 whinge(mod, NULL, "mc_rank_create_v1: Couldn't find disabled " 534 "ranks array"); 535 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 536 } 537 538 if (id >= ndisabled) { 539 whinge(mod, NULL, "mc_rank_create_v1: Found rank %u with id " 540 "larger than supported by hardware", id); 541 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 542 } 543 544 status = disabled[id] ? "disabled" : "enabled"; 545 if (topo_prop_set_string(rank, PGNAME(RANK), RANK_STATUS, 546 TOPO_PROP_IMMUTABLE, status, &err) != 0) { 547 whinge(mod, NULL, "mc_rank_create_v1: failed to set %s " 548 "property: %d", RANK_STATUS, err); 549 return (topo_mod_seterrno(mod, err)); 550 } 551 552 return (0); 553 } 554 555 static int 556 mc_dimm_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 557 nvlist_t *dimm_nvl, uint_t id) 558 { 559 int err, ret; 560 tnode_t *dimm; 561 nvlist_t *fmri; 562 boolean_t present; 563 uint64_t size, density, rsize; 564 uint32_t cols, rows, width, ranks, banks, i; 565 566 /* 567 * First, figure out if this DIMM is present. If not, we don't bother 568 * creating anything. 569 */ 570 if (nvlist_lookup_boolean_value(dimm_nvl, 571 MCINTEL_NVLIST_V1_DIMM_PRESENT, &present) != 0) { 572 return (-1); 573 } else if (!present) { 574 return (0); 575 } 576 577 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, DIMM, id, 578 NULL, auth, NULL, NULL, NULL); 579 if (fmri == NULL) { 580 whinge(mod, NULL, "mc_dimm_create_v1: topo_mod_hcfmri " 581 "failed\n"); 582 return (-1); 583 } 584 585 if ((dimm = topo_node_bind(mod, pnode, DIMM, id, fmri)) == NULL) { 586 whinge(mod, NULL, "mc_dimm_create_v1: node bind failed for " 587 "DIMM\n"); 588 nvlist_free(fmri); 589 return (-1); 590 } 591 592 if (topo_node_fru_set(dimm, NULL, 0, &err) != 0) { 593 whinge(mod, NULL, "mc_dimm_create_v1: fru set failed: " 594 "%d\n", err); 595 nvlist_free(fmri); 596 return (topo_mod_seterrno(mod, err)); 597 } 598 nvlist_free(fmri); 599 600 if (topo_pgroup_create(dimm, &dimm_pgroup, &err) != 0) { 601 whinge(mod, NULL, "mc_dimm_create_v1: failed to create " 602 "property group: %d\n", err); 603 return (topo_mod_seterrno(mod, err)); 604 } 605 606 ret = 0; 607 if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_SIZE, 608 &size) == 0) { 609 char buf[64]; 610 const char *suffix; 611 uint64_t tsize; 612 ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_SIZE, 613 TOPO_PROP_IMMUTABLE, size, &err); 614 tsize = size; 615 616 /* 617 * We must manually cons up a dimm-size property which is the 618 * size of the dimm as a round number with a prefix such as M or 619 * G. This is used by Intel CPU eversholt rules. Older memory 620 * controller drivers did this in the driver, but we instead opt 621 * to do so in user land. 622 */ 623 if (tsize >= (1ULL << 40)) { 624 tsize /= (1ULL << 40); 625 suffix = "T"; 626 } else if (tsize >= (1ULL << 30)) { 627 tsize /= (1ULL << 30); 628 suffix = "G"; 629 } else if (tsize >= (1ULL << 20)) { 630 tsize /= (1ULL << 20); 631 suffix = "M"; 632 } else { 633 suffix = NULL; 634 } 635 636 if (suffix != NULL) { 637 if (snprintf(buf, sizeof (buf), "%"PRIu64"%s", tsize, 638 suffix) >= sizeof (buf)) { 639 whinge(mod, NULL, "failed to construct DIMM " 640 "size due to buffer overflow"); 641 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 642 } 643 ret = topo_prop_set_string(dimm, PGNAME(DIMM), 644 DIMM_STRING_SIZE, TOPO_PROP_IMMUTABLE, buf, &err); 645 if (ret != 0) { 646 return (topo_mod_seterrno(mod, err)); 647 } 648 } 649 } else { 650 size = 0; 651 } 652 653 if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS, 654 &cols) == 0) { 655 ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_COL, 656 TOPO_PROP_IMMUTABLE, cols, &err); 657 if (ret != 0) { 658 return (topo_mod_seterrno(mod, err)); 659 } 660 } 661 662 if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NROWS, 663 &rows) == 0) { 664 ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_ROW, 665 TOPO_PROP_IMMUTABLE, rows, &err); 666 if (ret != 0) { 667 return (topo_mod_seterrno(mod, err)); 668 } 669 } 670 671 if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY, 672 &density) == 0) { 673 ret = topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_DENSITY, 674 TOPO_PROP_IMMUTABLE, density, &err); 675 if (ret != 0) { 676 return (topo_mod_seterrno(mod, err)); 677 } 678 } 679 680 if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH, 681 &width) == 0) { 682 ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_WIDTH, 683 TOPO_PROP_IMMUTABLE, width, &err); 684 if (ret != 0) { 685 return (topo_mod_seterrno(mod, err)); 686 } 687 } 688 689 if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_BANKS, 690 &banks) == 0) { 691 ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_BANKS, 692 TOPO_PROP_IMMUTABLE, banks, &err); 693 if (ret != 0) { 694 return (topo_mod_seterrno(mod, err)); 695 } 696 } else { 697 banks = 0; 698 } 699 700 if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RANKS, 701 &ranks) == 0) { 702 ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_RANKS, 703 TOPO_PROP_IMMUTABLE, ranks, &err); 704 if (ret != 0) { 705 return (topo_mod_seterrno(mod, err)); 706 } 707 } 708 709 if (ret != 0) { 710 return (-1); 711 } 712 713 if (topo_node_range_create(mod, dimm, RANK, 0, ranks - 1) < 0) { 714 whinge(mod, NULL, "mc_dimm_create_v1: rank node range " 715 "create failed\n"); 716 return (-1); 717 } 718 719 rsize = 0; 720 if (size != 0 && banks != 0) { 721 rsize = size / banks; 722 } 723 724 for (i = 0; i < ranks; i++) { 725 if (mc_rank_create_v1(mod, dimm, auth, dimm_nvl, rsize, i) != 726 0) { 727 return (-1); 728 } 729 } 730 731 return (0); 732 } 733 734 static int 735 mc_channel_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 736 nvlist_t *chan_nvl, uint_t id, const char *cmode) 737 { 738 int err; 739 tnode_t *chnode; 740 nvlist_t *fmri; 741 nvlist_t **dimms; 742 uint_t ndimms, i; 743 744 if (mkrsrc(mod, pnode, DRAMCHANNEL, id, auth, &fmri) != 0) { 745 whinge(mod, NULL, "mc_channel_create_v1: mkrsrc failed\n"); 746 return (-1); 747 } 748 749 if ((chnode = topo_node_bind(mod, pnode, DRAMCHANNEL, id, fmri)) == 750 NULL) { 751 whinge(mod, NULL, "mc_channel_create_v1: node bind failed" 752 " for dram-channel\n"); 753 nvlist_free(fmri); 754 return (-1); 755 } 756 757 nvlist_free(fmri); 758 if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) { 759 whinge(mod, NULL, "mc_channel_create_v1: fru set failed: " 760 "%d\n", err); 761 return (topo_mod_seterrno(mod, err)); 762 } 763 764 if (topo_pgroup_create(chnode, &dimm_channel_pgroup, &err) != 0) { 765 whinge(mod, NULL, "mc_channel_create_v1: failed to create " 766 "property group: %d\n", err); 767 return (topo_mod_seterrno(mod, err)); 768 } 769 770 if (topo_prop_set_string(chnode, PGNAME(CHAN), CHAN_PROP_MODE, 771 TOPO_PROP_IMMUTABLE, cmode, &err) != 0) { 772 return (topo_mod_seterrno(mod, err)); 773 } 774 775 if (nvlist_lookup_nvlist_array(chan_nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS, 776 &dimms, &ndimms) != 0) { 777 whinge(mod, NULL, "mc_channel_create_v1: No DIMMS provided"); 778 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 779 } 780 781 if (topo_node_range_create(mod, chnode, DIMM, 0, ndimms - 1) < 0) { 782 whinge(mod, NULL, "mc_channel_create_v1: dimm node range " 783 "create failed\n"); 784 return (-1); 785 } 786 787 for (i = 0; i < ndimms; i++) { 788 if (mc_dimm_create_v1(mod, chnode, auth, dimms[i], i) != 0) 789 return (-1); 790 } 791 792 return (0); 793 } 794 795 static int 796 mc_imc_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name, 797 nvlist_t *auth, nvlist_t *mc_nvl, uint_t id) 798 { 799 int err, ret; 800 tnode_t *mcnode; 801 nvlist_t *fmri, **channels; 802 boolean_t ecc; 803 char *page, *cmode; 804 uint_t nchans, i; 805 806 if (mkrsrc(mod, pnode, name, id, auth, &fmri) != 0) { 807 whinge(mod, NULL, "mc_imc_create_v1: mkrsrc failed\n"); 808 return (-1); 809 } 810 811 if ((mcnode = topo_node_bind(mod, pnode, name, id, fmri)) == NULL) { 812 whinge(mod, NULL, "mc_imc_create_v1: node bind failed" 813 " for memory-controller\n"); 814 nvlist_free(fmri); 815 return (-1); 816 } 817 818 nvlist_free(fmri); 819 if (topo_node_fru_set(mcnode, NULL, 0, &err) != 0) { 820 whinge(mod, NULL, "mc_imc_create_v1: fru set failed: " 821 "%d\n", err); 822 return (topo_mod_seterrno(mod, err)); 823 } 824 825 if (topo_pgroup_create(mcnode, &mc_pgroup, &err) != 0) { 826 whinge(mod, NULL, "mc_imc_create_v1: failed to create " 827 "property group: %d\n", err); 828 return (topo_mod_seterrno(mod, err)); 829 } 830 831 /* 832 * Add properties to the controller. Our contract allows for these 833 * properties to be missing. 834 */ 835 ret = 0; 836 if (nvlist_lookup_boolean_value(mc_nvl, MCINTEL_NVLIST_V1_MC_ECC, 837 &ecc) == 0) { 838 const char *pval = ecc ? "enabled" : "disabled"; 839 ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_ECC, 840 TOPO_PROP_IMMUTABLE, pval, &err); 841 if (ret != 0) { 842 return (topo_mod_seterrno(mod, err)); 843 } 844 } 845 846 if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_POLICY, 847 &page) == 0) { 848 ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_POLICY, 849 TOPO_PROP_IMMUTABLE, page, &err); 850 if (ret != 0) { 851 return (topo_mod_seterrno(mod, err)); 852 } 853 } 854 855 if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE, 856 &cmode) != 0) { 857 cmode = NULL; 858 } 859 860 if (nvlist_lookup_nvlist_array(mc_nvl, MCINTEL_NVLIST_V1_MC_CHANNELS, 861 &channels, &nchans) != 0) { 862 whinge(mod, NULL, "mc_imc_create_v1: missing channels entry"); 863 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 864 } 865 866 if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, 0, 867 nchans - 1) < 0) { 868 whinge(mod, NULL, "mc_imc_create_v1: channel node range create " 869 "failed\n"); 870 return (-1); 871 } 872 873 for (i = 0; i < nchans; i++) { 874 if (mc_channel_create_v1(mod, mcnode, auth, channels[i], i, 875 cmode) != 0) { 876 return (-1); 877 } 878 } 879 880 return (0); 881 } 882 883 static int 884 mc_nb_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name, 885 nvlist_t *auth, nvlist_t *nvl) 886 { 887 nvlist_t **mc_nvl; 888 uint_t nmc, i; 889 890 if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS, 891 &mc_nvl, &nmc) != 0) { 892 whinge(mod, NULL, "mc_nb_create_v1: failed to find memory " 893 "controller information\n"); 894 return (-1); 895 } 896 897 if (topo_node_range_create(mod, pnode, name, 0, nmc - 1) < 0) { 898 whinge(mod, NULL, 899 "mc_nb_create_v1: node range create failed\n"); 900 return (-1); 901 } 902 903 for (i = 0; i < nmc; i++) { 904 if (mc_imc_create_v1(mod, pnode, name, auth, mc_nvl[i], i) != 0) 905 return (-1); 906 } 907 908 return (0); 909 } 910 911 int 912 mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 913 const char *name, nvlist_t *auth) 914 { 915 mc_snapshot_info_t mcs; 916 void *buf = NULL; 917 nvlist_t *nvl; 918 uint8_t ver; 919 int rc; 920 921 if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 922 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 923 ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) { 924 925 whinge(mod, NULL, "mc failed to snapshot %s\n", 926 strerror(errno)); 927 928 free(buf); 929 (void) close(mc_fd); 930 return (0); 931 } 932 (void) close(mc_fd); 933 (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 934 topo_mod_free(mod, buf, mcs.mcs_size); 935 936 if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) { 937 whinge(mod, NULL, "mc nvlist is not versioned\n"); 938 nvlist_free(nvl); 939 return (0); 940 } else if (ver != MCINTEL_NVLIST_VERS0 && 941 ver != MCINTEL_NVLIST_VERS1) { 942 whinge(mod, NULL, "mc nvlist version mismatch\n"); 943 nvlist_free(nvl); 944 return (0); 945 } 946 947 if (ver == MCINTEL_NVLIST_VERS1) { 948 rc = mc_nb_create_v1(mod, pnode, name, auth, nvl); 949 } else { 950 rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl); 951 } 952 953 nvlist_free(nvl); 954 return (rc); 955 } 956 957 void 958 onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 959 const char *name, nvlist_t *auth) 960 { 961 if (mc_onchip(topo_node_instance(pnode))) 962 (void) mc_node_create(mod, chip_smbid, pnode, name, auth); 963 } 964 965 int 966 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 967 nvlist_t *auth) 968 { 969 return (mc_node_create(mod, IGNORE_ID, pnode, name, auth)); 970 } 971