All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution
@ 2016-01-20 10:37 Hidehiro Kawai
  2016-01-20 10:37 ` [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory Hidehiro Kawai
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Hidehiro Kawai @ 2016-01-20 10:37 UTC (permalink / raw)
  To: Simon Horman, Eric W. Biederman; +Cc: kexec, Corey Minyard

If the second kernel for crash dumping hangs up while booting, no
information related to the first kernel will be saved.  This makes
crash cause analysis difficult.  So, some enterprise users want to
save minimal information befor booting the second kernel.

One of the approaches is to use panic notifier call or pstore
feature.  For example, a panic notifier callback registered by IPMI
driver saves the panic message to BMC's SEL before booting the second
kernel.  Similarly, pstore saves kernel logs to a non-volatile memory
on the server.  However, since these functionalities run with crashed
kernel, they may fail to complete their work and boot the second
kernel.

So, another approach; saving minimal information to BMC's SEL in the
purgatory.  Since the purgatory code doesn't rely on the crashed
kernel, we can run it safely after verifying the hash of the code.

This patch set is the first step to the final goal; it provides
a basic support for IPMI command execution in purgatory.  IPMI
specification defines multiple interfaces to BMC, and this patch set
uses one of them, KCS I/F, which talks with BMC via I/O port like
keyboard controllers.  As a use case for that, options to start/stop
BMC's watchdog timer before booting the second kernel are also
provided.  These options are useful for the cases where:

 - you want to automatically reboot the server when the second kernel
   hangs up while booting
 - you want to prevent the second kernel from being stopped by the
   watchdog timer enabled while the first kernel is running

If the BMC doesn't work well, the IPMI command execution can take
indefinite time and fail to boot the second kernel.  To avoid this,
timeout logic based on RTC polling is also implemented.

NOTE: This is an RFC version, so some parts are incomplete; these
codes are unconditionally built into the kexec binary, and I/O ports
for KCS I/F and timeout (5 seconds) are hard-coded, and etc.

Future plan:
Add an option to save the panic message and instruction pointers to
BMC's SEL in purgatory.  To realize this, we first need to pass the
panic message to the purgatory.  Instruction pointers are already
passed to the second kernel through ELF notes, so just read them.

---

Hidehiro Kawai (4):
      purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory
      purgatory: Introduce timeout API
      purgatory/x86: Support CMOS RTC
      purgatory/ipmi: Add timeout logic to IPMI command processing


 kexec/ipmi.h                   |    9 +
 kexec/kexec.c                  |   18 ++
 kexec/kexec.h                  |    6 +
 purgatory/Makefile             |    5 +
 purgatory/arch/i386/Makefile   |    1 
 purgatory/arch/i386/rtc_cmos.c |  104 ++++++++++++++
 purgatory/arch/x86_64/Makefile |    1 
 purgatory/include/purgatory.h  |    3 
 purgatory/include/time.h       |   33 +++++
 purgatory/ipmi.c               |  293 ++++++++++++++++++++++++++++++++++++++++
 purgatory/purgatory.c          |    4 +
 purgatory/time.c               |   58 ++++++++
 12 files changed, 533 insertions(+), 2 deletions(-)
 create mode 100644 kexec/ipmi.h
 create mode 100644 purgatory/arch/i386/rtc_cmos.c
 create mode 100644 purgatory/include/time.h
 create mode 100644 purgatory/ipmi.c
 create mode 100644 purgatory/time.c


-- 
Hidehiro Kawai
Hitachi, Ltd. Research & Development Group



_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory
  2016-01-20 10:37 [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Hidehiro Kawai
@ 2016-01-20 10:37 ` Hidehiro Kawai
  2016-01-21 15:40   ` Corey Minyard
  2016-01-20 10:37 ` [RFC PATCH 2/4] purgatory: Introduce timeout API Hidehiro Kawai
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Hidehiro Kawai @ 2016-01-20 10:37 UTC (permalink / raw)
  To: Simon Horman, Eric W. Biederman; +Cc: kexec, Corey Minyard

This patch adds an interface to BMC via KCS I/F and a functionality
to start/stop BMC watchdog timer in purgatory.  Starting the watchdog
timer is useful to automatically reboot the server when we fail to
boot the second kernel.  Stopping the watchdog timer is useful to
prevent the second kernel from being stopped by the watchdog timer
enabled while the first kernel is running.

If you specify --ipmi-wdt-start or --ipmi-wdt-stop option to kexec
command, BMC's watchdog timer will start or stop respectively while
executing purgatory.  You can't specify the both options at the same
time.  The start operation doesn't change the parameters of the
watchdog timer such as initial counter and action, you need to set
those parameters in the first OS.  On the other hand, the stop
operation changes the parameters.  You need to reset them when you
want to reuse the watchdog timer.

Signed-off-by: Hidehiro Kawai <hidehiro.kawai.ez@hitachi.com>
---
 kexec/ipmi.h                  |    9 ++
 kexec/kexec.c                 |   18 +++
 kexec/kexec.h                 |    6 +
 purgatory/Makefile            |    1 
 purgatory/include/purgatory.h |    3 +
 purgatory/ipmi.c              |  232 +++++++++++++++++++++++++++++++++++++++++
 purgatory/purgatory.c         |    4 +
 7 files changed, 272 insertions(+), 1 deletion(-)
 create mode 100644 kexec/ipmi.h
 create mode 100644 purgatory/ipmi.c

diff --git a/kexec/ipmi.h b/kexec/ipmi.h
new file mode 100644
index 0000000..395a2c7
--- /dev/null
+++ b/kexec/ipmi.h
@@ -0,0 +1,9 @@
+#ifndef IPMI_H
+#define IPMI_H
+
+/* Options for IPMI code excuted in purgatory */
+#define IPMI_WDT_DO_NOTHING	0
+#define IPMI_WDT_START		(1 << 0)
+#define IPMI_WDT_STOP		(1 << 1)
+
+#endif /* IPMI_H */
diff --git a/kexec/kexec.c b/kexec/kexec.c
index f0bd527..8a8f268 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -49,6 +49,7 @@
 #include "kexec-sha256.h"
 #include "kexec-zlib.h"
 #include "kexec-lzma.h"
+#include "ipmi.h"
 #include <arch/options.h>
 
 unsigned long long mem_min = 0;
@@ -57,6 +58,7 @@ static unsigned long kexec_flags = 0;
 /* Flags for kexec file (fd) based syscall */
 static unsigned long kexec_file_flags = 0;
 int kexec_debug = 0;
+int opt_ipmi_wdt = IPMI_WDT_DO_NOTHING;
 
 void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr)
 {
@@ -643,6 +645,10 @@ static void update_purgatory(struct kexec_info *info)
 	if (!info->rhdr.e_shdr) {
 		return;
 	}
+
+	elf_rel_set_symbol(&info->rhdr, "ipmi_wdt", &opt_ipmi_wdt,
+			   sizeof(opt_ipmi_wdt));
+
 	arch_update_purgatory(info);
 	memset(region, 0, sizeof(region));
 	sha256_starts(&ctx);
@@ -1345,6 +1351,12 @@ int main(int argc, char *argv[])
 		case OPT_KEXEC_FILE_SYSCALL:
 			/* We already parsed it. Nothing to do. */
 			break;
+		case OPT_IPMI_WDT_START:
+			opt_ipmi_wdt |= IPMI_WDT_START;
+			break;
+		case OPT_IPMI_WDT_STOP:
+			opt_ipmi_wdt |= IPMI_WDT_STOP;
+			break;
 		default:
 			break;
 		}
@@ -1370,6 +1382,12 @@ int main(int argc, char *argv[])
 		    "\"--mem-max\" parameter\n");
 	}
 
+	if ((opt_ipmi_wdt & IPMI_WDT_START) &&
+	    (opt_ipmi_wdt & IPMI_WDT_STOP)) {
+		die("You can't specify both --ipmi-wdt-start and "
+		    "--ipmi-wdt-stop\n");
+	}
+
 	fileind = optind;
 	/* Reset getopt for the next pass; called in other source modules */
 	opterr = 1;
