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