diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2022-10-30 18:12:02 +0100 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2022-10-30 18:12:02 +0100 |
commit | 1f3c9b4ae26ce1d46d954d509b83b8684ada5625 (patch) | |
tree | 260b922ec4d5d153cc6db745e27931cfd464472f /lib/ipmi_chassis.c | |
parent | 8fcf0ba6f182918bd584bb80bf0b8998acad26a8 (diff) | |
parent | 8b758ee941f4c1ffea0532caa74b1fcd8101d1d8 (diff) |
Merge branch 'release/debian/1.8.19-1'debian/1.8.19-1
Diffstat (limited to 'lib/ipmi_chassis.c')
-rw-r--r-- | lib/ipmi_chassis.c | 1575 |
1 files changed, 1134 insertions, 441 deletions
diff --git a/lib/ipmi_chassis.c b/lib/ipmi_chassis.c index 7b5c2a8..7ac6770 100644 --- a/lib/ipmi_chassis.c +++ b/lib/ipmi_chassis.c @@ -29,13 +29,13 @@ * 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. */ -#define _SVID_SOURCE || _BSD_SOURCE || _POSIX_C_SOURCE >= 1 || \ - _XOPEN_SOURCE || _POSIX_SOURCE #include <stdlib.h> #include <string.h> #include <stdio.h> #include <time.h> +#include <errno.h> +#include <limits.h> #include <ipmitool/bswap.h> #include <ipmitool/helper.h> @@ -44,9 +44,132 @@ #include <ipmitool/ipmi_intf.h> #include <ipmitool/ipmi_strings.h> #include <ipmitool/ipmi_chassis.h> +#include <ipmitool/ipmi_time.h> + +#define CHASSIS_BOOT_MBOX_IANA_SZ 3 +#define CHASSIS_BOOT_MBOX_BLOCK_SZ 16 +#define CHASSIS_BOOT_MBOX_BLOCK0_SZ \ + (CHASSIS_BOOT_MBOX_BLOCK_SZ - CHASSIS_BOOT_MBOX_IANA_SZ) +#define CHASSIS_BOOT_MBOX_MAX_BLOCK 0xFF +#define CHASSIS_BOOT_MBOX_MAX_BLOCKS (CHASSIS_BOOT_MBOX_MAX_BLOCK + 1) + +/* Get/Set system boot option boot flags bit definitions */ +/* Boot flags byte 1 bits */ +#define BF1_VALID_SHIFT 7 +#define BF1_INVALID 0 +#define BF1_VALID (1 << BF1_VALID_SHIFT) +#define BF1_VALID_MASK BF1_VALID + +#define BF1_PERSIST_SHIFT 6 +#define BF1_ONCE 0 +#define BF1_PERSIST (1 << BF1_PERSIST_SHIFT) +#define BF1_PERSIST_MASK BF1_PERSIST + +#define BF1_BOOT_TYPE_SHIFT 5 +#define BF1_BOOT_TYPE_LEGACY 0 +#define BF1_BOOT_TYPE_EFI (1 << BF1_BOOT_TYPE_SHIFT) +#define BF1_BOOT_TYPE_MASK BF1_BOOT_TYPE_EFI + +/* Boot flags byte 2 bits */ +#define BF2_CMOS_CLEAR_SHIFT 7 +#define BF2_CMOS_CLEAR (1 << BF2_CMOS_CLEAR_SHIFT) +#define BF2_CMOS_CLEAR_MASK BF2_CMOS_CLEAR + +#define BF2_KEYLOCK_SHIFT 6 +#define BF2_KEYLOCK (1 << BF2_KEYLOCK_SHIFT) +#define BF2_KEYLOCK_MASK BF2_KEYLOCK + +#define BF2_BOOTDEV_SHIFT 2 +#define BF2_BOOTDEV_DEFAULT (0 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_PXE (1 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_HDD (2 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_HDD_SAFE (3 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_DIAG_PART (4 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_CDROM (5 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_SETUP (6 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_REMOTE_FDD (7 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_REMOTE_CDROM (8 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_REMOTE_PRIMARY_MEDIA (9 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_REMOTE_HDD (11 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_FDD (15 << BF2_BOOTDEV_SHIFT) +#define BF2_BOOTDEV_MASK (0xF << BF2_BOOTDEV_SHIFT) + +#define BF2_BLANK_SCREEN_SHIFT 1 +#define BF2_BLANK_SCREEN (1 << BF2_BLANK_SCREEN_SHIFT) +#define BF2_BLANK_SCREEN_MASK BF2_BLANK_SCREEN + +#define BF2_RESET_LOCKOUT_SHIFT 0 +#define BF2_RESET_LOCKOUT (1 << BF2_RESET_LOCKOUT_SHIFT) +#define BF2_RESET_LOCKOUT_MASK BF2_RESET_LOCKOUT + +/* Boot flags byte 3 bits */ +#define BF3_POWER_LOCKOUT_SHIFT 7 +#define BF3_POWER_LOCKOUT (1 << BF3_POWER_LOCKOUT_SHIFT) +#define BF3_POWER_LOCKOUT_MASK BF3_POWER_LOCKOUT + +#define BF3_VERBOSITY_SHIFT 5 +#define BF3_VERBOSITY_DEFAULT (0 << BF3_VERBOSITY_SHIFT) +#define BF3_VERBOSITY_QUIET (1 << BF3_VERBOSITY_SHIFT) +#define BF3_VERBOSITY_VERBOSE (2 << BF3_VERBOSITY_SHIFT) +#define BF3_VERBOSITY_MASK (3 << BF3_VERBOSITY_SHIFT) + +#define BF3_EVENT_TRAPS_SHIFT 4 +#define BF3_EVENT_TRAPS (1 << BF3_EVENT_TRAPS_SHIFT) +#define BF3_EVENT_TRAPS_MASK BF3_EVENT_TRAPS + +#define BF3_PASSWD_BYPASS_SHIFT 3 +#define BF3_PASSWD_BYPASS (1 << BF3_PASSWD_BYPASS_SHIFT) +#define BF3_PASSWD_BYPASS_MASK BF3_PASSWD_BYPASS + +#define BF3_SLEEP_LOCKOUT_SHIFT 2 +#define BF3_SLEEP_LOCKOUT (1 << BF3_SLEEP_LOCKOUT_SHIFT) +#define BF3_SLEEP_LOCKOUT_MASK BF3_SLEEP_LOCKOUT + +#define BF3_CONSOLE_REDIR_SHIFT 0 +#define BF3_CONSOLE_REDIR_DEFAULT (0 << BF3_CONSOLE_REDIR_SHIFT) +#define BF3_CONSOLE_REDIR_SUPPRESS (1 << BF3_CONSOLE_REDIR_SHIFT) +#define BF3_CONSOLE_REDIR_ENABLE (2 << BF3_CONSOLE_REDIR_SHIFT) +#define BF3_CONSOLE_REDIR_MASK (3 << BF3_CONSOLE_REDIR_SHIFT) + +/* Boot flags byte 4 bits */ +#define BF4_SHARED_MODE_SHIFT 3 +#define BF4_SHARED_MODE (1 << BF4_SHARED_MODE_SHIFT) +#define BF4_SHARED_MODE_MASK BF4_SHARED_MODE + +#define BF4_BIOS_MUX_SHIFT 0 +#define BF4_BIOS_MUX_DEFAULT (0 << BF4_BIOS_MUX_SHIFT) +#define BF4_BIOS_MUX_BMC (1 << BF4_BIOS_MUX_SHIFT) +#define BF4_BIOS_MUX_SYSTEM (2 << BF4_BIOS_MUX_SHIFT) +#define BF4_BIOS_MUX_MASK (7 << BF4_BIOS_MUX_SHIFT) + + +typedef struct { + uint8_t iana[CHASSIS_BOOT_MBOX_IANA_SZ]; + uint8_t data[CHASSIS_BOOT_MBOX_BLOCK0_SZ]; +} mbox_b0_data_t; + +typedef struct { + uint8_t block; + union { + uint8_t data[CHASSIS_BOOT_MBOX_BLOCK_SZ]; + mbox_b0_data_t b0; + }; +} mbox_t; extern int verbose; +static const struct valstr get_bootparam_cc_vals[] = { + { 0x80, "Unsupported parameter" }, + { 0x00, NULL } +}; + +static const struct valstr set_bootparam_cc_vals[] = { + { 0x80, "Unsupported parameter" }, + { 0x81, "Attempt to set 'in progress' while not in 'complete' state" }, + { 0x82, "Parameter is read-only" }, + { 0x00, NULL } +}; + int ipmi_chassis_power_status(struct ipmi_intf * intf) { @@ -59,11 +182,11 @@ ipmi_chassis_power_status(struct ipmi_intf * intf) req.msg.data_len = 0; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to get Chassis Power Status"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Get Chassis Power Status failed: %s", val2str(rsp->ccode, completion_code_vals)); return -1; @@ -98,12 +221,12 @@ ipmi_chassis_power_control(struct ipmi_intf * intf, uint8_t ctl) req.msg.data_len = 1; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to set Chassis Power Control to %s", val2str(ctl, ipmi_chassis_power_control_vals)); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Set Chassis Power Control to %s failed: %s", val2str(ctl, ipmi_chassis_power_control_vals), val2str(rsp->ccode, completion_code_vals)); @@ -131,8 +254,8 @@ ipmi_chassis_identify(struct ipmi_intf * intf, char * arg) req.msg.netfn = IPMI_NETFN_CHASSIS; req.msg.cmd = 0x4; - if (arg != NULL) { - if (strncmp(arg, "force", 5) == 0) { + if (arg) { + if (!strcmp(arg, "force")) { identify_data.force_on = 1; } else { if ( (rc = str2uchar(arg, &identify_data.interval)) != 0) { @@ -154,11 +277,11 @@ ipmi_chassis_identify(struct ipmi_intf * intf, char * arg) } rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to set Chassis Identify"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Set Chassis Identify failed: %s", val2str(rsp->ccode, completion_code_vals)); if (identify_data.force_on != 0) { @@ -172,7 +295,7 @@ ipmi_chassis_identify(struct ipmi_intf * intf, char * arg) } printf("Chassis identify interval: "); - if (arg == NULL) { + if (!arg) { printf("default (15 seconds)\n"); } else { if (identify_data.force_on != 0) { @@ -202,11 +325,11 @@ ipmi_chassis_poh(struct ipmi_intf * intf) req.msg.cmd = 0xf; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to get Chassis Power-On-Hours"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Get Chassis Power-On-Hours failed: %s", val2str(rsp->ccode, completion_code_vals)); return -1; @@ -245,52 +368,18 @@ ipmi_chassis_restart_cause(struct ipmi_intf * intf) req.msg.cmd = 0x7; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to get Chassis Restart Cause"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Get Chassis Restart Cause failed: %s", val2str(rsp->ccode, completion_code_vals)); return -1; } - printf("System restart cause: "); - - switch (rsp->data[0] & 0xf) { - case 0: - printf("unknown\n"); - break; - case 1: - printf("chassis power control command\n"); - break; - case 2: - printf("reset via pushbutton\n"); - break; - case 3: - printf("power-up via pushbutton\n"); - break; - case 4: - printf("watchdog expired\n"); - break; - case 5: - printf("OEM\n"); - break; - case 6: - printf("power-up due to always-restore power policy\n"); - break; - case 7: - printf("power-up due to restore-previous power policy\n"); - break; - case 8: - printf("reset via PEF\n"); - break; - case 9: - printf("power-cycle via PEF\n"); - break; - default: - printf("invalid\n"); - } + printf("System restart cause: %s\n", + val2str(rsp->data[0] & 0xf, ipmi_chassis_restart_cause_vals)); return 0; } @@ -306,11 +395,11 @@ ipmi_chassis_status(struct ipmi_intf * intf) req.msg.cmd = 0x1; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Error sending Chassis Status command"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Error sending Chassis Status command: %s", val2str(rsp->ccode, completion_code_vals)); return -1; @@ -389,11 +478,11 @@ ipmi_chassis_selftest(struct ipmi_intf * intf) req.msg.cmd = 0x4; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Error sending Get Self Test command"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Error sending Get Self Test command: %s", val2str(rsp->ccode, completion_code_vals)); return -1; @@ -447,61 +536,166 @@ ipmi_chassis_selftest(struct ipmi_intf * intf) } static int -ipmi_chassis_set_bootparam(struct ipmi_intf * intf, uint8_t param, uint8_t * data, int len) +ipmi_chassis_set_bootparam(struct ipmi_intf * intf, + uint8_t param, void *data, int len) { struct ipmi_rs * rsp; struct ipmi_rq req; - uint8_t msg_data[16]; + struct { + uint8_t param; + uint8_t data[]; + } *msg_data; + int rc = -1; + size_t msgsize = 1 + len; /* Single-byte parameter plus the data */ + static const uint8_t BOOTPARAM_MASK = 0x7F; + + msg_data = malloc(msgsize); + if (!msg_data) { + goto out; + } + memset(msg_data, 0, msgsize); - memset(msg_data, 0, 16); - msg_data[0] = param & 0x7f; - memcpy(msg_data+1, data, len); + msg_data->param = param & BOOTPARAM_MASK; + memcpy(msg_data->data, data, len); memset(&req, 0, sizeof(req)); req.msg.netfn = IPMI_NETFN_CHASSIS; req.msg.cmd = 0x8; - req.msg.data = msg_data; - req.msg.data_len = len + 1; + req.msg.data = (uint8_t *)msg_data; + req.msg.data_len = msgsize; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Error setting Chassis Boot Parameter %d", param); return -1; } - if (rsp->ccode > 0) { + + rc = rsp->ccode; + if (rc) { if (param != 0) { - lprintf(LOG_ERR, "Set Chassis Boot Parameter %d failed: %s", - param, val2str(rsp->ccode, completion_code_vals)); + lprintf(LOG_ERR, + "Set Chassis Boot Parameter %d failed: %s", + param, + specific_val2str(rsp->ccode, + set_bootparam_cc_vals, + completion_code_vals)); } - return -1; + goto out; } lprintf(LOG_DEBUG, "Chassis Set Boot Parameter %d to %s", param, buf2str(data, len)); - return 0; + +out: + free_n(&msg_data); + return rc; +} + +/* Flags to ipmi_chassis_get_bootparam() */ +typedef enum { + PARAM_NO_GENERIC_INFO, /* Do not print generic boot parameter info */ + PARAM_NO_DATA_DUMP, /* Do not dump parameter data */ + PARAM_NO_RANGE_ERROR, /* Do not report out of range info to user */ + PARAM_SPECIFIC /* Parameter-specific flags start with this */ +} chassis_bootparam_flags_t; + +/* Flags to ipmi_chassis_get_bootparam() for Boot Mailbox parameter (7) */ +typedef enum { + MBOX_PARSE_USE_TEXT = PARAM_SPECIFIC, /* Use text output vs. hex */ + MBOX_PARSE_ALLBLOCKS /* Parse all blocks, not just one */ +} chassis_bootmbox_parse_t; + +#define BP_FLAG(x) (1 << (x)) + +static +void +chassis_bootmailbox_parse(void *buf, size_t len, int flags) +{ + void *blockdata; + size_t datalen; + bool use_text = flags & BP_FLAG(MBOX_PARSE_USE_TEXT); + bool all_blocks = flags & BP_FLAG(MBOX_PARSE_ALLBLOCKS); + + mbox_t *mbox; + + if (!buf || !len) { + return; + } + + mbox = buf; + blockdata = mbox->data; + datalen = len - sizeof(mbox->block); + if (!all_blocks) { + /* Print block selector only if a single block is printed */ + printf(" Selector : %d\n", mbox->block); + } + if (!mbox->block) { + uint32_t iana = ipmi24toh(mbox->b0.iana); + /* For block zero print the IANA Private Enterprise Number */ + printf(" IANA PEN : %" PRIu32 " [%s]\n", + iana, + val2str(iana, ipmi_oem_info)); + blockdata = mbox->b0.data; + datalen -= sizeof(mbox->b0.iana); + } + + printf(" Block "); + if (all_blocks) { + printf("%3" PRIu8 " Data : ", mbox->block); + } + else { + printf("Data : "); + } + if (use_text) { + /* Ensure the data string is null-terminated */ + unsigned char text[CHASSIS_BOOT_MBOX_BLOCK_SZ + 1] = { 0 }; + memcpy(text, blockdata, datalen); + printf("'%s'\n", text); + } + else { + printf("%s\n", buf2str(blockdata, datalen)); + } } static int -ipmi_chassis_get_bootparam(struct ipmi_intf * intf, char * arg) +ipmi_chassis_get_bootparam(struct ipmi_intf * intf, + int argc, char *argv[], int flags) { struct ipmi_rs * rsp; struct ipmi_rq req; uint8_t msg_data[3]; uint8_t param_id = 0; + bool skip_generic = flags & BP_FLAG(PARAM_NO_GENERIC_INFO); + bool skip_data = flags & BP_FLAG(PARAM_NO_DATA_DUMP); + bool skip_range = flags & BP_FLAG(PARAM_NO_RANGE_ERROR); + int rc = -1; - if (arg == NULL) - return -1; + if (argc < 1 || !argv[0]) { + goto out; + } - if (str2uchar(arg, ¶m_id) != 0) { - lprintf(LOG_ERR, "Invalid parameter '%s' given instead of bootparam.", - arg); - return (-1); + if (str2uchar(argv[0], ¶m_id)) { + lprintf(LOG_ERR, + "Invalid parameter '%s' given instead of bootparam.", + argv[0]); + goto out; } + --argc; + ++argv; + memset(msg_data, 0, 3); msg_data[0] = param_id & 0x7f; - msg_data[1] = 0; - msg_data[2] = 0; + + if (argc) { + if (str2uchar(argv[0], &msg_data[1])) { + lprintf(LOG_ERR, + "Invalid argument '%s' given to" + " bootparam %" PRIu8, + argv[0], msg_data[1]); + goto out; + } + } memset(&req, 0, sizeof(req)); req.msg.netfn = IPMI_NETFN_CHASSIS; @@ -510,13 +704,22 @@ ipmi_chassis_get_bootparam(struct ipmi_intf * intf, char * arg) req.msg.data_len = 3; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { - lprintf(LOG_ERR, "Error Getting Chassis Boot Parameter %s", arg); + if (!rsp) { + lprintf(LOG_ERR, + "Error Getting Chassis Boot Parameter %" PRIu8, + msg_data[0]); return -1; } - if (rsp->ccode > 0) { - lprintf(LOG_ERR, "Get Chassis Boot Parameter %s failed: %s", - arg, val2str(rsp->ccode, completion_code_vals)); + if (IPMI_CC_PARAM_OUT_OF_RANGE == rsp->ccode && skip_range) { + return -1; + } + if (rsp->ccode) { + lprintf(LOG_ERR, + "Get Chassis Boot Parameter %" PRIu8 " failed: %s", + msg_data[0], + specific_val2str(rsp->ccode, + get_bootparam_cc_vals, + completion_code_vals)); return -1; } @@ -526,10 +729,17 @@ ipmi_chassis_get_bootparam(struct ipmi_intf * intf, char * arg) param_id = 0; param_id = (rsp->data[1] & 0x7f); - printf("Boot parameter version: %d\n", rsp->data[0]); - printf("Boot parameter %d is %s\n", rsp->data[1] & 0x7f, - (rsp->data[1] & 0x80) ? "invalid/locked" : "valid/unlocked"); - printf("Boot parameter data: %s\n", buf2str(rsp->data+2, rsp->data_len - 2)); + if (!skip_generic) { + printf("Boot parameter version: %d\n", rsp->data[0]); + printf("Boot parameter %d is %s\n", rsp->data[1] & 0x7f, + (rsp->data[1] & 0x80) + ? "invalid/locked" + : "valid/unlocked"); + if (!skip_data) { + printf("Boot parameter data: %s\n", + buf2str(rsp->data+2, rsp->data_len - 2)); + } + } switch(param_id) { @@ -622,132 +832,181 @@ ipmi_chassis_get_bootparam(struct ipmi_intf * intf, char * arg) { printf( " Boot Flags :\n"); - if((rsp->data[2]&0x80) == 0x80) + if(rsp->data[2] & BF1_VALID) printf(" - Boot Flag Valid\n"); else printf(" - Boot Flag Invalid\n"); - if((rsp->data[2]&0x40) == 0x40) + if(rsp->data[2] & BF1_PERSIST) printf(" - Options apply to all future boots\n"); else printf(" - Options apply to only next boot\n"); - if((rsp->data[2]&0x20) == 0x20) + if(rsp->data[2] & BF1_BOOT_TYPE_EFI) printf(" - BIOS EFI boot \n"); else printf(" - BIOS PC Compatible (legacy) boot \n"); - if((rsp->data[3]&0x80) == 0x80) + if(rsp->data[3] & BF2_CMOS_CLEAR) printf(" - CMOS Clear\n"); - if((rsp->data[3]&0x40) == 0x40) + if(rsp->data[3] & BF2_KEYLOCK) printf(" - Lock Keyboard\n"); printf(" - Boot Device Selector : "); - switch( ((rsp->data[3]>>2)&0x0f)) + switch(rsp->data[3] & BF2_BOOTDEV_MASK) { - case 0: printf("No override\n"); break; - case 1: printf("Force PXE\n"); break; - case 2: printf("Force Boot from default Hard-Drive\n"); break; - case 3: printf("Force Boot from default Hard-Drive, request Safe-Mode\n"); break; - case 4: printf("Force Boot from Diagnostic Partition\n"); break; - case 5: printf("Force Boot from CD/DVD\n"); break; - case 6: printf("Force Boot into BIOS Setup\n"); break; - case 15: printf("Force Boot from Floppy/primary removable media\n"); break; - default: printf("Flag error\n"); break; + case BF2_BOOTDEV_DEFAULT: + printf("No override\n"); + break; + case BF2_BOOTDEV_PXE: + printf("Force PXE\n"); + break; + case BF2_BOOTDEV_HDD: + printf("Force Boot from default Hard-Drive\n"); + break; + case BF2_BOOTDEV_HDD_SAFE: + printf("Force Boot from default Hard-Drive, " + "request Safe-Mode\n"); + break; + case BF2_BOOTDEV_DIAG_PART: + printf("Force Boot from Diagnostic Partition\n"); + break; + case BF2_BOOTDEV_CDROM: + printf("Force Boot from CD/DVD\n"); + break; + case BF2_BOOTDEV_SETUP: + printf("Force Boot into BIOS Setup\n"); + break; + case BF2_BOOTDEV_REMOTE_FDD: + printf("Force Boot from remotely connected " + "Floppy/primary removable media\n"); + break; + case BF2_BOOTDEV_REMOTE_CDROM: + printf("Force Boot from remotely connected " + "CD/DVD\n"); + break; + case BF2_BOOTDEV_REMOTE_PRIMARY_MEDIA: + printf("Force Boot from primary remote media\n"); + break; + case BF2_BOOTDEV_REMOTE_HDD: + printf("Force Boot from remotely connected " + "Hard-Drive\n"); + break; + case BF2_BOOTDEV_FDD: + printf("Force Boot from Floppy/primary " + "removable media\n"); + break; + default: + printf("Flag error\n"); + break; } - if((rsp->data[3]&0x02) == 0x02) + if(rsp->data[3] & BF2_BLANK_SCREEN) printf(" - Screen blank\n"); - if((rsp->data[3]&0x01) == 0x01) + if(rsp->data[3] & BF2_RESET_LOCKOUT) printf(" - Lock out Reset buttons\n"); - if((rsp->data[4]&0x80) == 0x80) - printf(" - Lock out (power off/sleep request) vi Power Button\n"); - printf(" - Console Redirection control : "); - switch( ((rsp->data[4]>>5)&0x03)) + if(rsp->data[4] & BF3_POWER_LOCKOUT) + printf(" - Lock out (power off/sleep " + "request) via Power Button\n"); + + printf(" - BIOS verbosity : "); + switch(rsp->data[4] & BF3_VERBOSITY_MASK) { - case 0: printf("System Default\n"); break; - case 1: printf("Request Quiet Display\n"); break; - case 2: printf("Request Verbose Display\n"); break; - default: printf("Flag error\n"); break; + case BF3_VERBOSITY_DEFAULT: + printf("System Default\n"); + break; + case BF3_VERBOSITY_QUIET: + printf("Request Quiet Display\n"); + break; + case BF3_VERBOSITY_VERBOSE: + printf("Request Verbose Display\n"); + break; + default: + printf("Flag error\n"); + break; } - if((rsp->data[4]&0x10) == 0x10) + if(rsp->data[4] & BF3_EVENT_TRAPS) printf(" - Force progress event traps\n"); - if((rsp->data[4]&0x08) == 0x08) + if(rsp->data[4] & BF3_PASSWD_BYPASS) printf(" - User password bypass\n"); - if((rsp->data[4]&0x04) == 0x04) + if(rsp->data[4] & BF3_SLEEP_LOCKOUT) printf(" - Lock Out Sleep Button\n"); - if((rsp->data[4]&0x02) == 0x02) - printf(" - Lock Out Sleep Button\n"); - printf(" - BIOS verbosity : "); - switch( ((rsp->data[4]>>0)&0x03)) + printf(" - Console Redirection control : "); + switch(rsp->data[4] & BF3_CONSOLE_REDIR_MASK) { - case 0: printf("Console redirection occurs per BIOS configuration setting (default)\n"); break; - case 1: printf("Suppress (skip) console redirection if enabled\n"); break; - case 2: printf("Request console redirection be enabled\n"); break; - default: printf("Flag error\n"); break; + case BF3_CONSOLE_REDIR_DEFAULT: + printf( + "Console redirection occurs per BIOS " + "configuration setting (default)\n"); + break; + case BF3_CONSOLE_REDIR_SUPPRESS: + printf("Suppress (skip) console redirection " + "if enabled\n"); + break; + case BF3_CONSOLE_REDIR_ENABLE: + printf("Request console redirection be " + "enabled\n"); + break; + default: + printf("Flag error\n"); + break; } - if((rsp->data[5]&0x08) == 0x08) + if(rsp->data[5] & BF4_SHARED_MODE) printf(" - BIOS Shared Mode Override\n"); printf(" - BIOS Mux Control Override : "); - switch( ((rsp->data[5]>>0)&0x07)) - { - case 0: printf("BIOS uses recommended setting of the mux at the end of POST\n"); break; - case 1: printf("Requests BIOS to force mux to BMC at conclusion of POST/start of OS boot\n"); break; - case 2: printf("Requests BIOS to force mux to system at conclusion of POST/start of OS boot\n"); break; - default: printf("Flag error\n"); break; + switch (rsp->data[5] & BF4_BIOS_MUX_MASK) { + case BF4_BIOS_MUX_DEFAULT: + printf("BIOS uses recommended setting of the " + "mux at the end of POST\n"); + break; + case BF4_BIOS_MUX_BMC: + printf( + "Requests BIOS to force mux to BMC at " + "conclusion of POST/start of OS boot\n"); + break; + case BF4_BIOS_MUX_SYSTEM: + printf( + "Requests BIOS to force mux to system " + "at conclusion of POST/start of " + "OS boot\n"); + break; + default: + printf("Flag error\n"); + break; } } break; case 6: { unsigned long session_id; - unsigned long timestamp; - char time_buf[40]; - time_t out_time; + uint32_t timestamp; session_id = ((unsigned long) rsp->data[3]); session_id |= (((unsigned long) rsp->data[4])<<8); session_id |= (((unsigned long) rsp->data[5])<<16); session_id |= (((unsigned long) rsp->data[6])<<24); - timestamp = ((unsigned long) rsp->data[7]); - timestamp |= (((unsigned long) rsp->data[8])<<8); - timestamp |= (((unsigned long) rsp->data[9])<<16); - timestamp |= (((unsigned long) rsp->data[10])<<24); - - memset(time_buf, 0, 40); - strftime( - time_buf, - sizeof(time_buf), - "%m/%d/%Y %H:%M:%S", localtime(&out_time) - ); + timestamp = ipmi32toh(&rsp->data[7]); printf(" Boot Initiator Info :\n"); printf(" Channel Number : %d\n", (rsp->data[2] & 0x0f)); printf(" Session Id : %08lXh\n",session_id); - if(timestamp != 0) - { - printf(" Timestamp : %08lXh, %s\n",timestamp,time_buf); - } - else - { - printf(" Timestamp : %08lXh, undefined\n",timestamp); - } - + printf(" Timestamp : %s\n", ipmi_timestamp_numeric(timestamp)); } break; case 7: - { - printf(" Selector : %d\n", rsp->data[2] ); - printf(" Block Data : %s\n", buf2str(rsp->data+3, rsp->data_len - 2)); - } - break; + chassis_bootmailbox_parse(rsp->data + 2, + rsp->data_len - 2, + flags); + break; default: - printf(" Undefined byte\n"); + printf(" Unsupported parameter %" PRIu8 "\n", param_id); break; } - return 0; + rc = IPMI_CC_OK; +out: + return rc; } static int @@ -777,24 +1036,25 @@ get_bootparam_options(char *optstring, {NULL} /* End marker */ }, *op; + const char *optkw = "options="; - if (strncmp(optstring, "options=", 8) != 0) { + if (strncmp(optstring, optkw, strlen(optkw))) { lprintf(LOG_ERR, "No options= keyword found \"%s\"", optstring); return -1; } token = strtok_r(optstring + 8, ",", &saveptr); - while (token != NULL) { + while (token) { int setbit = 0; - if (strcmp(token, "help") == 0) { + if (!strcmp(token, "help")) { optionError = 1; break; } - if (strncmp(token, "no-", 3) == 0) { + if (!strcmp(token, "no-")) { setbit = 1; token += 3; } - for (op = options; op->name != NULL; ++op) { - if (strncmp(token, op->name, strlen(op->name)) == 0) { + for (op = options; op->name; ++op) { + if (!strcmp(token, op->name)) { if (setbit) { *set_flag |= op->value; } else { @@ -803,7 +1063,7 @@ get_bootparam_options(char *optstring, break; } } - if (op->name == NULL) { + if (!op->name) { /* Option not found */ optionError = 1; if (setbit) { @@ -816,7 +1076,7 @@ get_bootparam_options(char *optstring, if (optionError) { lprintf(LOG_NOTICE, " Legal options are:"); lprintf(LOG_NOTICE, " %-8s: print this message", "help"); - for (op = options; op->name != NULL; ++op) { + for (op = options; op->name; ++op) { lprintf(LOG_NOTICE, " %-8s: %s", op->name, op->desc); } lprintf(LOG_NOTICE, " Any Option may be prepended with no-" @@ -846,14 +1106,17 @@ ipmi_chassis_get_bootvalid(struct ipmi_intf * intf) req.msg.data_len = 3; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Error Getting Chassis Boot Parameter %d", param_id); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Get Chassis Boot Parameter %d failed: %s", - param_id, val2str(rsp->ccode, completion_code_vals)); + param_id, + specific_val2str(rsp->ccode, + get_bootparam_cc_vals, + completion_code_vals)); return -1; } @@ -863,77 +1126,97 @@ ipmi_chassis_get_bootvalid(struct ipmi_intf * intf) return(rsp->data[2]); } +typedef enum { + SET_COMPLETE, + SET_IN_PROGRESS, + COMMIT_WRITE, + RESERVED +} progress_t; + + +static +void +chassis_bootparam_set_in_progress(struct ipmi_intf *intf, progress_t progress) +{ + /* + * By default try to set/clear set-in-progress parameter before/after + * changing any boot parameters. If setting fails, the code will set + * this flag to false and stop trying to fiddle with it for future + * requests. + */ + static bool use_progress = true; + uint8_t flag = progress; + int rc; + + if (!use_progress) { + return; + } + + rc = ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, + &flag, 1); + + /* + * Only disable future checks if set in progress status setting failed. + * Setting of other statuses may fail legitimately. + */ + if (rc && SET_IN_PROGRESS == progress) { + use_progress = false; + } +} + +typedef enum { + BIOS_POST_ACK = 1 << 0, + OS_LOADER_ACK = 1 << 1, + OS_SERVICE_PARTITION_ACK = 1 << 2, + SMS_ACK = 1 << 3, + OEM_ACK = 1 << 4, + RESERVED_ACK_MASK = 7 << 5 +} bootinfo_ack_t; + +static +int +chassis_bootparam_clear_ack(struct ipmi_intf *intf, bootinfo_ack_t flag) +{ + uint8_t flags[2] = { flag & ~RESERVED_ACK_MASK, + flag & ~RESERVED_ACK_MASK }; + + return ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_INFO_ACK, + flags, 2); +} + static int ipmi_chassis_set_bootvalid(struct ipmi_intf *intf, uint8_t set_flag, uint8_t clr_flag) { int bootvalid; - uint8_t flags[5]; - int rc = 0; - int use_progress = 1; - uint8_t param_id = IPMI_CHASSIS_BOOTPARAM_FLAG_VALID; + uint8_t flags[2]; + int rc; - if (use_progress) { - /* set set-in-progress flag */ - memset(flags, 0, 5); - flags[0] = 0x01; - rc = ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, flags, 1); - if (rc < 0) - use_progress = 0; - } - - memset(flags, 0, 5); - flags[0] = 0x01; - flags[1] = 0x01; - rc = ipmi_chassis_set_bootparam(intf, IPMI_CHASSIS_BOOTPARAM_INFO_ACK, - flags, 2); + chassis_bootparam_set_in_progress(intf, SET_IN_PROGRESS); + rc = chassis_bootparam_clear_ack(intf, BIOS_POST_ACK); - if (rc < 0) { - if (use_progress) { - /* set-in-progress = set-complete */ - memset(flags, 0, 5); - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); - } - return -1; + if (rc) { + goto out; } bootvalid = ipmi_chassis_get_bootvalid(intf); - if (bootvalid < 0) { - if (use_progress) { - /* set-in-progress = set-complete */ - memset(flags, 0, 5); - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); - } - return -1; - } - flags[0] = (bootvalid & ~clr_flag) | set_flag; - - rc = ipmi_chassis_set_bootparam(intf, param_id, flags, 1); - - if (rc == 0) { - if (use_progress) { - /* set-in-progress = commit-write */ - memset(flags, 0, 5); - flags[0] = 0x02; - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); - } + lprintf(LOG_ERR, "Failed to read boot valid flag"); + rc = bootvalid; + goto out; } - if (use_progress) { - /* set-in-progress = set-complete */ - memset(flags, 0, 5); - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); + flags[0] = (bootvalid & ~clr_flag) | set_flag; + rc = ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_FLAG_VALID, + flags, 1); + if (IPMI_CC_OK == rc) { + chassis_bootparam_set_in_progress(intf, COMMIT_WRITE); } +out: + chassis_bootparam_set_in_progress(intf, SET_COMPLETE); return rc; } @@ -941,107 +1224,372 @@ static int ipmi_chassis_set_bootdev(struct ipmi_intf * intf, char * arg, uint8_t *iflags) { uint8_t flags[5]; - int rc = 0; - int use_progress = 1; + int rc; - if (use_progress) { - /* set set-in-progress flag */ - memset(flags, 0, 5); - flags[0] = 0x01; - rc = ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, flags, 1); - if (rc < 0) - use_progress = 0; - } - - memset(flags, 0, 5); - flags[0] = 0x01; - flags[1] = 0x01; - rc = ipmi_chassis_set_bootparam(intf, IPMI_CHASSIS_BOOTPARAM_INFO_ACK, - flags, 2); + chassis_bootparam_set_in_progress(intf, SET_IN_PROGRESS); + rc = chassis_bootparam_clear_ack(intf, BIOS_POST_ACK); if (rc < 0) { - if (use_progress) { - /* set-in-progress = set-complete */ - memset(flags, 0, 5); - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); - } - return -1; + goto out; } - if (iflags == NULL) - memset(flags, 0, 5); + if (!iflags) + memset(flags, 0, sizeof(flags)); else memcpy(flags, iflags, sizeof (flags)); - if (arg == NULL) + if (!arg) flags[1] |= 0x00; - else if (strncmp(arg, "none", 4) == 0) + else if (!strcmp(arg, "none")) flags[1] |= 0x00; - else if (strncmp(arg, "pxe", 3) == 0 || - strncmp(arg, "force_pxe", 9) == 0) + else if (!strcmp(arg, "pxe") || + !strcmp(arg, "force_pxe")) + { flags[1] |= 0x04; - else if (strncmp(arg, "disk", 4) == 0 || - strncmp(arg, "force_disk", 10) == 0) + } + else if (!strcmp(arg, "disk") || + !strcmp(arg, "force_disk")) + { flags[1] |= 0x08; - else if (strncmp(arg, "safe", 4) == 0 || - strncmp(arg, "force_safe", 10) == 0) + } + else if (!strcmp(arg, "safe") || + !strcmp(arg, "force_safe")) + { flags[1] |= 0x0c; - else if (strncmp(arg, "diag", 4) == 0 || - strncmp(arg, "force_diag", 10) == 0) + } + else if (!strcmp(arg, "diag") || + !strcmp(arg, "force_diag")) + { flags[1] |= 0x10; - else if (strncmp(arg, "cdrom", 5) == 0 || - strncmp(arg, "force_cdrom", 11) == 0) + } + else if (!strcmp(arg, "cdrom") || + !strcmp(arg, "force_cdrom")) + { flags[1] |= 0x14; - else if (strncmp(arg, "floppy", 6) == 0 || - strncmp(arg, "force_floppy", 12) == 0) + } + else if (!strcmp(arg, "floppy") || + !strcmp(arg, "force_floppy")) + { flags[1] |= 0x3c; - else if (strncmp(arg, "bios", 4) == 0 || - strncmp(arg, "force_bios", 10) == 0) + } + else if (!strcmp(arg, "bios") || + !strcmp(arg, "force_bios")) + { flags[1] |= 0x18; + } else { lprintf(LOG_ERR, "Invalid argument: %s", arg); - if (use_progress) { - /* set-in-progress = set-complete */ - memset(flags, 0, 5); - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); - } - return -1; + rc = -1; + goto out; } /* set flag valid bit */ flags[0] |= 0x80; - rc = ipmi_chassis_set_bootparam(intf, IPMI_CHASSIS_BOOTPARAM_BOOT_FLAGS, - flags, 5); - if (rc == 0) { - if (use_progress) { - /* set-in-progress = commit-write */ - memset(flags, 0, 5); - flags[0] = 0x02; - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); + rc = ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_BOOT_FLAGS, + flags, 5); + if (IPMI_CC_OK == rc) { + chassis_bootparam_set_in_progress(intf, COMMIT_WRITE); + printf("Set Boot Device to %s\n", arg); + } + +out: + chassis_bootparam_set_in_progress(intf, SET_COMPLETE); + return rc; +} + +static void chassis_bootmailbox_help() +{ + lprintf(LOG_NOTICE, +"bootmbox get [text] [block <block>]\n" +" Read the entire Boot Initiator Mailbox or the specified <block>.\n" +" If 'text' option is specified, the data is output as plain text, otherwise\n" +" hex dump mode is used.\n" +"\n" +"bootmbox set text [block <block>] <IANA_PEN> \"<data_string>\"\n" +"bootmbox set [block <block>] <IANA_PEN> <data_byte> [<data_byte> ...]\n" +" Write the specified <block> or the entire Boot Initiator Mailbox.\n" +" It is required to specify a decimal IANA Enterprise Number recognized\n" +" by the boot initiator on the target system. Refer to your target system\n" +" manufacturer for details. The rest of the arguments are either separate\n" +" data byte values separated by spaces, or a single text string argument.\n" +"\n" +" When single block write is requested, the total length of <data> may not\n" +" exceed 13 bytes for block 0, or 16 bytes otherwise.\n" +"\n" +"bootmbox help\n" +" Show this help."); +} + +static +int +chassis_set_bootmailbox(struct ipmi_intf *intf, int16_t block, bool use_text, + int argc, char *argv[]) +{ + int rc = -1; + int32_t iana = 0; + size_t blocks = 0; + size_t datasize = 0; + off_t string_offset = 0; + + lprintf(LOG_INFO, "Writing Boot Mailbox..."); + + if (argc < 1 || str2int(argv[0], &iana)) { + lprintf(LOG_ERR, + "No valid IANA PEN specified!\n"); + chassis_bootmailbox_help(); + goto out; + } + ++argv; + --argc; + + if (argc < 1) { + lprintf(LOG_ERR, + "No data provided!\n"); + chassis_bootmailbox_help(); + goto out; + } + + /* + * Initialize the data size. For text mode it is just the + * single argument string length plus one byte for \0 termination. + * For byte mode the length is the number of byte arguments without + * any additional termination. + */ + if (!use_text) { + datasize = argc; + } + else { + datasize = strlen(argv[0]) + 1; /* Include the terminator */ + } + + lprintf(LOG_INFO, "Data size: %u", datasize); + + /* Decide how many blocks we will be writing */ + if (block >= 0) { + blocks = 1; + } + else { + /* + * We need to write all data, so calculate the data + * size in blocks and set the starting block to zero. + */ + blocks = CHASSIS_BOOT_MBOX_IANA_SZ; + blocks += datasize; + blocks += CHASSIS_BOOT_MBOX_BLOCK_SZ - 1; + blocks /= CHASSIS_BOOT_MBOX_BLOCK_SZ; + + block = 0; + } + + lprintf(LOG_INFO, "Blocks to write: %d", blocks); + + if (blocks > CHASSIS_BOOT_MBOX_MAX_BLOCKS) { + lprintf(LOG_ERR, + "Data size %zu exceeds maximum (%d)", + datasize, + (CHASSIS_BOOT_MBOX_BLOCK_SZ + * CHASSIS_BOOT_MBOX_MAX_BLOCKS) + - CHASSIS_BOOT_MBOX_IANA_SZ); + goto out; + } + + /* Indicate that we're touching the boot parameters */ + chassis_bootparam_set_in_progress(intf, SET_IN_PROGRESS); + + for (size_t bindex = 0; + datasize > 0 && bindex < blocks; + ++bindex, ++block) + { + /* The request data structure */ + mbox_t mbox = { .block = block, {{0}} }; + + /* Destination for input data */ + uint8_t *data = mbox.data; + + /* The maximum amount of data this block may hold */ + size_t maxblocksize = sizeof(mbox.data); + + /* The actual amount of data in this block */ + size_t blocksize; + off_t unused = 0; + + /* Block 0 needs special care as it has IANA PEN specifier */ + if (!block) { + data = mbox.b0.data; + maxblocksize = sizeof(mbox.b0.data); + htoipmi24(iana, mbox.b0.iana); } - printf("Set Boot Device to %s\n", arg); + /* + * Find out how many bytes we are going to write to this + * block. + */ + if (datasize > maxblocksize) { + blocksize = maxblocksize; + } + else { + blocksize = datasize; + } + + /* Remember how much data remains */ + datasize -= blocksize; + + if (!use_text) { + args2buf(argc, argv, data, blocksize); + argc -= blocksize; + argv += blocksize; + } + else { + memcpy(data, argv[0] + string_offset, blocksize); + string_offset += blocksize; + } + + lprintf(LOG_INFO, "Block %3" PRId16 ": %s", block, + buf2str_extended(data, blocksize, " ")); + + unused = maxblocksize - blocksize; + rc = ipmi_chassis_set_bootparam(intf, + IPMI_CHASSIS_BOOTPARAM_INIT_MBOX, + &mbox, + sizeof(mbox) - unused); + if (IPMI_CC_PARAM_OUT_OF_RANGE == rc) { + lprintf(LOG_ERR, + "Hit end of mailbox writing block %" PRId16, + block); + } + if (rc) { + goto complete; + } } - if (use_progress) { - /* set-in-progress = set-complete */ - memset(flags, 0, 5); - ipmi_chassis_set_bootparam(intf, - IPMI_CHASSIS_BOOTPARAM_SET_IN_PROGRESS, - flags, 1); + lprintf(LOG_INFO, + "Wrote %zu blocks of Boot Initiator Mailbox", + blocks); + chassis_bootparam_set_in_progress(intf, COMMIT_WRITE); + + rc = chassis_bootparam_clear_ack(intf, BIOS_POST_ACK | OS_LOADER_ACK); + +complete: + chassis_bootparam_set_in_progress(intf, SET_COMPLETE); +out: + return rc; +} + +static +int +chassis_get_bootmailbox(struct ipmi_intf *intf, + int16_t block, bool use_text) +{ + int rc = IPMI_CC_UNSPECIFIED_ERROR; + char param_str[2]; /* Max "7" */ + char block_str[4]; /* Max "255" */ + char *bpargv[] = { param_str, block_str }; + int flags; + + flags = use_text ? BP_FLAG(MBOX_PARSE_USE_TEXT) : 0; + + snprintf(param_str, sizeof(param_str), + "%" PRIu8, IPMI_CHASSIS_BOOTPARAM_INIT_MBOX); + + if (block >= 0) { + snprintf(block_str, sizeof(block_str), + "%" PRIu8, (uint8_t)block); + + rc = ipmi_chassis_get_bootparam(intf, + ARRAY_SIZE(bpargv), + bpargv, + flags); + } + else { + int currblk; + + flags |= BP_FLAG(MBOX_PARSE_ALLBLOCKS); + for (currblk = 0; currblk <= UCHAR_MAX; ++currblk) { + snprintf(block_str, sizeof(block_str), + "%" PRIu8, (uint8_t)currblk); + + if (currblk) { + /* + * If block 0 succeeded, we don't want to + * print generic info for each next block, + * and we don't want range error to be + * reported when we hit the end of blocks. + */ + flags |= BP_FLAG(PARAM_NO_GENERIC_INFO); + flags |= BP_FLAG(PARAM_NO_RANGE_ERROR); + } + + rc = ipmi_chassis_get_bootparam(intf, + ARRAY_SIZE(bpargv), + bpargv, + flags); + + if (rc) { + if (currblk) { + rc = IPMI_CC_OK; + } + break; + } + } } return rc; } +static +int +chassis_bootmailbox(struct ipmi_intf *intf, int argc, char *argv[]) +{ + int rc = IPMI_CC_UNSPECIFIED_ERROR; + bool use_text = false; /* Default to data dump I/O mode */ + int16_t block = -1; /* By default print all blocks */ + const char *cmd; + + if ((argc < 1) || !strcmp(argv[0], "help")) { + chassis_bootmailbox_help(); + goto out; + } else { + cmd = argv[0]; + ++argv; + --argc; + + if (argc > 0 && !strcmp(argv[0], "text")) { + use_text = true; + ++argv; + --argc; + } + + if (argc > 0 && !strcmp(argv[0], "block")) { + if (argc < 2) { + chassis_bootmailbox_help(); + goto out; + } + if(str2short(argv[1], &block)) { + lprintf(LOG_ERR, + "Invalid block %s", argv[1]); + goto out; + } + argv += 2; + argc -= 2; + + } + + if (!strcmp(cmd, "get")) { + rc = chassis_get_bootmailbox(intf, block, use_text); + } + else if (!strcmp(cmd, "set")) { + rc = chassis_set_bootmailbox(intf, block, use_text, + argc, argv); + } + } + +out: + return rc; +} + + static int ipmi_chassis_power_policy(struct ipmi_intf * intf, uint8_t policy) { @@ -1055,11 +1603,11 @@ ipmi_chassis_power_policy(struct ipmi_intf * intf, uint8_t policy) req.msg.data_len = 1; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Error in Power Restore Policy command"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Power Restore Policy command failed: %s", val2str(rsp->ccode, completion_code_vals)); return -1; @@ -1100,25 +1648,25 @@ ipmi_power_main(struct ipmi_intf * intf, int argc, char ** argv) int rc = 0; uint8_t ctl = 0; - if ((argc < 1) || (strncmp(argv[0], "help", 4) == 0)) { + if (argc < 1 || !strcmp(argv[0], "help")) { lprintf(LOG_NOTICE, "chassis power Commands: status, on, off, cycle, reset, diag, soft"); return 0; } - if (strncmp(argv[0], "status", 6) == 0) { + if (!strcmp(argv[0], "status")) { rc = ipmi_chassis_print_power_status(intf); return rc; } - if ((strncmp(argv[0], "up", 2) == 0) || (strncmp(argv[0], "on", 2) == 0)) + if (!strcmp(argv[0], "up") || !strcmp(argv[0], "on")) ctl = IPMI_CHASSIS_CTL_POWER_UP; - else if ((strncmp(argv[0], "down", 4) == 0) || (strncmp(argv[0], "off", 3) == 0)) + else if (!strcmp(argv[0], "down") || !strcmp(argv[0], "off")) ctl = IPMI_CHASSIS_CTL_POWER_DOWN; - else if (strncmp(argv[0], "cycle", 5) == 0) + else if (!strcmp(argv[0], "cycle")) ctl = IPMI_CHASSIS_CTL_POWER_CYCLE; - else if (strncmp(argv[0], "reset", 5) == 0) + else if (!strcmp(argv[0], "reset")) ctl = IPMI_CHASSIS_CTL_HARD_RESET; - else if (strncmp(argv[0], "diag", 4) == 0) + else if (!strcmp(argv[0], "diag")) ctl = IPMI_CHASSIS_CTL_PULSE_DIAG; - else if ((strncmp(argv[0], "acpi", 4) == 0) || (strncmp(argv[0], "soft", 4) == 0)) + else if (!strcmp(argv[0], "acpi") || !strcmp(argv[0], "soft")) ctl = IPMI_CHASSIS_CTL_ACPI_SOFT; else { lprintf(LOG_ERR, "Invalid chassis power command: %s", argv[0]); @@ -1146,55 +1694,270 @@ ipmi_chassis_set_bootflag_help() get_bootparam_options("options=help", &set_flag, &clr_flag); } +/* + * Sugar. Macros for internal use by bootdev_parse_options() to make + * the structure initialization look better. Can't use scope-limited + * static consts for initializers with gcc5, alas. + */ +#define BF1_OFFSET 0 +#define BF2_OFFSET 1 +#define BF3_OFFSET 2 +#define BF4_OFFSET 3 +#define BF_BYTE_COUNT 5 + +/* A helper for ipmi_chassis_main() to parse bootdev options */ +static +bool +bootdev_parse_options(char *optstring, uint8_t flags[]) +{ + char *token; + char *saveptr = NULL; + int optionError = 0; + + static const struct bootdev_opt_s { + char *name; + off_t offset; + unsigned char mask; + unsigned char value; + char *desc; + } *op; + static const struct bootdev_opt_s options[] = { + /* data 1 */ + { + "valid", + BF1_OFFSET, + BF1_VALID_MASK, + BF1_VALID, + "Boot flags valid" + }, + { + "persistent", + BF1_OFFSET, + BF1_PERSIST_MASK, + BF1_PERSIST, + "Changes are persistent for " + "all future boots" + }, + { + "efiboot", + BF1_OFFSET, + BF1_BOOT_TYPE_MASK, + BF1_BOOT_TYPE_EFI, + "Extensible Firmware Interface " + "Boot (EFI)" + }, + /* data 2 */ + { + "clear-cmos", + BF2_OFFSET, + BF2_CMOS_CLEAR_MASK, + BF2_CMOS_CLEAR, + "CMOS clear" + }, + { + "lockkbd", + BF2_OFFSET, + BF2_KEYLOCK_MASK, + BF2_KEYLOCK, + "Lock Keyboard" + }, + /* data2[5:2] is parsed elsewhere */ + { + "screenblank", + BF2_OFFSET, + BF2_BLANK_SCREEN_MASK, + BF2_BLANK_SCREEN, + "Screen Blank" + }, + { + "lockoutreset", + BF2_OFFSET, + BF2_RESET_LOCKOUT_MASK, + BF2_RESET_LOCKOUT, + "Lock out Reset buttons" + }, + /* data 3 */ + { + "lockout_power", + BF3_OFFSET, + BF3_POWER_LOCKOUT_MASK, + BF3_POWER_LOCKOUT, + "Lock out (power off/sleep " + "request) via Power Button" + }, + { + "verbose=default", + BF3_OFFSET, + BF3_VERBOSITY_MASK, + BF3_VERBOSITY_DEFAULT, + "Request quiet BIOS display" + }, + { + "verbose=no", + BF3_OFFSET, + BF3_VERBOSITY_MASK, + BF3_VERBOSITY_QUIET, + "Request quiet BIOS display" + }, + { + "verbose=yes", + BF3_OFFSET, + BF3_VERBOSITY_MASK, + BF3_VERBOSITY_VERBOSE, + "Request verbose BIOS display" + }, + { + "force_pet", + BF3_OFFSET, + BF3_EVENT_TRAPS_MASK, + BF3_EVENT_TRAPS, + "Force progress event traps" + }, + { + "upw_bypass", + BF3_OFFSET, + BF3_PASSWD_BYPASS_MASK, + BF3_PASSWD_BYPASS, + "User password bypass" + }, + { + "lockout_sleep", + BF3_OFFSET, + BF3_SLEEP_LOCKOUT_MASK, + BF3_SLEEP_LOCKOUT, + "Lock out the Sleep button" + }, + { + "cons_redirect=default", + BF3_OFFSET, + BF3_CONSOLE_REDIR_MASK, + BF3_CONSOLE_REDIR_DEFAULT, + "Console redirection occurs per " + "BIOS configuration setting" + }, + { + "cons_redirect=skip", + BF3_OFFSET, + BF3_CONSOLE_REDIR_MASK, + BF3_CONSOLE_REDIR_SUPPRESS, + "Suppress (skip) console " + "redirection if enabled" + }, + { + "cons_redirect=enable", + BF3_OFFSET, + BF3_CONSOLE_REDIR_MASK, + BF3_CONSOLE_REDIR_ENABLE, + "Request console redirection " + "be enabled" + }, + /* data 4 */ + /* data4[7:4] reserved */ + /* data4[3] BIOS Shared Mode Override, not implemented here */ + /* data4[2:0] BIOS Mux Control Override, not implemented here */ + + /* data5 reserved */ + + {NULL} /* End marker */ + }; + + memset(&flags[0], 0, BF_BYTE_COUNT); + token = strtok_r(optstring, ",", &saveptr); + while (token) { + if (!strcmp(token, "help")) { + optionError = 1; + break; + } + for (op = options; op->name; ++op) { + if (!strcmp(token, op->name)) { + flags[op->offset] &= ~(op->mask); + flags[op->offset] |= op->value; + break; + } + } + if (!op->name) { + /* Option not found */ + optionError = 1; + lprintf(LOG_ERR, "Invalid option: %s", token); + } + token = strtok_r(NULL, ",", &saveptr); + } + if (optionError) { + lprintf(LOG_NOTICE, "Legal options settings are:"); + lprintf(LOG_NOTICE, " %-22s: %s", + "help", + "print this message"); + for (op = options; op->name; ++op) { + lprintf(LOG_NOTICE, " %-22s: %s", op->name, op->desc); + } + return false; + } + + return true; +} + int ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) { - int rc = 0; + int rc = -1; - if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) { - lprintf(LOG_NOTICE, "Chassis Commands: status, power, identify, policy, restart_cause, poh, bootdev, bootparam, selftest"); + if (!argc || !strcmp(argv[0], "help")) { + lprintf(LOG_NOTICE, "Chassis Commands:\n" + " status, power, policy, restart_cause\n" + " poh, identify, selftest,\n" + " bootdev, bootparam, bootmbox"); } - else if (strncmp(argv[0], "status", 6) == 0) { + else if (!strcmp(argv[0], "status")) { rc = ipmi_chassis_status(intf); } - else if (strncmp(argv[0], "selftest", 8) == 0) { + else if (!strcmp(argv[0], "selftest")) { rc = ipmi_chassis_selftest(intf); } - else if (strncmp(argv[0], "power", 5) == 0) { + else if (!strcmp(argv[0], "power")) { uint8_t ctl = 0; - if ((argc < 2) || (strncmp(argv[1], "help", 4) == 0)) { + if (argc < 2 || !strcmp(argv[1], "help")) { lprintf(LOG_NOTICE, "chassis power Commands: status, on, off, cycle, reset, diag, soft"); - return 0; + rc = 0; + goto out; } - if (strncmp(argv[1], "status", 6) == 0) { + if (!strcmp(argv[1], "status")) { rc = ipmi_chassis_print_power_status(intf); - return rc; + goto out; } - if ((strncmp(argv[1], "up", 2) == 0) || (strncmp(argv[1], "on", 2) == 0)) + if (!strcmp(argv[1], "up") || + !strcmp(argv[1], "on")) + { ctl = IPMI_CHASSIS_CTL_POWER_UP; - else if ((strncmp(argv[1], "down", 4) == 0) || (strncmp(argv[1], "off", 3) == 0)) + } + else if (!strcmp(argv[1], "down") || + !strcmp(argv[1], "off")) + { ctl = IPMI_CHASSIS_CTL_POWER_DOWN; - else if (strncmp(argv[1], "cycle", 5) == 0) + } + else if (!strcmp(argv[1], "cycle")) ctl = IPMI_CHASSIS_CTL_POWER_CYCLE; - else if (strncmp(argv[1], "reset", 5) == 0) + else if (!strcmp(argv[1], "reset")) ctl = IPMI_CHASSIS_CTL_HARD_RESET; - else if (strncmp(argv[1], "diag", 4) == 0) + else if (!strcmp(argv[1], "diag")) ctl = IPMI_CHASSIS_CTL_PULSE_DIAG; - else if ((strncmp(argv[1], "acpi", 4) == 0) || (strncmp(argv[1], "soft", 4) == 0)) + else if (!strcmp(argv[1], "acpi") || + !strcmp(argv[1], "soft")) + { ctl = IPMI_CHASSIS_CTL_ACPI_SOFT; + } else { lprintf(LOG_ERR, "Invalid chassis power command: %s", argv[1]); - return -1; + goto out; } rc = ipmi_chassis_power_control(intf, ctl); } - else if (strncmp(argv[0], "identify", 8) == 0) { + else if (!strcmp(argv[0], "identify")) { if (argc < 2) { rc = ipmi_chassis_identify(intf, NULL); } - else if (strncmp(argv[1], "help", 4) == 0) { + else if (!strcmp(argv[1], "help")) { lprintf(LOG_NOTICE, "chassis identify <interval>"); lprintf(LOG_NOTICE, " default is 15 seconds"); lprintf(LOG_NOTICE, " 0 to turn off"); @@ -1203,14 +1966,14 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) rc = ipmi_chassis_identify(intf, argv[1]); } } - else if (strncmp(argv[0], "poh", 3) == 0) { + else if (!strcmp(argv[0], "poh")) { rc = ipmi_chassis_poh(intf); } - else if (strncmp(argv[0], "restart_cause", 13) == 0) { + else if (!strcmp(argv[0], "restart_cause")) { rc = ipmi_chassis_restart_cause(intf); } - else if (strncmp(argv[0], "policy", 4) == 0) { - if ((argc < 2) || (strncmp(argv[1], "help", 4) == 0)) { + else if (!strcmp(argv[0], "policy")) { + if (argc < 2 || !strcmp(argv[1], "help")) { lprintf(LOG_NOTICE, "chassis policy <state>"); lprintf(LOG_NOTICE, " list : return supported policies"); lprintf(LOG_NOTICE, " always-on : turn on when power is restored"); @@ -1218,13 +1981,13 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) lprintf(LOG_NOTICE, " always-off : stay off after power is restored"); } else { uint8_t ctl; - if (strncmp(argv[1], "list", 4) == 0) + if (!strcmp(argv[1], "list")) ctl = IPMI_CHASSIS_POLICY_NO_CHANGE; - else if (strncmp(argv[1], "always-on", 9) == 0) + else if (!strcmp(argv[1], "always-on")) ctl = IPMI_CHASSIS_POLICY_ALWAYS_ON; - else if (strncmp(argv[1], "previous", 8) == 0) + else if (!strcmp(argv[1], "previous")) ctl = IPMI_CHASSIS_POLICY_PREVIOUS; - else if (strncmp(argv[1], "always-off", 10) == 0) + else if (!strcmp(argv[1], "always-off")) ctl = IPMI_CHASSIS_POLICY_ALWAYS_OFF; else { lprintf(LOG_ERR, "Invalid chassis policy: %s", argv[1]); @@ -1233,21 +1996,26 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) rc = ipmi_chassis_power_policy(intf, ctl); } } - else if (strncmp(argv[0], "bootparam", 9) == 0) { - if ((argc < 3) || (strncmp(argv[1], "help", 4) == 0)) { + else if (!strcmp(argv[0], "bootparam")) { + if (argc < 3 || !strcmp(argv[1], "help")) { lprintf(LOG_NOTICE, "bootparam get <param #>"); ipmi_chassis_set_bootflag_help(); } else { - if (strncmp(argv[1], "get", 3) == 0) { - rc = ipmi_chassis_get_bootparam(intf, argv[2]); + if (!strcmp(argv[1], "get")) { + rc = ipmi_chassis_get_bootparam(intf, + argc - 2, + argv + 2, + 0); } - else if (strncmp(argv[1], "set", 3) == 0) { + else if (!strcmp(argv[1], "set")) { unsigned char set_flag=0; unsigned char clr_flag=0; - if (strncmp(argv[2], "help", 4) == 0 || - argc < 4 || (argc >= 4 && - strncmp(argv[2], "bootflag", 8) != 0)) { + if (!strcmp(argv[2], "help") + || argc < 4 + || (argc >= 4 + && strcmp(argv[2], "bootflag"))) + { ipmi_chassis_set_bootflag_help(); } else { if (argc == 5) { @@ -1263,8 +2031,8 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) lprintf(LOG_NOTICE, "bootparam get|set <option> [value ...]"); } } - else if (strncmp(argv[0], "bootdev", 7) == 0) { - if ((argc < 2) || (strncmp(argv[1], "help", 4) == 0)) { + else if (!strcmp(argv[0], "bootdev")) { + if (argc < 2 || !strcmp(argv[1], "help")) { lprintf(LOG_NOTICE, "bootdev <device> [clear-cmos=yes|no]"); lprintf(LOG_NOTICE, "bootdev <device> [options=help,...]"); lprintf(LOG_NOTICE, " none : Do not change boot device order"); @@ -1276,114 +2044,39 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv) lprintf(LOG_NOTICE, " bios : Force boot into BIOS Setup"); lprintf(LOG_NOTICE, " floppy: Force boot from Floppy/primary removable media"); } else { - if (argc < 3) - rc = ipmi_chassis_set_bootdev(intf, argv[1], NULL); - else if (strncmp(argv[2], "clear-cmos=", 11) == 0) { - if (strncmp(argv[2]+11, "yes", 3) == 0) { - uint8_t flags[5] = {0, (1<<7), 0, 0, 0}; - rc = ipmi_chassis_set_bootdev(intf, argv[1], flags); - } else - rc = ipmi_chassis_set_bootdev(intf, argv[1], NULL); - } - else if (strncmp(argv[2], "options=", 8) == 0) { - char *token; - char *saveptr = NULL; - int optionError = 0; - unsigned char flags[5]; - static struct { - char *name; - int i; - unsigned char mask; - unsigned char value; - char *desc; - } options[] = { - /* data 1 */ - {"valid", 0, (1<<7), (1<<7), - "Boot flags valid"}, - {"persistent", 0, (1<<6), (1<<6), - "Changes are persistent for all future boots"}, - {"efiboot", 0, (1<<5), (1<<5), - "Extensible Firmware Interface Boot (EFI)"}, - /* data 2 */ - {"clear-cmos", 1, (1<<7), (1<<7), - "CMOS clear"}, - {"lockkbd", 1, (1<<6), (1<<6), - "Lock Keyboard"}, - /* data2[5:2] is parsed elsewhere */ - {"screenblank", 1, (1<<1), (1<<1), - "Screen Blank"}, - {"lockoutreset", 1, (1<<0), (1<<0), - "Lock out Resetbuttons"}, - /* data 3 */ - {"lockout_power", 2, (1<<7), (1<<7), - "Lock out (power off/sleep request) via Power Button"}, - {"verbose=default", 2, (3<<5), (0<<5), - "Request quiet BIOS display"}, - {"verbose=no", 2, (3<<5), (1<<5), - "Request quiet BIOS display"}, - {"verbose=yes", 2, (3<<5), (2<<5), - "Request verbose BIOS display"}, - {"force_pet", 2, (1<<4), (1<<4), - "Force progress event traps"}, - {"upw_bypass", 2, (1<<3), (1<<3), - "User password bypass"}, - {"lockout_sleep", 2, (1<<2), (1<<2), - "Log Out Sleep Button"}, - {"cons_redirect=default", 2, (3<<0), (0<<0), - "Console redirection occurs per BIOS configuration setting"}, - {"cons_redirect=skip", 2, (3<<0), (1<<0), - "Suppress (skip) console redirection if enabled"}, - {"cons_redirect=enable", 2, (3<<0), (2<<0), - "Suppress (skip) console redirection if enabled"}, - /* data 4 */ - /* data4[7:4] reserved */ - /* data4[3] BIOS Shared Mode Override, not implemented here */ - /* data4[2:0] BIOS Mux Control Override, not implemented here */ - - /* data5 reserved */ - - {NULL} /* End marker */ - }, *op; - - memset(&flags[0], 0, sizeof(flags)); - token = strtok_r(argv[2] + 8, ",", &saveptr); - while (token != NULL) { - if (strcmp(token, "help") == 0) { - optionError = 1; - break; - } - for (op = options; op->name != NULL; ++op) { - if (strcmp(token, op->name) == 0) { - flags[op->i] &= op->mask; - flags[op->i] |= op->value; - break; - } + static const char *kw = "options="; + char *optstr = NULL; + uint8_t flags[BF_BYTE_COUNT]; + bool use_flags = false; + + if (argc >= 3) { + if (!strcmp(argv[2], "clear-cmos=yes")) { + /* Exclusive clear-cmos, no other flags */ + optstr = "clear-cmos"; } - if (op->name == NULL) { - /* Option not found */ - optionError = 1; - lprintf(LOG_ERR, "Invalid option: %s", token); + else if (!strncmp(argv[2], kw, strlen(kw))) { + optstr = argv[2] + strlen(kw); } - token = strtok_r(NULL, ",", &saveptr); } - if (optionError) { - lprintf(LOG_NOTICE, "Legal options settings are:"); - lprintf(LOG_NOTICE, "\thelp:\tprint this message"); - for (op = options; op->name != NULL; ++op) { - lprintf(LOG_NOTICE, "\t%s:\t%s", op->name, op->desc); - } - return (-1); + if (optstr) { + if (!bootdev_parse_options(optstr, flags)) + goto out; + + use_flags = true; } - rc = ipmi_chassis_set_bootdev(intf, argv[1], flags); - } - else - rc = ipmi_chassis_set_bootdev(intf, argv[1], NULL); + rc = ipmi_chassis_set_bootdev(intf, argv[1], + use_flags + ? flags + : NULL); } } + else if (!strcmp(argv[0], "bootmbox")) { + rc = chassis_bootmailbox(intf, argc -1, argv + 1); + } else { lprintf(LOG_ERR, "Invalid chassis command: %s", argv[0]); - return -1; } +out: return rc; } |