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 */
47 static nvlist_t *fps_nvlist_create();
48
49 /* ereport piece generators */
50 static int fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id);
51 static int fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri);
52 static int fps_post_ereport(nvlist_t *ereport);
53 static uint64_t fps_ena_generate(uint64_t timestamp, uint32_t cpuid,
54 uchar_t format);
55
56 /* cpu check and name convert */
57 static char *fps_get_cpu_brand(uint32_t cpu_id);
58 static char *fps_convert_cpu_brand(char *brand);
59
60 /* ereport struct functions */
61 int fps_generate_ereport_struct(struct fps_test_ereport *report);
62 void setup_fps_test_struct(int mask, struct fps_test_ereport *rep, ...);
63 void initialize_fps_test_struct(struct fps_test_ereport *init_me);
64
65 /*
66 * fps_nvlist_create() allocates the memory for an
67 * nvlist.
68 */
69 static nvlist_t *
fps_nvlist_create()70 fps_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 */
96 static uint64_t
fps_ena_generate(uint64_t timestamp,uint32_t cpuid,uchar_t format)97 fps_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 */
132 static int
fps_fmri_svc_set(nvlist_t * fmri_svc,const char * svc_fmri)133 fps_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 */
159 static int
fps_fmri_cpu_set(nvlist_t * fmri_cpu,uint32_t cpu_id)160 fps_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 */
183 static int
fps_post_ereport(nvlist_t * ereport)184 fps_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 */
209 static char *
fps_convert_cpu_brand(char * brand)210 fps_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 */
234 static char *
fps_get_cpu_brand(uint32_t cpu_id)235 fps_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 */
276 int
fps_generate_ereport_struct(struct fps_test_ereport * report)277 fps_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 */
412 void
initialize_fps_test_struct(struct fps_test_ereport * init_me)413 initialize_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 */
432 void
setup_fps_test_struct(int mask,struct fps_test_ereport * rep,...)433 setup_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