chip_subr.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"
/*
* Support function for the i86pc chip enumerator
*/
#include <sys/types.h>
#include <stdarg.h>
#include <strings.h>
#include <sys/fm/protocol.h>
#include "chip.h"
/*
* Whinge a debug message via topo_mod_dprintf and increment the
* given error counter.
*/
void
whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
{
va_list ap;
char buf[160];
if (nerr != NULL)
++*nerr;
va_start(ap, fmt);
(void) vsnprintf(buf, sizeof (buf), fmt, ap);
va_end(ap);
topo_mod_dprintf(mod, "%s", buf);
}
/*
* Given an nvpair of a limited number of data types, extract the property
* name and value and add that combination to the given node in the
* specified property group using the corresponding topo_prop_set_* function
* for the data type. Return 1 on success, otherwise 0.
*/
int
nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
{
int success = 0;
int err;
char *pname = nvpair_name(nvp);
switch (nvpair_type(nvp)) {
case DATA_TYPE_BOOLEAN_VALUE: {
boolean_t val;
if (nvpair_value_boolean_value(nvp, &val) == 0 &&
topo_prop_set_string(node, pgname, pname,
TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0)
success = 1;
break;
}
case DATA_TYPE_UINT32: {
uint32_t val;
if (nvpair_value_uint32(nvp, &val) == 0 &&
topo_prop_set_uint32(node, pgname, pname,
TOPO_PROP_IMMUTABLE, val, &err) == 0)
success = 1;
break;
}
case DATA_TYPE_UINT64: {
uint64_t val;
if (nvpair_value_uint64(nvp, &val) == 0 &&
topo_prop_set_uint64(node, pgname, pname,
TOPO_PROP_IMMUTABLE, val, &err) == 0)
success = 1;
break;
}
case DATA_TYPE_UINT32_ARRAY: {
uint32_t *arrp;
uint_t nelem;
if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 &&
nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname,
TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0)
success = 1;
break;
}
case DATA_TYPE_STRING: {
char *str;
if (nvpair_value_string(nvp, &str) == 0 &&
topo_prop_set_string(node, pgname, pname,
TOPO_PROP_IMMUTABLE, str, &err) == 0)
success = 1;
break;
}
default:
whinge(mod, &err, "nvprop_add: Can't handle type %d for "
"'%s' in property group %s of %s node\n",
nvpair_type(nvp), pname, pgname, topo_node_name(node));
break;
}
return (success ? 0 : 1);
}
/*
* Lookup string data named pname in the given kstat_t and add that
* as property named pname in the given property group pgname on the indicated
* topo node. Fill pvalp with a pointer to the string value, valid until
* kstat_close is called (or the given kstat_t is otherwise invalidated).
*/
int
add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
const char *pgname, const char *pname, const char **pvalp)
{
const char *pval;
kstat_named_t *k;
int err = 0;
if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
return (-1);
pval = k->value.str.addr.ptr;
if (topo_prop_set_string(node, pgname, pname,
TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
if (pvalp)
*pvalp = pval;
return (0);
} else {
whinge(mod, &err, "chip_strprop: failed to add '%s'\n",
pname);
return (-1);
}
}
/*
* Lookup an int32 item named pname in the given kstat_t and add that
* as property named pname in the given property group pgname on the indicated
* topo node. Fill pvalp with the property value.
*/
int
add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
const char *pgname, const char *pname, int32_t *pvalp)
{
kstat_named_t *k;
int32_t pval;
int err;
if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
return (-1);
pval = k->value.l;
if (topo_prop_set_int32(node, pgname, pname,
TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
if (pvalp)
*pvalp = pval;
return (0);
} else {
whinge(mod, &err, "chip_longprop: failed to add '%s'\n",
pname);
return (-1);
}
}
/*
* In a given kstat_t lookup a variable number of int32 properties named in
* const char * varargs and each each in the given property group on the
* node. Fill an array of the retrieved values.
*/
int
add_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
const char *pgname, int32_t *pvalap, ...)
{
const char *pname;
va_list ap;
int nerr = 0;
va_start(ap, pvalap);
while ((pname = va_arg(ap, const char *)) != NULL) {
if (add_kstat_longprop(mod, node, ksp, pgname, pname,
pvalap) != 0)
nerr++; /* have whinged elsewhere */
if (pvalap != NULL)
++pvalap;
}
va_end(ap);
return (nerr == 0 ? 0 : -1);
}
/*
* Construct an hc scheme resource FMRI for a node named name with
* instance number inst, parented by the given parent node pnode.
*/
int
mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst,
nvlist_t *auth, nvlist_t **nvl)
{
*nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name,
inst, NULL, auth, NULL, NULL, NULL);
return (nvl != NULL ? 0 : -1); /* caller must free nvlist */
}
/*
* Construct a cpu scheme FMRI with the given data; the caller must free
* the allocated nvlist with nvlist_free().
*/
nvlist_t *
cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
{
int err;
nvlist_t *asru;
if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
return (NULL);
err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
if (s != NULL)
err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
if (err != 0) {
nvlist_free(asru);
(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
return (NULL);
}
return (asru);
}
/*
* Construct a mem scheme FMRI for the given unum string; the caller must
* free the allocated nvlist with nvlist_free().
*/
nvlist_t *
mem_fmri_create(topo_mod_t *mod, const char *unum)
{
nvlist_t *asru;
if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
return (NULL);
if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0 ||
nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum) != 0) {
nvlist_free(asru);
return (NULL);
}
return (asru);
}
/*
* Registered method for asru computation for rank nodes. The 'node'
* argument identifies the node for which we seek an asru. The 'in'
* argument is used to select which asru we will return, as follows:
*
* - the node name must be "dimm" or "rank"
* - if 'in' is NULL then return any statically defined asru for this node
* - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru
* with unum being the hc path to the dimm or rank (this method is called
* as part of dynamic asru computation for rank nodes only, but
* it is also called directly to construct a "mem" scheme asru for a dimm
* node)
* - if 'in' in addition includes an hc-specific member which specifies
* asru-physaddr or asru-offset then these are includes in the "mem" scheme
* asru as additional members physaddr and offset
*/
int
mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru)
{
int incl_pa = 0, incl_offset = 0;
nvlist_t *hcsp, *ap;
char *unum, *scheme;
uint64_t pa, offset;
int err = 0;
if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 ||
strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR,
&pa) == 0)
incl_pa = 1;
if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET,
&offset) == 0)
incl_offset = 1;
}
/* use 'fmri' to obtain resource path; could use node resource */
if (topo_mod_nvl2str(mod, fmri, &unum) < 0)
return (-1); /* mod errno set */
ap = mem_fmri_create(mod, unum);
topo_mod_strfree(mod, unum);
if (ap == NULL)
return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
if (incl_pa)
err += nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa) != 0;
if (incl_offset)
err += nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset) != 0;
if (err != 0) {
nvlist_free(ap);
return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
}
*asru = ap;
return (0);
}
/*ARGSUSED*/
int
mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
nvlist_t *in, nvlist_t **out)
{
nvlist_t *asru;
nvlist_t *args, *pargs;
int err;
if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 &&
strcmp(topo_node_name(node), CS_NODE_NAME) != 0)
return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0)
return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) {
if (err == ENOENT) {
if (topo_mod_nvdup(mod, args, &asru) < 0)
return (topo_mod_seterrno(mod, EMOD_NOMEM));
} else {
return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
}
} else if (mem_asru_create(mod, pargs, &asru) != 0) {
return (-1); /* mod errno already set */
}
if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
nvlist_free(asru);
return (topo_mod_seterrno(mod, EMOD_NOMEM));
}
err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU);
err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI);
err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru);
if (err != 0) {
nvlist_free(asru);
nvlist_free(*out);
return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
}
nvlist_free(asru);
return (0);
}