diff options
Diffstat (limited to 'lib/ipmi_event.c')
-rw-r--r-- | lib/ipmi_event.c | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/lib/ipmi_event.c b/lib/ipmi_event.c new file mode 100644 index 0000000..2f1032e --- /dev/null +++ b/lib/ipmi_event.c @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_event.h> +#include <ipmitool/ipmi_sdr.h> + + +static void +ipmi_event_msg_print(struct ipmi_intf * intf, struct platform_event_msg * pmsg) +{ + struct sel_event_record sel_event; + + memset(&sel_event, 0, sizeof(struct sel_event_record)); + + sel_event.record_id = 0; + sel_event.sel_type.standard_type.gen_id = 2; + + sel_event.sel_type.standard_type.evm_rev = pmsg->evm_rev; + sel_event.sel_type.standard_type.sensor_type = pmsg->sensor_type; + sel_event.sel_type.standard_type.sensor_num = pmsg->sensor_num; + sel_event.sel_type.standard_type.event_type = pmsg->event_type; + sel_event.sel_type.standard_type.event_dir = pmsg->event_dir; + sel_event.sel_type.standard_type.event_data[0] = pmsg->event_data[0]; + sel_event.sel_type.standard_type.event_data[1] = pmsg->event_data[1]; + sel_event.sel_type.standard_type.event_data[2] = pmsg->event_data[2]; + + if (verbose) + ipmi_sel_print_extended_entry_verbose(intf, &sel_event); + else + ipmi_sel_print_extended_entry(intf, &sel_event); +} + +static int +ipmi_send_platform_event(struct ipmi_intf * intf, struct platform_event_msg * emsg) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t rqdata[8]; + uint8_t chmed; + + memset(&req, 0, sizeof(req)); + memset(rqdata, 0, 8); + + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = 0x02; + req.msg.data = rqdata; + + chmed = ipmi_current_channel_medium(intf); + if (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) { + /* system interface, need extra generator ID */ + req.msg.data_len = 8; + rqdata[0] = 0x41; // As per Fig. 29-2 and Table 5-4 + memcpy(rqdata+1, emsg, sizeof(struct platform_event_msg)); + } + else { + req.msg.data_len = 7; + memcpy(rqdata, emsg, sizeof(struct platform_event_msg)); + } + + ipmi_event_msg_print(intf, emsg); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Platform Event Message command failed"); + return -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Platform Event Message command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + return 0; +} + +#define EVENT_THRESH_STATE_LNC_LO 0 +#define EVENT_THRESH_STATE_LNC_HI 1 +#define EVENT_THRESH_STATE_LCR_LO 2 +#define EVENT_THRESH_STATE_LCR_HI 3 +#define EVENT_THRESH_STATE_LNR_LO 4 +#define EVENT_THRESH_STATE_LNR_HI 5 +#define EVENT_THRESH_STATE_UNC_LO 6 +#define EVENT_THRESH_STATE_UNC_HI 7 +#define EVENT_THRESH_STATE_UCR_LO 8 +#define EVENT_THRESH_STATE_UCR_HI 9 +#define EVENT_THRESH_STATE_UNR_LO 10 +#define EVENT_THRESH_STATE_UNR_HI 11 + +static const struct valstr ipmi_event_thresh_lo[] = { + { EVENT_THRESH_STATE_LNC_LO, "lnc" }, + { EVENT_THRESH_STATE_LCR_LO, "lcr" }, + { EVENT_THRESH_STATE_LNR_LO, "lnr" }, + { EVENT_THRESH_STATE_UNC_LO, "unc" }, + { EVENT_THRESH_STATE_UCR_LO, "ucr" }, + { EVENT_THRESH_STATE_UNR_LO, "unr" }, + { 0, NULL }, +}; +static const struct valstr ipmi_event_thresh_hi[] = { + { EVENT_THRESH_STATE_LNC_HI, "lnc" }, + { EVENT_THRESH_STATE_LCR_HI, "lcr" }, + { EVENT_THRESH_STATE_LNR_HI, "lnr" }, + { EVENT_THRESH_STATE_UNC_HI, "unc" }, + { EVENT_THRESH_STATE_UCR_HI, "ucr" }, + { EVENT_THRESH_STATE_UNR_HI, "unr" }, + { 0, NULL }, +}; + +static int +ipmi_send_platform_event_num(struct ipmi_intf * intf, int num) +{ + struct platform_event_msg emsg; + + memset(&emsg, 0, sizeof(struct platform_event_msg)); + + /* IPMB/LAN/etc */ + switch (num) { + case 1: /* temperature */ + printf("Sending SAMPLE event: Temperature - " + "Upper Critical - Going High\n"); + emsg.evm_rev = 0x04; + emsg.sensor_type = 0x01; + emsg.sensor_num = 0x30; + emsg.event_dir = EVENT_DIR_ASSERT; + emsg.event_type = 0x01; + emsg.event_data[0] = EVENT_THRESH_STATE_UCR_HI; + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + break; + case 2: /* voltage error */ + printf("Sending SAMPLE event: Voltage Threshold - " + "Lower Critical - Going Low\n"); + emsg.evm_rev = 0x04; + emsg.sensor_type = 0x02; + emsg.sensor_num = 0x60; + emsg.event_dir = EVENT_DIR_ASSERT; + emsg.event_type = 0x01; + emsg.event_data[0] = EVENT_THRESH_STATE_LCR_LO; + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + break; + case 3: /* correctable ECC */ + printf("Sending SAMPLE event: Memory - Correctable ECC\n"); + emsg.evm_rev = 0x04; + emsg.sensor_type = 0x0c; + emsg.sensor_num = 0x53; + emsg.event_dir = EVENT_DIR_ASSERT; + emsg.event_type = 0x6f; + emsg.event_data[0] = 0x00; + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + break; + default: + lprintf(LOG_ERR, "Invalid event number: %d", num); + return -1; + } + + return ipmi_send_platform_event(intf, &emsg); +} + +static int +ipmi_event_find_offset(uint8_t code, + struct ipmi_event_sensor_types * evt, + char * desc) +{ + if (desc == NULL || code == 0) + return 0x00; + + while (evt->type) { + if (evt->code == code && evt->desc != NULL && + strncasecmp(desc, evt->desc, __maxlen(desc, evt->desc)) == 0) + return evt->offset; + evt++; + } + + lprintf(LOG_WARN, "Unable to find matching event offset for '%s'", desc); + return -1; +} + +static void +print_sensor_states(uint8_t sensor_type, uint8_t event_type) +{ + ipmi_sdr_print_discrete_state_mini( + "Sensor States: \n ", "\n ", sensor_type, + event_type, 0xff, 0xff); + printf("\n"); +} + + +static int +ipmi_event_fromsensor(struct ipmi_intf * intf, char * id, char * state, char * evdir) +{ + struct ipmi_rs * rsp; + struct sdr_record_list * sdr; + struct platform_event_msg emsg; + int off; + uint8_t target, lun, channel; + + if (id == NULL) { + lprintf(LOG_ERR, "No sensor ID supplied"); + return -1; + } + + memset(&emsg, 0, sizeof(struct platform_event_msg)); + emsg.evm_rev = 0x04; + + if (evdir == NULL) + emsg.event_dir = EVENT_DIR_ASSERT; + else if (strncasecmp(evdir, "assert", 6) == 0) + emsg.event_dir = EVENT_DIR_ASSERT; + else if (strncasecmp(evdir, "deassert", 8) == 0) + emsg.event_dir = EVENT_DIR_DEASSERT; + else { + lprintf(LOG_ERR, "Invalid event direction %s. Must be 'assert' or 'deassert'", evdir); + return -1; + } + + printf("Finding sensor %s... ", id); + sdr = ipmi_sdr_find_sdr_byid(intf, id); + if (sdr == NULL) { + printf("not found!\n"); + return -1; + } + printf("ok\n"); + + switch (sdr->type) + { + case SDR_RECORD_TYPE_FULL_SENSOR: + case SDR_RECORD_TYPE_COMPACT_SENSOR: + + emsg.sensor_type = sdr->record.common->sensor.type; + emsg.sensor_num = sdr->record.common->keys.sensor_num; + emsg.event_type = sdr->record.common->event_type; + target = sdr->record.common->keys.owner_id; + lun = sdr->record.common->keys.lun; + channel = sdr->record.common->keys.channel; + break; + default: + lprintf(LOG_ERR, "Unknown sensor type for id '%s'", id); + return -1; + } + + emsg.event_data[1] = 0xff; + emsg.event_data[2] = 0xff; + + switch (emsg.event_type) + { + /* + * Threshold Class + */ + case 1: + { + int dir = 0; + int hilo = 0; + off = 1; + + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + printf("Sensor States:\n"); + printf(" lnr : Lower Non-Recoverable \n"); + printf(" lcr : Lower Critical\n"); + printf(" lnc : Lower Non-Critical\n"); + printf(" unc : Upper Non-Critical\n"); + printf(" ucr : Upper Critical\n"); + printf(" unr : Upper Non-Recoverable\n"); + return -1; + } + + if (0 != strncasecmp(state, "lnr", 3) && + 0 != strncasecmp(state, "lcr", 3) && + 0 != strncasecmp(state, "lnc", 3) && + 0 != strncasecmp(state, "unc", 3) && + 0 != strncasecmp(state, "ucr", 3) && + 0 != strncasecmp(state, "unr", 3)) + { + lprintf(LOG_ERR, "Invalid threshold identifier %s", state); + return -1; + } + + if (state[0] == 'u') + hilo = 1; + else + hilo = 0; + + if (emsg.event_dir == EVENT_DIR_ASSERT) + dir = hilo; + else + dir = !hilo; + + if ((emsg.event_dir == EVENT_DIR_ASSERT && hilo == 1) || + (emsg.event_dir == EVENT_DIR_DEASSERT && hilo == 0)) + emsg.event_data[0] = (uint8_t)(str2val(state, ipmi_event_thresh_hi) & 0xf); + else if ((emsg.event_dir == EVENT_DIR_ASSERT && hilo == 0) || + (emsg.event_dir == EVENT_DIR_DEASSERT && hilo == 1)) + emsg.event_data[0] = (uint8_t)(str2val(state, ipmi_event_thresh_lo) & 0xf); + else { + lprintf(LOG_ERR, "Invalid Event"); + return -1; + } + + rsp = ipmi_sdr_get_sensor_thresholds(intf, emsg.sensor_num, + target, lun, channel); + if (rsp == NULL) { + lprintf(LOG_ERR, + "Command Get Sensor Thresholds failed: invalid response."); + return (-1); + } else if (rsp->ccode != 0) { + lprintf(LOG_ERR, "Command Get Sensor Thresholds failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return (-1); + } + + /* threshold reading */ + emsg.event_data[2] = rsp->data[(emsg.event_data[0] / 2) + 1]; + + rsp = ipmi_sdr_get_sensor_hysteresis(intf, emsg.sensor_num, + target, lun, channel); + if (rsp != NULL && rsp->ccode == 0) + off = dir ? rsp->data[0] : rsp->data[1]; + if (off <= 0) + off = 1; + + /* trigger reading */ + if (dir) { + if ((emsg.event_data[2] + off) > 0xff) + emsg.event_data[1] = 0xff; + else + emsg.event_data[1] = emsg.event_data[2] + off; + } + else { + if ((emsg.event_data[2] - off) < 0) + emsg.event_data[1] = 0; + else + emsg.event_data[1] = emsg.event_data[2] - off; + } + + /* trigger in byte 2, threshold in byte 3 */ + emsg.event_data[0] |= 0x50; + } + break; + + /* + * Digital Discrete + */ + case 3: case 4: case 5: case 6: case 8: case 9: + { + int x; + const char * digi_on[] = { "present", "assert", "limit", + "fail", "yes", "on", "up" }; + const char * digi_off[] = { "absent", "deassert", "nolimit", + "nofail", "no", "off", "down" }; + /* + * print list of available states for this sensor + */ + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + print_sensor_states(emsg.sensor_type, emsg.event_type); + printf("Sensor State Shortcuts:\n"); + for (x = 0; x < sizeof(digi_on)/sizeof(*digi_on); x++) { + printf(" %-9s %-9s\n", digi_on[x], digi_off[x]); + } + return 0; + } + + off = 0; + for (x = 0; x < sizeof(digi_on)/sizeof(*digi_on); x++) { + if (strncasecmp(state, digi_on[x], strlen(digi_on[x])) == 0) { + emsg.event_data[0] = 1; + off = 1; + break; + } + else if (strncasecmp(state, digi_off[x], strlen(digi_off[x])) == 0) { + emsg.event_data[0] = 0; + off = 1; + break; + } + } + if (off == 0) { + off = ipmi_event_find_offset( + emsg.event_type, generic_event_types, state); + if (off < 0) + return -1; + emsg.event_data[0] = off; + } + } + break; + + /* + * Generic Discrete + */ + case 2: case 7: case 10: case 11: case 12: + { + /* + * print list of available states for this sensor + */ + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + print_sensor_states(emsg.sensor_type, emsg.event_type); + return 0; + } + off = ipmi_event_find_offset( + emsg.event_type, generic_event_types, state); + if (off < 0) + return -1; + emsg.event_data[0] = off; + } + break; + + /* + * Sensor-Specific Discrete + */ + case 0x6f: + { + /* + * print list of available states for this sensor + */ + if (state == NULL || strncasecmp(state, "list", 4) == 0) { + print_sensor_states(emsg.sensor_type, emsg.event_type); + return 0; + } + off = ipmi_event_find_offset( + emsg.sensor_type, sensor_specific_types, state); + if (off < 0) + return -1; + emsg.event_data[0] = off; + } + break; + + default: + return -1; + + } + + return ipmi_send_platform_event(intf, &emsg); +} + +static int +ipmi_event_fromfile(struct ipmi_intf * intf, char * file) +{ + FILE * fp; + struct ipmi_rs * rsp; + struct ipmi_rq req; + struct sel_event_record sel_event; + uint8_t rqdata[8]; + char buf[1024]; + char * ptr, * tok; + int i, j; + uint8_t chmed; + int rc = 0; + + if (file == NULL) + return -1; + + memset(rqdata, 0, 8); + + /* setup Platform Event Message command */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_SE; + req.msg.cmd = 0x02; + req.msg.data = rqdata; + req.msg.data_len = 7; + + chmed = ipmi_current_channel_medium(intf); + if (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) { + /* system interface, need extra generator ID */ + rqdata[0] = 0x41; // As per Fig. 29-2 and Table 5-4 + req.msg.data_len = 8; + } + + fp = ipmi_open_file_read(file); + if (fp == NULL) + return -1; + + while (feof(fp) == 0) { + if (fgets(buf, 1024, fp) == NULL) + continue; + + /* clip off optional comment tail indicated by # */ + ptr = strchr(buf, '#'); + if (ptr) + *ptr = '\0'; + else + ptr = buf + strlen(buf); + + /* clip off trailing and leading whitespace */ + ptr--; + while (isspace((int)*ptr) && ptr >= buf) + *ptr-- = '\0'; + ptr = buf; + while (isspace((int)*ptr)) + ptr++; + if (strlen(ptr) == 0) + continue; + + /* parse the event, 7 bytes with optional comment */ + /* 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # event */ + i = 0; + tok = strtok(ptr, " "); + while (tok) { + if (i == 7) + break; + j = i++; + if (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) + j++; + rqdata[j] = (uint8_t)strtol(tok, NULL, 0); + tok = strtok(NULL, " "); + } + if (i < 7) { + lprintf(LOG_ERR, "Invalid Event: %s", + buf2str(rqdata, sizeof(rqdata))); + continue; + } + + memset(&sel_event, 0, sizeof(struct sel_event_record)); + sel_event.record_id = 0; + sel_event.sel_type.standard_type.gen_id = 2; + + j = (chmed == IPMI_CHANNEL_MEDIUM_SYSTEM) ? 1 : 0; + sel_event.sel_type.standard_type.evm_rev = rqdata[j++]; + sel_event.sel_type.standard_type.sensor_type = rqdata[j++]; + sel_event.sel_type.standard_type.sensor_num = rqdata[j++]; + sel_event.sel_type.standard_type.event_type = rqdata[j] & 0x7f; + sel_event.sel_type.standard_type.event_dir = (rqdata[j++] & 0x80) >> 7; + sel_event.sel_type.standard_type.event_data[0] = rqdata[j++]; + sel_event.sel_type.standard_type.event_data[1] = rqdata[j++]; + sel_event.sel_type.standard_type.event_data[2] = rqdata[j++]; + + ipmi_sel_print_std_entry(intf, &sel_event); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Platform Event Message command failed"); + rc = -1; + } + else if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Platform Event Message command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + rc = -1; + } + } + + fclose(fp); + return rc; +} + +static void +ipmi_event_usage(void) +{ + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "usage: event <num>"); + lprintf(LOG_NOTICE, " Send generic test events"); + lprintf(LOG_NOTICE, " 1 : Temperature - Upper Critical - Going High"); + lprintf(LOG_NOTICE, " 2 : Voltage Threshold - Lower Critical - Going Low"); + lprintf(LOG_NOTICE, " 3 : Memory - Correctable ECC"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "usage: event file <filename>"); + lprintf(LOG_NOTICE, " Read and generate events from file"); + lprintf(LOG_NOTICE, " Use the 'sel save' command to generate from SEL"); + lprintf(LOG_NOTICE, ""); + lprintf(LOG_NOTICE, "usage: event <sensorid> <state> [event_dir]"); + lprintf(LOG_NOTICE, " sensorid : Sensor ID string to use for event data"); + lprintf(LOG_NOTICE, " state : Sensor state, use 'list' to see possible states for sensor"); + lprintf(LOG_NOTICE, " event_dir : assert, deassert [default=assert]"); + lprintf(LOG_NOTICE, ""); +} + +int +ipmi_event_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int rc = 0; + + if (argc == 0 || strncmp(argv[0], "help", 4) == 0) { + ipmi_event_usage(); + return 0; + } + if (strncmp(argv[0], "file", 4) == 0) { + if (argc < 2) { + ipmi_event_usage(); + return 0; + } + return ipmi_event_fromfile(intf, argv[1]); + } + if (strlen(argv[0]) == 1) { + switch (argv[0][0]) { + case '1': return ipmi_send_platform_event_num(intf, 1); + case '2': return ipmi_send_platform_event_num(intf, 2); + case '3': return ipmi_send_platform_event_num(intf, 3); + } + } + if (argc < 2) + rc = ipmi_event_fromsensor(intf, argv[0], NULL, NULL); + else if (argc < 3) + rc = ipmi_event_fromsensor(intf, argv[0], argv[1], NULL); + else + rc = ipmi_event_fromsensor(intf, argv[0], argv[1], argv[2]); + + return rc; +} |