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 <kstat.h>
28#include <libnvpair.h>
29#include <libsysevent.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <string.h>
33#include <sys/fm/protocol.h>
34#include <sys/fm/util.h>
35#include <sys/types.h>
36#include <sys/processor.h>
37#include <unistd.h>
38#include <fp.h>
39#include <fps_defines.h>
40#include <fps_ereport.h>
41#include <fpst-defines.h>
42
43#define	CLASS_HEAD "ereport.cpu"
44#define	CLASS_TAIL "fpu.fpscrub"
45
46/* nvlist */
47static nvlist_t *fps_nvlist_create();
48
49/* ereport piece generators */
50static int fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id);
51static int fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri);
52static int fps_post_ereport(nvlist_t *ereport);
53static uint64_t fps_ena_generate(uint64_t timestamp, uint32_t cpuid,
54		uchar_t format);
55
56/* cpu check and name convert */
57static char *fps_get_cpu_brand(uint32_t cpu_id);
58static char *fps_convert_cpu_brand(char *brand);
59
60/* ereport struct functions */
61int fps_generate_ereport_struct(struct fps_test_ereport *report);
62void setup_fps_test_struct(int mask, struct fps_test_ereport *rep, ...);
63void initialize_fps_test_struct(struct fps_test_ereport *init_me);
64
65/*
66 * fps_nvlist_create() allocates the memory for an
67 * nvlist.
68 */
69static nvlist_t *
70fps_nvlist_create()
71{
72	int nr_malloc;
73	nvlist_t *nvl;
74	struct timeval timeout;
75
76	timeout.tv_sec = 0;
77	timeout.tv_usec = 10000;
78	nr_malloc = 0;
79
80	nvl = NULL;
81	(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
82
83	while (nvl == NULL && nr_malloc < 10) {
84		(void) select(1, NULL, NULL, NULL, &timeout);
85		(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
86		nr_malloc++;
87	}
88
89	return (nvl);
90}
91
92/*
93 * fps_ena_generate(uint64_t timestamp, processorid_t cpuid,
94 * uchar_t format)creates the ENA for the ereport.
95 */
96static uint64_t
97fps_ena_generate(uint64_t timestamp, uint32_t cpuid, uchar_t format)
98{
99	uint64_t ena = 0;
100
101	switch (format) {
102	case FM_ENA_FMT1:
103		if (timestamp) {
104			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
105			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
106			    ENA_FMT1_CPUID_MASK) |
107			    ((timestamp << ENA_FMT1_TIME_SHFT) &
108			    ENA_FMT1_TIME_MASK));
109		} else {
110			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
111			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
112			    ENA_FMT1_CPUID_MASK) |
113			    ((gethrtime() << ENA_FMT1_TIME_SHFT) &
114			    ENA_FMT1_TIME_MASK));
115		}
116		break;
117	case FM_ENA_FMT2:
118		ena = (uint64_t)((format & ENA_FORMAT_MASK) |
119		    ((timestamp << ENA_FMT2_TIME_SHFT) & ENA_FMT2_TIME_MASK));
120		break;
121	default:
122		break;
123	}
124
125	return (ena);
126}
127
128/*
129 * fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri)
130 * adds the detector data to fmri_svc.
131 */
132static int
133fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri)
134{
135	if (fmri_svc == NULL)
136		return (1);
137
138	if (svc_fmri == NULL)
139		return (1);
140
141	if (nvlist_add_uint8(fmri_svc, FM_VERSION, FM_SVC_SCHEME_VERSION) != 0)
142		return (1);
143
144	if (nvlist_add_string(fmri_svc, FM_FMRI_SCHEME,
145	    FM_FMRI_SCHEME_SVC) != 0)
146		return (1);
147
148	if (nvlist_add_string(fmri_svc, FM_FMRI_SVC_NAME,
149	    svc_fmri) != 0)
150		return (1);
151
152	return (0);
153}
154
155/*
156 * fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id)
157 * adds the resource data to fmri_cpu.
158 */
159static int
160fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id)
161{
162	if (fmri_cpu == NULL)
163		return (1);
164
165	if (nvlist_add_uint8(fmri_cpu, FM_VERSION,
166	    FM_CPU_SCHEME_VERSION) != 0)
167		return (1);
168
169	if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME,
170	    FM_FMRI_SCHEME_CPU) != 0)
171		return (1);
172
173	if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0)
174		return (1);
175	return (0);
176}
177
178/*
179 * fps_post_ereport(nvlist_t *ereport) posts an
180 * ereport to the sysevent error channel.  The error
181 * channel is assumed to be established by fps-transport.so.
182 */
183static int
184fps_post_ereport(nvlist_t *ereport)
185{
186	evchan_t *scp;
187
188	if (sysevent_evc_bind(CHANNEL, &scp, BIND_FLAGS) != 0) {
189		return (1);
190	}
191
192	if (sysevent_evc_publish(scp, CLASS, SUBCLASS, VENDOR,
193	    PUBLISHER, ereport, EVCH_NOSLEEP) != 0) {
194		return (1);
195	}
196
197	(void) sleep(1);
198
199	(void) fflush(NULL);
200	(void) sysevent_evc_unbind(scp);
201
202	return (0);
203}
204/*
205 * fps_convert_cpu_brand(char *brand) changes
206 * the kstat data to match the ereport class
207 * names.
208 */
209static char *
210fps_convert_cpu_brand(char *brand)
211{
212	if (brand == NULL)
213		return (NULL);
214
215	if (strcasecmp(brand, USIII_KSTAT) == 0)
216		return (USIII);
217	else if (strcasecmp(brand, USIIIi_KSTAT) == 0)
218		return (USIIIi);
219	else if (strcasecmp(brand, USIIIP_KSTAT) == 0)
220		return (USIIIP);
221	else if (strcasecmp(brand, USIV_KSTAT) == 0)
222		return (USIV);
223	else if (strcasecmp(brand, USIVP_KSTAT) == 0)
224		return (USIVP);
225	else
226		return (NULL);
227}
228
229/*
230 * get_cpu_brand(uint32_t cpu_id)gets the
231 * brand of the CPU and returns the CPU
232 * name to use in the ereport class name.
233 */
234static char *
235fps_get_cpu_brand(uint32_t cpu_id)
236{
237	char *brand;
238	kstat_ctl_t *kc;
239	kstat_t *ksp;
240	kstat_named_t *knp;
241
242	kc = kstat_open();
243	if (kc == NULL) {
244		return (NULL);
245	}
246
247	if ((ksp = kstat_lookup(kc, "cpu_info", (int)cpu_id, NULL)) == NULL) {
248		(void) kstat_close(kc);
249		return (NULL);
250	}
251
252	if ((kstat_read(kc, ksp, NULL)) == -1) {
253		(void) kstat_close(kc);
254		return (NULL);
255	}
256
257	if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) {
258		(void) kstat_close(kc);
259		return (NULL);
260	}
261
262	brand = fps_convert_cpu_brand(KSTAT_NAMED_STR_PTR(knp));
263	(void) kstat_close(kc);
264
265	if (brand == NULL)
266		return (NULL);
267
268	return (brand);
269}
270
271/*
272 * fps_generate_ereport_struct(struct fps_test_ereport *report)
273 * takes report and constructs an nvlist that will be used
274 * for the ereport.
275 */
276int
277fps_generate_ereport_struct(struct fps_test_ereport *report)
278{
279	char class_name[FM_MAX_CLASS];
280	char *cpu_brand;
281	char *string_data;
282	int expect_size;
283	int is_valid_cpu;
284	int mask;
285	int observe_size;
286	int ret;
287	nvlist_t *detector;
288	nvlist_t *ereport;
289	nvlist_t *resource;
290	uint32_t cpu_id;
291	uint32_t test;
292	uint8_t fps_ver;
293	uint64_t ena;
294	uint64_t ereport_time;
295	uint64_t *expect;
296	uint64_t *observe;
297
298	if (report == NULL)
299		return (FPU_EREPORT_FAIL);
300
301	ret = FPU_FOROFFLINE;
302	cpu_id = report->cpu_id;
303	test = report->test_id;
304	mask = report->mask;
305	is_valid_cpu = report->is_valid_cpu;
306	expect_size = report->expected_size;
307	expect = report->expected;
308	observe_size = report->observed_size;
309	observe = report->observed;
310	string_data = report->info;
311
312	/* allocate nvlists */
313	if ((ereport = fps_nvlist_create()) == NULL)
314		_exit(FPU_EREPORT_FAIL);
315
316	if ((detector = fps_nvlist_create()) == NULL) {
317		_exit(FPU_EREPORT_FAIL);
318	}
319
320	/* setup class */
321	if ((cpu_brand = fps_get_cpu_brand(cpu_id)) == NULL)
322		_exit(FPU_EREPORT_FAIL);
323
324	if ((snprintf(class_name, FM_MAX_CLASS, "%s.%s.%s",
325	    CLASS_HEAD, cpu_brand, CLASS_TAIL)) < 0)
326		_exit(FPU_EREPORT_FAIL);
327
328	/* setup ena */
329	ereport_time = gethrtime();
330	ena = fps_ena_generate(ereport_time, cpu_id, FM_ENA_FMT1);
331
332	/* setup detector */
333	if (fps_fmri_svc_set(detector, getenv("SMF_FMRI")) != 0) {
334		_exit(FPU_EREPORT_FAIL);
335	}
336
337	/* setup fps-version */
338	fps_ver = FPS_VERSION;
339
340	/* setup resource */
341	if (is_valid_cpu) {
342		resource = fps_nvlist_create();
343
344		if (fps_fmri_cpu_set(resource, cpu_id)) {
345			_exit(FPU_EREPORT_FAIL);
346		}
347	} else {
348		resource = NULL;
349	}
350
351	/* put it together */
352	if (nvlist_add_string(ereport, NAME_FPS_CLASS, class_name) != 0)
353		_exit(FPU_EREPORT_FAIL);
354
355	if (ena != 0) {
356		if (nvlist_add_uint64(ereport, NAME_FPS_ENA, ena) != 0)
357			_exit(FPU_EREPORT_FAIL);
358	} else
359		_exit(FPU_EREPORT_FAIL);
360
361	if (nvlist_add_nvlist(ereport, NAME_FPS_DETECTOR,
362	    (nvlist_t *)detector) != 0)
363		_exit(FPU_EREPORT_FAIL);
364
365	if (nvlist_add_uint8(ereport, NAME_FPS_VERSION, fps_ver) != 0)
366		_exit(FPU_EREPORT_FAIL);
367
368	if (nvlist_add_uint32(ereport, NAME_FPS_TEST_ID, test) != 0)
369		ret = FPU_EREPORT_INCOM;
370
371	if (nvlist_add_uint64_array(ereport, NAME_FPS_EXPECTED_VALUE,
372	    expect, expect_size) != 0)
373		ret = FPU_EREPORT_INCOM;
374
375	if (nvlist_add_uint64_array(ereport, NAME_FPS_OBSERVED_VALUE,
376	    observe, observe_size) != 0)
377		ret = FPU_EREPORT_INCOM;
378
379	if (mask & IS_EREPORT_INFO) {
380		if (nvlist_add_string(ereport, NAME_FPS_STRING_DATA,
381		    string_data) != 0)
382			ret = FPU_EREPORT_INCOM;
383	}
384
385	if (is_valid_cpu) {
386		if (nvlist_add_nvlist(ereport, NAME_FPS_RESOURCE,
387		    (nvlist_t *)resource) != 0)
388			_exit(FPU_EREPORT_FAIL);
389	}
390
391	/* publish */
392	if (fps_post_ereport(ereport)) {
393		ret = FPU_EREPORT_FAIL;
394	}
395
396	/* free nvlists */
397	nvlist_free(ereport);
398
399	if (resource != NULL)
400		nvlist_free(resource);
401
402	if (detector != NULL)
403		nvlist_free(detector);
404
405	return (ret);
406}
407
408/*
409 * initialize_fps_test_struct(struct fps_test_ereport *init_me)
410 * creates the initial values for the init_me.
411 */
412void
413initialize_fps_test_struct(struct fps_test_ereport *init_me)
414{
415	if (init_me == NULL)
416		return;
417
418	init_me->cpu_id = 0;
419	init_me->test_id = 0;
420	init_me->observed_size = 0;
421	init_me->expected_size = 0;
422	init_me->is_valid_cpu = 1;
423	init_me->info[0] = '\0';
424	init_me->mask = NO_EREPORT_INFO;
425}
426
427/*
428 * setup_fps_test_struct(int mask, struct fps_test_ereport *rep,
429 * ...) takes a variable amount of input and stores it in rep
430 * based on mask provided.
431 */
432void
433setup_fps_test_struct(int mask, struct fps_test_ereport *rep, ...)
434{
435	char *data;
436	int i;
437	uint64_t *exp_arg;
438	uint64_t *obs_arg;
439	va_list argptr;
440
441	if (rep == NULL)
442		return;
443
444	/* begin parsing args */
445	va_start(argptr, rep);
446
447	/* test id */
448	rep->test_id = va_arg(argptr, int);
449
450	/* observed */
451	obs_arg = va_arg(argptr, uint64_t *);
452
453	/* expected */
454	exp_arg = va_arg(argptr, uint64_t *);
455
456	/* observed size */
457	rep->observed_size = va_arg(argptr, int);
458
459	/* expected size */
460	rep->expected_size = va_arg(argptr, int);
461
462	/* copy arrays of observed and expected */
463	if (rep->observed_size < 1 || rep->expected_size < 1)
464		return;
465
466	if (obs_arg == NULL || exp_arg == NULL)
467		return;
468
469	for (i = 0; i < rep->observed_size; i++)
470		rep->observed[i] = obs_arg[i];
471
472	for (i = 0; i < rep->expected_size; i++)
473		rep->expected[i] = exp_arg[i];
474
475	rep->mask = mask;
476
477	/* copy string data if there */
478	if (mask & IS_EREPORT_INFO)	{
479		data = va_arg(argptr, char *);
480
481		if (data == NULL) {
482			va_end(argptr);
483
484			return;
485		}
486
487		(void) strlcpy(rep->info, data, MAX_INFO_SIZE-1);
488	}
489
490	va_end(argptr);
491}
492