Blob Blame History Raw
From ca5cc560f60ebe601b5219699f56f99938895b4b Mon Sep 17 00:00:00 2001
From: Jim Mankovich <jmank@hp.com>
Date: Tue, 11 Mar 2014 10:26:43 -0600
Subject: [PATCH] Add options to chassis bootparam set bootflag


Signed-off-by: Jim Mankovich <jmank@hp.com>
---
 doc/ipmitool.1     |   39 ++++++++-
 lib/ipmi_chassis.c |  233 ++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 255 insertions(+), 17 deletions(-)

diff --git a/doc/ipmitool.1 b/doc/ipmitool.1
index a73d564..b35eff8 100644
--- a/doc/ipmitool.1
+++ b/doc/ipmitool.1
@@ -2309,14 +2309,16 @@ Get boot parameter. Currently supported values for <\fBparam #\fR> are:
 .br 
 
 .TP 
-\fIset\fP <\fBoption\fR> [\fBvalue ...\fR]
+\fIset\fP <\fBdevice\fR> [<\fIoptions\fP=\fBhelp,...\fR>]
 .br 
 
-Set boot parameter.
+Set boot device parameter used for next boot.  Various options may be used
+to change when the the next boot device is cleared.
+Run \fI"options=help"\fP for a list of available bootparam set device options.
 
 .RS
 .TP 
-Currently supported values for \fB<option>\fR are:
+Currently supported bootparam \fBdevice\fR settings are:
 .TP 
 \fIforce_pxe\fP
 .br 
@@ -2349,6 +2351,37 @@ Force boot from CD/DVD
 Force boot into BIOS setup
 
 .RE
+.RS
+.TP 
+Currently supported bootparam \fBoptions\fR settings are associated with BMC Boot Valid Bit Clearing and are as follows:   Any option can be prefixed with "no-" to invert the sense of the operation.
+.TP 
+\fIPEF\fP
+.br 
+
+Clear valid bit on reset/power cycle caused by PEF
+.TP 
+\fItimeout\fP
+.br 
+
+Automatically clear boot flag valid bit if Chassis Control command is
+not received within 60 seconds.
+.TP 
+\fIwatchdog\fP
+.br 
+
+Clear valid bit on reset/power cycle caused by watchdog timeout
+.TP 
+\fIreset\fP
+.br 
+
+Clear valid bit on push button reset / soft-reset
+.TP 
+\fIpower\fP
+.br 
+
+Clear valid bit on power up via power push button or wake event
+
+.RE
 .RE
 .RE
 .RE
diff --git a/lib/ipmi_chassis.c b/lib/ipmi_chassis.c
index 2d47974..d4e88ee 100644
--- a/lib/ipmi_chassis.c
+++ b/lib/ipmi_chassis.c
@@ -755,6 +755,193 @@ ipmi_chassis_get_bootparam(struct ipmi_intf * intf, char * arg)
 }
 
 static int
+get_bootparam_options(char *optstring,
+		unsigned char *set_flag, unsigned char *clr_flag)
+{
+	char *token;
+	char *saveptr = NULL;
+	int optionError = 0;
+	*set_flag = 0;
+	*clr_flag = 0;
+	static struct {
+		char *name;
+		unsigned char value;
+		char *desc;
+	} options[] = {
+	{"PEF",          0x10,
+	    "Clear valid bit on reset/power cycle cause by PEF"},
+	{"timeout",      0x08,
+	    "Automatically clear boot flag valid bit on timeout"},
+	{"watchdog",     0x04,
+	    "Clear valid bit on reset/power cycle cause by watchdog"},
+	{"reset",        0x02,
+	    "Clear valid bit on push button reset/soft reset"},
+	{"power", 0x01,
+	    "Clear valid bit on power up via power push button or wake event"},
+
+	{NULL}	/* End marker */
+	}, *op;
+
+	if (strncmp(optstring, "options=", 8) != 0) {
+		lprintf(LOG_ERR, "No options= keyword found \"%s\"", optstring);
+		return -1;
+	}
+	token = strtok_r(optstring + 8, ",", &saveptr);
+	while (token != NULL) {
+		int setbit = 0;
+		if (strcmp(token, "help") == 0) {
+			optionError = 1;
+			break;
+		}
+		if (strncmp(token, "no-", 3) == 0) {
+			setbit = 1;
+			token += 3;
+		}
+		for (op = options; op->name != NULL; ++op) {
+			if (strncmp(token, op->name, strlen(op->name)) == 0) {
+				if (setbit) {
+				    *set_flag |= op->value;
+				} else {
+				    *clr_flag |= op->value;
+				}
+				break;
+			}
+		}
+		if (op->name == NULL) {
+			/* Option not found */
+			optionError = 1;
+			if (setbit) {
+				token -=3;
+			}
+			lprintf(LOG_ERR, "Invalid option: %s", token);
+		}
+		token = strtok_r(NULL, ",", &saveptr);
+	}
+	if (optionError) {
+		lprintf(LOG_NOTICE, " Legal options are:");
+		lprintf(LOG_NOTICE, "  %-8s: print this message", "help");
+		for (op = options; op->name != NULL; ++op) {
+			lprintf(LOG_NOTICE, "  %-8s: %s", op->name, op->desc);
+		}
+		lprintf(LOG_NOTICE, " Any Option may be prepended with no-"
+				    " to invert sense of operation\n");
+		return (-1);
+	}
+	return (0);
+}
+
+static int
+ipmi_chassis_get_bootvalid(struct ipmi_intf * intf)
+{
+	struct ipmi_rs * rsp;
+	struct ipmi_rq req;
+	uint8_t msg_data[3];
+	uint8_t param_id = IPMI_CHASSIS_BOOTPARAM_FLAG_VALID;
+	memset(msg_data, 0, 3);
+
+	msg_data[0] = param_id & 0x7f;
+	msg_data[1] = 0;
+	msg_data[2] = 0;
+
+	memset(&req, 0, sizeof(req));
+	req.msg.netfn = IPMI_NETFN_CHASSIS;
+	req.msg.cmd = 0x9;
+	req.msg.data = msg_data;
+	req.msg.data_len = 3;
+
+	rsp = intf->sendrecv(intf, &req);
+	if (rsp == NULL) {
+		lprintf(LOG_ERR,
+			"Error Getting Chassis Boot Parameter %d", param_id);
+		return -1;
+	}
+	if (rsp->ccode > 0) {
+		lprintf(LOG_ERR, "Get Chassis Boot Parameter %d failed: %s",
+			param_id, val2str(rsp->ccode, completion_code_vals));
+		return -1;
+	}
+
+	if (verbose > 2)
+		printbuf(rsp->data, rsp->data_len, "Boot Option");
+
+	return(rsp->data[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;
+
+	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);
+
+	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;
+	}
+
+	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);
+		}
+	}
+
+	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 rc;
+}
+
+static int
 ipmi_chassis_set_bootdev(struct ipmi_intf * intf, char * arg, uint8_t *iflags)
 {
 	uint8_t flags[5];
@@ -946,6 +1133,23 @@ ipmi_power_main(struct ipmi_intf * intf, int argc, char ** argv)
 	return rc;
 }
 
