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 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27/*
28 * Support routines for managing per-CPU state.
29 */
30
31#include <cmd_cpu.h>
32#include <cmd_ecache.h>
33#include <cmd_mem.h>
34#include <cmd.h>
35
36#include <stdio.h>
37#include <string.h>
38#include <strings.h>
39#include <errno.h>
40#include <kstat.h>
41#include <fm/fmd_api.h>
42#include <sys/async.h>
43#include <sys/fm/protocol.h>
44#include <sys/fm/cpu/UltraSPARC-III.h>
45#include <sys/cheetahregs.h>
46
47/*
48 * The unused argument 'clcode' is needed for our sun4v sibling.
49 */
50
51/*ARGSUSED*/
52int
53cmd_xr_fill(fmd_hdl_t *hdl, nvlist_t *nvl, cmd_xr_t *xr, cmd_errcl_t clcode)
54{
55	if (nvlist_lookup_uint16(nvl, FM_EREPORT_PAYLOAD_NAME_SYND,
56	    &xr->xr_synd) != 0)
57		return (-1);
58	if (nvlist_lookup_uint8(nvl, FM_EREPORT_PAYLOAD_NAME_SYND_STATUS,
59	    &xr->xr_synd_status) != 0)
60		return (-1);
61	if (nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_NAME_AFAR,
62	    &xr->xr_afar) != 0)
63		return (-1);
64	if (nvlist_lookup_uint8(nvl, FM_EREPORT_PAYLOAD_NAME_AFAR_STATUS,
65	    &xr->xr_afar_status) != 0)
66		return (-1);
67	return (0);
68}
69
70/*
71 * Search for the entry that matches the ena and the AFAR
72 * if we have a valid AFAR, otherwise just match the ENA
73 */
74cmd_xxcu_trw_t *
75cmd_trw_lookup(uint64_t ena, uint8_t afar_status, uint64_t afar)
76{
77	int i;
78
79	if (afar_status == AFLT_STAT_VALID) {
80		for (i = 0; i < cmd.cmd_xxcu_ntrw; i++) {
81			if (cmd.cmd_xxcu_trw[i].trw_ena == ena &&
82			    cmd.cmd_xxcu_trw[i].trw_afar == afar)
83				return (&cmd.cmd_xxcu_trw[i]);
84		}
85	} else  {
86		for (i = 0; i < cmd.cmd_xxcu_ntrw; i++) {
87		if (cmd.cmd_xxcu_trw[i].trw_ena == ena)
88			return (&cmd.cmd_xxcu_trw[i]);
89		}
90	}
91	return (NULL);
92}
93
94/*ARGSUSED*/
95cmd_errcl_t
96cmd_train_match(cmd_errcl_t trw_mask, cmd_errcl_t resolved_err)
97{
98	return (cmd_xxcu_train_match(trw_mask));
99}
100
101/*ARGSUSED*/
102int
103cmd_afar_status_check(uint8_t afar_status, cmd_errcl_t clcode)
104{
105	if (afar_status == AFLT_STAT_VALID)
106		return (0);
107	return (-1);
108}
109
110const errdata_t l3errdata =
111	{ &cmd.cmd_l3data_serd, "l3cachedata", CMD_PTR_CPU_L3DATA  };
112const errdata_t l2errdata =
113	{ &cmd.cmd_l2data_serd, "l2cachedata", CMD_PTR_CPU_L2DATA };
114
115void
116cmd_fill_errdata(cmd_errcl_t clcode, cmd_cpu_t *cpu, cmd_case_t **cc,
117    const errdata_t **ed)
118{
119	if (CMD_ERRCL_ISL2XXCU(clcode)) {
120		*ed = &l2errdata;
121		*cc = &cpu->cpu_l2data;
122	} else {
123		*ed = &l3errdata;
124		*cc = &cpu->cpu_l3data;
125	}
126}
127
128/*ARGSUSED*/
129int
130cmd_cpu_synd_check(uint16_t synd, cmd_errcl_t clcode)
131{
132	if (synd == CH_POISON_SYND_FROM_XXU_WRITE ||
133	    synd == CH_POISON_SYND_FROM_XXU_WRMERGE ||
134	    synd == CH_POISON_SYND_FROM_DSTAT23)
135		return (-1);
136	else
137		return (0);
138}
139/*ARGSUSED*/
140int
141cmd_afar_valid(fmd_hdl_t *hdl, nvlist_t *nvl, cmd_errcl_t clcode,
142    uint64_t *afar)
143{
144	uint8_t afar_status;
145
146	if (nvlist_lookup_uint8(nvl,
147	    FM_EREPORT_PAYLOAD_NAME_AFAR_STATUS, &afar_status) == 0) {
148		if (afar_status == AFLT_STAT_VALID) {
149			(void) nvlist_lookup_uint64(nvl,
150			    FM_EREPORT_PAYLOAD_NAME_AFAR, afar);
151			return (0);
152		} else
153			return (-1);
154	} else
155		return (-1);
156}
157
158char *
159cmd_cpu_getfrustr_by_id(fmd_hdl_t *hdl, uint32_t cpuid)
160{
161	kstat_named_t *kn;
162	kstat_ctl_t *kc;
163	kstat_t *ksp;
164	int i;
165
166	if ((kc = kstat_open()) == NULL)
167		return (NULL); /* errno is set for us */
168
169	if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL ||
170	    kstat_read(kc, ksp, NULL) == -1) {
171		int oserr = errno;
172		(void) kstat_close(kc);
173		(void) cmd_set_errno(oserr);
174		return (NULL);
175	}
176
177	for (kn = ksp->ks_data, i = 0; i < ksp->ks_ndata; i++, kn++) {
178		if (strcmp(kn->name, "cpu_fru") == 0) {
179			char *str = fmd_hdl_strdup(hdl,
180			    KSTAT_NAMED_STR_PTR(kn), FMD_SLEEP);
181			(void) kstat_close(kc);
182			return (str);
183		}
184	}
185
186	(void) kstat_close(kc);
187	(void) cmd_set_errno(ENOENT);
188	return (NULL);
189}
190
191char *
192cmd_cpu_getfrustr(fmd_hdl_t *hdl, cmd_cpu_t *cp)
193{
194	return (cmd_cpu_getfrustr_by_id(hdl, cp->cpu_cpuid));
195}
196
197/*ARGSUSED*/
198char *
199cmd_cpu_getpartstr(fmd_hdl_t *hdl, cmd_cpu_t *cp) {
200	return (NULL);
201}
202
203/*ARGSUSED*/
204char *
205cmd_cpu_getserialstr(fmd_hdl_t *hdl, cmd_cpu_t *cp) {
206	return (NULL);
207}
208
209/*ARGSUSED*/
210nvlist_t *
211cmd_cpu_mkfru(fmd_hdl_t *hdl, char *frustr, char *serialstr, char *partstr)
212{
213	char *comp;
214	nvlist_t *fru, *hcelem;
215
216	if (strncmp(frustr, CPU_FRU_FMRI, sizeof (CPU_FRU_FMRI) - 1) != 0)
217		return (NULL);
218
219	comp = frustr + sizeof (CPU_FRU_FMRI) - 1;
220
221	if (nvlist_alloc(&hcelem, NV_UNIQUE_NAME, 0) != 0)
222		return (NULL);
223
224	if (nvlist_add_string(hcelem, FM_FMRI_HC_NAME,
225	    FM_FMRI_LEGACY_HC) != 0 ||
226	    nvlist_add_string(hcelem, FM_FMRI_HC_ID, comp) != 0) {
227		nvlist_free(hcelem);
228		return (NULL);
229	}
230
231	if (nvlist_alloc(&fru, NV_UNIQUE_NAME, 0) != 0) {
232		nvlist_free(hcelem);
233		return (NULL);
234	}
235
236	if (nvlist_add_uint8(fru, FM_VERSION, FM_HC_SCHEME_VERSION) != 0 ||
237	    nvlist_add_string(fru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0 ||
238	    (partstr != NULL &&
239	    nvlist_add_string(fru, FM_FMRI_HC_PART, partstr) != 0) ||
240	    (serialstr != NULL &&
241	    nvlist_add_string(fru, FM_FMRI_HC_SERIAL_ID,
242	    serialstr) != 0) ||
243	    nvlist_add_string(fru, FM_FMRI_HC_ROOT, "") != 0 ||
244	    nvlist_add_uint32(fru, FM_FMRI_HC_LIST_SZ, 1) != 0 ||
245	    nvlist_add_nvlist_array(fru, FM_FMRI_HC_LIST, &hcelem, 1) != 0) {
246		nvlist_free(hcelem);
247		nvlist_free(fru);
248		return (NULL);
249	}
250
251	nvlist_free(hcelem);
252	return (fru);
253}
254
255nvlist_t *
256cmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t cert,
257    nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
258{
259	(void) nvlist_add_nvlist(fru, FM_FMRI_AUTHORITY,
260	    cmd.cmd_auth);
261	return (fmd_nvl_create_fault(hdl, class, cert, asru, fru, rsrc));
262}
263