xref: /illumos-gate/usr/src/cmd/fm/schemes/cpu/cpu.c (revision 7257d1b4)
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 2008 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 /*
40  * The scheme plugin for cpu FMRIs.
41  */
42 
43 ssize_t
44 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
45 {
46 	int err;
47 	uint8_t version, type;
48 	uint32_t cpuid, index, way;
49 	uint64_t serint;
50 	char *serstr = NULL;
51 
52 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0)
53 		return (fmd_fmri_set_errno(EINVAL));
54 
55 	if (version == CPU_SCHEME_VERSION0) {
56 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
57 		    nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
58 		    != 0)
59 			return (fmd_fmri_set_errno(EINVAL));
60 
61 		return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
62 		    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
63 		    (u_longlong_t)serint));
64 
65 	} else if (version == CPU_SCHEME_VERSION1) {
66 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
67 			return (fmd_fmri_set_errno(EINVAL));
68 
69 		/*
70 		 * Serial number is an optional element
71 		 */
72 		if ((err = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
73 		    &serstr)) != 0)
74 
75 			if (err != ENOENT)
76 				return (fmd_fmri_set_errno(EINVAL));
77 
78 		/*
79 		 * Cache index, way and type are optional elements
80 		 * But if we have one of them, we must have them all.
81 		 */
82 		err = nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_INDEX,
83 		    &index);
84 		err |= nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_WAY, &way);
85 		err |= nvlist_lookup_uint8(nvl, FM_FMRI_CPU_CACHE_TYPE, &type);
86 
87 		/* Insure there were no errors accessing the nvl */
88 		if (err != 0 && err != ENOENT)
89 			return (fmd_fmri_set_errno(EINVAL));
90 
91 		if (serstr == NULL) {
92 			/* If we have a serial string and no cache info */
93 			if (err == ENOENT)
94 				return (snprintf(buf, buflen, "cpu:///%s=%u",
95 				    FM_FMRI_CPU_ID, cpuid));
96 			else {
97 				return (snprintf(buf, buflen,
98 				    "cpu:///%s=%u/%s=%u/%s=%u/%s=%d",
99 				    FM_FMRI_CPU_ID, cpuid,
100 				    FM_FMRI_CPU_CACHE_INDEX, index,
101 				    FM_FMRI_CPU_CACHE_WAY, way,
102 				    FM_FMRI_CPU_CACHE_TYPE, type));
103 			}
104 		} else {
105 			if (err == ENOENT) {
106 				return (snprintf(buf, buflen,
107 				    "cpu:///%s=%u/%s=%s",
108 				    FM_FMRI_CPU_ID, cpuid,
109 				    FM_FMRI_CPU_SERIAL_ID, serstr));
110 			} else {
111 				return (snprintf(buf, buflen,
112 				    "cpu:///%s=%u/%s=%s/%s=%u/%s=%u/%s=%d",
113 				    FM_FMRI_CPU_ID, cpuid,
114 				    FM_FMRI_CPU_SERIAL_ID, serstr,
115 				    FM_FMRI_CPU_CACHE_INDEX, index,
116 				    FM_FMRI_CPU_CACHE_WAY, way,
117 				    FM_FMRI_CPU_CACHE_TYPE, type));
118 			}
119 		}
120 	} else
121 		return (fmd_fmri_set_errno(EINVAL));
122 }
123 
124 /*
125  * Determine if a cpuid is present.
126  */
127 /*ARGSUSED*/
128 static int
129 cpu_cpuid_present(uint32_t cpuid)
130 {
131 #ifdef	sparc
132 	/*
133 	 * For SPARC, use kstats to see if the cpuid is present.
134 	 * Note that this may need to change for sun4v.
135 	 */
136 	kstat_ctl_t *kc;
137 	kstat_t *ksp = NULL;
138 	if ((kc = kstat_open()) == NULL)
139 		return (-1); /* errno is set for us */
140 	ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL);
141 	(void) kstat_close(kc);
142 	return ((ksp == NULL) ? 0 : 1);
143 #else	/* sparc */
144 	/*
145 	 * For x64, just return true.
146 	 */
147 	return (1);
148 #endif	/* sparc */
149 }
150 
151 static int
152 cpu_get_serialid_kstat(uint32_t cpuid, uint64_t *serialidp)
153 {
154 	kstat_named_t *kn;
155 	kstat_ctl_t *kc;
156 	kstat_t *ksp;
157 	int i;
158 
159 	if ((kc = kstat_open()) == NULL)
160 		return (-1); /* errno is set for us */
161 
162 	if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL) {
163 		(void) kstat_close(kc);
164 		return (fmd_fmri_set_errno(ENOENT));
165 	}
166 
167 	if (kstat_read(kc, ksp, NULL) == -1) {
168 		int oserr = errno;
169 		(void) kstat_close(kc);
170 		return (fmd_fmri_set_errno(oserr));
171 	}
172 
173 	for (kn = ksp->ks_data, i = 0; i < ksp->ks_ndata; i++, kn++) {
174 		if (strcmp(kn->name, "device_ID") == 0) {
175 			*serialidp = kn->value.ui64;
176 			(void) kstat_close(kc);
177 			return (0);
178 		}
179 	}
180 
181 	(void) kstat_close(kc);
182 	return (fmd_fmri_set_errno(ENOENT));
183 }
184 
185 static int
186 cpu_get_serialid_V1(uint32_t cpuid, char *serbuf, size_t len)
187 {
188 	int err;
189 	uint64_t serial = 0;
190 
191 	err = cpu_get_serialid_kstat(cpuid, &serial);
192 
193 	(void) snprintf(serbuf, len, "%llX", (u_longlong_t)serial);
194 	return (err);
195 }
196 
197 static int
198 cpu_get_serialid_V0(uint32_t cpuid, uint64_t *serialidp)
199 {
200 	return (cpu_get_serialid_kstat(cpuid, serialidp));
201 }
202 
203 int
204 fmd_fmri_expand(nvlist_t *nvl)
205 {
206 	uint8_t version;
207 	uint32_t cpuid;
208 	uint64_t serialid;
209 	char *serstr, serbuf[21]; /* sizeof (UINT64_MAX) + '\0' */
210 	int rc, err;
211 	topo_hdl_t *thp;
212 
213 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
214 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
215 		return (fmd_fmri_set_errno(EINVAL));
216 
217 	/*
218 	 * If the cpu-scheme topology exports this method expand(), invoke it.
219 	 */
220 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
221 		return (fmd_fmri_set_errno(EINVAL));
222 
223 	rc = topo_fmri_expand(thp, nvl, &err);
224 	fmd_fmri_topo_rele(thp);
225 	if (err != ETOPO_METHOD_NOTSUP)
226 		return (rc);
227 
228 	if (version == CPU_SCHEME_VERSION0) {
229 		if ((rc = nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
230 		    &serialid)) != 0) {
231 			if (rc != ENOENT)
232 				return (fmd_fmri_set_errno(rc));
233 
234 			if (cpu_get_serialid_V0(cpuid, &serialid) != 0)
235 				return (-1); /* errno is set for us */
236 
237 			if ((rc = nvlist_add_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
238 			    serialid)) != 0)
239 				return (fmd_fmri_set_errno(rc));
240 		}
241 	} else if (version == CPU_SCHEME_VERSION1) {
242 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
243 		    &serstr)) != 0) {
244 			if (rc != ENOENT)
245 				return (fmd_fmri_set_errno(rc));
246 
247 			if (cpu_get_serialid_V1(cpuid, serbuf, 21) != 0)
248 				return (0); /* Serial number is optional */
249 
250 			if ((rc = nvlist_add_string(nvl, FM_FMRI_CPU_SERIAL_ID,
251 			    serbuf)) != 0)
252 				return (fmd_fmri_set_errno(rc));
253 		}
254 	} else {
255 		return (fmd_fmri_set_errno(EINVAL));
256 	}
257 
258 	return (0);
259 }
260 
261 int
262 fmd_fmri_present(nvlist_t *nvl)
263 {
264 	int rc, err;
265 	uint8_t version;
266 	uint32_t cpuid;
267 	uint64_t nvlserid, curserid;
268 	char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */
269 	topo_hdl_t *thp;
270 
271 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
272 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
273 		return (fmd_fmri_set_errno(EINVAL));
274 
275 	/*
276 	 * If the cpu-scheme topology exports this method present(), invoke it.
277 	 */
278 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
279 		return (fmd_fmri_set_errno(EINVAL));
280 	rc = topo_fmri_present(thp, nvl, &err);
281 	fmd_fmri_topo_rele(thp);
282 	if (err != ETOPO_METHOD_NOTSUP)
283 		return (rc);
284 
285 	if (version == CPU_SCHEME_VERSION0) {
286 		if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
287 		    &nvlserid) != 0)
288 			return (fmd_fmri_set_errno(EINVAL));
289 		if (cpu_get_serialid_V0(cpuid, &curserid) != 0)
290 			return (errno == ENOENT ? 0 : -1);
291 
292 		return (curserid == nvlserid);
293 
294 	} else if (version == CPU_SCHEME_VERSION1) {
295 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
296 		    &nvlserstr)) != 0)
297 			if (rc != ENOENT)
298 				return (fmd_fmri_set_errno(EINVAL));
299 
300 		/*
301 		 * If serial id is not available, just check if the cpuid
302 		 * is present.
303 		 */
304 		if (cpu_get_serialid_V1(cpuid, curserbuf, 21) != 0)
305 			return (cpu_cpuid_present(cpuid));
306 
307 		return (strcmp(curserbuf, nvlserstr) == 0 ? 1 : 0);
308 
309 	} else {
310 		return (fmd_fmri_set_errno(EINVAL));
311 	}
312 }
313 
314 int
315 fmd_fmri_unusable(nvlist_t *nvl)
316 {
317 	int rc, err = 0;
318 	uint8_t version;
319 	uint32_t cpuid;
320 	topo_hdl_t *thp;
321 
322 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
323 	    version > FM_CPU_SCHEME_VERSION ||
324 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
325 		return (fmd_fmri_set_errno(EINVAL));
326 
327 	/*
328 	 * If the cpu-scheme topology exports this method unusable(), invoke it.
329 	 */
330 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
331 		return (fmd_fmri_set_errno(EINVAL));
332 	rc = topo_fmri_unusable(thp, nvl, &err);
333 	fmd_fmri_topo_rele(thp);
334 	if (err != ETOPO_METHOD_NOTSUP)
335 		return (rc);
336 
337 	return (p_online(cpuid, P_STATUS) == P_FAULTED);
338 }
339 int
340 fmd_fmri_contains(nvlist_t *er, nvlist_t *ee)
341 {
342 	int ret1, ret2;
343 	char *erserstr, *eeserstr;
344 	uint8_t  ertype, eetype, erversion, eeversion;
345 	uint64_t erserint, eeserint;
346 	uint32_t erval, eeval;
347 	size_t count;
348 
349 	if (nvlist_lookup_uint32(er, FM_FMRI_CPU_ID, &erval) != 0)
350 		return (0);
351 	if (nvlist_lookup_uint32(ee, FM_FMRI_CPU_ID, &eeval) != 0)
352 		return (0);
353 	if (erval != eeval)
354 		return (0);
355 
356 	if (nvlist_lookup_uint8(er, FM_VERSION, &erversion) != 0)
357 		return (0);
358 
359 	if (nvlist_lookup_uint8(ee, FM_VERSION, &eeversion) != 0)
360 		return (0);
361 
362 	if (erversion != eeversion)
363 		return (0);
364 
365 	if (erversion == CPU_SCHEME_VERSION0) {
366 		if (nvlist_lookup_uint64(er, FM_FMRI_CPU_SERIAL_ID,
367 		    &erserint) != 0)
368 			return (0);
369 		if (nvlist_lookup_uint64(ee, FM_FMRI_CPU_SERIAL_ID,
370 		    &eeserint) != 0)
371 			return (0);
372 		if (erserint != eeserint)
373 			return (0);
374 	} else if (erversion == CPU_SCHEME_VERSION1) {
375 		/* Serial ID is an optional element */
376 		if ((ret1 = nvlist_lookup_string(er, FM_FMRI_CPU_SERIAL_ID,
377 		    &erserstr)) != 0)
378 			if (ret1 != ENOENT)
379 				return (0);
380 		if ((ret2 = nvlist_lookup_string(ee, FM_FMRI_CPU_SERIAL_ID,
381 		    &eeserstr)) != 0)
382 			if (ret2 != ENOENT)
383 				return (0);
384 		if (ret1 == 0 && ret2 == 0) {
385 			count = strlen(erserstr);
386 			if (strncmp(erserstr, eeserstr, count) != 0)
387 				return (0);
388 		}
389 	}
390 	if (nvlist_lookup_uint32(er, FM_FMRI_CPU_CACHE_INDEX, &erval) != 0)
391 		return (0);
392 	if (nvlist_lookup_uint32(ee, FM_FMRI_CPU_CACHE_INDEX, &eeval) != 0)
393 		return (0);
394 
395 	if (erval != eeval)
396 		return (0);
397 
398 	if (nvlist_lookup_uint32(er, FM_FMRI_CPU_CACHE_WAY, &erval) != 0)
399 		return (0);
400 	if (nvlist_lookup_uint32(ee, FM_FMRI_CPU_CACHE_WAY, &eeval) != 0)
401 		return (0);
402 
403 	if (erval != eeval)
404 		return (0);
405 
406 	if (nvlist_lookup_uint8(er, FM_FMRI_CPU_CACHE_TYPE, &ertype) != 0)
407 		return (0);
408 	if (nvlist_lookup_uint8(ee, FM_FMRI_CPU_CACHE_TYPE, &eetype) != 0)
409 		return (0);
410 
411 	if (eetype != ertype)
412 		return (0);
413 
414 	return (1);
415 }
416