bug-grub
[Top][All Lists]
Advanced

[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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]