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