/*
* 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/devfm.h>
#include <libnvpair.h>
#include <sys/smbios.h>
#include <fm/topo_mod.h>
#include <sys/fm/protocol.h>
#include <sys/smbios_impl.h>
#include "chip.h"
#define CPU_SLOTS 64
#define DIMM_SLOTS 512
#define MC_INSTANCES 128
#define MAXNAMELEN 256
#define LABEL 1
#define SKIP_CS 9999
typedef struct cpu_smbios {
id_t cpu_id;
uint8_t status;
uint8_t fru;
}csmb_t;
typedef struct dimm_smbios {
id_t dimm_id;
id_t extdimm_id;
const char *bankloc;
}dsmb_t;
typedef struct mct_smbios {
id_t extmct_id;
id_t mct_id;
id_t p_id;
}msmb_t;
csmb_t cpusmb[CPU_SLOTS];
dsmb_t dimmsmb[DIMM_SLOTS];
msmb_t mctsmb[MC_INSTANCES];
static int ncpu_ids = 0;
static int bb_count = 0;
static int ndimm_ids, nmct_ids = 0;
static int fill_chip_smbios = 0;
typedef int smbios_rec_f(topo_mod_t *, const smbios_struct_t *);
static smbios_struct_t *
smb_export(const smb_struct_t *stp, smbios_struct_t *sp)
{
const smb_header_t *hdr;
if (stp == NULL)
return (NULL);
hdr = stp->smbst_hdr;
sp->smbstr_id = hdr->smbh_hdl;
sp->smbstr_type = hdr->smbh_type;
sp->smbstr_data = hdr;
sp->smbstr_size = (size_t)(stp->smbst_end - (uchar_t *)hdr);
return (sp);
}
static int
extdimmslot_to_dimmslot(topo_mod_t *mod, id_t chip_smbid, int channum,
int csnum)
{
smbios_memdevice_ext_t emd;
smbios_memdevice_t md;
int i, j, k;
int match = 0;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp == NULL)
return (-1);
if (chip_smbid == IGNORE_ID && bb_count <= 1 && nmct_ids <= 1) {
for (i = 0; i < ndimm_ids; i++) {
(void) smbios_info_extmemdevice(shp,
dimmsmb[i].extdimm_id, &emd);
if (emd.smbmdeve_drch == channum) {
if (csnum == SKIP_CS)
return (emd.smbmdeve_md);
for (k = 0; k < emd.smbmdeve_ncs; k++)
if (emd.smbmdeve_cs[k] == csnum)
return (emd.smbmdeve_md);
}
}
}
for (j = 0; j < nmct_ids; j++) {
if (mctsmb[j].p_id == chip_smbid) {
for (i = 0; i < ndimm_ids; i++) {
(void) smbios_info_extmemdevice(shp,
dimmsmb[i].extdimm_id, &emd);
(void) smbios_info_memdevice(shp,
emd.smbmdeve_md, &md);
if (md.smbmd_array == mctsmb[j].mct_id &&
emd.smbmdeve_drch == channum) {
match = 1;
break;
}
}
if (match) {
if (csnum == SKIP_CS)
return (emd.smbmdeve_md);
for (k = 0; k < emd.smbmdeve_ncs; k++)
if (emd.smbmdeve_cs[k] == csnum)
return (emd.smbmdeve_md);
}
}
}
return (-1);
}
id_t
memnode_to_smbiosid(topo_mod_t *mod, uint16_t chip_smbid, const char *name,
uint64_t nodeid, void *data)
{
if (strcmp(name, CS_NODE_NAME) == 0) {
int channum, csnum;
id_t dimmslot = -1;
if (data == NULL)
return (-1);
channum = *(int *)data;
csnum = nodeid;
/*
* Set the DIMM Slot label to the Chip Select Node
* Set the "data" to carry the DIMM instance
*/
dimmslot = extdimmslot_to_dimmslot(mod, chip_smbid, channum,
csnum);
if (dimmslot != -1 && dimmsmb[0].dimm_id != 0)
*((id_t *)data) = dimmslot % (dimmsmb[0].dimm_id);
else
*((id_t *)data) = -1;
return (dimmslot);
} else if (strcmp(name, DIMM_NODE_NAME) == 0) {
static int dimmnum = 0;
/*
* On certain Intel Chips, topology does not have
* chip-select nodes, it has the below layout
* chip/memory-controller/dram-channel/dimm
* so we check if channel instance is passed
* and get the SMBIOS ID based on the channel
*/
if (data != NULL) {
int channum;
id_t dimmslot = -1;
channum = *(int *)data;
dimmslot = extdimmslot_to_dimmslot(mod, chip_smbid,
channum, SKIP_CS);
return (dimmslot);
}
dimmnum = nodeid;
return (dimmsmb[dimmnum].dimm_id);
}
return (-1);
}
int
chip_get_smbstruct(topo_mod_t *mod, const smbios_struct_t *sp)
{
smbios_processor_t p;
smbios_memdevice_t md;
smbios_processor_ext_t extp;
smbios_memarray_ext_t extma;
smbios_memdevice_ext_t extmd;
int ext_match = 0;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp == NULL)
return (-1);
switch (sp->smbstr_type) {
case SMB_TYPE_BASEBOARD:
bb_count++;
break;
case SMB_TYPE_MEMARRAY:
mctsmb[nmct_ids].mct_id = sp->smbstr_id;
nmct_ids++;
break;
case SUN_OEM_EXT_MEMARRAY:
if (shp != NULL) {
if (smbios_info_extmemarray(shp,
sp->smbstr_id, &extma) != 0) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"smbios_info_extmemarray()"
"failed\n");
return (-1);
}
} else
return (-1);
for (int i = 0; i < nmct_ids; i++) {
if (extma.smbmae_ma == mctsmb[i].mct_id) {
mctsmb[i].extmct_id = sp->smbstr_id;
mctsmb[i].p_id = extma.smbmae_comp;
ext_match = 1;
break;
}
}
if (!ext_match) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"EXT_MEMARRAY-MEMARRAY records are mismatched\n");
ext_match = 0;
return (-1);
}
break;
case SMB_TYPE_MEMDEVICE:
dimmsmb[ndimm_ids].dimm_id = sp->smbstr_id;
if (shp != NULL) {
if (smbios_info_memdevice(shp,
sp->smbstr_id, &md) != 0)
return (-1);
} else
return (-1);
dimmsmb[ndimm_ids].bankloc = md.smbmd_bloc;
ndimm_ids++;
break;
/*
* Every SMB_TYPE_MEMDEVICE SHOULD have a
* corresponding SUN_OEM_EXT_MEMDEVICE
*/
case SUN_OEM_EXT_MEMDEVICE:
if (smbios_info_extmemdevice(shp,
sp->smbstr_id, &extmd) != 0) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"smbios_info_extmemdevice()"
"failed\n");
return (-1);
}
for (int i = 0; i < ndimm_ids; i++) {
if (extmd.smbmdeve_md == dimmsmb[i].dimm_id) {
dimmsmb[i].extdimm_id = sp->smbstr_id;
ext_match = 1;
break;
}
}
if (!ext_match) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"EXT_MEMDEVICE-MEMDEVICE records are mismatched\n");
ext_match = 0;
return (-1);
}
break;
case SMB_TYPE_PROCESSOR:
cpusmb[ncpu_ids].cpu_id = sp->smbstr_id;
if (shp != NULL) {
if (smbios_info_processor(shp,
sp->smbstr_id, &p) != 0) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"smbios_info_processor()"
"failed\n");
return (-1);
}
}
cpusmb[ncpu_ids].status = p.smbp_status;
ncpu_ids++;
break;
/*
* Every SMB_TYPE_PROCESSOR SHOULD have a
* corresponding SUN_OEM_EXT_PROCESSOR
*/
case SUN_OEM_EXT_PROCESSOR:
if (smbios_info_extprocessor(shp,
sp->smbstr_id, &extp) != 0) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"smbios_info_extprocessor()"
"failed\n");
return (-1);
}
for (int i = 0; i < ncpu_ids; i++) {
if (extp.smbpe_processor == cpusmb[i].cpu_id) {
cpusmb[i].fru = extp.smbpe_fru;
ext_match = 1;
break;
}
}
if (!ext_match) {
topo_mod_dprintf(mod, "chip_get_smbstruct : "
"EXT_PROCESSOR-PROCESSOR records are mismatched\n");
ext_match = 0;
return (-1);
}
break;
}
return (0);
}
static int
chip_smbios_iterate(topo_mod_t *mod, smbios_rec_f *func_iter)
{
const smb_struct_t *sp;
smbios_struct_t s;
int i, rv = 0;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp == NULL)
return (rv);
sp = shp->sh_structs;
for (i = 0; i < shp->sh_nstructs; i++, sp++) {
if (sp->smbst_hdr->smbh_type != SMB_TYPE_INACTIVE &&
(rv = func_iter(mod, smb_export(sp, &s))) != 0)
break;
}
return (rv);
}
int
init_chip_smbios(topo_mod_t *mod)
{
if (!fill_chip_smbios) {
if (chip_smbios_iterate(mod, chip_get_smbstruct) == -1)
return (-1);
fill_chip_smbios = 1;
}
return (0);
}
int
chip_status_smbios_get(topo_mod_t *mod, id_t smb_id)
{
/*
* Type-4 Socket Status bit definitions per SMBIOS Version 2.6
*
* STATUS
* CPU Socket Populated
* CPU Socket Unpopulated
* Populated : Enabled
* Populated : Disabled by BIOS (Setup)
* Populated : Disabled by BIOS (Error)
* Populated : Idle
*/
uint8_t enabled = 0x01;
uint8_t populated = 0x40;
for (int i = 0; i < ncpu_ids; i++) {
if (smb_id == cpusmb[i].cpu_id) {
if (cpusmb[i].status == (enabled | populated))
return (1);
}
}
topo_mod_dprintf(mod, "chip_status_smbios_get() failed"
" considering that Type 4 ID : %d is disabled", smb_id);
return (0);
}
int
chip_fru_smbios_get(topo_mod_t *mod, id_t smb_id)
{
/*
* smbios_processor_ext_t->smbpe_fru : if set to 1
* processor is a FRU
*/
uint8_t fru = 1;
for (int i = 0; i < ncpu_ids; i++) {
if (smb_id == cpusmb[i].cpu_id) {
if (cpusmb[i].fru == fru)
return (1);
else
return (0);
}
}
topo_mod_dprintf(mod, "chip_fru_smbios_get() failed"
" considering that Type 4 ID : %d is not a FRU", smb_id);
return (0);
}
/*
* This could be defined as topo_mod_strlen()
*/
size_t
chip_strlen(const char *str)
{
int len = 0;
if (str != NULL)
len = strlen(str);
return (len);
}
/*
* We clean Serials, Revisions, Part No. strings, to
* avoid getting lost when fmd synthesizes these
* strings. :, =, /, ' ' characters are replaced
* with character '-' any non-printable characters
* as seen with !isprint() is also replaced with '-'
* Labels are checked only for non-printable characters.
*/
static const char *
chip_cleanup_smbios_str(topo_mod_t *mod, const char *begin, int str_type)
{
char buf[MAXNAMELEN];
const char *end, *cp;
char *pp;
char c;
int i;
end = begin + strlen(begin);
while (begin < end && isspace(*begin))
begin++;
while (begin < end && isspace(*(end - 1)))
end--;
if (begin >= end)
return (NULL);
cp = begin;
for (i = 0; i < MAXNAMELEN - 1; i++) {
if (cp >= end)
break;
c = *cp;
if (str_type == LABEL) {
if (!isprint(c))
buf[i] = '-';
else
buf[i] = c;
} else {
if (c == ':' || c == '=' || c == '/' ||
isspace(c) || !isprint(c))
buf[i] = '-';
else
buf[i] = c;
}
cp++;
}
buf[i] = 0;
pp = topo_mod_strdup(mod, buf);
if (str_type == LABEL)
topo_mod_strfree(mod, (char *)begin);
return (pp);
}
const char *
chip_label_smbios_get(topo_mod_t *mod, tnode_t *pnode, id_t smb_id,
char *ksmbios_label)
{
smbios_info_t c;
char *label = NULL;
char *buf = NULL;
const char *lsmbios_label = NULL;
int bufsz = 0;
char *delim = NULL, *blank = " ";
const char *dimm_bank = NULL;
const char *clean_label = NULL;
int err;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp != NULL) {
/*
* Get Parent FRU's label
*/
if (topo_prop_get_string(pnode, TOPO_PGROUP_PROTOCOL,
TOPO_PROP_LABEL, &label, &err) == -1)
topo_mod_dprintf(mod, "Failed to get"
" Label of Parent Node error : %d\n", err);
if (label != NULL)
label = (char *)chip_cleanup_smbios_str(mod,
label, LABEL);
/*
* On Intel the driver gets the label from ksmbios
* so we check if we already have it, if not we
* get it from libsmbios
*/
if (ksmbios_label == NULL && smb_id != -1) {
if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) {
for (int i = 0; i < ndimm_ids; i++) {
if (smb_id == dimmsmb[i].dimm_id) {
dimm_bank = dimmsmb[i].bankloc;
break;
}
}
if (dimm_bank != NULL) {
bufsz += chip_strlen(blank) +
chip_strlen(dimm_bank);
}
lsmbios_label = c.smbi_location;
}
} else
lsmbios_label = ksmbios_label;
if (label != NULL && lsmbios_label != NULL)
delim = "/";
bufsz += chip_strlen(label) + chip_strlen(delim) +
chip_strlen(lsmbios_label) + 1;
buf = topo_mod_alloc(mod, bufsz);
if (buf != NULL) {
if (label != NULL) {
(void) strlcpy(buf, label, bufsz);
if (lsmbios_label != NULL) {
(void) strlcat(buf, delim, bufsz);
/*
* If we are working on a DIMM
* and we are deriving from libsmbios
* smbi_location has the Device Locator.
* add the Device Locator
* add Bank Locator latter
*/
(void) strlcat(buf, lsmbios_label,
bufsz);
}
} else if (lsmbios_label != NULL)
(void) strlcpy(buf, lsmbios_label,
bufsz);
if (dimm_bank != NULL) {
(void) strlcat(buf, blank, bufsz);
(void) strlcat(buf, dimm_bank, bufsz);
}
}
clean_label = chip_cleanup_smbios_str(mod, buf, LABEL);
topo_mod_strfree(mod, label);
return (clean_label);
}
topo_mod_dprintf(mod, "Failed to get Label\n");
return (NULL);
}
const char *
chip_serial_smbios_get(topo_mod_t *mod, id_t smb_id)
{
smbios_info_t c;
const char *clean_serial = NULL;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp != NULL && smb_id != -1)
if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) {
clean_serial = chip_cleanup_smbios_str(mod,
c.smbi_serial, 0);
return (clean_serial);
}
topo_mod_dprintf(mod, "Failed to get Serial \n");
return (NULL);
}
const char *
chip_part_smbios_get(topo_mod_t *mod, id_t smb_id)
{
smbios_info_t c;
const char *clean_part = NULL;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp != NULL && smb_id != -1)
if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) {
clean_part = chip_cleanup_smbios_str(mod,
c.smbi_part, 0);
return (clean_part);
}
topo_mod_dprintf(mod, "Failed to get Part\n");
return (NULL);
}
const char *
chip_rev_smbios_get(topo_mod_t *mod, id_t smb_id)
{
smbios_info_t c;
const char *clean_rev = NULL;
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp != NULL && smb_id != -1)
if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) {
clean_rev = chip_cleanup_smbios_str(mod,
c.smbi_version, 0);
return (clean_rev);
}
topo_mod_dprintf(mod, "Failed to get Revision\n");
return (NULL);
}