1 /* -*-mode: C; indent-tabs-mode: t; -*- 2 * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * Redistribution of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * Redistribution in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * Neither the name of Sun Microsystems, Inc. or the names of 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * This software is provided "AS IS," without a warranty of any kind. 20 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 21 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A 22 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. 23 * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE 24 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING 25 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL 26 * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, 27 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR 28 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF 29 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 30 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 31 */ 32 33 #include <string.h> 34 #include <math.h> 35 #define __USE_XOPEN /* glibc2 needs this for strptime */ 36 #include <time.h> 37 #include <ctype.h> 38 #include <errno.h> 39 40 #include <ipmitool/helper.h> 41 #include <ipmitool/log.h> 42 #include <ipmitool/ipmi.h> 43 #include <ipmitool/ipmi_mc.h> 44 #include <ipmitool/ipmi_intf.h> 45 #include <ipmitool/ipmi_sel.h> 46 #include <ipmitool/ipmi_sdr.h> 47 #include <ipmitool/ipmi_fru.h> 48 #include <ipmitool/ipmi_sensor.h> 49 50 extern int verbose; 51 static int sel_extended = 0; 52 static int sel_oem_nrecs = 0; 53 54 static IPMI_OEM sel_iana = IPMI_OEM_UNKNOWN; 55 56 struct ipmi_sel_oem_msg_rec { 57 int value[14]; 58 char *string[14]; 59 char *text; 60 } *sel_oem_msg; 61 62 #define SEL_BYTE(n) (n-3) /* So we can refer to byte positions in log entries (byte 3 is at index 0, etc) */ 63 64 // Definiation for the Decoding the SEL OEM Bytes for DELL Platfoms 65 #define BIT(x) (1 << x) /* Select the Bit */ 66 #define SIZE_OF_DESC 128 /* Max Size of the description String to be displyed for the Each sel entry */ 67 #define MAX_CARDNO_STR 32 /* Max Size of Card number string */ 68 #define MAX_DIMM_STR 32 /* Max Size of DIMM string */ 69 #define MAX_CARD_STR 32 /* Max Size of Card string */ 70 /* 71 * Reads values found in message translation file. XX is a wildcard, R means reserved. 72 * Returns -1 for XX, -2 for R, -3 for non-hex (string), or positive integer from a hex value. 73 */ 74 static int ipmi_sel_oem_readval(char *str) 75 { 76 int ret; 77 if (!strcmp(str, "XX")) { 78 return -1; 79 } 80 if (!strcmp(str, "R")) { 81 return -2; 82 } 83 if (sscanf(str, "0x%x", &ret) != 1) { 84 return -3; 85 } 86 return ret; 87 } 88 89 /* 90 * This is where the magic happens. SEL_BYTE is a bit ugly, but it allows 91 * reference to byte positions instead of array indexes which (hopefully) 92 * helps make the code easier to read. 93 */ 94 static int ipmi_sel_oem_match(uint8_t *evt, struct ipmi_sel_oem_msg_rec rec) 95 { 96 if (evt[2] == rec.value[SEL_BYTE(3)] && 97 ((rec.value[SEL_BYTE(4)] < 0) || (evt[3] == rec.value[SEL_BYTE(4)])) && 98 ((rec.value[SEL_BYTE(5)] < 0) || (evt[4] == rec.value[SEL_BYTE(5)])) && 99 ((rec.value[SEL_BYTE(6)] < 0) || (evt[5] == rec.value[SEL_BYTE(6)])) && 100 ((rec.value[SEL_BYTE(7)] < 0) || (evt[6] == rec.value[SEL_BYTE(7)])) && 101 ((rec.value[SEL_BYTE(11)] < 0) || (evt[10] == rec.value[SEL_BYTE(11)])) && 102 ((rec.value[SEL_BYTE(12)] < 0) || (evt[11] == rec.value[SEL_BYTE(12)]))) { 103 return 1; 104 } else { 105 return 0; 106 } 107 } 108 109 int ipmi_sel_oem_init(const char * filename) 110 { 111 FILE * fp; 112 int i, j, k, n, byte; 113 char buf[15][150]; 114 115 if (filename == NULL) { 116 lprintf(LOG_ERR, "No SEL OEM filename provided"); 117 return -1; 118 } 119 120 fp = ipmi_open_file_read(filename); 121 if (fp == NULL) { 122 lprintf(LOG_ERR, "Could not open %s file", filename); 123 return -1; 124 } 125 126 /* count number of records (lines) in input file */ 127 sel_oem_nrecs = 0; 128 while (fscanf(fp, "%*[^\n]\n") == 0) { 129 sel_oem_nrecs++; 130 } 131 132 printf("nrecs=%d\n", sel_oem_nrecs); 133 134 rewind(fp); 135 sel_oem_msg = (struct ipmi_sel_oem_msg_rec *)calloc(sel_oem_nrecs, 136 sizeof(struct ipmi_sel_oem_msg_rec)); 137 138 for (i=0; i < sel_oem_nrecs; i++) { 139 n=fscanf(fp, "\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"" 140 "%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"" 141 "%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"" 142 "%[^\"]\",\"%[^\"]\",\"%[^\"]\"\n", 143 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], 144 buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], 145 buf[12], buf[13], buf[14]); 146 147 if (n != 15) { 148 lprintf (LOG_ERR, "Encountered problems reading line %d of %s", 149 i+1, filename); 150 fclose(fp); 151 fp = NULL; 152 sel_oem_nrecs = 0; 153 /* free all the memory allocated so far */ 154 for (j=0; j<i ; j++) { 155 for (k=3; k<17; k++) { 156 if (sel_oem_msg[j].value[SEL_BYTE(k)] == -3) { 157 free(sel_oem_msg[j].string[SEL_BYTE(k)]); 158 sel_oem_msg[j].string[SEL_BYTE(k)] = NULL; 159 } 160 } 161 } 162 free(sel_oem_msg); 163 sel_oem_msg = NULL; 164 return -1; 165 } 166 167 for (byte = 3; byte < 17; byte++) { 168 if ((sel_oem_msg[i].value[SEL_BYTE(byte)] = 169 ipmi_sel_oem_readval(buf[SEL_BYTE(byte)])) == -3) { 170 sel_oem_msg[i].string[SEL_BYTE(byte)] = 171 (char *)malloc(strlen(buf[SEL_BYTE(byte)]) + 1); 172 strcpy(sel_oem_msg[i].string[SEL_BYTE(byte)], 173 buf[SEL_BYTE(byte)]); 174 } 175 } 176 sel_oem_msg[i].text = (char *)malloc(strlen(buf[SEL_BYTE(17)]) + 1); 177 strcpy(sel_oem_msg[i].text, buf[SEL_BYTE(17)]); 178 } 179 180 fclose(fp); 181 fp = NULL; 182 return 0; 183 } 184 185 static void ipmi_sel_oem_message(struct sel_event_record * evt, int verbose) 186 { 187 /* 188 * Note: although we have a verbose argument, currently the output 189 * isn't affected by it. 190 */ 191 int i, j; 192 193 for (i=0; i < sel_oem_nrecs; i++) { 194 if (ipmi_sel_oem_match((uint8_t *)evt, sel_oem_msg[i])) { 195 printf (csv_output ? ",\"%s\"" : " | %s", sel_oem_msg[i].text); 196 for (j=4; j<17; j++) { 197 if (sel_oem_msg[i].value[SEL_BYTE(j)] == -3) { 198 printf (csv_output ? ",%s=0x%x" : " %s = 0x%x", 199 sel_oem_msg[i].string[SEL_BYTE(j)], 200 ((uint8_t *)evt)[SEL_BYTE(j)]); 201 } 202 } 203 } 204 } 205 } 206 207 static const struct valstr event_dir_vals[] = { 208 { 0, "Assertion Event" }, 209 { 1, "Deassertion Event" }, 210 { 0, NULL }, 211 }; 212 213 static const char * 214 ipmi_get_event_type(uint8_t code) 215 { 216 if (code == 0) 217 return "Unspecified"; 218 if (code == 1) 219 return "Threshold"; 220 if (code >= 0x02 && code <= 0x0b) 221 return "Generic Discrete"; 222 if (code == 0x6f) 223 return "Sensor-specific Discrete"; 224 if (code >= 0x70 && code <= 0x7f) 225 return "OEM"; 226 return "Reserved"; 227 } 228 229 static char * 230 ipmi_sel_timestamp(uint32_t stamp) 231 { 232 static char tbuf[40]; 233 time_t s = (time_t)stamp; 234 memset(tbuf, 0, 40); 235 strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&s)); 236 return tbuf; 237 } 238 239 static char * 240 ipmi_sel_timestamp_date(uint32_t stamp) 241 { 242 static char tbuf[11]; 243 time_t s = (time_t)stamp; 244 strftime(tbuf, sizeof(tbuf), "%m/%d/%Y", gmtime(&s)); 245 return tbuf; 246 } 247 248 static char * 249 ipmi_sel_timestamp_time(uint32_t stamp) 250 { 251 static char tbuf[9]; 252 time_t s = (time_t)stamp; 253 strftime(tbuf, sizeof(tbuf), "%H:%M:%S", gmtime(&s)); 254 return tbuf; 255 } 256 257 static char * 258 hex2ascii (uint8_t * hexChars, uint8_t numBytes) 259 { 260 int count; 261 static char hexString[SEL_OEM_NOTS_DATA_LEN+1]; /*Max Size*/ 262 263 if(numBytes > SEL_OEM_NOTS_DATA_LEN) 264 numBytes = SEL_OEM_NOTS_DATA_LEN; 265 266 for(count=0;count < numBytes;count++) 267 { 268 if((hexChars[count]<0x40)||(hexChars[count]>0x7e)) 269 hexString[count]='.'; 270 else 271 hexString[count]=hexChars[count]; 272 } 273 hexString[numBytes]='\0'; 274 return hexString; 275 } 276 277 IPMI_OEM 278 ipmi_get_oem(struct ipmi_intf * intf) 279 { 280 /* Execute a Get Device ID command to determine the OEM */ 281 struct ipmi_rs * rsp; 282 struct ipmi_rq req; 283 struct ipm_devid_rsp *devid; 284 285 if (intf->fd == 0) { 286 if( sel_iana != IPMI_OEM_UNKNOWN ){ 287 return sel_iana; 288 } 289 return IPMI_OEM_UNKNOWN; 290 } 291 292 /* 293 * Return the cached manufacturer id if the device is open and 294 * we got an identified OEM owner. Otherwise just attempt to read 295 * it. 296 */ 297 if (intf->opened && intf->manufacturer_id != IPMI_OEM_UNKNOWN) { 298 return intf->manufacturer_id; 299 } 300 301 memset(&req, 0, sizeof(req)); 302 req.msg.netfn = IPMI_NETFN_APP; 303 req.msg.cmd = BMC_GET_DEVICE_ID; 304 req.msg.data_len = 0; 305 306 rsp = intf->sendrecv(intf, &req); 307 if (rsp == NULL) { 308 lprintf(LOG_ERR, "Get Device ID command failed"); 309 return IPMI_OEM_UNKNOWN; 310 } 311 if (rsp->ccode > 0) { 312 lprintf(LOG_ERR, "Get Device ID command failed: %#x %s", 313 rsp->ccode, val2str(rsp->ccode, completion_code_vals)); 314 return IPMI_OEM_UNKNOWN; 315 } 316 317 devid = (struct ipm_devid_rsp *) rsp->data; 318 319 lprintf(LOG_DEBUG,"Iana: %u", 320 IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id)); 321 322 return IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id); 323 } 324 325 static int 326 ipmi_sel_add_entry(struct ipmi_intf * intf, struct sel_event_record * rec) 327 { 328 struct ipmi_rs * rsp; 329 struct ipmi_rq req; 330 331 memset(&req, 0, sizeof(req)); 332 req.msg.netfn = IPMI_NETFN_STORAGE; 333 req.msg.cmd = IPMI_CMD_ADD_SEL_ENTRY; 334 req.msg.data = (unsigned char *)rec; 335 req.msg.data_len = 16; 336 337 ipmi_sel_print_std_entry(intf, rec); 338 339 rsp = intf->sendrecv(intf, &req); 340 if (rsp == NULL) { 341 lprintf(LOG_ERR, "Add SEL Entry failed"); 342 return -1; 343 } 344 else if (rsp->ccode > 0) { 345 lprintf(LOG_ERR, "Add SEL Entry failed: %s", 346 val2str(rsp->ccode, completion_code_vals)); 347 return -1; 348 } 349 350 return 0; 351 } 352 353 354 static int 355 ipmi_sel_add_entries_fromfile(struct ipmi_intf * intf, const char * filename) 356 { 357 FILE * fp; 358 char buf[1024]; 359 char * ptr, * tok; 360 int i, j; 361 int rc = 0; 362 uint8_t rqdata[8]; 363 struct sel_event_record sel_event; 364 365 if (filename == NULL) 366 return -1; 367 368 fp = ipmi_open_file_read(filename); 369 if (fp == NULL) 370 return -1; 371 372 while (feof(fp) == 0) { 373 if (fgets(buf, 1024, fp) == NULL) 374 continue; 375 376 /* clip off optional comment tail indicated by # */ 377 ptr = strchr(buf, '#'); 378 if (ptr) 379 *ptr = '\0'; 380 else 381 ptr = buf + strlen(buf); 382 383 /* clip off trailing and leading whitespace */ 384 ptr--; 385 while (isspace((int)*ptr) && ptr >= buf) 386 *ptr-- = '\0'; 387 ptr = buf; 388 while (isspace((int)*ptr)) 389 ptr++; 390 if (strlen(ptr) == 0) 391 continue; 392 393 /* parse the event, 7 bytes with optional comment */ 394 /* 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # event */ 395 i = 0; 396 tok = strtok(ptr, " "); 397 while (tok) { 398 if (i == 7) 399 break; 400 j = i++; 401 if (str2uchar(tok, &rqdata[j]) != 0) { 402 break; 403 } 404 tok = strtok(NULL, " "); 405 } 406 if (i < 7) { 407 lprintf(LOG_ERR, "Invalid Event: %s", 408 buf2str(rqdata, sizeof(rqdata))); 409 continue; 410 } 411 412 memset(&sel_event, 0, sizeof(struct sel_event_record)); 413 sel_event.record_id = 0x0000; 414 sel_event.record_type = 0x02; 415 sel_event.sel_type.standard_type.gen_id = 0x00; 416 sel_event.sel_type.standard_type.evm_rev = rqdata[0]; 417 sel_event.sel_type.standard_type.sensor_type = rqdata[1]; 418 sel_event.sel_type.standard_type.sensor_num = rqdata[2]; 419 sel_event.sel_type.standard_type.event_type = rqdata[3] & 0x7f; 420 sel_event.sel_type.standard_type.event_dir = (rqdata[3] & 0x80) >> 7; 421 sel_event.sel_type.standard_type.event_data[0] = rqdata[4]; 422 sel_event.sel_type.standard_type.event_data[1] = rqdata[5]; 423 sel_event.sel_type.standard_type.event_data[2] = rqdata[6]; 424 425 rc = ipmi_sel_add_entry(intf, &sel_event); 426 if (rc < 0) 427 break; 428 } 429 430 fclose(fp); 431 return rc; 432 } 433 434 static struct ipmi_event_sensor_types oem_kontron_event_reading_types[] __attribute__((unused)) = { 435 { 0x70 , 0x00 , 0xff, IPMI_EVENT_CLASS_DISCRETE , "OEM Firmware Info 1", "Code Assert" }, 436 { 0x71 , 0x00 , 0xff, IPMI_EVENT_CLASS_DISCRETE , "OEM Firmware Info 2", "Code Assert" }, 437 }; 438 439 char * 440 get_kontron_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec) 441 { 442 char * description = NULL; 443 /* 444 * Kontron OEM events are described in the product's user manual, but are limited in favor of 445 * sensor specific 446 */ 447 448 /* Only standard records are defined so far */ 449 if( rec->record_type < 0xC0 ){ 450 struct ipmi_event_sensor_types *st=NULL; 451 for ( st=oem_kontron_event_reading_types ; st->type != NULL; st++){ 452 if (st->code == rec->sel_type.standard_type.event_type ){ 453 size_t len =strlen(st->desc); 454 description = (char*)malloc( len + 1 ); 455 memcpy(description, st->desc , len); 456 description[len] = 0;; 457 return description; 458 } 459 } 460 } 461 462 return NULL; 463 } 464 465 char * 466 get_newisys_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec) 467 { 468 /* 469 * Newisys OEM event descriptions can be retrieved through an 470 * OEM IPMI command. 471 */ 472 struct ipmi_rs * rsp; 473 struct ipmi_rq req; 474 uint8_t msg_data[6]; 475 char * description = NULL; 476 477 memset(&req, 0, sizeof(req)); 478 req.msg.netfn = 0x2E; 479 req.msg.cmd = 0x01; 480 req.msg.data_len = sizeof(msg_data); 481 482 msg_data[0] = 0x15; /* IANA LSB */ 483 msg_data[1] = 0x24; /* IANA */ 484 msg_data[2] = 0x00; /* IANA MSB */ 485 msg_data[3] = 0x01; /* Subcommand */ 486 msg_data[4] = rec->record_id & 0x00FF; /* SEL Record ID LSB */ 487 msg_data[5] = (rec->record_id & 0xFF00) >> 8; /* SEL Record ID MSB */ 488 489 req.msg.data = msg_data; 490 491 rsp = intf->sendrecv(intf, &req); 492 if (rsp == NULL) { 493 if (verbose) 494 lprintf(LOG_ERR, "Error issuing OEM command"); 495 return NULL; 496 } 497 if (rsp->ccode > 0) { 498 if (verbose) 499 lprintf(LOG_ERR, "OEM command returned error code: %s", 500 val2str(rsp->ccode, completion_code_vals)); 501 return NULL; 502 } 503 504 /* Verify our response before we use it */ 505 if (rsp->data_len < 5) 506 { 507 lprintf(LOG_ERR, "Newisys OEM response too short"); 508 return NULL; 509 } 510 else if (rsp->data_len != (4 + rsp->data[3])) 511 { 512 lprintf(LOG_ERR, "Newisys OEM response has unexpected length"); 513 return NULL; 514 } 515 else if (IPM_DEV_MANUFACTURER_ID(rsp->data) != IPMI_OEM_NEWISYS) 516 { 517 lprintf(LOG_ERR, "Newisys OEM response has unexpected length"); 518 return NULL; 519 } 520 521 description = (char*)malloc(rsp->data[3] + 1); 522 memcpy(description, rsp->data + 4, rsp->data[3]); 523 description[rsp->data[3]] = 0;; 524 525 return description; 526 } 527 528 char * 529 get_supermicro_evt_desc(struct ipmi_intf *intf, struct sel_event_record *rec) 530 { 531 struct ipmi_rs *rsp; 532 struct ipmi_rq req; 533 char *desc = NULL; 534 char *str; 535 int chipset_type = 1; 536 int data1; 537 int data2; 538 int data3; 539 int length; 540 int sensor_type; 541 uint8_t i = 0; 542 uint16_t oem_id = 0; 543 /* Get the OEM event Bytes of the SEL Records byte 13, 14, 15 to 544 * data1,data2,data3 545 */ 546 data1 = rec->sel_type.standard_type.event_data[0]; 547 data2 = rec->sel_type.standard_type.event_data[1]; 548 data3 = rec->sel_type.standard_type.event_data[2]; 549 /* Check for the Standard Event type == 0x6F */ 550 if (rec->sel_type.standard_type.event_type != 0x6F) { 551 return NULL; 552 } 553 /* Allocate mem for te Description string */ 554 desc = (char *)malloc(SIZE_OF_DESC); 555 if (desc == NULL) { 556 lprintf(LOG_ERR, "ipmitool: malloc failure"); 557 return NULL; 558 } 559 memset(desc,0,SIZE_OF_DESC); 560 sensor_type = rec->sel_type.standard_type.sensor_type; 561 switch (sensor_type) { 562 case SENSOR_TYPE_MEMORY: 563 memset(&req, 0, sizeof (req)); 564 req.msg.netfn = IPMI_NETFN_APP; 565 req.msg.lun = 0; 566 req.msg.cmd = BMC_GET_DEVICE_ID; 567 req.msg.data = NULL; 568 req.msg.data_len = 0; 569 570 rsp = intf->sendrecv(intf, &req); 571 if (rsp == NULL) { 572 lprintf(LOG_ERR, " Error getting system info"); 573 if (desc != NULL) { 574 free(desc); 575 desc = NULL; 576 } 577 return NULL; 578 } else if (rsp->ccode > 0) { 579 lprintf(LOG_ERR, " Error getting system info: %s", 580 val2str(rsp->ccode, completion_code_vals)); 581 if (desc != NULL) { 582 free(desc); 583 desc = NULL; 584 } 585 return NULL; 586 } 587 /* check the chipset type */ 588 oem_id = ipmi_get_oem_id(intf); 589 if (oem_id == 0) { 590 return NULL; 591 } 592 length = sizeof(supermicro_X8); 593 for (i = 0; i < length; i++) { 594 if (oem_id == supermicro_X8[i]) { 595 chipset_type = 0; 596 break; 597 } 598 } 599 length = sizeof(supermicro_x9); 600 for (i = 0; i < length; i++) { 601 if (oem_id == supermicro_x9[i]) { 602 chipset_type = 2; 603 break; 604 } 605 } 606 if (chipset_type == 0) { 607 snprintf(desc, SIZE_OF_DESC, "@DIMM%2X(CPU%x)", 608 data2, 609 (data3 & 0x03) + 1); 610 } else if (chipset_type == 1) { 611 snprintf(desc, SIZE_OF_DESC, "@DIMM%c%c(CPU%x)", 612 (data2 >> 4) + 0x40 + (data3 & 0x3) * 4, 613 (data2 & 0xf) + 0x27, (data3 & 0x03) + 1); 614 } else if (chipset_type == 2) { 615 snprintf(desc, SIZE_OF_DESC, "@DIMM%c%c(CPU%x)", 616 (data2 >> 4) + 0x40 + (data3 & 0x3) * 3, 617 (data2 & 0xf) + 0x27, (data3 & 0x03) + 1); 618 } else { 619 snprintf(desc, SIZE_OF_DESC, ""); 620 } 621 break; 622 case SENSOR_TYPE_SUPERMICRO_OEM: 623 if (data1 == 0x80 && data3 == 0xFF) { 624 if (data2 == 0x0) { 625 snprintf(desc, SIZE_OF_DESC, "BMC unexpected reset"); 626 } else if (data2 == 0x1) { 627 snprintf(desc, SIZE_OF_DESC, "BMC cold reset"); 628 } else if (data2 == 0x2) { 629 snprintf(desc, SIZE_OF_DESC, "BMC warm reset"); 630 } 631 } 632 break; 633 } 634 return desc; 635 } 636 637 /* 638 * Function : Decoding the SEL OEM Bytes for the DELL Platforms. 639 * Description : The below fucntion will decode the SEL Events OEM Bytes for the Dell specific Sensors only. 640 * The below function will append the additional information Strings/description to the normal sel desc. 641 * With this the SEL will display additional information sent via OEM Bytes of the SEL Record. 642 * NOTE : Specific to DELL Platforms only. 643 * Returns : Pointer to the char string. 644 */ 645 char * get_dell_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec) 646 { 647 int data1, data2, data3; 648 int sensor_type; 649 char *desc = NULL; 650 651 unsigned char count; 652 unsigned char node; 653 unsigned char num; 654 unsigned char dimmNum; 655 unsigned char dimmsPerNode; 656 char dimmStr[MAX_DIMM_STR]; 657 char cardStr[MAX_CARD_STR]; 658 char numStr[MAX_CARDNO_STR]; 659 char tmpdesc[SIZE_OF_DESC]; 660 char* str; 661 unsigned char incr = 0; 662 unsigned char i=0,j = 0; 663 unsigned char postCode; 664 struct ipmi_rs *rsp; 665 struct ipmi_rq req; 666 char tmpData; 667 int version; 668 /* Get the OEM event Bytes of the SEL Records byte 13, 14, 15 to Data1,data2,data3 */ 669 data1 = rec->sel_type.standard_type.event_data[0]; 670 data2 = rec->sel_type.standard_type.event_data[1]; 671 data3 = rec->sel_type.standard_type.event_data[2]; 672 /* Check for the Standard Event type == 0x6F */ 673 if (0x6F == rec->sel_type.standard_type.event_type) 674 { 675 sensor_type = rec->sel_type.standard_type.sensor_type; 676 /* Allocate mem for te Description string */ 677 desc = (char*)malloc(SIZE_OF_DESC); 678 if(NULL == desc) 679 return NULL; 680 memset(desc,0,SIZE_OF_DESC); 681 memset(tmpdesc,0,SIZE_OF_DESC); 682 switch (sensor_type) { 683 case SENSOR_TYPE_PROCESSOR: /* Processor/CPU related OEM Sel Byte Decoding for DELL Platforms only */ 684 if((OEM_CODE_IN_BYTE2 == (data1 & DATA_BYTE2_SPECIFIED_MASK))) 685 { 686 if(0x00 == (data1 & MASK_LOWER_NIBBLE)) 687 snprintf(desc,SIZE_OF_DESC,"CPU Internal Err | "); 688 if(0x06 == (data1 & MASK_LOWER_NIBBLE)) 689 { 690 snprintf(desc,SIZE_OF_DESC,"CPU Protocol Err | "); 691 692 } 693 694 /* change bit location to a number */ 695 for (count= 0; count < 8; count++) 696 { 697 if (BIT(count)& data2) 698 { 699 count++; 700 /* 0x0A - CPU sensor number */ 701 if((0x06 == (data1 & MASK_LOWER_NIBBLE)) && (0x0A == rec->sel_type.standard_type.sensor_num)) 702 snprintf(desc,SIZE_OF_DESC,"FSB %d ",count); // Which CPU Has generated the FSB 703 else 704 snprintf(desc,SIZE_OF_DESC,"CPU %d | APIC ID %d ",count,data3); /* Specific CPU related info */ 705 break; 706 } 707 } 708 } 709 break; 710 case SENSOR_TYPE_MEMORY: /* Memory or DIMM related OEM Sel Byte Decoding for DELL Platforms only */ 711 case SENSOR_TYPE_EVT_LOG: /* Events Logging for Memory or DIMM related OEM Sel Byte Decoding for DELL Platforms only */ 712 713 /* Get the current version of the IPMI Spec Based on that Decoding of memory info is done.*/ 714 memset(&req, 0, sizeof (req)); 715 req.msg.netfn = IPMI_NETFN_APP; 716 req.msg.lun = 0; 717 req.msg.cmd = BMC_GET_DEVICE_ID; 718 req.msg.data = NULL; 719 req.msg.data_len = 0; 720 721 rsp = intf->sendrecv(intf, &req); 722 if (NULL == rsp) 723 { 724 lprintf(LOG_ERR, " Error getting system info"); 725 if (desc != NULL) { 726 free(desc); 727 desc = NULL; 728 } 729 return NULL; 730 } 731 else if (rsp->ccode > 0) 732 { 733 lprintf(LOG_ERR, " Error getting system info: %s", 734 val2str(rsp->ccode, completion_code_vals)); 735 if (desc != NULL) { 736 free(desc); 737 desc = NULL; 738 } 739 return NULL; 740 } 741 version = rsp->data[4]; 742 /* Memory DIMMS */ 743 if( (data1 & OEM_CODE_IN_BYTE2) || (data1 & OEM_CODE_IN_BYTE3 ) ) 744 { 745 /* Memory Redundancy related oem bytes docoding .. */ 746 if( (SENSOR_TYPE_MEMORY == sensor_type) && (0x0B == rec->sel_type.standard_type.event_type) ) 747 { 748 if(0x00 == (data1 & MASK_LOWER_NIBBLE)) 749 { 750 snprintf(desc,SIZE_OF_DESC," Redundancy Regained | "); 751 } 752 else if(0x01 == (data1 & MASK_LOWER_NIBBLE)) 753 { 754 snprintf(desc,SIZE_OF_DESC,"Redundancy Lost | "); 755 } 756 } /* Correctable and uncorrectable ECC Error Decoding */ 757 else if(SENSOR_TYPE_MEMORY == sensor_type) 758 { 759 if(0x00 == (data1 & MASK_LOWER_NIBBLE)) 760 { 761 /* 0x1C - Memory Sensor Number */ 762 if(0x1C == rec->sel_type.standard_type.sensor_num) 763 { 764 /*Add the complete information about the Memory Configs.*/ 765 if((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3 )) 766 { 767 count = 0; 768 snprintf(desc,SIZE_OF_DESC,"CRC Error on:"); 769 for(i=0;i<4;i++) 770 { 771 if((BIT(i))&(data2)) 772 { 773 if(count) 774 { 775 str = desc+strlen(desc); 776 *str++ = ','; 777 str = '\0'; 778 count = 0; 779 } 780 switch(i) /* Which type of memory config is present.. */ 781 { 782 case 0: snprintf(tmpdesc,SIZE_OF_DESC,"South Bound Memory"); 783 strcat(desc,tmpdesc); 784 count++; 785 break; 786 case 1: snprintf(tmpdesc,SIZE_OF_DESC,"South Bound Config"); 787 strcat(desc,tmpdesc); 788 count++; 789 break; 790 case 2: snprintf(tmpdesc,SIZE_OF_DESC,"North Bound memory"); 791 strcat(desc,tmpdesc); 792 count++; 793 break; 794 case 3: snprintf(tmpdesc,SIZE_OF_DESC,"North Bound memory-corr"); 795 strcat(desc,tmpdesc); 796 count++; 797 break; 798 default: 799 break; 800 } 801 } 802 } 803 if(data3>=0x00 && data3<0xFF) 804 { 805 snprintf(tmpdesc,SIZE_OF_DESC,"|Failing_Channel:%d",data3); 806 strcat(desc,tmpdesc); 807 } 808 } 809 break; 810 } 811 snprintf(desc,SIZE_OF_DESC,"Correctable ECC | "); 812 } 813 else if(0x01 == (data1 & MASK_LOWER_NIBBLE)) 814 { 815 snprintf(desc,SIZE_OF_DESC,"UnCorrectable ECC | "); 816 } 817 } /* Corr Memory log disabled */ 818 else if(SENSOR_TYPE_EVT_LOG == sensor_type) 819 { 820 if(0x00 == (data1 & MASK_LOWER_NIBBLE)) 821 snprintf(desc,SIZE_OF_DESC,"Corr Memory Log Disabled | "); 822 } 823 } 824 else 825 { 826 if(SENSOR_TYPE_SYS_EVENT == sensor_type) 827 { 828 if(0x02 == (data1 & MASK_LOWER_NIBBLE)) 829 snprintf(desc,SIZE_OF_DESC,"Unknown System Hardware Failure "); 830 } 831 if(SENSOR_TYPE_EVT_LOG == sensor_type) 832 { 833 if(0x03 == (data1 & MASK_LOWER_NIBBLE)) 834 snprintf(desc,SIZE_OF_DESC,"All Even Logging Dissabled"); 835 } 836 } 837 /* 838 * Based on the above error, we need to find whcih memory slot or 839 * Card has got the Errors/Sel Generated. 840 */ 841 if(data1 & OEM_CODE_IN_BYTE2 ) 842 { 843 /* Find the Card Type */ 844 if((0x0F != (data2 >> 4)) && ((data2 >> 4) < 0x08)) 845 { 846 tmpData = ('A'+ (data2 >> 4)); 847 if( (SENSOR_TYPE_MEMORY == sensor_type) && (0x0B == rec->sel_type.standard_type.event_type) ) 848 { 849 snprintf(tmpdesc, SIZE_OF_DESC, "Bad Card %c", tmpData); 850 } 851 else 852 { 853 snprintf(tmpdesc, SIZE_OF_DESC, "Card %c", tmpData); 854 } 855 strcat(desc, tmpdesc); 856 } /* Find the Bank Number of the DIMM */ 857 if (0x0F != (data2 & MASK_LOWER_NIBBLE)) 858 { 859 if(0x51 == version) 860 { 861 snprintf(tmpdesc, SIZE_OF_DESC, "Bank %d", ((data2 & 0x0F)+1)); 862 strcat(desc, tmpdesc); 863 } 864 else 865 { 866 incr = (data2 & 0x0f) << 3; 867 } 868 } 869 870 } 871 /* Find the DIMM Number of the Memory which has Generated the Fault or Sel */ 872 if(data1 & OEM_CODE_IN_BYTE3 ) 873 { 874 // Based on the IPMI Spec Need Identify the DIMM Details. 875 // For the SPEC 1.5 Only the DIMM Number is Valid. 876 if(0x51 == version) 877 { 878 snprintf(tmpdesc, SIZE_OF_DESC, "DIMM %c", ('A'+ data3)); 879 strcat(desc, tmpdesc); 880 } 881 /* For the SPEC 2.0 Decode the DIMM Number as it supports more.*/ 882 else if( ((data2 >> 4) > 0x07) && (0x0F != (data2 >> 4) )) 883 { 884 strcpy(dimmStr, " DIMM"); 885 str = desc+strlen(desc); 886 dimmsPerNode = 4; 887 if(0x09 == (data2 >> 4)) dimmsPerNode = 6; 888 else if(0x0A == (data2 >> 4)) dimmsPerNode = 8; 889 else if(0x0B == (data2 >> 4)) dimmsPerNode = 9; 890 else if(0x0C == (data2 >> 4)) dimmsPerNode = 12; 891 else if(0x0D == (data2 >> 4)) dimmsPerNode = 24; 892 else if(0x0E == (data2 >> 4)) dimmsPerNode = 3; 893 count = 0; 894 for (i = 0; i < 8; i++) 895 { 896 if (BIT(i) & data3) 897 { 898 if(count) 899 { 900 strcat(str,","); 901 count = 0x00; 902 } 903 node = (incr + i)/dimmsPerNode; 904 dimmNum = ((incr + i)%dimmsPerNode)+1; 905 dimmStr[5] = node + 'A'; 906 sprintf(tmpdesc,"%d",dimmNum); 907 for(j = 0; j < strlen(tmpdesc);j++) 908 dimmStr[6+j] = tmpdesc[j]; 909 dimmStr[6+j] = '\0'; 910 strcat(str,dimmStr); // final DIMM Details. 911 count++; 912 } 913 } 914 } 915 else 916 { 917 strcpy(dimmStr, " DIMM"); 918 str = desc+strlen(desc); 919 count = 0; 920 for (i = 0; i < 8; i++) 921 { 922 if (BIT(i) & data3) 923 { 924 // check if more than one DIMM, if so add a comma to the string. 925 sprintf(tmpdesc,"%d",(i + incr + 1)); 926 if(count) 927 { 928 strcat(str,","); 929 count = 0x00; 930 } 931 for(j = 0; j < strlen(tmpdesc);j++) 932 dimmStr[5+j] = tmpdesc[j]; 933 dimmStr[5+j] = '\0'; 934 strcat(str, dimmStr); 935 count++; 936 } 937 } 938 } 939 } 940 break; 941 /* Sensor In system charectorization Error Decoding. 942 Sensor type 0x20*/ 943 case SENSOR_TYPE_TXT_CMD_ERROR: 944 if((0x00 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3))) 945 { 946 switch(data3) 947 { 948 case 0x01: 949 snprintf(desc,SIZE_OF_DESC,"BIOS TXT Error"); 950 break; 951 case 0x02: 952 snprintf(desc,SIZE_OF_DESC,"Processor/FIT TXT"); 953 break; 954 case 0x03: 955 snprintf(desc,SIZE_OF_DESC,"BIOS ACM TXT Error"); 956 break; 957 case 0x04: 958 snprintf(desc,SIZE_OF_DESC,"SINIT ACM TXT Error"); 959 break; 960 case 0xff: 961 snprintf(desc,SIZE_OF_DESC,"Unrecognized TT Error12"); 962 break; 963 default: 964 break; 965 } 966 } 967 break; 968 /* OS Watch Dog Timer Sel Events */ 969 case SENSOR_TYPE_WTDOG: 970 971 if(SENSOR_TYPE_OEM_SEC_EVENT == data1) 972 { 973 if(0x04 == data2) 974 { 975 snprintf(desc,SIZE_OF_DESC,"Hard Reset|Interrupt type None,SMS/OS Timer used at expiration"); 976 } 977 } 978 979 break; 980 /* This Event is for BMC to Othe Hardware or CPU . */ 981 case SENSOR_TYPE_VER_CHANGE: 982 if((0x02 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3))) 983 { 984 if(0x02 == data2) 985 { 986 if(0x00 == data3) 987 { 988 snprintf(desc, SIZE_OF_DESC, "between BMC/iDRAC Firmware and other hardware"); 989 } 990 else if(0x01 == data3) 991 { 992 snprintf(desc, SIZE_OF_DESC, "between BMC/iDRAC Firmware and CPU"); 993 } 994 } 995 } 996 break; 997 /* Flex or Mac tuning OEM Decoding for the DELL. */ 998 case SENSOR_TYPE_OEM_SEC_EVENT: 999 /* 0x25 - Virtual MAC sensory number - Dell OEM */ 1000 if(0x25 == rec->sel_type.standard_type.sensor_num) 1001 { 1002 if(0x01 == (data1 & MASK_LOWER_NIBBLE)) 1003 { 1004 snprintf(desc, SIZE_OF_DESC, "Failed to program Virtual Mac Address"); 1005 if((data1 & OEM_CODE_IN_BYTE2)&&(data1 & OEM_CODE_IN_BYTE3)) 1006 { 1007 snprintf(tmpdesc, SIZE_OF_DESC, " at bus:%.2x device:%.2x function:%x", 1008 data3 &0x7F, (data2 >> 3) & 0x1F, 1009 data2 & 0x07); 1010 strcat(desc,tmpdesc); 1011 } 1012 } 1013 else if(0x02 == (data1 & MASK_LOWER_NIBBLE)) 1014 { 1015 snprintf(desc, SIZE_OF_DESC, "Device option ROM failed to support link tuning or flex address"); 1016 } 1017 else if(0x03 == (data1 & MASK_LOWER_NIBBLE)) 1018 { 1019 snprintf(desc, SIZE_OF_DESC, "Failed to get link tuning or flex address data from BMC/iDRAC"); 1020 } 1021 } 1022 break; 1023 case SENSOR_TYPE_CRIT_INTR: 1024 case SENSOR_TYPE_OEM_NFATAL_ERROR: /* Non - Fatal PCIe Express Error Decoding */ 1025 case SENSOR_TYPE_OEM_FATAL_ERROR: /* Fatal IO Error Decoding */ 1026 /* 0x29 - QPI Linx Error Sensor Dell OEM */ 1027 if(0x29 == rec->sel_type.standard_type.sensor_num) 1028 { 1029 if((0x02 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3))) 1030 { 1031 snprintf(tmpdesc, SIZE_OF_DESC, "Partner-(LinkId:%d,AgentId:%d)|",(data2 & 0xC0),(data2 & 0x30)); 1032 strcat(desc,tmpdesc); 1033 snprintf(tmpdesc, SIZE_OF_DESC, "ReportingAgent(LinkId:%d,AgentId:%d)|",(data2 & 0x0C),(data2 & 0x03)); 1034 strcat(desc,tmpdesc); 1035 if(0x00 == (data3 & 0xFC)) 1036 { 1037 snprintf(tmpdesc, SIZE_OF_DESC, "LinkWidthDegraded|"); 1038 strcat(desc,tmpdesc); 1039 } 1040 if(BIT(1)& data3) 1041 { 1042 snprintf(tmpdesc,SIZE_OF_DESC,"PA_Type:IOH|"); 1043 } 1044 else 1045 { 1046 snprintf(tmpdesc,SIZE_OF_DESC,"PA-Type:CPU|"); 1047 } 1048 strcat(desc,tmpdesc); 1049 if(BIT(0)& data3) 1050 { 1051 snprintf(tmpdesc,SIZE_OF_DESC,"RA-Type:IOH"); 1052 } 1053 else 1054 { 1055 snprintf(tmpdesc,SIZE_OF_DESC,"RA-Type:CPU"); 1056 } 1057 strcat(desc,tmpdesc); 1058 } 1059 } 1060 else 1061 { 1062 1063 if(0x02 == (data1 & MASK_LOWER_NIBBLE)) 1064 { 1065 sprintf(desc,"%s","IO channel Check NMI"); 1066 } 1067 else 1068 { 1069 if(0x00 == (data1 & MASK_LOWER_NIBBLE)) 1070 { 1071 snprintf(desc, SIZE_OF_DESC, "%s","PCIe Error |"); 1072 } 1073 else if(0x01 == (data1 & MASK_LOWER_NIBBLE)) 1074 { 1075 snprintf(desc, SIZE_OF_DESC, "%s","I/O Error |"); 1076 } 1077 else if(0x04 == (data1 & MASK_LOWER_NIBBLE)) 1078 { 1079 snprintf(desc, SIZE_OF_DESC, "%s","PCI PERR |"); 1080 } 1081 else if(0x05 == (data1 & MASK_LOWER_NIBBLE)) 1082 { 1083 snprintf(desc, SIZE_OF_DESC, "%s","PCI SERR |"); 1084 } 1085 else 1086 { 1087 snprintf(desc, SIZE_OF_DESC, "%s"," "); 1088 } 1089 if (data3 & 0x80) 1090 snprintf(tmpdesc, SIZE_OF_DESC, "Slot %d", data3 & 0x7F); 1091 else 1092 snprintf(tmpdesc, SIZE_OF_DESC, "PCI bus:%.2x device:%.2x function:%x", 1093 data3 &0x7F, (data2 >> 3) & 0x1F, 1094 data2 & 0x07); 1095 1096 strcat(desc,tmpdesc); 1097 } 1098 } 1099 break; 1100 /* POST Fatal Errors generated from the Server with much more info*/ 1101 case SENSOR_TYPE_FRM_PROG: 1102 if((0x0F == (data1 & MASK_LOWER_NIBBLE))&&(data1 & OEM_CODE_IN_BYTE2)) 1103 { 1104 switch(data2) 1105 { 1106 case 0x80: 1107 snprintf(desc, SIZE_OF_DESC, "No memory is detected.");break; 1108 case 0x81: 1109 snprintf(desc,SIZE_OF_DESC, "Memory is detected but is not configurable.");break; 1110 case 0x82: 1111 snprintf(desc, SIZE_OF_DESC, "Memory is configured but not usable.");break; 1112 case 0x83: 1113 snprintf(desc, SIZE_OF_DESC, "System BIOS shadow failed.");break; 1114 case 0x84: 1115 snprintf(desc, SIZE_OF_DESC, "CMOS failed.");break; 1116 case 0x85: 1117 snprintf(desc, SIZE_OF_DESC, "DMA controller failed.");break; 1118 case 0x86: 1119 snprintf(desc, SIZE_OF_DESC, "Interrupt controller failed.");break; 1120 case 0x87: 1121 snprintf(desc, SIZE_OF_DESC, "Timer refresh failed.");break; 1122 case 0x88: 1123 snprintf(desc, SIZE_OF_DESC, "Programmable interval timer error.");break; 1124 case 0x89: 1125 snprintf(desc, SIZE_OF_DESC, "Parity error.");break; 1126 case 0x8A: 1127 snprintf(desc, SIZE_OF_DESC, "SIO failed.");break; 1128 case 0x8B: 1129 snprintf(desc, SIZE_OF_DESC, "Keyboard controller failed.");break; 1130 case 0x8C: 1131 snprintf(desc, SIZE_OF_DESC, "System management interrupt initialization failed.");break; 1132 case 0x8D: 1133 snprintf(desc, SIZE_OF_DESC, "TXT-SX Error.");break; 1134 case 0xC0: 1135 snprintf(desc, SIZE_OF_DESC, "Shutdown test failed.");break; 1136 case 0xC1: 1137 snprintf(desc, SIZE_OF_DESC, "BIOS POST memory test failed.");break; 1138 case 0xC2: 1139 snprintf(desc, SIZE_OF_DESC, "RAC configuration failed.");break; 1140 case 0xC3: 1141 snprintf(desc, SIZE_OF_DESC, "CPU configuration failed.");break; 1142 case 0xC4: 1143 snprintf(desc, SIZE_OF_DESC, "Incorrect memory configuration.");break; 1144 case 0xFE: 1145 snprintf(desc, SIZE_OF_DESC, "General failure after video."); 1146 break; 1147 } 1148 } 1149 break; 1150 1151 default: 1152 break; 1153 } 1154 } 1155 else 1156 { 1157 sensor_type = rec->sel_type.standard_type.event_type; 1158 } 1159 return desc; 1160 } 1161 1162 char * 1163 ipmi_get_oem_desc(struct ipmi_intf * intf, struct sel_event_record * rec) 1164 { 1165 char * desc = NULL; 1166 1167 switch (ipmi_get_oem(intf)) 1168 { 1169 case IPMI_OEM_NEWISYS: 1170 desc = get_newisys_evt_desc(intf, rec); 1171 break; 1172 case IPMI_OEM_KONTRON: 1173 desc = get_kontron_evt_desc(intf, rec); 1174 break; 1175 case IPMI_OEM_DELL: // Dell Decoding of the OEM Bytes from SEL Record. 1176 desc = get_dell_evt_desc(intf, rec); 1177 break; 1178 case IPMI_OEM_SUPERMICRO: 1179 case IPMI_OEM_SUPERMICRO_47488: 1180 desc = get_supermicro_evt_desc(intf, rec); 1181 break; 1182 case IPMI_OEM_UNKNOWN: 1183 default: 1184 break; 1185 } 1186 1187 return desc; 1188 } 1189 1190 1191 void 1192 ipmi_get_event_desc(struct ipmi_intf * intf, struct sel_event_record * rec, char ** desc) 1193 { 1194 uint8_t code, offset; 1195 struct ipmi_event_sensor_types *evt = NULL; 1196 char *sfx = NULL; /* This will be assigned if the Platform is DELL, 1197 additional info is appended to the current Description */ 1198 1199 if (desc == NULL) 1200 return; 1201 *desc = NULL; 1202 1203 if ((rec->sel_type.standard_type.event_type >= 0x70) && (rec->sel_type.standard_type.event_type < 0x7F)) { 1204 *desc = ipmi_get_oem_desc(intf, rec); 1205 return; 1206 } else if (rec->sel_type.standard_type.event_type == 0x6f) { 1207 if( rec->sel_type.standard_type.sensor_type >= 0xC0 && rec->sel_type.standard_type.sensor_type < 0xF0) { 1208 IPMI_OEM iana = ipmi_get_oem(intf); 1209 1210 switch(iana){ 1211 case IPMI_OEM_KONTRON: 1212 lprintf(LOG_DEBUG, "oem sensor type %x %d using oem type supplied description", 1213 rec->sel_type.standard_type.sensor_type , iana); 1214 1215 evt = oem_kontron_event_types; 1216 code = rec->sel_type.standard_type.sensor_type; 1217 break; 1218 case IPMI_OEM_DELL: /* OEM Bytes Decoding for DELLi */ 1219 evt = sensor_specific_types; 1220 code = rec->sel_type.standard_type.sensor_type; 1221 if ( (OEM_CODE_IN_BYTE2 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)) || 1222 (OEM_CODE_IN_BYTE3 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE3_SPECIFIED_MASK)) ) 1223 { 1224 if(rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) 1225 evt->data = rec->sel_type.standard_type.event_data[1]; 1226 1227 sfx = ipmi_get_oem_desc(intf, rec); 1228 } 1229 break; 1230 case IPMI_OEM_SUPERMICRO: 1231 case IPMI_OEM_SUPERMICRO_47488: 1232 evt = sensor_specific_types; 1233 code = rec->sel_type.standard_type.sensor_type; 1234 sfx = ipmi_get_oem_desc(intf, rec); 1235 break; 1236 /* add your oem sensor assignation here */ 1237 } 1238 if( evt == NULL ){ 1239 lprintf(LOG_DEBUG, "oem sensor type %x using standard type supplied description", 1240 rec->sel_type.standard_type.sensor_type ); 1241 } 1242 } else { 1243 switch (ipmi_get_oem(intf)) { 1244 case IPMI_OEM_SUPERMICRO: 1245 case IPMI_OEM_SUPERMICRO_47488: 1246 evt = sensor_specific_types; 1247 code = rec->sel_type.standard_type.sensor_type; 1248 sfx = ipmi_get_oem_desc(intf, rec); 1249 break; 1250 } 1251 } 1252 if( evt == NULL ){ 1253 evt = sensor_specific_types; 1254 code = rec->sel_type.standard_type.sensor_type; 1255 } 1256 /* 1257 * Check for the OEM DELL Interface based on the Dell Specific Vendor Code. 1258 * If its Dell Platform, do the OEM Byte decode from the SEL Records. 1259 * Additional information should be written by the ipmi_get_oem_desc() 1260 */ 1261 if(ipmi_get_oem(intf) == IPMI_OEM_DELL) { 1262 code = rec->sel_type.standard_type.sensor_type; 1263 if ( (OEM_CODE_IN_BYTE2 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)) || 1264 (OEM_CODE_IN_BYTE3 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE3_SPECIFIED_MASK)) ) 1265 { 1266 if(rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) 1267 evt->data = rec->sel_type.standard_type.event_data[1]; 1268 sfx = ipmi_get_oem_desc(intf, rec); 1269 1270 } 1271 else if(SENSOR_TYPE_OEM_SEC_EVENT == rec->sel_type.standard_type.event_data[0]) 1272 { 1273 /* 0x23 : Sensor Number.*/ 1274 if(0x23 == rec->sel_type.standard_type.sensor_num) 1275 { 1276 evt->data = rec->sel_type.standard_type.event_data[1]; 1277 sfx = ipmi_get_oem_desc(intf, rec); 1278 } 1279 } 1280 } 1281 } else { 1282 evt = generic_event_types; 1283 code = rec->sel_type.standard_type.event_type; 1284 } 1285 1286 offset = rec->sel_type.standard_type.event_data[0] & 0xf; 1287 1288 while (evt->type) { 1289 if ((evt->code == code && evt->offset == offset && evt->desc != NULL) && 1290 ((evt->data == ALL_OFFSETS_SPECIFIED) || 1291 ((rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) && 1292 (evt->data == rec->sel_type.standard_type.event_data[1])))) 1293 { 1294 /* Increase the Malloc size to current_size + Dellspecific description size */ 1295 *desc = (char *)malloc(strlen(evt->desc) + 48 + SIZE_OF_DESC); 1296 if (NULL == *desc) { 1297 lprintf(LOG_ERR, "ipmitool: malloc failure"); 1298 return; 1299 } 1300 memset(*desc, 0, strlen(evt->desc)+ 48 + SIZE_OF_DESC); 1301 /* 1302 * Additional info is present for the DELL Platforms. 1303 * Append the same to the evt->desc string. 1304 */ 1305 if (sfx) { 1306 sprintf(*desc, "%s (%s)", evt->desc, sfx); 1307 free(sfx); 1308 sfx = NULL; 1309 } else { 1310 sprintf(*desc, "%s", evt->desc); 1311 } 1312 return; 1313 } 1314 evt++; 1315 } 1316 /* The Above while Condition was not met beacouse the below sensor type were Newly defined OEM 1317 Secondary Events. 0xC1, 0xC2, 0xC3. */ 1318 if((sfx) && (0x6F == rec->sel_type.standard_type.event_type)) 1319 { 1320 uint8_t flag = 0x00; 1321 switch(code) 1322 { 1323 case SENSOR_TYPE_FRM_PROG: 1324 if(0x0F == offset) 1325 flag = 0x01; 1326 break; 1327 case SENSOR_TYPE_OEM_SEC_EVENT: 1328 if((0x01 == offset) || (0x02 == offset) || (0x03 == offset)) 1329 flag = 0x01; 1330 break; 1331 case SENSOR_TYPE_OEM_NFATAL_ERROR: 1332 if((0x00 == offset) || (0x02 == offset)) 1333 flag = 0x01; 1334 break; 1335 case SENSOR_TYPE_OEM_FATAL_ERROR: 1336 if(0x01 == offset) 1337 flag = 0x01; 1338 break; 1339 case SENSOR_TYPE_SUPERMICRO_OEM: 1340 flag = 0x02; 1341 break; 1342 default: 1343 break; 1344 } 1345 if(flag) 1346 { 1347 *desc = (char *)malloc( 48 + SIZE_OF_DESC); 1348 if (NULL == *desc) 1349 { 1350 lprintf(LOG_ERR, "ipmitool: malloc failure"); 1351 return; 1352 } 1353 memset(*desc, 0, 48 + SIZE_OF_DESC); 1354 if (flag == 0x02) { 1355 sprintf(*desc, "%s", sfx); 1356 return; 1357 } 1358 sprintf(*desc, "(%s)",sfx); 1359 } 1360 free(sfx); 1361 sfx = NULL; 1362 } 1363 } 1364 1365 1366 const char * 1367 ipmi_sel_get_oem_sensor_type(IPMI_OEM iana, uint8_t code) 1368 { 1369 struct ipmi_event_sensor_types *st = NULL; 1370 1371 switch(iana){ 1372 case IPMI_OEM_KONTRON: 1373 st = oem_kontron_event_types; 1374 break; 1375 /* add you oem sensor type lookup assignement here */ 1376 default: 1377 lprintf(LOG_DEBUG, "ipmitool: missing OEM sensor type for %ul",iana); 1378 break; 1379 } 1380 1381 if( st != NULL ) 1382 for (; st->type != NULL; st++) 1383 if (st->code == code) 1384 return st->type; 1385 1386 return ipmi_sel_get_sensor_type(code); 1387 } 1388 1389 const char * 1390 ipmi_sel_get_oem_sensor_type_offset(IPMI_OEM iana, uint8_t code, uint8_t offset) 1391 { 1392 struct ipmi_event_sensor_types *st = NULL; 1393 1394 switch(iana){ 1395 case IPMI_OEM_KONTRON: 1396 st = oem_kontron_event_types; 1397 break; 1398 /* add you oem sensor type lookup assignement here */ 1399 default: 1400 lprintf(LOG_DEBUG, 1401 "ipmitool: missing OEM sensor type offset for %ul",iana); 1402 break; 1403 } 1404 1405 if( st != NULL ) 1406 for (; st->type != NULL; st++) 1407 { 1408 if (st->code == code && st->offset == (offset&0xf)) 1409 return st->type; 1410 } 1411 1412 return ipmi_sel_get_oem_sensor_type(iana,code); 1413 } 1414 1415 const char * 1416 ipmi_sel_get_sensor_type(uint8_t code) 1417 { 1418 struct ipmi_event_sensor_types *st; 1419 for (st = sensor_specific_types; st->type != NULL; st++) 1420 if (st->code == code) 1421 return st->type; 1422 return "Unknown"; 1423 } 1424 1425 const char * 1426 ipmi_sel_get_sensor_type_offset(uint8_t code, uint8_t offset) 1427 { 1428 struct ipmi_event_sensor_types *st; 1429 for (st = sensor_specific_types; st->type != NULL; st++) 1430 if (st->code == code && st->offset == (offset&0xf)) 1431 return st->type; 1432 1433 return ipmi_sel_get_sensor_type(code); 1434 } 1435 1436 static int 1437 ipmi_sel_get_info(struct ipmi_intf * intf) 1438 { 1439 struct ipmi_rs * rsp; 1440 struct ipmi_rq req; 1441 uint16_t e, version; 1442 uint32_t f; 1443 int pctfull = 0; 1444 uint32_t fs = 0xffffffff; 1445 uint32_t zeros = 0; 1446 1447 1448 memset(&req, 0, sizeof(req)); 1449 req.msg.netfn = IPMI_NETFN_STORAGE; 1450 req.msg.cmd = IPMI_CMD_GET_SEL_INFO; 1451 1452 rsp = intf->sendrecv(intf, &req); 1453 if (rsp == NULL) { 1454 lprintf(LOG_ERR, "Get SEL Info command failed"); 1455 return -1; 1456 } 1457 if (rsp->ccode > 0) { 1458 lprintf(LOG_ERR, "Get SEL Info command failed: %s", 1459 val2str(rsp->ccode, completion_code_vals)); 1460 return -1; 1461 } 1462 if (verbose > 2) 1463 printbuf(rsp->data, rsp->data_len, "sel_info"); 1464 1465 printf("SEL Information\n"); 1466 version = rsp->data[0]; 1467 printf("Version : %d.%d (%s)\n", 1468 version & 0xf, (version>>4) & 0xf, 1469 (version == 0x51 || version == 0x02) ? "v1.5, v2 compliant" : "Unknown"); 1470 1471 /* save the entry count and free space to determine percent full */ 1472 e = buf2short(rsp->data + 1); 1473 f = buf2short(rsp->data + 3); 1474 printf("Entries : %d\n", e); 1475 printf("Free Space : %d bytes %s\n", f ,(f==65535 ? "or more" : "" )); 1476 1477 if (e) { 1478 e *= 16; /* each entry takes 16 bytes */ 1479 f += e; /* this is supposed to give the total size ... */ 1480 pctfull = (int)(100 * ( (double)e / (double)f )); 1481 } 1482 1483 if( f >= 65535 ) { 1484 printf("Percent Used : %s\n", "unknown" ); 1485 } 1486 else { 1487 printf("Percent Used : %d%%\n", pctfull); 1488 } 1489 1490 1491 if ((!memcmp(rsp->data + 5, &fs, 4)) || 1492 (!memcmp(rsp->data + 5, &zeros, 4))) 1493 printf("Last Add Time : Not Available\n"); 1494 else 1495 printf("Last Add Time : %s\n", 1496 ipmi_sel_timestamp(buf2long(rsp->data + 5))); 1497 1498 if ((!memcmp(rsp->data + 9, &fs, 4)) || 1499 (!memcmp(rsp->data + 9, &zeros, 4))) 1500 printf("Last Del Time : Not Available\n"); 1501 else 1502 printf("Last Del Time : %s\n", 1503 ipmi_sel_timestamp(buf2long(rsp->data + 9))); 1504 1505 1506 printf("Overflow : %s\n", 1507 rsp->data[13] & 0x80 ? "true" : "false"); 1508 printf("Supported Cmds : "); 1509 if (rsp->data[13] & 0x0f) 1510 { 1511 if (rsp->data[13] & 0x08) 1512 printf("'Delete' "); 1513 if (rsp->data[13] & 0x04) 1514 printf("'Partial Add' "); 1515 if (rsp->data[13] & 0x02) 1516 printf("'Reserve' "); 1517 if (rsp->data[13] & 0x01) 1518 printf("'Get Alloc Info' "); 1519 } 1520 else 1521 printf("None"); 1522 printf("\n"); 1523 1524 /* get sel allocation info if supported */ 1525 if (rsp->data[13] & 1) { 1526 memset(&req, 0, sizeof(req)); 1527 req.msg.netfn = IPMI_NETFN_STORAGE; 1528 req.msg.cmd = IPMI_CMD_GET_SEL_ALLOC_INFO; 1529 1530 rsp = intf->sendrecv(intf, &req); 1531 if (rsp == NULL) { 1532 lprintf(LOG_ERR, 1533 "Get SEL Allocation Info command failed"); 1534 return -1; 1535 } 1536 if (rsp->ccode > 0) { 1537 lprintf(LOG_ERR, 1538 "Get SEL Allocation Info command failed: %s", 1539 val2str(rsp->ccode, completion_code_vals)); 1540 return -1; 1541 } 1542 1543 printf("# of Alloc Units : %d\n", buf2short(rsp->data)); 1544 printf("Alloc Unit Size : %d\n", buf2short(rsp->data + 2)); 1545 printf("# Free Units : %d\n", buf2short(rsp->data + 4)); 1546 printf("Largest Free Blk : %d\n", buf2short(rsp->data + 6)); 1547 printf("Max Record Size : %d\n", rsp->data[8]); 1548 } 1549 return 0; 1550 } 1551 1552 uint16_t 1553 ipmi_sel_get_std_entry(struct ipmi_intf * intf, uint16_t id, 1554 struct sel_event_record * evt) 1555 { 1556 struct ipmi_rq req; 1557 struct ipmi_rs * rsp; 1558 uint8_t msg_data[6]; 1559 uint16_t next; 1560 int data_count; 1561 1562 memset(msg_data, 0, 6); 1563 msg_data[0] = 0x00; /* no reserve id, not partial get */ 1564 msg_data[1] = 0x00; 1565 msg_data[2] = id & 0xff; 1566 msg_data[3] = (id >> 8) & 0xff; 1567 msg_data[4] = 0x00; /* offset */ 1568 msg_data[5] = 0xff; /* length */ 1569 1570 memset(&req, 0, sizeof(req)); 1571 req.msg.netfn = IPMI_NETFN_STORAGE; 1572 req.msg.cmd = IPMI_CMD_GET_SEL_ENTRY; 1573 req.msg.data = msg_data; 1574 req.msg.data_len = 6; 1575 1576 rsp = intf->sendrecv(intf, &req); 1577 if (rsp == NULL) { 1578 lprintf(LOG_ERR, "Get SEL Entry %x command failed", id); 1579 return 0; 1580 } 1581 if (rsp->ccode > 0) { 1582 lprintf(LOG_ERR, "Get SEL Entry %x command failed: %s", 1583 id, val2str(rsp->ccode, completion_code_vals)); 1584 return 0; 1585 } 1586 1587 /* save next entry id */ 1588 next = (rsp->data[1] << 8) | rsp->data[0]; 1589 1590 lprintf(LOG_DEBUG, "SEL Entry: %s", buf2str(rsp->data+2, rsp->data_len-2)); 1591 memset(evt, 0, sizeof(*evt)); 1592 1593 /*Clear SEL Structure*/ 1594 evt->record_id = 0; 1595 evt->record_type = 0; 1596 if (evt->record_type < 0xc0) 1597 { 1598 evt->sel_type.standard_type.timestamp = 0; 1599 evt->sel_type.standard_type.gen_id = 0; 1600 evt->sel_type.standard_type.evm_rev = 0; 1601 evt->sel_type.standard_type.sensor_type = 0; 1602 evt->sel_type.standard_type.sensor_num = 0; 1603 evt->sel_type.standard_type.event_type = 0; 1604 evt->sel_type.standard_type.event_dir = 0; 1605 evt->sel_type.standard_type.event_data[0] = 0; 1606 evt->sel_type.standard_type.event_data[1] = 0; 1607 evt->sel_type.standard_type.event_data[2] = 0; 1608 } 1609 else if (evt->record_type < 0xe0) 1610 { 1611 evt->sel_type.oem_ts_type.timestamp = 0; 1612 evt->sel_type.oem_ts_type.manf_id[0] = 0; 1613 evt->sel_type.oem_ts_type.manf_id[1] = 0; 1614 evt->sel_type.oem_ts_type.manf_id[2] = 0; 1615 for(data_count=0; data_count < SEL_OEM_TS_DATA_LEN ; data_count++) 1616 evt->sel_type.oem_ts_type.oem_defined[data_count] = 0; 1617 } 1618 else 1619 { 1620 for(data_count=0; data_count < SEL_OEM_NOTS_DATA_LEN ; data_count++) 1621 evt->sel_type.oem_nots_type.oem_defined[data_count] = 0; 1622 } 1623 1624 /* save response into SEL event structure */ 1625 evt->record_id = (rsp->data[3] << 8) | rsp->data[2]; 1626 evt->record_type = rsp->data[4]; 1627 if (evt->record_type < 0xc0) 1628 { 1629 evt->sel_type.standard_type.timestamp = (rsp->data[8] << 24) | (rsp->data[7] << 16) | 1630 (rsp->data[6] << 8) | rsp->data[5]; 1631 evt->sel_type.standard_type.gen_id = (rsp->data[10] << 8) | rsp->data[9]; 1632 evt->sel_type.standard_type.evm_rev = rsp->data[11]; 1633 evt->sel_type.standard_type.sensor_type = rsp->data[12]; 1634 evt->sel_type.standard_type.sensor_num = rsp->data[13]; 1635 evt->sel_type.standard_type.event_type = rsp->data[14] & 0x7f; 1636 evt->sel_type.standard_type.event_dir = (rsp->data[14] & 0x80) >> 7; 1637 evt->sel_type.standard_type.event_data[0] = rsp->data[15]; 1638 evt->sel_type.standard_type.event_data[1] = rsp->data[16]; 1639 evt->sel_type.standard_type.event_data[2] = rsp->data[17]; 1640 } 1641 else if (evt->record_type < 0xe0) 1642 { 1643 evt->sel_type.oem_ts_type.timestamp= (rsp->data[8] << 24) | (rsp->data[7] << 16) | 1644 (rsp->data[6] << 8) | rsp->data[5]; 1645 evt->sel_type.oem_ts_type.manf_id[0]= rsp->data[11]; 1646 evt->sel_type.oem_ts_type.manf_id[1]= rsp->data[10]; 1647 evt->sel_type.oem_ts_type.manf_id[2]= rsp->data[9]; 1648 for(data_count=0; data_count < SEL_OEM_TS_DATA_LEN ; data_count++) 1649 evt->sel_type.oem_ts_type.oem_defined[data_count] = rsp->data[(data_count+12)]; 1650 } 1651 else 1652 { 1653 for(data_count=0; data_count < SEL_OEM_NOTS_DATA_LEN ; data_count++) 1654 evt->sel_type.oem_nots_type.oem_defined[data_count] = rsp->data[(data_count+5)]; 1655 } 1656 return next; 1657 } 1658 1659 static void 1660 ipmi_sel_print_event_file(struct ipmi_intf * intf, struct sel_event_record * evt, FILE * fp) 1661 { 1662 char * description; 1663 1664 if (fp == NULL) 1665 return; 1666 1667 ipmi_get_event_desc(intf, evt, &description); 1668 1669 fprintf(fp, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x # %s #0x%02x %s\n", 1670 evt->sel_type.standard_type.evm_rev, 1671 evt->sel_type.standard_type.sensor_type, 1672 evt->sel_type.standard_type.sensor_num, 1673 evt->sel_type.standard_type.event_type | (evt->sel_type.standard_type.event_dir << 7), 1674 evt->sel_type.standard_type.event_data[0], 1675 evt->sel_type.standard_type.event_data[1], 1676 evt->sel_type.standard_type.event_data[2], 1677 ( 1678 (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) 1679 ? 1680 ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 1681 : 1682 ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 1683 ), 1684 evt->sel_type.standard_type.sensor_num, 1685 (description != NULL) ? description : "Unknown"); 1686 1687 if (description != NULL) { 1688 free(description); 1689 description = NULL; 1690 } 1691 } 1692 1693 void 1694 ipmi_sel_print_extended_entry(struct ipmi_intf * intf, struct sel_event_record * evt) 1695 { 1696 sel_extended++; 1697 ipmi_sel_print_std_entry(intf, evt); 1698 sel_extended--; 1699 } 1700 1701 void 1702 ipmi_sel_print_std_entry(struct ipmi_intf * intf, struct sel_event_record * evt) 1703 { 1704 char * description; 1705 struct sdr_record_list * sdr = NULL; 1706 int data_count; 1707 1708 if (sel_extended && (evt->record_type < 0xc0)) 1709 sdr = ipmi_sdr_find_sdr_bynumtype(intf, evt->sel_type.standard_type.gen_id, evt->sel_type.standard_type.sensor_num, evt->sel_type.standard_type.sensor_type); 1710 1711 1712 if (!evt) 1713 return; 1714 1715 if (csv_output) 1716 printf("%x,", evt->record_id); 1717 else 1718 printf("%4x | ", evt->record_id); 1719 1720 if (evt->record_type == 0xf0) 1721 { 1722 if (csv_output) 1723 printf(",,"); 1724 1725 printf ("Linux kernel panic: %.11s\n", (char *) evt + 5); 1726 return; 1727 } 1728 1729 if (evt->record_type < 0xe0) 1730 { 1731 if ((evt->sel_type.standard_type.timestamp < 0x20000000)||(evt->sel_type.oem_ts_type.timestamp < 0x20000000)){ 1732 printf(" Pre-Init "); 1733 1734 if (csv_output) 1735 printf(","); 1736 else 1737 printf(" |"); 1738 1739 printf("%010d", evt->sel_type.standard_type.timestamp ); 1740 if (csv_output) 1741 printf(","); 1742 else 1743 printf("| "); 1744 } 1745 else { 1746 if (evt->record_type < 0xc0) 1747 printf("%s", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp)); 1748 else 1749 printf("%s", ipmi_sel_timestamp_date(evt->sel_type.oem_ts_type.timestamp)); 1750 if (csv_output) 1751 printf(","); 1752 else 1753 printf(" | "); 1754 1755 if (evt->record_type < 0xc0) 1756 printf("%s", ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp)); 1757 else 1758 printf("%s", ipmi_sel_timestamp_time(evt->sel_type.oem_ts_type.timestamp)); 1759 1760 if (csv_output) 1761 printf(","); 1762 else 1763 printf(" | "); 1764 } 1765 1766 } 1767 else 1768 { 1769 if (csv_output) 1770 printf(",,"); 1771 } 1772 1773 if (evt->record_type >= 0xc0) 1774 { 1775 printf ("OEM record %02x", evt->record_type); 1776 if (csv_output) 1777 printf(","); 1778 else 1779 printf(" | "); 1780 1781 if(evt->record_type <= 0xdf) 1782 { 1783 printf ("%02x%02x%02x", evt->sel_type.oem_ts_type.manf_id[0], evt->sel_type.oem_ts_type.manf_id[1], evt->sel_type.oem_ts_type.manf_id[2]); 1784 if (csv_output) 1785 printf(","); 1786 else 1787 printf(" | "); 1788 for(data_count=0;data_count < SEL_OEM_TS_DATA_LEN;data_count++) 1789 printf("%02x", evt->sel_type.oem_ts_type.oem_defined[data_count]); 1790 } 1791 else 1792 { 1793 for(data_count=0;data_count < SEL_OEM_NOTS_DATA_LEN;data_count++) 1794 printf("%02x", evt->sel_type.oem_nots_type.oem_defined[data_count]); 1795 } 1796 ipmi_sel_oem_message(evt, 0); 1797 printf ("\n"); 1798 return; 1799 } 1800 1801 /* lookup SDR entry based on sensor number and type */ 1802 if (sdr != NULL) { 1803 printf("%s ", 1804 ( 1805 (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) 1806 ? 1807 ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 1808 : 1809 ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 1810 ) 1811 ); 1812 switch (sdr->type) { 1813 case SDR_RECORD_TYPE_FULL_SENSOR: 1814 printf("%s", sdr->record.full->id_string); 1815 break; 1816 case SDR_RECORD_TYPE_COMPACT_SENSOR: 1817 printf("%s", sdr->record.compact->id_string); 1818 break; 1819 case SDR_RECORD_TYPE_EVENTONLY_SENSOR: 1820 printf("%s", sdr->record.eventonly->id_string); 1821 break; 1822 case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR: 1823 printf("%s", sdr->record.fruloc->id_string); 1824 break; 1825 case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR: 1826 printf("%s", sdr->record.mcloc->id_string); 1827 break; 1828 case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR: 1829 printf("%s", sdr->record.genloc->id_string); 1830 break; 1831 default: 1832 printf("#%02x", evt->sel_type.standard_type.sensor_num); 1833 break; 1834 } 1835 } else { 1836 printf("%s",( 1837 (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) 1838 ? 1839 ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 1840 : 1841 ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 1842 )); 1843 if (evt->sel_type.standard_type.sensor_num != 0) 1844 printf(" #0x%02x", evt->sel_type.standard_type.sensor_num); 1845 } 1846 1847 if (csv_output) 1848 printf(","); 1849 else 1850 printf(" | "); 1851 1852 ipmi_get_event_desc(intf, evt, &description); 1853 if (description) { 1854 printf("%s", description); 1855 free(description); 1856 description = NULL; 1857 } 1858 1859 if (csv_output) { 1860 printf(","); 1861 } else { 1862 printf(" | "); 1863 } 1864 1865 if (evt->sel_type.standard_type.event_dir) { 1866 printf("Deasserted"); 1867 } else { 1868 printf("Asserted"); 1869 } 1870 1871 if (sdr != NULL && evt->sel_type.standard_type.event_type == 1) { 1872 /* 1873 * Threshold Event 1874 */ 1875 float trigger_reading = 0.0; 1876 float threshold_reading = 0.0; 1877 uint8_t threshold_reading_provided = 0; 1878 1879 /* trigger reading in event data byte 2 */ 1880 if (((evt->sel_type.standard_type.event_data[0] >> 6) & 3) == 1) { 1881 trigger_reading = sdr_convert_sensor_reading( 1882 sdr->record.full, evt->sel_type.standard_type.event_data[1]); 1883 } 1884 1885 /* trigger threshold in event data byte 3 */ 1886 if (((evt->sel_type.standard_type.event_data[0] >> 4) & 3) == 1) { 1887 threshold_reading = sdr_convert_sensor_reading( 1888 sdr->record.full, evt->sel_type.standard_type.event_data[2]); 1889 threshold_reading_provided = 1; 1890 } 1891 1892 if (csv_output) 1893 printf(","); 1894 else 1895 printf(" | "); 1896 1897 printf("Reading %.*f", 1898 (trigger_reading==(int)trigger_reading) ? 0 : 2, 1899 trigger_reading); 1900 if (threshold_reading_provided) { 1901 printf(" %s Threshold %.*f %s", 1902 ((evt->sel_type.standard_type.event_data[0] & 0xf) % 2) ? ">" : "<", 1903 (threshold_reading==(int)threshold_reading) ? 0 : 2, 1904 threshold_reading, 1905 ipmi_sdr_get_unit_string(sdr->record.common->unit.pct, 1906 sdr->record.common->unit.modifier, 1907 sdr->record.common->unit.type.base, 1908 sdr->record.common->unit.type.modifier)); 1909 } 1910 } 1911 else if (evt->sel_type.standard_type.event_type == 0x6f) { 1912 int print_sensor = 1; 1913 switch (ipmi_get_oem(intf)) { 1914 case IPMI_OEM_SUPERMICRO: 1915 case IPMI_OEM_SUPERMICRO_47488: 1916 print_sensor = 0; 1917 break; 1918 } 1919 /* 1920 * Sensor-Specific Discrete 1921 */ 1922 if (print_sensor && evt->sel_type.standard_type.sensor_type == 0xC && /*TODO*/ 1923 evt->sel_type.standard_type.sensor_num == 0 && 1924 (evt->sel_type.standard_type.event_data[0] & 0x30) == 0x20) { 1925 /* break down memory ECC reporting if we can */ 1926 if (csv_output) 1927 printf(","); 1928 else 1929 printf(" | "); 1930 1931 printf("CPU %d DIMM %d", 1932 evt->sel_type.standard_type.event_data[2] & 0x0f, 1933 (evt->sel_type.standard_type.event_data[2] & 0xf0) >> 4); 1934 } 1935 } 1936 1937 printf("\n"); 1938 } 1939 1940 void 1941 ipmi_sel_print_std_entry_verbose(struct ipmi_intf * intf, struct sel_event_record * evt) 1942 { 1943 char * description; 1944 int data_count; 1945 1946 if (!evt) 1947 return; 1948 1949 printf("SEL Record ID : %04x\n", evt->record_id); 1950 1951 if (evt->record_type == 0xf0) 1952 { 1953 printf (" Record Type : Linux kernel panic (OEM record %02x)\n", evt->record_type); 1954 printf (" Panic string : %.11s\n\n", (char *) evt + 5); 1955 return; 1956 } 1957 1958 printf(" Record Type : %02x", evt->record_type); 1959 if (evt->record_type >= 0xc0) 1960 { 1961 if (evt->record_type < 0xe0) 1962 printf(" (OEM timestamped)"); 1963 else 1964 printf(" (OEM non-timestamped)"); 1965 } 1966 printf("\n"); 1967 1968 if (evt->record_type < 0xe0) 1969 { 1970 printf(" Timestamp : "); 1971 if (evt->record_type < 0xc0) 1972 printf("%s %s", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp), 1973 ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp)); 1974 else 1975 printf("%s %s", ipmi_sel_timestamp_date(evt->sel_type.oem_ts_type.timestamp), 1976 ipmi_sel_timestamp_time(evt->sel_type.oem_ts_type.timestamp)); 1977 printf("\n"); 1978 } 1979 1980 if (evt->record_type >= 0xc0) 1981 { 1982 if(evt->record_type <= 0xdf) 1983 { 1984 printf (" Manufactacturer ID : %02x%02x%02x\n", evt->sel_type.oem_ts_type.manf_id[0], 1985 evt->sel_type.oem_ts_type.manf_id[1], evt->sel_type.oem_ts_type.manf_id[2]); 1986 printf (" OEM Defined : "); 1987 for(data_count=0;data_count < SEL_OEM_TS_DATA_LEN;data_count++) 1988 printf("%02x", evt->sel_type.oem_ts_type.oem_defined[data_count]); 1989 printf(" [%s]\n\n",hex2ascii (evt->sel_type.oem_ts_type.oem_defined, SEL_OEM_TS_DATA_LEN)); 1990 } 1991 else 1992 { 1993 printf (" OEM Defined : "); 1994 for(data_count=0;data_count < SEL_OEM_NOTS_DATA_LEN;data_count++) 1995 printf("%02x", evt->sel_type.oem_nots_type.oem_defined[data_count]); 1996 printf(" [%s]\n\n",hex2ascii (evt->sel_type.oem_nots_type.oem_defined, SEL_OEM_NOTS_DATA_LEN)); 1997 ipmi_sel_oem_message(evt, 1); 1998 } 1999 return; 2000 } 2001 2002 printf(" Generator ID : %04x\n", 2003 evt->sel_type.standard_type.gen_id); 2004 printf(" EvM Revision : %02x\n", 2005 evt->sel_type.standard_type.evm_rev); 2006 printf(" Sensor Type : %s\n", 2007 ( 2008 (evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0) 2009 ? 2010 ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 2011 : 2012 ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]) 2013 ) 2014 ); 2015 printf(" Sensor Number : %02x\n", 2016 evt->sel_type.standard_type.sensor_num); 2017 printf(" Event Type : %s\n", 2018 ipmi_get_event_type(evt->sel_type.standard_type.event_type)); 2019 printf(" Event Direction : %s\n", 2020 val2str(evt->sel_type.standard_type.event_dir, event_dir_vals)); 2021 printf(" Event Data : %02x%02x%02x\n", 2022 evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]); 2023 ipmi_get_event_desc(intf, evt, &description); 2024 printf(" Description : %s\n", 2025 description ? description : ""); 2026 free(description); 2027 description = NULL; 2028 2029 printf("\n"); 2030 } 2031 2032 2033 void 2034 ipmi_sel_print_extended_entry_verbose(struct ipmi_intf * intf, struct sel_event_record * evt) 2035 { 2036 struct sdr_record_list * sdr; 2037 char * description; 2038 2039 if (!evt) 2040 return; 2041 2042 sdr = ipmi_sdr_find_sdr_bynumtype(intf, 2043 evt->sel_type.standard_type.gen_id, 2044 evt->sel_type.standard_type.sensor_num, 2045 evt->sel_type.standard_type.sensor_type); 2046 if (sdr == NULL) 2047 { 2048 ipmi_sel_print_std_entry_verbose(intf, evt); 2049 return; 2050 } 2051 2052 printf("SEL Record ID : %04x\n", evt->record_id); 2053 2054 if (evt->record_type == 0xf0) 2055 { 2056 printf (" Record Type : " 2057 "Linux kernel panic (OEM record %02x)\n", 2058 evt->record_type); 2059 printf (" Panic string : %.11s\n\n", 2060 (char *) evt + 5); 2061 return; 2062 } 2063 2064 printf(" Record Type : %02x\n", evt->record_type); 2065 if (evt->record_type < 0xe0) 2066 { 2067 printf(" Timestamp : "); 2068 printf("%s %s\n", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp), 2069 ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp)); 2070 } 2071 2072 2073 printf(" Generator ID : %04x\n", 2074 evt->sel_type.standard_type.gen_id); 2075 printf(" EvM Revision : %02x\n", 2076 evt->sel_type.standard_type.evm_rev); 2077 printf(" Sensor Type : %s\n", 2078 ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])); 2079 printf(" Sensor Number : %02x\n", 2080 evt->sel_type.standard_type.sensor_num); 2081 printf(" Event Type : %s\n", 2082 ipmi_get_event_type(evt->sel_type.standard_type.event_type)); 2083 printf(" Event Direction : %s\n", 2084 val2str(evt->sel_type.standard_type.event_dir, event_dir_vals)); 2085 printf(" Event Data (RAW) : %02x%02x%02x\n", 2086 evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]); 2087 2088 /* break down event data field 2089 * as per IPMI Spec 2.0 Table 29-6 */ 2090 if (evt->sel_type.standard_type.event_type == 1 && sdr->type == SDR_RECORD_TYPE_FULL_SENSOR) { 2091 /* Threshold */ 2092 switch ((evt->sel_type.standard_type.event_data[0] >> 6) & 3) { /* EV1[7:6] */ 2093 case 0: 2094 /* unspecified byte 2 */ 2095 break; 2096 case 1: 2097 /* trigger reading in byte 2 */ 2098 printf(" Trigger Reading : %.3f", 2099 sdr_convert_sensor_reading(sdr->record.full, 2100 evt->sel_type.standard_type.event_data[1])); 2101 /* determine units with possible modifiers */ 2102 printf ("%s\n", ipmi_sdr_get_unit_string(sdr->record.common->unit.pct, 2103 sdr->record.common->unit.modifier, 2104 sdr->record.common->unit.type.base, 2105 sdr->record.common->unit.type.modifier)); 2106 break; 2107 case 2: 2108 /* oem code in byte 2 */ 2109 printf(" OEM Data : %02x\n", 2110 evt->sel_type.standard_type.event_data[1]); 2111 break; 2112 case 3: 2113 /* sensor-specific extension code in byte 2 */ 2114 printf(" Sensor Extension Code : %02x\n", 2115 evt->sel_type.standard_type.event_data[1]); 2116 break; 2117 } 2118 switch ((evt->sel_type.standard_type.event_data[0] >> 4) & 3) { /* EV1[5:4] */ 2119 case 0: 2120 /* unspecified byte 3 */ 2121 break; 2122 case 1: 2123 /* trigger threshold value in byte 3 */ 2124 printf(" Trigger Threshold : %.3f", 2125 sdr_convert_sensor_reading(sdr->record.full, 2126 evt->sel_type.standard_type.event_data[2])); 2127 /* determine units with possible modifiers */ 2128 printf ("%s\n", ipmi_sdr_get_unit_string(sdr->record.common->unit.pct, 2129 sdr->record.common->unit.modifier, 2130 sdr->record.common->unit.type.base, 2131 sdr->record.common->unit.type.modifier)); 2132 break; 2133 case 2: 2134 /* OEM code in byte 3 */ 2135 printf(" OEM Data : %02x\n", 2136 evt->sel_type.standard_type.event_data[2]); 2137 break; 2138 case 3: 2139 /* sensor-specific extension code in byte 3 */ 2140 printf(" Sensor Extension Code : %02x\n", 2141 evt->sel_type.standard_type.event_data[2]); 2142 break; 2143 } 2144 } else if (evt->sel_type.standard_type.event_type >= 0x2 && evt->sel_type.standard_type.event_type <= 0xc) { 2145 /* Generic Discrete */ 2146 } else if (evt->sel_type.standard_type.event_type == 0x6f) { 2147 2148 /* Sensor-Specific Discrete */ 2149 if (evt->sel_type.standard_type.sensor_type == 0xC && 2150 evt->sel_type.standard_type.sensor_num == 0 && /**** THIS LOOK TO BE OEM ****/ 2151 (evt->sel_type.standard_type.event_data[0] & 0x30) == 0x20) 2152 { 2153 /* break down memory ECC reporting if we can */ 2154 printf(" Event Data : CPU %d DIMM %d\n", 2155 evt->sel_type.standard_type.event_data[2] & 0x0f, 2156 (evt->sel_type.standard_type.event_data[2] & 0xf0) >> 4); 2157 } 2158 else if( 2159 evt->sel_type.standard_type.sensor_type == 0x2b && /* Version change */ 2160 evt->sel_type.standard_type.event_data[0] == 0xC1 /* Data in Data 2 */ 2161 ) 2162 2163 { 2164 //evt->sel_type.standard_type.event_data[1] 2165 } 2166 else 2167 { 2168 /* FIXME : Add sensor specific discrete types */ 2169 printf(" Event Interpretation : Missing\n"); 2170 } 2171 } else if (evt->sel_type.standard_type.event_type >= 0x70 && evt->sel_type.standard_type.event_type <= 0x7f) { 2172 /* OEM */ 2173 } else { 2174 printf(" Event Data : %02x%02x%02x\n", 2175 evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]); 2176 } 2177 2178 ipmi_get_event_desc(intf, evt, &description); 2179 printf(" Description : %s\n", 2180 description ? description : ""); 2181 free(description); 2182 description = NULL; 2183 2184 printf("\n"); 2185 } 2186 2187 static int 2188 __ipmi_sel_savelist_entries(struct ipmi_intf * intf, int count, const char * savefile, 2189 int binary) 2190 { 2191 struct ipmi_rs * rsp; 2192 struct ipmi_rq req; 2193 uint16_t next_id = 0, curr_id = 0; 2194 struct sel_event_record evt; 2195 int n=0; 2196 FILE * fp = NULL; 2197 2198 memset(&req, 0, sizeof(req)); 2199 req.msg.netfn = IPMI_NETFN_STORAGE; 2200 req.msg.cmd = IPMI_CMD_GET_SEL_INFO; 2201 2202 rsp = intf->sendrecv(intf, &req); 2203 if (rsp == NULL) { 2204 lprintf(LOG_ERR, "Get SEL Info command failed"); 2205 return -1; 2206 } 2207 if (rsp->ccode > 0) { 2208 lprintf(LOG_ERR, "Get SEL Info command failed: %s", 2209 val2str(rsp->ccode, completion_code_vals)); 2210 return -1; 2211 } 2212 if (verbose > 2) 2213 printbuf(rsp->data, rsp->data_len, "sel_info"); 2214 2215 if (rsp->data[1] == 0 && rsp->data[2] == 0) { 2216 lprintf(LOG_ERR, "SEL has no entries"); 2217 return 0; 2218 } 2219 2220 memset(&req, 0, sizeof(req)); 2221 req.msg.netfn = IPMI_NETFN_STORAGE; 2222 req.msg.cmd = IPMI_CMD_RESERVE_SEL; 2223 2224 rsp = intf->sendrecv(intf, &req); 2225 if (rsp == NULL) { 2226 lprintf(LOG_ERR, "Reserve SEL command failed"); 2227 return -1; 2228 } 2229 if (rsp->ccode > 0) { 2230 lprintf(LOG_ERR, "Reserve SEL command failed: %s", 2231 val2str(rsp->ccode, completion_code_vals)); 2232 return -1; 2233 } 2234 2235 if (count < 0) { 2236 /** Show only the most recent 'count' records. */ 2237 int delta; 2238 uint16_t entries; 2239 2240 req.msg.cmd = IPMI_CMD_GET_SEL_INFO; 2241 rsp = intf->sendrecv(intf, &req); 2242 if (rsp == NULL) { 2243 lprintf(LOG_ERR, "Get SEL Info command failed"); 2244 return -1; 2245 } 2246 if (rsp->ccode > 0) { 2247 lprintf(LOG_ERR, "Get SEL Info command failed: %s", 2248 val2str(rsp->ccode, completion_code_vals)); 2249 return -1; 2250 } 2251 entries = buf2short(rsp->data + 1); 2252 if (-count > entries) 2253 count = -entries; 2254 2255 /* Get first record. */ 2256 next_id = ipmi_sel_get_std_entry(intf, 0, &evt); 2257 2258 delta = next_id - evt.record_id; 2259 2260 /* Get last record. */ 2261 next_id = ipmi_sel_get_std_entry(intf, 0xffff, &evt); 2262 2263 next_id = evt.record_id + count * delta + delta; 2264 } 2265 2266 if (savefile != NULL) { 2267 fp = ipmi_open_file_write(savefile); 2268 } 2269 2270 while (next_id != 0xffff) { 2271 curr_id = next_id; 2272 lprintf(LOG_DEBUG, "SEL Next ID: %04x", curr_id); 2273 2274 next_id = ipmi_sel_get_std_entry(intf, curr_id, &evt); 2275 if (next_id == 0) { 2276 /* 2277 * usually next_id of zero means end but 2278 * retry because some hardware has quirks 2279 * and will return 0 randomly. 2280 */ 2281 next_id = ipmi_sel_get_std_entry(intf, curr_id, &evt); 2282 if (next_id == 0) 2283 break; 2284 } 2285 2286 if (verbose) 2287 ipmi_sel_print_std_entry_verbose(intf, &evt); 2288 else 2289 ipmi_sel_print_std_entry(intf, &evt); 2290 2291 if (fp != NULL) { 2292 if (binary) 2293 fwrite(&evt, 1, 16, fp); 2294 else 2295 ipmi_sel_print_event_file(intf, &evt, fp); 2296 } 2297 2298 if (++n == count) { 2299 break; 2300 } 2301 } 2302 2303 if (fp != NULL) 2304 fclose(fp); 2305 2306 return 0; 2307 } 2308 2309 static int 2310 ipmi_sel_list_entries(struct ipmi_intf * intf, int count) 2311 { 2312 return __ipmi_sel_savelist_entries(intf, count, NULL, 0); 2313 } 2314 2315 static int 2316 ipmi_sel_save_entries(struct ipmi_intf * intf, int count, const char * savefile) 2317 { 2318 return __ipmi_sel_savelist_entries(intf, count, savefile, 0); 2319 } 2320 2321 /* 2322 * ipmi_sel_interpret 2323 * 2324 * return 0 on success, 2325 * -1 on error 2326 */ 2327 static int 2328 ipmi_sel_interpret(struct ipmi_intf *intf, unsigned long iana, 2329 const char *readfile, const char *format) 2330 { 2331 FILE *fp = 0; 2332 struct sel_event_record evt; 2333 char *buffer = NULL; 2334 char *cursor = NULL; 2335 int status = 0; 2336 /* since the interface is not used, iana is taken from 2337 * the command line 2338 */ 2339 sel_iana = iana; 2340 if (strncmp("pps", format, 3) == 0) { 2341 /* Parser for the following format */ 2342 /* 0x001F: Event: at Mar 27 06:41:10 2007;from:(0x9a,0,7); 2343 * sensor:(0xc3,119); event:0x6f(asserted): 0xA3 0x00 0x88 2344 * commonly found in PPS shelf managers 2345 * Supports a tweak for hotswap events that are already interpreted. 2346 */ 2347 fp = ipmi_open_file(readfile, 0); 2348 if (fp == NULL) { 2349 lprintf(LOG_ERR, "Failed to open file '%s' for reading.", 2350 readfile); 2351 return (-1); 2352 } 2353 buffer = (char *)malloc((size_t)256); 2354 if (buffer == NULL) { 2355 lprintf(LOG_ERR, "ipmitool: malloc failure"); 2356 fclose(fp); 2357 return (-1); 2358 } 2359 do { 2360 /* Only allow complete lines to be parsed, 2361 * hardcoded maximum line length 2362 */ 2363 if (fgets(buffer, 256, fp) == NULL) { 2364 status = (-1); 2365 break; 2366 } 2367 if (strlen(buffer) > 255) { 2368 lprintf(LOG_ERR, "ipmitool: invalid entry found in file."); 2369 continue; 2370 } 2371 cursor = buffer; 2372 /* assume normal "System" event */ 2373 evt.record_type = 2; 2374 errno = 0; 2375 evt.record_id = strtol((const char *)cursor, (char **)NULL, 16); 2376 if (errno != 0) { 2377 lprintf(LOG_ERR, "Invalid record ID."); 2378 status = (-1); 2379 break; 2380 } 2381 evt.sel_type.standard_type.evm_rev = 4; 2382 2383 /* FIXME: convert*/ 2384 evt.sel_type.standard_type.timestamp; 2385 2386 /* skip timestamp */ 2387 cursor = index((const char *)cursor, ';'); 2388 cursor++; 2389 2390 /* FIXME: parse originator */ 2391 evt.sel_type.standard_type.gen_id = 0x0020; 2392 2393 /* skip originator info */ 2394 cursor = index((const char *)cursor, ';'); 2395 cursor++; 2396 2397 /* Get sensor type */ 2398 cursor = index((const char *)cursor, '('); 2399 cursor++; 2400 2401 errno = 0; 2402 evt.sel_type.standard_type.sensor_type = 2403 strtol((const char *)cursor, (char **)NULL, 16); 2404 if (errno != 0) { 2405 lprintf(LOG_ERR, "Invalid Sensor Type."); 2406 status = (-1); 2407 break; 2408 } 2409 cursor = index((const char *)cursor, ','); 2410 cursor++; 2411 2412 errno = 0; 2413 evt.sel_type.standard_type.sensor_num = 2414 strtol((const char *)cursor, (char **)NULL, 10); 2415 if (errno != 0) { 2416 lprintf(LOG_ERR, "Invalid Sensor Number."); 2417 status = (-1); 2418 break; 2419 } 2420 2421 /* skip to event type info */ 2422 cursor = index((const char *)cursor, ':'); 2423 cursor++; 2424 2425 errno = 0; 2426 evt.sel_type.standard_type.event_type= 2427 strtol((const char *)cursor, (char **)NULL, 16); 2428 if (errno != 0) { 2429 lprintf(LOG_ERR, "Invalid Event Type."); 2430 status = (-1); 2431 break; 2432 } 2433 2434 /* skip to event dir info */ 2435 cursor = index((const char *)cursor, '('); 2436 cursor++; 2437 if (*cursor == 'a') { 2438 evt.sel_type.standard_type.event_dir = 0; 2439 } else { 2440 evt.sel_type.standard_type.event_dir = 1; 2441 } 2442 /* skip to data info */ 2443 cursor = index((const char *)cursor, ' '); 2444 cursor++; 2445 2446 if (evt.sel_type.standard_type.sensor_type == 0xF0) { 2447 /* got to FRU id */ 2448 while (!isdigit(*cursor)) { 2449 cursor++; 2450 } 2451 /* store FRUid */ 2452 errno = 0; 2453 evt.sel_type.standard_type.event_data[2] = 2454 strtol(cursor, (char **)NULL, 10); 2455 if (errno != 0) { 2456 lprintf(LOG_ERR, "Invalid Event Data#2."); 2457 status = (-1); 2458 break; 2459 } 2460 2461 /* Get to previous state */ 2462 cursor = index((const char *)cursor, 'M'); 2463 cursor++; 2464 2465 /* Set previous state */ 2466 errno = 0; 2467 evt.sel_type.standard_type.event_data[1] = 2468 strtol(cursor, (char **)NULL, 10); 2469 if (errno != 0) { 2470 lprintf(LOG_ERR, "Invalid Event Data#1."); 2471 status = (-1); 2472 break; 2473 } 2474 2475 /* Get to current state */ 2476 cursor = index((const char *)cursor, 'M'); 2477 cursor++; 2478 2479 /* Set current state */ 2480 errno = 0; 2481 evt.sel_type.standard_type.event_data[0] = 2482 0xA0 | strtol(cursor, (char **)NULL, 10); 2483 if (errno != 0) { 2484 lprintf(LOG_ERR, "Invalid Event Data#0."); 2485 status = (-1); 2486 break; 2487 } 2488 2489 /* skip to cause */ 2490 cursor = index((const char *)cursor, '='); 2491 cursor++; 2492 errno = 0; 2493 evt.sel_type.standard_type.event_data[1] |= 2494 (strtol(cursor, (char **)NULL, 16)) << 4; 2495 if (errno != 0) { 2496 lprintf(LOG_ERR, "Invalid Event Data#1."); 2497 status = (-1); 2498 break; 2499 } 2500 } else if (*cursor == '0') { 2501 errno = 0; 2502 evt.sel_type.standard_type.event_data[0] = 2503 strtol((const char *)cursor, (char **)NULL, 16); 2504 if (errno != 0) { 2505 lprintf(LOG_ERR, "Invalid Event Data#0."); 2506 status = (-1); 2507 break; 2508 } 2509 cursor = index((const char *)cursor, ' '); 2510 cursor++; 2511 2512 errno = 0; 2513 evt.sel_type.standard_type.event_data[1] = 2514 strtol((const char *)cursor, (char **)NULL, 16); 2515 if (errno != 0) { 2516 lprintf(LOG_ERR, "Invalid Event Data#1."); 2517 status = (-1); 2518 break; 2519 } 2520 2521 cursor = index((const char *)cursor, ' '); 2522 cursor++; 2523 2524 errno = 0; 2525 evt.sel_type.standard_type.event_data[2] = 2526 strtol((const char *)cursor, (char **)NULL, 16); 2527 if (errno != 0) { 2528 lprintf(LOG_ERR, "Invalid Event Data#2."); 2529 status = (-1); 2530 break; 2531 } 2532 } else { 2533 lprintf(LOG_ERR, "ipmitool: can't guess format."); 2534 } 2535 /* parse the PPS line into a sel_event_record */ 2536 if (verbose) { 2537 ipmi_sel_print_std_entry_verbose(intf, &evt); 2538 } else { 2539 ipmi_sel_print_std_entry(intf, &evt); 2540 } 2541 cursor = NULL; 2542 } while (status == 0); /* until file is completely read */ 2543 cursor = NULL; 2544 free(buffer); 2545 buffer = NULL; 2546 fclose(fp); 2547 } else { 2548 lprintf(LOG_ERR, "Given format '%s' is unknown.", format); 2549 status = (-1); 2550 } 2551 return status; 2552 } 2553 2554 2555 static int 2556 ipmi_sel_writeraw(struct ipmi_intf * intf, const char * savefile) 2557 { 2558 return __ipmi_sel_savelist_entries(intf, 0, savefile, 1); 2559 } 2560 2561 2562 static int 2563 ipmi_sel_readraw(struct ipmi_intf * intf, const char * inputfile) 2564 { 2565 struct sel_event_record evt; 2566 int ret = 0; 2567 FILE* fp = 0; 2568 2569 fp = ipmi_open_file(inputfile, 0); 2570 if (fp) 2571 { 2572 size_t bytesRead; 2573 2574 do { 2575 if ((bytesRead = fread(&evt, 1, 16, fp)) == 16) 2576 { 2577 if (verbose) 2578 ipmi_sel_print_std_entry_verbose(intf, &evt); 2579 else 2580 ipmi_sel_print_std_entry(intf, &evt); 2581 } 2582 else 2583 { 2584 if (bytesRead != 0) 2585 { 2586 lprintf(LOG_ERR, "ipmitool: incomplete record found in file."); 2587 ret = -1; 2588 } 2589 2590 break; 2591 } 2592 2593 } while (1); 2594 fclose(fp); 2595 } 2596 else 2597 { 2598 lprintf(LOG_ERR, "ipmitool: could not open input file."); 2599 ret = -1; 2600 } 2601 return ret; 2602 } 2603 2604 2605 2606 static uint16_t 2607 ipmi_sel_reserve(struct ipmi_intf * intf) 2608 { 2609 struct ipmi_rs * rsp; 2610 struct ipmi_rq req; 2611 2612 memset(&req, 0, sizeof(req)); 2613 req.msg.netfn = IPMI_NETFN_STORAGE; 2614 req.msg.cmd = IPMI_CMD_RESERVE_SEL; 2615 2616 rsp = intf->sendrecv(intf, &req); 2617 if (rsp == NULL) { 2618 lprintf(LOG_WARN, "Unable to reserve SEL"); 2619 return 0; 2620 } 2621 if (rsp->ccode > 0) { 2622 printf("Unable to reserve SEL: %s", 2623 val2str(rsp->ccode, completion_code_vals)); 2624 return 0; 2625 } 2626 2627 return (rsp->data[0] | (rsp->data[1] << 8)); 2628 } 2629 2630 2631 2632 /* 2633 * ipmi_sel_get_time 2634 * 2635 * return 0 on success, 2636 * -1 on error 2637 */ 2638 static int 2639 ipmi_sel_get_time(struct ipmi_intf * intf) 2640 { 2641 struct ipmi_rs * rsp; 2642 struct ipmi_rq req; 2643 static char tbuf[40]; 2644 uint32_t timei; 2645 time_t time; 2646 2647 memset(&req, 0, sizeof(req)); 2648 req.msg.netfn = IPMI_NETFN_STORAGE; 2649 req.msg.cmd = IPMI_GET_SEL_TIME; 2650 2651 rsp = intf->sendrecv(intf, &req); 2652 2653 if (rsp == NULL) { 2654 lprintf(LOG_ERR, "Get SEL Time command failed"); 2655 return -1; 2656 } 2657 if (rsp->ccode > 0) { 2658 lprintf(LOG_ERR, "Get SEL Time command failed: %s", 2659 val2str(rsp->ccode, completion_code_vals)); 2660 return -1; 2661 } 2662 if (rsp->data_len != 4) { 2663 lprintf(LOG_ERR, "Get SEL Time command failed: " 2664 "Invalid data length %d", rsp->data_len); 2665 return -1; 2666 } 2667 2668 memcpy(&timei, rsp->data, 4); 2669 #if WORDS_BIGENDIAN 2670 time = (time_t)(BSWAP_32(timei)); 2671 #else 2672 time = (time_t)timei; 2673 #endif 2674 2675 strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&time)); 2676 printf("%s\n", tbuf); 2677 2678 return 0; 2679 } 2680 2681 2682 2683 /* 2684 * ipmi_sel_set_time 2685 * 2686 * return 0 on success, 2687 * -1 on error 2688 */ 2689 static int 2690 ipmi_sel_set_time(struct ipmi_intf * intf, const char * time_string) 2691 { 2692 struct ipmi_rs * rsp; 2693 struct ipmi_rq req; 2694 struct tm tm = {0}; 2695 time_t t; 2696 uint32_t timei; 2697 const char * time_format = "%m/%d/%Y %H:%M:%S"; 2698 2699 memset(&req, 0, sizeof(req)); 2700 req.msg.netfn = IPMI_NETFN_STORAGE; 2701 req.msg.cmd = IPMI_SET_SEL_TIME; 2702 2703 /* See if user requested set to current client system time */ 2704 if (strncasecmp(time_string, "now", 3) == 0) { 2705 t = time(NULL); 2706 } 2707 else { 2708 /* Now how do we get our time_t from our ascii version? */ 2709 if (strptime(time_string, time_format, &tm) == 0) { 2710 lprintf(LOG_ERR, "Specified time could not be parsed"); 2711 return -1; 2712 } 2713 tm.tm_isdst = (-1); /* look up DST information */ 2714 t = mktime(&tm); 2715 if (t < 0) { 2716 lprintf(LOG_ERR, "Specified time could not be parsed"); 2717 return -1; 2718 } 2719 } 2720 2721 { 2722 //modify UTC time to local time expressed in number of seconds from 1/1/70 0:0:0 1970 GMT 2723 struct tm * tm_tmp = {0}; 2724 int gt_year,gt_yday,gt_hour,lt_year,lt_yday,lt_hour; 2725 int delta_hour; 2726 tm_tmp=gmtime(&t); 2727 gt_year=tm_tmp->tm_year; 2728 gt_yday=tm_tmp->tm_yday; 2729 gt_hour=tm_tmp->tm_hour; 2730 memset(&*tm_tmp, 0, sizeof(struct tm)); 2731 tm_tmp=localtime(&t); 2732 lt_year=tm_tmp->tm_year; 2733 lt_yday=tm_tmp->tm_yday; 2734 lt_hour=tm_tmp->tm_hour; 2735 delta_hour=lt_hour - gt_hour; 2736 if ( (lt_year > gt_year) || ((lt_year == gt_year) && (lt_yday > gt_yday)) ) 2737 delta_hour += 24; 2738 if ( (lt_year < gt_year) || ((lt_year == gt_year) && (lt_yday < gt_yday)) ) 2739 delta_hour -= 24; 2740 2741 t += (delta_hour * 60 * 60); 2742 } 2743 2744 timei = (uint32_t)t; 2745 req.msg.data = (uint8_t *)&timei; 2746 req.msg.data_len = 4; 2747 2748 #if WORDS_BIGENDIAN 2749 timei = BSWAP_32(timei); 2750 #endif 2751 2752 rsp = intf->sendrecv(intf, &req); 2753 if (rsp == NULL) { 2754 lprintf(LOG_ERR, "Set SEL Time command failed"); 2755 return -1; 2756 } 2757 if (rsp->ccode > 0) { 2758 lprintf(LOG_ERR, "Set SEL Time command failed: %s", 2759 val2str(rsp->ccode, completion_code_vals)); 2760 return -1; 2761 } 2762 2763 ipmi_sel_get_time(intf); 2764 2765 return 0; 2766 } 2767 2768 2769 2770 static int 2771 ipmi_sel_clear(struct ipmi_intf * intf) 2772 { 2773 struct ipmi_rs * rsp; 2774 struct ipmi_rq req; 2775 uint16_t reserve_id; 2776 uint8_t msg_data[6]; 2777 2778 reserve_id = ipmi_sel_reserve(intf); 2779 if (reserve_id == 0) 2780 return -1; 2781 2782 memset(msg_data, 0, 6); 2783 msg_data[0] = reserve_id & 0xff; 2784 msg_data[1] = reserve_id >> 8; 2785 msg_data[2] = 'C'; 2786 msg_data[3] = 'L'; 2787 msg_data[4] = 'R'; 2788 msg_data[5] = 0xaa; 2789 2790 memset(&req, 0, sizeof(req)); 2791 req.msg.netfn = IPMI_NETFN_STORAGE; 2792 req.msg.cmd = IPMI_CMD_CLEAR_SEL; 2793 req.msg.data = msg_data; 2794 req.msg.data_len = 6; 2795 2796 rsp = intf->sendrecv(intf, &req); 2797 if (rsp == NULL) { 2798 lprintf(LOG_ERR, "Unable to clear SEL"); 2799 return -1; 2800 } 2801 if (rsp->ccode > 0) { 2802 lprintf(LOG_ERR, "Unable to clear SEL: %s", 2803 val2str(rsp->ccode, completion_code_vals)); 2804 return -1; 2805 } 2806 2807 printf("Clearing SEL. Please allow a few seconds to erase.\n"); 2808 return 0; 2809 } 2810 2811 static int 2812 ipmi_sel_delete(struct ipmi_intf * intf, int argc, char ** argv) 2813 { 2814 struct ipmi_rs * rsp; 2815 struct ipmi_rq req; 2816 uint16_t id; 2817 uint8_t msg_data[4]; 2818 int rc = 0; 2819 2820 if (argc == 0 || strncmp(argv[0], "help", 4) == 0) { 2821 lprintf(LOG_ERR, "usage: delete <id>...<id>\n"); 2822 return -1; 2823 } 2824 2825 id = ipmi_sel_reserve(intf); 2826 if (id == 0) 2827 return -1; 2828 2829 memset(msg_data, 0, 4); 2830 msg_data[0] = id & 0xff; 2831 msg_data[1] = id >> 8; 2832 2833 for (; argc != 0; argc--) 2834 { 2835 id = (uint16_t) strtoul(argv[argc-1], NULL, 0); 2836 if (str2ushort(argv[argc-1], &id) != 0) { 2837 lprintf(LOG_ERR, "Given SEL ID '%s' is invalid.", 2838 argv[argc-1]); 2839 rc = (-1); 2840 continue; 2841 } 2842 msg_data[2] = id & 0xff; 2843 msg_data[3] = id >> 8; 2844 2845 memset(&req, 0, sizeof(req)); 2846 req.msg.netfn = IPMI_NETFN_STORAGE; 2847 req.msg.cmd = IPMI_CMD_DELETE_SEL_ENTRY; 2848 req.msg.data = msg_data; 2849 req.msg.data_len = 4; 2850 2851 rsp = intf->sendrecv(intf, &req); 2852 if (rsp == NULL) { 2853 lprintf(LOG_ERR, "Unable to delete entry %d", id); 2854 rc = -1; 2855 } 2856 else if (rsp->ccode > 0) { 2857 lprintf(LOG_ERR, "Unable to delete entry %d: %s", id, 2858 val2str(rsp->ccode, completion_code_vals)); 2859 rc = -1; 2860 } 2861 else { 2862 printf("Deleted entry %d\n", id); 2863 } 2864 } 2865 2866 return rc; 2867 } 2868 2869 static int 2870 ipmi_sel_show_entry(struct ipmi_intf * intf, int argc, char ** argv) 2871 { 2872 uint16_t id; 2873 int i, oldv; 2874 struct sel_event_record evt; 2875 struct sdr_record_list * sdr; 2876 struct entity_id entity; 2877 struct sdr_record_list * list, * entry; 2878 int rc = 0; 2879 2880 if (argc == 0 || strncmp(argv[0], "help", 4) == 0) { 2881 lprintf(LOG_ERR, "usage: sel get <id>...<id>"); 2882 return -1; 2883 } 2884 2885 if (ipmi_sel_reserve(intf) == 0) { 2886 lprintf(LOG_ERR, "Unable to reserve SEL"); 2887 return -1; 2888 } 2889 2890 for (i=0; i<argc; i++) { 2891 if (str2ushort(argv[i], &id) != 0) { 2892 lprintf(LOG_ERR, "Given SEL ID '%s' is invalid.", 2893 argv[i]); 2894 rc = (-1); 2895 continue; 2896 } 2897 2898 lprintf(LOG_DEBUG, "Looking up SEL entry 0x%x", id); 2899 2900 /* lookup SEL entry based on ID */ 2901 if (!ipmi_sel_get_std_entry(intf, id, &evt)) { 2902 lprintf(LOG_DEBUG, "SEL Entry 0x%x not found.", id); 2903 rc = (-1); 2904 continue; 2905 } 2906 if (evt.sel_type.standard_type.sensor_num == 0 && evt.sel_type.standard_type.sensor_type == 0 && evt.record_type == 0) { 2907 lprintf(LOG_WARN, "SEL Entry 0x%x not found", id); 2908 rc = -1; 2909 continue; 2910 } 2911 2912 /* lookup SDR entry based on sensor number and type */ 2913 ipmi_sel_print_extended_entry_verbose(intf, &evt); 2914 2915 sdr = ipmi_sdr_find_sdr_bynumtype(intf, evt.sel_type.standard_type.gen_id, evt.sel_type.standard_type.sensor_num, evt.sel_type.standard_type.sensor_type); 2916 if (sdr == NULL) { 2917 continue; 2918 } 2919 2920 /* print SDR entry */ 2921 oldv = verbose; 2922 verbose = verbose ? : 1; 2923 switch (sdr->type) { 2924 case SDR_RECORD_TYPE_FULL_SENSOR: 2925 case SDR_RECORD_TYPE_COMPACT_SENSOR: 2926 ipmi_sensor_print_fc(intf, sdr->record.common, 2927 sdr->type); 2928 entity.id = sdr->record.common->entity.id; 2929 entity.instance = sdr->record.common->entity.instance; 2930 break; 2931 case SDR_RECORD_TYPE_EVENTONLY_SENSOR: 2932 ipmi_sdr_print_sensor_eventonly(intf, sdr->record.eventonly); 2933 entity.id = sdr->record.eventonly->entity.id; 2934 entity.instance = sdr->record.eventonly->entity.instance; 2935 break; 2936 default: 2937 verbose = oldv; 2938 continue; 2939 } 2940 verbose = oldv; 2941 2942 /* lookup SDR entry based on entity id */ 2943 list = ipmi_sdr_find_sdr_byentity(intf, &entity); 2944 for (entry=list; entry; entry=entry->next) { 2945 /* print FRU devices we find for this entity */ 2946 if (entry->type == SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR) 2947 ipmi_fru_print(intf, entry->record.fruloc); 2948 } 2949 2950 if ((argc > 1) && (i<(argc-1))) 2951 printf("----------------------\n\n"); 2952 } 2953 2954 return rc; 2955 } 2956 2957 int ipmi_sel_main(struct ipmi_intf * intf, int argc, char ** argv) 2958 { 2959 int rc = 0; 2960 2961 if (argc == 0) 2962 rc = ipmi_sel_get_info(intf); 2963 else if (strncmp(argv[0], "help", 4) == 0) 2964 lprintf(LOG_ERR, "SEL Commands: " 2965 "info clear delete list elist get add time save readraw writeraw interpret"); 2966 else if (strncmp(argv[0], "interpret", 9) == 0) { 2967 uint32_t iana = 0; 2968 if (argc < 4) { 2969 lprintf(LOG_NOTICE, "usage: sel interpret iana filename format(pps)"); 2970 return 0; 2971 } 2972 if (str2uint(argv[1], &iana) != 0) { 2973 lprintf(LOG_ERR, "Given IANA '%s' is invalid.", 2974 argv[1]); 2975 return (-1); 2976 } 2977 rc = ipmi_sel_interpret(intf, iana, argv[2], argv[3]); 2978 } 2979 else if (strncmp(argv[0], "info", 4) == 0) 2980 rc = ipmi_sel_get_info(intf); 2981 else if (strncmp(argv[0], "save", 4) == 0) { 2982 if (argc < 2) { 2983 lprintf(LOG_NOTICE, "usage: sel save <filename>"); 2984 return 0; 2985 } 2986 rc = ipmi_sel_save_entries(intf, 0, argv[1]); 2987 } 2988 else if (strncmp(argv[0], "add", 3) == 0) { 2989 if (argc < 2) { 2990 lprintf(LOG_NOTICE, "usage: sel add <filename>"); 2991 return 0; 2992 } 2993 rc = ipmi_sel_add_entries_fromfile(intf, argv[1]); 2994 } 2995 else if (strncmp(argv[0], "writeraw", 8) == 0) { 2996 if (argc < 2) { 2997 lprintf(LOG_NOTICE, "usage: sel writeraw <filename>"); 2998 return 0; 2999 } 3000 rc = ipmi_sel_writeraw(intf, argv[1]); 3001 } 3002 else if (strncmp(argv[0], "readraw", 7) == 0) { 3003 if (argc < 2) { 3004 lprintf(LOG_NOTICE, "usage: sel readraw <filename>"); 3005 return 0; 3006 } 3007 rc = ipmi_sel_readraw(intf, argv[1]); 3008 } 3009 else if (strncmp(argv[0], "ereadraw", 8) == 0) { 3010 if (argc < 2) { 3011 lprintf(LOG_NOTICE, "usage: sel ereadraw <filename>"); 3012 return 0; 3013 } 3014 sel_extended = 1; 3015 rc = ipmi_sel_readraw(intf, argv[1]); 3016 } 3017 else if (strncmp(argv[0], "list", 4) == 0 || 3018 strncmp(argv[0], "elist", 5) == 0) { 3019 /* 3020 * Usage: 3021 * list - show all SEL entries 3022 * list first <n> - show the first (oldest) <n> SEL entries 3023 * list last <n> - show the last (newsest) <n> SEL entries 3024 */ 3025 int count = 0; 3026 int sign = 1; 3027 char *countstr = NULL; 3028 3029 if (strncmp(argv[0], "elist", 5) == 0) 3030 sel_extended = 1; 3031 else 3032 sel_extended = 0; 3033 3034 if (argc == 2) { 3035 countstr = argv[1]; 3036 } 3037 else if (argc == 3) { 3038 countstr = argv[2]; 3039 3040 if (strncmp(argv[1], "last", 4) == 0) { 3041 sign = -1; 3042 } 3043 else if (strncmp(argv[1], "first", 5) != 0) { 3044 lprintf(LOG_ERR, "Unknown sel list option"); 3045 return -1; 3046 } 3047 } 3048 3049 if (countstr) { 3050 if (str2int(countstr, &count) != 0) { 3051 lprintf(LOG_ERR, "Numeric argument required; got '%s'", 3052 countstr); 3053 return -1; 3054 } 3055 } 3056 count *= sign; 3057 3058 rc = ipmi_sel_list_entries(intf,count); 3059 } 3060 else if (strncmp(argv[0], "clear", 5) == 0) 3061 rc = ipmi_sel_clear(intf); 3062 else if (strncmp(argv[0], "delete", 6) == 0) { 3063 if (argc < 2) 3064 lprintf(LOG_ERR, "usage: sel delete <id>...<id>"); 3065 else 3066 rc = ipmi_sel_delete(intf, argc-1, &argv[1]); 3067 } 3068 else if (strncmp(argv[0], "get", 3) == 0) { 3069 if (argc < 2) 3070 lprintf(LOG_ERR, "usage: sel get <entry>"); 3071 else 3072 rc = ipmi_sel_show_entry(intf, argc-1, &argv[1]); 3073 } 3074 else if (strncmp(argv[0], "time", 4) == 0) { 3075 if (argc < 2) 3076 lprintf(LOG_ERR, "sel time commands: get set"); 3077 else if (strncmp(argv[1], "get", 3) == 0) 3078 ipmi_sel_get_time(intf); 3079 else if (strncmp(argv[1], "set", 3) == 0) { 3080 if (argc < 3) 3081 lprintf(LOG_ERR, "usage: sel time set \"mm/dd/yyyy hh:mm:ss\""); 3082 else 3083 rc = ipmi_sel_set_time(intf, argv[2]); 3084 } else { 3085 lprintf(LOG_ERR, "sel time commands: get set"); 3086 } 3087 } 3088 else { 3089 lprintf(LOG_ERR, "Invalid SEL command: %s", argv[0]); 3090 rc = -1; 3091 } 3092 3093 return rc; 3094 } 3095