xref: /illumos-gate/usr/src/cmd/fm/schemes/cpu/cpu.c (revision c8589f13ba961772dd5a0d699c5bb926f3006c33)
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 
176 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
177 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
178 		return (fmd_fmri_set_errno(EINVAL));
179 
180 	/*
181 	 * If the cpu-scheme topology exports this method expand(), invoke it.
182 	 */
183 	rc = topo_fmri_expand(fmd_fmri_topology(TOPO_VERSION), nvl, &err);
184 	if (err != ETOPO_METHOD_NOTSUP)
185 		return (rc);
186 
187 	if (version == CPU_SCHEME_VERSION0) {
188 		if ((rc = nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
189 		    &serialid)) != 0) {
190 			if (rc != ENOENT)
191 				return (fmd_fmri_set_errno(rc));
192 
193 			if (cpu_get_serialid_V0(cpuid, &serialid) != 0)
194 				return (-1); /* errno is set for us */
195 
196 			if ((rc = nvlist_add_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
197 			    serialid)) != 0)
198 				return (fmd_fmri_set_errno(rc));
199 		}
200 	} else if (version == CPU_SCHEME_VERSION1) {
201 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
202 		    &serstr)) != 0) {
203 			if (rc != ENOENT)
204 				return (fmd_fmri_set_errno(rc));
205 
206 			if (cpu_get_serialid_V1(cpuid, serbuf, 21) != 0)
207 				return (0); /* Serial number is optional */
208 
209 			if ((rc = nvlist_add_string(nvl, FM_FMRI_CPU_SERIAL_ID,
210 			    serbuf)) != 0)
211 				return (fmd_fmri_set_errno(rc));
212 		}
213 	} else {
214 		return (fmd_fmri_set_errno(EINVAL));
215 	}
216 
217 	return (0);
218 }
219 
220 int
221 fmd_fmri_present(nvlist_t *nvl)
222 {
223 	int rc, err;
224 	uint8_t version;
225 	uint32_t cpuid;
226 	uint64_t nvlserid, curserid;
227 	char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */
228 
229 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
230 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
231 		return (fmd_fmri_set_errno(EINVAL));
232 
233 	/*
234 	 * If the cpu-scheme topology exports this method present(), invoke it.
235 	 */
236 	rc = topo_fmri_present(fmd_fmri_topology(TOPO_VERSION), nvl, &err);
237 	if (err != ETOPO_METHOD_NOTSUP)
238 		return (rc);
239 
240 	if (version == CPU_SCHEME_VERSION0) {
241 		if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
242 		    &nvlserid) != 0)
243 			return (fmd_fmri_set_errno(EINVAL));
244 		if (cpu_get_serialid_V0(cpuid, &curserid) != 0)
245 			return (errno == ENOENT ? 0 : -1);
246 
247 		return (curserid == nvlserid);
248 
249 	} else if (version == CPU_SCHEME_VERSION1) {
250 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
251 		    &nvlserstr)) != 0)
252 			if (rc != ENOENT)
253 				return (fmd_fmri_set_errno(EINVAL));
254 
255 		/*
256 		 * If serial id is not available, just check if the cpuid
257 		 * is present.
258 		 */
259 		if (cpu_get_serialid_V1(cpuid, curserbuf, 21) != 0)
260 			return (cpu_cpuid_present(cpuid));
261 
262 		return (strcmp(curserbuf, nvlserstr) == 0 ? 1 : 0);
263 
264 	} else {
265 		return (fmd_fmri_set_errno(EINVAL));
266 	}
267 }
268 
269 int
270 fmd_fmri_unusable(nvlist_t *nvl)
271 {
272 	int rc, err;
273 	uint8_t version;
274 	uint32_t cpuid;
275 
276 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
277 	    version > FM_CPU_SCHEME_VERSION ||
278 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
279 		return (fmd_fmri_set_errno(EINVAL));
280 
281 	/*
282 	 * If the cpu-scheme topology exports this method unusable(), invoke it.
283 	 */
284 	rc = topo_fmri_unusable(fmd_fmri_topology(TOPO_VERSION), nvl, &err);
285 	if (err != ETOPO_METHOD_NOTSUP)
286 		return (rc);
287 
288 	return (p_online(cpuid, P_STATUS) == P_FAULTED);
289 }
290