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
41ssize_t
42fmd_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*/
68static int
69cpu_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
91static int
92cpu_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
125static int
126cpu_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
137static int
138cpu_get_serialid_V0(uint32_t cpuid, uint64_t *serialidp)
139{
140	return (cpu_get_serialid_kstat(cpuid, serialidp));
141}
142
143int
144fmd_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
201int
202fmd_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
254int
255fmd_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
313int
314fmd_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}
338int
339fmd_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