chip_intel.c revision 20c794b39650d115e17a15983b6b82e46238cf45
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <alloca.h>
#include <kstat.h>
#include <fcntl.h>
#include <errno.h>
#include <libnvpair.h>
#include <sys/types.h>
#include <sys/bitmap.h>
#include <sys/processor.h>
#include <sys/param.h>
#include <sys/fm/protocol.h>
#include <sys/systeminfo.h>
#include <sys/mc.h>
#include <sys/mc_amd.h>
#include <sys/mc_intel.h>
#include <fm/topo_mod.h>
#include "chip.h"
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
static const topo_pgroup_info_t dimm_channel_pgroup =
{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_pgroup_info_t dimm_pgroup =
{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_pgroup_info_t rank_pgroup =
{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_pgroup_info_t mc_pgroup =
{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_method_t rank_methods[] = {
{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
mem_asru_compute },
{ NULL }
};
static const topo_method_t dimm_methods[] = {
{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
simple_dimm_label},
{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
simple_dimm_label_mp},
{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
seq_dimm_label},
{ NULL }
};
static int mc_fd;
int
mc_offchip_open()
{
mc_fd = open("/dev/mc/mc", O_RDONLY);
return (mc_fd != -1);
}
void
mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
nvlist_t **ranks_nvp, int nranks, char *serial, char *part, char *rev)
{
int i;
int rank;
tnode_t *rnode;
nvpair_t *nvp;
nvlist_t *fmri;
int err = 0;
rank = dimm * 2;
if (topo_node_range_create(mod, dnode, RANK, rank,
rank + nranks - 1) < 0) {
whinge(mod, NULL, "mc_add_dimms: node range create failed"
" for rank\n");
return;
}
for (i = 0; i < nranks; i++) {
fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
RANK, rank, NULL, auth, part, rev, serial);
if (fmri == NULL) {
whinge(mod, NULL,
"mc_add_ranks: topo_mod_hcfmri failed\n");
return;
}
if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
fmri)) == NULL) {
nvlist_free(fmri);
whinge(mod, NULL, "mc_add_ranks: node bind failed"
" for ranks\n");
return;
}
(void) topo_node_fru_set(rnode, NULL, 0, &err);
if (topo_method_register(mod, rnode, rank_methods) < 0)
whinge(mod, &err, "rank_create: "
"topo_method_register failed");
(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
nvlist_free(fmri);
(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
}
rank++;
}
}
static void
mc_add_dimms(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
nvlist_t **nvl, uint_t ndimms)
{
int i;
nvlist_t *fmri;
tnode_t *dnode;
nvpair_t *nvp;
int err;
nvlist_t **ranks_nvp;
uint_t nranks = 0;
char *serial = NULL;
char *part = NULL;
char *rev = NULL;
char *label = NULL;
char *name;
if (topo_node_range_create(mod, pnode, DIMM, 0, ndimms-1) < 0) {
whinge(mod, NULL,
"mc_add_dimms: node range create failed\n");
return;
}
for (i = 0; i < ndimms; i++) {
for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl[i], nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
(void) nvpair_value_nvlist_array(nvp,
&ranks_nvp, &nranks);
} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
(void) nvpair_value_string(nvp, &serial);
} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
(void) nvpair_value_string(nvp, &part);
} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
(void) nvpair_value_string(nvp, &rev);
} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
(void) nvpair_value_string(nvp, &label);
}
}
fmri = NULL;
fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
DIMM, i, NULL, auth, part, rev, serial);
if (fmri == NULL) {
whinge(mod, NULL,
"mc_add_dimms: topo_mod_hcfmri failed\n");
return;
}
if ((dnode = topo_node_bind(mod, pnode, DIMM, i,
fmri)) == NULL) {
nvlist_free(fmri);
whinge(mod, NULL, "mc_add_dimms: node bind failed"
" for dimm\n");
return;
}
if (topo_method_register(mod, dnode, dimm_methods) < 0)
whinge(mod, NULL, "mc_add_dimms: "
"topo_method_register failed");
(void) topo_node_fru_set(dnode, fmri, 0, &err);
nvlist_free(fmri);
(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl[i], nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
strcmp(name, FM_FAULT_FRU_LABEL) != 0) {
(void) nvprop_add(mod, nvp, PGNAME(DIMM),
dnode);
}
}
if (label)
(void) topo_node_label_set(dnode, label, &err);
if (nranks) {
mc_add_ranks(mod, dnode, auth, i, ranks_nvp, nranks,
serial, part, rev);
}
}
}
static int
mc_add_channel(topo_mod_t *mod, tnode_t *pnode, int channel, nvlist_t *auth,
nvlist_t *nvl)
{
tnode_t *mc_channel;
nvlist_t *fmri;
nvlist_t **dimm_nvl;
uint_t ndimms;
int err;
if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
return (-1);
}
if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
fmri)) == NULL) {
whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
DRAMCHANNEL);
nvlist_free(fmri);
return (-1);
}
(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
nvlist_free(fmri);
(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
&ndimms) == 0) {
mc_add_dimms(mod, mc_channel, auth, dimm_nvl, ndimms);
}
return (0);
}
static int
mc_nb_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
nvlist_t *nvl)
{
int err;
int i, j;
int channel;
int nmc;
tnode_t *mcnode;
nvlist_t *fmri;
nvlist_t **channel_nvl;
uint_t nchannels;
if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
&nchannels) != 0) {
whinge(mod, NULL,
"mc_nb_create: failed to find channel information\n");
return (-1);
}
nmc = nchannels / 2;
if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
whinge(mod, NULL,
"mc_nb_create: node range create failed\n");
return (-1);
}
channel = 0;
for (i = 0; i < nmc; i++) {
if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
return (-1);
}
if ((mcnode = topo_node_bind(mod, pnode, name, i,
fmri)) == NULL) {
whinge(mod, NULL, "chip_create: node bind failed"
" for memory-controller\n");
nvlist_free(fmri);
return (-1);
}
(void) topo_node_fru_set(mcnode, NULL, 0, &err);
nvlist_free(fmri);
(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
channel + 1) < 0) {
whinge(mod, NULL,
"mc_nb_create: channel node range create failed\n");
return (-1);
}
for (j = 0; j < 2; j++) {
if (mc_add_channel(mod, mcnode, channel, auth,
channel_nvl[channel]) < 0) {
return (-1);
}
channel++;
}
}
return (NULL);
}
int
mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
nvlist_t *auth)
{
mc_snapshot_info_t mcs;
void *buf = NULL;
nvlist_t *nvl;
uint8_t ver;
int rc;
if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
(buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
whinge(mod, NULL, "mc failed to snapshot %s\n",
strerror(errno));
free(buf);
(void) close(mc_fd);
return (NULL);
}
(void) close(mc_fd);
(void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
topo_mod_free(mod, buf, mcs.mcs_size);
if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
whinge(mod, NULL, "mc nvlist is not versioned\n");
nvlist_free(nvl);
return (NULL);
} else if (ver != MCINTEL_NVLIST_VERS0) {
whinge(mod, NULL, "mc nvlist version mismatch\n");
nvlist_free(nvl);
return (NULL);
}
rc = mc_nb_create(mod, pnode, name, auth, nvl);
nvlist_free(nvl);
return (rc);
}