xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/cpu.c (revision a62774df)
17aec1d6eScindi /*
27aec1d6eScindi  * CDDL HEADER START
37aec1d6eScindi  *
47aec1d6eScindi  * The contents of this file are subject to the terms of the
580ab886dSwesolows  * Common Development and Distribution License (the "License").
680ab886dSwesolows  * You may not use this file except in compliance with the License.
77aec1d6eScindi  *
87aec1d6eScindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97aec1d6eScindi  * or http://www.opensolaris.org/os/licensing.
107aec1d6eScindi  * See the License for the specific language governing permissions
117aec1d6eScindi  * and limitations under the License.
127aec1d6eScindi  *
137aec1d6eScindi  * When distributing Covered Code, include this CDDL HEADER in each
147aec1d6eScindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157aec1d6eScindi  * If applicable, add the following below this CDDL HEADER, with the
167aec1d6eScindi  * fields enclosed by brackets "[]" replaced with your own identifying
177aec1d6eScindi  * information: Portions Copyright [yyyy] [name of copyright owner]
187aec1d6eScindi  *
197aec1d6eScindi  * CDDL HEADER END
207aec1d6eScindi  */
2180ab886dSwesolows 
227aec1d6eScindi /*
23*a62774dfSSinanallur Balasubramanian  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247aec1d6eScindi  * Use is subject to license terms.
257aec1d6eScindi  */
267aec1d6eScindi 
27*a62774dfSSinanallur Balasubramanian 
287aec1d6eScindi #include <errno.h>
297aec1d6eScindi #include <limits.h>
307aec1d6eScindi #include <strings.h>
317aec1d6eScindi #include <unistd.h>
3214ea4bb7Ssd #include <topo_error.h>
337aec1d6eScindi #include <fm/topo_mod.h>
347aec1d6eScindi #include <sys/fm/protocol.h>
357aec1d6eScindi 
360eb822a1Scindi #include <topo_method.h>
370eb822a1Scindi #include <cpu.h>
387aec1d6eScindi 
3914ea4bb7Ssd /*
4014ea4bb7Ssd  * platform specific cpu module
4114ea4bb7Ssd  */
4214ea4bb7Ssd #define	PLATFORM_CPU_VERSION	CPU_VERSION
4314ea4bb7Ssd #define	PLATFORM_CPU_NAME	"platform-cpu"
4414ea4bb7Ssd 
457aec1d6eScindi static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
460eb822a1Scindi     topo_instance_t, void *, void *);
477aec1d6eScindi static void cpu_release(topo_mod_t *, tnode_t *);
487aec1d6eScindi static int cpu_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
497aec1d6eScindi     nvlist_t **);
507aec1d6eScindi static int cpu_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
517aec1d6eScindi     nvlist_t **);
527aec1d6eScindi static int cpu_fmri_asru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
537aec1d6eScindi     nvlist_t **);
54d5ea2cedSScott Davenport static int cpu_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
55d5ea2cedSScott Davenport     nvlist_t *, nvlist_t **);
567aec1d6eScindi static nvlist_t *fmri_create(topo_mod_t *, uint32_t, uint8_t, char *);
577aec1d6eScindi 
587aec1d6eScindi static const topo_method_t cpu_methods[] = {
597aec1d6eScindi 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
607aec1d6eScindi 	    TOPO_STABILITY_INTERNAL, cpu_nvl2str },
617aec1d6eScindi 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
627aec1d6eScindi 	    TOPO_STABILITY_INTERNAL, cpu_str2nvl },
637aec1d6eScindi 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
647aec1d6eScindi 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
657aec1d6eScindi 	    cpu_fmri_asru },
660eb822a1Scindi 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
67d5ea2cedSScott Davenport 	    TOPO_STABILITY_INTERNAL, cpu_fmri_create_meth },
687aec1d6eScindi 	{ NULL }
697aec1d6eScindi };
707aec1d6eScindi 
710eb822a1Scindi static const topo_modops_t cpu_ops =
720eb822a1Scindi 	{ cpu_enum, cpu_release };
730eb822a1Scindi 
747aec1d6eScindi static const topo_modinfo_t cpu_info =
750eb822a1Scindi 	{ "cpu", FM_FMRI_SCHEME_CPU, CPU_VERSION, &cpu_ops };
767aec1d6eScindi 
770eb822a1Scindi int
cpu_init(topo_mod_t * mod,topo_version_t version)780eb822a1Scindi cpu_init(topo_mod_t *mod, topo_version_t version)
797aec1d6eScindi {
807aec1d6eScindi 	cpu_node_t *cpuip;
817aec1d6eScindi 
820eb822a1Scindi 	if (getenv("TOPOCPUDEBUG"))
830eb822a1Scindi 		topo_mod_setdebug(mod);
847aec1d6eScindi 	topo_mod_dprintf(mod, "initializing cpu builtin\n");
857aec1d6eScindi 
860eb822a1Scindi 	if (version != CPU_VERSION)
870eb822a1Scindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
880eb822a1Scindi 
897aec1d6eScindi 	if ((cpuip = topo_mod_zalloc(mod, sizeof (cpu_node_t))) == NULL)
900eb822a1Scindi 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
917aec1d6eScindi 
927aec1d6eScindi 	if ((cpuip->cn_kc = kstat_open()) == NULL) {
937aec1d6eScindi 		topo_mod_dprintf(mod, "kstat_open failed: %s\n",
947aec1d6eScindi 		    strerror(errno));
957aec1d6eScindi 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
960eb822a1Scindi 		return (-1);
977aec1d6eScindi 	}
987aec1d6eScindi 
997aec1d6eScindi 	cpuip->cn_ncpustats = sysconf(_SC_CPUID_MAX);
1007aec1d6eScindi 	if ((cpuip->cn_cpustats = topo_mod_zalloc(mod, (
1017aec1d6eScindi 	    cpuip->cn_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
1027aec1d6eScindi 		(void) kstat_close(cpuip->cn_kc);
1037aec1d6eScindi 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
1040eb822a1Scindi 		return (-1);
1057aec1d6eScindi 	}
1067aec1d6eScindi 
1070eb822a1Scindi 	if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) {
1087aec1d6eScindi 		topo_mod_dprintf(mod, "failed to register cpu_info: "
1097aec1d6eScindi 		    "%s\n", topo_mod_errmsg(mod));
1107aec1d6eScindi 		topo_mod_free(mod, cpuip->cn_cpustats,
1117aec1d6eScindi 		    (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
1127aec1d6eScindi 		(void) kstat_close(cpuip->cn_kc);
1137aec1d6eScindi 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
1140eb822a1Scindi 		return (-1);
1157aec1d6eScindi 	}
1160eb822a1Scindi 
1170eb822a1Scindi 	topo_mod_setspecific(mod, (void *)cpuip);
1180eb822a1Scindi 
1190eb822a1Scindi 	return (0);
1207aec1d6eScindi }
1217aec1d6eScindi 
1227aec1d6eScindi void
cpu_fini(topo_mod_t * mod)1237aec1d6eScindi cpu_fini(topo_mod_t *mod)
1247aec1d6eScindi {
1257aec1d6eScindi 	cpu_node_t *cpuip;
1267aec1d6eScindi 
1270eb822a1Scindi 	cpuip = topo_mod_getspecific(mod);
1287aec1d6eScindi 
1297aec1d6eScindi 	if (cpuip->cn_cpustats != NULL)
1307aec1d6eScindi 		topo_mod_free(mod, cpuip->cn_cpustats,
1317aec1d6eScindi 		    (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
1327aec1d6eScindi 
1337aec1d6eScindi 	(void) kstat_close(cpuip->cn_kc);
1347aec1d6eScindi 	topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
1357aec1d6eScindi 
1367aec1d6eScindi 	topo_mod_unregister(mod);
1377aec1d6eScindi }
1387aec1d6eScindi 
1397aec1d6eScindi static int
cpu_kstat_init(cpu_node_t * cpuip,int i)1407aec1d6eScindi cpu_kstat_init(cpu_node_t *cpuip, int i)
1417aec1d6eScindi {
1427aec1d6eScindi 	kstat_t *ksp;
1437aec1d6eScindi 
1447aec1d6eScindi 	if (cpuip->cn_cpustats[i] == NULL) {
1457aec1d6eScindi 		if ((ksp = kstat_lookup(cpuip->cn_kc, "cpu_info", i, NULL)) ==
1467aec1d6eScindi 		    NULL || kstat_read(cpuip->cn_kc, ksp, NULL) < 0)
1477aec1d6eScindi 			return (-1);
1487aec1d6eScindi 
1497aec1d6eScindi 		cpuip->cn_cpustats[i] = ksp;
1507aec1d6eScindi 	} else {
1517aec1d6eScindi 		ksp = cpuip->cn_cpustats[i];
1527aec1d6eScindi 	}
1537aec1d6eScindi 
1547aec1d6eScindi 	return (ksp->ks_instance);
1557aec1d6eScindi }
1567aec1d6eScindi 
1577aec1d6eScindi /*ARGSUSED*/
1587aec1d6eScindi static int
cpu_create(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,cpu_node_t * cpuip)1597aec1d6eScindi cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name,
1607aec1d6eScindi     topo_instance_t min, topo_instance_t max, cpu_node_t *cpuip)
1617aec1d6eScindi {
1627aec1d6eScindi 	int i;
1637aec1d6eScindi 	processorid_t cpu_id;
1647aec1d6eScindi 	char *s, sbuf[21];
1657aec1d6eScindi 	kstat_named_t *ks;
1667aec1d6eScindi 	nvlist_t *fmri;
1677aec1d6eScindi 
1687aec1d6eScindi 	for (i = 0; i <= cpuip->cn_ncpustats; i++) {
1697aec1d6eScindi 
1707aec1d6eScindi 		if ((cpu_id = cpu_kstat_init(cpuip, i)) < 0)
1717aec1d6eScindi 			continue;
1727aec1d6eScindi 
1737aec1d6eScindi 		if ((ks = kstat_data_lookup(cpuip->cn_cpustats[i],
1747aec1d6eScindi 		    "device_ID")) != NULL) {
1757aec1d6eScindi 			(void) snprintf(sbuf, 21, "%llX", ks->value.ui64);
1767aec1d6eScindi 			s = sbuf;
1777aec1d6eScindi 		} else {
1787aec1d6eScindi 			s = NULL;
1797aec1d6eScindi 		}
1807aec1d6eScindi 
1817aec1d6eScindi 		if ((fmri = fmri_create(mod, cpu_id, 0, s)) == NULL)
1827aec1d6eScindi 			continue;
1830eb822a1Scindi 		(void) topo_node_bind(mod, rnode, name, cpu_id, fmri);
1847aec1d6eScindi 		nvlist_free(fmri);
1857aec1d6eScindi 	}
1867aec1d6eScindi 
1877aec1d6eScindi 	return (0);
1887aec1d6eScindi }
1897aec1d6eScindi 
1907aec1d6eScindi 
1917aec1d6eScindi /*ARGSUSED*/
1927aec1d6eScindi static int
cpu_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused2)1937aec1d6eScindi cpu_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1940eb822a1Scindi     topo_instance_t min, topo_instance_t max, void *arg, void *notused2)
1957aec1d6eScindi {
19614ea4bb7Ssd 	topo_mod_t *nmp;
1977aec1d6eScindi 	cpu_node_t *cpuip = (cpu_node_t *)arg;
1987aec1d6eScindi 
19914ea4bb7Ssd 	if ((nmp = topo_mod_load(mod, PLATFORM_CPU_NAME,
2007bebe46cSjc 	    PLATFORM_CPU_VERSION)) == NULL) {
20114ea4bb7Ssd 		if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) {
20214ea4bb7Ssd 			/*
20314ea4bb7Ssd 			 * There is no platform specific cpu module, so use
20414ea4bb7Ssd 			 * the default enumeration with kstats of this builtin
20514ea4bb7Ssd 			 * cpu module.
20614ea4bb7Ssd 			 */
20714ea4bb7Ssd 			if (topo_node_range_create(mod, pnode, name, 0,
2087bebe46cSjc 			    cpuip->cn_ncpustats + 1) < 0) {
20914ea4bb7Ssd 				topo_mod_dprintf(mod,
2107bebe46cSjc 				    "cpu enumeration failed to create "
2117bebe46cSjc 				    "cpu range [0-%d]: %s\n",
2127bebe46cSjc 				    cpuip->cn_ncpustats + 1,
2137bebe46cSjc 				    topo_mod_errmsg(mod));
21414ea4bb7Ssd 				return (-1); /* mod_errno set */
21514ea4bb7Ssd 			}
21614ea4bb7Ssd 			(void) topo_method_register(mod, pnode, cpu_methods);
21714ea4bb7Ssd 			return (cpu_create(mod, pnode, name, min, max, cpuip));
21814ea4bb7Ssd 
21914ea4bb7Ssd 		} else {
22014ea4bb7Ssd 			/* Fail to load the module */
22114ea4bb7Ssd 			topo_mod_dprintf(mod,
2227bebe46cSjc 			    "Failed to load module %s: %s",
2237bebe46cSjc 			    PLATFORM_CPU_NAME,
2247bebe46cSjc 			    topo_mod_errmsg(mod));
22514ea4bb7Ssd 			return (-1);
22614ea4bb7Ssd 		}
2277aec1d6eScindi 	}
2287aec1d6eScindi 
22914ea4bb7Ssd 	if (topo_mod_enumerate(nmp, pnode, PLATFORM_CPU_NAME, name,
2307bebe46cSjc 	    min, max, NULL) < 0) {
23114ea4bb7Ssd 		topo_mod_dprintf(mod,
2327bebe46cSjc 		    "%s failed to enumerate: %s",
2337bebe46cSjc 		    PLATFORM_CPU_NAME,
2347bebe46cSjc 		    topo_mod_errmsg(mod));
23514ea4bb7Ssd 		return (-1);
23614ea4bb7Ssd 	}
2377aec1d6eScindi 	(void) topo_method_register(mod, pnode, cpu_methods);
2387aec1d6eScindi 
23914ea4bb7Ssd 	return (0);
2407aec1d6eScindi }
2417aec1d6eScindi 
2427aec1d6eScindi static void
cpu_release(topo_mod_t * mod,tnode_t * node)2437aec1d6eScindi cpu_release(topo_mod_t *mod, tnode_t *node)
2447aec1d6eScindi {
2457aec1d6eScindi 	topo_method_unregister_all(mod, node);
2467aec1d6eScindi }
2477aec1d6eScindi 
2487aec1d6eScindi ssize_t
fmri_nvl2str(nvlist_t * nvl,uint8_t version,char * buf,size_t buflen)2497aec1d6eScindi fmri_nvl2str(nvlist_t *nvl, uint8_t version, char *buf, size_t buflen)
2507aec1d6eScindi {
2517aec1d6eScindi 	int rc;
2527bebe46cSjc 	uint8_t	type;
253*a62774dfSSinanallur Balasubramanian 	uint32_t cpuid, way;
254*a62774dfSSinanallur Balasubramanian 	uint32_t	index;
255*a62774dfSSinanallur Balasubramanian 	uint16_t	bit;
2567aec1d6eScindi 	uint64_t serint;
2577bebe46cSjc 	char *serstr = NULL;
2587aec1d6eScindi 
2597aec1d6eScindi 	if (version == CPU_SCHEME_VERSION0) {
2607aec1d6eScindi 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
2617aec1d6eScindi 		    nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
2627aec1d6eScindi 		    != 0)
2637aec1d6eScindi 			return (0);
2647aec1d6eScindi 
2657aec1d6eScindi 		return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
2667aec1d6eScindi 		    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
2677aec1d6eScindi 		    (u_longlong_t)serint));
2687bebe46cSjc 
2697aec1d6eScindi 	} else if (version == CPU_SCHEME_VERSION1) {
2707aec1d6eScindi 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
2717aec1d6eScindi 			return (0);
2727aec1d6eScindi 
2737aec1d6eScindi 		/*
2747aec1d6eScindi 		 * Serial number is an optional element
2757aec1d6eScindi 		 */
2767aec1d6eScindi 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
2777aec1d6eScindi 		    &serstr)) != 0)
2787bebe46cSjc 
2797bebe46cSjc 			if (rc != ENOENT)
2807bebe46cSjc 				return (0);
2817bebe46cSjc 
2827bebe46cSjc 		/*
2837bebe46cSjc 		 * Cache index, way and type are optional elements
2847bebe46cSjc 		 * But if we have one of them, we must have them all.
2857bebe46cSjc 		 */
2867bebe46cSjc 		rc = nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_INDEX,
2877bebe46cSjc 		    &index);
2887bebe46cSjc 		rc |= nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_WAY, &way);
289*a62774dfSSinanallur Balasubramanian 		rc |= nvlist_lookup_uint16(nvl, FM_FMRI_CPU_CACHE_BIT, &bit);
2907bebe46cSjc 		rc |= nvlist_lookup_uint8(nvl, FM_FMRI_CPU_CACHE_TYPE, &type);
2917bebe46cSjc 
2927bebe46cSjc 		/* Insure there were no errors accessing the nvl */
2937bebe46cSjc 		if (rc != 0 && rc != ENOENT)
2947bebe46cSjc 			return (0);
2957bebe46cSjc 
2967bebe46cSjc 		if (serstr == NULL) {
2977bebe46cSjc 			/* If we have a serial string and no cache info */
2987aec1d6eScindi 			if (rc == ENOENT)
2997aec1d6eScindi 				return (snprintf(buf, buflen, "cpu:///%s=%u",
3007aec1d6eScindi 				    FM_FMRI_CPU_ID, cpuid));
3017bebe46cSjc 			else {
3027bebe46cSjc 				return (snprintf(buf, buflen,
303*a62774dfSSinanallur Balasubramanian 				    "cpu:///%s=%u/%s=%u/%s=%u/%s=%d/%s=%d",
3047bebe46cSjc 				    FM_FMRI_CPU_ID, cpuid,
3057bebe46cSjc 				    FM_FMRI_CPU_CACHE_INDEX, index,
3067bebe46cSjc 				    FM_FMRI_CPU_CACHE_WAY, way,
307*a62774dfSSinanallur Balasubramanian 				    FM_FMRI_CPU_CACHE_BIT, bit,
3087bebe46cSjc 				    FM_FMRI_CPU_CACHE_TYPE, type));
3097bebe46cSjc 			}
3107bebe46cSjc 		} else {
3117bebe46cSjc 			if (rc == ENOENT) {
3127bebe46cSjc 				return (snprintf(buf, buflen,
3137bebe46cSjc 				    "cpu:///%s=%u/%s=%s",
3147bebe46cSjc 				    FM_FMRI_CPU_ID, cpuid,
3157bebe46cSjc 				    FM_FMRI_CPU_SERIAL_ID, serstr));
3167bebe46cSjc 			} else {
3177bebe46cSjc 				return (snprintf(buf, buflen,
318*a62774dfSSinanallur Balasubramanian 				"cpu:///%s=%u/%s=%s/%s=%u/%s=%u/%s=%d/%s=%d",
3197bebe46cSjc 				    FM_FMRI_CPU_ID, cpuid,
3207bebe46cSjc 				    FM_FMRI_CPU_SERIAL_ID, serstr,
3217bebe46cSjc 				    FM_FMRI_CPU_CACHE_INDEX, index,
3227bebe46cSjc 				    FM_FMRI_CPU_CACHE_WAY, way,
323*a62774dfSSinanallur Balasubramanian 				    FM_FMRI_CPU_CACHE_BIT, bit,
3247bebe46cSjc 				    FM_FMRI_CPU_CACHE_TYPE, type));
3257bebe46cSjc 			}
3267bebe46cSjc 		}
3277bebe46cSjc 	} else
3287aec1d6eScindi 		return (0);
3297aec1d6eScindi }
3307aec1d6eScindi 
3317aec1d6eScindi /*ARGSUSED*/
3327aec1d6eScindi static int
cpu_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)3337aec1d6eScindi cpu_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3347aec1d6eScindi     nvlist_t *in, nvlist_t **out)
3357aec1d6eScindi {
3367aec1d6eScindi 	uint8_t fver;
3377aec1d6eScindi 	ssize_t len;
3387aec1d6eScindi 	char *name;
3397aec1d6eScindi 
3407aec1d6eScindi 	if (version > TOPO_METH_NVL2STR_VERSION)
3417aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
3427aec1d6eScindi 
3437aec1d6eScindi 	if (nvlist_lookup_uint8(in, FM_VERSION, &fver) != 0)
3447aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
3457aec1d6eScindi 
3467aec1d6eScindi 	if ((len = fmri_nvl2str(in, fver, NULL, 0)) == 0 ||
3477aec1d6eScindi 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
3487aec1d6eScindi 	    fmri_nvl2str(in, fver, name, len + 1) == 0)
3497aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3507aec1d6eScindi 
3517aec1d6eScindi 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
3527aec1d6eScindi 		topo_mod_free(mod, name, len + 1);
3537aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3547aec1d6eScindi 	}
3557aec1d6eScindi 
3567aec1d6eScindi 	if (nvlist_add_string(*out, "fmri-string", name) != 0) {
3577aec1d6eScindi 		topo_mod_free(mod, name, len + 1);
3587aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3597aec1d6eScindi 	}
3607aec1d6eScindi 	topo_mod_free(mod, name, len + 1);
3617aec1d6eScindi 
3627aec1d6eScindi 	return (0);
3637aec1d6eScindi }
3647aec1d6eScindi 
3657aec1d6eScindi /*ARGSUSED*/
3667aec1d6eScindi static int
cpu_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)3677aec1d6eScindi cpu_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3687aec1d6eScindi     nvlist_t *in, nvlist_t **out)
3697aec1d6eScindi {
3707aec1d6eScindi 	int err;
37180ab886dSwesolows 	ulong_t cpuid;
372*a62774dfSSinanallur Balasubramanian 	uint8_t	type = 0;
373*a62774dfSSinanallur Balasubramanian 	uint32_t way = 0;
374*a62774dfSSinanallur Balasubramanian 	uint32_t index = 0;
375*a62774dfSSinanallur Balasubramanian 	int	index_present = 0;
376*a62774dfSSinanallur Balasubramanian 	uint16_t	bit = 0;
3777bebe46cSjc 	char *str, *s, *end, *serial_end;
3787aec1d6eScindi 	char *serial = NULL;
3797aec1d6eScindi 	nvlist_t *fmri;
3807aec1d6eScindi 
3817aec1d6eScindi 	if (version > TOPO_METH_STR2NVL_VERSION)
3827aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
3837aec1d6eScindi 
3847aec1d6eScindi 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
3857aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3867aec1d6eScindi 
3877aec1d6eScindi 	/* We're expecting a string version of a cpu scheme FMRI */
3887aec1d6eScindi 	if (strncmp(str, "cpu:///", 7) != 0)
3897aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3907aec1d6eScindi 
3917aec1d6eScindi 	s = strchr(str + 7, '=');
3927aec1d6eScindi 	if (s == NULL)
3937aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3947aec1d6eScindi 
3957aec1d6eScindi 	++s;
3967aec1d6eScindi 	cpuid = strtoul(s, &end, 0);
3977aec1d6eScindi 
3987aec1d6eScindi 	if (cpuid == ULONG_MAX && errno == ERANGE)
3997aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
4007aec1d6eScindi 
4017bebe46cSjc 	/* If there is a serial #, then there might also be cache data */
4027aec1d6eScindi 	if (*(s = end) == '/') {
4037aec1d6eScindi 		s = strchr(s, '=');
4047aec1d6eScindi 		++s;
4057aec1d6eScindi 		serial = s;
4067bebe46cSjc 		serial_end = strchr(s, '/');
4077bebe46cSjc 		/* If there is cache data, all must be present */
408*a62774dfSSinanallur Balasubramanian 		if (serial_end != NULL) {
409*a62774dfSSinanallur Balasubramanian 			/* Now terminate the serial string */
410*a62774dfSSinanallur Balasubramanian 			*serial_end = '\0';
411*a62774dfSSinanallur Balasubramanian 			index_present = 1;
412*a62774dfSSinanallur Balasubramanian 			s = serial_end + 1;
4137bebe46cSjc 			s = strchr(s, '=');
4147bebe46cSjc 			++s;
4157bebe46cSjc 			index = strtoul(s, &end, 0);
4167bebe46cSjc 			if (*(s = end) != '/') {
4177bebe46cSjc 				return (topo_mod_seterrno(mod,
4187bebe46cSjc 				    EMOD_FMRI_MALFORM));
4197bebe46cSjc 			}
4207bebe46cSjc 			s = strchr(s, '=');
421*a62774dfSSinanallur Balasubramanian 			if (s == NULL) {
422*a62774dfSSinanallur Balasubramanian 				return (topo_mod_seterrno(mod,
423*a62774dfSSinanallur Balasubramanian 				    EMOD_FMRI_MALFORM));
424*a62774dfSSinanallur Balasubramanian 			}
4257bebe46cSjc 			++s;
4267bebe46cSjc 			way = strtoul(s, &end, 0);
4277bebe46cSjc 			if (*(s = end) != '/') {
4287bebe46cSjc 				return (topo_mod_seterrno(mod,
4297bebe46cSjc 				    EMOD_FMRI_MALFORM));
4307bebe46cSjc 			}
4317bebe46cSjc 			s = strchr(s, '=');
432*a62774dfSSinanallur Balasubramanian 			if (s == NULL) {
433*a62774dfSSinanallur Balasubramanian 				return (topo_mod_seterrno(mod,
434*a62774dfSSinanallur Balasubramanian 				    EMOD_FMRI_MALFORM));
435*a62774dfSSinanallur Balasubramanian 			}
4367bebe46cSjc 			++s;
437*a62774dfSSinanallur Balasubramanian 			bit = strtoul(s, &end, 0);
4387bebe46cSjc 			if (*(s = end) != '/') {
4397bebe46cSjc 				return (topo_mod_seterrno(mod,
4407bebe46cSjc 				    EMOD_FMRI_MALFORM));
4417bebe46cSjc 			}
442*a62774dfSSinanallur Balasubramanian 			s = strchr(s, '=');
443*a62774dfSSinanallur Balasubramanian 			if (s == NULL) {
444*a62774dfSSinanallur Balasubramanian 				return (topo_mod_seterrno(mod,
445*a62774dfSSinanallur Balasubramanian 				    EMOD_FMRI_MALFORM));
446*a62774dfSSinanallur Balasubramanian 			}
447*a62774dfSSinanallur Balasubramanian 			++s;
448*a62774dfSSinanallur Balasubramanian 			type = strtoul(s, &end, 0);
4497bebe46cSjc 		}
4507bebe46cSjc 
4517aec1d6eScindi 	}
4527aec1d6eScindi 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
4537aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
4547aec1d6eScindi 
4557aec1d6eScindi 	err = nvlist_add_uint8(fmri, FM_VERSION, CPU_SCHEME_VERSION1);
4567aec1d6eScindi 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
45780ab886dSwesolows 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, (uint32_t)cpuid);
4587aec1d6eScindi 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, 0);
4597aec1d6eScindi 	if (serial != NULL)
4607aec1d6eScindi 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID,
4617aec1d6eScindi 		    serial);
4627aec1d6eScindi 
463*a62774dfSSinanallur Balasubramanian 	if (index_present) {
4647bebe46cSjc 		err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_CACHE_INDEX,
4657bebe46cSjc 		    index);
4667bebe46cSjc 		err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_CACHE_WAY,
4677bebe46cSjc 		    way);
468*a62774dfSSinanallur Balasubramanian 		err |= nvlist_add_uint16(fmri, FM_FMRI_CPU_CACHE_BIT,
469*a62774dfSSinanallur Balasubramanian 		    bit);
4707bebe46cSjc 		err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_CACHE_TYPE,
4717bebe46cSjc 		    type);
4727bebe46cSjc 	}
4737aec1d6eScindi 	if (err != 0) {
4747aec1d6eScindi 		nvlist_free(fmri);
4757aec1d6eScindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
4767aec1d6eScindi 	}
4777aec1d6eScindi 	*out = fmri;
4787aec1d6eScindi 
4797aec1d6eScindi 	return (0);
4807aec1d6eScindi }
4817aec1d6eScindi 
4827aec1d6eScindi static nvlist_t *
fmri_create(topo_mod_t * mod,uint32_t cpu_id,uint8_t cpumask,char * s)4837aec1d6eScindi fmri_create(topo_mod_t *mod, uint32_t cpu_id, uint8_t cpumask, char *s)
4847aec1d6eScindi {
4857aec1d6eScindi 	int err;
4867aec1d6eScindi 	nvlist_t *fmri;
4877aec1d6eScindi 
4887aec1d6eScindi 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
4897aec1d6eScindi 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
4907aec1d6eScindi 		return (NULL);
4917aec1d6eScindi 	}
4927aec1d6eScindi 
4937aec1d6eScindi 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION);
4947aec1d6eScindi 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
4957aec1d6eScindi 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpu_id);
4967aec1d6eScindi 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask);
4977aec1d6eScindi 	if (s != NULL)
4987aec1d6eScindi 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, s);
4997aec1d6eScindi 	if (err != 0) {
5007aec1d6eScindi 		nvlist_free(fmri);
5017aec1d6eScindi 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
5027aec1d6eScindi 		return (NULL);
5037aec1d6eScindi 	}
5047aec1d6eScindi 
5057aec1d6eScindi 	return (fmri);
5067aec1d6eScindi }
5077aec1d6eScindi 
5087aec1d6eScindi /*ARGSUSED*/
5097aec1d6eScindi static int
cpu_fmri_asru(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)5107aec1d6eScindi cpu_fmri_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version,
5117aec1d6eScindi     nvlist_t *in, nvlist_t **out)
5127aec1d6eScindi {
5137aec1d6eScindi 	int rc;
5147aec1d6eScindi 	uint32_t cpu_id;
5157aec1d6eScindi 	uint8_t cpumask = 0;
5167aec1d6eScindi 	char *serial = NULL;
5177aec1d6eScindi 
5187aec1d6eScindi 	if ((rc = nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpu_id)) != 0) {
5197aec1d6eScindi 		if (rc == ENOENT)
5207aec1d6eScindi 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
5217aec1d6eScindi 		else
5227aec1d6eScindi 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
5237aec1d6eScindi 	}
5247aec1d6eScindi 
5257aec1d6eScindi 	(void) nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &serial);
5267aec1d6eScindi 	(void) nvlist_lookup_uint8(in, FM_FMRI_CPU_MASK, &cpumask);
5277aec1d6eScindi 
5287aec1d6eScindi 	*out = fmri_create(mod, cpu_id, cpumask, serial);
5297aec1d6eScindi 
5307aec1d6eScindi 	return (0);
5317aec1d6eScindi }
532d5ea2cedSScott Davenport 
533d5ea2cedSScott Davenport /*ARGSUSED*/
534d5ea2cedSScott Davenport static int
cpu_fmri_create_meth(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)535d5ea2cedSScott Davenport cpu_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
536d5ea2cedSScott Davenport     nvlist_t *in, nvlist_t **out)
537d5ea2cedSScott Davenport {
538d5ea2cedSScott Davenport 	int		rc;
539d5ea2cedSScott Davenport 	nvlist_t	*args;
540d5ea2cedSScott Davenport 	uint32_t	cpu_id;
541d5ea2cedSScott Davenport 	uint8_t		cpumask = 0;
542d5ea2cedSScott Davenport 	char		*serial = NULL;
543d5ea2cedSScott Davenport 
544d5ea2cedSScott Davenport 	if (version > TOPO_METH_FMRI_VERSION) {
545d5ea2cedSScott Davenport 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
546d5ea2cedSScott Davenport 	}
547d5ea2cedSScott Davenport 
548d5ea2cedSScott Davenport 	rc = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args);
549d5ea2cedSScott Davenport 	if (rc != 0) {
550d5ea2cedSScott Davenport 		/*
551d5ea2cedSScott Davenport 		 * This routine requires arguments to be packed in the
552d5ea2cedSScott Davenport 		 * format used in topo_fmri_create()
553d5ea2cedSScott Davenport 		 */
554d5ea2cedSScott Davenport 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
555d5ea2cedSScott Davenport 	}
556d5ea2cedSScott Davenport 
557d5ea2cedSScott Davenport 	if (nvlist_lookup_string(args, FM_FMRI_CPU_SERIAL_ID, &serial) != 0 ||
558d5ea2cedSScott Davenport 	    nvlist_lookup_uint32(args, FM_FMRI_CPU_ID, &cpu_id) != 0 ||
559d5ea2cedSScott Davenport 	    nvlist_lookup_uint8(args, FM_FMRI_CPU_MASK, &cpumask) != 0) {
560d5ea2cedSScott Davenport 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
561d5ea2cedSScott Davenport 	}
562d5ea2cedSScott Davenport 
563d5ea2cedSScott Davenport 	*out = fmri_create(mod, cpu_id, cpumask, serial);
564d5ea2cedSScott Davenport 	if (*out == NULL) {
565d5ea2cedSScott Davenport 		return (-1);
566d5ea2cedSScott Davenport 	}
567d5ea2cedSScott Davenport 
568d5ea2cedSScott Davenport 	return (0);
569d5ea2cedSScott Davenport }
570