+void
+ipmi_chassis_set_bootflag_help()
+{
+	unsigned char set_flag;
+	unsigned char clr_flag;
+	lprintf(LOG_NOTICE, "bootparam set bootflag <device> [options=...]");
+	lprintf(LOG_NOTICE, " Legal devices are:");
+	lprintf(LOG_NOTICE, "  none        : No override");
+	lprintf(LOG_NOTICE, "  force_pxe   : Force PXE boot");
+	lprintf(LOG_NOTICE, "  force_disk  : Force boot from default Hard-drive");
+	lprintf(LOG_NOTICE, "  force_safe  : Force boot from default Hard-drive, request Safe Mode");
+	lprintf(LOG_NOTICE, "  force_diag  : Force boot from Diagnostic Partition");
+	lprintf(LOG_NOTICE, "  force_cdrom : Force boot from CD/DVD");
+	lprintf(LOG_NOTICE, "  force_bios  : Force boot into BIOS Setup");
+	get_bootparam_options("options=help", &set_flag, &clr_flag);
+}
+
 int
 ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv)
 {
@@ -1036,26 +1240,27 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv)
 	else if (strncmp(argv[0], "bootparam", 9) == 0) {
 		if ((argc < 3) || (strncmp(argv[1], "help", 4) == 0)) {
 			lprintf(LOG_NOTICE, "bootparam get <param #>");
-			lprintf(LOG_NOTICE, "bootparam set bootflag <flag>");
-			lprintf(LOG_NOTICE, "  force_pxe   : Force PXE boot");
-			lprintf(LOG_NOTICE, "  force_disk  : Force boot from default Hard-drive");
-			lprintf(LOG_NOTICE, "  force_safe  : Force boot from default Hard-drive, request Safe Mode");
-			lprintf(LOG_NOTICE, "  force_diag  : Force boot from Diagnostic Partition");
-			lprintf(LOG_NOTICE, "  force_cdrom : Force boot from CD/DVD");
-			lprintf(LOG_NOTICE, "  force_bios  : Force boot into BIOS Setup");
+		    ipmi_chassis_set_bootflag_help();
 		}
 		else {
 			if (strncmp(argv[1], "get", 3) == 0) {
 				rc = ipmi_chassis_get_bootparam(intf, argv[2]);
 			}
 			else if (strncmp(argv[1], "set", 3) == 0) {
-				if (argc < 4) {
-					lprintf(LOG_NOTICE, "bootparam set <option> [value ...]");
+			    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)) {
+					ipmi_chassis_set_bootflag_help();
 				} else {
-					if (strncmp(argv[2], "bootflag", 8) == 0)
-						rc = ipmi_chassis_set_bootdev(intf, argv[3], NULL);
-					else
-						lprintf(LOG_NOTICE, "bootparam set <option> [value ...]");
+					if (argc == 5) {
+						get_bootparam_options(argv[4], &set_flag, &clr_flag);
+					}
+					rc = ipmi_chassis_set_bootdev(intf, argv[3], NULL);
+					if (argc == 5 && (set_flag != 0 || clr_flag != 0)) {
+						rc = ipmi_chassis_set_bootvalid(intf, set_flag, clr_flag);
+					}
 				}
 			}
 			else
@@ -1166,7 +1371,7 @@ ipmi_chassis_main(struct ipmi_intf * intf, int argc, char ** argv)
 				token = strtok_r(NULL, ",", &saveptr);
 			}
 			if (optionError) {
-				lprintf(LOG_NOTICE, "Legal options are:");
+				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);
-- 
1.7.9.5