xref: /illumos-gate/usr/src/cmd/fm/schemes/cpu/cpu.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
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 2006 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 
33 #include <strings.h>
34 #include <errno.h>
35 #include <kstat.h>
36 
37 #ifdef	sparc
38 #include <cpu_mdesc.h>
39 #endif
40 
41 /*
42  * The scheme plugin for cpu FMRIs.
43  */
44 
45 #ifdef sparc
46 #define	ONTARIO_PLAT_NAME	"SUNW,Sun-Fire-T200"
47 #define	ERIE_PLAT_NAME		"SUNW,Sun-Fire-T1000"
48 #define	PELTON_PLAT_NAME	"SUNW,Netra-T2000"
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 == (uint32_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, PELTON_PLAT_NAME) == 0 ||
234 		    strcmp(platform, ERIE_PLAT_NAME) == 0) {
235 			if (cpu_phys2virt(cpuid, &cpuvid) != 0)
236 				return (fmd_fmri_set_errno(ENOENT));
237 
238 			(void) nvlist_remove_all(nvl, FM_FMRI_CPU_VID);
239 			if ((rc = nvlist_add_uint32(nvl, FM_FMRI_CPU_VID,
240 			    cpuvid)) != 0)
241 				return (fmd_fmri_set_errno(rc));
242 		}
243 	}
244 #endif /* sparc */
245 
246 	return (0);
247 }
248 
249 int
250 fmd_fmri_present(nvlist_t *nvl)
251 {
252 	int rc;
253 	uint8_t version;
254 	uint32_t cpuid;
255 	uint64_t nvlserid, curserid;
256 	char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */
257 
258 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
259 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
260 		return (fmd_fmri_set_errno(EINVAL));
261 
262 	if (version == CPU_SCHEME_VERSION0) {
263 		if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID,
264 		    &nvlserid) != 0)
265 			return (fmd_fmri_set_errno(EINVAL));
266 		if (cpu_get_serialid_V0(cpuid, &curserid) != 0)
267 			return (errno == ENOENT ? 0 : -1);
268 
269 		return (curserid == nvlserid);
270 
271 	} else if (version == CPU_SCHEME_VERSION1) {
272 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
273 		    &nvlserstr)) != 0)
274 			if (rc != ENOENT)
275 				return (fmd_fmri_set_errno(EINVAL));
276 
277 		/*
278 		 * Serial id may not be available, return true
279 		 */
280 		if (cpu_get_serialid_V1(cpuid, curserbuf, 21) != 0)
281 			return (1);
282 
283 		return (strcmp(curserbuf, nvlserstr) == 0 ? 1 : 0);
284 
285 	} else {
286 		return (fmd_fmri_set_errno(EINVAL));
287 	}
288 }
289 
290 int
291 fmd_fmri_unusable(nvlist_t *nvl)
292 {
293 	uint8_t version;
294 	uint32_t cpuid;
295 
296 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
297 	    version > FM_CPU_SCHEME_VERSION ||
298 	    nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
299 		return (fmd_fmri_set_errno(EINVAL));
300 #ifdef sparc
301 	{
302 		uint32_t cpuvid;
303 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_VID, &cpuvid) == 0) {
304 			/*
305 			 * This FMRI has a 'cpuvid' member, but its value could
306 			 * be stale -- especially when restoring saved state.
307 			 * Do a fresh lookup and use the result for p_online().
308 			 */
309 			if (cpu_phys2virt(cpuid, &cpuvid) != 0)
310 				return (fmd_fmri_set_errno(ENOENT));
311 			return (p_online(cpuvid, P_STATUS) == P_FAULTED);
312 		}
313 	}
314 #endif /* sparc */
315 
316 	return (p_online(cpuid, P_STATUS) == P_FAULTED);
317 }
318 
319 #ifdef	sparc
320 int
321 fmd_fmri_init(void)
322 {
323 	return (cpu_mdesc_init());
324 }
325 
326 void
327 fmd_fmri_fini(void)
328 {
329 	cpu_mdesc_fini();
330 }
331 #endif	/* sparc */
332