summaryrefslogtreecommitdiff
path: root/kern/bmcpanic-2.4.19.patch
diff options
context:
space:
mode:
Diffstat (limited to 'kern/bmcpanic-2.4.19.patch')
-rw-r--r--kern/bmcpanic-2.4.19.patch997
1 files changed, 997 insertions, 0 deletions
diff --git a/kern/bmcpanic-2.4.19.patch b/kern/bmcpanic-2.4.19.patch
new file mode 100644
index 0000000..ab1d5dd
--- /dev/null
+++ b/kern/bmcpanic-2.4.19.patch
@@ -0,0 +1,997 @@
+--- linux-2.4.19-orig/drivers/char/misc.c Fri Aug 2 20:39:43 2002
++++ linux-2.4.19/drivers/char/misc.c Thu Feb 27 15:07:25 2003
+@@ -70,6 +70,7 @@
+ extern void gfx_register(void);
+ #endif
+ extern void streamable_init(void);
++extern int bmc_panic_init(void);
+ extern int rtc_DP8570A_init(void);
+ extern int rtc_MK48T08_init(void);
+ extern int ds1286_init(void);
+@@ -266,6 +267,9 @@
+ #endif
+ #ifdef CONFIG_PMAC_PBOOK
+ pmu_device_init();
++#endif
++#ifdef CONFIG_BMCPANIC
++ bmc_panic_init();
+ #endif
+ #ifdef CONFIG_SGI_NEWPORT_GFX
+ gfx_register ();
+--- linux-2.4.19-orig/kernel/panic.c Fri Aug 2 20:39:46 2002
++++ linux-2.4.19/kernel/panic.c Thu Feb 27 15:07:25 2003
+@@ -17,6 +17,10 @@
+ #include <linux/sysrq.h>
+ #include <linux/interrupt.h>
+
++char *panic_string;
++#ifdef CONFIG_BMCPANIC
++ extern void bmcpanic_action(void);
++#endif
+ asmlinkage void sys_sync(void); /* it's really int */
+
+ int panic_timeout;
+@@ -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.
+ */
+- machine_restart(NULL);
++#ifdef CONFIG_BMCPANIC
++ bmcpanic_action();
++#else
++ machine_restart(NULL);
++#endif
+ }
+ #ifdef __sparc__
+ {
+--- linux-2.4.19-orig/kernel/ksyms.c Fri Aug 2 20:39:46 2002
++++ linux-2.4.19/kernel/ksyms.c Thu Feb 27 15:07:25 2003
+@@ -60,6 +60,7 @@
+
+ extern void *sys_call_table;
+
++extern char *panic_string;
+ extern struct timezone sys_tz;
+ extern int request_dma(unsigned int dmanr, char * deviceID);
+ extern void free_dma(unsigned int dmanr);
+@@ -570,3 +571,5 @@
+
+ EXPORT_SYMBOL(tasklist_lock);
+ EXPORT_SYMBOL(pidhash);
++EXPORT_SYMBOL(panic_notifier_list);
++EXPORT_SYMBOL(panic_string);
+--- linux-2.4.19-orig/Documentation/Configure.help Fri Aug 2 20:39:42 2002
++++ linux-2.4.19/Documentation/Configure.help Thu Feb 27 15:07:25 2003
+@@ -16640,6 +16640,20 @@
+
+ If unsure, say N.
+
++BMC Panic Handler
++CONFIG_BMCPANIC
++ If you say Y here, additional functions will be added to the
++ panic handler via the panic notifier list.
++
++ If your system has IPMI support and a BMC (Baseboard Management
++ Controller) on the motherboard, then the following additional
++ functions will be performed if a panic occurs:
++ - Write an OS Critical Stop message to the firmware System Event Log
++ - Turn on the Critical LED on the Telco Alarms panel (if present)
++ - Send a BMC LAN alert via SNMP to a network operations center,
++ if the firmware Platform Event Filter configuration is set to
++ enable this.
++
+ Cobalt Networks support
+ CONFIG_COBALT
+ Support for Cobalt Networks x86-based servers.
+--- linux-2.4.19-orig/drivers/char/Makefile Fri Aug 2 20:39:43 2002
++++ linux-2.4.19/drivers/char/Makefile Thu Feb 27 15:07:25 2003
+@@ -230,6 +230,9 @@
+ obj-y += ftape/ftape.o
+ endif
+
++bmc_panic-objs := bmc_ipmi.o bmc_selmsg.o
++obj-$(CONFIG_BMCPANIC) += bmc_panic.o
++
+ obj-$(CONFIG_H8) += h8.o
+ obj-$(CONFIG_PPDEV) += ppdev.o
+ obj-$(CONFIG_DZ) += dz.o
+@@ -279,6 +282,9 @@
+ ./conmakehash $(FONTMAPFILE) > consolemap_deftbl.c
+
+ consolemap_deftbl.o: consolemap_deftbl.c $(TOPDIR)/include/linux/types.h
++
++bmc_panic.o: $(bmc_panic-objs)
++ $(LD) -r -o $@ $(bmc_panic-objs)
+
+ .DELETE_ON_ERROR:
+
+--- linux-2.4.19-orig/drivers/char/Config.in Fri Aug 2 20:39:43 2002
++++ linux-2.4.19/drivers/char/Config.in Thu Feb 27 15:07:25 2003
+@@ -127,6 +127,10 @@
+ if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
+ int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
+ fi
++bool 'BMC Panic Handler' CONFIG_BMCPANIC
++if [ "$CONFIG_BMCPANIC" != "n" ]; then
++ int ' Action after Panic (0=reset,1=power down,2=power cycle)' CONFIG_BMCPANIC_ACTION 0
++fi
+ if [ "$CONFIG_PARPORT" != "n" ]; then
+ dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
+ if [ "$CONFIG_PRINTER" != "n" ]; then
+--- /dev/null Fri Mar 23 23:37:44 2001
++++ linux-2.4.19/drivers/char/bmc_ipmi.c Thu Feb 27 15:07:25 2003
+@@ -0,0 +1,531 @@
++/*
++ * bmc_ipmi.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_BMCPANIC should be =y (rather than =m) to
++ * ensure that this handler is loaded early enough to cover boot
++ * time panic conditions. CONFIG_BMCPANIC_ACTION can only be
++ * defined if CONFIG_BMCPANIC=y.
++ *
++ * Author: Andy Cress <arcress at users.sourceforge.net>
++ *
++ * Change History:
++ * 01/31/03 Andy Cress - created from valinux ipmi_kcs driver v2.1
++ *
++ * 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; /* don't even try old kcs */
++#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 (%d) cmd=%02x. Retrying transfer\n", state,request->cmd);
++ 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);
++}
++
++extern void init_SEL(void);
++extern void cleanup_SEL(void);
++
++int bmc_panic_init(void)
++{
++ int i, rc;
++ uchar cc;
++ uchar bdev[16];
++ DEVICE_ID_RESPONSE *dev_id;
++
++ printk("bmc_panic ipmi 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;
++ }
++ }
++ init_SEL();
++
++ return(rc);
++}
++
++#ifdef MODULE
++int init_module(void)
++{
++ return (bmc_panic_init());
++}
++
++void cleanup_module(void)
++{
++ printk("bmc_panic: Driver shutting down.\n");
++ cleanup_SEL();
++}
++#endif
++
++/* end bmc_ipmi.c */
+--- /dev/null Fri Mar 23 23:37:44 2001
++++ linux-2.4.19/drivers/char/bmc_selmsg.c Thu Feb 27 15:07:25 2003
+@@ -0,0 +1,269 @@
++/*
++ * bmc_selmsg.c
++ * routines to send IMB and BMC requests to the SEL and alarms panel.
++ *
++ * 05/07/01 Todd Davis - created
++ * 09/17/01 Andy Cress - some cleanup
++ * 09/26/01 Andy Cress - added setAlarmLED, changed notifier priority
++ * 10/05/01 Andy Cress - fixed setAlarmLED, changed OEM bytes in panic
++ * 10/08/01 Andy Cress - added getAlarmLED
++ * 10/09/01 Andy Cress - save 3 chars of panic_string in SEL
++ * 10/25/01 Andy Cress - fixed confusion w DEBUG macro & LINUX_DEBUG
++ * 11/05/01 Andy Cress - adapted to open source driver calls
++ * 11/14/01 Andy Cress - code complete, see also bmc_ipmi.c
++ * 01/15/02 Andy Cress - changed to show BMC_PANIC tags
++ * 03/22/02 Andy Cress - changed printk messages
++ * 04/16/02 Andy Cress - added bmcpanic_action() routine
++ * 06/04/02 Andy Cress - added bmc_poweroff() routine
++ * 02/26/02 Andy Cress - major rework to use ipmi_send_recv, trim bloat
++ */
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/notifier.h> /* Routines to manage notifier chains for passing
++ * status changes to any interested routines. */
++
++///////////////////////////////////////////////////////////
++// Global definitions
++//////////////////////////////////////////////////////////
++#define uchar unsigned char
++#define ulong unsigned long
++
++#define BMC_SA 0x20 // BMC slave address
++#define NETFN_CHAS 0x00
++#define NETFN_SENS_EVENT 0x04
++#define NETFN_APP 0x06
++#define NETFN_STORAGE 0x0A
++
++#define CHASSIS_CTL 0x02
++#define GET_SEL_INFO 0x40
++#define PLATFORM_EVENT 0x02
++#define KERNEL_SENSOR_ID 0x21
++
++/* Defines for the Alarms Panel */
++#define MASTER_WRITE_READ 0x52 // Command from IPMI 1.5 Table 38-8
++#define ALARMS_PANEL_WRITE 0x40 // I2C Address 0x40 write to front panel
++#define ALARMS_PANEL_READ 0x41 // I2C Address 0x41 read from front panel
++#define PRIVATE_BUS_ID 0x03 // TAM 8574 lives on 2ndary private bus
++
++// See Table 36-3 for sensor types - 20h for OS critical stop
++// See Table 36-1 for event types - 6Fh Sensor Specific
++// discrete sensor class
++// See Table 23-6/36-3 for event data
++// data1 - 00b 00b 0001b (runtime) 0000b (initialization stop)
++// data2 - ffh (unspecified)
++// data3 - ffh (unspecified)
++
++///////////////////////////////////////////////////////////
++// Global variables
++//////////////////////////////////////////////////////////
++static int fIPMIok = 1; /* Are IPMI commands supported? */
++static int has_paniced;
++
++extern struct notifier_block *panic_notifier_list;
++extern char *panic_string;
++extern char *die_str;
++extern long die_err;
++
++extern void machine_restart(char *cmd); /*from include/linux/reboot.h*/
++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);
++
++///////////////////////////////////////////////////////////
++// Subroutines
++//////////////////////////////////////////////////////////
++
++static int ReadSELinfo(void)
++{
++ unsigned char cc;
++ unsigned char obuf[16];
++ unsigned char ibuf[16];
++ int rc, i;
++
++ rc = ipmi_send_recv(GET_SEL_INFO,NETFN_STORAGE,0,obuf,0,
++ ibuf,sizeof(ibuf),&i,&cc);
++ if (rc == 0 && cc != 0) rc = cc;
++ if (rc == 0)
++ printk("bmc_panic: Code %d SEL Ver %d Support %d\n",
++ ibuf[0],ibuf[1],ibuf[14]);
++ return(rc);
++} /* end ReadSELinfo()*/
++
++static unsigned char getAlarmLED(void)
++{
++ unsigned char cc;
++ unsigned char obuf[16];
++ unsigned char ibuf[16];
++ int olen, rc, i;
++ unsigned char alarms;
++
++ /* Get Alarm LED values */
++ obuf[0] = PRIVATE_BUS_ID; // 0x03;
++ obuf[1] = ALARMS_PANEL_READ; // 0x41;
++ obuf[2] = 1; // one byte of alarms data
++ obuf[3] = 0; // initial alarms value
++ olen = 3;
++ rc = ipmi_send_recv(MASTER_WRITE_READ,NETFN_APP,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);
++ return(alarms);
++} /*end getAlarmLED*/
++
++static int setAlarmLED(unsigned char alarms)
++{
++ unsigned char cc;
++ unsigned char obuf[16];
++ unsigned char ibuf[16];
++ int olen, rc, i;
++
++ obuf[0] = PRIVATE_BUS_ID; // 0x03;
++ obuf[1] = ALARMS_PANEL_WRITE; // 0x40;
++ obuf[2] = 1; // one byte of alarms data
++ obuf[3] = (alarms & 0x0D) | 0xF0; // turn on critical alarm
++ olen = 4;
++ rc = ipmi_send_recv(MASTER_WRITE_READ,NETFN_APP,0,
++ obuf,olen,ibuf,0,&i,&cc);
++ printk("ipmi_panic: set crit alarm rc=%d cc=%x\n",rc,cc);
++ if (rc == 0 && cc != 0) rc = cc;
++ return(rc);
++} /*end setAlarmLED*/
++
++static int insertPanicRecord(ulong event)
++{
++ unsigned char cc;
++ unsigned char obuf[16];
++ unsigned char ibuf[16];
++ int olen, rc, i;
++
++ /* Log the OS Critical Stop to the SEL (BMC firmware log). */
++ obuf[0] = 0x21; /* Kernel generator ID, IPMI table 5-4 */
++ obuf[1] = 0x03; /* 3=IPMI10, 4=IPMI15, set as back-compatible w 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. */
++ /*
++ * Most panics only have event codes == 0, so use panic_string.
++ * Start of panic string usu indicates module name.
++ */
++ obuf[3] = panic_string[0];
++ obuf[6] = panic_string[1];
++ obuf[7] = panic_string[2];
++ /*
++ * Add some bits to decode panic type
++ * String above is ASCII, so it will be betw 0x20 and 0x7f.
++ */
++ if (die_str != NULL && strncmp(die_str,"Oops",4) == 0)
++ obuf[3] |= 0x80; /* Is an Oops */
++ if (event == 1) obuf[6] |= 0x80; /* In interrupt handler */
++ if (die_err & 0x01) obuf[7] |= 0x80; /* Null ptr dereference */
++ olen = 8;
++ rc = ipmi_send_recv(PLATFORM_EVENT,NETFN_SENS_EVENT,0,
++ obuf,olen,ibuf,0,&i,&cc);
++ printk("bmc_panic: log OS Critical Stop rc=%d cc=%x, %c%c%c\n",
++ rc,cc, obuf[3],obuf[6],obuf[7]);
++ if (rc == 0 && cc != 0) rc = cc;
++ return(rc);
++} /*end insertPanicRecord()*/
++
++
++static int panic_event(struct notifier_block *this, unsigned long event,
++ void *ptr)
++{
++ unsigned char alarm;
++
++ if (has_paniced) return NOTIFY_DONE;
++ has_paniced = 1;
++
++#ifdef LINUX_DEBUG
++ SELprintf("panic_string(%p): %s\n",panic_string,panic_string);
++#endif
++
++ if (fIPMIok) {
++ insertPanicRecord(event);
++ alarm = getAlarmLED();
++ if (alarm != 0) // valid, ok to setAlarmLED
++ setAlarmLED(alarm);
++ }
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block panic_block = {
++ panic_event,
++ NULL,
++ 200 /* priority: INT_MAX >= x >= 0 */
++};
++
++/* Initialize the module - Try SEL routines */
++void init_SEL(void)
++{
++#ifdef LINUX_DEBUG
++ printk (KERN_INFO "init_SEL: Reading SEL info\n");
++#endif
++ if (ReadSELinfo() != 0) {
++ /* Nothing to do if the system can't do SEL functions. */
++ fIPMIok = 0;
++ printk(KERN_ERR "bmc_panic: IPMI failure. unregister device\n");
++ }
++ else {
++ fIPMIok = 1;
++ printk (KERN_INFO "Registering bmc_panic with panic notifier\n");
++ notifier_chain_register(&panic_notifier_list, &panic_block);
++ }
++ return ;
++}
++
++
++/* Cleanup - unregister the appropriate file from /proc */
++void cleanup_SEL(void)
++{
++ printk ( KERN_INFO "Unregistering with panic notifier\n");
++ notifier_chain_unregister(&panic_notifier_list, &panic_block);
++}
++
++#ifdef CONFIG_BMCPANIC_ACTION
++#define BMCPANIC_ACTION CONFIG_BMCPANIC_ACTION
++#else
++#define BMCPANIC_ACTION 0
++#endif
++void bmcpanic_action(void)
++{
++ int ret, i;
++ unsigned char obuf, cc;
++
++ if (fIPMIok) ret = BMCPANIC_ACTION;
++ else ret = 0; /* do machine_reset */
++ switch(ret)
++ {
++ case 1: /* power down */
++ obuf = 0;
++ ret = ipmi_send_recv(CHASSIS_CTL,NETFN_CHAS,0,&obuf,1,NULL,0,&i,&cc);
++ break;
++ case 2: /* power cycle */
++ obuf = 2;
++ ret = ipmi_send_recv(CHASSIS_CTL,NETFN_CHAS,0,&obuf,1,NULL,0,&i,&cc);
++ break;
++ case 3: /* hard reset */
++ obuf = 3; /* IPMI hard reset */
++ ret = ipmi_send_recv(CHASSIS_CTL,NETFN_CHAS,0,&obuf,1,NULL,0,&i,&cc);
++ break;
++ case 0: /* soft reset */
++ default:
++ machine_restart(NULL); /* normal Linux reset (arch/i386/) */
++ }
++}
++
++void bmc_poweroff(void)
++{
++ int ret, i;
++ unsigned char obuf, cc;
++
++ if (fIPMIok) {
++ obuf = 0;
++ ret = ipmi_send_recv(CHASSIS_CTL,NETFN_CHAS,0,&obuf,1,NULL,0,&i,&cc);
++ }
++}
++
++/* end bmc_selmsg.c */
+--- linux-2.4.19-orig/arch/i386/kernel/process.c Fri Aug 2 20:39:42 2002
++++ linux-2.4.19/arch/i386/kernel/process.c Thu Feb 27 15:07:25 2003
+@@ -426,10 +426,19 @@
+ {
+ }
+
++#ifdef CONFIG_BMCPANIC
++extern void bmc_poweroff(void);
++#endif
++
+ void machine_power_off(void)
+ {
+ if (pm_power_off)
+ pm_power_off();
++
++#ifdef CONFIG_BMCPANIC
++ bmc_poweroff();
++#endif
++
+ }
+
+ extern void show_trace(unsigned long* esp);
+--- linux-2.4.19-orig/arch/i386/kernel/traps.c Fri Aug 2 20:39:42 2002
++++ linux-2.4.19/arch/i386/kernel/traps.c Thu Feb 27 15:07:25 2003
+@@ -273,6 +273,8 @@
+ }
+
+ spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
++char die_str[64] = "";
++long die_err = 0;
+
+ void die(const char * str, struct pt_regs * regs, long err)
+ {
+@@ -281,6 +283,8 @@
+ bust_spinlocks(1);
+ handle_BUG(regs);
+ printk("%s: %04lx\n", str, err & 0xffff);
++ die_err = err;
++ strncpy(die_str,str,sizeof(die_str)-1);
+ show_registers(regs);
+ bust_spinlocks(0);
+ spin_unlock_irq(&die_lock);
+--- linux-2.4.19-orig/arch/i386/kernel/i386_ksyms.c Fri Aug 2 20:39:42 2002
++++ linux-2.4.19/arch/i386/kernel/i386_ksyms.c Thu Feb 27 15:07:25 2003
+@@ -31,6 +31,8 @@
+
+ extern void dump_thread(struct pt_regs *, struct user *);
+ extern spinlock_t rtc_lock;
++extern char *die_str;
++extern long die_err;
+
+ #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
+ extern void machine_real_restart(unsigned char *, int);
+@@ -141,6 +143,9 @@
+ EXPORT_SYMBOL(__global_save_flags);
+ EXPORT_SYMBOL(__global_restore_flags);
+ EXPORT_SYMBOL(smp_call_function);
++
++EXPORT_SYMBOL(die_str);
++EXPORT_SYMBOL(die_err);
+
+ /* TLB flushing */
+ EXPORT_SYMBOL(flush_tlb_page);