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