summaryrefslogtreecommitdiff
path: root/kern/bmcpanic-2.5.62.patch
diff options
context:
space:
mode:
Diffstat (limited to 'kern/bmcpanic-2.5.62.patch')
-rw-r--r--kern/bmcpanic-2.5.62.patch830
1 files changed, 830 insertions, 0 deletions
diff --git a/kern/bmcpanic-2.5.62.patch b/kern/bmcpanic-2.5.62.patch
new file mode 100644
index 0000000..44e9141
--- /dev/null
+++ b/kern/bmcpanic-2.5.62.patch
@@ -0,0 +1,830 @@
+diff -Naru linux-2.5.62/arch/i386/kernel/reboot.c linux-2.5.62p/arch/i386/kernel/reboot.c
+--- linux-2.5.62/arch/i386/kernel/reboot.c 2003-03-07 16:33:50.000000000 +0800
++++ linux-2.5.62p/arch/i386/kernel/reboot.c 2003-03-06 22:27:16.000000000 +0800
+@@ -221,6 +221,10 @@
+ : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100)));
+ }
+
++#ifdef CONFIG_IPMI_PANIC_ACTION
++extern void ipmi_reset(int mode);
++#endif
++
+ void machine_restart(char * __unused)
+ {
+ #if CONFIG_SMP
+@@ -277,6 +281,9 @@
+ }
+ }
+
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ ipmi_reset(1);
++#endif
+ machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
+ }
+
+@@ -288,5 +295,8 @@
+ {
+ if (pm_power_off)
+ pm_power_off();
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ ipmi_reset(0);
++#endif
+ }
+
+diff -Naru linux-2.5.62/drivers/char/ipmi/ipmi_msghandler.c linux-2.5.62p/drivers/char/ipmi/ipmi_msghandler.c
+--- linux-2.5.62/drivers/char/ipmi/ipmi_msghandler.c 2003-03-07 16:34:18.000000000 +0800
++++ linux-2.5.62p/drivers/char/ipmi/ipmi_msghandler.c 2003-03-06 22:27:16.000000000 +0800
+@@ -45,6 +45,14 @@
+ #include <linux/notifier.h>
+ #include <linux/init.h>
+
++#ifdef CONFIG_IPMI_PANIC_ACTION
++#define uchar unsigned char
++extern int ipmi_panic_init(void);
++extern int ipmi_send_recv(uchar cmd, uchar netfn, uchar lun,
++ uchar *sbuf, int slen, uchar *rbuf, int rlen,
++ int *nret, uchar *cc);
++#endif
++
+ struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
+ static int ipmi_init_msghandler(void);
+
+@@ -345,7 +353,7 @@
+ unsigned int i;
+
+ for (i=intf->curr_seq;
+- i!=(intf->curr_seq-1);
++ (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
+ i=(i+1)%IPMI_IPMB_NUM_SEQ)
+ {
+ if (! intf->seq_table[i].inuse)
+@@ -437,6 +445,7 @@
+ return rv;
+ }
+
++static int fIPMIok = 0; /* true if an IPMI interface is valid */
+
+ int ipmi_create_user(unsigned int if_num,
+ struct ipmi_user_hndl *handler,
+@@ -480,6 +489,7 @@
+ goto out_unlock;
+ }
+
++ fIPMIok = 1; /* at least one IPMI interface is valid */
+ new_user->handler = handler;
+ new_user->handler_data = handler_data;
+ new_user->intf = ipmi_interfaces[if_num];
+@@ -906,8 +916,6 @@
+ probably, so abort. */
+ spin_unlock_irqrestore(&(intf->seq_lock),
+ flags);
+- ipmi_free_recv_msg(recv_msg);
+- ipmi_free_smi_msg(smi_msg);
+ goto out_err;
+ }
+
+@@ -1794,8 +1802,13 @@
+ return rv;
+ }
+
++extern void machine_restart(char * __unused);
++extern void machine_power_off(void);
++extern char *panic_string;
++
+ #ifdef CONFIG_IPMI_PANIC_EVENT
+
++#ifndef CONFIG_IPMI_PANIC_ACTION
+ static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
+ {
+ }
+@@ -1828,12 +1841,9 @@
+ data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */
+ data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */
+
+- /* These used to have the first three bytes of the panic string,
+- but not only is that not terribly useful, it's not available
+- any more. */
+- data[3] = 0;
+- data[6] = 0;
+- data[7] = 0;
++ data[3] = panic_string[0];
++ data[6] = panic_string[1];
++ data[7] = panic_string[2];
+
+ smi_msg.done = dummy_smi_done_handler;
+ recv_msg.done = dummy_recv_done_handler;
+@@ -1857,6 +1867,30 @@
+ intf->my_lun);
+ }
+ }
++#endif
++
++void ipmi_reset(int mode)
++{
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ unsigned char imode;
++ unsigned char cc;
++ int rc, i;
++
++ /* Input mode param: 0 = soft reset, 1 = power down, 2 = power cycle */
++ if (!fIPMIok || mode == 0 || mode > 3) {
++ machine_restart(NULL); /* normal Linux reset (arch/i386/) */
++ return;
++ }
++ /*
++ * IPMI Chassis Reset modes:
++ * 0 = power down, 1 = power up, 2 = power cycle, 3 = hard reset
++ */
++ if (mode == 1) imode = 0;
++ else imode = mode;
++ rc = ipmi_send_recv(0x02,0x00,0,&imode,1,NULL,0,&i,&cc);
++#endif
++ return;
++}
+ #endif /* CONFIG_IPMI_PANIC_EVENT */
+
+ static int has_paniced = 0;
+@@ -1875,14 +1909,55 @@
+ /* For every registered interface, set it to run to completion. */
+ for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+ intf = ipmi_interfaces[i];
+- if (intf == NULL)
+- continue;
+-
++ if (intf == NULL) continue;
+ intf->handlers->set_run_to_completion(intf->send_info, 1);
+ }
+
+ #ifdef CONFIG_IPMI_PANIC_EVENT
+- send_panic_events();
++ if (fIPMIok) {
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ unsigned char alarms, cc;
++ unsigned char obuf[16];
++ unsigned char ibuf[16];
++ int olen, rc;
++
++ /* Get Alarm LED values */
++ obuf[0] = 0x03; // PRIVATE_BUS_ID;
++ obuf[1] = 0x41; // ALARMS_PANEL_READ;
++ obuf[2] = 1; // one byte of alarms data
++ obuf[3] = 0; // initial alarms value
++ olen = 3;
++ rc = ipmi_send_recv(0x52,0x06,0,obuf,olen,ibuf,4,&i,&cc);
++ alarms = ibuf[0];
++ printk("ipmi_panic: get alarms rc=%d cc=%x, alarms=%02x\n",
++ rc,cc,alarms);
++ if (alarms == 0) alarms = 0x0F; /* 0=err, fallback to default */
++ if (rc == 0) { // valid, so ok to set alarm LED
++ /* Set Crit Alarm LED */
++ obuf[0] = 0x03; // PRIVATE_BUS_ID;
++ obuf[1] = 0x40; // ALARMS_PANEL_WRITE;
++ obuf[2] = 1; // one byte of alarms data
++ obuf[3] = (alarms & 0x0D) | 0xF0; // turn on critical alarm
++ olen = 4;
++ rc = ipmi_send_recv(0x52,0x06,0,obuf,olen,NULL,0,&i,&cc);
++ printk("ipmi_panic: set crit alarm rc=%d cc=%x\n",rc,cc);
++ }
++ /* Log the OS Critical Stop to the SEL (BMC firmware log). */
++ obuf[0] = 0x21; /* Kernel generator ID, IPMI table 5-4 */
++ obuf[1] = 0x03; /* This is for IPMI 1.0. */
++ obuf[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */
++ obuf[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */
++ obuf[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */
++ obuf[3] = panic_string[0];
++ obuf[6] = panic_string[1];
++ obuf[7] = panic_string[2];
++ olen = 8;
++ rc = ipmi_send_recv(0x02,0x04,0,obuf,olen,NULL,0,&i,&cc);
++ printk("ipmi_panic: log OS Critical Stop rc=%d cc=%x\n",rc,cc);
++#else
++ send_panic_events();
++#endif
++ }
+ #endif
+
+ return NOTIFY_DONE;
+@@ -1914,6 +1989,9 @@
+
+ notifier_chain_register(&panic_notifier_list, &panic_block);
+
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ ipmi_panic_init();
++#endif
+ initialized = 1;
+
+ printk(KERN_INFO "ipmi: message handler initialized\n");
+@@ -1971,3 +2049,6 @@
+ EXPORT_SYMBOL(ipmi_get_my_address);
+ EXPORT_SYMBOL(ipmi_set_my_LUN);
+ EXPORT_SYMBOL(ipmi_get_my_LUN);
++#ifdef CONFIG_IPMI_PANIC_ACTION
++EXPORT_SYMBOL(ipmi_reset);
++#endif
+diff -Naru linux-2.5.62/drivers/char/ipmi/ipmi_panic.c linux-2.5.62p/drivers/char/ipmi/ipmi_panic.c
+--- linux-2.5.62/drivers/char/ipmi/ipmi_panic.c 1970-01-01 08:00:00.000000000 +0800
++++ linux-2.5.62p/drivers/char/ipmi/ipmi_panic.c 2003-03-06 22:27:16.000000000 +0800
+@@ -0,0 +1,515 @@
++/*
++ * ipmi_panic.c
++ *
++ * This code is needed to run a streamlined IPMI KCS command when
++ * the rest of the system may be dead (panic time). It must wait
++ * for completion of the receive function also.
++ * There will be zero or one BMC, with KCS as a minimum and perhaps
++ * other interfaces, so doing KCS to a default BMC LUN is valid here.
++ *
++ * Note that CONFIG_IPMI_HANDLER should be =y (rather than =m) to
++ * ensure that this handler is loaded early enough to cover boot
++ * time panic conditions. CONFIG_IPMI_PANIC_ACTION can only be
++ * defined if CONFIG_IPMI_HANDLER=y.
++ *
++ * Author: Andy Cress <arcress at users.sourceforge.net>
++ *
++ * Change History:
++ * 01/31/03 Andy Cress - created for kernel 2.5.44 with OpenIPMI
++ *
++ * Copyright 2003 Intel Corp.
++ *
++ * 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 <linux/config.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/semaphore.h>
++#include <linux/delay.h>
++
++#define uchar unsigned char
++
++#define KCS_READY_DELAY 5
++#define BMC_RESPONSE_DELAY 5
++#define BMC_RETRY_DELAY 60
++
++#if defined(__ia64__)
++#define KCS_BASE 0x8a2
++#else
++#define KCS_BASE 0xca2
++#endif
++#define KCS_STATUS_REG (KCS_BASE + 1)
++#define KCS_COMMAND_REG (KCS_BASE + 1)
++#define KCS_DATAIN_REG (KCS_BASE + 0)
++#define KCS_DATAOUT_REG (KCS_BASE + 0)
++
++/* State bits based on S1 & S0 below */
++#define KCS_STATE_MASK 0xC0
++#define KCS_IDLE_STATE 0x00
++#define KCS_READ_STATE 0x40
++#define KCS_WRITE_STATE 0x80
++#define KCS_ERROR_STATE 0xC0
++
++#define KCS_IBF 0x02
++#define KCS_OBF 0x01
++#define KCS_SMS_ATN 0x04
++
++#define SEND_INIT 1
++#define SEND_START 2
++#define SEND_NEXT 3
++#define SEND_END 4
++#define RECV_START 5
++#define RECV_INIT 6
++#define RECV_NEXT 7
++#define RECV_INIT2 8
++#define RECV_END 9
++#define END 10
++#define ERROR 0
++
++/* SMS Transfer Stream Control Codes */
++#define GET_STATUS_ABORT 0x60
++#define WRITE_START 0x61
++#define WRITE_END 0x62
++#define KCS_READ 0x68
++
++#define MAX_INVALID_RESPONSE_COUNT 2
++#define MIN_BMC_RESPONSE_SIZE 3
++#define MAX_IMB_PACKET_SIZE 33
++#define MAX_BMC_RESPONSE_SIZE (MIN_BMC_RESPONSE_SIZE + MAX_IMB_PACKET_SIZE)
++#define MAX_XFER_LENGTH (MAX_IMB_PACKET_SIZE * 2)
++
++#define MAX_BUFFER_SIZE 64
++
++typedef struct bmc_response
++ {
++ unsigned char lun :2;
++ unsigned char netfn :6;
++ unsigned char cmd;
++ unsigned char cc;
++ unsigned char data[1];
++ }BMC_RESPONSE;
++
++typedef struct bmc_request
++ {
++ unsigned char lun :2;
++ unsigned char netfn :6;
++ unsigned char cmd;
++ unsigned char data[1];
++ }BMC_REQUEST;
++
++/* GET_DEVICE_ID RESPONSE (11 bytes) */
++typedef struct device_id_response
++ {
++ unsigned char device_id;
++
++ unsigned char device_revision :4;
++ unsigned char reserved :3;
++ unsigned char provides_sdr :1;
++
++ unsigned char major_firmware_revision :7;
++ #define NORMAL_OPERATION 0
++ #define DEVICE_BUSY 1
++ unsigned char device_available :1;
++
++ unsigned char minor_firmware_revision;
++ unsigned char ipmi_version_major :4;
++ unsigned char ipmi_version_minor :4;
++
++ unsigned char supports_sensor_device :1;
++ unsigned char supports_sdr_device :1;
++ unsigned char supports_sel_device :1;
++ unsigned char supports_fru_device :1;
++ unsigned char supports_ipmb_receiver :1;
++ unsigned char supports_ipmb_generator :1;
++ unsigned char supports_bridge :1;
++ unsigned char supports_chassis_device :1;
++
++ unsigned char manufacturer_id1;
++ unsigned char manufacturer_id2;
++ unsigned char manufacturer_id3;
++ unsigned short product_id;
++ } DEVICE_ID_RESPONSE;
++
++/*************************************/
++
++#if defined(__ia64__)
++static char kcs_new = 1;
++#else
++static char kcs_new = 0;
++#endif
++DECLARE_MUTEX(kcs_sem);
++
++/*************************************/
++
++/*
++ * kcs chip mashing stuff
++ */
++static int wait_while_ibf(void)
++{
++ unsigned char status_byte;
++
++ status_byte = inb_p(KCS_STATUS_REG);
++ if ((status_byte & KCS_IBF) == 0) return (0);
++ mdelay(KCS_READY_DELAY);
++ status_byte = inb_p(KCS_STATUS_REG);
++ if (status_byte & KCS_IBF) return (-1);
++ return (0);
++}
++
++static int is_obf_set(void)
++{
++ unsigned char cs;
++ cs = inb_p(KCS_STATUS_REG);
++ return ((cs & KCS_OBF) == KCS_OBF);
++}
++
++static int wait_until_obf(void)
++{
++ int retries = 0;
++
++ while (retries < 2) {
++ if (is_obf_set()) return (0);
++ mdelay(KCS_READY_DELAY);
++ retries++;
++ }
++ return (-ETIMEDOUT);
++}
++
++static unsigned char get_kcs_state(void)
++{
++ unsigned char cs;
++
++ cs = inb_p(KCS_STATUS_REG);
++ return (cs & KCS_STATE_MASK);
++}
++
++static unsigned char read_kcs_data(void)
++{
++ unsigned char data;
++
++ data = inb_p(KCS_DATAOUT_REG);
++ return (data);
++}
++
++static void write_kcs_data(unsigned char data)
++{
++ outb_p(data, KCS_DATAIN_REG);
++}
++
++static void write_kcs_cmd(unsigned char cmd)
++{
++ outb_p(cmd, KCS_COMMAND_REG);
++}
++
++static int clear_obf(void)
++{
++ read_kcs_data();
++ return (0);
++}
++
++static int kcs_xfer(BMC_REQUEST * request, int request_len,
++ BMC_RESPONSE * response, int *response_len)
++{
++ unsigned char *xmit_buffer, *recv_buffer;
++ int i = 0, rc = 0, state = SEND_INIT, bad = 0;
++
++ xmit_buffer = (unsigned char *) request;
++ recv_buffer = (unsigned char *) response;
++
++ while (1) {
++ if (state == END)
++ break;
++ else if (bad > 2) {
++ printk("[ipmi_panic] Maximum retries exceeded.\n");
++ rc = -EIO;
++ break;
++ }
++ switch (state) {
++ case SEND_INIT:
++ {
++ i = 0;
++ state = SEND_START;
++ wait_while_ibf();
++ if (kcs_new) clear_obf();
++ }
++ case SEND_START:
++ {
++ state = SEND_NEXT;
++ write_kcs_cmd(WRITE_START);
++ wait_while_ibf();
++ }
++ case SEND_NEXT:
++ {
++ if (i == (request_len - 1)) {
++ state = SEND_END;
++ break;
++ }
++ if (get_kcs_state() != KCS_WRITE_STATE) {
++ state = ERROR;
++ break;
++ }
++ write_kcs_data(xmit_buffer[i++]);
++ wait_while_ibf();
++ if (kcs_new) clear_obf();
++ break;
++ }
++ case SEND_END:
++ {
++ if (!kcs_new) wait_while_ibf();
++ write_kcs_cmd(WRITE_END);
++ wait_while_ibf();
++ if (get_kcs_state() != KCS_WRITE_STATE) {
++ state = ERROR;
++ break;
++ }
++ if (kcs_new) clear_obf();
++ write_kcs_data(xmit_buffer[i++]);
++ wait_while_ibf();
++ state = RECV_START;
++ }
++ case RECV_START:
++ {
++ switch (get_kcs_state()) {
++ case KCS_ERROR_STATE:
++ {
++ state = ERROR;
++ break;
++ }
++ case KCS_WRITE_STATE:
++ case KCS_IDLE_STATE:
++ {
++ mdelay(BMC_RESPONSE_DELAY);
++ break;
++ }
++ case KCS_READ_STATE:
++ {
++ i = 0;
++ memset(recv_buffer, 0,
++ *response_len);
++ state = RECV_INIT;
++ break;
++ }
++ }
++ break;
++ }
++ case RECV_INIT:
++ {
++ switch (get_kcs_state()) {
++ case KCS_ERROR_STATE:
++ case KCS_WRITE_STATE:
++ {
++ state = ERROR;
++ break;
++ }
++ case KCS_IDLE_STATE:
++ {
++ state = RECV_END;
++ break;
++ }
++ case KCS_READ_STATE:
++ {
++ if (is_obf_set())
++ state = RECV_NEXT;
++ else mdelay(1);
++ break;
++ }
++ default:
++ {
++ mdelay(1);
++ break;
++ }
++ }
++ break;
++ }
++ case RECV_NEXT:
++ {
++ if (i >= *response_len) {
++ rc = -EOVERFLOW;
++ state = ERROR;
++ break;
++ }
++ recv_buffer[i++] = read_kcs_data();
++ if (!kcs_new) wait_while_ibf();
++ write_kcs_data(KCS_READ);
++ if (kcs_new) wait_while_ibf();
++ state = RECV_INIT2;
++ break;
++ }
++ case RECV_INIT2:
++ {
++ switch (get_kcs_state()) {
++ case KCS_ERROR_STATE:
++ case KCS_WRITE_STATE:
++ {
++ state = ERROR;
++ break;
++ }
++ case KCS_IDLE_STATE:
++ {
++ if (kcs_new) {
++ if (wait_until_obf() == 0) {
++ clear_obf();
++ state = RECV_END;
++ } else {
++ state = ERROR;
++ }
++ } else {
++ state = RECV_END;
++ }
++ break;
++ }
++ case KCS_READ_STATE:
++ {
++ if (kcs_new) {
++ if (wait_until_obf() == 0)
++ state = RECV_NEXT;
++ else state = ERROR;
++ } else {
++ if (is_obf_set())
++ state = RECV_NEXT;
++ }
++ break;
++ }
++ }
++ break;
++ }
++ case RECV_END:
++ {
++ if ((i < MIN_BMC_RESPONSE_SIZE) ||
++ (response->netfn != (request->netfn | 0x01))
++ || (response->cmd != request->cmd)) {
++ if (request->cmd == 0x01 &&
++ request->netfn == 0x06 &&
++ response->netfn == 0x2b) /*ok*/;
++ else { /* flag the error */
++ printk("[ipmi_panic] Request/Response CMD/NETFN mismatch error\n");
++
++ printk(" i=%d, RQcmd/RQnetfn=0x%x/0x%x,RScmd/RSnetfn=0x%x/0x%x\n",
++ i, request->cmd, request->netfn,
++ response->cmd, response->netfn);
++ mdelay(BMC_RETRY_DELAY);
++ bad++;
++ state = SEND_INIT;
++ break;
++ }
++ }
++
++ *response_len = i;
++ rc = 0;
++ state = END;
++ break;
++ }
++ case ERROR:
++ default:
++ {
++ printk("[ipmi_panic] BMC in bad state. Retrying transfer\n");
++ mdelay(BMC_RETRY_DELAY);
++ bad++;
++ state = SEND_INIT;
++ break;
++ }
++ }
++ }
++ return (rc);
++}
++
++int ipmi_send_recv(uchar cmd, uchar netfn, uchar lun, uchar *sbuf, int slen,
++ uchar *rbuf, int rlen, int *nret, uchar *cc)
++{
++ uchar bmc_outbuf[MAX_BUFFER_SIZE];
++ uchar bmc_inbuf[MAX_BUFFER_SIZE];
++ BMC_REQUEST *bmc_req;
++ BMC_RESPONSE *bmc_resp;
++ int bmc_outlen;
++ int bmc_inlen;
++ int rc = 0;
++
++ if (kcs_new == 2) return (-ENXIO);
++
++ memset(bmc_outbuf,0, sizeof(bmc_outbuf));
++ memset(bmc_inbuf,0, sizeof(bmc_inbuf));
++ bmc_req = (BMC_REQUEST *)bmc_outbuf;
++ bmc_resp = (BMC_RESPONSE *)bmc_inbuf;
++ bmc_req->cmd = cmd;
++ bmc_req->netfn = netfn;
++ bmc_req->lun = lun;
++ bmc_outlen = slen + 2;
++ bmc_inlen = sizeof(bmc_inbuf);
++ if (slen > 0) memcpy(bmc_req->data,sbuf,slen);
++
++ rc = kcs_xfer(bmc_req, bmc_outlen, bmc_resp, &bmc_inlen);
++ if (bmc_resp->cc == 0xcc) /* flaky NMI fixup */
++ rc = kcs_xfer(bmc_req, bmc_outlen, bmc_resp, &bmc_inlen); /*retry*/
++
++ /* copy the response */
++ *cc = bmc_resp->cc;
++ if (bmc_inlen > rlen) bmc_inlen = rlen;
++ *nret = bmc_inlen;
++ if (bmc_inlen > 0) memcpy(rbuf,bmc_resp->data,bmc_inlen);
++
++ return(rc);
++}
++
++int ipmi_panic_init(void)
++{
++ int i, rc;
++ uchar cc;
++ uchar bdev[16];
++ DEVICE_ID_RESPONSE *dev_id;
++
++ printk("ipmi_panic driver at io 0x%x\n", KCS_BASE);
++ if ((inb_p(KCS_STATUS_REG) == 0xFF) &&
++ (inb_p(KCS_DATAIN_REG) == 0xFF)) {
++ printk("--KCS ISA window not present, exiting.\n");
++ return (-ENXIO);
++ }
++
++ /* Get Device ID */
++ rc = ipmi_send_recv(0x01,0x06,0,NULL,0,bdev,sizeof(bdev),&i,&cc);
++ if (rc != 0) kcs_new = 2; /* bad */
++ else if (cc != 0) kcs_new = 2; /* bad */
++ else
++ {
++ dev_id = (DEVICE_ID_RESPONSE *)&bdev[0];
++ printk("--BMC version %x.%x, IPMI version %d.%d\n",
++ dev_id->major_firmware_revision,
++ dev_id->minor_firmware_revision,
++ dev_id->ipmi_version_major, dev_id->ipmi_version_minor);
++ if ((dev_id->ipmi_version_major == 0) &&
++ (dev_id->ipmi_version_minor == 9)) {
++ printk("--Using legacy KCS state machine\n");
++ kcs_new = 0;
++ } else {
++ printk("--Using new KCS state machine\n");
++ kcs_new = 1;
++ }
++ }
++ /* dont care about chipset vendor, but test Lancewood & IPF to be sure*/
++
++ return(rc);
++}
++
++/* end ipmi_panic.c */
+diff -Naru linux-2.5.62/drivers/char/ipmi/Kconfig linux-2.5.62p/drivers/char/ipmi/Kconfig
+--- linux-2.5.62/drivers/char/ipmi/Kconfig 2003-03-07 16:34:23.000000000 +0800
++++ linux-2.5.62p/drivers/char/ipmi/Kconfig 2003-03-06 22:46:58.000000000 +0800
+@@ -7,8 +7,14 @@
+ tristate 'IPMI top-level message handler'
+ help
+ This enables the central IPMI message handler, required for IPMI
+- to work. Note that you must have this enabled to do any other IPMI
+- things. See IPMI.txt for more details.
++ to work.
++
++ IPMI is a standard for managing sensors (temperature,
++ voltage, etc.) in a system.
++
++ See Documentation/IPMI.txt for more details on the driver.
++
++ If unsure, say N.
+
+ config IPMI_PANIC_EVENT
+ bool 'Generate a panic event to all BMCs on a panic'
+@@ -36,5 +42,11 @@
+ depends on IPMI_HANDLER
+ help
+ This enables the IPMI watchdog timer.
++config IPMI_PANIC_ACTION
++ int 'Action after Panic (0=reset,1=power down,2=power cycle)'
++ requires IPMI_HANDLER=y
++ default "0"
++ help
++ Set the Action after Panic
+
+ endmenu
+diff -Naru linux-2.5.62/drivers/char/ipmi/Makefile linux-2.5.62p/drivers/char/ipmi/Makefile
+--- linux-2.5.62/drivers/char/ipmi/Makefile 2003-03-07 16:34:54.000000000 +0800
++++ linux-2.5.62p/drivers/char/ipmi/Makefile 2003-03-06 22:27:16.000000000 +0800
+@@ -4,7 +4,7 @@
+
+ ipmi_kcs_drv-objs := ipmi_kcs_sm.o ipmi_kcs_intf.o
+
+-obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
++obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o ipmi_panic.o
+ obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
+ obj-$(CONFIG_IPMI_KCS) += ipmi_kcs_drv.o
+ obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
+diff -Naru linux-2.5.62/drivers/char/Kconfig linux-2.5.62p/drivers/char/Kconfig
+--- linux-2.5.62/drivers/char/Kconfig 2003-03-07 16:33:38.000000000 +0800
++++ linux-2.5.62p/drivers/char/Kconfig 2003-03-07 16:22:02.000000000 +0800
+@@ -999,5 +999,6 @@
+ out to lunch past a certain margin. It can reboot the system
+ or merely print a warning.
+
++
+ endmenu
+
+diff -Naru linux-2.5.62/kernel/panic.c linux-2.5.62p/kernel/panic.c
+--- linux-2.5.62/kernel/panic.c 2003-03-07 16:34:58.000000000 +0800
++++ linux-2.5.62p/kernel/panic.c 2003-03-06 22:27:07.000000000 +0800
+@@ -20,6 +20,10 @@
+ asmlinkage void sys_sync(void); /* it's really int */
+
+ int panic_timeout;
++char *panic_string;
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ extern void ipmi_reset(int mode);
++#endif
+
+ struct notifier_block *panic_notifier_list;
+
+@@ -53,6 +57,7 @@
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
++ panic_string = buf;
+ printk(KERN_EMERG "Kernel panic: %s\n",buf);
+ if (in_interrupt())
+ printk(KERN_EMERG "In interrupt handler - not syncing\n");
+@@ -81,7 +86,11 @@
+ * choosing not too. It might crash, be corrupt or do
+ * more harm than good for other reasons.
+ */
++#ifdef CONFIG_IPMI_PANIC_ACTION
++ ipmi_reset(CONFIG_IPMI_PANIC_ACTION);
++#else
+ machine_restart(NULL);
++#endif
+ }
+ #ifdef __sparc__
+ {