2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * AMD memory enumeration
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <unistd.h>
2N/A#include <stropts.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <sys/mc.h>
2N/A#include <sys/mc_amd.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <strings.h>
2N/A#include <sys/stat.h>
2N/A#include <fcntl.h>
2N/A
2N/A#include "chip.h"
2N/A
2N/A#define MAX_CHANNUM 1
2N/A#define MAX_DIMMNUM 7
2N/A#define MAX_CSNUM 7
2N/A
2N/Aextern const topo_method_t rank_methods[];
2N/A
2N/Astatic const topo_pgroup_info_t cs_pgroup =
2N/A { PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/Astatic const topo_pgroup_info_t dimm_pgroup =
2N/A { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/Astatic const topo_pgroup_info_t mc_pgroup =
2N/A { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/Astatic const topo_pgroup_info_t rank_pgroup =
2N/A { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/Astatic const topo_pgroup_info_t chan_pgroup =
2N/A { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/A
2N/Astatic const topo_method_t dimm_methods[] = {
2N/A { SIMPLE_DIMM_LBL, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, simple_dimm_label},
2N/A { SIMPLE_DIMM_LBL_MP, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, simple_dimm_label_mp},
2N/A { SEQ_DIMM_LBL, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, seq_dimm_label},
2N/A { G4_DIMM_LBL, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, g4_dimm_label},
2N/A { G12F_DIMM_LBL, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, g12f_dimm_label},
2N/A { GET_DIMM_SERIAL, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, get_dimm_serial},
2N/A { NULL }
2N/A};
2N/A
2N/Aconst topo_method_t ntv_page_retire_methods[] = {
2N/A { TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
2N/A TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
2N/A ntv_page_retire },
2N/A { TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
2N/A TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
2N/A ntv_page_unretire },
2N/A { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
2N/A TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
2N/A ntv_page_service_state },
2N/A { NULL }
2N/A};
2N/A
2N/A/*
2N/A * Serials, Labels are obtained from SMBIOS, so
2N/A * we leave out the related methods, any other
2N/A * methods that will be added to gen_cs_methods
2N/A * should be added to x86pi_gen_cs_methods too
2N/A */
2N/Astatic const topo_method_t x86pi_gen_cs_methods[] = {
2N/A { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
2N/A TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
2N/A mem_asru_compute },
2N/A { NULL }
2N/A};
2N/A
2N/Astatic const topo_method_t gen_cs_methods[] = {
2N/A { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
2N/A TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
2N/A mem_asru_compute },
2N/A { SIMPLE_CS_LBL_MP, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, simple_cs_label_mp},
2N/A { GET_DIMM_SERIAL, "Property method", 0,
2N/A TOPO_STABILITY_INTERNAL, get_dimm_serial},
2N/A { NULL }
2N/A};
2N/A
2N/Astatic nvlist_t *cs_fmri[MC_CHIP_NCS];
2N/A
2N/A/*
2N/A * Called when there is no memory-controller driver to provide topology
2N/A * information. Generate a maximal memory topology that is appropriate
2N/A * for the chip revision. The memory-controller node has already been
2N/A * bound as mcnode, and the parent of that is cnode.
2N/A *
2N/A * We create a tree of dram-channel and chip-select nodes below the
2N/A * memory-controller node. There will be two dram channels and 8 chip-selects
2N/A * below each, regardless of actual socket type, processor revision and so on.
2N/A * This is adequate for generic diagnosis up to family 0x10 revision E and
2N/A * models 00h - 0Fh of family 0x15.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Aamd_generic_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *cnode,
2N/A tnode_t *mcnode, int family, int model, nvlist_t *auth)
2N/A{
2N/A int chan, cs;
2N/A
2N/A /*
2N/A * Elsewhere we have already returned for families less than 0xf.
2N/A * This "generic" topology is adequate for all of family 0xf and
2N/A * for revisions A to E of family 0x10, and for models 00h - 0Fh
2N/A * of family 0x15 (for the list of models in each revision, refer
2N/A * to usr/src/uts/i86pc/os/cpuid_subr.c).
2N/A * We cover all family 0x10 models, till model A.
2N/A */
2N/A switch (family) {
2N/A case 0xf:
2N/A /* supports all revisions of family 0xf */
2N/A break;
2N/A case 0x10:
2N/A /* supports revisions A to E of family 0x10 */
2N/A if (model > 0xa)
2N/A return (1);
2N/A break;
2N/A case 0x15:
2N/A /* supports family 0x15 models 00h-0Fh */
2N/A if (model > 0xf)
2N/A return (1);
2N/A break;
2N/A default:
2N/A return (1);
2N/A }
2N/A
2N/A if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0,
2N/A MAX_CHANNUM) < 0) {
2N/A whinge(mod, NULL, "amd_generic_mc_create: range create for "
2N/A "channels failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A for (chan = 0; chan <= MAX_CHANNUM; chan++) {
2N/A tnode_t *chnode;
2N/A nvlist_t *fmri;
2N/A int err;
2N/A
2N/A if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth,
2N/A &fmri) != 0) {
2N/A whinge(mod, NULL, "amd_generic_mc_create: mkrsrc "
2N/A "failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME,
2N/A chan, fmri)) == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, NULL, "amd_generic_mc_create: node "
2N/A "bind failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A nvlist_free(fmri);
2N/A
2N/A (void) topo_pgroup_create(chnode, &chan_pgroup, &err);
2N/A
2N/A (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
2N/A TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err);
2N/A
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A if (topo_node_label_set(chnode, NULL, &err) == -1)
2N/A whinge(mod, NULL, "amd_generic_mc_create: "
2N/A "topo_node_label_set\n");
2N/A if (topo_node_fru_set(chnode, NULL, 0, &err) != 0)
2N/A whinge(mod, NULL, "amd_generic_mc_create: "
2N/A "topo_node_fru_set failed\n");
2N/A }
2N/A
2N/A if (topo_node_range_create(mod, chnode, CS_NODE_NAME,
2N/A 0, MAX_CSNUM) < 0) {
2N/A whinge(mod, NULL, "amd_generic_mc_create: "
2N/A "range create for cs failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A for (cs = 0; cs <= MAX_CSNUM; cs++) {
2N/A tnode_t *csnode;
2N/A
2N/A if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth,
2N/A &fmri) != 0) {
2N/A whinge(mod, NULL, "amd_generic_mc_create: "
2N/A "mkrsrc for cs failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME,
2N/A cs, fmri)) == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, NULL, "amd_generic_mc_create: "
2N/A "bind for cs failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Dynamic ASRU for page faults within a chip-select.
2N/A * The topology does not represent pages (there are
2N/A * too many) so when a page is faulted we generate
2N/A * an ASRU to represent the individual page.
2N/A * If SMBIOS meets FMA needs, derive labels & serials
2N/A * for DIMMS and apply to chip-select nodes.
2N/A * If deriving from SMBIOS, skip IPMI
2N/A */
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A if (topo_method_register(mod, csnode,
2N/A x86pi_gen_cs_methods) < 0)
2N/A whinge(mod, NULL,
2N/A "amd_generic_mc_create: "
2N/A "method registration failed\n");
2N/A } else {
2N/A if (topo_method_register(mod, csnode,
2N/A gen_cs_methods) < 0)
2N/A whinge(mod, NULL,
2N/A "amd_generic_mc_create: method"
2N/A "registration failed\n");
2N/A }
2N/A
2N/A (void) topo_node_asru_set(csnode, fmri,
2N/A TOPO_ASRU_COMPUTE, &err);
2N/A nvlist_free(fmri);
2N/A
2N/A /*
2N/A * If SMBIOS meets FMA needs, set DIMM as the FRU for
2N/A * the chip-select node. Use the channel & chip-select
2N/A * numbers to get the DIMM instance.
2N/A * Send via inst : dram channel number
2N/A * Receive via inst : dimm instance
2N/A */
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A int inst;
2N/A id_t dimm_smbid;
2N/A const char *serial;
2N/A const char *part;
2N/A const char *rev;
2N/A char *label;
2N/A
2N/A (void) topo_pgroup_create(csnode,
2N/A &cs_pgroup, &err);
2N/A inst = chan;
2N/A dimm_smbid = memnode_to_smbiosid(mod, smbid,
2N/A CS_NODE_NAME, cs, &inst);
2N/A serial = chip_serial_smbios_get(mod,
2N/A dimm_smbid);
2N/A part = chip_part_smbios_get(mod,
2N/A dimm_smbid);
2N/A rev = chip_rev_smbios_get(mod, dimm_smbid);
2N/A label = (char *)chip_label_smbios_get(mod,
2N/A chnode, dimm_smbid, NULL);
2N/A
2N/A (void) topo_prop_set_string(csnode, PGNAME(CS),
2N/A FM_FMRI_HC_V1_FRU_SN, TOPO_PROP_IMMUTABLE,
2N/A serial, &err);
2N/A (void) topo_prop_set_string(csnode, PGNAME(CS),
2N/A FM_FMRI_HC_V1_FRU_PN, TOPO_PROP_IMMUTABLE,
2N/A part, &err);
2N/A (void) topo_prop_set_string(csnode, PGNAME(CS),
2N/A FM_FMRI_HC_V1_FRU_REV, TOPO_PROP_IMMUTABLE,
2N/A rev, &err);
2N/A
2N/A /*
2N/A * We apply DIMM labels to chip-select nodes,
2N/A * FRU for chip-selects should be DIMMs, and
2N/A * we do not derive dimm nodes for Family 0x10
2N/A * so FRU fmri is NULL, but FRU Labels are set,
2N/A * the FRU labels point to the DIMM.
2N/A */
2N/A (void) topo_node_label_set(csnode, label, &err);
2N/A topo_mod_strfree(mod, label);
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic nvlist_t *
2N/Aamd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
2N/A{
2N/A mc_snapshot_info_t mcs;
2N/A void *buf = NULL;
2N/A uint8_t ver;
2N/A
2N/A nvlist_t *nvl = NULL;
2N/A char path[64];
2N/A int fd, err;
2N/A
2N/A (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
2N/A fd = open(path, O_RDONLY);
2N/A
2N/A if (fd == -1) {
2N/A /*
2N/A * Some v20z and v40z systems may have had the 3rd-party
2N/A * NWSnps packagae installed which installs a /dev/mc
2N/A * link. So try again via /devices.
2N/A */
2N/A (void) snprintf(path, sizeof (path),
2N/A "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
2N/A MC_AMD_DEV_OFFSET + id);
2N/A fd = open(path, O_RDONLY);
2N/A }
2N/A
2N/A if (fd == -1)
2N/A return (NULL); /* do not whinge */
2N/A
2N/A if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
2N/A (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
2N/A ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
2N/A
2N/A whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
2N/A path, strerror(errno));
2N/A
2N/A if (buf != NULL)
2N/A topo_mod_free(mod, buf, mcs.mcs_size);
2N/A (void) close(fd);
2N/A return (NULL);
2N/A }
2N/A
2N/A (void) close(fd);
2N/A err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
2N/A topo_mod_free(mod, buf, mcs.mcs_size);
2N/A
2N/A if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
2N/A whinge(mod, NULL, "mc nvlist is not versioned\n");
2N/A nvlist_free(nvl);
2N/A return (NULL);
2N/A } else if (ver != MC_NVLIST_VERS1) {
2N/A whinge(mod, NULL, "mc nvlist version mismatch\n");
2N/A nvlist_free(nvl);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (err ? NULL : nvl);
2N/A}
2N/A
2N/Aint
2N/Aamd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl,
2N/A nvlist_t *auth)
2N/A{
2N/A uint64_t *csnumarr;
2N/A char **csnamearr;
2N/A uint_t ncs, ncsname;
2N/A tnode_t *ranknode;
2N/A nvlist_t *fmri, *pfmri = NULL;
2N/A uint64_t dsz, rsz;
2N/A int nerr = 0;
2N/A int err;
2N/A int i;
2N/A
2N/A if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
2N/A &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
2N/A &csnamearr, &ncsname) != 0 || ncs != ncsname) {
2N/A whinge(mod, &nerr, "amd_rank_create: "
2N/A "csnums/csnames extraction failed\n");
2N/A return (nerr);
2N/A }
2N/A
2N/A if (topo_node_resource(pnode, &pfmri, &err) < 0) {
2N/A whinge(mod, &nerr, "amd_rank_create: parent fmri lookup "
2N/A "failed\n");
2N/A return (nerr);
2N/A }
2N/A
2N/A if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
2N/A whinge(mod, &nerr, "amd_rank_create: range create failed\n");
2N/A nvlist_free(pfmri);
2N/A return (nerr);
2N/A }
2N/A
2N/A if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
2N/A &err) == 0) {
2N/A rsz = dsz / ncs;
2N/A } else {
2N/A whinge(mod, &nerr, "amd_rank_create: parent dimm has no "
2N/A "size\n");
2N/A return (nerr);
2N/A }
2N/A
2N/A for (i = 0; i < ncs; i++) {
2N/A if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
2N/A whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n");
2N/A continue;
2N/A }
2N/A
2N/A if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
2N/A fmri)) == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, &nerr, "amd_rank_create: node bind "
2N/A "failed\n");
2N/A continue;
2N/A }
2N/A
2N/A nvlist_free(fmri);
2N/A if (FM_AWARE_SMBIOS(mod))
2N/A (void) topo_node_fru_set(ranknode, NULL, 0, &err);
2N/A else
2N/A (void) topo_node_fru_set(ranknode, pfmri, 0, &err);
2N/A
2N/A /*
2N/A * If a rank is faulted the asru is the associated
2N/A * chip-select, but if a page within a rank is faulted
2N/A * the asru is just that page. Hence the dual preconstructed
2N/A * and computed ASRU.
2N/A */
2N/A if (topo_method_register(mod, ranknode, rank_methods) < 0)
2N/A whinge(mod, &nerr, "amd_rank_create: "
2N/A "topo_method_register failed");
2N/A
2N/A if (! is_xpv() && topo_method_register(mod, ranknode,
2N/A ntv_page_retire_methods) < 0)
2N/A whinge(mod, &nerr, "amd_rank_create: "
2N/A "topo_method_register failed");
2N/A
2N/A (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
2N/A TOPO_ASRU_COMPUTE, &err);
2N/A
2N/A (void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
2N/A
2N/A (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
2N/A TOPO_PROP_IMMUTABLE, rsz, &err);
2N/A
2N/A (void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
2N/A TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
2N/A
2N/A (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
2N/A TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
2N/A }
2N/A
2N/A nvlist_free(pfmri);
2N/A
2N/A return (nerr);
2N/A}
2N/A
2N/Astatic int
2N/Aamd_dimm_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
2N/A const char *name, nvlist_t *mc, nvlist_t *auth)
2N/A{
2N/A int i, err, nerr = 0;
2N/A int perr = 0;
2N/A nvpair_t *nvp;
2N/A tnode_t *dimmnode;
2N/A nvlist_t *fmri, **dimmarr = NULL;
2N/A uint64_t num;
2N/A uint_t ndimm;
2N/A id_t smbid;
2N/A const char *serial;
2N/A const char *part;
2N/A const char *rev;
2N/A
2N/A if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
2N/A whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A if (ndimm == 0)
2N/A return (0); /* no dimms present on this node */
2N/A
2N/A if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
2N/A whinge(mod, NULL, "amd_dimm_create: range create failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A for (i = 0; i < ndimm; i++) {
2N/A if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
2N/A whinge(mod, &nerr, "amd_dimm_create: dimm num property "
2N/A "missing\n");
2N/A continue;
2N/A }
2N/A
2N/A if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
2N/A whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n");
2N/A continue;
2N/A }
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A smbid = memnode_to_smbiosid(mod, chip_smbid,
2N/A DIMM_NODE_NAME, i, NULL);
2N/A serial = chip_serial_smbios_get(mod, smbid);
2N/A part = chip_part_smbios_get(mod, smbid);
2N/A rev = chip_rev_smbios_get(mod, smbid);
2N/A perr += nvlist_add_string(fmri, FM_FMRI_HC_V1_FRU_SN,
2N/A serial);
2N/A perr += nvlist_add_string(fmri, FM_FMRI_HC_V1_FRU_PN,
2N/A part);
2N/A perr += nvlist_add_string(fmri, FM_FMRI_HC_V1_FRU_REV,
2N/A rev);
2N/A
2N/A if (perr != 0)
2N/A whinge(mod, NULL, "amd_dimm_create:"
2N/A "nvlist_add_string failed\n");
2N/A }
2N/A
2N/A if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
2N/A == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, &nerr, "amd_dimm_create: node bind "
2N/A "failed\n");
2N/A continue;
2N/A }
2N/A
2N/A if (!FM_AWARE_SMBIOS(mod))
2N/A if (topo_method_register(mod,
2N/A dimmnode, dimm_methods) < 0)
2N/A whinge(mod, &nerr, "amd_dimm_create: "
2N/A "topo_method_register failed");
2N/A
2N/A (void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
2N/A
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A char *label;
2N/A
2N/A nvlist_free(fmri);
2N/A (void) topo_node_resource(dimmnode,
2N/A &fmri, &err);
2N/A
2N/A label = (char *)chip_label_smbios_get(mod,
2N/A pnode, smbid, NULL);
2N/A if (topo_node_label_set(dimmnode, label,
2N/A &perr) == -1)
2N/A topo_mod_dprintf(mod, "Failed"
2N/A "to set label\n");
2N/A topo_mod_strfree(mod, label);
2N/A
2N/A (void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
2N/A FM_FMRI_HC_V1_FRU_SN, TOPO_PROP_IMMUTABLE,
2N/A serial, &err);
2N/A (void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
2N/A FM_FMRI_HC_V1_FRU_PN, TOPO_PROP_IMMUTABLE,
2N/A part, &err);
2N/A (void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
2N/A FM_FMRI_HC_V1_FRU_REV, TOPO_PROP_IMMUTABLE,
2N/A rev, &err);
2N/A }
2N/A
2N/A (void) topo_node_asru_set(dimmnode, fmri, 0, &err);
2N/A (void) topo_node_fru_set(dimmnode, fmri, 0, &err);
2N/A nvlist_free(fmri);
2N/A
2N/A for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
2N/A if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
2N/A strcmp(nvpair_name(nvp), "csnums") == 0 ||
2N/A nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
2N/A strcmp(nvpair_name(nvp), "csnames") == 0)
2N/A continue; /* used in amd_rank_create() */
2N/A
2N/A nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
2N/A }
2N/A
2N/A nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth);
2N/A }
2N/A
2N/A return (nerr == 0 ? 0 : -1);
2N/A}
2N/A
2N/Astatic int
2N/Aamd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
2N/A nvlist_t *auth)
2N/A{
2N/A int i, err, nerr = 0;
2N/A nvpair_t *nvp;
2N/A tnode_t *csnode;
2N/A nvlist_t *fmri, **csarr = NULL;
2N/A uint64_t csnum;
2N/A uint_t ncs;
2N/A
2N/A if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
2N/A return (-1);
2N/A
2N/A if (ncs == 0)
2N/A return (0); /* no chip-selects configured on this node */
2N/A
2N/A if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
2N/A return (-1);
2N/A
2N/A for (i = 0; i < ncs; i++) {
2N/A if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
2N/A whinge(mod, &nerr, "amd_cs_create: cs num property "
2N/A "missing\n");
2N/A continue;
2N/A }
2N/A
2N/A if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
2N/A whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n");
2N/A continue;
2N/A }
2N/A
2N/A if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
2N/A == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, &nerr, "amd_cs_create: node bind failed\n");
2N/A continue;
2N/A }
2N/A
2N/A cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */
2N/A
2N/A (void) topo_node_asru_set(csnode, fmri, 0, &err);
2N/A
2N/A (void) topo_node_fru_set(csnode, fmri, 0, &err);
2N/A
2N/A (void) topo_pgroup_create(csnode, &cs_pgroup, &err);
2N/A
2N/A for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(csarr[i], nvp)) {
2N/A nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
2N/A }
2N/A }
2N/A
2N/A return (nerr == 0 ? 0 : -1);
2N/A}
2N/A
2N/Astatic int
2N/Aamd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
2N/A nvlist_t *auth)
2N/A{
2N/A tnode_t *chnode;
2N/A nvlist_t *fmri;
2N/A char *socket;
2N/A int i, nchan;
2N/A nvlist_t *pfmri = NULL;
2N/A int err, nerr = 0;
2N/A
2N/A /*
2N/A * We will enumerate the number of channels present even if only
2N/A * channel A is in use (i.e., running in 64-bit mode). Only
2N/A * the socket 754 package has a single channel.
2N/A */
2N/A if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
2N/A &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0)
2N/A nchan = 1;
2N/A else
2N/A nchan = 2;
2N/A
2N/A topo_mod_strfree(mod, socket);
2N/A
2N/A if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
2N/A return (-1);
2N/A
2N/A (void) topo_node_fru(pnode, &pfmri, NULL, &err);
2N/A
2N/A for (i = 0; i < nchan; i++) {
2N/A if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
2N/A whinge(mod, &nerr, "amd_dramchan_create: mkrsrc "
2N/A "failed\n");
2N/A continue;
2N/A }
2N/A
2N/A if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
2N/A == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, &nerr, "amd_dramchan_create: node bind "
2N/A "failed\n");
2N/A continue;
2N/A }
2N/A
2N/A (void) topo_node_asru_set(chnode, fmri, 0, &err);
2N/A if (pfmri)
2N/A (void) topo_node_fru_set(chnode, pfmri, 0, &err);
2N/A
2N/A nvlist_free(fmri);
2N/A
2N/A (void) topo_pgroup_create(chnode, &chan_pgroup, &err);
2N/A
2N/A (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
2N/A TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
2N/A }
2N/A if (pfmri)
2N/A nvlist_free(pfmri);
2N/A
2N/A return (nerr == 0 ? 0 : -1);
2N/A}
2N/A
2N/Astatic int
2N/Aamd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
2N/A{
2N/A nvpair_t *nvp;
2N/A int nerr = 0;
2N/A
2N/A if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
2N/A whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
2N/A return (-1);
2N/A }
2N/A
2N/A for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(htnvl, nvp)) {
2N/A if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
2N/A nerr++;
2N/A }
2N/A
2N/A return (nerr == 0 ? 0 : -1);
2N/A}
2N/A
2N/Avoid
2N/Aamd_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *pnode,
2N/A const char *name, nvlist_t *auth, int32_t procnodeid,
2N/A int32_t procnodes_per_pkg, int family,
2N/A int model, int *nerrp)
2N/A{
2N/A tnode_t *mcnode;
2N/A nvlist_t *rfmri, *fmri;
2N/A nvpair_t *nvp;
2N/A nvlist_t *mc = NULL;
2N/A int i, err;
2N/A int mcnum = procnodeid % procnodes_per_pkg;
2N/A char *serial = NULL;
2N/A char *part = NULL;
2N/A char *rev = NULL;
2N/A
2N/A /*
2N/A * Return with no error for anything before AMD family 0xf - we
2N/A * won't generate even a generic memory topology for earlier
2N/A * families.
2N/A */
2N/A if (family < 0xf)
2N/A return;
2N/A
2N/A if (topo_node_lookup(pnode, name, mcnum) != NULL)
2N/A return;
2N/A
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A (void) topo_node_resource(pnode, &rfmri, &err);
2N/A (void) nvlist_lookup_string(rfmri, "serial", &serial);
2N/A (void) nvlist_lookup_string(rfmri, "part", &part);
2N/A (void) nvlist_lookup_string(rfmri, "revision", &rev);
2N/A }
2N/A
2N/A if (mkrsrc(mod, pnode, name, mcnum, auth, &fmri) != 0) {
2N/A if (FM_AWARE_SMBIOS(mod))
2N/A nvlist_free(rfmri);
2N/A whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
2N/A return;
2N/A }
2N/A
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A (void) nvlist_add_string(fmri, "serial", serial);
2N/A (void) nvlist_add_string(fmri, "part", part);
2N/A (void) nvlist_add_string(fmri, "revision", rev);
2N/A nvlist_free(rfmri);
2N/A }
2N/A
2N/A if ((mcnode = topo_node_bind(mod, pnode, name, mcnum,
2N/A fmri)) == NULL) {
2N/A nvlist_free(fmri);
2N/A whinge(mod, nerrp, "mc_create: mc bind failed\n");
2N/A return;
2N/A }
2N/A if (topo_node_fru_set(mcnode, NULL, 0, &err) < 0)
2N/A whinge(mod, nerrp, "mc_create: topo_node_fru_set failed\n");
2N/A
2N/A if (FM_AWARE_SMBIOS(mod)) {
2N/A if (topo_node_label_set(mcnode, NULL, &err) == -1)
2N/A topo_mod_dprintf(mod, "Failed to set label\n");
2N/A }
2N/A
2N/A nvlist_free(fmri);
2N/A
2N/A if (topo_pgroup_create(mcnode, &mc_pgroup, &err) < 0)
2N/A whinge(mod, nerrp, "mc_create: topo_pgroup_create failed\n");
2N/A
2N/A if (topo_prop_set_int32(mcnode, PGNAME(MCT), MCT_PROCNODE_ID,
2N/A TOPO_PROP_IMMUTABLE, procnodeid, nerrp) != 0)
2N/A whinge(mod, nerrp, "mc_create: topo_prop_set_int32 failed to"
2N/A "add node id\n");
2N/A
2N/A if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
2N/A /*
2N/A * If a memory-controller driver exists for this chip model
2N/A * it has not attached or has otherwise malfunctioned;
2N/A * alternatively no memory-controller driver exists for this
2N/A * (presumably newly-released) cpu model. We fallback to
2N/A * creating a generic maximal topology.
2N/A */
2N/A if (amd_generic_mc_create(mod, smbid, pnode, mcnode,
2N/A family, model, auth) != 0)
2N/A whinge(mod, nerrp,
2N/A "mc_create: amd_generic_mc_create failed\n");
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Add memory controller properties
2N/A */
2N/A for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(mc, nvp)) {
2N/A char *name = nvpair_name(nvp);
2N/A data_type_t type = nvpair_type(nvp);
2N/A
2N/A if (type == DATA_TYPE_NVLIST_ARRAY &&
2N/A (strcmp(name, "cslist") == 0 ||
2N/A strcmp(name, "dimmlist") == 0)) {
2N/A continue;
2N/A } else if (type == DATA_TYPE_UINT8 &&
2N/A strcmp(name, MC_NVLIST_VERSTR) == 0) {
2N/A continue;
2N/A } else if (type == DATA_TYPE_NVLIST &&
2N/A strcmp(name, "htconfig") == 0) {
2N/A nvlist_t *htnvl;
2N/A
2N/A (void) nvpair_value_nvlist(nvp, &htnvl);
2N/A if (amd_htconfig(mod, pnode, htnvl) != 0)
2N/A whinge(mod, nerrp,
2N/A "mc_create: amd_htconfig failed\n");
2N/A } else {
2N/A if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
2N/A whinge(mod, nerrp,
2N/A "mc_create: nvprop_add failed\n");
2N/A }
2N/A }
2N/A
2N/A if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
2N/A amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
2N/A amd_dimm_create(mod, smbid, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
2N/A whinge(mod, nerrp, "mc_create: create children failed\n");
2N/A
2N/A /*
2N/A * Free the fmris for the chip-selects allocated in amd_cs_create
2N/A */
2N/A for (i = 0; i < MC_CHIP_NCS; i++) {
2N/A if (cs_fmri[i] != NULL) {
2N/A nvlist_free(cs_fmri[i]);
2N/A cs_fmri[i] = NULL;
2N/A }
2N/A }
2N/A
2N/A nvlist_free(mc);
2N/A}