xref: /illumos-gate/usr/src/cmd/fm/schemes/cpu/cpu.c (revision fbd1c0dae6f4a2ccc2ce0527c7f19d3dd5ea90b8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/processor.h>
31 #include <fm/fmd_fmri.h>
32 #include <fm/libtopo.h>
33 
34 #include <strings.h>
35 #include <errno.h>
36 #include <kstat.h>
37 
38 /*
39  * The scheme plugin for cpu FMRIs.
40  */
41 
42 ssize_t
43 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
44 {
45 	int err;
46 	uint8_t version;
47 	uint32_t cpuid;
48 	uint64_t serint;
49 	char *serstr;
50 
51 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0)
52 		return (fmd_fmri_set_errno(EINVAL));
53 
54 	if (version == CPU_SCHEME_VERSION0) {
55 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
56 		    nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
57 		    != 0)
58 			return (fmd_fmri_set_errno(EINVAL));
59 
60 		return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
61 		    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
62 		    (u_longlong_t)serint));
63 
64 	} else if (version == CPU_SCHEME_VERSION1) {
65 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
66 			return (fmd_fmri_set_errno(EINVAL));
67 
68 		/*
69 		 * Serial number is an optional element
70 		 */
71 		if ((err = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
72 		    &serstr)) != 0)
73 			if (err == ENOENT)
74 				return (snprintf(buf, buflen, "cpu:///%s=%u",
75 				    FM_FMRI_CPU_ID, cpuid));
76 			else
77 				return (fmd_fmri_set_errno(EINVAL));
78 		else
79 			return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%s",
80 			    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
81 			    serstr));
82 
83 	} else {
84 		return (fmd_fmri_set_errno(EINVAL));
85 	}
86 }
87 
88 /*
89  * Determine if a cpuid is present.
90  */
91 /*ARGSUSED*/
92 static int
93 cpu_cpuid_present(uint32_t cpuid)
94 {
95 #ifdef	sparc
96 	/*
97 	 * For SPARC, use kstats to see if the cpuid is present.
98 	 * Note that this may need to change for sun4v.
99 	 */
100 	kstat_ctl_t *kc;
101 	kstat_t *ksp = NULL;
102 	if ((kc = kstat_open()) == NULL)
103 		return (-1); /* errno is set for us */
104 	ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL);
105 	(void) kstat_close(kc);
106 	return ((ksp == NULL) ? 0 : 1);
107 #else	/* sparc */
108 	/*
109 	 * For x64, just return true.
110 	 */
111 	return (1);
112 #endif	/* sparc */
113 }
114 
115 static int
116 cpu_get_serialid_kstat(uint32_t cpuid, uint64_t *serialidp)
117 {
118 	kstat_named_t *kn;
119 	kstat_ctl_t *kc;
120 	kstat_t *ksp;
121 	int i;
122 
123 	if ((kc = kstat_open()) == NULL)
124 		return (-1); /* errno is set for us */
125 
126 	if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL) {
127 		(void) kstat_close(kc);
128 		return (fmd_fmri_set_errno(ENOENT));
129 	}
130 
131 	if (kstat_read(kc, ksp, NULL) == -1) {
132 		int oserr = errno;
133 		(void) kstat_close(kc);
134 		return (fmd_fmri_set_errno(oserr));
135 	}
136 
137 	for (kn = ksp->ks_data, i = 0; i < ksp->ks_ndata; i++, kn++) {
138 		if (strcmp(kn->name, "device_ID") == 0) {
139 			*serialidp = kn->value.ui64;
140 			(void) kstat_close(kc);
141 			return (0);
142 		}
143 	}
144 
145 	(void) kstat_close(kc);
146 	return (fmd_fmri_set_errno(ENOENT));
147 }
148 
149 static int
150 cpu_get_serialid_V1(uint32_t cpuid, char *serbuf, size_t len)
151 {
152 	int err;
153 	uint64_t serial = 0;
154 
155 	err = cpu_get_serialid_kstat(cpuid, &serial);
156 
157 	(void) snprintf(serbuf, len, "%llX", (u_longlong_t)serial);
158 	return (err);
159 }
160 
161 static int
162 cpu_get_serialid_V0(uint32_t cpuid, uint64_t *serialidp)
163 {
164 	return (cpu_get_serialid_kstat(cpuid, serialidp));
165 }
166 
167 int
168 fmd_fmri_expand(nvlist_t *nvl)
169 {
170 	uint8_t version;
171 	uint32_t cpuid;
172 	uint64_t serialid;
173 	char *serstr, serbuf[21]; /* sizeof (UINT64_MAX) + '\0' */
174 	int rc, err;
175 	topo_hdl_t *thp;
176 
177 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
178 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
179 		return (fmd_fmri_set_errno(EINVAL));
180 
181 	/*
182 	 * If the cpu-scheme topology exports this method expand(), invoke it.
183 	 */
184 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
185 		return (fmd_fmri_set_errno(EINVAL));
186 
187 	rc = topo_fmri_expand(thp, nvl, &err);
188 	fmd_fmri_topo_rele(thp);
189 	if (err != ETOPO_METHOD_NOTSUP)
190 		return (rc);
191 
192 	if (version == CPU_SCHEME_VERSION0) {
193 		if ((rc = nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
194 		    &serialid)) != 0) {
195 			if (rc != ENOENT)
196 				return (fmd_fmri_set_errno(rc));
197 
198 			if (cpu_get_serialid_V0(cpuid, &serialid) != 0)
199 				return (-1); /* errno is set for us */
200 
201 			if ((rc = nvlist_add_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
202 			    serialid)) != 0)
203 				return (fmd_fmri_set_errno(rc));
204 		}
205 	} else if (version == CPU_SCHEME_VERSION1) {
206 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
207 		    &serstr)) != 0) {
208 			if (rc != ENOENT)
209 				return (fmd_fmri_set_errno(rc));
210 
211 			if (cpu_get_serialid_V1(cpuid, serbuf, 21) != 0)
212 				return (0); /* Serial number is optional */
213 
214 			if ((rc = nvlist_add_string(nvl, FM_FMRI_CPU_SERIAL_ID,
215 			    serbuf)) != 0)
216 				return (fmd_fmri_set_errno(rc));
217 		}
218 	} else {
219 		return (fmd_fmri_set_errno(EINVAL));
220 	}
221 
222 	return (0);
223 }
224 
225 int
226 fmd_fmri_present(nvlist_t *nvl)
227 {
228 	int rc, err;
229 	uint8_t version;
230 	uint32_t cpuid;
231 	uint64_t nvlserid, curserid;
232 	char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */
233 	topo_hdl_t *thp;
234 
235 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
236 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
237 		return (fmd_fmri_set_errno(EINVAL));
238 
239 	/*
240 	 * If the cpu-scheme topology exports this method present(), invoke it.
241 	 */
242 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
243 		return (fmd_fmri_set_errno(EINVAL));
244 	rc = topo_fmri_present(thp, nvl, &err);
245 	fmd_fmri_topo_rele(thp);
246 	if (err != ETOPO_METHOD_NOTSUP)
247 		return (rc);
248 
249 	if (version == CPU_SCHEME_VERSION0) {
250 		if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
251 		    &nvlserid) != 0)
252 			return (fmd_fmri_set_errno(EINVAL));
253 		if (cpu_get_serialid_V0(cpuid, &curserid) != 0)
254 			return (errno == ENOENT ? 0 : -1);
255 
256 		return (curserid == nvlserid);
257 
258 	} else if (version == CPU_SCHEME_VERSION1) {
259 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
260 		    &nvlserstr)) != 0)
261 			if (rc != ENOENT)
262 				return (fmd_fmri_set_errno(EINVAL));
263 
264 		/*
265 		 * If serial id is not available, just check if the cpuid
266 		 * is present.
267 		 */
268 		if (cpu_get_serialid_V1(cpuid, curserbuf, 21) != 0)
269 			return (cpu_cpuid_present(cpuid));
270 
271 		return (strcmp(curserbuf, nvlserstr) == 0 ? 1 : 0);
272 
273 	} else {
274 		return (fmd_fmri_set_errno(EINVAL));
275 	}
276 }
277 
278 int
279 fmd_fmri_unusable(nvlist_t *nvl)
280 {
281 	int rc, err;
282 	uint8_t version;
283 	uint32_t cpuid;
284 	topo_hdl_t *thp;
285 
286 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
287 	    version > FM_CPU_SCHEME_VERSION ||
288 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
289 		return (fmd_fmri_set_errno(EINVAL));
290 
291 	/*
292 	 * If the cpu-scheme topology exports this method unusable(), invoke it.
293 	 */
294 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
295 		return (fmd_fmri_set_errno(EINVAL));
296 	rc = topo_fmri_unusable(thp, nvl, &err);
297 	fmd_fmri_topo_rele(thp);
298 	if (err != ETOPO_METHOD_NOTSUP)
299 		return (rc);
300 
301 	return (p_online(cpuid, P_STATUS) == P_FAULTED);
302 }
303