diff --git a/kexec/kexec.h b/kexec/kexec.h
index c02ac8f..4638866 100644
--- a/kexec/kexec.h
+++ b/kexec/kexec.h
@@ -224,7 +224,9 @@ extern int file_types;
 #define OPT_LOAD_PRESERVE_CONTEXT 259
 #define OPT_LOAD_JUMP_BACK_HELPER 260
 #define OPT_ENTRY		261
-#define OPT_MAX			262
+#define OPT_IPMI_WDT_START	262
+#define OPT_IPMI_WDT_STOP	263
+#define OPT_MAX			264
 #define KEXEC_OPTIONS \
 	{ "help",		0, 0, OPT_HELP }, \
 	{ "version",		0, 0, OPT_VERSION }, \
@@ -244,6 +246,8 @@ extern int file_types;
 	{ "reuseinitrd",	0, 0, OPT_REUSE_INITRD }, \
 	{ "kexec-file-syscall",	0, 0, OPT_KEXEC_FILE_SYSCALL }, \
 	{ "debug",		0, 0, OPT_DEBUG }, \
+	{ "ipmi-wdt-start",	0, 0, OPT_IPMI_WDT_START }, \
+	{ "ipmi-wdt-stop",	0, 0, OPT_IPMI_WDT_STOP }, \
 
 #define KEXEC_OPT_STR "h?vdfxyluet:ps"
 
diff --git a/purgatory/Makefile b/purgatory/Makefile
index 2b5c061..ae1d2bd 100644
--- a/purgatory/Makefile
+++ b/purgatory/Makefile
@@ -9,6 +9,7 @@
 
 PURGATORY = purgatory/purgatory.ro
 PURGATORY_SRCS =
+PURGATORY_SRCS += purgatory/ipmi.c
 PURGATORY_SRCS += purgatory/purgatory.c
 PURGATORY_SRCS += purgatory/printf.c
 PURGATORY_SRCS += purgatory/string.c
diff --git a/purgatory/include/purgatory.h b/purgatory/include/purgatory.h
index 788ce49..9b9ffbe 100644
--- a/purgatory/include/purgatory.h
+++ b/purgatory/include/purgatory.h
@@ -1,11 +1,14 @@
 #ifndef PURGATORY_H
 #define PURGATORY_H
 
+extern int ipmi_wdt;
+
 void putchar(int ch);
 void sprintf(char *buffer, const char *fmt, ...)
 	__attribute__ ((format (printf, 2, 3)));
 void printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
 void setup_arch(void);
 void post_verification_setup_arch(void);
+void ipmi_wdt_start_stop(void);
 
 #endif /* PURGATORY_H */
diff --git a/purgatory/ipmi.c b/purgatory/ipmi.c
new file mode 100644
index 0000000..0acb853
--- /dev/null
+++ b/purgatory/ipmi.c
@@ -0,0 +1,232 @@
+#include <sys/io.h>
+#include <purgatory.h>
+#include "../kexec/ipmi.h"
+
+#define KCS_PORT_DEFAULT	0xca2
+#define KCS_PORT_DATA		(kcs_port + 0)
+#define KCS_PORT_STATUS		(kcs_port + 1)
+#define KCS_PORT_CMD		(kcs_port + 1)
+
+#define KCS_STATUS_OBF		0x1
+#define KCS_STATUS_IBF		0x2
+
+#define KCS_CMD_WRITE_START	0x61
+#define KCS_CMD_WRITE_END	0x62
+#define KCS_CMD_READ		0x68
+
+#define GET_STATUS_STATE(status) (((status) >> 6) & 0x03)
+#define KCS_IDLE_STATE	0x0
+#define KCS_READ_STATE	0x1
+#define KCS_WRITE_STATE	0x2
+#define KCS_ERROR_STATE	0x3
+
+int kcs_port = KCS_PORT_DEFAULT;
+int ipmi_wdt = IPMI_WDT_DO_NOTHING;
+
+/* IPMI command to start BMC watchdog timer */
+const unsigned char cmd_start_wdt[] = {
+	0x06 << 2,	/* App */
+	0x22,		/* Reset Watchdog Timer */
+};
+
+/* IPMI command to stop BMC watchdog timer */
+const unsigned char cmd_stop_wdt[] = {
+	0x06 << 2,	/* App */
+	0x24,		/* Set Watchdog Timer Command */
+	0x84,		/* Timer Use: don't log, and SMS/OS use */
+	0x00,		/* Timer Actions: no action */
+	0x00,		/* Pre-timeout interval: 0 */
+	0x10,		/* Timer Use Expiration flag clear: SMS/OS */
+	0xff,		/* Initial countdown value: 0xffff */
+	0xff,
+};
+
+static inline unsigned char read_status(void)
+{
+	return inb(KCS_PORT_STATUS);
+}
+
+unsigned char wait_out(void)
+{
+	unsigned char status;
+
+	do {
+		status = read_status();
+	} while ((status & KCS_STATUS_OBF) == 0);
+
+	return status;
+}
+
+unsigned char wait_in(void)
+{
+	unsigned char status;
+
+	do {
+		status = read_status();
+	} while (status & KCS_STATUS_IBF);
+
+	return status;
+}
+
+unsigned char read_data(void)
+{
+	wait_out();
+	return inb(KCS_PORT_DATA);
+}
+
+void clear_obf(void)
+{
+	if (inb(KCS_PORT_STATUS) & KCS_STATUS_OBF)
+		read_data();
+}
+
+unsigned char write_data(unsigned char byte)
+{
+	clear_obf();
+	outb(byte, KCS_PORT_DATA);
+	return wait_in();
+}
+
+unsigned char write_cmd(unsigned char byte)
+{
+	clear_obf();
+	outb(byte, KCS_PORT_CMD);
+	return wait_in();
+}
+
+/*
+ * Issue a given IPMI command via KCS I/F.
+ *
+ * Return 0 on success, otherwise -1.
+ */
+int write_ipmi_cmd(const unsigned char *cmd, int size)
+{
+	unsigned char status;
+	int i;
+
+	wait_in();
+	status = write_cmd(KCS_CMD_WRITE_START);
+	if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
+		return -1;
+
+	for (i = 0; i < size - 1; i++) {
+		status = write_data(cmd[i]);
+
+		if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
+			return -1;
+	}
+
+	/* last write */
+	status = write_cmd(KCS_CMD_WRITE_END);
+	if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
+		return -1;
+
+	write_data(cmd[i]);
+
+	return 0;
+}
+
+/*
+ * Read result bytes for the previously issued IPMI command.
+ *
+ * Return the completion code, which is 0 on success.  Otherwise, return
+ * non-zero value.
+ */
+unsigned char read_result(void)
+{
+	unsigned char state;
+	unsigned char data[4] = { 0 };
+	int count = 0;
+
+	while (1) {
+		state = GET_STATUS_STATE(wait_in());
+		if (state == KCS_READ_STATE) {
+			data[count] = read_data();
+			outb(KCS_CMD_READ, KCS_PORT_DATA);
+		} else if (state == KCS_IDLE_STATE) {
+			data[count] = read_data();
+			break;
+		} else {
+			/*
+			 * Error! Set 0xff (unspecified error) as the
+			 * completion code.
+			 */
+			data[2] = 0xff;
+			break;
+		}
+
+		/*
+		 * We are interested only in the completion code in the
+		 * 3rd byte. Skip the following bytes.
+		 */
+		if (count < 3)
+			count++;
+	}
+
+	return data[2]; /* Return completion code */
+}
+
+/*
+ * Issue one IPMI command and check the result.
+ *
+ * Return 0 on success, otherwise -1.
+ */
+int issue_ipmi_cmd(const unsigned char *cmd, int size)
+{
+	int i, ret;
+	unsigned char comp_code;
+
+	/*
+	 * Retry 3 times at most on error.
+	 *
+	 * write_ipmi_cmd() issues WRITE_START KCS command at the beginning,
+	 * which aborts the ongoing IPMI command and starts new one.  So
+	 * basically, write_ipmi_cmd() will succeed anytime.
+	 *
+	 * This command abort can fail on some BMC depending on its KCS I/F
+	 * state, and  we get ERROR_STATE from the status register in that
+	 * case.  However, there is no problem.  We can recover it by
+	 * simply retrying from writing WRITE_START KCS command.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = write_ipmi_cmd(cmd, size);
+		if (ret < 0)
+			continue;
+
+		comp_code = read_result();
+		if (comp_code == 0)
+			break; /* succeeded */
+	}
+
+	return (i < 3) ? 0 : -1;
+}
+
+int do_start_wdt(void)
+{
+	int ret;
+
+	printf("IPMI: starting watchdog timer...");
+	ret = issue_ipmi_cmd(cmd_start_wdt, sizeof(cmd_start_wdt));
+	printf("done\n");
+
+	return ret;
+}
+
+int do_stop_wdt(void)
+{
+	int ret;
+
+	printf("IPMI: stopping watchdog timer...");
+	ret = issue_ipmi_cmd(cmd_stop_wdt, sizeof(cmd_stop_wdt));
+	printf("done\n");
+
+	return ret;
+}
+
+void ipmi_wdt_start_stop(void)
+{
+	if (ipmi_wdt & IPMI_WDT_START)
+		do_start_wdt();
+	else if (ipmi_wdt & IPMI_WDT_STOP)
+		do_stop_wdt();
+}
diff --git a/purgatory/purgatory.c b/purgatory/purgatory.c
index 3bbcc09..f60879d 100644
--- a/purgatory/purgatory.c
+++ b/purgatory/purgatory.c
@@ -48,5 +48,9 @@ void purgatory(void)
 			/* loop forever */
 		}
 	}
