120c794b3Sgavinm /* 220c794b3Sgavinm * CDDL HEADER START 320c794b3Sgavinm * 420c794b3Sgavinm * The contents of this file are subject to the terms of the 520c794b3Sgavinm * Common Development and Distribution License (the "License"). 620c794b3Sgavinm * You may not use this file except in compliance with the License. 720c794b3Sgavinm * 820c794b3Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 920c794b3Sgavinm * or http://www.opensolaris.org/os/licensing. 1020c794b3Sgavinm * See the License for the specific language governing permissions 1120c794b3Sgavinm * and limitations under the License. 1220c794b3Sgavinm * 1320c794b3Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 1420c794b3Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1520c794b3Sgavinm * If applicable, add the following below this CDDL HEADER, with the 1620c794b3Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 1720c794b3Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 1820c794b3Sgavinm * 1920c794b3Sgavinm * CDDL HEADER END 2020c794b3Sgavinm */ 2120c794b3Sgavinm 2220c794b3Sgavinm /* 23efd31e1dSTrang Do * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24eb00b1c8SRobert Mustacchi * Copyright 2019 Joyent, Inc. 2520c794b3Sgavinm */ 2620c794b3Sgavinm 2720c794b3Sgavinm #include <unistd.h> 2820c794b3Sgavinm #include <stdio.h> 2920c794b3Sgavinm #include <stdlib.h> 3020c794b3Sgavinm #include <stdarg.h> 3120c794b3Sgavinm #include <string.h> 3220c794b3Sgavinm #include <strings.h> 3320c794b3Sgavinm #include <limits.h> 3420c794b3Sgavinm #include <alloca.h> 3520c794b3Sgavinm #include <kstat.h> 3620c794b3Sgavinm #include <fcntl.h> 3720c794b3Sgavinm #include <errno.h> 3820c794b3Sgavinm #include <libnvpair.h> 3920c794b3Sgavinm #include <sys/types.h> 4020c794b3Sgavinm #include <sys/bitmap.h> 4120c794b3Sgavinm #include <sys/processor.h> 4220c794b3Sgavinm #include <sys/param.h> 4320c794b3Sgavinm #include <sys/fm/protocol.h> 4420c794b3Sgavinm #include <sys/systeminfo.h> 4520c794b3Sgavinm #include <sys/mc.h> 4620c794b3Sgavinm #include <sys/mc_amd.h> 4720c794b3Sgavinm #include <sys/mc_intel.h> 4820c794b3Sgavinm #include <fm/topo_mod.h> 4920c794b3Sgavinm 5020c794b3Sgavinm #include "chip.h" 5120c794b3Sgavinm 5220c794b3Sgavinm #ifndef MAX 5320c794b3Sgavinm #define MAX(a, b) ((a) > (b) ? (a) : (b)) 5420c794b3Sgavinm #endif 5520c794b3Sgavinm 56eb00b1c8SRobert Mustacchi /* 57eb00b1c8SRobert Mustacchi * These are names that we use for properties. In the v0 scheme we try to pull 58eb00b1c8SRobert Mustacchi * these directly from the memory controller (which also causes a lot more 59eb00b1c8SRobert Mustacchi * variation). In the v1 scheme we translate to the name that topo wants to use, 60eb00b1c8SRobert Mustacchi * regardless of what the parent is called. 61eb00b1c8SRobert Mustacchi */ 62eb00b1c8SRobert Mustacchi #define MC_PROP_ECC "memory-ecc" 63eb00b1c8SRobert Mustacchi #define MC_PROP_POLICY "memory-policy" 64eb00b1c8SRobert Mustacchi #define CHAN_PROP_MODE "channel-mode" 65eb00b1c8SRobert Mustacchi #define DIMM_SIZE "size" 66eb00b1c8SRobert Mustacchi #define DIMM_STRING_SIZE "dimm-size" 67eb00b1c8SRobert Mustacchi #define DIMM_COL "ncolumn" 68eb00b1c8SRobert Mustacchi #define DIMM_ROW "nrow" 69eb00b1c8SRobert Mustacchi #define DIMM_DENSITY "density" 70eb00b1c8SRobert Mustacchi #define DIMM_WIDTH "width" 71eb00b1c8SRobert Mustacchi #define DIMM_RANKS "ranks" 72eb00b1c8SRobert Mustacchi #define DIMM_BANKS "nbanks" 73eb00b1c8SRobert Mustacchi #define DIMM_HDRL "hdrl-enabled" 74eb00b1c8SRobert Mustacchi #define DIMM_HDRL_PARITY "hdrl-parity" 75eb00b1c8SRobert Mustacchi #define DIMM_3DRANK "3d-subranks" 76*ae6d4bc3SRobert Mustacchi #define RANK_STATUS "dimm-rank-status" 77*ae6d4bc3SRobert Mustacchi #define RANK_SIZE "dimm-rank-size" 78eb00b1c8SRobert Mustacchi 7920c794b3Sgavinm static const topo_pgroup_info_t dimm_channel_pgroup = 8020c794b3Sgavinm { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 8120c794b3Sgavinm static const topo_pgroup_info_t dimm_pgroup = 8220c794b3Sgavinm { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 8320c794b3Sgavinm static const topo_pgroup_info_t rank_pgroup = 8420c794b3Sgavinm { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 8520c794b3Sgavinm static const topo_pgroup_info_t mc_pgroup = 8620c794b3Sgavinm { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 8720c794b3Sgavinm 8820c794b3Sgavinm static const topo_method_t dimm_methods[] = { 8920c794b3Sgavinm { SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, 9020c794b3Sgavinm simple_dimm_label}, 9120c794b3Sgavinm { SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL, 9220c794b3Sgavinm simple_dimm_label_mp}, 9320c794b3Sgavinm { SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, 9420c794b3Sgavinm seq_dimm_label}, 9520c794b3Sgavinm { NULL } 9620c794b3Sgavinm }; 9720c794b3Sgavinm 98e4b86885SCheng Sean Ye extern const topo_method_t rank_methods[]; 99e4b86885SCheng Sean Ye extern const topo_method_t ntv_page_retire_methods[]; 100e4b86885SCheng Sean Ye 10120c794b3Sgavinm static int mc_fd; 10220c794b3Sgavinm 10320c794b3Sgavinm int 10420c794b3Sgavinm mc_offchip_open() 10520c794b3Sgavinm { 10620c794b3Sgavinm mc_fd = open("/dev/mc/mc", O_RDONLY); 10720c794b3Sgavinm return (mc_fd != -1); 10820c794b3Sgavinm } 10920c794b3Sgavinm 110e3d60c9bSAdrian Frost static int 111e3d60c9bSAdrian Frost mc_onchip(topo_instance_t id) 112e3d60c9bSAdrian Frost { 113e3d60c9bSAdrian Frost char path[64]; 114e3d60c9bSAdrian Frost 115e3d60c9bSAdrian Frost (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); 116e3d60c9bSAdrian Frost mc_fd = open(path, O_RDONLY); 117e3d60c9bSAdrian Frost return (mc_fd != -1); 118e3d60c9bSAdrian Frost } 119e3d60c9bSAdrian Frost 12085738508SVuong Nguyen static void 12120c794b3Sgavinm mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm, 12285738508SVuong Nguyen nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part, 12385738508SVuong Nguyen char *rev, int maxranks) 12420c794b3Sgavinm { 12520c794b3Sgavinm int i; 12620c794b3Sgavinm int rank; 12720c794b3Sgavinm tnode_t *rnode; 12820c794b3Sgavinm nvpair_t *nvp; 12920c794b3Sgavinm nvlist_t *fmri; 13020c794b3Sgavinm int err = 0; 13120c794b3Sgavinm 13285738508SVuong Nguyen /* 13385738508SVuong Nguyen * If start_rank is defined, it is assigned to the first rank of this 13485738508SVuong Nguyen * dimm. 13585738508SVuong Nguyen */ 13685738508SVuong Nguyen rank = start_rank >= 0 ? start_rank : dimm * maxranks; 13720c794b3Sgavinm if (topo_node_range_create(mod, dnode, RANK, rank, 13820c794b3Sgavinm rank + nranks - 1) < 0) { 13985738508SVuong Nguyen whinge(mod, NULL, "mc_add_ranks: node range create failed" 14020c794b3Sgavinm " for rank\n"); 14120c794b3Sgavinm return; 14220c794b3Sgavinm } 14320c794b3Sgavinm for (i = 0; i < nranks; i++) { 14420c794b3Sgavinm fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION, 14520c794b3Sgavinm RANK, rank, NULL, auth, part, rev, serial); 14620c794b3Sgavinm if (fmri == NULL) { 14720c794b3Sgavinm whinge(mod, NULL, 14820c794b3Sgavinm "mc_add_ranks: topo_mod_hcfmri failed\n"); 14920c794b3Sgavinm return; 15020c794b3Sgavinm } 15120c794b3Sgavinm if ((rnode = topo_node_bind(mod, dnode, RANK, rank, 15220c794b3Sgavinm fmri)) == NULL) { 15320c794b3Sgavinm nvlist_free(fmri); 15420c794b3Sgavinm whinge(mod, NULL, "mc_add_ranks: node bind failed" 15520c794b3Sgavinm " for ranks\n"); 15620c794b3Sgavinm return; 15720c794b3Sgavinm } 15820c794b3Sgavinm (void) topo_node_fru_set(rnode, NULL, 0, &err); 15920c794b3Sgavinm 16020c794b3Sgavinm if (topo_method_register(mod, rnode, rank_methods) < 0) 161*ae6d4bc3SRobert Mustacchi whinge(mod, &err, "mc_add_ranks: " 16220c794b3Sgavinm "topo_method_register failed"); 16320c794b3Sgavinm 164e4b86885SCheng Sean Ye if (! is_xpv() && topo_method_register(mod, rnode, 165e4b86885SCheng Sean Ye ntv_page_retire_methods) < 0) 166e4b86885SCheng Sean Ye whinge(mod, &err, "mc_add_ranks: " 167e4b86885SCheng Sean Ye "topo_method_register failed"); 168e4b86885SCheng Sean Ye 16920c794b3Sgavinm (void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err); 17020c794b3Sgavinm 171074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod)) 172d3c57c1fSTom Pothier (void) topo_node_label_set(rnode, NULL, &err); 173074bb90dSTom Pothier 17420c794b3Sgavinm nvlist_free(fmri); 17520c794b3Sgavinm 17620c794b3Sgavinm (void) topo_pgroup_create(rnode, &rank_pgroup, &err); 17720c794b3Sgavinm for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL; 17820c794b3Sgavinm nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) { 17920c794b3Sgavinm (void) nvprop_add(mod, nvp, PGNAME(RANK), rnode); 18020c794b3Sgavinm } 18120c794b3Sgavinm rank++; 18220c794b3Sgavinm } 18320c794b3Sgavinm } 18420c794b3Sgavinm 18520c794b3Sgavinm static void 186074bb90dSTom Pothier mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 187074bb90dSTom Pothier nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks) 18820c794b3Sgavinm { 18920c794b3Sgavinm int i; 19020c794b3Sgavinm nvlist_t *fmri; 19120c794b3Sgavinm tnode_t *dnode; 19220c794b3Sgavinm nvpair_t *nvp; 19320c794b3Sgavinm int err; 19420c794b3Sgavinm nvlist_t **ranks_nvp; 19585738508SVuong Nguyen int32_t start_rank = -1; 19620c794b3Sgavinm uint_t nranks = 0; 197491f61a1SYanmin Sun uint32_t dimm_number; 19820c794b3Sgavinm char *serial = NULL; 19920c794b3Sgavinm char *part = NULL; 20020c794b3Sgavinm char *rev = NULL; 20120c794b3Sgavinm char *label = NULL; 20220c794b3Sgavinm char *name; 203074bb90dSTom Pothier id_t smbid; 20420c794b3Sgavinm 205491f61a1SYanmin Sun if (topo_node_range_create(mod, pnode, DIMM, 0, 206491f61a1SYanmin Sun maxdimms ? maxdimms-1 : ndimms-1) < 0) { 20720c794b3Sgavinm whinge(mod, NULL, 20820c794b3Sgavinm "mc_add_dimms: node range create failed\n"); 20920c794b3Sgavinm return; 21020c794b3Sgavinm } 21120c794b3Sgavinm for (i = 0; i < ndimms; i++) { 212491f61a1SYanmin Sun dimm_number = i; 21320c794b3Sgavinm for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 21420c794b3Sgavinm nvp = nvlist_next_nvpair(nvl[i], nvp)) { 21520c794b3Sgavinm name = nvpair_name(nvp); 21620c794b3Sgavinm if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) { 21720c794b3Sgavinm (void) nvpair_value_nvlist_array(nvp, 21820c794b3Sgavinm &ranks_nvp, &nranks); 21985738508SVuong Nguyen } else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) { 22085738508SVuong Nguyen (void) nvpair_value_int32(nvp, &start_rank); 22120c794b3Sgavinm } else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) { 22220c794b3Sgavinm (void) nvpair_value_string(nvp, &serial); 22320c794b3Sgavinm } else if (strcmp(name, FM_FMRI_HC_PART) == 0) { 22420c794b3Sgavinm (void) nvpair_value_string(nvp, &part); 22520c794b3Sgavinm } else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) { 22620c794b3Sgavinm (void) nvpair_value_string(nvp, &rev); 22720c794b3Sgavinm } else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) { 22820c794b3Sgavinm (void) nvpair_value_string(nvp, &label); 229491f61a1SYanmin Sun } else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) { 230491f61a1SYanmin Sun (void) nvpair_value_uint32(nvp, &dimm_number); 23120c794b3Sgavinm } 23220c794b3Sgavinm } 23320c794b3Sgavinm fmri = NULL; 234074bb90dSTom Pothier 235074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod)) { 236074bb90dSTom Pothier int channum; 237074bb90dSTom Pothier 238074bb90dSTom Pothier channum = topo_node_instance(pnode); 239efd31e1dSTrang Do smbid = memnode_to_smbiosid(mod, chip_smbid, 240efd31e1dSTrang Do DIMM_NODE_NAME, i, &channum); 241074bb90dSTom Pothier if (serial == NULL) 242074bb90dSTom Pothier serial = (char *)chip_serial_smbios_get(mod, 243074bb90dSTom Pothier smbid); 244074bb90dSTom Pothier if (part == NULL) 245074bb90dSTom Pothier part = (char *)chip_part_smbios_get(mod, 246074bb90dSTom Pothier smbid); 247074bb90dSTom Pothier if (rev == NULL) 248074bb90dSTom Pothier rev = (char *)chip_rev_smbios_get(mod, 249074bb90dSTom Pothier smbid); 250074bb90dSTom Pothier } 251074bb90dSTom Pothier 25220c794b3Sgavinm fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 253491f61a1SYanmin Sun DIMM, dimm_number, NULL, auth, part, rev, serial); 25420c794b3Sgavinm if (fmri == NULL) { 25520c794b3Sgavinm whinge(mod, NULL, 25620c794b3Sgavinm "mc_add_dimms: topo_mod_hcfmri failed\n"); 25720c794b3Sgavinm return; 25820c794b3Sgavinm } 259491f61a1SYanmin Sun if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number, 26020c794b3Sgavinm fmri)) == NULL) { 26120c794b3Sgavinm nvlist_free(fmri); 26220c794b3Sgavinm whinge(mod, NULL, "mc_add_dimms: node bind failed" 26320c794b3Sgavinm " for dimm\n"); 26420c794b3Sgavinm return; 26520c794b3Sgavinm } 26620c794b3Sgavinm 267074bb90dSTom Pothier if (!FM_AWARE_SMBIOS(mod)) 268074bb90dSTom Pothier if (topo_method_register(mod, dnode, dimm_methods) < 0) 269074bb90dSTom Pothier whinge(mod, NULL, "mc_add_dimms: " 270074bb90dSTom Pothier "topo_method_register failed"); 27120c794b3Sgavinm 27220c794b3Sgavinm (void) topo_pgroup_create(dnode, &dimm_pgroup, &err); 27320c794b3Sgavinm 27420c794b3Sgavinm for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; 27520c794b3Sgavinm nvp = nvlist_next_nvpair(nvl[i], nvp)) { 27620c794b3Sgavinm name = nvpair_name(nvp); 27720c794b3Sgavinm if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 && 27885738508SVuong Nguyen strcmp(name, FM_FAULT_FRU_LABEL) != 0 && 27985738508SVuong Nguyen strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) { 28020c794b3Sgavinm (void) nvprop_add(mod, nvp, PGNAME(DIMM), 28120c794b3Sgavinm dnode); 28220c794b3Sgavinm } 28320c794b3Sgavinm } 284074bb90dSTom Pothier 285074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod)) { 286074bb90dSTom Pothier nvlist_free(fmri); 287074bb90dSTom Pothier (void) topo_node_resource(dnode, &fmri, &err); 288074bb90dSTom Pothier /* 289074bb90dSTom Pothier * We will use a full absolute parent/child label 290074bb90dSTom Pothier */ 291074bb90dSTom Pothier label = (char *)chip_label_smbios_get(mod, 292074bb90dSTom Pothier pnode, smbid, label); 293074bb90dSTom Pothier } 294074bb90dSTom Pothier 295074bb90dSTom Pothier (void) topo_node_label_set(dnode, label, &err); 296074bb90dSTom Pothier 297074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod)) 298074bb90dSTom Pothier topo_mod_strfree(mod, label); 299074bb90dSTom Pothier 300074bb90dSTom Pothier (void) topo_node_fru_set(dnode, fmri, 0, &err); 301074bb90dSTom Pothier (void) topo_node_asru_set(dnode, fmri, 0, &err); 302074bb90dSTom Pothier nvlist_free(fmri); 30320c794b3Sgavinm 30420c794b3Sgavinm if (nranks) { 305491f61a1SYanmin Sun mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp, 306491f61a1SYanmin Sun start_rank, nranks, serial, part, rev, maxranks); 30720c794b3Sgavinm } 30820c794b3Sgavinm } 30920c794b3Sgavinm } 31020c794b3Sgavinm 31120c794b3Sgavinm static int 312074bb90dSTom Pothier mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 313074bb90dSTom Pothier int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks) 31420c794b3Sgavinm { 31520c794b3Sgavinm tnode_t *mc_channel; 31620c794b3Sgavinm nvlist_t *fmri; 31720c794b3Sgavinm nvlist_t **dimm_nvl; 318e3d60c9bSAdrian Frost nvpair_t *nvp; 319e3d60c9bSAdrian Frost char *name; 32020c794b3Sgavinm uint_t ndimms; 32120c794b3Sgavinm int err; 32220c794b3Sgavinm 32320c794b3Sgavinm if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) { 32420c794b3Sgavinm whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n"); 32520c794b3Sgavinm return (-1); 32620c794b3Sgavinm } 32720c794b3Sgavinm if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel, 32820c794b3Sgavinm fmri)) == NULL) { 32920c794b3Sgavinm whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n", 33020c794b3Sgavinm DRAMCHANNEL); 33120c794b3Sgavinm nvlist_free(fmri); 33220c794b3Sgavinm return (-1); 33320c794b3Sgavinm } 33420c794b3Sgavinm (void) topo_node_fru_set(mc_channel, NULL, 0, &err); 33520c794b3Sgavinm nvlist_free(fmri); 33620c794b3Sgavinm (void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err); 337074bb90dSTom Pothier 338074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod)) 339074bb90dSTom Pothier (void) topo_node_label_set(mc_channel, NULL, &err); 340074bb90dSTom Pothier 34120c794b3Sgavinm if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl, 34220c794b3Sgavinm &ndimms) == 0) { 343074bb90dSTom Pothier mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl, 344074bb90dSTom Pothier ndimms, maxdimms, maxranks); 345e3d60c9bSAdrian Frost } 346e3d60c9bSAdrian Frost for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 347e3d60c9bSAdrian Frost nvp = nvlist_next_nvpair(nvl, nvp)) { 348e3d60c9bSAdrian Frost name = nvpair_name(nvp); 349e3d60c9bSAdrian Frost if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) { 350e3d60c9bSAdrian Frost (void) nvprop_add(mod, nvp, PGNAME(CHAN), 351e3d60c9bSAdrian Frost mc_channel); 352e3d60c9bSAdrian Frost } 35320c794b3Sgavinm } 354074bb90dSTom Pothier 35520c794b3Sgavinm return (0); 35620c794b3Sgavinm } 35720c794b3Sgavinm 35820c794b3Sgavinm static int 359074bb90dSTom Pothier mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 360074bb90dSTom Pothier const char *name, nvlist_t *auth, nvlist_t *nvl) 36120c794b3Sgavinm { 36220c794b3Sgavinm int err; 36320c794b3Sgavinm int i, j; 36420c794b3Sgavinm int channel; 365e3d60c9bSAdrian Frost uint8_t nmc; 366e3d60c9bSAdrian Frost uint8_t maxranks; 367491f61a1SYanmin Sun uint8_t maxdimms; 36820c794b3Sgavinm tnode_t *mcnode; 36920c794b3Sgavinm nvlist_t *fmri; 37020c794b3Sgavinm nvlist_t **channel_nvl; 371e3d60c9bSAdrian Frost nvpair_t *nvp; 372e3d60c9bSAdrian Frost char *pname; 37320c794b3Sgavinm uint_t nchannels; 37420c794b3Sgavinm 37520c794b3Sgavinm if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl, 37620c794b3Sgavinm &nchannels) != 0) { 37720c794b3Sgavinm whinge(mod, NULL, 37820c794b3Sgavinm "mc_nb_create: failed to find channel information\n"); 37920c794b3Sgavinm return (-1); 38020c794b3Sgavinm } 38185738508SVuong Nguyen if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) { 38285738508SVuong Nguyen /* 38385738508SVuong Nguyen * Assume channels are evenly divided among the controllers. 38485738508SVuong Nguyen * Convert nchannels to channels per controller 38585738508SVuong Nguyen */ 38685738508SVuong Nguyen nchannels = nchannels / nmc; 38785738508SVuong Nguyen } else { 388e3d60c9bSAdrian Frost /* 389e3d60c9bSAdrian Frost * if number of memory controllers is not specified then there 390e3d60c9bSAdrian Frost * are two channels per controller and the nchannels is total 391e3d60c9bSAdrian Frost * we will set up nmc as number of controllers and convert 392e3d60c9bSAdrian Frost * nchannels to channels per controller 393e3d60c9bSAdrian Frost */ 394e3d60c9bSAdrian Frost nmc = nchannels / 2; 395e3d60c9bSAdrian Frost nchannels = nchannels / nmc; 396e3d60c9bSAdrian Frost } 397e3d60c9bSAdrian Frost if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0) 398e3d60c9bSAdrian Frost maxranks = 2; 399491f61a1SYanmin Sun if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0) 400491f61a1SYanmin Sun maxdimms = 0; 40120c794b3Sgavinm if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) { 40220c794b3Sgavinm whinge(mod, NULL, 40320c794b3Sgavinm "mc_nb_create: node range create failed\n"); 40420c794b3Sgavinm return (-1); 40520c794b3Sgavinm } 40620c794b3Sgavinm channel = 0; 40720c794b3Sgavinm for (i = 0; i < nmc; i++) { 40820c794b3Sgavinm if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { 40920c794b3Sgavinm whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n"); 41020c794b3Sgavinm return (-1); 41120c794b3Sgavinm } 41220c794b3Sgavinm if ((mcnode = topo_node_bind(mod, pnode, name, i, 41320c794b3Sgavinm fmri)) == NULL) { 414e3d60c9bSAdrian Frost whinge(mod, NULL, "mc_nb_create: node bind failed" 41520c794b3Sgavinm " for memory-controller\n"); 41620c794b3Sgavinm nvlist_free(fmri); 41720c794b3Sgavinm return (-1); 41820c794b3Sgavinm } 41920c794b3Sgavinm 42020c794b3Sgavinm (void) topo_node_fru_set(mcnode, NULL, 0, &err); 42120c794b3Sgavinm nvlist_free(fmri); 42220c794b3Sgavinm (void) topo_pgroup_create(mcnode, &mc_pgroup, &err); 42320c794b3Sgavinm 424074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod)) 425074bb90dSTom Pothier (void) topo_node_label_set(mcnode, NULL, &err); 426074bb90dSTom Pothier 42720c794b3Sgavinm if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel, 428e3d60c9bSAdrian Frost channel + nchannels - 1) < 0) { 42920c794b3Sgavinm whinge(mod, NULL, 43020c794b3Sgavinm "mc_nb_create: channel node range create failed\n"); 43120c794b3Sgavinm return (-1); 43220c794b3Sgavinm } 433e3d60c9bSAdrian Frost for (j = 0; j < nchannels; j++) { 434074bb90dSTom Pothier if (mc_add_channel(mod, chip_smbid, mcnode, channel, 435074bb90dSTom Pothier auth, channel_nvl[channel], maxdimms, 436074bb90dSTom Pothier maxranks) < 0) { 43720c794b3Sgavinm return (-1); 43820c794b3Sgavinm } 43920c794b3Sgavinm channel++; 44020c794b3Sgavinm } 441e3d60c9bSAdrian Frost for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 442e3d60c9bSAdrian Frost nvp = nvlist_next_nvpair(nvl, nvp)) { 443e3d60c9bSAdrian Frost pname = nvpair_name(nvp); 444e3d60c9bSAdrian Frost if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 && 445e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 && 446e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 && 447491f61a1SYanmin Sun strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 && 448e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 && 449e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_MEM) != 0) { 450e3d60c9bSAdrian Frost (void) nvprop_add(mod, nvp, PGNAME(MCT), 451e3d60c9bSAdrian Frost mcnode); 452e3d60c9bSAdrian Frost } 453e3d60c9bSAdrian Frost } 45420c794b3Sgavinm } 45520c794b3Sgavinm 4565cc5d5ceSToomas Soome return (0); 45720c794b3Sgavinm } 45820c794b3Sgavinm 459*ae6d4bc3SRobert Mustacchi static int 460*ae6d4bc3SRobert Mustacchi mc_rank_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 461*ae6d4bc3SRobert Mustacchi nvlist_t *dimm_nvl, uint64_t rsize, uint32_t id) 462*ae6d4bc3SRobert Mustacchi { 463*ae6d4bc3SRobert Mustacchi nvlist_t *fmri; 464*ae6d4bc3SRobert Mustacchi tnode_t *rank; 465*ae6d4bc3SRobert Mustacchi int err; 466*ae6d4bc3SRobert Mustacchi boolean_t *disabled; 467*ae6d4bc3SRobert Mustacchi uint_t ndisabled; 468*ae6d4bc3SRobert Mustacchi const char *status; 469*ae6d4bc3SRobert Mustacchi 470*ae6d4bc3SRobert Mustacchi fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, RANK, id, 471*ae6d4bc3SRobert Mustacchi NULL, auth, NULL, NULL, NULL); 472*ae6d4bc3SRobert Mustacchi if (fmri == NULL) { 473*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: topo_mod_hcfmri " 474*ae6d4bc3SRobert Mustacchi "failed\n"); 475*ae6d4bc3SRobert Mustacchi return (-1); 476*ae6d4bc3SRobert Mustacchi } 477*ae6d4bc3SRobert Mustacchi 478*ae6d4bc3SRobert Mustacchi if ((rank = topo_node_bind(mod, pnode, RANK, id, fmri)) == NULL) { 479*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: node bind failed for " 480*ae6d4bc3SRobert Mustacchi "DIMM\n"); 481*ae6d4bc3SRobert Mustacchi nvlist_free(fmri); 482*ae6d4bc3SRobert Mustacchi return (-1); 483*ae6d4bc3SRobert Mustacchi } 484*ae6d4bc3SRobert Mustacchi 485*ae6d4bc3SRobert Mustacchi if (topo_method_register(mod, rank, rank_methods) < 0) { 486*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: topo_method_register " 487*ae6d4bc3SRobert Mustacchi "failed for rank_methods: %d", topo_mod_errno(mod)); 488*ae6d4bc3SRobert Mustacchi } 489*ae6d4bc3SRobert Mustacchi 490*ae6d4bc3SRobert Mustacchi if (!is_xpv() && topo_method_register(mod, rank, 491*ae6d4bc3SRobert Mustacchi ntv_page_retire_methods) != 0) { 492*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: topo_method_register " 493*ae6d4bc3SRobert Mustacchi "failed for page retire: %d", topo_mod_errno(mod)); 494*ae6d4bc3SRobert Mustacchi } 495*ae6d4bc3SRobert Mustacchi 496*ae6d4bc3SRobert Mustacchi if (topo_node_asru_set(rank, fmri, TOPO_ASRU_COMPUTE, &err) != 0) { 497*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to set asru: %d", 498*ae6d4bc3SRobert Mustacchi err); 499*ae6d4bc3SRobert Mustacchi nvlist_free(fmri); 500*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 501*ae6d4bc3SRobert Mustacchi } 502*ae6d4bc3SRobert Mustacchi 503*ae6d4bc3SRobert Mustacchi if (topo_node_fru_set(rank, NULL, 0, &err) != 0) { 504*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: fru set failed: " 505*ae6d4bc3SRobert Mustacchi "%d\n", err); 506*ae6d4bc3SRobert Mustacchi nvlist_free(fmri); 507*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 508*ae6d4bc3SRobert Mustacchi } 509*ae6d4bc3SRobert Mustacchi nvlist_free(fmri); 510*ae6d4bc3SRobert Mustacchi 511*ae6d4bc3SRobert Mustacchi if (topo_pgroup_create(rank, &rank_pgroup, &err) != 0) { 512*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to create " 513*ae6d4bc3SRobert Mustacchi "property group: %d\n", err); 514*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 515*ae6d4bc3SRobert Mustacchi } 516*ae6d4bc3SRobert Mustacchi 517*ae6d4bc3SRobert Mustacchi /* 518*ae6d4bc3SRobert Mustacchi * The traditional northbridge driver broke down each rank into the 519*ae6d4bc3SRobert Mustacchi * interleave targets that led to it. At this time, the imc driver (the 520*ae6d4bc3SRobert Mustacchi * only v1 provider) does not supply that information and therefore we 521*ae6d4bc3SRobert Mustacchi * cannot set that. Instead we just set basic properties on this, the 522*ae6d4bc3SRobert Mustacchi * size of the rank and whether or not it is disabled. 523*ae6d4bc3SRobert Mustacchi */ 524*ae6d4bc3SRobert Mustacchi if (rsize != 0 && topo_prop_set_uint64(rank, PGNAME(RANK), RANK_SIZE, 525*ae6d4bc3SRobert Mustacchi TOPO_PROP_IMMUTABLE, rsize, &err) != 0) { 526*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to set %s " 527*ae6d4bc3SRobert Mustacchi "property: %d", RANK_SIZE, err); 528*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 529*ae6d4bc3SRobert Mustacchi } 530*ae6d4bc3SRobert Mustacchi 531*ae6d4bc3SRobert Mustacchi if (nvlist_lookup_boolean_array(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RDIS, 532*ae6d4bc3SRobert Mustacchi &disabled, &ndisabled) != 0) { 533*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: Couldn't find disabled " 534*ae6d4bc3SRobert Mustacchi "ranks array"); 535*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 536*ae6d4bc3SRobert Mustacchi } 537*ae6d4bc3SRobert Mustacchi 538*ae6d4bc3SRobert Mustacchi if (id >= ndisabled) { 539*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: Found rank %u with id " 540*ae6d4bc3SRobert Mustacchi "larger than supported by hardware", id); 541*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 542*ae6d4bc3SRobert Mustacchi } 543*ae6d4bc3SRobert Mustacchi 544*ae6d4bc3SRobert Mustacchi status = disabled[id] ? "disabled" : "enabled"; 545*ae6d4bc3SRobert Mustacchi if (topo_prop_set_string(rank, PGNAME(RANK), RANK_STATUS, 546*ae6d4bc3SRobert Mustacchi TOPO_PROP_IMMUTABLE, status, &err) != 0) { 547*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to set %s " 548*ae6d4bc3SRobert Mustacchi "property: %d", RANK_STATUS, err); 549*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 550*ae6d4bc3SRobert Mustacchi } 551*ae6d4bc3SRobert Mustacchi 552*ae6d4bc3SRobert Mustacchi return (0); 553*ae6d4bc3SRobert Mustacchi } 554*ae6d4bc3SRobert Mustacchi 555eb00b1c8SRobert Mustacchi static int 556eb00b1c8SRobert Mustacchi mc_dimm_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 557eb00b1c8SRobert Mustacchi nvlist_t *dimm_nvl, uint_t id) 558eb00b1c8SRobert Mustacchi { 559eb00b1c8SRobert Mustacchi int err, ret; 560eb00b1c8SRobert Mustacchi tnode_t *dimm; 561eb00b1c8SRobert Mustacchi nvlist_t *fmri; 562eb00b1c8SRobert Mustacchi boolean_t present; 563*ae6d4bc3SRobert Mustacchi uint64_t size, density, rsize; 564*ae6d4bc3SRobert Mustacchi uint32_t cols, rows, width, ranks, banks, i; 565eb00b1c8SRobert Mustacchi 566eb00b1c8SRobert Mustacchi /* 567eb00b1c8SRobert Mustacchi * First, figure out if this DIMM is present. If not, we don't bother 568eb00b1c8SRobert Mustacchi * creating anything. 569eb00b1c8SRobert Mustacchi */ 570eb00b1c8SRobert Mustacchi if (nvlist_lookup_boolean_value(dimm_nvl, 571eb00b1c8SRobert Mustacchi MCINTEL_NVLIST_V1_DIMM_PRESENT, &present) != 0) { 572eb00b1c8SRobert Mustacchi return (-1); 573eb00b1c8SRobert Mustacchi } else if (!present) { 574eb00b1c8SRobert Mustacchi return (0); 575eb00b1c8SRobert Mustacchi } 576eb00b1c8SRobert Mustacchi 577eb00b1c8SRobert Mustacchi fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, DIMM, id, 578eb00b1c8SRobert Mustacchi NULL, auth, NULL, NULL, NULL); 579eb00b1c8SRobert Mustacchi if (fmri == NULL) { 580eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: topo_mod_hcfmri " 581eb00b1c8SRobert Mustacchi "failed\n"); 582eb00b1c8SRobert Mustacchi return (-1); 583eb00b1c8SRobert Mustacchi } 584eb00b1c8SRobert Mustacchi 585eb00b1c8SRobert Mustacchi if ((dimm = topo_node_bind(mod, pnode, DIMM, id, fmri)) == NULL) { 586eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: node bind failed for " 587eb00b1c8SRobert Mustacchi "DIMM\n"); 588eb00b1c8SRobert Mustacchi nvlist_free(fmri); 589eb00b1c8SRobert Mustacchi return (-1); 590eb00b1c8SRobert Mustacchi } 591eb00b1c8SRobert Mustacchi 592eb00b1c8SRobert Mustacchi if (topo_node_fru_set(dimm, NULL, 0, &err) != 0) { 593eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: fru set failed: " 594eb00b1c8SRobert Mustacchi "%d\n", err); 595eb00b1c8SRobert Mustacchi nvlist_free(fmri); 596eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 597eb00b1c8SRobert Mustacchi } 598eb00b1c8SRobert Mustacchi nvlist_free(fmri); 599eb00b1c8SRobert Mustacchi 600eb00b1c8SRobert Mustacchi if (topo_pgroup_create(dimm, &dimm_pgroup, &err) != 0) { 601eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: failed to create " 602eb00b1c8SRobert Mustacchi "property group: %d\n", err); 603eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 604eb00b1c8SRobert Mustacchi } 605eb00b1c8SRobert Mustacchi 606eb00b1c8SRobert Mustacchi ret = 0; 607eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_SIZE, 608eb00b1c8SRobert Mustacchi &size) == 0) { 609eb00b1c8SRobert Mustacchi char buf[64]; 610eb00b1c8SRobert Mustacchi const char *suffix; 611*ae6d4bc3SRobert Mustacchi uint64_t tsize; 612eb00b1c8SRobert Mustacchi ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_SIZE, 613eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, size, &err); 614*ae6d4bc3SRobert Mustacchi tsize = size; 615eb00b1c8SRobert Mustacchi 616eb00b1c8SRobert Mustacchi /* 617eb00b1c8SRobert Mustacchi * We must manually cons up a dimm-size property which is the 618eb00b1c8SRobert Mustacchi * size of the dimm as a round number with a prefix such as M or 619eb00b1c8SRobert Mustacchi * G. This is used by Intel CPU eversholt rules. Older memory 620eb00b1c8SRobert Mustacchi * controller drivers did this in the driver, but we instead opt 621eb00b1c8SRobert Mustacchi * to do so in user land. 622eb00b1c8SRobert Mustacchi */ 623*ae6d4bc3SRobert Mustacchi if (tsize >= (1ULL << 40)) { 624*ae6d4bc3SRobert Mustacchi tsize /= (1ULL << 40); 625eb00b1c8SRobert Mustacchi suffix = "T"; 626*ae6d4bc3SRobert Mustacchi } else if (tsize >= (1ULL << 30)) { 627*ae6d4bc3SRobert Mustacchi tsize /= (1ULL << 30); 628eb00b1c8SRobert Mustacchi suffix = "G"; 629*ae6d4bc3SRobert Mustacchi } else if (tsize >= (1ULL << 20)) { 630*ae6d4bc3SRobert Mustacchi tsize /= (1ULL << 20); 631eb00b1c8SRobert Mustacchi suffix = "M"; 632eb00b1c8SRobert Mustacchi } else { 633eb00b1c8SRobert Mustacchi suffix = NULL; 634eb00b1c8SRobert Mustacchi } 635eb00b1c8SRobert Mustacchi 636eb00b1c8SRobert Mustacchi if (suffix != NULL) { 637*ae6d4bc3SRobert Mustacchi if (snprintf(buf, sizeof (buf), "%"PRIu64"%s", tsize, 638eb00b1c8SRobert Mustacchi suffix) >= sizeof (buf)) { 639eb00b1c8SRobert Mustacchi whinge(mod, NULL, "failed to construct DIMM " 640eb00b1c8SRobert Mustacchi "size due to buffer overflow"); 641eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 642eb00b1c8SRobert Mustacchi } 643*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_string(dimm, PGNAME(DIMM), 644eb00b1c8SRobert Mustacchi DIMM_STRING_SIZE, TOPO_PROP_IMMUTABLE, buf, &err); 645*ae6d4bc3SRobert Mustacchi if (ret != 0) { 646*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 647*ae6d4bc3SRobert Mustacchi } 648eb00b1c8SRobert Mustacchi } 649*ae6d4bc3SRobert Mustacchi } else { 650*ae6d4bc3SRobert Mustacchi size = 0; 651eb00b1c8SRobert Mustacchi } 652eb00b1c8SRobert Mustacchi 653eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS, 654eb00b1c8SRobert Mustacchi &cols) == 0) { 655*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_COL, 656eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, cols, &err); 657*ae6d4bc3SRobert Mustacchi if (ret != 0) { 658*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 659*ae6d4bc3SRobert Mustacchi } 660eb00b1c8SRobert Mustacchi } 661eb00b1c8SRobert Mustacchi 662eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NROWS, 663eb00b1c8SRobert Mustacchi &rows) == 0) { 664*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_ROW, 665eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, rows, &err); 666*ae6d4bc3SRobert Mustacchi if (ret != 0) { 667*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 668*ae6d4bc3SRobert Mustacchi } 669eb00b1c8SRobert Mustacchi } 670eb00b1c8SRobert Mustacchi 671eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY, 672eb00b1c8SRobert Mustacchi &density) == 0) { 673*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_DENSITY, 674eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, density, &err); 675*ae6d4bc3SRobert Mustacchi if (ret != 0) { 676*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 677*ae6d4bc3SRobert Mustacchi } 678eb00b1c8SRobert Mustacchi } 679eb00b1c8SRobert Mustacchi 680eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH, 681eb00b1c8SRobert Mustacchi &width) == 0) { 682*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_WIDTH, 683eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, width, &err); 684*ae6d4bc3SRobert Mustacchi if (ret != 0) { 685*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 686*ae6d4bc3SRobert Mustacchi } 687eb00b1c8SRobert Mustacchi } 688eb00b1c8SRobert Mustacchi 689eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_BANKS, 690eb00b1c8SRobert Mustacchi &banks) == 0) { 691*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_BANKS, 692eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, banks, &err); 693*ae6d4bc3SRobert Mustacchi if (ret != 0) { 694*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 695*ae6d4bc3SRobert Mustacchi } 696*ae6d4bc3SRobert Mustacchi } else { 697*ae6d4bc3SRobert Mustacchi banks = 0; 698eb00b1c8SRobert Mustacchi } 699eb00b1c8SRobert Mustacchi 700eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RANKS, 701eb00b1c8SRobert Mustacchi &ranks) == 0) { 702*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_RANKS, 703eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, ranks, &err); 704*ae6d4bc3SRobert Mustacchi if (ret != 0) { 705*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 706*ae6d4bc3SRobert Mustacchi } 707*ae6d4bc3SRobert Mustacchi } 708*ae6d4bc3SRobert Mustacchi 709*ae6d4bc3SRobert Mustacchi if (ret != 0) { 710*ae6d4bc3SRobert Mustacchi return (-1); 711*ae6d4bc3SRobert Mustacchi } 712*ae6d4bc3SRobert Mustacchi 713*ae6d4bc3SRobert Mustacchi if (topo_node_range_create(mod, dimm, RANK, 0, ranks - 1) < 0) { 714*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: rank node range " 715*ae6d4bc3SRobert Mustacchi "create failed\n"); 716*ae6d4bc3SRobert Mustacchi return (-1); 717*ae6d4bc3SRobert Mustacchi } 718*ae6d4bc3SRobert Mustacchi 719*ae6d4bc3SRobert Mustacchi rsize = 0; 720*ae6d4bc3SRobert Mustacchi if (size != 0 && banks != 0) { 721*ae6d4bc3SRobert Mustacchi rsize = size / banks; 722eb00b1c8SRobert Mustacchi } 723eb00b1c8SRobert Mustacchi 724*ae6d4bc3SRobert Mustacchi for (i = 0; i < ranks; i++) { 725*ae6d4bc3SRobert Mustacchi if (mc_rank_create_v1(mod, dimm, auth, dimm_nvl, rsize, i) != 726*ae6d4bc3SRobert Mustacchi 0) { 727*ae6d4bc3SRobert Mustacchi return (-1); 728*ae6d4bc3SRobert Mustacchi } 729*ae6d4bc3SRobert Mustacchi } 730*ae6d4bc3SRobert Mustacchi 731*ae6d4bc3SRobert Mustacchi return (0); 732eb00b1c8SRobert Mustacchi } 733eb00b1c8SRobert Mustacchi 734eb00b1c8SRobert Mustacchi static int 735eb00b1c8SRobert Mustacchi mc_channel_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, 736eb00b1c8SRobert Mustacchi nvlist_t *chan_nvl, uint_t id, const char *cmode) 737eb00b1c8SRobert Mustacchi { 738eb00b1c8SRobert Mustacchi int err; 739eb00b1c8SRobert Mustacchi tnode_t *chnode; 740eb00b1c8SRobert Mustacchi nvlist_t *fmri; 741eb00b1c8SRobert Mustacchi nvlist_t **dimms; 742eb00b1c8SRobert Mustacchi uint_t ndimms, i; 743eb00b1c8SRobert Mustacchi 744eb00b1c8SRobert Mustacchi if (mkrsrc(mod, pnode, DRAMCHANNEL, id, auth, &fmri) != 0) { 745eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: mkrsrc failed\n"); 746eb00b1c8SRobert Mustacchi return (-1); 747eb00b1c8SRobert Mustacchi } 748eb00b1c8SRobert Mustacchi 749eb00b1c8SRobert Mustacchi if ((chnode = topo_node_bind(mod, pnode, DRAMCHANNEL, id, fmri)) == 750eb00b1c8SRobert Mustacchi NULL) { 751eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: node bind failed" 752eb00b1c8SRobert Mustacchi " for dram-channel\n"); 753eb00b1c8SRobert Mustacchi nvlist_free(fmri); 754eb00b1c8SRobert Mustacchi return (-1); 755eb00b1c8SRobert Mustacchi } 756eb00b1c8SRobert Mustacchi 757eb00b1c8SRobert Mustacchi nvlist_free(fmri); 758eb00b1c8SRobert Mustacchi if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) { 759eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: fru set failed: " 760eb00b1c8SRobert Mustacchi "%d\n", err); 761eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 762eb00b1c8SRobert Mustacchi } 763eb00b1c8SRobert Mustacchi 764eb00b1c8SRobert Mustacchi if (topo_pgroup_create(chnode, &dimm_channel_pgroup, &err) != 0) { 765eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: failed to create " 766eb00b1c8SRobert Mustacchi "property group: %d\n", err); 767eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 768eb00b1c8SRobert Mustacchi } 769eb00b1c8SRobert Mustacchi 770eb00b1c8SRobert Mustacchi if (topo_prop_set_string(chnode, PGNAME(CHAN), CHAN_PROP_MODE, 771eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, cmode, &err) != 0) { 772eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 773eb00b1c8SRobert Mustacchi } 774eb00b1c8SRobert Mustacchi 775eb00b1c8SRobert Mustacchi if (nvlist_lookup_nvlist_array(chan_nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS, 776eb00b1c8SRobert Mustacchi &dimms, &ndimms) != 0) { 777eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: No DIMMS provided"); 778eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 779eb00b1c8SRobert Mustacchi } 780eb00b1c8SRobert Mustacchi 781eb00b1c8SRobert Mustacchi if (topo_node_range_create(mod, chnode, DIMM, 0, ndimms - 1) < 0) { 782eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: dimm node range " 783eb00b1c8SRobert Mustacchi "create failed\n"); 784eb00b1c8SRobert Mustacchi return (-1); 785eb00b1c8SRobert Mustacchi } 786eb00b1c8SRobert Mustacchi 787eb00b1c8SRobert Mustacchi for (i = 0; i < ndimms; i++) { 788eb00b1c8SRobert Mustacchi if (mc_dimm_create_v1(mod, chnode, auth, dimms[i], i) != 0) 789eb00b1c8SRobert Mustacchi return (-1); 790eb00b1c8SRobert Mustacchi } 791eb00b1c8SRobert Mustacchi 792eb00b1c8SRobert Mustacchi return (0); 793eb00b1c8SRobert Mustacchi } 794eb00b1c8SRobert Mustacchi 795eb00b1c8SRobert Mustacchi static int 796eb00b1c8SRobert Mustacchi mc_imc_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name, 797eb00b1c8SRobert Mustacchi nvlist_t *auth, nvlist_t *mc_nvl, uint_t id) 798eb00b1c8SRobert Mustacchi { 799eb00b1c8SRobert Mustacchi int err, ret; 800eb00b1c8SRobert Mustacchi tnode_t *mcnode; 801eb00b1c8SRobert Mustacchi nvlist_t *fmri, **channels; 802eb00b1c8SRobert Mustacchi boolean_t ecc; 803eb00b1c8SRobert Mustacchi char *page, *cmode; 804eb00b1c8SRobert Mustacchi uint_t nchans, i; 805eb00b1c8SRobert Mustacchi 806eb00b1c8SRobert Mustacchi if (mkrsrc(mod, pnode, name, id, auth, &fmri) != 0) { 807*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: mkrsrc failed\n"); 808eb00b1c8SRobert Mustacchi return (-1); 809eb00b1c8SRobert Mustacchi } 810eb00b1c8SRobert Mustacchi 811eb00b1c8SRobert Mustacchi if ((mcnode = topo_node_bind(mod, pnode, name, id, fmri)) == NULL) { 812*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: node bind failed" 813eb00b1c8SRobert Mustacchi " for memory-controller\n"); 814eb00b1c8SRobert Mustacchi nvlist_free(fmri); 815eb00b1c8SRobert Mustacchi return (-1); 816eb00b1c8SRobert Mustacchi } 817eb00b1c8SRobert Mustacchi 818eb00b1c8SRobert Mustacchi nvlist_free(fmri); 819eb00b1c8SRobert Mustacchi if (topo_node_fru_set(mcnode, NULL, 0, &err) != 0) { 820*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: fru set failed: " 821eb00b1c8SRobert Mustacchi "%d\n", err); 822eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 823eb00b1c8SRobert Mustacchi } 824eb00b1c8SRobert Mustacchi 825eb00b1c8SRobert Mustacchi if (topo_pgroup_create(mcnode, &mc_pgroup, &err) != 0) { 826*ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: failed to create " 827eb00b1c8SRobert Mustacchi "property group: %d\n", err); 828eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 829eb00b1c8SRobert Mustacchi } 830eb00b1c8SRobert Mustacchi 831eb00b1c8SRobert Mustacchi /* 832eb00b1c8SRobert Mustacchi * Add properties to the controller. Our contract allows for these 833eb00b1c8SRobert Mustacchi * properties to be missing. 834eb00b1c8SRobert Mustacchi */ 835eb00b1c8SRobert Mustacchi ret = 0; 836eb00b1c8SRobert Mustacchi if (nvlist_lookup_boolean_value(mc_nvl, MCINTEL_NVLIST_V1_MC_ECC, 837eb00b1c8SRobert Mustacchi &ecc) == 0) { 838eb00b1c8SRobert Mustacchi const char *pval = ecc ? "enabled" : "disabled"; 839*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_ECC, 840eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, pval, &err); 841*ae6d4bc3SRobert Mustacchi if (ret != 0) { 842*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 843*ae6d4bc3SRobert Mustacchi } 844eb00b1c8SRobert Mustacchi } 845eb00b1c8SRobert Mustacchi 846eb00b1c8SRobert Mustacchi if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_POLICY, 847eb00b1c8SRobert Mustacchi &page) == 0) { 848*ae6d4bc3SRobert Mustacchi ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_POLICY, 849eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, page, &err); 850*ae6d4bc3SRobert Mustacchi if (ret != 0) { 851*ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err)); 852*ae6d4bc3SRobert Mustacchi } 853eb00b1c8SRobert Mustacchi } 854eb00b1c8SRobert Mustacchi 855eb00b1c8SRobert Mustacchi if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE, 856eb00b1c8SRobert Mustacchi &cmode) != 0) { 857eb00b1c8SRobert Mustacchi cmode = NULL; 858eb00b1c8SRobert Mustacchi } 859eb00b1c8SRobert Mustacchi 860eb00b1c8SRobert Mustacchi if (nvlist_lookup_nvlist_array(mc_nvl, MCINTEL_NVLIST_V1_MC_CHANNELS, 861eb00b1c8SRobert Mustacchi &channels, &nchans) != 0) { 862eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: missing channels entry"); 863eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 864eb00b1c8SRobert Mustacchi } 865eb00b1c8SRobert Mustacchi 866eb00b1c8SRobert Mustacchi if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, 0, 867eb00b1c8SRobert Mustacchi nchans - 1) < 0) { 868eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: channel node range create " 869eb00b1c8SRobert Mustacchi "failed\n"); 870eb00b1c8SRobert Mustacchi return (-1); 871eb00b1c8SRobert Mustacchi } 872eb00b1c8SRobert Mustacchi 873eb00b1c8SRobert Mustacchi for (i = 0; i < nchans; i++) { 874eb00b1c8SRobert Mustacchi if (mc_channel_create_v1(mod, mcnode, auth, channels[i], i, 875eb00b1c8SRobert Mustacchi cmode) != 0) { 876eb00b1c8SRobert Mustacchi return (-1); 877eb00b1c8SRobert Mustacchi } 878eb00b1c8SRobert Mustacchi } 879eb00b1c8SRobert Mustacchi 880eb00b1c8SRobert Mustacchi return (0); 881eb00b1c8SRobert Mustacchi } 882eb00b1c8SRobert Mustacchi 883eb00b1c8SRobert Mustacchi static int 884eb00b1c8SRobert Mustacchi mc_nb_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name, 885eb00b1c8SRobert Mustacchi nvlist_t *auth, nvlist_t *nvl) 886eb00b1c8SRobert Mustacchi { 887eb00b1c8SRobert Mustacchi nvlist_t **mc_nvl; 888eb00b1c8SRobert Mustacchi uint_t nmc, i; 889eb00b1c8SRobert Mustacchi 890eb00b1c8SRobert Mustacchi if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS, 891eb00b1c8SRobert Mustacchi &mc_nvl, &nmc) != 0) { 892eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_nb_create_v1: failed to find memory " 893eb00b1c8SRobert Mustacchi "controller information\n"); 894eb00b1c8SRobert Mustacchi return (-1); 895eb00b1c8SRobert Mustacchi } 896eb00b1c8SRobert Mustacchi 897eb00b1c8SRobert Mustacchi if (topo_node_range_create(mod, pnode, name, 0, nmc - 1) < 0) { 898eb00b1c8SRobert Mustacchi whinge(mod, NULL, 899*ae6d4bc3SRobert Mustacchi "mc_nb_create_v1: node range create failed\n"); 900eb00b1c8SRobert Mustacchi return (-1); 901eb00b1c8SRobert Mustacchi } 902eb00b1c8SRobert Mustacchi 903eb00b1c8SRobert Mustacchi for (i = 0; i < nmc; i++) { 904eb00b1c8SRobert Mustacchi if (mc_imc_create_v1(mod, pnode, name, auth, mc_nvl[i], i) != 0) 905eb00b1c8SRobert Mustacchi return (-1); 906eb00b1c8SRobert Mustacchi } 907eb00b1c8SRobert Mustacchi 908eb00b1c8SRobert Mustacchi return (0); 909eb00b1c8SRobert Mustacchi } 910eb00b1c8SRobert Mustacchi 91120c794b3Sgavinm int 912074bb90dSTom Pothier mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 913074bb90dSTom Pothier const char *name, nvlist_t *auth) 91420c794b3Sgavinm { 91520c794b3Sgavinm mc_snapshot_info_t mcs; 91620c794b3Sgavinm void *buf = NULL; 91720c794b3Sgavinm nvlist_t *nvl; 91820c794b3Sgavinm uint8_t ver; 91920c794b3Sgavinm int rc; 92020c794b3Sgavinm 92120c794b3Sgavinm if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 92220c794b3Sgavinm (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 92320c794b3Sgavinm ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) { 92420c794b3Sgavinm 92520c794b3Sgavinm whinge(mod, NULL, "mc failed to snapshot %s\n", 92620c794b3Sgavinm strerror(errno)); 92720c794b3Sgavinm 92820c794b3Sgavinm free(buf); 92920c794b3Sgavinm (void) close(mc_fd); 9305cc5d5ceSToomas Soome return (0); 93120c794b3Sgavinm } 93220c794b3Sgavinm (void) close(mc_fd); 93320c794b3Sgavinm (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 93420c794b3Sgavinm topo_mod_free(mod, buf, mcs.mcs_size); 93520c794b3Sgavinm 93620c794b3Sgavinm if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) { 93720c794b3Sgavinm whinge(mod, NULL, "mc nvlist is not versioned\n"); 93820c794b3Sgavinm nvlist_free(nvl); 9395cc5d5ceSToomas Soome return (0); 940eb00b1c8SRobert Mustacchi } else if (ver != MCINTEL_NVLIST_VERS0 && 941eb00b1c8SRobert Mustacchi ver != MCINTEL_NVLIST_VERS1) { 94220c794b3Sgavinm whinge(mod, NULL, "mc nvlist version mismatch\n"); 94320c794b3Sgavinm nvlist_free(nvl); 9445cc5d5ceSToomas Soome return (0); 94520c794b3Sgavinm } 94620c794b3Sgavinm 947eb00b1c8SRobert Mustacchi if (ver == MCINTEL_NVLIST_VERS1) { 948eb00b1c8SRobert Mustacchi rc = mc_nb_create_v1(mod, pnode, name, auth, nvl); 949eb00b1c8SRobert Mustacchi } else { 950eb00b1c8SRobert Mustacchi rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl); 951eb00b1c8SRobert Mustacchi } 95220c794b3Sgavinm 95320c794b3Sgavinm nvlist_free(nvl); 95420c794b3Sgavinm return (rc); 95520c794b3Sgavinm } 956e3d60c9bSAdrian Frost 957e3d60c9bSAdrian Frost void 958074bb90dSTom Pothier onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 959074bb90dSTom Pothier const char *name, nvlist_t *auth) 960e3d60c9bSAdrian Frost { 961e3d60c9bSAdrian Frost if (mc_onchip(topo_node_instance(pnode))) 962074bb90dSTom Pothier (void) mc_node_create(mod, chip_smbid, pnode, name, auth); 963e3d60c9bSAdrian Frost } 964e3d60c9bSAdrian Frost 965e3d60c9bSAdrian Frost int 966e3d60c9bSAdrian Frost mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 967e3d60c9bSAdrian Frost nvlist_t *auth) 968e3d60c9bSAdrian Frost { 969074bb90dSTom Pothier return (mc_node_create(mod, IGNORE_ID, pnode, name, auth)); 970e3d60c9bSAdrian Frost } 971