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