From 2a78ff24820f2fc6a956874327abe85271159ba3 Mon Sep 17 00:00:00 2001 From: Alexander Amelkin Date: Sat, 16 Jun 2018 20:11:07 +0300 Subject: [PATCH] mc: watchdog: Add `set` command Add `ipmitool mc watchdog set` command in full compliance with IPMI spec 2.0 section 27.6. Setting of all fields is fully supported. Signed-off-by: Alexander Amelkin --- include/ipmitool/ipmi_mc.h | 13 ++ lib/ipmi_mc.c | 288 +++++++++++++++++++++++++++++++++---- 2 files changed, 272 insertions(+), 29 deletions(-) diff --git a/include/ipmitool/ipmi_mc.h b/include/ipmitool/ipmi_mc.h index 0e3c59f..0918f22 100644 --- a/include/ipmitool/ipmi_mc.h +++ b/include/ipmitool/ipmi_mc.h @@ -170,6 +170,19 @@ struct ipm_get_watchdog_rsp { #define IPM_WATCHDOG_CLEAR_BIOS_POST 0x04 #define IPM_WATCHDOG_CLEAR_BIOS_FRB2 0x02 +/* Use */ +#define IPMI_WDT_USE_NOLOG_SHIFT 7 +#define IPMI_WDT_USE_DONTSTOP_SHIFT 6 +#define IPMI_WDT_USE_MASK 0x07 + +/* Pre-timeout interrupt type */ +#define IPMI_WDT_INTR_SHIFT 4 +#define IPMI_WDT_INTR_MASK 0x07 /* Apply to the intr value, not to the data byte */ + +/* Action */ +#define IPMI_WDT_ACTION_MASK 0x07 + + /* IPMI 2.0 command for system information*/ #define IPMI_SET_SYS_INFO 0x58 #define IPMI_GET_SYS_INFO 0x59 diff --git a/lib/ipmi_mc.c b/lib/ipmi_mc.c index 4580bfb..78fba9a 100644 --- a/lib/ipmi_mc.c +++ b/lib/ipmi_mc.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -230,10 +232,33 @@ printf_sysinfo_usage(int full_help) static void print_watchdog_usage(void) { - lprintf(LOG_NOTICE, "usage: watchdog :"); - lprintf(LOG_NOTICE, " get : Get Current Watchdog settings"); - lprintf(LOG_NOTICE, " reset : Restart Watchdog timer based on most recent settings"); - lprintf(LOG_NOTICE, " off : Shut off a running Watchdog timer"); + lprintf(LOG_NOTICE, +"usage: watchdog :\n" +"\n" +" set [ ...]\n" +" Set Watchdog settings\n" +" Options: (* = mandatory)\n" +" timeout=<1-6553> - [0] Initial countdown value, sec\n" +" pretimeout=<1-255> - [0] Pre-timeout interval, sec\n" +" int= - [-] Pre-timeout interrupt type\n" +" use= - [-] Timer use\n" +" clear= - [-] Clear timer use expiration\n" +" flag, can be specified\n" +" multiple times\n" +" action= - [none] Timer action\n" +" nolog - [-] Don't log the timer use\n" +" dontstop - [-] Don't stop the timer\n" +" while applying settings\n" +"\n" +" get\n" +" Get Current settings\n" +"\n" +" reset\n" +" Restart Watchdog timer based on the most recent settings\n" +"\n" +" off\n" +" Shut off a running Watchdog timer" + ); } /* ipmi_mc_get_enables - print out MC enables @@ -625,6 +650,63 @@ static int ipmi_mc_get_selftest(struct ipmi_intf * intf) return rv; } +struct wdt_string_s { + const char *get; /* The name of 'timer use' for `watchdog get` command */ + const char *set; /* The name of 'timer use' for `watchdog set` command */ +}; + + +#define WDTS(g,s) &(const struct wdt_string_s){ (g), (s) } + +const struct wdt_string_s *wdt_use[] = { + WDTS("Reserved", "none"), + WDTS("BIOS FRB2", "frb2"), + WDTS("BIOS/POST", "post"), + WDTS("OS Load", "osload"), + WDTS("SMS/OS", "sms"), + WDTS("OEM", "oem"), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + NULL +}; + +const struct wdt_string_s *wdt_int[] = { + WDTS("None", "none"), + WDTS("SMI", "smi"), + WDTS("NMI/Diagnostic", "nmi"), + WDTS("Messaging", "msg"), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + NULL +}; + +const struct wdt_string_s *wdt_action[] = { + WDTS("No action", "none"), + WDTS("Hard Reset", "reset"), + WDTS("Power Down", "poweroff"), + WDTS("Power Cycle", "cycle"), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + WDTS("Reserved", NULL), + NULL +}; + +int find_set_wdt_string(const struct wdt_string_s *w[], const char *s) +{ + int val = 0; + while (w[val]) { + if (!strcmp(s, w[val]->set)) break; + ++val; + } + if (!w[val]) { + return -1; + } + return val; +} + /* ipmi_mc_get_watchdog * * @intf: ipmi interface @@ -632,29 +714,6 @@ static int ipmi_mc_get_selftest(struct ipmi_intf * intf) * returns 0 on success * returns -1 on error */ - -const char *wdt_use_string[8] = { - "Reserved", - "BIOS FRB2", - "BIOS/POST", - "OS Load", - "SMS/OS", - "OEM", - "Reserved", - "Reserved" -}; - -const char *wdt_action_string[8] = { - "No action", - "Hard Reset", - "Power Down", - "Power Cycle", - "Reserved", - "Reserved", - "Reserved", - "Reserved" -}; - static int ipmi_mc_get_watchdog(struct ipmi_intf * intf) { @@ -682,11 +741,11 @@ ipmi_mc_get_watchdog(struct ipmi_intf * intf) wdt_res = (struct ipm_get_watchdog_rsp *) rsp->data; printf("Watchdog Timer Use: %s (0x%02x)\n", - wdt_use_string[(wdt_res->timer_use & 0x07 )], wdt_res->timer_use); + wdt_use[(wdt_res->timer_use & 0x07 )]->get, wdt_res->timer_use); printf("Watchdog Timer Is: %s\n", wdt_res->timer_use & 0x40 ? "Started/Running" : "Stopped"); printf("Watchdog Timer Actions: %s (0x%02x)\n", - wdt_action_string[(wdt_res->timer_actions&0x07)], wdt_res->timer_actions); + wdt_action[(wdt_res->timer_actions&0x07)]->get, wdt_res->timer_actions); printf("Pre-timeout interval: %d seconds\n", wdt_res->pre_timeout); printf("Timer Expiration Flags: 0x%02x\n", wdt_res->timer_use_exp); printf("Initial Countdown: %i sec\n", @@ -697,6 +756,167 @@ ipmi_mc_get_watchdog(struct ipmi_intf * intf) return 0; } +/* ipmi_mc_set_watchdog + * + * @intf: ipmi interface + * @argc: argument count + * @argv: arguments + * + * returns 0 on success + * returns non-zero (-1 or IPMI completion code) on error + */ +static int +ipmi_mc_set_watchdog(struct ipmi_intf * intf, int argc, char *argv[]) +{ + const int MAX_TIMEOUT = 6553; /* Seconds, makes almost USHRT_MAX when + converted to 100ms intervals */ + const int MAX_PRETIMEOUT = 255; /* Seconds */ + + struct ipmi_rs * rsp; + struct ipmi_rq req = {0}; + unsigned char msg_data[6] = {0}; + struct ipm_get_watchdog_rsp * wdt_res; + uint16_t timeout = 0; + uint8_t pretimeout = 0; + uint8_t intr = 0; + uint8_t use = 0; + uint8_t clear = 0; + uint8_t action = 0; + bool nolog = false; + bool dontstop = false; + int rc = -1; + bool options_error = true; + int i; + + if (!argc || !strcmp(argv[0], "help")) { + goto out; + } + + for (i = 0; i < argc; ++i) { + unsigned long val; + int j; + char *vstr = strchr(argv[i], '='); + if (vstr) + vstr++; /* Point to the value */ + + switch (argv[i][0]) { /* only check the first letter to allow for + shortcuts */ + case 't': /* timeout */ + val = strtoul(vstr, NULL, 10); + if (val < 1 || val > MAX_TIMEOUT) { + lprintf(LOG_ERR, "Timeout value %lu is out of range (1-%d)\n", + val, MAX_TIMEOUT); + goto out; + } + timeout = val * 10; /* Convert seconds to 100ms intervals */ + break; + case 'p': /* pretimeout */ + val = strtoul(vstr, NULL, 10); + if (val < 1 || val > MAX_PRETIMEOUT) { + lprintf(LOG_ERR, "Pretimeout value %lu is out of range (1-%d)\n", + val, MAX_PRETIMEOUT); + goto out; + } + pretimeout = val; /* Convert seconds to 100ms intervals */ + break; + case 'i': /* int */ + if (0 > (val = find_set_wdt_string(wdt_int, vstr))) { + lprintf(LOG_ERR, "Interrupt type '%s' is not valid\n", vstr); + goto out; + } + intr = val; + break; + case 'u': /* use */ + if (0 > (val = find_set_wdt_string(wdt_use, vstr))) { + lprintf(LOG_ERR, "Use '%s' is not valid\n", vstr); + goto out; + } + use = val; + break; + case 'a': /* action */ + if (0 > (val = find_set_wdt_string(wdt_action, vstr))) { + lprintf(LOG_ERR, "Use '%s' is not valid\n", vstr); + goto out; + } + action = val; + break; + case 'c': /* clear */ + if (0 > (val = find_set_wdt_string(wdt_use, vstr))) { + lprintf(LOG_ERR, "Use '%s' is not valid\n", vstr); + goto out; + } + clear |= 1 << val; + break; + case 'n': /* nolog */ + nolog = true; + break; + case 'd': /* dontstop */ + dontstop = true; + break; + + default: + lprintf(LOG_ERR, "Invalid option '%s'", argv[i]); + break; + } + } + + options_error = false; + + /* Fill data bytes according to IPMI 2.0 Spec section 27.6 */ + msg_data[0] = nolog << IPMI_WDT_USE_NOLOG_SHIFT; + msg_data[0] |= dontstop << IPMI_WDT_USE_DONTSTOP_SHIFT; + msg_data[0] |= use & IPMI_WDT_USE_MASK; + + msg_data[1] = (intr & IPMI_WDT_INTR_MASK) << IPMI_WDT_INTR_SHIFT; + msg_data[1] = action & IPMI_WDT_ACTION_MASK; + + msg_data[2] = pretimeout; + + msg_data[3] = clear; + + msg_data[4] = timeout & 0xFF; /* LSB */ + msg_data[5] = timeout >> 8; /* MSB */ + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = BMC_SET_WATCHDOG_TIMER; + req.msg.data_len = 6; + req.msg.data = msg_data; + + lprintf(LOG_INFO, + "Sending Set Watchdog command [%02X %02X %02X %02X %02X %02X]:" + , msg_data[0], msg_data[1], msg_data[2] + , msg_data[3], msg_data[4], msg_data[5] + ); + lprintf(LOG_INFO, " - nolog = %d", nolog); + lprintf(LOG_INFO, " - dontstop = %d", dontstop); + lprintf(LOG_INFO, " - use = 0x%02hhX", use); + lprintf(LOG_INFO, " - intr = 0x%02hhX", intr); + lprintf(LOG_INFO, " - action = 0x%02hhX", action); + lprintf(LOG_INFO, " - pretimeout = %hhu", pretimeout); + lprintf(LOG_INFO, " - clear = 0x%02hhX", clear); + lprintf(LOG_INFO, " - timeout = %hu", timeout); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Watchdog Timer command failed"); + goto out; + } + + rc = rsp->ccode; + if (rc) { + lprintf(LOG_ERR, "Set Watchdog Timer command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + goto out; + } + + lprintf(LOG_NOTICE, "Watchdog Timer was successfully configured"); + +out: + if (options_error) print_watchdog_usage(); + + return rc; +} + /* ipmi_mc_shutoff_watchdog * * @intf: ipmi interface @@ -859,6 +1079,16 @@ ipmi_mc_main(struct ipmi_intf * intf, int argc, char ** argv) print_watchdog_usage(); rc = 0; } + else if (strncmp(argv[1], "set", 3) == 0) { + if (argc < 3) { /* Requires options */ + lprintf(LOG_ERR, "Not enough parameters given."); + print_watchdog_usage(); + rc = (-1); + } + else { + rc = ipmi_mc_set_watchdog(intf, argc - 2, &(argv[2])); + } + } else if (strncmp(argv[1], "get", 3) == 0) { rc = ipmi_mc_get_watchdog(intf); }