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