+
+	if (ipmi_wdt)
+		ipmi_wdt_start_stop();
+
 	post_verification_setup_arch();
 }



_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/4] purgatory: Introduce timeout API
  2016-01-20 10:37 [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Hidehiro Kawai
  2016-01-20 10:37 ` [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory Hidehiro Kawai
@ 2016-01-20 10:37 ` Hidehiro Kawai
  2016-01-20 10:37 ` [RFC PATCH 3/4] purgatory/x86: Support CMOS RTC Hidehiro Kawai
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Hidehiro Kawai @ 2016-01-20 10:37 UTC (permalink / raw)
  To: Simon Horman, Eric W. Biederman; +Cc: kexec, Corey Minyard

This patch introduces timeout API for purgatory code.

Because we don't want to use interrupts in purgatory, this timeout
API is based on polling of the clock.

If you want to start a timer, call init_timeout() with seconds to
the timeout and an instance of struct timeout_info which manages a
virtual timer.  Then, you call EXIT_ON_TIMEOUT() macro periodically
to check if the timeout happens.  If it happens, the control jumps
to timed_out label for timeout handling.  This means you need to
implement timed_out label in each caller function.

To make this timeout API work, an actual implementation of
get_unix_time() which returns a UNIX epoch time is needed.

Signed-off-by: Hidehiro Kawai <hidehiro.kawai.ez@hitachi.com>
---
 purgatory/Makefile       |    4 ++-
 purgatory/include/time.h |   33 ++++++++++++++++++++++++++
 purgatory/time.c         |   58 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 1 deletion(-)
 create mode 100644 purgatory/include/time.h
 create mode 100644 purgatory/time.c

diff --git a/purgatory/Makefile b/purgatory/Makefile
index ae1d2bd..80caeab 100644
--- a/purgatory/Makefile
+++ b/purgatory/Makefile
@@ -13,10 +13,12 @@ PURGATORY_SRCS += purgatory/ipmi.c
 PURGATORY_SRCS += purgatory/purgatory.c
 PURGATORY_SRCS += purgatory/printf.c
 PURGATORY_SRCS += purgatory/string.c
+PURGATORY_SRCS += purgatory/time.c
 PURGATORY_MAP = purgatory/purgatory.map
 
 dist += purgatory/Makefile $(PURGATORY_SRCS)				\
-	purgatory/include/purgatory.h purgatory/include/string.h
+	purgatory/include/purgatory.h purgatory/include/string.h	\
+	purgatory/include/time.h
 
 include $(srcdir)/purgatory/arch/alpha/Makefile
 include $(srcdir)/purgatory/arch/arm/Makefile
diff --git a/purgatory/include/time.h b/purgatory/include/time.h
new file mode 100644
index 0000000..edaf692
--- /dev/null
+++ b/purgatory/include/time.h
@@ -0,0 +1,33 @@
+#ifndef TIME_H
+#define TIME_H
+
+typedef long long time64_t;
+
+struct timeout_info {
+	time64_t start;
+	time64_t end;
+	int timed_out;
+};
+
+time64_t date2unix(int year, int mon, int day, int hour, int min, int sec);
+int check_timeout(struct timeout_info *toi);
+void init_timeout(struct timeout_info *toi, time64_t left);
+
+/**
+ * Check if the timeout happens, and if so, jump to timed_out label.
+ *
+ * @toi: an instance of struct timeout_info initialized by init_timeout().
+ * @check_clock: if this value is 0, this macro doesn't check the actual
+ *               clock.  This is useful for the exit path after timeout.
+ */
+#define EXIT_ON_TIMEOUT(toi, check_clock)       \
+do {                                    	\
+	if ((toi)->timed_out)			\
+		goto timed_out;			\
+	if (check_timeout((toi))) {		\
+		(toi)->timed_out = 1;		\
+		goto timed_out;			\
+	}					\
+} while (0)
+
+#endif /* TIME_H */
diff --git a/purgatory/time.c b/purgatory/time.c
new file mode 100644
index 0000000..0732670
--- /dev/null
+++ b/purgatory/time.c
@@ -0,0 +1,58 @@
+#include "time.h"
+
+/* Calculate unix-epoch time based on Julian day number calculation. */
+time64_t date2unix(int year, int mon, int day, int hour, int min, int sec)
+{
+	time64_t days, seconds;
+
+	mon -= 3;
+	if (mon < 0) {
+		year--;
+		mon += 12;
+	}
+
+	/* days since 1970/1/1 (= 0 day) */
+	days = 365*year + year/4 - year/100 + year/400 + (153*mon + 2)/5 +
+		day - 719469;
+
+	seconds = ((days*24 + hour)*60 + min)*60 + sec;
+
+	return seconds;
+}
+
+/*
+ * Dummy for systems which doesn't have clock access implementation.
+ * You need to override this to enable the timeout mechanism.
+ */
+time64_t __attribute__((weak)) get_unix_time(void)
+{
+	/* This means it never times out. */
+	return 0;
+}
+
+int check_timeout(struct timeout_info *toi)
+{
+	time64_t now;
+
+	if (toi->timed_out)
+		return 1;
+
+	now = get_unix_time();
+	if (toi->end <= now)
+		toi->timed_out = 1;
+
+	return toi->timed_out;
+}
+
+void init_timeout(struct timeout_info *toi, time64_t left)
+{
+	toi->start = get_unix_time();
+
+	/*
+	 * The precision of RTC is one second.  To wait at least specified
+	 * seconds, we do +1.
+	 */
+	toi->end = toi->start + left + 1;
+
+	toi->timed_out = 0;
+}



_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/4] purgatory/x86: Support CMOS RTC
  2016-01-20 10:37 [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Hidehiro Kawai
  2016-01-20 10:37 ` [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory Hidehiro Kawai
  2016-01-20 10:37 ` [RFC PATCH 2/4] purgatory: Introduce timeout API Hidehiro Kawai
@ 2016-01-20 10:37 ` Hidehiro Kawai
  2016-01-20 10:37 ` [RFC PATCH 4/4] purgatory/ipmi: Add timeout logic to IPMI command processing Hidehiro Kawai
  2016-01-21 15:38 ` [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Corey Minyard
  4 siblings, 0 replies; 9+ messages in thread
From: Hidehiro Kawai @ 2016-01-20 10:37 UTC (permalink / raw)
  To: Simon Horman, Eric W. Biederman; +Cc: kexec, Corey Minyard

Support CMOS RTC based on MC148618A RTC by overriding get_unix_time()
function.  This is needed for the timeout API previously added.

Signed-off-by: Hidehiro Kawai <hidehiro.kawai.ez@hitachi.com>
---
 purgatory/arch/i386/Makefile   |    1 
 purgatory/arch/i386/rtc_cmos.c |  104 ++++++++++++++++++++++++++++++++++++++++
 purgatory/arch/x86_64/Makefile |    1 
 3 files changed, 106 insertions(+)
 create mode 100644 purgatory/arch/i386/rtc_cmos.c

diff --git a/purgatory/arch/i386/Makefile b/purgatory/arch/i386/Makefile
index 1532219..cb77328 100644
--- a/purgatory/arch/i386/Makefile
+++ b/purgatory/arch/i386/Makefile
@@ -13,6 +13,7 @@ i386_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c
 i386_PURGATORY_SRCS += purgatory/arch/i386/vga.c
 i386_PURGATORY_SRCS += purgatory/arch/i386/pic.c
 i386_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c
+i386_PURGATORY_SRCS += purgatory/arch/i386/rtc_cmos.c
 
 dist += purgatory/arch/i386/Makefile $(i386_PURGATORY_SRCS)	\
 	purgatory/arch/i386/purgatory-x86.h			\
diff --git a/purgatory/arch/i386/rtc_cmos.c b/purgatory/arch/i386/rtc_cmos.c
new file mode 100644
index 0000000..3dbbd01
--- /dev/null
+++ b/purgatory/arch/i386/rtc_cmos.c
@@ -0,0 +1,104 @@
+#include <sys/io.h>
+#include "time.h"
+
+#define CMOS_PORT0		0x70
+#define  CMOS_NMI_DISABLE	0x80
+#define CMOS_PORT1		0x71
+
+#define RTC_SECONDS		0x00
+#define RTC_MINUTES		0x02
+#define RTC_HOURS		0x04
+#define RTC_DAY			0x07
+#define RTC_MONTH		0x08
+#define RTC_YEAR		0x09
+
+#define RTC_REGA		0x0a
+#define  RTC_UIP		0x80
+#define RTC_REGB		0x0b
+#define  RTC_HOURFORM		0x02
+#define  RTC_DM			0x04
+
+static int is_bcd;	/* 1 if the Data Mode is BCD */
+static int is_12hr;	/* 1 if the Hour Format is 12-hr mode */
+
+unsigned char read_cmos(int idx)
+{
+	static unsigned char nmi_disable_bit = 0xff;
+
+	/* Preserve NMI Disable bit at the first time */
+	if (nmi_disable_bit == 0xff)
+		nmi_disable_bit = inb(CMOS_PORT0) & CMOS_NMI_DISABLE;
+
+	outb(nmi_disable_bit | (idx & 0x7f), CMOS_PORT0);
+
+	return inb(CMOS_PORT1);
+}
+
+/**
+ * Return UNIX-epoch time.
+ *
+ * If the RTC is about to be updated (UIP bit is set), this function wait
+ * for that.  It takes at most 2228us with MC146818A RTC according to
+ * the specification.  Modern MC146818A-compatible RTCs in chipset will work
+ * faster, but not so much.  So you shouldn't call this function too often.
+ */
+time64_t get_unix_time(void)
+{
+	static int is_first_time = 1;
+	unsigned char byte;
+	int ss, mm, hh;
+	int day, mon, year;
+	int is_pm;
+
+	/* Check the modes at the first time. */
+	if (is_first_time) {
+		byte = read_cmos(RTC_REGB);
+		if (!(byte & RTC_DM))
+			is_bcd = 1;
+
+		if (!(byte & RTC_HOURFORM))
+			is_12hr = 1;
+
+		is_first_time = 0;
+	}
+
+ 	/* Wait for the clock update to be done if it is in progress */
+	do {
+		byte = read_cmos(RTC_REGA);
+	} while (byte & RTC_UIP);
+
+
+	/*
+	 * Now, we have 244us at least until the clock update will start.
+	 */
+	ss = read_cmos(RTC_SECONDS);
+	mm = read_cmos(RTC_MINUTES);
+	hh = read_cmos(RTC_HOURS);
+	day = read_cmos(RTC_DAY);
+	mon = read_cmos(RTC_MONTH);
+	year = read_cmos(RTC_YEAR);
+
+	/* If the RTC is in 12-hr mode, bit-7 of HOUR byte indicates PM. */
+	is_pm = (hh & 0x80);
+	hh &= 0x7f; /* drop AM/PM bit */
+
+	if (is_bcd) {
+		ss = (ss >> 4) * 10 + (ss & 0xf);
+		mm = (mm >> 4) * 10 + (mm & 0xf);
+		hh = (hh >> 4) * 10 + (hh & 0xf);
+		day = (day >> 4) * 10 + (day & 0xf);
+		mon = (mon >> 4) * 10 + (mon & 0xf);
+		year = (year >> 4) * 10 + (year & 0xf) +
+			2000; /* FIXME? */
+	}
+
+	if (is_12hr) {
+		if (is_pm && hh != 12)
+			hh += 12;
+
+		if (!is_pm && hh == 12)
+			hh = 0;
+	}
+
+	return date2unix(year, mon, day, hh, mm, ss);
+}
diff --git a/purgatory/arch/x86_64/Makefile b/purgatory/arch/x86_64/Makefile
index 7300937..bca1f71 100644
--- a/purgatory/arch/x86_64/Makefile
+++ b/purgatory/arch/x86_64/Makefile
@@ -22,5 +22,6 @@ x86_64_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c
 x86_64_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c
 x86_64_PURGATORY_SRCS += purgatory/arch/i386/vga.c
 x86_64_PURGATORY_SRCS += purgatory/arch/i386/pic.c
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/rtc_cmos.c
 
 x86_64_PURGATORY_EXTRA_CFLAGS = -mcmodel=large



_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 4/4] purgatory/ipmi: Add timeout logic to IPMI command processing
  2016-01-20 10:37 [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Hidehiro Kawai
                   ` (2 preceding siblings ...)
  2016-01-20 10:37 ` [RFC PATCH 3/4] purgatory/x86: Support CMOS RTC Hidehiro Kawai
@ 2016-01-20 10:37 ` Hidehiro Kawai
  2016-01-21 15:38 ` [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Corey Minyard
  4 siblings, 0 replies; 9+ messages in thread
From: Hidehiro Kawai @ 2016-01-20 10:37 UTC (permalink / raw)
  To: Simon Horman, Eric W. Biederman; +Cc: kexec, Corey Minyard

If you enable the IPMI command issuing features and your BMC is
malfunctioning, you may loops indefinitely while processing IPMI
commands.  To guarantee that the second kernel starts to boot in
fixed seconds, this patch introduces a timeout for the total
processing time of IPMI commands.  The timeout is 5 seconds by
default.

Signed-off-by: Hidehiro Kawai <hidehiro.kawai.ez@hitachi.com>
---
 purgatory/ipmi.c |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)

diff --git a/purgatory/ipmi.c b/purgatory/ipmi.c
index 0acb853..f4a093a 100644
--- a/purgatory/ipmi.c
+++ b/purgatory/ipmi.c
@@ -1,6 +1,7 @@
 #include <sys/io.h>
 #include <purgatory.h>
 #include "../kexec/ipmi.h"
+#include "time.h"
 
 #define KCS_PORT_DEFAULT	0xca2
 #define KCS_PORT_DATA		(kcs_port + 0)
@@ -41,6 +42,8 @@ const unsigned char cmd_stop_wdt[] = {
 	0xff,
 };
 
+static struct timeout_info ipmi_to; /* Total timeout for IPMI operations */
+
 static inline unsigned char read_status(void)
 {
 	return inb(KCS_PORT_STATUS);
@@ -49,29 +52,50 @@ static inline unsigned char read_status(void)
 unsigned char wait_out(void)
 {
 	unsigned char status;
+	static int count = 0;
 
 	do {
+		count++;
+		if (count % 1024 == 0)
+			EXIT_ON_TIMEOUT(&ipmi_to, 1);
+
 		status = read_status();
 	} while ((status & KCS_STATUS_OBF) == 0);
 
 	return status;
+
+timed_out:
+	return 0xff;
 }
 
 unsigned char wait_in(void)
 {
 	unsigned char status;
+	static int count = 0;
 
 	do {
+		count++;
+		if (count % 1024 == 0)
+			EXIT_ON_TIMEOUT(&ipmi_to, 1);
+
 		status = read_status();
 	} while (status & KCS_STATUS_IBF);
 
 	return status;
+
+timed_out:
+	return 0xff;
 }
 
 unsigned char read_data(void)
 {
 	wait_out();
+	EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 	return inb(KCS_PORT_DATA);
+
+timed_out:
+	return 0xff;
 }
 
 void clear_obf(void)
@@ -105,12 +129,17 @@ int write_ipmi_cmd(const unsigned char *cmd, int size)
 	int i;
 
 	wait_in();
+	EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 	status = write_cmd(KCS_CMD_WRITE_START);
+	EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 	if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
 		return -1;
 
 	for (i = 0; i < size - 1; i++) {
 		status = write_data(cmd[i]);
+		EXIT_ON_TIMEOUT(&ipmi_to, 0);
 
 		if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
 			return -1;
@@ -118,12 +147,18 @@ int write_ipmi_cmd(const unsigned char *cmd, int size)
 
 	/* last write */
 	status = write_cmd(KCS_CMD_WRITE_END);
+	EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 	if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
 		return -1;
 
 	write_data(cmd[i]);
+	EXIT_ON_TIMEOUT(&ipmi_to, 0);
 
 	return 0;
+
+timed_out:
+	return -1;
 }
 
 /*
@@ -140,11 +175,17 @@ unsigned char read_result(void)
 
 	while (1) {
 		state = GET_STATUS_STATE(wait_in());
+		EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 		if (state == KCS_READ_STATE) {
 			data[count] = read_data();
+			EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 			outb(KCS_CMD_READ, KCS_PORT_DATA);
 		} else if (state == KCS_IDLE_STATE) {
 			data[count] = read_data();
+			EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 			break;
 		} else {
 			/*
@@ -164,6 +205,9 @@ unsigned char read_result(void)
 	}
 
 	return data[2]; /* Return completion code */
+
+timed_out:
+	return 0xff;
 }
 
 /*
@@ -190,15 +234,22 @@ int issue_ipmi_cmd(const unsigned char *cmd, int size)
 	 */
 	for (i = 0; i < 3; i++) {
 		ret = write_ipmi_cmd(cmd, size);
+		EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 		if (ret < 0)
 			continue;
 
 		comp_code = read_result();
+		EXIT_ON_TIMEOUT(&ipmi_to, 0);
+
 		if (comp_code == 0)
 			break; /* succeeded */
 	}
 
 	return (i < 3) ? 0 : -1;
+
+timed_out:
+	return -1;
 }
 
 int do_start_wdt(void)
@@ -207,7 +258,11 @@ int do_start_wdt(void)
 
 	printf("IPMI: starting watchdog timer...");
 	ret = issue_ipmi_cmd(cmd_start_wdt, sizeof(cmd_start_wdt));
-	printf("done\n");
+
+	if (ret == 0)
+		printf("done\n");
+	else
+		printf("failed\n");
 
 	return ret;
 }
@@ -218,13 +273,19 @@ int do_stop_wdt(void)
 
 	printf("IPMI: stopping watchdog timer...");
 	ret = issue_ipmi_cmd(cmd_stop_wdt, sizeof(cmd_stop_wdt));
-	printf("done\n");
+
+	if (ret == 0)
+		printf("done\n");
+	else
+		printf("failed\n");
 
 	return ret;
 }
 
 void ipmi_wdt_start_stop(void)
 {
+	init_timeout(&ipmi_to, 5);
+
 	if (ipmi_wdt & IPMI_WDT_START)
 		do_start_wdt();
 	else if (ipmi_wdt & IPMI_WDT_STOP)



_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution
  2016-01-20 10:37 [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Hidehiro Kawai
                   ` (3 preceding siblings ...)
  2016-01-20 10:37 ` [RFC PATCH 4/4] purgatory/ipmi: Add timeout logic to IPMI command processing Hidehiro Kawai
@ 2016-01-21 15:38 ` Corey Minyard
  2016-01-22  4:39   ` 河合英宏 / KAWAI,HIDEHIRO
  4 siblings, 1 reply; 9+ messages in thread
From: Corey Minyard @ 2016-01-21 15:38 UTC (permalink / raw)
  To: Hidehiro Kawai, Simon Horman, Eric W. Biederman; +Cc: kexec

I understand what you are trying to accomplish here, but I'm not sure of
the wisdom of this approach.  I'll give some more information and the
kexec maintainers can decide, I suppose.

The KCS interface given here probably covers ~70% of the systems out there
right now.  Other systems have:
   * KCS interfaces at a different port or in a different place like 
memory, PCI,
     and with different register sizes and spacing.
   * Other standard interfaces.  SMIC (probably not relevant), BT 
(faster, it does
     block transfers) and SSIF (which is IPMI over I2C).
   * Those other standard interfaces can be in different places, just 
like KCS.
     Hundreds of I2C interfaces exist.
   * Non-standard interfaces.  Power systems have their own IPMI interfaces,
     for instance.  Some systems have IPMI over serial ports, though 
hopefully
     that has pretty much gone away.

I'd guess that over half of the IPMI SI driver is discovering and 
handling all the
various interface types, locations from all the sources it can come from.

As time goes on that 70% number is decreasing in favour of other faster
and more convenient interfaces.  I expect that SSIF will become much more
popular over time because it has block transfer capability and all the 
hardware
is already there on systems.

This is no different, of course, than any other common hardware 
interface out
there.  USB, ATA, etc.  But it makes it hard to cover all the 
possibilities in something
like purgatory.

I know how valuable this information can be.  It has saved my butt on 
occasions,
which is why I go through the inconvenience of handling it in the IPMI 
driver.
But it seems to me that the failure rate of doing this in the crashing 
kernel should
be pretty low.  Not zero of course.  But I have no idea what it is.

-corey

On 01/20/2016 04:37 AM, Hidehiro Kawai wrote:
> If the second kernel for crash dumping hangs up while booting, no
> information related to the first kernel will be saved.  This makes
> crash cause analysis difficult.  So, some enterprise users want to
> save minimal information befor booting the second kernel.
>
> One of the approaches is to use panic notifier call or pstore
> feature.  For example, a panic notifier callback registered by IPMI
> driver saves the panic message to BMC's SEL before booting the second
> kernel.  Similarly, pstore saves kernel logs to a non-volatile memory
> on the server.  However, since these functionalities run with crashed
> kernel, they may fail to complete their work and boot the second
> kernel.
>
> So, another approach; saving minimal information to BMC's SEL in the
> purgatory.  Since the purgatory code doesn't rely on the crashed
> kernel, we can run it safely after verifying the hash of the code.
>
> This patch set is the first step to the final goal; it provides
> a basic support for IPMI command execution in purgatory.  IPMI
> specification defines multiple interfaces to BMC, and this patch set
> uses one of them, KCS I/F, which talks with BMC via I/O port like
> keyboard controllers.  As a use case for that, options to start/stop
> BMC's watchdog timer before booting the second kernel are also
> provided.  These options are useful for the cases where:
>
>   - you want to automatically reboot the server when the second kernel
>     hangs up while booting
>   - you want to prevent the second kernel from being stopped by the
>     watchdog timer enabled while the first kernel is running
>
> If the BMC doesn't work well, the IPMI command execution can take
> indefinite time and fail to boot the second kernel.  To avoid this,
> timeout logic based on RTC polling is also implemented.
>
> NOTE: This is an RFC version, so some parts are incomplete; these
> codes are unconditionally built into the kexec binary, and I/O ports
> for KCS I/F and timeout (5 seconds) are hard-coded, and etc.
>
> Future plan:
> Add an option to save the panic message and instruction pointers to
> BMC's SEL in purgatory.  To realize this, we first need to pass the
> panic message to the purgatory.  Instruction pointers are already
> passed to the second kernel through ELF notes, so just read them.
>
> ---
>
> Hidehiro Kawai (4):
>        purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory
>        purgatory: Introduce timeout API
>        purgatory/x86: Support CMOS RTC
>        purgatory/ipmi: Add timeout logic to IPMI command processing
>
>
>   kexec/ipmi.h                   |    9 +
>   kexec/kexec.c                  |   18 ++
>   kexec/kexec.h                  |    6 +
>   purgatory/Makefile             |    5 +
>   purgatory/arch/i386/Makefile   |    1
>   purgatory/arch/i386/rtc_cmos.c |  104 ++++++++++++++
>   purgatory/arch/x86_64/Makefile |    1
>   purgatory/include/purgatory.h  |    3
>   purgatory/include/time.h       |   33 +++++
>   purgatory/ipmi.c               |  293 ++++++++++++++++++++++++++++++++++++++++
>   purgatory/purgatory.c          |    4 +
>   purgatory/time.c               |   58 ++++++++
>   12 files changed, 533 insertions(+), 2 deletions(-)
>   create mode 100644 kexec/ipmi.h
>   create mode 100644 purgatory/arch/i386/rtc_cmos.c
>   create mode 100644 purgatory/include/time.h
>   create mode 100644 purgatory/ipmi.c
>   create mode 100644 purgatory/time.c
>
>


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory
  2016-01-20 10:37 ` [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory Hidehiro Kawai
@ 2016-01-21 15:40   ` Corey Minyard
  2016-01-22  5:10     ` 河合英宏 / KAWAI,HIDEHIRO
  0 siblings, 1 reply; 9+ messages in thread
From: Corey Minyard @ 2016-01-21 15:40 UTC (permalink / raw)
  To: Hidehiro Kawai, Simon Horman, Eric W. Biederman; +Cc: kexec

A general note here.  It does not appear that you implement the
error recovery states in your state machine.  If the system fails
in the middle of doing an IPMI operation, it is likely to fail.

If you do this you will need to detect and abort any running
operation.  Implementing the full state machine is probably the
best approach, it should handle this, though it is rather complex.

-corey

On 01/20/2016 04:37 AM, Hidehiro Kawai wrote:
> This patch adds an interface to BMC via KCS I/F and a functionality
> to start/stop BMC watchdog timer in purgatory.  Starting the watchdog
> timer is useful to automatically reboot the server when we fail to
> boot the second kernel.  Stopping the watchdog timer is useful to
> prevent the second kernel from being stopped by the watchdog timer
> enabled while the first kernel is running.
>
> If you specify --ipmi-wdt-start or --ipmi-wdt-stop option to kexec
> command, BMC's watchdog timer will start or stop respectively while
> executing purgatory.  You can't specify the both options at the same
> time.  The start operation doesn't change the parameters of the
> watchdog timer such as initial counter and action, you need to set
> those parameters in the first OS.  On the other hand, the stop
> operation changes the parameters.  You need to reset them when you
> want to reuse the watchdog timer.
>
> Signed-off-by: Hidehiro Kawai <hidehiro.kawai.ez@hitachi.com>
> ---
>   kexec/ipmi.h                  |    9 ++
>   kexec/kexec.c                 |   18 +++
>   kexec/kexec.h                 |    6 +
>   purgatory/Makefile            |    1
>   purgatory/include/purgatory.h |    3 +
>   purgatory/ipmi.c              |  232 +++++++++++++++++++++++++++++++++++++++++
>   purgatory/purgatory.c         |    4 +
>   7 files changed, 272 insertions(+), 1 deletion(-)
>   create mode 100644 kexec/ipmi.h
>   create mode 100644 purgatory/ipmi.c
>
> diff --git a/kexec/ipmi.h b/kexec/ipmi.h
> new file mode 100644
> index 0000000..395a2c7
> --- /dev/null
> +++ b/kexec/ipmi.h
> @@ -0,0 +1,9 @@
> +#ifndef IPMI_H
> +#define IPMI_H
> +
> +/* Options for IPMI code excuted in purgatory */
> +#define IPMI_WDT_DO_NOTHING	0
> +#define IPMI_WDT_START		(1 << 0)
> +#define IPMI_WDT_STOP		(1 << 1)
> +
> +#endif /* IPMI_H */
> diff --git a/kexec/kexec.c b/kexec/kexec.c
> index f0bd527..8a8f268 100644
> --- a/kexec/kexec.c
> +++ b/kexec/kexec.c
> @@ -49,6 +49,7 @@
>   #include "kexec-sha256.h"
>   #include "kexec-zlib.h"
>   #include "kexec-lzma.h"
> +#include "ipmi.h"
>   #include <arch/options.h>
>   
>   unsigned long long mem_min = 0;
> @@ -57,6 +58,7 @@ static unsigned long kexec_flags = 0;
>   /* Flags for kexec file (fd) based syscall */
>   static unsigned long kexec_file_flags = 0;
>   int kexec_debug = 0;
> +int opt_ipmi_wdt = IPMI_WDT_DO_NOTHING;
>   
>   void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr)
>   {
> @@ -643,6 +645,10 @@ static void update_purgatory(struct kexec_info *info)
>   	if (!info->rhdr.e_shdr) {
>   		return;
>   	}
> +
> +	elf_rel_set_symbol(&info->rhdr, "ipmi_wdt", &opt_ipmi_wdt,
> +			   sizeof(opt_ipmi_wdt));
> +
>   	arch_update_purgatory(info);
>   	memset(region, 0, sizeof(region));
>   	sha256_starts(&ctx);
> @@ -1345,6 +1351,12 @@ int main(int argc, char *argv[])
>   		case OPT_KEXEC_FILE_SYSCALL:
>   			/* We already parsed it. Nothing to do. */
>   			break;
> +		case OPT_IPMI_WDT_START:
> +			opt_ipmi_wdt |= IPMI_WDT_START;
> +			break;
> +		case OPT_IPMI_WDT_STOP:
> +			opt_ipmi_wdt |= IPMI_WDT_STOP;
> +			break;
>   		default:
>   			break;
>   		}
> @@ -1370,6 +1382,12 @@ int main(int argc, char *argv[])
>   		    "\"--mem-max\" parameter\n");
>   	}
>   
> +	if ((opt_ipmi_wdt & IPMI_WDT_START) &&
> +	    (opt_ipmi_wdt & IPMI_WDT_STOP)) {
> +		die("You can't specify both --ipmi-wdt-start and "
> +		    "--ipmi-wdt-stop\n");
> +	}
> +
>   	fileind = optind;
>   	/* Reset getopt for the next pass; called in other source modules */
>   	opterr = 1;
> diff --git a/kexec/kexec.h b/kexec/kexec.h
> index c02ac8f..4638866 100644
> --- a/kexec/kexec.h
> +++ b/kexec/kexec.h
> @@ -224,7 +224,9 @@ extern int file_types;
>   #define OPT_LOAD_PRESERVE_CONTEXT 259
>   #define OPT_LOAD_JUMP_BACK_HELPER 260
>   #define OPT_ENTRY		261
> -#define OPT_MAX			262
> +#define OPT_IPMI_WDT_START	262
> +#define OPT_IPMI_WDT_STOP	263
> +#define OPT_MAX			264
>   #define KEXEC_OPTIONS \
>   	{ "help",		0, 0, OPT_HELP }, \
>   	{ "version",		0, 0, OPT_VERSION }, \
> @@ -244,6 +246,8 @@ extern int file_types;
>   	{ "reuseinitrd",	0, 0, OPT_REUSE_INITRD }, \
>   	{ "kexec-file-syscall",	0, 0, OPT_KEXEC_FILE_SYSCALL }, \
>   	{ "debug",		0, 0, OPT_DEBUG }, \
> +	{ "ipmi-wdt-start",	0, 0, OPT_IPMI_WDT_START }, \
> +	{ "ipmi-wdt-stop",	0, 0, OPT_IPMI_WDT_STOP }, \
>   
>   #define KEXEC_OPT_STR "h?vdfxyluet:ps"
>   
> diff --git a/purgatory/Makefile b/purgatory/Makefile
> index 2b5c061..ae1d2bd 100644
> --- a/purgatory/Makefile
> +++ b/purgatory/Makefile
> @@ -9,6 +9,7 @@
>   
>   PURGATORY = purgatory/purgatory.ro
>   PURGATORY_SRCS =
> +PURGATORY_SRCS += purgatory/ipmi.c
>   PURGATORY_SRCS += purgatory/purgatory.c
>   PURGATORY_SRCS += purgatory/printf.c
>   PURGATORY_SRCS += purgatory/string.c
> diff --git a/purgatory/include/purgatory.h b/purgatory/include/purgatory.h
> index 788ce49..9b9ffbe 100644
> --- a/purgatory/include/purgatory.h
> +++ b/purgatory/include/purgatory.h
> @@ -1,11 +1,14 @@
>   #ifndef PURGATORY_H
>   #define PURGATORY_H
>   
> +extern int ipmi_wdt;
> +
>   void putchar(int ch);
>   void sprintf(char *buffer, const char *fmt, ...)
>   	__attribute__ ((format (printf, 2, 3)));
>   void printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
>   void setup_arch(void);
>   void post_verification_setup_arch(void);
> +void ipmi_wdt_start_stop(void);
>   
>   #endif /* PURGATORY_H */
> diff --git a/purgatory/ipmi.c b/purgatory/ipmi.c
> new file mode 100644
> index 0000000..0acb853
> --- /dev/null
> +++ b/purgatory/ipmi.c
> @@ -0,0 +1,232 @@
> +#include <sys/io.h>
> +#include <purgatory.h>
> +#include "../kexec/ipmi.h"
> +
> +#define KCS_PORT_DEFAULT	0xca2
> +#define KCS_PORT_DATA		(kcs_port + 0)
> +#define KCS_PORT_STATUS		(kcs_port + 1)
> +#define KCS_PORT_CMD		(kcs_port + 1)
> +
> +#define KCS_STATUS_OBF		0x1
> +#define KCS_STATUS_IBF		0x2
> +
> +#define KCS_CMD_WRITE_START	0x61
> +#define KCS_CMD_WRITE_END	0x62
> +#define KCS_CMD_READ		0x68
> +
> +#define GET_STATUS_STATE(status) (((status) >> 6) & 0x03)
> +#define KCS_IDLE_STATE	0x0
> +#define KCS_READ_STATE	0x1
> +#define KCS_WRITE_STATE	0x2
> +#define KCS_ERROR_STATE	0x3
> +
> +int kcs_port = KCS_PORT_DEFAULT;
> +int ipmi_wdt = IPMI_WDT_DO_NOTHING;
> +
> +/* IPMI command to start BMC watchdog timer */
> +const unsigned char cmd_start_wdt[] = {
> +	0x06 << 2,	/* App */
> +	0x22,		/* Reset Watchdog Timer */
> +};
> +
> +/* IPMI command to stop BMC watchdog timer */
> +const unsigned char cmd_stop_wdt[] = {
> +	0x06 << 2,	/* App */
> +	0x24,		/* Set Watchdog Timer Command */
> +	0x84,		/* Timer Use: don't log, and SMS/OS use */
> +	0x00,		/* Timer Actions: no action */
> +	0x00,		/* Pre-timeout interval: 0 */
> +	0x10,		/* Timer Use Expiration flag clear: SMS/OS */
> +	0xff,		/* Initial countdown value: 0xffff */
> +	0xff,
> +};
> +
> +static inline unsigned char read_status(void)
> +{
> +	return inb(KCS_PORT_STATUS);
> +}
> +
> +unsigned char wait_out(void)
> +{
> +	unsigned char status;
> +
> +	do {
> +		status = read_status();
> +	} while ((status & KCS_STATUS_OBF) == 0);
> +
> +	return status;
> +}
> +
> +unsigned char wait_in(void)
> +{
> +	unsigned char status;
> +
> +	do {
> +		status = read_status();
> +	} while (status & KCS_STATUS_IBF);
> +
> +	return status;
> +}
> +
> +unsigned char read_data(void)
> +{
> +	wait_out();
> +	return inb(KCS_PORT_DATA);
> +}
> +
> +void clear_obf(void)
> +{
> +	if (inb(KCS_PORT_STATUS) & KCS_STATUS_OBF)
> +		read_data();
> +}
> +
> +unsigned char write_data(unsigned char byte)
> +{
> +	clear_obf();
> +	outb(byte, KCS_PORT_DATA);
> +	return wait_in();
> +}
> +
> +unsigned char write_cmd(unsigned char byte)
> +{
> +	clear_obf();
> +	outb(byte, KCS_PORT_CMD);
> +	return wait_in();
> +}
> +
> +/*
> + * Issue a given IPMI command via KCS I/F.
> + *
> + * Return 0 on success, otherwise -1.
> + */
> +int write_ipmi_cmd(const unsigned char *cmd, int size)
> +{
> +	unsigned char status;
> +	int i;
> +
> +	wait_in();
> +	status = write_cmd(KCS_CMD_WRITE_START);
> +	if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
> +		return -1;
> +
> +	for (i = 0; i < size - 1; i++) {
> +		status = write_data(cmd[i]);
> +
> +		if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
> +			return -1;
> +	}
> +
> +	/* last write */
> +	status = write_cmd(KCS_CMD_WRITE_END);
> +	if (GET_STATUS_STATE(status) != KCS_WRITE_STATE)
> +		return -1;
> +
> +	write_data(cmd[i]);
> +
> +	return 0;
> +}
> +
> +/*
> + * Read result bytes for the previously issued IPMI command.
> + *
> + * Return the completion code, which is 0 on success.  Otherwise, return
> + * non-zero value.
> + */
> +unsigned char read_result(void)
> +{
> +	unsigned char state;
> +	unsigned char data[4] = { 0 };
> +	int count = 0;
> +
> +	while (1) {
> +		state = GET_STATUS_STATE(wait_in());
> +		if (state == KCS_READ_STATE) {
> +			data[count] = read_data();
> +			outb(KCS_CMD_READ, KCS_PORT_DATA);
> +		} else if (state == KCS_IDLE_STATE) {
> +			data[count] = read_data();
> +			break;
> +		} else {
> +			/*
> +			 * Error! Set 0xff (unspecified error) as the
> +			 * completion code.
> +			 */
> +			data[2] = 0xff;
> +			break;
> +		}
> +
> +		/*
> +		 * We are interested only in the completion code in the
> +		 * 3rd byte. Skip the following bytes.
> +		 */
> +		if (count < 3)
> +			count++;
> +	}
> +
> +	return data[2]; /* Return completion code */
> +}
> +
> +/*
> + * Issue one IPMI command and check the result.
> + *
> + * Return 0 on success, otherwise -1.
> + */
> +int issue_ipmi_cmd(const unsigned char *cmd, int size)
> +{
> +	int i, ret;
> +	unsigned char comp_code;
> +
> +	/*
> +	 * Retry 3 times at most on error.
> +	 *
> +	 * write_ipmi_cmd() issues WRITE_START KCS command at the beginning,
> +	 * which aborts the ongoing IPMI command and starts new one.  So
> +	 * basically, write_ipmi_cmd() will succeed anytime.
> +	 *
> +	 * This command abort can fail on some BMC depending on its KCS I/F
> +	 * state, and  we get ERROR_STATE from the status register in that
> +	 * case.  However, there is no problem.  We can recover it by
> +	 * simply retrying from writing WRITE_START KCS command.
> +	 */
> +	for (i = 0; i < 3; i++) {
> +		ret = write_ipmi_cmd(cmd, size);
> +		if (ret < 0)
> +			continue;
> +
> +		comp_code = read_result();
> +		if (comp_code == 0)
> +			break; /* succeeded */
> +	}
> +
> +	return (i < 3) ? 0 : -1;
> +}
> +
> +int do_start_wdt(void)
> +{
> +	int ret;
> +
> +	printf("IPMI: starting watchdog timer...");
> +	ret = issue_ipmi_cmd(cmd_start_wdt, sizeof(cmd_start_wdt));
> +	printf("done\n");
> +
> +	return ret;
> +}
> +
> +int do_stop_wdt(void)
> +{
> +	int ret;
> +
> +	printf("IPMI: stopping watchdog timer...");
> +	ret = issue_ipmi_cmd(cmd_stop_wdt, sizeof(cmd_stop_wdt));
> +	printf("done\n");
> +
> +	return ret;
> +}
> +
> +void ipmi_wdt_start_stop(void)
> +{
> +	if (ipmi_wdt & IPMI_WDT_START)
> +		do_start_wdt();
> +	else if (ipmi_wdt & IPMI_WDT_STOP)
> +		do_stop_wdt();
> +}
> diff --git a/purgatory/purgatory.c b/purgatory/purgatory.c
> index 3bbcc09..f60879d 100644
> --- a/purgatory/purgatory.c
> +++ b/purgatory/purgatory.c
> @@ -48,5 +48,9 @@ void purgatory(void)
>   			/* loop forever */
>   		}
>   	}
> +
> +	if (ipmi_wdt)
> +		ipmi_wdt_start_stop();
> +
>   	post_verification_setup_arch();
>   }
>
>


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution
  2016-01-21 15:38 ` [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Corey Minyard
@ 2016-01-22  4:39   ` 河合英宏 / KAWAI,HIDEHIRO
  0 siblings, 0 replies; 9+ messages in thread
From: 河合英宏 / KAWAI,HIDEHIRO @ 2016-01-22  4:39 UTC (permalink / raw)
  To: 'minyard@acm.org', Simon Horman, Eric W. Biederman; +Cc: kexec

Hello,

Thanks for your comments.

> I understand what you are trying to accomplish here, but I'm not sure of
> the wisdom of this approach.  I'll give some more information and the
> kexec maintainers can decide, I suppose.
> 
> The KCS interface given here probably covers ~70% of the systems out there
> right now.

I decided to use KCS I/F because I believe it covers relatively high
rate of servers and it is the only system interface which one of our
targets supports for.

>  Other systems have:
>    * KCS interfaces at a different port or in a different place like
> memory, PCI,
>      and with different register sizes and spacing.

At least, I'm going to add an option to change the base I/O port.
I'm not sure about other cases because I don't know how supporting
them raises the cover rate and I don't have hardware to test.

>    * Other standard interfaces.  SMIC (probably not relevant), BT
> (faster, it does
>      block transfers) and SSIF (which is IPMI over I2C).
>    * Those other standard interfaces can be in different places, just
> like KCS.
>      Hundreds of I2C interfaces exist.
>    * Non-standard interfaces.  Power systems have their own IPMI interfaces,
>      for instance.  Some systems have IPMI over serial ports, though
> hopefully
>      that has pretty much gone away.
> 
> I'd guess that over half of the IPMI SI driver is discovering and
> handling all the
> various interface types, locations from all the sources it can come from.
> 
> As time goes on that 70% number is decreasing in favour of other faster
> and more convenient interfaces.  I expect that SSIF will become much more
> popular over time because it has block transfer capability and all the
> hardware
> is already there on systems.

Thanks for this information.  I'll check out the BMC implementation
of other servers, then I'll decide whether I should support SSIF, too.

> This is no different, of course, than any other common hardware
> interface out
> there.  USB, ATA, etc.  But it makes it hard to cover all the
> possibilities in something
> like purgatory.

Such kind of device sometimes become a cause of dump failure
due to wrong DMA or interrupts.  So we shouldn't use them.
Also, I think it is not suitable for purgatory to implement
their drivers.

Other possible options are to use serial device and NVRAM (like
the pstore driver).  However, serial port is not always available
for this purpose (actually this is our case), and NVRAM is not
always equipped.  Furthermore, we'd like to start/stop
the BMC's watchdog timer before starting the 2nd kernel.  This
can be done only by IPMI.

> I know how valuable this information can be.  It has saved my butt on
> occasions,
> which is why I go through the inconvenience of handling it in the IPMI
> driver.
> But it seems to me that the failure rate of doing this in the crashing
> kernel should
> be pretty low.  Not zero of course.  But I have no idea what it is.

At first, I tried to use crash_kexec_post_notifiers to call
the callback of IPMI driver before jumping to the 2nd kernel.
But the feature has a couple of bugs and I tried to fix them:

https://lkml.org/lkml/2015/7/10/316
https://lkml.org/lkml/2015/7/23/864

However, no one supports this bugfix and the discussion didn't
make a progress because calling notifiers before kdump is not
reliable.  So, I suggested to use the purgatory.

Regards,

--
Hidehiro Kawai
Hitachi, Ltd. Research & Development Group


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory
  2016-01-21 15:40   ` Corey Minyard
@ 2016-01-22  5:10     ` 河合英宏 / KAWAI,HIDEHIRO
  0 siblings, 0 replies; 9+ messages in thread
From: 河合英宏 / KAWAI,HIDEHIRO @ 2016-01-22  5:10 UTC (permalink / raw)
  To: 'minyard@acm.org', Simon Horman, Eric W. Biederman; +Cc: kexec

> A general note here.  It does not appear that you implement the
> error recovery states in your state machine.  If the system fails
> in the middle of doing an IPMI operation, it is likely to fail.

The reason why I din't implement the error handling is that
I think the error rate is low and it may take many seconds (but I
don't have any statistical data, that's my anticipation).

The most important thing is to start booting the 2nd kernel surely
and as soon as possible.  For example, if a user uses a feature
like fence_kdump and if the execution of fence_kdump gets delayed,
the crashed host will be shot down by other host waiting for the
notification from fence_kdump.

Also, to keep the code simple is important for the reliability.

Anyway, I'll rethink whether I can implement the error handling
in simple logic or not.

> If you do this you will need to detect and abort any running
> operation.  Implementing the full state machine is probably the
> best approach, it should handle this, though it is rather complex.
> 
> -corey

Regards,
--
Hidehiro Kawai
Hitachi, Ltd. Research & Development Group

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-01-22  5:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-20 10:37 [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Hidehiro Kawai
2016-01-20 10:37 ` [RFC PATCH 1/4] purgatory/ipmi: Support BMC watchdog timer start/stop in purgatory Hidehiro Kawai
2016-01-21 15:40   ` Corey Minyard
2016-01-22  5:10     ` 河合英宏 / KAWAI,HIDEHIRO
2016-01-20 10:37 ` [RFC PATCH 2/4] purgatory: Introduce timeout API Hidehiro Kawai
2016-01-20 10:37 ` [RFC PATCH 3/4] purgatory/x86: Support CMOS RTC Hidehiro Kawai
2016-01-20 10:37 ` [RFC PATCH 4/4] purgatory/ipmi: Add timeout logic to IPMI command processing Hidehiro Kawai
2016-01-21 15:38 ` [RFC PATCH 0/4] purgatory: Add basic support for IPMI command execution Corey Minyard
2016-01-22  4:39   ` 河合英宏 / KAWAI,HIDEHIRO

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.