/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2019, Joyent, Inc. * Copyright 2020 Oxide Computer Company */ /* * Support function for the i86pc chip enumerator */ #include #include #include #include #include #include #include #include #include "chip.h" static void fmri_dprint(topo_mod_t *, const char *, uint32_t, nvlist_t *); static boolean_t is_page_fmri(nvlist_t *); /* * 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 nvlist 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 * nvlist_free is called. */ int add_nvlist_strprop(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl, const char *pgname, const char *pname, const char **pvalp) { char *pval; int err = 0; if (nvlist_lookup_string(nvl, pname, &pval) != 0) return (-1); if (topo_prop_set_string(node, pgname, pname, TOPO_PROP_IMMUTABLE, pval, &err) == 0) { if (pvalp) *pvalp = pval; return (0); } else { whinge(mod, &err, "add_nvlist_strprop: failed to add '%s'\n", pname); return (-1); } } /* * Lookup an int32 item named pname in the given nvlist 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_nvlist_longprop(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl, const char *pgname, const char *pname, int32_t *pvalp) { int32_t pval; int err; if ((nvlist_lookup_int32(nvl, pname, &pval)) != 0) return (-1); if (topo_prop_set_int32(node, pgname, pname, TOPO_PROP_IMMUTABLE, pval, &err) == 0) { if (pvalp) *pvalp = pval; return (0); } else { whinge(mod, &err, "add_nvlist_longprop: failed to add '%s'\n", pname); return (-1); } } /* * In a given nvlist 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_nvlist_longprops(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl, 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_nvlist_longprop(mod, node, nvl, 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); } /*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, *args, *pargs, *hcsp; int err; uint64_t pa, offset; 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) { pargs = args; } else { return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); } } if (topo_mod_nvdup(mod, pargs, &asru) != 0) return (topo_mod_seterrno(mod, EMOD_NOMEM)); err = 0; /* * if 'in' includes an hc-specific member which specifies asru-physaddr * or asru-offset then rename them to asru and physaddr respectively. */ if (nvlist_lookup_nvlist(asru, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0) { err += nvlist_remove(hcsp, "asru-"FM_FMRI_HC_SPECIFIC_PHYSADDR, DATA_TYPE_UINT64); err += nvlist_add_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR, pa); } if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_HC_SPECIFIC_OFFSET, &offset) == 0) { err += nvlist_remove(hcsp, "asru-"FM_FMRI_HC_SPECIFIC_OFFSET, DATA_TYPE_UINT64); err += nvlist_add_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET, offset); } } if (err != 0 || 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); } static int set_retnvl(topo_mod_t *mod, nvlist_t **out, const char *retname, uint32_t ret) { nvlist_t *nvl; if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) < 0) return (topo_mod_seterrno(mod, EMOD_NOMEM)); if (nvlist_add_uint32(nvl, retname, ret) != 0) { nvlist_free(nvl); return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } *out = nvl; return (0); } /* * If we're getting called then the question of whether this dimm is plugged * in has already been answered. What we don't know for sure is whether it's * the same dimm or a different one plugged in the same slot. To check, we * try and compare the serial numbers on the dimm in the current topology with * the serial num from the unum fmri that got passed into this function as the * argument. * */ static int fmri_replaced(topo_mod_t *mod, tnode_t *node, nvlist_t *unum, int *errp) { tnode_t *dimmnode; nvlist_t *resource; int rc, err; char *old_serial, *curr_serial; fmd_agent_hdl_t *hdl; /* * If input is a page, return "replaced" if the offset is invalid. */ if (is_page_fmri(unum) && (hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { rc = fmd_agent_page_isretired(hdl, unum); err = fmd_agent_errno(hdl); fmd_agent_close(hdl); if (rc == FMD_AGENT_RETIRE_DONE && err == EINVAL) return (FMD_OBJ_STATE_NOT_PRESENT); } /* * If a serial number for the dimm was available at the time of the * fault, it will have been added as a string to the unum nvlist */ if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial)) return (FMD_OBJ_STATE_UNKNOWN); /* * If the current serial number is available for the DIMM that this rank * belongs to, it will be accessible as a property on the parent (dimm) * node. If there is a serial id in the resource fmri, then use that. * Otherwise fall back to looking for a serial id property in the * protocol group. */ dimmnode = topo_node_parent(node); if (topo_node_resource(dimmnode, &resource, &err) != -1) { if (nvlist_lookup_string(resource, FM_FMRI_HC_SERIAL_ID, &curr_serial) == 0) { if (strcmp(old_serial, curr_serial) != 0) { nvlist_free(resource); return (FMD_OBJ_STATE_REPLACED); } else { nvlist_free(resource); return (FMD_OBJ_STATE_STILL_PRESENT); } } nvlist_free(resource); } if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) { if (err == ETOPO_PROP_NOENT) { return (FMD_OBJ_STATE_UNKNOWN); } else { *errp = EMOD_NVL_INVAL; whinge(mod, NULL, "rank_fmri_present: Unexpected " "error retrieving serial from node"); return (-1); } } if (strcmp(old_serial, curr_serial) != 0) { topo_mod_strfree(mod, curr_serial); return (FMD_OBJ_STATE_REPLACED); } topo_mod_strfree(mod, curr_serial); return (FMD_OBJ_STATE_STILL_PRESENT); } /* * In the event we encounter problems comparing serials or if a comparison isn't * possible, we err on the side of caution and set is_present to TRUE. */ int rank_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { int is_present, err; if (version > TOPO_METH_PRESENT_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); switch (fmri_replaced(mod, node, in, &err)) { case FMD_OBJ_STATE_REPLACED: case FMD_OBJ_STATE_NOT_PRESENT: is_present = 0; break; case FMD_OBJ_STATE_UNKNOWN: case FMD_OBJ_STATE_STILL_PRESENT: is_present = 1; break; default: return (topo_mod_seterrno(mod, err)); } fmri_dprint(mod, "rank_fmri_present", is_present, in); return (set_retnvl(mod, out, TOPO_METH_PRESENT_RET, is_present)); } int rank_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { int is_replaced, err; if (version > TOPO_METH_REPLACED_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); is_replaced = fmri_replaced(mod, node, in, &err); if (is_replaced == -1) return (topo_mod_seterrno(mod, err)); fmri_dprint(mod, "rank_fmri_replaced", is_replaced, in); return (set_retnvl(mod, out, TOPO_METH_REPLACED_RET, is_replaced)); } static void fmri_dprint(topo_mod_t *mod, const char *op, uint32_t rc, nvlist_t *fmri) { char *fmristr; const char *status; if (getenv("TOPOCHIPDBG") == NULL) return; switch (rc) { case FMD_AGENT_RETIRE_DONE: status = "sync success"; break; case FMD_AGENT_RETIRE_ASYNC: status = "async retiring"; break; case FMD_AGENT_RETIRE_FAIL: status = "not retired"; break; default: status = "unknown status"; } if (fmri != NULL && topo_mod_nvl2str(mod, fmri, &fmristr) == 0) { topo_mod_dprintf(mod, "[%s]: %s => %d (\"%s\")\n", fmristr, op, rc, status); topo_mod_strfree(mod, fmristr); } } struct strand_walk_data { tnode_t *parent; fmd_agent_hdl_t *hdl; int (*func)(fmd_agent_hdl_t *, int, int, int); int err; int done; int fail; int async; }; static int strand_walker(topo_mod_t *mod, tnode_t *node, void *pdata) { struct strand_walk_data *swdp = pdata; int32_t chipid, coreid, strandid; int err, rc; /* * Terminate the walk if we reach start-node's sibling */ if (node != swdp->parent && topo_node_parent(node) == topo_node_parent(swdp->parent)) return (TOPO_WALK_TERMINATE); if (strcmp(topo_node_name(node), STRAND) != 0) return (TOPO_WALK_NEXT); if (topo_prop_get_int32(node, PGNAME(STRAND), STRAND_CHIP_ID, &chipid, &err) < 0 || topo_prop_get_int32(node, PGNAME(STRAND), STRAND_CORE_ID, &coreid, &err) < 0) { swdp->err++; return (TOPO_WALK_NEXT); } strandid = topo_node_instance(node); rc = swdp->func(swdp->hdl, chipid, coreid, strandid); if (rc == FMD_AGENT_RETIRE_DONE) swdp->done++; else if (rc == FMD_AGENT_RETIRE_FAIL) swdp->fail++; else if (rc == FMD_AGENT_RETIRE_ASYNC) swdp->async++; else swdp->err++; if (getenv("TOPOCHIPDBG") != NULL) { const char *op; if (swdp->func == fmd_agent_cpu_retire) op = "retire"; else if (swdp->func == fmd_agent_cpu_unretire) op = "unretire"; else if (swdp->func == fmd_agent_cpu_isretired) op = "check status"; else op = "unknown op"; topo_mod_dprintf(mod, "%s cpu (%d:%d:%d): rc = %d, err = %s\n", op, (int)chipid, (int)coreid, (int)strandid, rc, fmd_agent_errmsg(swdp->hdl)); } return (TOPO_WALK_NEXT); } static int walk_strands(topo_mod_t *mod, struct strand_walk_data *swdp, tnode_t *parent, int (*func)(fmd_agent_hdl_t *, int, int, int)) { topo_walk_t *twp; int err; swdp->parent = parent; swdp->func = func; swdp->err = swdp->done = swdp->fail = swdp->async = 0; if ((swdp->hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) { swdp->fail++; return (0); } twp = topo_mod_walk_init(mod, parent, strand_walker, swdp, &err); if (twp == NULL) { fmd_agent_close(swdp->hdl); return (-1); } err = topo_walk_step(twp, TOPO_WALK_CHILD); topo_walk_fini(twp); fmd_agent_close(swdp->hdl); if (err == TOPO_WALK_ERR || swdp->err > 0) return (-1); return (0); } /* ARGSUSED */ int retire_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { struct strand_walk_data swd; uint32_t rc; if (version > TOPO_METH_RETIRE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (walk_strands(mod, &swd, node, fmd_agent_cpu_retire) == -1) return (-1); if (swd.fail > 0) rc = FMD_AGENT_RETIRE_FAIL; else if (swd.async > 0) rc = FMD_AGENT_RETIRE_ASYNC; else rc = FMD_AGENT_RETIRE_DONE; return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc)); } /* ARGSUSED */ int unretire_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { struct strand_walk_data swd; uint32_t rc; if (version > TOPO_METH_UNRETIRE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (walk_strands(mod, &swd, node, fmd_agent_cpu_unretire) == -1) return (-1); if (swd.fail > 0) rc = FMD_AGENT_RETIRE_FAIL; else if (swd.async > 0) rc = FMD_AGENT_RETIRE_ASYNC; else rc = FMD_AGENT_RETIRE_DONE; return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc)); } /* ARGSUSED */ int service_state_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { struct strand_walk_data swd; uint32_t rc; if (version > TOPO_METH_SERVICE_STATE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (walk_strands(mod, &swd, node, fmd_agent_cpu_isretired) == -1) return (-1); if (swd.done > 0) rc = (swd.fail + swd.async > 0) ? FMD_SERVICE_STATE_DEGRADED : FMD_SERVICE_STATE_UNUSABLE; else if (swd.async > 0) rc = FMD_SERVICE_STATE_ISOLATE_PENDING; else if (swd.fail > 0) rc = FMD_SERVICE_STATE_OK; else rc = FMD_SERVICE_STATE_UNKNOWN; return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc)); } /* ARGSUSED */ int unusable_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { struct strand_walk_data swd; uint32_t rc; if (version > TOPO_METH_UNUSABLE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (walk_strands(mod, &swd, node, fmd_agent_cpu_isretired) == -1) return (-1); rc = (swd.fail + swd.async > 0 || swd.done == 0) ? 0 : 1; return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, rc)); } static boolean_t is_page_fmri(nvlist_t *nvl) { nvlist_t *hcsp; uint64_t val; if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) == 0 && (nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET, &val) == 0 || nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET, &val) == 0 || nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR, &val) == 0 || nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, &val) == 0)) return (B_TRUE); return (B_FALSE); } /* ARGSUSED */ int ntv_page_retire(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { fmd_agent_hdl_t *hdl; uint32_t rc = FMD_AGENT_RETIRE_FAIL; if (version > TOPO_METH_RETIRE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (is_page_fmri(in)) { if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { rc = fmd_agent_page_retire(hdl, in); fmd_agent_close(hdl); } } fmri_dprint(mod, "ntv_page_retire", rc, in); return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc)); } /* ARGSUSED */ int ntv_page_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { fmd_agent_hdl_t *hdl; uint32_t rc = FMD_AGENT_RETIRE_FAIL; if (version > TOPO_METH_UNRETIRE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (is_page_fmri(in)) { if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { rc = fmd_agent_page_unretire(hdl, in); fmd_agent_close(hdl); } } fmri_dprint(mod, "ntv_page_unretire", rc, in); return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc)); } /* ARGSUSED */ int ntv_page_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { fmd_agent_hdl_t *hdl; uint32_t rc = FMD_SERVICE_STATE_UNKNOWN; if (version > TOPO_METH_SERVICE_STATE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (is_page_fmri(in)) { if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { rc = fmd_agent_page_isretired(hdl, in); fmd_agent_close(hdl); if (rc == FMD_AGENT_RETIRE_DONE) rc = FMD_SERVICE_STATE_UNUSABLE; else if (rc == FMD_AGENT_RETIRE_FAIL) rc = FMD_SERVICE_STATE_OK; else if (rc == FMD_AGENT_RETIRE_ASYNC) rc = FMD_SERVICE_STATE_ISOLATE_PENDING; } } topo_mod_dprintf(mod, "ntv_page_service_state: rc = %u\n", rc); return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc)); } /* ARGSUSED */ int ntv_page_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { fmd_agent_hdl_t *hdl; uint32_t rc = FMD_AGENT_RETIRE_FAIL; if (version > TOPO_METH_UNUSABLE_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (is_page_fmri(in)) { if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { rc = fmd_agent_page_isretired(hdl, in); fmd_agent_close(hdl); } } topo_mod_dprintf(mod, "ntv_page_unusable: rc = %u\n", rc); return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, rc == FMD_AGENT_RETIRE_DONE ? 1 : 0)); } /* * Determine whether or not we believe a chip has been replaced. While it's * tempting to just do a straight up comparison of the FMRI and its serial * number, things are not that straightforward. * * The presence of a serial number on the CPU is not always guaranteed. It is * possible that systems firmware can hide the information required to generate * a synthesized serial number or that it is strictly not present. As such, we * will only declare something replaced when both the old and current resource * have a serial number present. If it is missing for whatever reason, then we * cannot assume anything about a replacement having occurred. * * This logic applies regardless of whether or not we have an FM-aware SMBIOS. */ int chip_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { nvlist_t *rsrc = NULL; int err, ret; char *old_serial, *new_serial; if (version > TOPO_METH_REPLACED_VERSION) return (topo_mod_seterrno(mod, EMOD_VER_NEW)); if (topo_node_resource(node, &rsrc, &err) == -1) { return (topo_mod_seterrno(mod, err)); } if (nvlist_lookup_string(rsrc, FM_FMRI_HC_SERIAL_ID, &new_serial) != 0) { ret = FMD_OBJ_STATE_UNKNOWN; goto out; } if (nvlist_lookup_string(in, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0) { ret = FMD_OBJ_STATE_UNKNOWN; goto out; } if (strcmp(old_serial, new_serial) == 0) { ret = FMD_OBJ_STATE_STILL_PRESENT; } else { ret = FMD_OBJ_STATE_REPLACED; } out: nvlist_free(rsrc); return (set_retnvl(mod, out, TOPO_METH_REPLACED_RET, ret)); } void get_chip_kstat_strs(topo_mod_t *mod, kstat_ctl_t *kc, int32_t chipid, char **brandp, char **sktp) { kstat_t *ksp; kstat_named_t *ks; uint_t i; for (i = 0, ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next, i++) { if (strcmp(ksp->ks_module, "cpu_info") != 0) continue; if (kstat_read(kc, ksp, NULL) == -1) { topo_mod_dprintf(mod, "failed to read stat cpu_info:%u", i); continue; } if ((ks = kstat_data_lookup(ksp, "chip_id")) == NULL || chipid != ks->value.i32) { continue; } if ((ks = kstat_data_lookup(ksp, "brand")) != NULL) { *brandp = topo_mod_strdup(mod, ks->value.str.addr.ptr); } if ((ks = kstat_data_lookup(ksp, "socket_type")) != NULL) { if (strcmp(ks->value.str.addr.ptr, "Unknown") != 0) { *sktp = topo_mod_strdup(mod, ks->value.str.addr.ptr); } } return; } }