[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[grub legacy] Grub with IPMI WDT
From: |
Yuta Sugiura |
Subject: |
[grub legacy] Grub with IPMI WDT |
Date: |
Fri, 12 Sep 2008 22:00:33 +0900 |
User-agent: |
Thunderbird 2.0.0.14 (X11/20080501) |
Hi all,
We add function to use IPMI WDT on grub-legacy.
IPMI WDT (Intelligent Platform Management Interface Watch Dog Timer)
is usually equipped in mission-critical systems to detect hang-up and
reset the system.
There are no way to recover from hang-up happened during OS boot,
but if you use this function, you could recover from hang-up during OS boot.
This function made possible by activating IPMI WDT before the kernel
image is loaded.
If hang-up happened during OS boot ,(ex. when you test new kernel )
you can switch kernel to "safety" kernel in combination with fallback
function.
To use this function:
----------------------------
This patch was made for the grub-0.97.
0,patched as folloowing.
#patch -p0 < grub-0.97-ipmi.patch
1,Re-make config file
#cd grub-0.97
#autoreconf
2,Add configure option
#./configure --disable-auto-linux-mem-opt
3,make and install
#make
#make install
4,you need make configuration file in the same directory as menu.lst(or
grub.conf)
that named as "cgl.conf".
(If no exist "cgl.conf" file, this function to be disable. )
configuration file sample:
#Configuration for IPMI interface
[IPMI]
#set IPMI interface i/o port
io_port=0xca8
#set IPMI Register Spacig
reg_spacing=4
#set IPMI Register Size
reg_size=1
#Configuration for IPMI Watchdog timer
[Watchdog]
#Interface is depend on your machine.
#you can choice these three type:
# ipmi-kcs,ipmi-bt,ipmi-smic
interface=ipmi-kcs
#set timer value (1~6553 sec)
kernel_boot_timeout=60
#set timer value (1~6553 sec)
kernel_load_timeout=5
#set timeout action
#reset,power_down,power_cycle
timer_action=reset
application consideration
----------------------------
*After kick kernel, the sequence leave from grub,
because, the grub can not stop IPMI WDT.
so, if you use this function, you need to stop IPMI WDT.
*If your entry have "boot" line in the menu.lst this function
doesn't work.
Sincerely,
--
Yuta Sugiura
address@hidden
diff -Nurp grub-0.97/configure.ac grub-0.97.ipmi/configure.ac
--- grub-0.97/configure.ac 2005-05-08 11:36:03.000000000 +0900
+++ grub-0.97.ipmi/configure.ac 2008-09-11 20:23:13.000000000 +0900
@@ -650,6 +650,20 @@ else
AC_DEFINE(AUTO_LINUX_MEM_OPT, 1, [Define if you don't want to pass the mem=
option to Linux])
fi
+dnl **************************************************************
+dnl Configure for IPMI
+dnl **************************************************************
+
+AC_ARG_ENABLE(ipmi,
+ AS_HELP_STRING([--enable-ipmi],
+ [Enable IPMI Watchdog support (default: yes)]),
+ [use_ipmi=$enableval],
+ [use_ipmi=yes])
+if test "x$use_ipmi" = "xyes"; then
+ AC_DEFINE([SUPPORT_IPMI], [1], [Define support for IPMI])
+fi
+AM_CONDITIONAL([IPMI_SUPPORT], [test x"$use_ipmi" = "xyes"])
+
dnl Now substitute the variables.
AC_SUBST(FSYS_CFLAGS)
AC_SUBST(NET_CFLAGS)
diff -Nurp grub-0.97/netboot/timer.h grub-0.97.ipmi/netboot/timer.h
--- grub-0.97/netboot/timer.h 2003-07-09 20:45:38.000000000 +0900
+++ grub-0.97.ipmi/netboot/timer.h 2008-09-11 20:23:13.000000000 +0900
@@ -46,14 +46,17 @@
#define PPCB_SPKR 0x02 /* Bit 1 */
#define PPCB_T2GATE 0x01 /* Bit 0 */
+#include "linux-asm-io.h"
/* Ticks must be between 0 and 65535 (0 == 65536)
because it is a 16 bit counter */
extern void load_timer2(unsigned int ticks);
+int timer2_running(void);
extern inline int timer2_running(void)
{
return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
}
+void waiton_timer2(unsigned int ticks);
extern inline void waiton_timer2(unsigned int ticks)
{
load_timer2(ticks);
diff -Nurp grub-0.97/stage2/Makefile.am grub-0.97.ipmi/stage2/Makefile.am
--- grub-0.97/stage2/Makefile.am 2005-02-03 05:37:35.000000000 +0900
+++ grub-0.97.ipmi/stage2/Makefile.am 2008-09-11 20:23:13.000000000 +0900
@@ -13,13 +13,25 @@ EXTRA_DIST = setjmp.S apm.S $(noinst_SCR
# For <stage1.h>.
INCLUDES = -I$(top_srcdir)/stage1
+if IPMI_SUPPORT
+INCLUDES += -I$(top_srcdir)/netboot
+IPMI_SOURCES = ipmi_msgdefs.h ipmi_si_sm.h \
+ ipmi.h ipmi.c \
+ cgl_config.h cgl_config.c \
+ ipmi_bt_sm.c ipmi_smic_sm.c ipmi_kcs_sm.c \
+ ipmi_si_sm_io.h ipmi_si_sm_io.c \
+ ipmi_watchdog.h ipmi_watchdog.c
+endif
+
# The library for /sbin/grub.
noinst_LIBRARIES = libgrub.a
libgrub_a_SOURCES = boot.c builtins.c char_io.c cmdline.c common.c \
disk_io.c fsys_ext2fs.c fsys_fat.c fsys_ffs.c fsys_iso9660.c \
fsys_jfs.c fsys_minix.c fsys_reiserfs.c fsys_ufs2.c \
fsys_vstafs.c fsys_xfs.c gunzip.c md5.c serial.c stage2.c \
- terminfo.c tparm.c
+ terminfo.c tparm.c \
+ $(IPMI_SOURCES)
+
libgrub_a_CFLAGS = $(GRUB_CFLAGS) -I$(top_srcdir)/lib \
-DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \
-DFSYS_ISO9660=1 -DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 \
@@ -90,7 +102,8 @@ pre_stage2_exec_SOURCES = asm.S bios.c b
cmdline.c common.c console.c disk_io.c fsys_ext2fs.c \
fsys_fat.c fsys_ffs.c fsys_iso9660.c fsys_jfs.c fsys_minix.c \
fsys_reiserfs.c fsys_ufs2.c fsys_vstafs.c fsys_xfs.c gunzip.c \
- hercules.c md5.c serial.c smp-imps.c stage2.c terminfo.c tparm.c
+ hercules.c md5.c serial.c smp-imps.c stage2.c terminfo.c tparm.c \
+ $(IPMI_SOURCES)
pre_stage2_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
pre_stage2_exec_CCASFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
pre_stage2_exec_LDFLAGS = $(PRE_STAGE2_LINK)
diff -Nurp grub-0.97/stage2/builtins.c grub-0.97.ipmi/stage2/builtins.c
--- grub-0.97/stage2/builtins.c 2005-02-16 06:58:23.000000000 +0900
+++ grub-0.97.ipmi/stage2/builtins.c 2008-09-11 20:23:13.000000000 +0900
@@ -49,6 +49,11 @@
# include <md5.h>
#endif
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+# include "ipmi_watchdog.h"
+# include "cgl_config.h"
+#endif /* SUPPORT_IPMI */
+
/* The type of kernel loaded. */
kernel_t kernel_type;
/* The boot device. */
@@ -1712,20 +1717,49 @@ static struct builtin builtin_impsprobe
static int
initrd_func (char *arg, int flags)
{
+ int ret = 0;
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ {
+ int value;
+ value = cgl_get_watchdog_kernel_load_timeout();
+ if (value)
+ {
+ watchdog_action_type action_type;
+ action_type = cgl_get_watchdog_timer_action();
+ ipmi_watchdog_set_timer_action(action_type);
+ if (ipmi_watchdog_set_timeout(value) != 0)
+ {
+ printf ("set watchdog timeout failed\n");
+ printf ("Press any key to continue\n");
+ cgl_down_init_flag ();
+ getkey ();
+ return -1;
+ }
+ }
+ if (cgl_is_debug_mode())
+ {
+ printf (" Press any key to continue\n\n");
+ getkey ();
+ }
+ }
+#endif /* SUPPORT_IPMI */
switch (kernel_type)
{
case KERNEL_TYPE_LINUX:
case KERNEL_TYPE_BIG_LINUX:
if (! load_initrd (arg))
- return 1;
+ ret = 1;
break;
default:
errnum = ERR_NEED_LX_KERNEL;
- return 1;
+ ret = 1;
}
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ ipmi_watchdog_stop();
+#endif /* SUPPORT_IPMI */
- return 0;
+ return ret;
}
static struct builtin builtin_initrd =
@@ -2389,10 +2423,43 @@ kernel_func (char *arg, int flags)
/* Copy the command-line to MB_CMDLINE. */
grub_memmove (mb_cmdline, arg, len + 1);
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ {
+ int value;
+ value = cgl_get_watchdog_kernel_load_timeout();
+ if (value)
+ {
+ watchdog_action_type action_type;
+ action_type = cgl_get_watchdog_timer_action();
+ ipmi_watchdog_set_timer_action(action_type);
+ if (ipmi_watchdog_set_timeout(value) != 0)
+ {
+ printf ("set watchdog timeout failed\n");
+ printf ("Press any key to continue\n");
+ cgl_down_init_flag ();
+ getkey ();
+ return -1;
+ }
+ }
+ if (cgl_is_debug_mode())
+ {
+ printf (" Press any key to continue\n\n");
+ getkey ();
+ }
+ }
+#endif /* SUPPORT_IPMI */
kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags);
if (kernel_type == KERNEL_TYPE_NONE)
- return 1;
+ {
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ ipmi_watchdog_stop();
+#endif /* SUPPORT_IPMI */
+ return 1;
+ }
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ ipmi_watchdog_stop();
+#endif /* SUPPORT_IPMI */
mb_cmdline += len + 1;
return 0;
}
diff -Nurp grub-0.97/stage2/cgl_config.c grub-0.97.ipmi/stage2/cgl_config.c
--- grub-0.97/stage2/cgl_config.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/cgl_config.c 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ * some functions are picked from GRUB2.
+ */
+
+#include "cgl_config.h"
+#include "shared.h"
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#define MAX_STRING_LENGTH 1024
+
+struct watchdog_config
+{
+ char interface[MAX_STRING_LENGTH];
+ int kernel_boot_timeout;
+ int kernel_load_timeout;
+ watchdog_action_type action_type;
+};
+
+struct ipmi_config
+{
+ unsigned int io_port;
+ unsigned long mem_address;
+ int reg_spacing;
+ int reg_size;
+ int reg_shift;
+};
+
+struct cgl_config
+{
+ int initialized : 1;
+ int is_debug_mode : 1;
+ struct ipmi_config i_config;
+ struct watchdog_config w_config;
+};
+
+static struct cgl_config config;
+
+enum {
+ SECTION_STATE_NONE,
+ SECTION_STATE_IPMI,
+ SECTION_STATE_WATCHDOG,
+ SECTION_STATE_INVALID
+};
+
+static int section_state;
+
+static char grub_base_dir[MAX_STRING_LENGTH];
+
+/* Divide N by D, return the quotient, and store the remainder in *R. */
+static long long
+cgl_divmod64 (unsigned long long n, unsigned int d, unsigned int *r)
+{
+ /* This algorithm is typically implemented by hardware. The idea
+ is to get the highest bit in N, 64 times, by keeping
+ upper(N * 2^i) = upper((Q * 10 + M) * 2^i), where upper
+ represents the high 64 bits in 128-bits space. */
+ unsigned bits = 64;
+ unsigned long long q = 0;
+ unsigned m = 0;
+
+ /* Skip the slow computation, if 32-bit arithmetics are possible. */
+ if (n < 0xffffffff) {
+ if (r)
+ *r = ((unsigned int) n) % d;
+
+ return ((unsigned int) n) / d;
+ }
+
+ while (bits--) {
+ m <<= 1;
+
+ if (n & (1ULL << 63))
+ m |= 1;
+
+ q <<= 1;
+ n <<= 1;
+
+ if (m >= d) {
+ q |= 1;
+ m -= d;
+ }
+ }
+
+ if (r)
+ *r = m;
+
+ return q;
+}
+
+static unsigned long long
+cgl_strtoull (const char *str, char **end, int base)
+{
+ unsigned long long num = 0;
+ int found = 0;
+
+ /* Skip white spaces. */
+ while (*str && grub_isspace (*str))
+ str++;
+
+ /* Guess the base, if not specified. The prefix `0x' means 16, and
+ the prefix `0' means 8. */
+ if (base == 0 && str[0] == '0') {
+ if (str[1] == 'x') {
+ if (base == 0 || base == 16) {
+ base = 16;
+ str += 2;
+ }
+ }
+ else if (str[1] >= '0' && str[1] <= '7')
+ base = 8;
+ }
+
+ if (base == 0)
+ base = 10;
+
+ while (*str) {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9) {
+ digit += '0' - 'a' + 10;
+ if (digit >= (unsigned long) base)
+ break;
+ }
+
+ found = 1;
+
+ /* NUM * BASE + DIGIT > ~0ULL */
+ if (num > cgl_divmod64 (~0ULL - digit, base, 0))
+ return ~0ULL;
+
+ num = num * base + digit;
+ str++;
+ }
+
+ if (!found)
+ return 0;
+
+ if (end)
+ *end = (char *) str;
+
+ return num;
+}
+
+static unsigned long
+cgl_strtoul (const char *str, char **end, int base)
+{
+ unsigned long long num;
+
+ num = cgl_strtoull (str, end, base);
+ if (num > ~0UL)
+ return ~0UL;
+
+ return (unsigned long) num;
+}
+
+static int
+cgl_strtotimeout (const char *str, char **end, int base)
+{
+ unsigned long long num;
+
+ num = cgl_strtoull (str, end, base);
+ if (num > 6553)
+ return -1;
+
+ return (int) num;
+}
+
+static char *
+cgl_strchr (const char *s, int c)
+{
+ while (*s) {
+ if (*s == c)
+ return (char *) s;
+ s++;
+ }
+
+ return NULL;
+}
+
+static char *
+cgl_strrchr (const char *s, int c)
+{
+ char *p = 0;
+
+ while (*s) {
+ if (*s == c)
+ p = (char *) s;
+ s++;
+ }
+
+ return p;
+}
+
+static int
+cgl_strcasecmp (const char *s1, const char *s2)
+{
+ while (*s1 && *s2) {
+ if (grub_tolower (*s1) != grub_tolower (*s2))
+ return (int) grub_tolower (*s1) - (int) grub_tolower (*s2);
+
+ s1++;
+ s2++;
+ }
+
+ return (int) *s1 - (int) *s2;
+}
+
+static int
+cgl_strncasecmp (const char *s1, const char *s2, int c)
+{
+ int p = 1;
+
+ while (*s1 && *s2 && p < c) {
+ if (grub_tolower (*s1) != grub_tolower (*s2))
+ return (int) grub_tolower (*s1) - (int) grub_tolower (*s2);
+
+ s1++;
+ s2++;
+ p++;
+ }
+
+ return (int) *s1 - (int) *s2;
+}
+
+static char *
+cgl_strchug (char *s)
+{
+ char *p;
+
+ for (p = s; *p && grub_isspace(*p); p++)
+ ;
+
+ grub_memmove(s, p, grub_strlen(p) + 1);
+
+ return s;
+}
+
+static char *
+cgl_strchomp (char *s)
+{
+ int len = grub_strlen(s);
+
+ if (!s)
+ return NULL;
+
+ while (len--) {
+ if (grub_isspace(s[len]))
+ s[len] = '\0';
+ else
+ break;
+ }
+ return s;
+}
+
+static char *
+cgl_strip_line (char *line)
+{
+ char *s;
+
+ /* remove comment */
+ s = cgl_strchr(line, '#');
+ if (s)
+ *s = '\0';
+
+ s = cgl_strchomp(cgl_strchug((char*)line));
+
+ return s;
+}
+
+static void
+get_ipmi_value (const char *line)
+{
+ char *pos, *s;
+ int l;
+
+ if (line[0] == '\0')
+ return;
+
+ s = cgl_strip_line((char*)line);
+
+ pos = cgl_strchr(s, '=');
+ if (!pos)
+ return;
+
+ l = pos - s;
+ if (!grub_memcmp(s, "io_port", l-1))
+ config.i_config.io_port = cgl_strtoul(&s[l+1], 0, 0);
+ else if (!grub_memcmp(s, "mem_address", l-1))
+ config.i_config.mem_address = cgl_strtoul(&s[l+1], 0, 0);
+ else if (!grub_memcmp(s, "reg_spacing", l-1))
+ config.i_config.reg_spacing = cgl_strtoul(&s[l+1], 0, 0);
+ else if (!grub_memcmp(s, "reg_size", l-1))
+ config.i_config.reg_size = cgl_strtoul(&s[l+1], 0, 0);
+ else if (!grub_memcmp(s, "debug_mode", l-1)) {
+ if (s[l+1] != '0' && !cgl_strcasecmp(&s[l+1],"on")) /* debug mode
except 0 */
+ config.is_debug_mode = 1;
+ }
+ else
+ errnum = ERR_UNRECOGNIZED;
+}
+
+static void
+set_watchdog_action_type (const char *value)
+{
+ if (!value)
+ return;
+
+ if (!cgl_strcasecmp (value, "reset"))
+ config.w_config.action_type = WATCHDOG_ACTION_RESET;
+ else if (!cgl_strcasecmp (value, "power_down"))
+ config.w_config.action_type = WATCHDOG_ACTION_POWER_DOWN;
+ else if (!cgl_strcasecmp (value, "power_cycle"))
+ config.w_config.action_type = WATCHDOG_ACTION_POWER_CYCLE;
+ else
+ errnum = ERR_UNRECOGNIZED;
+}
+
+static void
+get_watchdog_value (const char *line)
+{
+ char *pos, *s;
+ int l;
+
+ if (line[0] == '\0')
+ return;
+
+ s = cgl_strip_line((char*)line);
+
+ pos = cgl_strchr(s, '=');
+ if (!pos)
+ return;
+
+ l = pos - s;
+ if (!grub_memcmp(s, "interface", l-1))
+ grub_strcpy(config.w_config.interface, &s[l+1]);
+ else if (!grub_memcmp(s, "kernel_boot_timeout", l-1))
+ config.w_config.kernel_boot_timeout = cgl_strtotimeout (&s[l+1], 0, 0);
+ else if (!grub_memcmp(s, "kernel_load_timeout", l-1))
+ config.w_config.kernel_load_timeout = cgl_strtotimeout (&s[l+1], 0, 0);
+ else if (!grub_memcmp(s, "timer_action", l-1))
+ set_watchdog_action_type (&s[l+1]);
+ else
+ errnum = ERR_UNRECOGNIZED;
+}
+
+static void
+parse_line (const char *line)
+{
+ switch(line[0]) {
+ case '[': /* section line */
+ if (!cgl_strncasecmp(line, "[IPMI]", 6))
+ section_state = SECTION_STATE_IPMI;
+ else if (!cgl_strncasecmp(line, "[Watchdog]", 10))
+ section_state = SECTION_STATE_WATCHDOG;
+ else if (section_state == SECTION_STATE_IPMI ||
+ section_state == SECTION_STATE_WATCHDOG)
+ {
+ section_state = SECTION_STATE_INVALID;
+ errnum = ERR_UNRECOGNIZED;
+ }
+ else
+ section_state = SECTION_STATE_INVALID;
+ break;
+ case '#': /* comment line */
+ break;
+ default:
+ if (section_state == SECTION_STATE_IPMI)
+ get_ipmi_value(line);
+ else if (section_state == SECTION_STATE_WATCHDOG)
+ get_watchdog_value(line);
+ break;
+ }
+}
+
+static void
+set_grub_base_dir (void)
+{
+ char *p;
+ int l;
+
+ grub_base_dir[0] = '\0';
+
+ p = cgl_strrchr(config_file, '/');
+ if (!p)
+ return;
+
+ l = grub_strlen(config_file) - grub_strlen(p) + 1;
+
+ if (l > MAX_STRING_LENGTH -1)
+ return;
+
+ grub_memcpy(grub_base_dir, config_file, l);
+ grub_base_dir[l+1] = '\0';
+}
+
+static int
+cgl_config_load (const char *filename)
+{
+ char c;
+ char line[MAX_STRING_LENGTH + 1];
+ int p = 0;
+ char fullpath_filename[MAX_STRING_LENGTH];
+
+ grub_memset (fullpath_filename, 0, sizeof(fullpath_filename));
+ set_grub_base_dir();
+
+ if (grub_strlen(grub_base_dir) + grub_strlen(filename) > MAX_STRING_LENGTH)
+ return FALSE;
+
+ grub_strcpy(fullpath_filename, grub_base_dir);
+ grub_strncat(fullpath_filename, filename, sizeof(fullpath_filename));
+
+ if (!grub_open((char *)fullpath_filename))
+ return FALSE;
+
+ section_state = SECTION_STATE_NONE;
+
+ while (grub_read(&c, 1)) {
+ if (c == '\n') {
+ line[p] = '\0';
+ parse_line(line);
+ p = 0;
+ } else if (p < MAX_STRING_LENGTH - 1){
+ line[p] = c;
+ p++;
+ } else {
+ /* the line is too long, ignore it ;-p */
+ p = 0;
+ }
+ }
+
+ grub_close();
+ if(errnum == ERR_UNRECOGNIZED) {
+ printf("\n\
+ io_port = 0x%x\n\
+ mem_address = %d\n\
+ reg_spacing = %d\n\
+ reg_size = %d \n",
+ config.i_config.io_port,
+ config.i_config.mem_address,
+ config.i_config.reg_spacing,
+ config.i_config.reg_size);
+
+ printf("\n\
+ interface = %s\n\
+ boot_timeout = %d\n\
+ load_timeout = %d\n\
+ action_type = %d \n",
+ config.w_config.interface,
+ config.w_config.kernel_boot_timeout,
+ config.w_config.kernel_load_timeout,
+ config.w_config.action_type);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+cgl_config_init (void)
+{
+ grub_memset(&config, 0, sizeof(struct cgl_config));
+
+ if (cgl_config_load("cgl.conf")) {
+ config.initialized = 1;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static struct cgl_config *
+cgl_get_config (void)
+{
+ if (config.initialized)
+ return &config;
+
+ return NULL;
+}
+
+void
+cgl_down_init_flag (void)
+{
+ config.initialized = 0;
+}
+int
+cgl_get_watchdog_kernel_boot_timeout (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->w_config.kernel_boot_timeout : 0;
+}
+
+int
+cgl_get_watchdog_kernel_load_timeout (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->w_config.kernel_load_timeout : 0;
+}
+
+const char *
+cgl_get_watchdog_interface (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->w_config.interface : "";
+}
+
+int
+cgl_get_ipmi_reg_spacing (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->i_config.reg_spacing : 0;
+}
+
+int
+cgl_get_ipmi_reg_size (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->i_config.reg_size : 0;
+}
+
+int
+cgl_get_ipmi_reg_shift (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->i_config.reg_shift : 0;
+}
+
+unsigned int
+cgl_get_ipmi_io_port (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->i_config.io_port : 0;
+}
+
+unsigned long
+cgl_get_ipmi_mem_address (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->i_config.mem_address : 0;
+}
+
+int
+cgl_is_debug_mode (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->is_debug_mode : 0;
+}
+
+watchdog_action_type
+cgl_get_watchdog_timer_action (void)
+{
+ struct cgl_config *cgl_config;
+
+ cgl_config = cgl_get_config();
+
+ return cgl_config ? cgl_config->w_config.action_type :
WATCHDOG_ACTION_RESET;
+}
+
+int
+cgl_init (void)
+{
+ return cgl_config_init();
+}
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/cgl_config.h grub-0.97.ipmi/stage2/cgl_config.h
--- grub-0.97/stage2/cgl_config.h 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/cgl_config.h 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CGL_CONFIG_H__
+#define __CGL_CONFIG_H__
+
+#include <ipmi_watchdog.h>
+
+int cgl_init (void);
+
+const char *cgl_get_watchdog_interface (void);
+int cgl_get_watchdog_kernel_boot_timeout (void);
+int cgl_get_watchdog_kernel_load_timeout (void);
+
+watchdog_action_type cgl_get_watchdog_timer_action (void);
+
+int cgl_get_ipmi_reg_spacing (void);
+int cgl_get_ipmi_reg_size (void);
+int cgl_get_ipmi_reg_shift (void);
+unsigned int cgl_get_ipmi_io_port (void);
+unsigned long cgl_get_ipmi_mem_address (void);
+
+int cgl_is_debug_mode (void);
+void cgl_down_init_flag (void);
+
+#endif /* __CGL_CONFIG_H__ */
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/char_io.c grub-0.97.ipmi/stage2/char_io.c
--- grub-0.97/stage2/char_io.c 2005-02-02 05:51:23.000000000 +0900
+++ grub-0.97.ipmi/stage2/char_io.c 2008-09-11 20:23:13.000000000 +0900
@@ -1217,6 +1217,16 @@ memcheck (int addr, int len)
return ! errnum;
}
+void
+grub_memcpy(void *dest, const void *src, int len)
+{
+ int i;
+ register char *d = (char*)dest, *s = (char*)src;
+
+ for (i = 0; i < len; i++)
+ d[i] = s[i];
+}
+
void *
grub_memmove (void *to, const void *from, int len)
{
diff -Nurp grub-0.97/stage2/cmdline.c grub-0.97.ipmi/stage2/cmdline.c
--- grub-0.97/stage2/cmdline.c 2004-08-17 08:23:01.000000000 +0900
+++ grub-0.97.ipmi/stage2/cmdline.c 2008-09-11 20:23:13.000000000 +0900
@@ -25,6 +25,11 @@
# include <etherboot.h>
#endif
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+# include "ipmi_watchdog.h"
+# include "cgl_config.h"
+#endif /* SUPPORT_IPMI */
+
grub_jmp_buf restart_cmdline_env;
/* Find the next word from CMDLINE and return the pointer. If
@@ -192,6 +197,7 @@ run_script (char *script, char *heap)
{
struct builtin *builtin;
char *arg;
+ int ret;
print_error ();
@@ -224,6 +230,32 @@ run_script (char *script, char *heap)
if (kernel_type == KERNEL_TYPE_NONE)
return 0;
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ {
+ int value;
+ value = cgl_get_watchdog_kernel_boot_timeout();
+ if (value)
+ {
+ watchdog_action_type action_type;
+ action_type = cgl_get_watchdog_timer_action();
+ ipmi_watchdog_set_timer_action(action_type);
+ if (ipmi_watchdog_set_timeout(value) != 0)
+ {
+ cgl_down_init_flag ();
+ printf (" set watchdog timeout failed \n");
+ printf ("Press any key to continue\n");
+ getkey ();
+ return 1;
+ }
+ }
+ if (cgl_is_debug_mode())
+ {
+ printf (" Press any key to continue\n\n");
+ getkey ();
+ }
+ }
+#endif /* SUPPORT_IPMI */
+
/* Otherwise, the command boot is run implicitly. */
grub_memmove (heap, "boot", 5);
}
@@ -252,6 +284,10 @@ run_script (char *script, char *heap)
/* Run BUILTIN->FUNC. */
arg = skip_to (1, heap);
- (builtin->func) (arg, BUILTIN_SCRIPT);
+ ret = (builtin->func) (arg, BUILTIN_SCRIPT);
+
+ if (ret < 0)
+ return 1;
+
}
}
diff -Nurp grub-0.97/stage2/ipmi.c grub-0.97.ipmi/stage2/ipmi.c
--- grub-0.97/stage2/ipmi.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi.c 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "ipmi.h"
+#include "ipmi_si_sm.h"
+#include "ipmi_msgdefs.h"
+#include "cgl_config.h"
+#include "shared.h"
+#include "timer.h"
+
+#define udelay(n) waiton_timer2(((n)*TICKS_PER_MS)/1000)
+#define DEFAULT_REG_SPACING 1
+#define DEFAULT_REG_SIZE 1
+#define DEFAULT_REG_SHIFT 0
+
+typedef enum {
+ SI_KCS,
+ SI_SMIC,
+ SI_BT
+} si_type;
+
+#ifndef SUPPORT_NETBOOT
+void load_timer2(unsigned int ticks)
+{
+ /* Set up the timer gate, turn off the speaker */
+ outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
+ outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
+ outb(ticks & 0xFF, TIMER2_PORT);
+ outb(ticks >> 8, TIMER2_PORT);
+}
+#endif /* SUPPORT_NETBOOT */
+
+struct ipmi_handler
+{
+ unsigned char ipmi_version;
+ struct si_sm_handler *si_handler;
+ struct si_sm_io io;
+};
+
+static int ipmi_initialized = 0;
+
+static struct ipmi_handler i_handler;
+
+static struct ipmi_default_vals
+{
+ si_type type;
+ char *name;
+ int port;
+} ipmi_defaults[] =
+{
+ { .type = SI_KCS, .name = "kcs", .port = 0xca2 },
+ { .type = SI_SMIC, .name = "smic", .port = 0xca9 },
+ { .type = SI_BT, .name = "bt", .port = 0xe4 }
+};
+
+static int ipmi_defaults_len = sizeof(ipmi_defaults)/sizeof(ipmi_defaults[0]);
+
+#define SI_SHORT_TIMEOUT_USEC 250
+
+static int try_get_dev_id (struct ipmi_handler *ipmi_handler);
+static int ipmi_probe_interface (struct ipmi_handler *ipmi_handler);
+static int ipmi_init_interface (struct ipmi_handler *ipmi_handler,
+ si_type type, si_addr_type addr_type,
+ unsigned long address,
+ int reg_spacing, int reg_size, int reg_shift);
+static int ipmi_cleanup (struct ipmi_handler *ipmi_handler);
+
+static int
+ipmi_cleanup (struct ipmi_handler *ipmi_handler)
+{
+ if (!ipmi_handler->si_handler)
+ return -1;
+
+ ipmi_handler->si_handler->cleanup();
+ ipmi_si_sm_io_cleanup(&ipmi_handler->io);
+
+ return 0;
+}
+
+static int
+ipmi_init_interface (struct ipmi_handler *ipmi_handler, si_type type,
+ si_addr_type addr_type, unsigned long address,
+ int reg_spacing, int reg_size, int reg_shift)
+{
+ switch (type) {
+ case SI_KCS:
+ ipmi_handler->si_handler = &kcs_smi_handler;
+ break;
+ case SI_SMIC:
+ ipmi_handler->si_handler = &smic_smi_handler;
+ break;
+ case SI_BT:
+ ipmi_handler->si_handler = &bt_smi_handler;
+ break;
+ default:
+ return -1;
+ }
+
+ if (ipmi_si_sm_io_setup(&ipmi_handler->io, addr_type, address,
+ reg_spacing, reg_size, reg_shift))
+ return -1;
+
+ ipmi_handler->si_handler->init(&ipmi_handler->io);
+
+ if (ipmi_handler->si_handler->detect() == 0 &&
+ try_get_dev_id(ipmi_handler) == 0) {
+ ipmi_initialized = 1;
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Based on try_get_dev_id() in ipmi_si_intf.c */
+static int
+try_get_dev_id(struct ipmi_handler *ipmi_handler)
+{
+ unsigned char msg[2];
+ unsigned char resp[IPMI_MAX_MSG_LENGTH];
+ unsigned long resp_len;
+ enum si_sm_result smi_result;
+
+ /* Do a Get Device ID command, since it comes back with some
+ useful info. */
+ msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+ msg[1] = IPMI_GET_DEVICE_ID_CMD;
+ ipmi_handler->si_handler->start_transaction(msg, 2);
+
+ smi_result = ipmi_handler->si_handler->event(0);
+ while (1) {
+ if (smi_result == SI_SM_CALL_WITH_DELAY ||
+ smi_result == SI_SM_CALL_WITH_TICK_DELAY) {
+ udelay(100);
+ smi_result = ipmi_handler->si_handler->event(100);
+ } else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
+ smi_result = ipmi_handler->si_handler->event(0);
+ } else {
+ break;
+ }
+ }
+ if (smi_result == SI_SM_HOSED) {
+ /* We couldn't get the state machine to run, so whatever's at
+ the port is probably not an IPMI SMI interface. */
+ return -1;
+ }
+
+ /* Otherwise, we got some data. */
+ resp_len = ipmi_handler->si_handler->get_result(resp, IPMI_MAX_MSG_LENGTH);
+ if (resp_len < 14) {
+ /* That's odd, it should be longer. */
+ return -1;
+ }
+
+ if ((resp[1] != IPMI_GET_DEVICE_ID_CMD) || (resp[2] != 0)) {
+ /* That's odd, it shouldn't be able to fail. */
+ return -1;
+ }
+
+ /* set version */
+ ipmi_handler->ipmi_version = resp[7];
+
+ return 0;
+}
+
+static int
+ipmi_probe_interface (struct ipmi_handler *ipmi_handler)
+{
+ int i;
+
+ /* cheap auto probe */
+ for (i = 0; i< ipmi_defaults_len; i++) {
+ int rv;
+
+ rv = ipmi_init_interface(ipmi_handler,
+ ipmi_defaults[i].type,
+ SI_ADDR_TYPE_IO,
+ ipmi_defaults[i].port,
+ DEFAULT_REG_SPACING,
+ DEFAULT_REG_SIZE,
+ DEFAULT_REG_SHIFT);
+
+ if (rv == 0)
+ return 0;
+
+ ipmi_cleanup(ipmi_handler);
+ }
+ return -1;
+}
+
+static int
+ipmi_set_manual_interface (struct ipmi_handler *ipmi_handler,
+ const char *interface)
+{
+ int i;
+ unsigned long address;
+ int reg_spacing;
+ int reg_size;
+ int reg_shift;
+ si_addr_type addr_type = 0;
+
+ if (grub_memcmp(interface, "ipmi-", grub_strlen("ipmi-") - 1) != 0)
+ return -1;
+
+ reg_spacing = cgl_get_ipmi_reg_spacing();
+ if (reg_spacing == 0)
+ reg_spacing = DEFAULT_REG_SPACING;
+ reg_size = cgl_get_ipmi_reg_size();
+ if (reg_size == 0)
+ reg_size = DEFAULT_REG_SIZE;
+ reg_shift = cgl_get_ipmi_reg_shift();
+ if (reg_shift == 0)
+ reg_shift = DEFAULT_REG_SHIFT;
+
+ address = cgl_get_ipmi_io_port();
+ if (address != 0) {
+ addr_type = SI_ADDR_TYPE_IO;
+ } else {
+ address = cgl_get_ipmi_mem_address();
+ if (address != 0) {
+ addr_type = SI_ADDR_TYPE_MEMORY;
+ }
+ }
+
+ for (i = 0; i < ipmi_defaults_len; i++) {
+ if (grub_strcmp(interface+5, ipmi_defaults[i].name) == 0) {
+ int rv;
+ if (address == 0) {
+ address = ipmi_defaults[i].port;
+ addr_type = SI_ADDR_TYPE_IO;
+ }
+ rv = ipmi_init_interface(ipmi_handler, ipmi_defaults[i].type,
+ addr_type, address,
+ reg_spacing, reg_size, reg_shift);
+ if (rv == 0)
+ return 0;
+
+ ipmi_cleanup(ipmi_handler);
+ }
+ }
+
+ return -1;
+}
+
+int
+ipmi_init (void)
+{
+ const char *interface;
+
+ if (ipmi_initialized)
+ return 0;
+
+ grub_memset(&i_handler, 0, sizeof(struct ipmi_handler));
+
+ interface = cgl_get_watchdog_interface();
+
+ if (grub_strlen(interface) == 0)
+ return -1;
+
+ if (!grub_strcmp(interface, "auto")) {
+ if (ipmi_probe_interface(&i_handler))
+ return -1;
+ } else {
+ if (ipmi_set_manual_interface(&i_handler, interface))
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+ipmi_quit (void)
+{
+ if (!ipmi_initialized) /* not initialized */
+ return;
+
+ ipmi_cleanup(&i_handler);
+ ipmi_initialized = 0;
+}
+
+unsigned char
+ipmi_get_version (void)
+{
+ if (!ipmi_initialized)
+ return 0;
+ return i_handler.ipmi_version;
+}
+
+int
+ipmi_request (struct ipmi_addr *addr, struct ipmi_msg *send_msg)
+{
+ enum si_sm_result result;
+ unsigned char send_data[IPMI_MAX_MSG_LENGTH];
+ unsigned int send_data_length;
+
+ switch (addr->addr_type) {
+ case IPMI_SYSTEM_INTERFACE_ADDR_TYPE:
+ if (send_msg->netfn & 1) {
+ /* Responses are not allowed to the SMI. */
+ return -1;
+ }
+
+ if (addr->lun > 3)
+ return -1;
+
+ if ((send_msg->netfn == IPMI_NETFN_APP_REQUEST) &&
+ ((send_msg->cmd == IPMI_SEND_MSG_CMD) ||
+ (send_msg->cmd == IPMI_GET_MSG_CMD) ||
+ (send_msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) {
+ /* We don't let the user do these, since we manage
+ the sequence numbers. */
+ return -1;
+ }
+ if ((send_msg->data_len + 2) > IPMI_MAX_MSG_LENGTH)
+ return -1;
+
+ send_data[0] = (send_msg->netfn << 2) | (addr->lun & 0x3);
+ send_data[1] = send_msg->cmd;
+ if (send_msg->data_len > 0)
+ grub_memcpy(&(send_data[2]), send_msg->data,
send_msg->data_len);
+ send_data_length = send_msg->data_len + 2;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ if (i_handler.si_handler->start_transaction(send_data, send_data_length))
+ return -1;
+
+ result = i_handler.si_handler->event(0);
+ while (result != SI_SM_IDLE) {
+ udelay(SI_SHORT_TIMEOUT_USEC);
+ result = i_handler.si_handler->event(SI_SHORT_TIMEOUT_USEC);
+ }
+
+ return 0;
+}
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
+
diff -Nurp grub-0.97/stage2/ipmi.h grub-0.97.ipmi/stage2/ipmi.h
--- grub-0.97/stage2/ipmi.h 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi.h 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* some structures are picked from ipmi.h linux kernel. */
+
+#ifndef __IPMI_H__
+#define __IPMI_H__
+
+#include "ipmi_msgdefs.h"
+
+struct ipmi_msg
+{
+ unsigned char netfn;
+ unsigned char cmd;
+ unsigned short data_len;
+ unsigned char *data;
+};
+
+#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE 0x0c
+struct ipmi_addr
+{
+ int addr_type;
+ short channel;
+ unsigned char lun;
+};
+#define IPMI_BMC_CHANNEL 0xf
+
+int ipmi_init (void);
+void ipmi_quit (void);
+unsigned char ipmi_get_version (void);
+
+#define IPMI_MAJOR_VERSION (ipmi_get_version() & 0xf)
+#define IPMI_MINOR_VERSION (ipmi_get_version() >> 4)
+
+int ipmi_request (struct ipmi_addr *addr, struct ipmi_msg *send_msg);
+
+#endif /* __IPMI_H__ */
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/ipmi_bt_sm.c grub-0.97.ipmi/stage2/ipmi_bt_sm.c
--- grub-0.97/stage2/ipmi_bt_sm.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_bt_sm.c 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,661 @@
+/*
+ * ipmi_bt_sm.c
+ *
+ * The state machine for an Open IPMI BT sub-driver under ipmi_si.c, part
+ * of the driver architecture at http://sourceforge.net/project/openipmi
+ *
+ * Author: Rocky Craig <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ipmi_msgdefs.h" /* for completion codes */
+#include "ipmi_si_sm.h"
+#include "cgl_config.h"
+#include "shared.h"
+
+#define BT_DEBUG_OF 0 /* Used in production */
+#define BT_DEBUG_ENABLE 1 /* Generic messages */
+#define BT_DEBUG_MSG 2 /* Prints all request/response buffers */
+#define BT_DEBUG_STATES 4 /* Verbose look at state changes */
+/* BT_DEBUG_OFF must be zero to correspond to the default uninitialized
+ value */
+
+static int bt_debug; /* 0 == BT_DEBUG_OFF */
+
+/* Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds,
+ and 64 byte buffers. However, one HP implementation wants 255 bytes of
+ buffer (with a documented message of 160 bytes) so go for the max.
+ Since the Open IPMI architecture is single-message oriented at this
+ stage, the queue depth of BT is of no concern. */
+
+#define BT_NORMAL_TIMEOUT 5 /* seconds */
+#define BT_NORMAL_RETRY_LIMIT 2
+#define BT_RESET_DELAY 6 /* seconds after warm reset */
+
+/* States are written in chronological order and usually cover
+ multiple rows of the state table discussion in the IPMI spec. */
+
+enum bt_states {
+ BT_STATE_IDLE = 0, /* Order is critical in this list */
+ BT_STATE_XACTION_START,
+ BT_STATE_WRITE_BYTES,
+ BT_STATE_WRITE_CONSUME,
+ BT_STATE_READ_WAIT,
+ BT_STATE_CLEAR_B2H,
+ BT_STATE_READ_BYTES,
+ BT_STATE_RESET1, /* These must come last */
+ BT_STATE_RESET2,
+ BT_STATE_RESET3,
+ BT_STATE_RESTART,
+ BT_STATE_PRINTME,
+ BT_STATE_CAPABILITIES_BEGIN,
+ BT_STATE_CAPABILITIES_END,
+ BT_STATE_LONG_BUSY /* BT doesn't get hosed :-) */
+};
+
+/* Macros seen at the end of state "case" blocks. They help with legibility
+ and debugging. */
+
+#define BT_STATE_CHANGE(X,Y) { bt_data.state = X; return Y; }
+
+#define BT_SI_SM_RETURN(Y) { last_printed = BT_STATE_PRINTME; return Y; }
+
+struct si_sm_data {
+ enum bt_states state;
+ unsigned char seq; /* BT sequence number */
+ struct si_sm_io *io;
+ unsigned char write_data[IPMI_MAX_MSG_LENGTH];
+ int write_count;
+ unsigned char read_data[IPMI_MAX_MSG_LENGTH];
+ int read_count;
+ int truncated;
+ long timeout; /* microseconds countdown */
+ int error_retries; /* end of "common" fields */
+ int nonzero_status; /* hung BMCs stay all 0 */
+ enum bt_states complete; /* to divert the state machine */
+ int BT_CAP_outreqs;
+ long BT_CAP_req2rsp;
+ int BT_CAP_retries; /* Recommended retries */
+};
+
+struct si_sm_data bt_data;
+
+#define BT_CLR_WR_PTR 0x01 /* See IPMI 1.5 table 11.6.4 */
+#define BT_CLR_RD_PTR 0x02
+#define BT_H2B_ATN 0x04
+#define BT_B2H_ATN 0x08
+#define BT_SMS_ATN 0x10
+#define BT_OEM0 0x20
+#define BT_H_BUSY 0x40
+#define BT_B_BUSY 0x80
+
+/* Some bits are toggled on each write: write once to set it, once
+ more to clear it; writing a zero does nothing. To absolutely
+ clear it, check its state and write if set. This avoids the "get
+ current then use as mask" scheme to modify one bit. Note that the
+ variable "bt" is hardcoded into these macros. */
+
+#define BT_STATUS bt_data.io->inputb(bt_data.io, 0)
+#define BT_CONTROL(x) bt_data.io->outputb(bt_data.io, 0, x)
+
+#define BMC2HOST bt_data.io->inputb(bt_data.io, 1)
+#define HOST2BMC(x) bt_data.io->outputb(bt_data.io, 1, x)
+
+#define BT_INTMASK_R bt_data.io->inputb(bt_data.io, 2)
+#define BT_INTMASK_W(x) bt_data.io->outputb(bt_data.io, 2, x)
+
+/* Convenience routines for debugging. These are not multi-open safe!
+ Note the macros have hardcoded variables in them. */
+
+static char *state2txt(unsigned char state)
+{
+ switch (state) {
+ case BT_STATE_IDLE: return("IDLE");
+ case BT_STATE_XACTION_START: return("XACTION");
+ case BT_STATE_WRITE_BYTES: return("WR_BYTES");
+ case BT_STATE_WRITE_CONSUME: return("WR_CONSUME");
+ case BT_STATE_READ_WAIT: return("RD_WAIT");
+ case BT_STATE_CLEAR_B2H: return("CLEAR_B2H");
+ case BT_STATE_READ_BYTES: return("RD_BYTES");
+ case BT_STATE_RESET1: return("RESET1");
+ case BT_STATE_RESET2: return("RESET2");
+ case BT_STATE_RESET3: return("RESET3");
+ case BT_STATE_RESTART: return("RESTART");
+ case BT_STATE_LONG_BUSY: return("LONG_BUSY");
+ case BT_STATE_CAPABILITIES_BEGIN: return("CAP_BEGIN");
+ case BT_STATE_CAPABILITIES_END: return("CAP_END");
+ }
+ return("BAD STATE");
+}
+#define STATE2TXT state2txt(bt_data.state)
+
+#define B_BUSY_STRING "B_BUSY "
+#define H_BUSY_STRING "H_BUSY "
+#define OEM0_STRING "OEM0 "
+#define SMS_STRING "SMS "
+#define B2H_STRING "B2H "
+#define H2B_STRING "H2B "
+
+static char *status2txt(unsigned char status)
+{
+ /*
+ * This cannot be called by two threads at the same time and
+ * the buffer is always consumed immediately, so the static is
+ * safe to use.
+ */
+ static char buf[40];
+
+ strcpy(buf, "[ ");
+ if (status & BT_B_BUSY)
+ grub_strncat(buf, B_BUSY_STRING, grub_strlen(B_BUSY_STRING));
+ if (status & BT_H_BUSY)
+ grub_strncat(buf, H_BUSY_STRING, grub_strlen(H_BUSY_STRING));
+ if (status & BT_OEM0)
+ grub_strncat(buf, OEM0_STRING, grub_strlen(OEM0_STRING));
+ if (status & BT_SMS_ATN)
+ grub_strncat(buf, SMS_STRING, grub_strlen(SMS_STRING));
+ if (status & BT_B2H_ATN)
+ grub_strncat(buf, B2H_STRING, grub_strlen(B2H_STRING));
+ if (status & BT_H2B_ATN)
+ grub_strncat(buf, H2B_STRING, grub_strlen(H2B_STRING));
+ grub_strncat(buf, "]", 1);
+ return buf;
+}
+#define STATUS2TXT status2txt(status)
+
+/* called externally at insmod time, and internally on cleanup */
+
+static void bt_init(struct si_sm_io *io)
+{
+ grub_memset(&bt_data, 0, sizeof(struct si_sm_data));
+ if (bt_data.io != io) { /* external: one-time only things */
+ bt_data.io = io;
+ bt_data.seq = 0;
+ }
+ bt_data.state = BT_STATE_IDLE; /* start here */
+ bt_data.complete = BT_STATE_IDLE; /* end here */
+ bt_data.BT_CAP_req2rsp = BT_NORMAL_TIMEOUT * 1000000;
+ bt_data.BT_CAP_retries = BT_NORMAL_RETRY_LIMIT;
+
+ if (cgl_is_debug_mode())
+ bt_debug = 7;
+}
+
+/* Jam a completion code (probably an error) into a response */
+
+static void force_result(struct si_sm_data *bt, unsigned char completion_code)
+{
+ bt->read_data[0] = 4; /* # following bytes */
+ bt->read_data[1] = bt->write_data[1] | 4; /* Odd NetFn/LUN */
+ bt->read_data[2] = bt->write_data[2]; /* seq (ignored) */
+ bt->read_data[3] = bt->write_data[3]; /* Command */
+ bt->read_data[4] = completion_code;
+ bt->read_count = 5;
+}
+
+/* The upper state machine starts here */
+
+static int bt_start_transaction(unsigned char *data,
+ unsigned int size)
+{
+ unsigned int i;
+
+ if (size < 2)
+ return IPMI_REQ_LEN_INVALID_ERR;
+ if (size > IPMI_MAX_MSG_LENGTH)
+ return IPMI_REQ_LEN_EXCEEDED_ERR;
+
+ if (bt_data.state == BT_STATE_LONG_BUSY)
+ return IPMI_NODE_BUSY_ERR;
+
+ if (bt_data.state != BT_STATE_IDLE)
+ return IPMI_NOT_IN_MY_STATE_ERR;
+
+ if (bt_debug & BT_DEBUG_MSG) {
+ grub_printf("BT: +++++++++++++++++ New command\n");
+ grub_printf("BT: NetFn/LUN CMD [%d data]:", size - 2);
+ for (i = 0; i < size; i ++)
+ grub_printf (" %x", data[i]);
+ grub_printf("\n");
+ }
+ bt_data.write_data[0] = size + 1; /* all data plus seq byte */
+ bt_data.write_data[1] = *data; /* NetFn/LUN */
+ bt_data.write_data[2] = bt_data.seq++;
+ grub_memcpy(bt_data.write_data + 3, data + 1, size - 1);
+ bt_data.write_count = size + 2;
+ bt_data.error_retries = 0;
+ bt_data.nonzero_status = 0;
+ bt_data.truncated = 0;
+ bt_data.state = BT_STATE_XACTION_START;
+ bt_data.timeout = bt_data.BT_CAP_req2rsp;
+ force_result(&bt_data, IPMI_ERR_UNSPECIFIED);
+ return 0;
+}
+
+/* After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE
+ it calls this. Strip out the length and seq bytes. */
+
+static int bt_get_result(unsigned char *data,
+ unsigned int length)
+{
+ int i, msg_len;
+
+ msg_len = bt_data.read_count - 2; /* account for length & seq */
+ if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) {
+ force_result(&bt_data, IPMI_ERR_UNSPECIFIED);
+ msg_len = 3;
+ }
+ data[0] = bt_data.read_data[1];
+ data[1] = bt_data.read_data[3];
+ if (length < msg_len || bt_data.truncated) {
+ data[2] = IPMI_ERR_MSG_TRUNCATED;
+ msg_len = 3;
+ } else
+ grub_memcpy(data + 2, bt_data.read_data + 4, msg_len - 2);
+
+ if (bt_debug & BT_DEBUG_MSG) {
+ grub_printf ("BT: result %d bytes:", msg_len);
+ for (i = 0; i < msg_len; i++)
+ grub_printf(" %x", data[i]);
+ grub_printf ("\n");
+ }
+ return msg_len;
+}
+
+/* This bit's functionality is optional */
+#define BT_BMC_HWRST 0x80
+
+static void reset_flags(struct si_sm_data *bt)
+{
+ if (bt_debug)
+ grub_printf("IPMI BT: flag reset %s\n", status2txt(BT_STATUS));
+ if (BT_STATUS & BT_H_BUSY)
+ BT_CONTROL(BT_H_BUSY); /* force clear */
+ BT_CONTROL(BT_CLR_WR_PTR); /* always reset */
+ BT_CONTROL(BT_SMS_ATN); /* always clear */
+ BT_INTMASK_W(BT_BMC_HWRST);
+}
+
+/* Get rid of an unwanted/stale response. This should only be needed for
+ BMCs that support multiple outstanding requests. */
+
+static void drain_BMC2HOST(struct si_sm_data *bt)
+{
+ int i, size;
+
+ if (!(BT_STATUS & BT_B2H_ATN)) /* Not signalling a response */
+ return;
+
+ BT_CONTROL(BT_H_BUSY); /* now set */
+ BT_CONTROL(BT_B2H_ATN); /* always clear */
+ BT_STATUS; /* pause */
+ BT_CONTROL(BT_B2H_ATN); /* some BMCs are stubborn */
+ BT_CONTROL(BT_CLR_RD_PTR); /* always reset */
+ if (bt_debug)
+ grub_printf("IPMI BT: stale response %s; ", status2txt(BT_STATUS));
+ size = BMC2HOST;
+ for (i = 0; i < size ; i++)
+ BMC2HOST;
+ BT_CONTROL(BT_H_BUSY); /* now clear */
+ if (bt_debug)
+ grub_printf("drained %d bytes\n", size + 1);
+}
+
+static inline void write_all_bytes(struct si_sm_data *bt)
+{
+ int i;
+
+ if (bt_debug & BT_DEBUG_MSG) {
+ grub_printf("BT: write %d bytes seq=0x%02X", bt->write_count, bt->seq);
+ for (i = 0; i < bt->write_count; i++)
+ grub_printf (" %x", bt->write_data[i]);
+ grub_printf ("\n");
+ }
+ for (i = 0; i < bt->write_count; i++)
+ HOST2BMC(bt->write_data[i]);
+}
+
+static inline int read_all_bytes(struct si_sm_data *bt)
+{
+ unsigned char i;
+
+ /* length is "framing info", minimum = 4: NetFn, Seq, Cmd, cCode.
+ Keep layout of first four bytes aligned with write_data[] */
+
+ bt->read_data[0] = BMC2HOST;
+ bt->read_count = bt->read_data[0];
+
+ if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) {
+ if (bt_debug & BT_DEBUG_MSG)
+ grub_printf("BT: bad raw rsp len=%d\n", bt->read_count);
+ bt->truncated = 1;
+ return 1; /* let next XACTION START clean it up */
+ }
+ for (i = 1; i <= bt->read_count; i++)
+ bt->read_data[i] = BMC2HOST;
+ bt->read_count++; /* Account internally for length byte */
+
+ if (bt_debug & BT_DEBUG_MSG) {
+ int max = bt->read_count;
+
+ grub_printf("BT: got %d bytes seq=0x%02X", max, bt->read_data[2]);
+ if (max > 16)
+ max = 16;
+ for (i = 0; i < max; i++)
+ grub_printf (" %x", bt->read_data[i]);
+ grub_printf ("%s\n", bt->read_count == max ? "" : " ...");
+ }
+
+ /* per the spec, the (NetFn[1], Seq[2], Cmd[3]) tuples must match */
+ if ((bt->read_data[3] == bt->write_data[3]) &&
+ (bt->read_data[2] == bt->write_data[2]) &&
+ ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8)))
+ return 1;
+
+ if (bt_debug & BT_DEBUG_MSG)
+ grub_printf("IPMI BT: bad packet: "
+ "want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n",
+ bt->write_data[1] | 0x04, bt->write_data[2],
bt->write_data[3],
+ bt->read_data[1], bt->read_data[2], bt->read_data[3]);
+ return 0;
+}
+
+/* Restart if retries are left, or return an error completion code */
+
+static enum si_sm_result error_recovery(struct si_sm_data *bt,
+ unsigned char status,
+ unsigned char cCode)
+{
+ char *reason;
+
+ bt->timeout = bt->BT_CAP_req2rsp;
+
+ switch (cCode) {
+ case IPMI_TIMEOUT_ERR:
+ reason = "timeout";
+ break;
+ default:
+ reason = "internal error";
+ break;
+ }
+
+ grub_printf("IPMI BT: %s in %s %s ", /* open-ended line */
+ reason, STATE2TXT, STATUS2TXT);
+
+ /* Per the IPMI spec, retries are based on the sequence number
+ known only to this module, so manage a restart here. */
+ (bt->error_retries)++;
+ if (bt->error_retries < bt->BT_CAP_retries) {
+ grub_printf("%d retries left\n",
+ bt->BT_CAP_retries - bt->error_retries);
+ bt->state = BT_STATE_RESTART;
+ return SI_SM_CALL_WITHOUT_DELAY;
+ }
+
+ grub_printf("failed %d retries, sending error response\n",
+ bt->BT_CAP_retries);
+ if (!bt->nonzero_status)
+ grub_printf("IPMI BT: stuck, try power cycle\n");
+
+ /* this is most likely during insmod */
+ else if (bt->seq <= (unsigned char)(bt->BT_CAP_retries & 0xFF)) {
+ grub_printf("IPMI: BT reset (takes 5 secs)\n");
+ bt->state = BT_STATE_RESET1;
+ return SI_SM_CALL_WITHOUT_DELAY;
+ }
+
+ /* Concoct a useful error message, set up the next state, and
+ be done with this sequence. */
+
+ bt->state = BT_STATE_IDLE;
+ switch (cCode) {
+ case IPMI_TIMEOUT_ERR:
+ if (status & BT_B_BUSY) {
+ cCode = IPMI_NODE_BUSY_ERR;
+ bt->state = BT_STATE_LONG_BUSY;
+ }
+ break;
+ default:
+ break;
+ }
+ force_result(bt, cCode);
+ return SI_SM_TRANSACTION_COMPLETE;
+}
+
+/* Check status and (usually) take action and change this state machine. */
+
+static enum si_sm_result bt_event(long time)
+{
+ unsigned char status, BT_CAP[8];
+ static enum bt_states last_printed = BT_STATE_PRINTME;
+ int i;
+
+ status = BT_STATUS;
+ bt_data.nonzero_status |= status;
+ if ((bt_debug & BT_DEBUG_STATES) && (bt_data.state != last_printed)) {
+ grub_printf("BT: %s %s TO=%ld - %ld \n",
+ STATE2TXT,
+ STATUS2TXT,
+ bt_data.timeout,
+ time);
+ last_printed = bt_data.state;
+ }
+
+ /* Commands that time out may still (eventually) provide a response.
+ This stale response will get in the way of a new response so remove
+ it if possible (hopefully during IDLE). Even if it comes up later
+ it will be rejected by its (now-forgotten) seq number. */
+
+ if ((bt_data.state < BT_STATE_WRITE_BYTES) && (status & BT_B2H_ATN)) {
+ drain_BMC2HOST(&bt_data);
+ BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
+ }
+
+ if ((bt_data.state != BT_STATE_IDLE) &&
+ (bt_data.state < BT_STATE_PRINTME)) { /* check timeout */
+ bt_data.timeout -= time;
+ if ((bt_data.timeout < 0) && (bt_data.state < BT_STATE_RESET1))
+ return error_recovery(&bt_data, status, IPMI_TIMEOUT_ERR);
+ }
+
+ switch (bt_data.state) {
+
+ /* Idle state first checks for asynchronous messages from another
+ channel, then does some opportunistic housekeeping. */
+
+ case BT_STATE_IDLE:
+ if (status & BT_SMS_ATN) {
+ BT_CONTROL(BT_SMS_ATN); /* clear it */
+ return SI_SM_ATTN;
+ }
+
+ if (status & BT_H_BUSY) /* clear a leftover H_BUSY */
+ BT_CONTROL(BT_H_BUSY);
+
+ /* Read BT capabilities if it hasn't been done yet */
+ if (!bt_data.BT_CAP_outreqs)
+ BT_STATE_CHANGE(BT_STATE_CAPABILITIES_BEGIN,
+ SI_SM_CALL_WITHOUT_DELAY);
+ bt_data.timeout = bt_data.BT_CAP_req2rsp;
+ BT_SI_SM_RETURN(SI_SM_IDLE);
+
+ case BT_STATE_XACTION_START:
+ if (status & (BT_B_BUSY | BT_H2B_ATN))
+ BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
+ if (BT_STATUS & BT_H_BUSY)
+ BT_CONTROL(BT_H_BUSY); /* force clear */
+ BT_STATE_CHANGE(BT_STATE_WRITE_BYTES,
+ SI_SM_CALL_WITHOUT_DELAY);
+
+ case BT_STATE_WRITE_BYTES:
+ if (status & BT_H_BUSY)
+ BT_CONTROL(BT_H_BUSY); /* clear */
+ BT_CONTROL(BT_CLR_WR_PTR);
+ write_all_bytes(&bt_data);
+ BT_CONTROL(BT_H2B_ATN); /* can clear too fast to catch */
+ BT_STATE_CHANGE(BT_STATE_WRITE_CONSUME,
+ SI_SM_CALL_WITHOUT_DELAY);
+
+ case BT_STATE_WRITE_CONSUME:
+ if (status & (BT_B_BUSY | BT_H2B_ATN))
+ BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
+ BT_STATE_CHANGE(BT_STATE_READ_WAIT,
+ SI_SM_CALL_WITHOUT_DELAY);
+
+ /* Spinning hard can suppress B2H_ATN and force a timeout */
+
+ case BT_STATE_READ_WAIT:
+ if (!(status & BT_B2H_ATN))
+ BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
+ BT_CONTROL(BT_H_BUSY); /* set */
+
+ /* Uncached, ordered writes should just proceeed serially but
+ some BMCs don't clear B2H_ATN with one hit. Fast-path a
+ workaround without too much penalty to the general case. */
+
+ BT_CONTROL(BT_B2H_ATN); /* clear it to ACK the BMC */
+ BT_STATE_CHANGE(BT_STATE_CLEAR_B2H,
+ SI_SM_CALL_WITHOUT_DELAY);
+
+ case BT_STATE_CLEAR_B2H:
+ if (status & BT_B2H_ATN) { /* keep hitting it */
+ BT_CONTROL(BT_B2H_ATN);
+ BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
+ }
+ BT_STATE_CHANGE(BT_STATE_READ_BYTES,
+ SI_SM_CALL_WITHOUT_DELAY);
+
+ case BT_STATE_READ_BYTES:
+ if (!(status & BT_H_BUSY)) /* check in case of retry */
+ BT_CONTROL(BT_H_BUSY);
+ BT_CONTROL(BT_CLR_RD_PTR); /* start of BMC2HOST buffer */
+ i = read_all_bytes(&bt_data); /* true == packet seq match */
+ BT_CONTROL(BT_H_BUSY); /* NOW clear */
+ if (!i) /* Not my message */
+ BT_STATE_CHANGE(BT_STATE_READ_WAIT,
+ SI_SM_CALL_WITHOUT_DELAY);
+ bt_data.state = bt_data.complete;
+ return bt_data.state == BT_STATE_IDLE ? /* where to next? */
+ SI_SM_TRANSACTION_COMPLETE : /* normal */
+ SI_SM_CALL_WITHOUT_DELAY; /* Startup magic */
+
+ case BT_STATE_LONG_BUSY: /* For example: after FW update */
+ if (!(status & BT_B_BUSY)) {
+ reset_flags(&bt_data); /* next state is now IDLE */
+ bt_init(bt_data.io);
+ }
+ return SI_SM_CALL_WITH_DELAY; /* No repeat printing */
+
+ case BT_STATE_RESET1:
+ reset_flags(&bt_data);
+ drain_BMC2HOST(&bt_data);
+ BT_STATE_CHANGE(BT_STATE_RESET2,
+ SI_SM_CALL_WITH_DELAY);
+
+ case BT_STATE_RESET2: /* Send a soft reset */
+ BT_CONTROL(BT_CLR_WR_PTR);
+ HOST2BMC(3); /* number of bytes following */
+ HOST2BMC(0x18); /* NetFn/LUN == Application, LUN 0 */
+ HOST2BMC(42); /* Sequence number */
+ HOST2BMC(3); /* Cmd == Soft reset */
+ BT_CONTROL(BT_H2B_ATN);
+ bt_data.timeout = BT_RESET_DELAY * 1000000;
+ BT_STATE_CHANGE(BT_STATE_RESET3,
+ SI_SM_CALL_WITH_DELAY);
+
+ case BT_STATE_RESET3: /* Hold off everything for a bit */
+ if (bt_data.timeout > 0)
+ return SI_SM_CALL_WITH_DELAY;
+ drain_BMC2HOST(&bt_data);
+ BT_STATE_CHANGE(BT_STATE_RESTART,
+ SI_SM_CALL_WITH_DELAY);
+
+ case BT_STATE_RESTART: /* don't reset retries or seq! */
+ bt_data.read_count = 0;
+ bt_data.nonzero_status = 0;
+ bt_data.timeout = bt_data.BT_CAP_req2rsp;
+ BT_STATE_CHANGE(BT_STATE_XACTION_START,
+ SI_SM_CALL_WITH_DELAY);
+
+ /* Get BT Capabilities, using timing of upper level state machine.
+ Set outreqs to prevent infinite loop on timeout. */
+ case BT_STATE_CAPABILITIES_BEGIN:
+ bt_data.BT_CAP_outreqs = 1;
+ {
+ unsigned char GetBT_CAP[] = { 0x18, 0x36 };
+ bt_data.state = BT_STATE_IDLE;
+ bt_start_transaction(GetBT_CAP, sizeof(GetBT_CAP));
+ }
+ bt_data.complete = BT_STATE_CAPABILITIES_END;
+ BT_STATE_CHANGE(BT_STATE_XACTION_START,
+ SI_SM_CALL_WITH_DELAY);
+
+ case BT_STATE_CAPABILITIES_END:
+ i = bt_get_result(BT_CAP, sizeof(BT_CAP));
+ bt_init(bt_data.io);
+ if ((i == 8) && !BT_CAP[2]) {
+ bt_data.BT_CAP_outreqs = BT_CAP[3];
+ bt_data.BT_CAP_req2rsp = BT_CAP[6] * 1000000;
+ bt_data.BT_CAP_retries = BT_CAP[7];
+ } else
+ grub_printf("IPMI BT: using default values\n");
+ if (!bt_data.BT_CAP_outreqs)
+ bt_data.BT_CAP_outreqs = 1;
+ grub_printf("IPMI BT: req2rsp=%ld secs retries=%d\n",
+ bt_data.BT_CAP_req2rsp / 1000000L,
bt_data.BT_CAP_retries);
+ bt_data.timeout = bt_data.BT_CAP_req2rsp;
+ return SI_SM_CALL_WITHOUT_DELAY;
+
+ default: /* should never occur */
+ return error_recovery(&bt_data, status, IPMI_ERR_UNSPECIFIED);
+ }
+ return SI_SM_CALL_WITH_DELAY;
+}
+
+static int bt_detect(void)
+{
+ /* It's impossible for the BT status and interrupt registers to be
+ all 1's, (assuming a properly functioning, self-initialized BMC)
+ but that's what you get from reading a bogus address, so we
+ test that first. The calling routine uses negative logic. */
+
+ if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF))
+ return 1;
+ reset_flags(&bt_data);
+ return 0;
+}
+
+static void bt_cleanup(void)
+{
+}
+
+struct si_sm_handler bt_smi_handler =
+{
+ .init = bt_init,
+ .start_transaction = bt_start_transaction,
+ .get_result = bt_get_result,
+ .event = bt_event,
+ .detect = bt_detect,
+ .cleanup = bt_cleanup,
+};
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/ipmi_kcs_sm.c grub-0.97.ipmi/stage2/ipmi_kcs_sm.c
--- grub-0.97/stage2/ipmi_kcs_sm.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_kcs_sm.c 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,517 @@
+/*
+ * ipmi_kcs_sm.c
+ *
+ * State machine for handling IPMI KCS interfaces.
+ *
+ * Author: MontaVista Software, Inc.
+ * Corey Minyard <address@hidden>
+ * address@hidden
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This state machine is taken from the state machine in the IPMI spec,
+ * pretty much verbatim. If you have questions about the states, see
+ * that document.
+ */
+
+#include "ipmi_msgdefs.h" /* for completion codes */
+#include "ipmi_si_sm.h"
+#include "cgl_config.h"
+#include "shared.h"
+
+/* kcs_debug is a bit-field
+ * KCS_DEBUG_ENABLE - turned on for now
+ * KCS_DEBUG_MSG - commands and their responses
+ * KCS_DEBUG_STATES - state machine
+ */
+#define KCS_DEBUG_STATES 4
+#define KCS_DEBUG_MSG 2
+#define KCS_DEBUG_ENABLE 1
+
+static int kcs_debug;
+
+/* The states the KCS driver may be in. */
+enum kcs_states {
+ KCS_IDLE, /* The KCS interface is currently
+ doing nothing. */
+ KCS_START_OP, /* We are starting an operation. The
+ data is in the output buffer, but
+ nothing has been done to the
+ interface yet. This was added to
+ the state machine in the spec to
+ wait for the initial IBF. */
+ KCS_WAIT_WRITE_START,/* We have written a write cmd to the
+ interface. */
+ KCS_WAIT_WRITE, /* We are writing bytes to the
+ interface. */
+ KCS_WAIT_WRITE_END, /* We have written the write end cmd
+ to the interface, and still need to
+ write the last byte. */
+ KCS_WAIT_READ, /* We are waiting to read data from
+ the interface. */
+ KCS_ERROR0, /* State to transition to the error
+ handler, this was added to the
+ state machine in the spec to be
+ sure IBF was there. */
+ KCS_ERROR1, /* First stage error handler, wait for
+ the interface to respond. */
+ KCS_ERROR2, /* The abort cmd has been written,
+ wait for the interface to
+ respond. */
+ KCS_ERROR3, /* We wrote some data to the
+ interface, wait for it to switch to
+ read mode. */
+ KCS_HOSED /* The hardware failed to follow the
+ state machine. */
+};
+
+#define MAX_KCS_READ_SIZE IPMI_MAX_MSG_LENGTH
+#define MAX_KCS_WRITE_SIZE IPMI_MAX_MSG_LENGTH
+
+/* Timeouts in microseconds. */
+#define IBF_RETRY_TIMEOUT 1000000
+#define OBF_RETRY_TIMEOUT 1000000
+#define MAX_ERROR_RETRIES 10
+#ifndef HZ
+#define HZ 100
+#endif
+#define ERROR0_OBF_WAIT_JIFFIES (2*HZ)
+
+struct si_sm_data
+{
+ enum kcs_states state;
+ struct si_sm_io *io;
+ unsigned char write_data[MAX_KCS_WRITE_SIZE];
+ int write_pos;
+ int write_count;
+ int orig_write_count;
+ unsigned char read_data[MAX_KCS_READ_SIZE];
+ int read_pos;
+ int truncated;
+
+ unsigned int error_retries;
+ long ibf_timeout;
+ long obf_timeout;
+ unsigned long error0_timeout;
+};
+
+struct si_sm_data kcs_data;
+
+static void init_kcs(struct si_sm_io *io)
+{
+ kcs_data.state = KCS_IDLE;
+ kcs_data.io = io;
+ kcs_data.write_pos = 0;
+ kcs_data.write_count = 0;
+ kcs_data.orig_write_count = 0;
+ kcs_data.read_pos = 0;
+ kcs_data.error_retries = 0;
+ kcs_data.truncated = 0;
+ kcs_data.ibf_timeout = IBF_RETRY_TIMEOUT;
+ kcs_data.obf_timeout = OBF_RETRY_TIMEOUT;
+
+ if (cgl_is_debug_mode())
+ kcs_debug = 7;
+}
+
+static inline unsigned char read_status(struct si_sm_data *kcs)
+{
+ return kcs->io->inputb(kcs->io, 1);
+}
+
+static inline unsigned char read_data(struct si_sm_data *kcs)
+{
+ return kcs->io->inputb(kcs->io, 0);
+}
+
+static inline void write_cmd(struct si_sm_data *kcs, unsigned char data)
+{
+ kcs->io->outputb(kcs->io, 1, data);
+}
+
+static inline void write_data(struct si_sm_data *kcs, unsigned char data)
+{
+ kcs->io->outputb(kcs->io, 0, data);
+}
+
+/* Control codes. */
+#define KCS_GET_STATUS_ABORT 0x60
+#define KCS_WRITE_START 0x61
+#define KCS_WRITE_END 0x62
+#define KCS_READ_BYTE 0x68
+
+/* Status bits. */
+#define GET_STATUS_STATE(status) (((status) >> 6) & 0x03)
+#define KCS_IDLE_STATE 0
+#define KCS_READ_STATE 1
+#define KCS_WRITE_STATE 2
+#define KCS_ERROR_STATE 3
+#define GET_STATUS_ATN(status) ((status) & 0x04)
+#define GET_STATUS_IBF(status) ((status) & 0x02)
+#define GET_STATUS_OBF(status) ((status) & 0x01)
+
+
+static inline void write_next_byte(struct si_sm_data *kcs)
+{
+ write_data(kcs, kcs->write_data[kcs->write_pos]);
+ (kcs->write_pos)++;
+ (kcs->write_count)--;
+}
+
+static inline void start_error_recovery(struct si_sm_data *kcs, char *reason)
+{
+ (kcs->error_retries)++;
+ if (kcs->error_retries > MAX_ERROR_RETRIES) {
+ if (kcs_debug & KCS_DEBUG_ENABLE)
+ grub_printf("ipmi_kcs_sm: kcs hosed: %s\n", reason);
+ kcs->state = KCS_HOSED;
+ } else {
+ int time1;
+ while ((time1 = getrtsecs()) == 0xFF);
+ kcs->error0_timeout = time1 + ERROR0_OBF_WAIT_JIFFIES;
+ kcs->state = KCS_ERROR0;
+ }
+}
+
+static inline void read_next_byte(struct si_sm_data *kcs)
+{
+ if (kcs->read_pos >= MAX_KCS_READ_SIZE) {
+ /* Throw the data away and mark it truncated. */
+ read_data(kcs);
+ kcs->truncated = 1;
+ } else {
+ kcs->read_data[kcs->read_pos] = read_data(kcs);
+ (kcs->read_pos)++;
+ }
+ write_data(kcs, KCS_READ_BYTE);
+}
+
+static inline int check_ibf(struct si_sm_data *kcs, unsigned char status,
+ long time)
+{
+ if (GET_STATUS_IBF(status)) {
+ kcs->ibf_timeout -= time;
+ if (kcs->ibf_timeout < 0) {
+ start_error_recovery(kcs, "IBF not ready in time");
+ kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+ return 1;
+ }
+ return 0;
+ }
+ kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+ return 1;
+}
+
+static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
+ long time)
+{
+ if (!GET_STATUS_OBF(status)) {
+ kcs->obf_timeout -= time;
+ if (kcs->obf_timeout < 0) {
+ start_error_recovery(kcs, "OBF not ready in time");
+ return 1;
+ }
+ return 0;
+ }
+ kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+ return 1;
+}
+
+static void clear_obf(struct si_sm_data *kcs, unsigned char status)
+{
+ if (GET_STATUS_OBF(status))
+ read_data(kcs);
+}
+
+static void restart_kcs_transaction(struct si_sm_data *kcs)
+{
+ kcs->write_count = kcs->orig_write_count;
+ kcs->write_pos = 0;
+ kcs->read_pos = 0;
+ kcs->state = KCS_WAIT_WRITE_START;
+ kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+ kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+ write_cmd(kcs, KCS_WRITE_START);
+}
+
+static int start_kcs_transaction(unsigned char *data, unsigned int size)
+{
+ unsigned int i;
+
+ if (size < 2)
+ return IPMI_REQ_LEN_INVALID_ERR;
+ if (size > MAX_KCS_WRITE_SIZE)
+ return IPMI_REQ_LEN_EXCEEDED_ERR;
+
+ if ((kcs_data.state != KCS_IDLE) && (kcs_data.state != KCS_HOSED))
+ return IPMI_NOT_IN_MY_STATE_ERR;
+
+ if (kcs_debug & KCS_DEBUG_MSG) {
+ grub_printf("start_kcs_transaction -");
+ for (i = 0; i < size; i ++) {
+ grub_printf(" %x", (unsigned char) (data [i]));
+ }
+ grub_printf ("\n");
+ }
+ kcs_data.error_retries = 0;
+ grub_memcpy(kcs_data.write_data, data, size);
+ kcs_data.write_count = size;
+ kcs_data.orig_write_count = size;
+ kcs_data.write_pos = 0;
+ kcs_data.read_pos = 0;
+ kcs_data.state = KCS_START_OP;
+ kcs_data.ibf_timeout = IBF_RETRY_TIMEOUT;
+ kcs_data.obf_timeout = OBF_RETRY_TIMEOUT;
+ return 0;
+}
+
+static int get_kcs_result(unsigned char *data, unsigned int length)
+{
+ if (length < kcs_data.read_pos) {
+ kcs_data.read_pos = length;
+ kcs_data.truncated = 1;
+ }
+
+ grub_memcpy(data, kcs_data.read_data, kcs_data.read_pos);
+
+ if ((length >= 3) && (kcs_data.read_pos < 3)) {
+ /* Guarantee that we return at least 3 bytes, with an
+ error in the third byte if it is too short. */
+ data[2] = IPMI_ERR_UNSPECIFIED;
+ kcs_data.read_pos = 3;
+ }
+ if (kcs_data.truncated) {
+ /* Report a truncated error. We might overwrite
+ another error, but that's too bad, the user needs
+ to know it was truncated. */
+ data[2] = IPMI_ERR_MSG_TRUNCATED;
+ kcs_data.truncated = 0;
+ }
+
+ return kcs_data.read_pos;
+}
+
+/* This implements the state machine defined in the IPMI manual, see
+ that for details on how this works. Divide that flowchart into
+ sections delimited by "Wait for IBF" and this will become clear. */
+static enum si_sm_result kcs_event(long time)
+{
+ unsigned char status;
+ unsigned char state;
+
+ status = read_status(&kcs_data);
+
+ if (kcs_debug & KCS_DEBUG_STATES)
+ grub_printf("KCS: State = %d, %x\n", kcs_data.state, status);
+
+ /* All states wait for ibf, so just do it here. */
+ if (!check_ibf(&kcs_data, status, time))
+ return SI_SM_CALL_WITH_DELAY;
+
+ /* Just about everything looks at the KCS state, so grab that, too. */
+ state = GET_STATUS_STATE(status);
+
+ switch (kcs_data.state) {
+ case KCS_IDLE:
+ /* If there's and interrupt source, turn it off. */
+ clear_obf(&kcs_data, status);
+
+ if (GET_STATUS_ATN(status))
+ return SI_SM_ATTN;
+ else
+ return SI_SM_IDLE;
+
+ case KCS_START_OP:
+ if (state != KCS_IDLE) {
+ start_error_recovery(&kcs_data, "State machine not idle at
start");
+ break;
+ }
+
+ clear_obf(&kcs_data, status);
+ write_cmd(&kcs_data, KCS_WRITE_START);
+ kcs_data.state = KCS_WAIT_WRITE_START;
+ break;
+
+ case KCS_WAIT_WRITE_START:
+ if (state != KCS_WRITE_STATE) {
+ start_error_recovery(&kcs_data,
+ "Not in write state at write start");
+ break;
+ }
+ read_data(&kcs_data);
+ if (kcs_data.write_count == 1) {
+ write_cmd(&kcs_data, KCS_WRITE_END);
+ kcs_data.state = KCS_WAIT_WRITE_END;
+ } else {
+ write_next_byte(&kcs_data);
+ kcs_data.state = KCS_WAIT_WRITE;
+ }
+ break;
+
+ case KCS_WAIT_WRITE:
+ if (state != KCS_WRITE_STATE) {
+ start_error_recovery(&kcs_data,
+ "Not in write state for write");
+ break;
+ }
+ clear_obf(&kcs_data, status);
+ if (kcs_data.write_count == 1) {
+ write_cmd(&kcs_data, KCS_WRITE_END);
+ kcs_data.state = KCS_WAIT_WRITE_END;
+ } else {
+ write_next_byte(&kcs_data);
+ }
+ break;
+
+ case KCS_WAIT_WRITE_END:
+ if (state != KCS_WRITE_STATE) {
+ start_error_recovery(&kcs_data,
+ "Not in write state for write end");
+ break;
+ }
+ clear_obf(&kcs_data, status);
+ write_next_byte(&kcs_data);
+ kcs_data.state = KCS_WAIT_READ;
+ break;
+
+ case KCS_WAIT_READ:
+ if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) {
+ start_error_recovery(&kcs_data,
+ "Not in read or idle in read state");
+ break;
+ }
+
+ if (state == KCS_READ_STATE) {
+ if (!check_obf(&kcs_data, status, time))
+ return SI_SM_CALL_WITH_DELAY;
+ read_next_byte(&kcs_data);
+ } else {
+ /* We don't implement this exactly like the state
+ machine in the spec. Some broken hardware
+ does not write the final dummy byte to the
+ read register. Thus obf will never go high
+ here. We just go straight to idle, and we
+ handle clearing out obf in idle state if it
+ happens to come in. */
+ clear_obf(&kcs_data, status);
+ kcs_data.orig_write_count = 0;
+ kcs_data.state = KCS_IDLE;
+ return SI_SM_TRANSACTION_COMPLETE;
+ }
+ break;
+
+ case KCS_ERROR0:
+ clear_obf(&kcs_data, status);
+ status = read_status(&kcs_data);
+ if (GET_STATUS_OBF(status)) { /* controller isn't responding */
+ int time1;
+ while ((time1 = getrtsecs()) == 0xFF);
+ if (time1 < kcs_data.error0_timeout)
+ return SI_SM_CALL_WITH_TICK_DELAY;
+ }
+ write_cmd(&kcs_data, KCS_GET_STATUS_ABORT);
+ kcs_data.state = KCS_ERROR1;
+ break;
+
+ case KCS_ERROR1:
+ clear_obf(&kcs_data, status);
+ write_data(&kcs_data, 0);
+ kcs_data.state = KCS_ERROR2;
+ break;
+
+ case KCS_ERROR2:
+ if (state != KCS_READ_STATE) {
+ start_error_recovery(&kcs_data,
+ "Not in read state for error2");
+ break;
+ }
+ if (!check_obf(&kcs_data, status, time))
+ return SI_SM_CALL_WITH_DELAY;
+
+ clear_obf(&kcs_data, status);
+ write_data(&kcs_data, KCS_READ_BYTE);
+ kcs_data.state = KCS_ERROR3;
+ break;
+
+ case KCS_ERROR3:
+ if (state != KCS_IDLE_STATE) {
+ start_error_recovery(&kcs_data,
+ "Not in idle state for error3");
+ break;
+ }
+
+ if (!check_obf(&kcs_data, status, time))
+ return SI_SM_CALL_WITH_DELAY;
+
+ clear_obf(&kcs_data, status);
+ if (kcs_data.orig_write_count) {
+ restart_kcs_transaction(&kcs_data);
+ } else {
+ kcs_data.state = KCS_IDLE;
+ return SI_SM_TRANSACTION_COMPLETE;
+ }
+ break;
+
+ case KCS_HOSED:
+ break;
+ }
+
+ if (kcs_data.state == KCS_HOSED) {
+ init_kcs(kcs_data.io);
+ return SI_SM_HOSED;
+ }
+
+ return SI_SM_CALL_WITHOUT_DELAY;
+}
+
+static int kcs_detect(void)
+{
+ /* It's impossible for the KCS status register to be all 1's,
+ (assuming a properly functioning, self-initialized BMC)
+ but that's what you get from reading a bogus address, so we
+ test that first. */
+ if (read_status(&kcs_data) == 0xff)
+ return 1;
+
+ return 0;
+}
+
+static void kcs_cleanup(void)
+{
+}
+
+struct si_sm_handler kcs_smi_handler =
+{
+ .init = init_kcs,
+ .start_transaction = start_kcs_transaction,
+ .get_result = get_kcs_result,
+ .event = kcs_event,
+ .detect = kcs_detect,
+ .cleanup = kcs_cleanup,
+};
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/ipmi_msgdefs.h grub-0.97.ipmi/stage2/ipmi_msgdefs.h
--- grub-0.97/stage2/ipmi_msgdefs.h 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_msgdefs.h 2008-09-11 20:23:13.000000000
+0900
@@ -0,0 +1,113 @@
+/*
+ * ipmi_smi.h
+ *
+ * MontaVista IPMI system management interface
+ *
+ * Author: MontaVista Software, Inc.
+ * Corey Minyard <address@hidden>
+ * address@hidden
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __IPMI_MSGDEFS_H
+#define __IPMI_MSGDEFS_H
+
+/* Various definitions for IPMI messages used by almost everything in
+ the IPMI stack. */
+
+/* NetFNs and commands used inside the IPMI stack. */
+
+#define IPMI_NETFN_SENSOR_EVENT_REQUEST 0x04
+#define IPMI_NETFN_SENSOR_EVENT_RESPONSE 0x05
+#define IPMI_GET_EVENT_RECEIVER_CMD 0x01
+
+#define IPMI_NETFN_APP_REQUEST 0x06
+#define IPMI_NETFN_APP_RESPONSE 0x07
+#define IPMI_GET_DEVICE_ID_CMD 0x01
+#define IPMI_COLD_RESET_CMD 0x02
+#define IPMI_WARM_RESET_CMD 0x03
+#define IPMI_CLEAR_MSG_FLAGS_CMD 0x30
+#define IPMI_GET_DEVICE_GUID_CMD 0x08
+#define IPMI_GET_MSG_FLAGS_CMD 0x31
+#define IPMI_SEND_MSG_CMD 0x34
+#define IPMI_GET_MSG_CMD 0x33
+#define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e
+#define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f
+#define IPMI_READ_EVENT_MSG_BUFFER_CMD 0x35
+#define IPMI_GET_CHANNEL_INFO_CMD 0x42
+
+#define IPMI_NETFN_STORAGE_REQUEST 0x0a
+#define IPMI_NETFN_STORAGE_RESPONSE 0x0b
+#define IPMI_ADD_SEL_ENTRY_CMD 0x44
+
+#define IPMI_NETFN_FIRMWARE_REQUEST 0x08
+#define IPMI_NETFN_FIRMWARE_RESPONSE 0x09
+
+/* The default slave address */
+#define IPMI_BMC_SLAVE_ADDR 0x20
+
+/* The BT interface on high-end HP systems supports up to 255 bytes in
+ * one transfer. Its "virtual" BMC supports some commands that are longer
+ * than 128 bytes. Use the full 256, plus NetFn/LUN, Cmd, cCode, plus
+ * some overhead; it's not worth the effort to dynamically size this based
+ * on the results of the "Get BT Capabilities" command. */
+#define IPMI_MAX_MSG_LENGTH 272 /* multiple of 16 */
+
+#define IPMI_CC_NO_ERROR 0x00
+#define IPMI_NODE_BUSY_ERR 0xc0
+#define IPMI_INVALID_COMMAND_ERR 0xc1
+#define IPMI_TIMEOUT_ERR 0xc3
+#define IPMI_ERR_MSG_TRUNCATED 0xc6
+#define IPMI_REQ_LEN_INVALID_ERR 0xc7
+#define IPMI_REQ_LEN_EXCEEDED_ERR 0xc8
+#define IPMI_NOT_IN_MY_STATE_ERR 0xd5 /* IPMI 2.0 */
+#define IPMI_LOST_ARBITRATION_ERR 0x81
+#define IPMI_BUS_ERR 0x82
+#define IPMI_NAK_ON_WRITE_ERR 0x83
+#define IPMI_ERR_UNSPECIFIED 0xff
+
+#define IPMI_CHANNEL_PROTOCOL_IPMB 1
+#define IPMI_CHANNEL_PROTOCOL_ICMB 2
+#define IPMI_CHANNEL_PROTOCOL_SMBUS 4
+#define IPMI_CHANNEL_PROTOCOL_KCS 5
+#define IPMI_CHANNEL_PROTOCOL_SMIC 6
+#define IPMI_CHANNEL_PROTOCOL_BT10 7
+#define IPMI_CHANNEL_PROTOCOL_BT15 8
+#define IPMI_CHANNEL_PROTOCOL_TMODE 9
+
+#define IPMI_CHANNEL_MEDIUM_IPMB 1
+#define IPMI_CHANNEL_MEDIUM_ICMB10 2
+#define IPMI_CHANNEL_MEDIUM_ICMB09 3
+#define IPMI_CHANNEL_MEDIUM_8023LAN 4
+#define IPMI_CHANNEL_MEDIUM_ASYNC 5
+#define IPMI_CHANNEL_MEDIUM_OTHER_LAN 6
+#define IPMI_CHANNEL_MEDIUM_PCI_SMBUS 7
+#define IPMI_CHANNEL_MEDIUM_SMBUS1 8
+#define IPMI_CHANNEL_MEDIUM_SMBUS2 9
+#define IPMI_CHANNEL_MEDIUM_USB1 10
+#define IPMI_CHANNEL_MEDIUM_USB2 11
+#define IPMI_CHANNEL_MEDIUM_SYSINTF 12
+
+#endif /* __IPMI_MSGDEFS_H */
diff -Nurp grub-0.97/stage2/ipmi_si_sm.h grub-0.97.ipmi/stage2/ipmi_si_sm.h
--- grub-0.97/stage2/ipmi_si_sm.h 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_si_sm.h 2008-09-11 20:23:13.000000000 +0900
@@ -0,0 +1,100 @@
+/*
+ * ipmi_si_sm.h
+ *
+ * State machine interface for low-level IPMI system management
+ * interface state machines. This code is the interface between
+ * the ipmi_smi code (that handles the policy of a KCS, SMIC, or
+ * BT interface) and the actual low-level state machine.
+ *
+ * Author: MontaVista Software, Inc.
+ * Corey Minyard <address@hidden>
+ * address@hidden
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __IPMI_SI_SM_H
+#define __IPMI_SI_SM_H
+
+#include "ipmi_si_sm_io.h"
+
+/* This is defined by the state machines themselves, it is an opaque
+ data type for them to use. */
+struct si_sm_data;
+
+/* Results of SMI events. */
+enum si_sm_result
+{
+ SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
+ SI_SM_CALL_WITH_DELAY, /* Delay some before calling again. */
+ SI_SM_CALL_WITH_TICK_DELAY, /* Delay at least 1 tick before calling
again. */
+ SI_SM_TRANSACTION_COMPLETE, /* A transaction is finished. */
+ SI_SM_IDLE, /* The SM is in idle state. */
+ SI_SM_HOSED, /* The hardware violated the state machine. */
+ SI_SM_ATTN /* The hardware is asserting attn and the
+ state machine is idle. */
+};
+
+/* Handler for the SMI state machine. */
+struct si_sm_handler
+{
+ /* Put the version number of the state machine here so the
+ upper layer can print it. */
+ char *version;
+
+ void (*init)(struct si_sm_io *io);
+
+ /* Start a new transaction in the state machine. This will
+ return -2 if the state machine is not idle, -1 if the size
+ is invalid (to large or too small), or 0 if the transaction
+ is successfully completed. */
+ int (*start_transaction)(unsigned char *data, unsigned int size);
+
+ /* Return the results after the transaction. This will return
+ -1 if the buffer is too small, zero if no transaction is
+ present, or the actual length of the result data. */
+ int (*get_result)(unsigned char *data, unsigned int length);
+
+ /* Call this periodically (for a polled interface) or upon
+ receiving an interrupt (for a interrupt-driven interface).
+ If interrupt driven, you should probably poll this
+ periodically when not in idle state. This should be called
+ with the time that passed since the last call, if it is
+ significant. Time is in microseconds. */
+ enum si_sm_result (*event)(long time);
+
+ /* Attempt to detect an SMI. Returns 0 on success or nonzero
+ on failure. */
+ int (*detect)(void);
+
+ /* The interface is shutting down, so clean it up. */
+ void (*cleanup)(void);
+};
+
+/* Current state machines that we can use. */
+extern struct si_sm_handler kcs_smi_handler;
+extern struct si_sm_handler smic_smi_handler;
+extern struct si_sm_handler bt_smi_handler;
+
+#endif /* __IPMI_SI_SM_H */
diff -Nurp grub-0.97/stage2/ipmi_si_sm_io.c
grub-0.97.ipmi/stage2/ipmi_si_sm_io.c
--- grub-0.97/stage2/ipmi_si_sm_io.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_si_sm_io.c 2008-09-11 20:23:13.000000000
+0900
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+/*
+ * based on ipmi_si_intf.c.
+ *
+ */
+
+#include "ipmi_si_sm_io.h"
+#include "linux-asm-io.h"
+#include "shared.h"
+
+static unsigned char
+port_inb(struct si_sm_io *io, unsigned int offset)
+{
+ unsigned int addr = io->addr_data;
+
+ return inb(addr + (offset * io->reg_spacing));
+}
+
+static void
+port_outb(struct si_sm_io *io, unsigned int offset, unsigned char b)
+{
+ unsigned int addr = io->addr_data;
+
+ outb(b, addr + (offset * io->reg_spacing));
+}
+
+static unsigned char
+port_inw(struct si_sm_io *io, unsigned int offset)
+{
+ unsigned int addr = io->addr_data;
+
+ return (inw(addr + (offset * io->reg_spacing)) >> io->reg_shift) & 0xff;
+}
+
+static void
+port_outw(struct si_sm_io *io, unsigned int offset, unsigned char b)
+{
+ unsigned int addr = io->addr_data;
+
+ outw(b << io->reg_shift, addr + (offset * io->reg_spacing));
+}
+
+static unsigned char
+port_inl(struct si_sm_io *io, unsigned int offset)
+{
+ unsigned int addr = io->addr_data;
+
+ return (inl(addr + (offset * io->reg_spacing)) >> io->reg_shift) & 0xff;
+}
+
+static void
+port_outl(struct si_sm_io *io, unsigned int offset, unsigned char b)
+{
+ unsigned int addr = io->addr_data;
+
+ outl(b << io->reg_shift, addr+(offset * io->reg_spacing));
+}
+
+static int
+port_setup(struct si_sm_io *io)
+{
+ unsigned int addr = io->addr_data;
+
+ if (!addr)
+ return -1;
+
+ /* Figure out the actual inb/inw/inl/etc routine to use based
+ upon the register size-> */
+ switch (io->reg_size) {
+ case 1:
+ io->inputb = port_inb;
+ io->outputb = port_outb;
+ break;
+ case 2:
+ io->inputb = port_inw;
+ io->outputb = port_outw;
+ break;
+ case 4:
+ io->inputb = port_inl;
+ io->outputb = port_outl;
+ break;
+ default:
+ grub_printf("ipmi_si: Invalid register size: %d\n",
+ io->reg_size);
+ return -1;
+ }
+
+ return 0;
+}
+
+static unsigned char
+mem_inb(struct si_sm_io *io, unsigned int offset)
+{
+ return readb(((char *)io->addr + (offset * (int)io->reg_spacing)));
+}
+
+static void
+mem_outb(struct si_sm_io *io, unsigned int offset, unsigned char b)
+{
+ writeb(b, ((char *)(io->addr)+(offset * io->reg_spacing)));
+}
+
+static unsigned char
+mem_inw(struct si_sm_io *io, unsigned int offset)
+{
+ return (readw(((char *)(io->addr)+(offset * io->reg_spacing))) >>
io->reg_shift)
+ & 0xff;
+}
+
+static void
+mem_outw(struct si_sm_io *io, unsigned int offset, unsigned char b)
+{
+ writeb(b << io->reg_shift, ((char *)(io->addr)+(offset *
io->reg_spacing)));
+}
+
+static unsigned char
+mem_inl(struct si_sm_io *io, unsigned int offset)
+{
+ return (readl(((char *)(io->addr)+(offset * io->reg_spacing))) >>
io->reg_shift)
+ & 0xff;
+}
+
+static void
+mem_outl(struct si_sm_io *io, unsigned int offset, unsigned char b)
+{
+ writel(b << io->reg_shift, ((char *)(io->addr)+(offset *
io->reg_spacing)));
+}
+
+static int
+mem_setup(struct si_sm_io *io)
+{
+ unsigned long addr = io->addr_data;
+
+ if (!addr)
+ return -1;
+
+ /* Figure out the actual readb/readw/readl/etc routine to use based
+ upon the register size. */
+ switch (io->reg_size) {
+ case 1:
+ io->inputb = mem_inb;
+ io->outputb = mem_outb;
+ break;
+ case 2:
+ io->inputb = mem_inw;
+ io->outputb = mem_outw;
+ break;
+ case 4:
+ io->inputb = mem_inl;
+ io->outputb = mem_outl;
+ break;
+ default:
+ grub_printf("ipmi_si: Invalid register size: %d\n",
+ io->reg_size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ipmi_si_sm_io_setup(struct si_sm_io *io, si_addr_type addr_type, unsigned long
addr,
+ int reg_spacing, int reg_size, int reg_shift)
+{
+ if (!io)
+ return -1;
+
+ io->addr = NULL;
+ io->addr_type = addr_type;
+ io->reg_spacing = reg_spacing;
+ io->reg_size = reg_size;
+ io->reg_shift = reg_shift;
+ io->addr_data = addr;
+
+ switch (addr_type) {
+ case SI_ADDR_TYPE_IO:
+ if(port_setup(io))
+ return -1;
+ break;
+ case SI_ADDR_TYPE_MEMORY:
+ if(mem_setup(io))
+ return -1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int
+ipmi_si_sm_io_cleanup(struct si_sm_io *io)
+{
+ return 0;
+}
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/ipmi_si_sm_io.h
grub-0.97.ipmi/stage2/ipmi_si_sm_io.h
--- grub-0.97/stage2/ipmi_si_sm_io.h 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_si_sm_io.h 2008-09-11 20:23:13.000000000
+0900
@@ -0,0 +1,72 @@
+/*
+ * ipmi_si_sm.h
+ *
+ * State machine interface for low-level IPMI system management
+ * interface state machines. This code is the interface between
+ * the ipmi_smi code (that handles the policy of a KCS, SMIC, or
+ * BT interface) and the actual low-level state machine.
+ *
+ * Author: MontaVista Software, Inc.
+ * Corey Minyard <address@hidden>
+ * address@hidden
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __IPMI_SI_SM_IO_H
+#define __IPMI_SI_SM_IO_H
+
+typedef enum {
+ SI_ADDR_TYPE_IO,
+ SI_ADDR_TYPE_MEMORY
+} si_addr_type;
+
+/* The structure for doing I/O in the state machine. The state
+ machine doesn't have the actual I/O routines, they are done through
+ this interface. */
+struct si_sm_io
+{
+ unsigned char (*inputb)(struct si_sm_io *io, unsigned int offset);
+ void (*outputb)(struct si_sm_io *io,
+ unsigned int offset,
+ unsigned char b);
+
+ /* Generic info used by the actual handling routines, the
+ state machine shouldn't touch these. */
+ void *addr;
+ int reg_spacing;
+ int reg_size;
+ int reg_shift;
+ si_addr_type addr_type;
+ long addr_data;
+};
+
+int ipmi_si_sm_io_setup (struct si_sm_io *io, si_addr_type addr_type,
unsigned long addr,
+ int reg_spacing, int reg_size, int reg_shift);
+int ipmi_si_sm_io_cleanup (struct si_sm_io *io);
+
+#endif /* __IPMI_SI_SM_IO_H */
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/ipmi_smic_sm.c grub-0.97.ipmi/stage2/ipmi_smic_sm.c
--- grub-0.97/stage2/ipmi_smic_sm.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_smic_sm.c 2008-09-11 20:23:13.000000000
+0900
@@ -0,0 +1,593 @@
+/*
+ * ipmi_smic_sm.c
+ *
+ * The state-machine driver for an IPMI SMIC driver
+ *
+ * It started as a copy of Corey Minyard's driver for the KSC interface
+ * and the kernel patch "mmcdev-patch-245" by HP
+ *
+ * modified by: Hannes Schulz <address@hidden>
+ * address@hidden
+ *
+ *
+ * Corey Minyard's driver for the KSC interface has the following
+ * copyright notice:
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ * the kernel patch "mmcdev-patch-245" by HP has the following
+ * copyright notice:
+ * (c) Copyright 2001 Grant Grundler (c) Copyright
+ * 2001 Hewlett-Packard Company
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ipmi_msgdefs.h" /* for completion codes */
+#include "ipmi_si_sm.h"
+#include "cgl_config.h"
+#include "shared.h"
+
+/* smic_debug is a bit-field
+ * SMIC_DEBUG_ENABLE - turned on for now
+ * SMIC_DEBUG_MSG - commands and their responses
+ * SMIC_DEBUG_STATES - state machine
+ */
+#define SMIC_DEBUG_STATES 4
+#define SMIC_DEBUG_MSG 2
+#define SMIC_DEBUG_ENABLE 1
+
+static int smic_debug = 1;
+
+enum smic_states {
+ SMIC_IDLE,
+ SMIC_START_OP,
+ SMIC_OP_OK,
+ SMIC_WRITE_START,
+ SMIC_WRITE_NEXT,
+ SMIC_WRITE_END,
+ SMIC_WRITE2READ,
+ SMIC_READ_START,
+ SMIC_READ_NEXT,
+ SMIC_READ_END,
+ SMIC_HOSED
+};
+
+#define MAX_SMIC_READ_SIZE 80
+#define MAX_SMIC_WRITE_SIZE 80
+#define SMIC_MAX_ERROR_RETRIES 3
+
+/* Timeouts in microseconds. */
+#define SMIC_RETRY_TIMEOUT 2000000
+
+/* SMIC Flags Register Bits */
+#define SMIC_RX_DATA_READY 0x80
+#define SMIC_TX_DATA_READY 0x40
+/*
+ * SMIC_SMI and SMIC_EVM_DATA_AVAIL are only used by
+ * a few systems, and then only by Systems Management
+ * Interrupts, not by the OS. Always ignore these bits.
+ *
+ */
+#define SMIC_SMI 0x10
+#define SMIC_EVM_DATA_AVAIL 0x08
+#define SMIC_SMS_DATA_AVAIL 0x04
+#define SMIC_FLAG_BSY 0x01
+
+/* SMIC Error Codes */
+#define EC_NO_ERROR 0x00
+#define EC_ABORTED 0x01
+#define EC_ILLEGAL_CONTROL 0x02
+#define EC_NO_RESPONSE 0x03
+#define EC_ILLEGAL_COMMAND 0x04
+#define EC_BUFFER_FULL 0x05
+
+struct si_sm_data
+{
+ enum smic_states state;
+ struct si_sm_io *io;
+ unsigned char write_data[MAX_SMIC_WRITE_SIZE];
+ int write_pos;
+ int write_count;
+ int orig_write_count;
+ unsigned char read_data[MAX_SMIC_READ_SIZE];
+ int read_pos;
+ int truncated;
+ unsigned int error_retries;
+ long smic_timeout;
+};
+
+struct si_sm_data smic_data;
+
+static void init_smic (struct si_sm_io *io)
+{
+ smic_data.state = SMIC_IDLE;
+ smic_data.io = io;
+ smic_data.write_pos = 0;
+ smic_data.write_count = 0;
+ smic_data.orig_write_count = 0;
+ smic_data.read_pos = 0;
+ smic_data.error_retries = 0;
+ smic_data.truncated = 0;
+ smic_data.smic_timeout = SMIC_RETRY_TIMEOUT;
+
+ if (cgl_is_debug_mode())
+ smic_debug = 7;
+}
+
+static int start_smic_transaction(unsigned char *data, unsigned int size)
+{
+ unsigned int i;
+
+ if (size < 2)
+ return IPMI_REQ_LEN_INVALID_ERR;
+ if (size > MAX_SMIC_WRITE_SIZE)
+ return IPMI_REQ_LEN_EXCEEDED_ERR;
+
+ if ((smic_data.state != SMIC_IDLE) && (smic_data.state != SMIC_HOSED))
+ return IPMI_NOT_IN_MY_STATE_ERR;
+
+ if (smic_debug & SMIC_DEBUG_MSG) {
+ grub_printf("start_smic_transaction -");
+ for (i = 0; i < size; i ++) {
+ grub_printf (" %x", (unsigned char) (data [i]));
+ }
+ grub_printf ("\n");
+ }
+ smic_data.error_retries = 0;
+ grub_memcpy(smic_data.write_data, data, size);
+ smic_data.write_count = size;
+ smic_data.orig_write_count = size;
+ smic_data.write_pos = 0;
+ smic_data.read_pos = 0;
+ smic_data.state = SMIC_START_OP;
+ smic_data.smic_timeout = SMIC_RETRY_TIMEOUT;
+ return 0;
+}
+
+static int smic_get_result(unsigned char *data, unsigned int length)
+{
+ int i;
+
+ if (smic_debug & SMIC_DEBUG_MSG) {
+ grub_printf ("smic_get result -");
+ for (i = 0; i < smic_data.read_pos; i ++) {
+ grub_printf (" %x", (smic_data.read_data [i]));
+ }
+ grub_printf ("\n");
+ }
+ if (length < smic_data.read_pos) {
+ smic_data.read_pos = length;
+ smic_data.truncated = 1;
+ }
+ grub_memcpy(data, smic_data.read_data, smic_data.read_pos);
+
+ if ((length >= 3) && (smic_data.read_pos < 3)) {
+ data[2] = IPMI_ERR_UNSPECIFIED;
+ smic_data.read_pos = 3;
+ }
+ if (smic_data.truncated) {
+ data[2] = IPMI_ERR_MSG_TRUNCATED;
+ smic_data.truncated = 0;
+ }
+ return smic_data.read_pos;
+}
+
+static inline unsigned char read_smic_flags(struct si_sm_data *smic)
+{
+ return smic->io->inputb(smic->io, 2);
+}
+
+static inline unsigned char read_smic_status(struct si_sm_data *smic)
+{
+ return smic->io->inputb(smic->io, 1);
+}
+
+static inline unsigned char read_smic_data(struct si_sm_data *smic)
+{
+ return smic->io->inputb(smic->io, 0);
+}
+
+static inline void write_smic_flags(struct si_sm_data *smic,
+ unsigned char flags)
+{
+ smic->io->outputb(smic->io, 2, flags);
+}
+
+static inline void write_smic_control(struct si_sm_data *smic,
+ unsigned char control)
+{
+ smic->io->outputb(smic->io, 1, control);
+}
+
+static inline void write_si_sm_data (struct si_sm_data *smic,
+ unsigned char data)
+{
+ smic->io->outputb(smic->io, 0, data);
+}
+
+static inline void start_error_recovery(struct si_sm_data *smic, char *reason)
+{
+ (smic->error_retries)++;
+ if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) {
+ if (smic_debug & SMIC_DEBUG_ENABLE) {
+ grub_printf("ipmi_smic_drv: smic hosed: %s\n", reason);
+ }
+ smic->state = SMIC_HOSED;
+ } else {
+ smic->write_count = smic->orig_write_count;
+ smic->write_pos = 0;
+ smic->read_pos = 0;
+ smic->state = SMIC_START_OP;
+ smic->smic_timeout = SMIC_RETRY_TIMEOUT;
+ }
+}
+
+static inline void write_next_byte(struct si_sm_data *smic)
+{
+ write_si_sm_data(smic, smic->write_data[smic->write_pos]);
+ (smic->write_pos)++;
+ (smic->write_count)--;
+}
+
+static inline void read_next_byte (struct si_sm_data *smic)
+{
+ if (smic->read_pos >= MAX_SMIC_READ_SIZE) {
+ read_smic_data (smic);
+ smic->truncated = 1;
+ } else {
+ smic->read_data[smic->read_pos] = read_smic_data(smic);
+ (smic->read_pos)++;
+ }
+}
+
+/* SMIC Control/Status Code Components */
+#define SMIC_GET_STATUS 0x00 /* Control form's name */
+#define SMIC_READY 0x00 /* Status form's name */
+#define SMIC_WR_START 0x01 /* Unified Control/Status names... */
+#define SMIC_WR_NEXT 0x02
+#define SMIC_WR_END 0x03
+#define SMIC_RD_START 0x04
+#define SMIC_RD_NEXT 0x05
+#define SMIC_RD_END 0x06
+#define SMIC_CODE_MASK 0x0f
+
+#define SMIC_CONTROL 0x00
+#define SMIC_STATUS 0x80
+#define SMIC_CS_MASK 0x80
+
+#define SMIC_SMS 0x40
+#define SMIC_SMM 0x60
+#define SMIC_STREAM_MASK 0x60
+
+/* SMIC Control Codes */
+#define SMIC_CC_SMS_GET_STATUS (SMIC_CONTROL|SMIC_SMS|SMIC_GET_STATUS)
+#define SMIC_CC_SMS_WR_START (SMIC_CONTROL|SMIC_SMS|SMIC_WR_START)
+#define SMIC_CC_SMS_WR_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_WR_NEXT)
+#define SMIC_CC_SMS_WR_END (SMIC_CONTROL|SMIC_SMS|SMIC_WR_END)
+#define SMIC_CC_SMS_RD_START (SMIC_CONTROL|SMIC_SMS|SMIC_RD_START)
+#define SMIC_CC_SMS_RD_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_RD_NEXT)
+#define SMIC_CC_SMS_RD_END (SMIC_CONTROL|SMIC_SMS|SMIC_RD_END)
+
+#define SMIC_CC_SMM_GET_STATUS (SMIC_CONTROL|SMIC_SMM|SMIC_GET_STATUS)
+#define SMIC_CC_SMM_WR_START (SMIC_CONTROL|SMIC_SMM|SMIC_WR_START)
+#define SMIC_CC_SMM_WR_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_WR_NEXT)
+#define SMIC_CC_SMM_WR_END (SMIC_CONTROL|SMIC_SMM|SMIC_WR_END)
+#define SMIC_CC_SMM_RD_START (SMIC_CONTROL|SMIC_SMM|SMIC_RD_START)
+#define SMIC_CC_SMM_RD_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_RD_NEXT)
+#define SMIC_CC_SMM_RD_END (SMIC_CONTROL|SMIC_SMM|SMIC_RD_END)
+
+/* SMIC Status Codes */
+#define SMIC_SC_SMS_READY (SMIC_STATUS|SMIC_SMS|SMIC_READY)
+#define SMIC_SC_SMS_WR_START (SMIC_STATUS|SMIC_SMS|SMIC_WR_START)
+#define SMIC_SC_SMS_WR_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_WR_NEXT)
+#define SMIC_SC_SMS_WR_END (SMIC_STATUS|SMIC_SMS|SMIC_WR_END)
+#define SMIC_SC_SMS_RD_START (SMIC_STATUS|SMIC_SMS|SMIC_RD_START)
+#define SMIC_SC_SMS_RD_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_RD_NEXT)
+#define SMIC_SC_SMS_RD_END (SMIC_STATUS|SMIC_SMS|SMIC_RD_END)
+
+#define SMIC_SC_SMM_READY (SMIC_STATUS|SMIC_SMM|SMIC_READY)
+#define SMIC_SC_SMM_WR_START (SMIC_STATUS|SMIC_SMM|SMIC_WR_START)
+#define SMIC_SC_SMM_WR_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_WR_NEXT)
+#define SMIC_SC_SMM_WR_END (SMIC_STATUS|SMIC_SMM|SMIC_WR_END)
+#define SMIC_SC_SMM_RD_START (SMIC_STATUS|SMIC_SMM|SMIC_RD_START)
+#define SMIC_SC_SMM_RD_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_RD_NEXT)
+#define SMIC_SC_SMM_RD_END (SMIC_STATUS|SMIC_SMM|SMIC_RD_END)
+
+/* these are the control/status codes we actually use
+ SMIC_CC_SMS_GET_STATUS 0x40
+ SMIC_CC_SMS_WR_START 0x41
+ SMIC_CC_SMS_WR_NEXT 0x42
+ SMIC_CC_SMS_WR_END 0x43
+ SMIC_CC_SMS_RD_START 0x44
+ SMIC_CC_SMS_RD_NEXT 0x45
+ SMIC_CC_SMS_RD_END 0x46
+
+ SMIC_SC_SMS_READY 0xC0
+ SMIC_SC_SMS_WR_START 0xC1
+ SMIC_SC_SMS_WR_NEXT 0xC2
+ SMIC_SC_SMS_WR_END 0xC3
+ SMIC_SC_SMS_RD_START 0xC4
+ SMIC_SC_SMS_RD_NEXT 0xC5
+ SMIC_SC_SMS_RD_END 0xC6
+ */
+
+static enum si_sm_result smic_event (long time)
+{
+ unsigned char status;
+ unsigned char flags;
+ unsigned char data;
+
+ if (smic_data.state == SMIC_HOSED) {
+ init_smic(smic_data.io);
+ return SI_SM_HOSED;
+ }
+ if (smic_data.state != SMIC_IDLE) {
+ if (smic_debug & SMIC_DEBUG_STATES) {
+ grub_printf("smic_event - smic_data.smic_timeout = %ld,"
+ " time = %ld\n",
+ smic_data.smic_timeout, time);
+ }
+ /* FIXME: smic_event is sometimes called with time >
SMIC_RETRY_TIMEOUT */
+ if (time < SMIC_RETRY_TIMEOUT) {
+ smic_data.smic_timeout -= time;
+ if (smic_data.smic_timeout < 0) {
+ start_error_recovery(&smic_data, "smic timed out.");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ }
+ }
+ flags = read_smic_flags(&smic_data);
+ if (flags & SMIC_FLAG_BSY)
+ return SI_SM_CALL_WITH_DELAY;
+
+ status = read_smic_status(&smic_data);
+ if (smic_debug & SMIC_DEBUG_STATES)
+ grub_printf("smic_event - state = %d, flags = 0x%x,"
+ " status = 0x%x\n",
+ smic_data.state, flags, status);
+
+ switch (smic_data.state) {
+ case SMIC_IDLE:
+ /* in IDLE we check for available messages */
+ if (flags & SMIC_SMS_DATA_AVAIL)
+ {
+ return SI_SM_ATTN;
+ }
+ return SI_SM_IDLE;
+
+ case SMIC_START_OP:
+ /* sanity check whether smic is really idle */
+ write_smic_control(&smic_data, SMIC_CC_SMS_GET_STATUS);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ smic_data.state = SMIC_OP_OK;
+ break;
+
+ case SMIC_OP_OK:
+ if (status != SMIC_SC_SMS_READY) {
+ /* this should not happen */
+ start_error_recovery(&smic_data,
+ "state = SMIC_OP_OK,"
+ " status != SMIC_SC_SMS_READY");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ /* OK so far; smic is idle let us start ... */
+ write_smic_control(&smic_data, SMIC_CC_SMS_WR_START);
+ write_next_byte(&smic_data);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ smic_data.state = SMIC_WRITE_START;
+ break;
+
+ case SMIC_WRITE_START:
+ if (status != SMIC_SC_SMS_WR_START) {
+ start_error_recovery(&smic_data,
+ "state = SMIC_WRITE_START, "
+ "status != SMIC_SC_SMS_WR_START");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ /* we must not issue WR_(NEXT|END) unless
+ TX_DATA_READY is set */
+ if (flags & SMIC_TX_DATA_READY) {
+ if (smic_data.write_count == 1) {
+ /* last byte */
+ write_smic_control(&smic_data, SMIC_CC_SMS_WR_END);
+ smic_data.state = SMIC_WRITE_END;
+ } else {
+ write_smic_control(&smic_data, SMIC_CC_SMS_WR_NEXT);
+ smic_data.state = SMIC_WRITE_NEXT;
+ }
+ write_next_byte(&smic_data);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ }
+ else {
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ break;
+
+ case SMIC_WRITE_NEXT:
+ if (status != SMIC_SC_SMS_WR_NEXT) {
+ start_error_recovery(&smic_data,
+ "state = SMIC_WRITE_NEXT, "
+ "status != SMIC_SC_SMS_WR_NEXT");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ /* this is the same code as in SMIC_WRITE_START */
+ if (flags & SMIC_TX_DATA_READY) {
+ if (smic_data.write_count == 1) {
+ write_smic_control(&smic_data, SMIC_CC_SMS_WR_END);
+ smic_data.state = SMIC_WRITE_END;
+ }
+ else {
+ write_smic_control(&smic_data, SMIC_CC_SMS_WR_NEXT);
+ smic_data.state = SMIC_WRITE_NEXT;
+ }
+ write_next_byte(&smic_data);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ }
+ else {
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ break;
+
+ case SMIC_WRITE_END:
+ if (status != SMIC_SC_SMS_WR_END) {
+ start_error_recovery (&smic_data,
+ "state = SMIC_WRITE_END, "
+ "status != SMIC_SC_SMS_WR_END");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ /* data register holds an error code */
+ data = read_smic_data(&smic_data);
+ if (data != 0) {
+ if (smic_debug & SMIC_DEBUG_ENABLE) {
+ grub_printf("SMIC_WRITE_END: data = %x\n", data);
+ }
+ start_error_recovery(&smic_data,
+ "state = SMIC_WRITE_END, "
+ "data != SUCCESS");
+ return SI_SM_CALL_WITH_DELAY;
+ } else {
+ smic_data.state = SMIC_WRITE2READ;
+ }
+ break;
+
+ case SMIC_WRITE2READ:
+ /* we must wait for RX_DATA_READY to be set before we
+ can continue */
+ if (flags & SMIC_RX_DATA_READY) {
+ write_smic_control(&smic_data, SMIC_CC_SMS_RD_START);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ smic_data.state = SMIC_READ_START;
+ } else {
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ break;
+
+ case SMIC_READ_START:
+ if (status != SMIC_SC_SMS_RD_START) {
+ start_error_recovery(&smic_data,
+ "state = SMIC_READ_START, "
+ "status != SMIC_SC_SMS_RD_START");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ if (flags & SMIC_RX_DATA_READY) {
+ read_next_byte(&smic_data);
+ write_smic_control(&smic_data, SMIC_CC_SMS_RD_NEXT);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ smic_data.state = SMIC_READ_NEXT;
+ } else {
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ break;
+
+ case SMIC_READ_NEXT:
+ switch (status) {
+ /* smic tells us that this is the last byte to be read
+ --> clean up */
+ case SMIC_SC_SMS_RD_END:
+ read_next_byte(&smic_data);
+ write_smic_control(&smic_data, SMIC_CC_SMS_RD_END);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ smic_data.state = SMIC_READ_END;
+ break;
+ case SMIC_SC_SMS_RD_NEXT:
+ if (flags & SMIC_RX_DATA_READY) {
+ read_next_byte(&smic_data);
+ write_smic_control(&smic_data, SMIC_CC_SMS_RD_NEXT);
+ write_smic_flags(&smic_data, flags | SMIC_FLAG_BSY);
+ smic_data.state = SMIC_READ_NEXT;
+ } else {
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ break;
+ default:
+ start_error_recovery(&smic_data,
+ "state = SMIC_READ_NEXT, "
+ "status !=
SMIC_SC_SMS_RD_(NEXT|END)");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ break;
+
+ case SMIC_READ_END:
+ if (status != SMIC_SC_SMS_READY) {
+ start_error_recovery(&smic_data,
+ "state = SMIC_READ_END, "
+ "status != SMIC_SC_SMS_READY");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ data = read_smic_data(&smic_data);
+ /* data register holds an error code */
+ if (data != 0) {
+ if (smic_debug & SMIC_DEBUG_ENABLE) {
+ grub_printf("SMIC_READ_END: data = %x\n", data);
+ }
+ start_error_recovery(&smic_data,
+ "state = SMIC_READ_END, "
+ "data != SUCCESS");
+ return SI_SM_CALL_WITH_DELAY;
+ } else {
+ smic_data.state = SMIC_IDLE;
+ return SI_SM_TRANSACTION_COMPLETE;
+ }
+
+ case SMIC_HOSED:
+ init_smic(smic_data.io);
+ return SI_SM_HOSED;
+
+ default:
+ if (smic_debug & SMIC_DEBUG_ENABLE) {
+ grub_printf("smic_data.state = %d\n", smic_data.state);
+ start_error_recovery(&smic_data, "state = UNKNOWN");
+ return SI_SM_CALL_WITH_DELAY;
+ }
+ }
+ smic_data.smic_timeout = SMIC_RETRY_TIMEOUT;
+ return SI_SM_CALL_WITHOUT_DELAY;
+}
+
+static int smic_detect(void)
+{
+ /* It's impossible for the SMIC fnags register to be all 1's,
+ (assuming a properly functioning, self-initialized BMC)
+ but that's what you get from reading a bogus address, so we
+ test that first. */
+ if (read_smic_flags(&smic_data) == 0xff)
+ return 1;
+
+ return 0;
+}
+
+static void smic_cleanup(void)
+{
+}
+
+struct si_sm_handler smic_smi_handler =
+{
+ .init = init_smic,
+ .start_transaction = start_smic_transaction,
+ .get_result = smic_get_result,
+ .event = smic_event,
+ .detect = smic_detect,
+ .cleanup = smic_cleanup,
+};
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/ipmi_watchdog.c
grub-0.97.ipmi/stage2/ipmi_watchdog.c
--- grub-0.97/stage2/ipmi_watchdog.c 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_watchdog.c 2008-09-11 20:23:13.000000000
+0900
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* based on ipmi_watchdog.c in linux kernel. */
+
+#include "ipmi_watchdog.h"
+#include "shared.h"
+
+/* values for byte 1 of the set command, byte 2 of the get response. */
+#define WDOG_DONT_LOG (1 << 7)
+#define WDOG_DONT_STOP_ON_SET (1 << 6)
+#define WDOG_SET_TIMER_USE(byte, use) \
+ byte = ((byte) & 0xf8) | ((use) & 0x7)
+#define WDOG_GET_TIMER_USE(byte) ((byte) & 0x7)
+#define WDOG_TIMER_USE_BIOS_FRB2 1
+#define WDOG_TIMER_USE_BIOS_POST 2
+#define WDOG_TIMER_USE_OS_LOAD 3
+#define WDOG_TIMER_USE_SMS_OS 4
+#define WDOG_TIMER_USE_OEM 5
+
+/* values for byte 2 of the set command, byte 3 of the get response. */
+#define WDOG_SET_PRETIMEOUT_ACT(byte, use) \
+ byte = ((byte) & 0x8f) | (((use) & 0x7) << 4)
+#define WDOG_GET_PRETIMEOUT_ACT(byte) (((byte) >> 4) & 0x7)
+#define WDOG_PRETIMEOUT_NONE 0
+#define WDOG_PRETIMEOUT_SMI 1
+#define WDOG_PRETIMEOUT_NMI 2
+#define WDOG_PRETIMEOUT_MSG_INT 3
+
+/* Actions to perform on a full timeout. */
+#define WDOG_SET_TIMEOUT_ACT(byte, use) \
+ byte = ((byte) & 0xf8) | ((use) & 0x7)
+#define WDOG_GET_TIMEOUT_ACT(byte) ((byte) & 0x7)
+#define WDOG_TIMEOUT_NONE 0
+#define WDOG_TIMEOUT_RESET 1
+#define WDOG_TIMEOUT_POWER_DOWN 2
+#define WDOG_TIMEOUT_POWER_CYCLE 3
+
+/* Setting/getting the watchdog timer value. This is for bytes 5 and
+ 6 (the timeout time) of the set command, and bytes 6 and 7 (the
+ timeout time) and 8 and 9 (the current countdown value) of the
+ response. The timeout value is given in seconds (in the command it
+ is 100ms intervals). */
+#define WDOG_SET_TIMEOUT(byte1, byte2, val) \
+ (byte1) = (((val) * 10) & 0xff), (byte2) = (((val) * 10) >> 8)
+#define WDOG_GET_TIMEOUT(byte1, byte2) \
+ (((byte1) | ((byte2) << 8)) / 10)
+
+#define IPMI_WDOG_RESET_TIMER 0x22
+#define IPMI_WDOG_SET_TIMER 0x24
+#define IPMI_WDOG_GET_TIMER 0x25
+
+static int watchdog_action = WDOG_TIMEOUT_RESET;
+
+int
+ipmi_watchdog_set_timeout (int timeout_value)
+{
+ struct ipmi_msg msg;
+ unsigned char data[6];
+ int rv;
+ struct ipmi_addr addr;
+
+ if (ipmi_init() != 0)
+ return -1;
+
+ data[0] = 0;
+ WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_OS_LOAD);
+
+ if ((IPMI_MAJOR_VERSION > 1) ||
+ ((IPMI_MAJOR_VERSION == 1) && (IPMI_MINOR_VERSION >= 5))) {
+ /* This is an IPMI 1.5-only feature. */
+ data[0] |= WDOG_DONT_STOP_ON_SET;
+ }
+
+ data[1] = 0;
+ WDOG_SET_TIMEOUT_ACT(data[1], watchdog_action);
+ /* No pretimeout */
+ WDOG_SET_PRETIMEOUT_ACT(data[1], WDOG_PRETIMEOUT_NONE);
+ data[2] = 0; /* No pretimeout. */
+ data[3] = 0;
+ WDOG_SET_TIMEOUT(data[4], data[5], timeout_value);
+
+ addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr.channel = IPMI_BMC_CHANNEL;
+ addr.lun = 0;
+
+ msg.netfn = IPMI_NETFN_APP_REQUEST;
+ msg.cmd = IPMI_WDOG_SET_TIMER;
+ msg.data = data;
+ msg.data_len = sizeof(data);
+
+ rv = ipmi_request((struct ipmi_addr *)&addr, &msg);
+
+ if (rv == 0)
+ rv = ipmi_watchdog_send_heartbeat();
+
+ return rv;
+}
+
+int
+ipmi_watchdog_send_heartbeat (void)
+{
+ struct ipmi_msg msg;
+ int rv;
+ struct ipmi_addr addr;
+
+ if (ipmi_init() != 0)
+ return -1;
+
+ addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr.channel = IPMI_BMC_CHANNEL;
+ addr.lun = 0;
+
+ msg.netfn = IPMI_NETFN_APP_REQUEST;
+ msg.cmd = IPMI_WDOG_RESET_TIMER;
+ msg.data = NULL;
+ msg.data_len = 0;
+
+ rv = ipmi_request((struct ipmi_addr *)&addr, &msg);
+
+ return rv;
+}
+
+int
+ipmi_watchdog_stop (void)
+{
+ struct ipmi_msg msg;
+ unsigned char data[6];
+ int rv;
+ struct ipmi_addr addr;
+
+ if (ipmi_init() != 0)
+ return -1;
+
+ data[0] = 0;
+ WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_OS_LOAD);
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ WDOG_SET_TIMEOUT_ACT(data[1], WDOG_TIMEOUT_NONE);
+ WDOG_SET_TIMEOUT(data[4], data[5], 0);
+
+ addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr.channel = IPMI_BMC_CHANNEL;
+ addr.lun = 0;
+
+ msg.netfn = IPMI_NETFN_APP_REQUEST;
+ msg.cmd = IPMI_WDOG_SET_TIMER;
+ msg.data = data;
+ msg.data_len = sizeof(data);
+
+ rv = ipmi_request((struct ipmi_addr *)&addr, &msg);
+
+ return rv;
+}
+
+int
+ipmi_watchdog_set_timer_action (watchdog_action_type type)
+{
+ switch (type) {
+ case WATCHDOG_ACTION_RESET:
+ watchdog_action = WDOG_TIMEOUT_RESET;
+ break;
+ case WATCHDOG_ACTION_POWER_DOWN:
+ watchdog_action = WDOG_TIMEOUT_POWER_DOWN;
+ break;
+ case WATCHDOG_ACTION_POWER_CYCLE:
+ watchdog_action = WDOG_TIMEOUT_POWER_CYCLE;
+ break;
+ default:
+ watchdog_action = WDOG_TIMEOUT_RESET;
+ break;
+ }
+ return 0;
+}
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
+
diff -Nurp grub-0.97/stage2/ipmi_watchdog.h
grub-0.97.ipmi/stage2/ipmi_watchdog.h
--- grub-0.97/stage2/ipmi_watchdog.h 1970-01-01 09:00:00.000000000 +0900
+++ grub-0.97.ipmi/stage2/ipmi_watchdog.h 2008-09-11 20:23:13.000000000
+0900
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 Hiroyuki Ikezoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __IPMI_WATCHDOG_H__
+#define __IPMI_WATCHDOG_H__
+
+#include "ipmi.h"
+
+typedef enum {
+ WATCHDOG_ACTION_RESET,
+ WATCHDOG_ACTION_POWER_DOWN,
+ WATCHDOG_ACTION_POWER_CYCLE
+} watchdog_action_type;
+
+int ipmi_watchdog_set_timeout (int timeout_value);
+int ipmi_watchdog_send_heartbeat (void);
+int ipmi_watchdog_stop (void);
+int ipmi_watchdog_set_timer_action (watchdog_action_type type);
+
+#endif /* __IPMI_WATCHDOG_H__ */
+
+/*
+vi:ts=4:nowrap:ai:expandtab:sw=4
+*/
diff -Nurp grub-0.97/stage2/shared.h grub-0.97.ipmi/stage2/shared.h
--- grub-0.97/stage2/shared.h 2004-06-20 01:40:09.000000000 +0900
+++ grub-0.97.ipmi/stage2/shared.h 2008-09-11 20:23:13.000000000 +0900
@@ -871,6 +871,7 @@ int grub_sprintf (char *buffer, const ch
int grub_tolower (int c);
int grub_isspace (int c);
int grub_strncat (char *s1, const char *s2, int n);
+void grub_memcpy(void *dest, const void *src, int len);
void *grub_memmove (void *to, const void *from, int len);
void *grub_memset (void *start, int c, int len);
int grub_strncat (char *s1, const char *s2, int n);
diff -Nurp grub-0.97/stage2/stage2.c grub-0.97.ipmi/stage2/stage2.c
--- grub-0.97/stage2/stage2.c 2005-03-20 02:51:57.000000000 +0900
+++ grub-0.97.ipmi/stage2/stage2.c 2008-09-11 20:23:13.000000000 +0900
@@ -20,6 +20,11 @@
#include <shared.h>
#include <term.h>
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+# include "ipmi_watchdog.h"
+# include "cgl_config.h"
+#endif /* SUPPORT_IPMI */
+
grub_jmp_buf restart_env;
#if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS)
@@ -711,7 +716,7 @@ restart:
/* Attempt to boot an entry. */
boot_entry:
-
+
cls ();
setcursor (1);
@@ -836,6 +841,7 @@ cmain (void)
char *config_entries, *menu_entries;
char *kill_buf = (char *) KILL_BUF;
+ struct term_entry *term_save;
auto void reset (void);
void reset (void)
{
@@ -1050,6 +1056,19 @@ cmain (void)
while (is_preset);
}
+#if !defined(GRUB_UTIL) && defined(SUPPORT_IPMI)
+ term_save = current_term;
+ current_term = term_table;
+ if (! cgl_init())
+ if (errnum == ERR_UNRECOGNIZED)
+ {
+ grub_printf ("cgl.conf have syntax error...\nPress any key to
continue...");
+ getkey ();
+ }
+ current_term = term_save;
+ errnum = 0;
+#endif /* SUPPORT_IPMI */
+
if (! num_entries)
{
/* If no acceptable config file, goto command-line, starting
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [grub legacy] Grub with IPMI WDT,
Yuta Sugiura <=