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