From a7f89980e5b3f4b9a74c70dbc5ffe8aabd28be28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 6 Jul 2014 18:04:32 +0200 Subject: Imported Upstream version 2.9.3 --- kern/alarms.h | 43 + kern/bmcpanic-2.4.18.patch | 997 +++++ kern/bmcpanic-2.4.19.patch | 997 +++++ kern/bmcpanic-2.4.20.patch | 1000 +++++ kern/bmcpanic-2.5.44.patch | 795 ++++ kern/bmcpanic-2.5.62.patch | 830 ++++ kern/bmcpanic-2.6.0.patch | 162 + kern/ipmi_kcs.patch | 1592 ++++++++ kern/linux-ipmi-2.4.20-v17.diff | 6647 +++++++++++++++++++++++++++++++ kern/linux-ipmi-2.5.44-v12.diff | 7362 +++++++++++++++++++++++++++++++++++ kern/linux-ipmi-emu-2.4.20-v17.diff | 2776 +++++++++++++ kern/openipmi-emu-rh80.patch | 1904 +++++++++ kern/openipmi-rh80.patch | 6633 +++++++++++++++++++++++++++++++ 13 files changed, 31738 insertions(+) create mode 100644 kern/alarms.h create mode 100644 kern/bmcpanic-2.4.18.patch create mode 100644 kern/bmcpanic-2.4.19.patch create mode 100644 kern/bmcpanic-2.4.20.patch create mode 100644 kern/bmcpanic-2.5.44.patch create mode 100644 kern/bmcpanic-2.5.62.patch create mode 100644 kern/bmcpanic-2.6.0.patch create mode 100644 kern/ipmi_kcs.patch create mode 100644 kern/linux-ipmi-2.4.20-v17.diff create mode 100644 kern/linux-ipmi-2.5.44-v12.diff create mode 100644 kern/linux-ipmi-emu-2.4.20-v17.diff create mode 100644 kern/openipmi-emu-rh80.patch create mode 100644 kern/openipmi-rh80.patch (limited to 'kern') diff --git a/kern/alarms.h b/kern/alarms.h new file mode 100644 index 0000000..c0ca46a --- /dev/null +++ b/kern/alarms.h @@ -0,0 +1,43 @@ +/* + * alarms.h + * + * Description of what the alarm panel bits mean. + * + * Note that bit = 0 means on, and bit = 1 means off for these LEDs + * (e.g. 0xFF is everything off). + * The low nibble (bits 0x0F) is for LEDs, and the high nibble (bits 0x30) is + * for the relays. The relays are triggered from the LED settings so the high + * nibble doesn't really matter when setting alarms (usu 0xF* in TAM though). + * + * The TAM API is available with the TAM rpm, for your convenience. + * See the bmcpanic.patch for an example of how to set an LED, + * at http://panicsel.sourceforge.net + */ + +// +// masks used to set LED states +// +#define AP_SET_NO_LED_MASK 0x0F // bits 0-3 = 1 +#define AP_SET_POWER_MASK 0x0E // bit0 = 0, others = 1 +#define AP_SET_CRITICAL_MASK 0x0D // bit1 = 0 +#define AP_SET_MAJOR_MASK 0x0B // bit2 = 0 +#define AP_SET_MINOR_MASK 0x07 // bit3 = 0 + +// +// masks used to get LED states +// +#define AP_NO_LED_SET 0x0E +#define AP_POWER_SET 0x01 +#define AP_CRITICAL_SET 0x0C +#define AP_MAJOR_SET 0x0A +#define AP_MINOR_SET 0x06 + +// +// masks used to get relay state +// +#define AP_ALL_RELAY_MASK 0x00 +#define AP_MINOR_RELAY_MASK 0x10 +#define AP_MAJOR_RELAY_MASK 0x20 +#define AP_NO_RELAY_MASK 0x30 + +/* end alarms.h */ diff --git a/kern/bmcpanic-2.4.18.patch b/kern/bmcpanic-2.4.18.patch new file mode 100644 index 0000000..9eae959 --- /dev/null +++ b/kern/bmcpanic-2.4.18.patch @@ -0,0 +1,997 @@ +--- linux-2.4.18-orig/drivers/char/misc.c Fri Nov 2 20:46:47 2001 ++++ linux-2.4.18/drivers/char/misc.c Thu Feb 27 14:28:39 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); +@@ -264,6 +265,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.18-orig/kernel/panic.c Sun Sep 30 15:26:08 2001 ++++ linux-2.4.18/kernel/panic.c Thu Feb 27 14:28:39 2003 +@@ -17,6 +17,10 @@ + #include + #include + ++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.18-orig/kernel/ksyms.c Mon Feb 25 14:38:13 2002 ++++ linux-2.4.18/kernel/ksyms.c Thu Feb 27 14:29:30 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); +@@ -559,3 +560,5 @@ + + EXPORT_SYMBOL(tasklist_lock); + EXPORT_SYMBOL(pidhash); ++EXPORT_SYMBOL(panic_notifier_list); ++EXPORT_SYMBOL(panic_string); +--- linux-2.4.18-orig/Documentation/Configure.help Mon Feb 25 14:37:51 2002 ++++ linux-2.4.18/Documentation/Configure.help Thu Feb 27 14:28:39 2003 +@@ -16277,6 +16277,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.18-orig/drivers/char/Makefile Mon Feb 25 14:37:57 2002 ++++ linux-2.4.18/drivers/char/Makefile Thu Feb 27 14:28:39 2003 +@@ -209,6 +209,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 +@@ -252,6 +255,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.18-orig/drivers/char/Config.in Mon Feb 25 14:37:57 2002 ++++ linux-2.4.18/drivers/char/Config.in Thu Feb 27 14:28:39 2003 +@@ -97,6 +97,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.18/drivers/char/bmc_ipmi.c Thu Feb 27 14:28:39 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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.18/drivers/char/bmc_selmsg.c Thu Feb 27 14:28:39 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 ++#include ++#include /* 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.18-orig/arch/i386/kernel/process.c Mon Feb 25 14:37:53 2002 ++++ linux-2.4.18/arch/i386/kernel/process.c Thu Feb 27 14:28:39 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.18-orig/arch/i386/kernel/traps.c Sun Sep 30 15:26:08 2001 ++++ linux-2.4.18/arch/i386/kernel/traps.c Thu Feb 27 14:28:39 2003 +@@ -238,6 +238,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) + { +@@ -245,6 +247,8 @@ + spin_lock_irq(&die_lock); + bust_spinlocks(1); + 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.18-orig/arch/i386/kernel/i386_ksyms.c Mon Feb 25 14:37:53 2002 ++++ linux-2.4.18/arch/i386/kernel/i386_ksyms.c Thu Feb 27 14:28:39 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); +@@ -140,6 +142,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); 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 + #include + ++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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include /* 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); diff --git a/kern/bmcpanic-2.4.20.patch b/kern/bmcpanic-2.4.20.patch new file mode 100644 index 0000000..11caf32 --- /dev/null +++ b/kern/bmcpanic-2.4.20.patch @@ -0,0 +1,1000 @@ +--- linux-2.4.20-orig/drivers/char/misc.c Fri Aug 2 20:39:43 2002 ++++ linux-2.4.20/drivers/char/misc.c Wed Feb 26 16:33:48 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.20-orig/kernel/panic.c Thu Nov 28 18:53:15 2002 ++++ linux-2.4.20/kernel/panic.c Wed Feb 26 14:26:36 2003 +@@ -17,6 +17,10 @@ + #include + #include + ++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.20-orig/kernel/ksyms.c Thu Nov 28 18:53:15 2002 ++++ linux-2.4.20/kernel/ksyms.c Wed Feb 26 14:27:26 2003 +@@ -61,6 +61,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); +@@ -576,6 +577,8 @@ + + EXPORT_SYMBOL(tasklist_lock); + EXPORT_SYMBOL(pidhash); ++EXPORT_SYMBOL(panic_notifier_list); ++EXPORT_SYMBOL(panic_string); + + /* debug */ + EXPORT_SYMBOL(dump_stack); +--- linux-2.4.20-orig/Documentation/Configure.help Thu Nov 28 18:53:08 2002 ++++ linux-2.4.20/Documentation/Configure.help Wed Feb 26 14:26:36 2003 +@@ -17414,6 +17414,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.20-orig/drivers/char/Makefile Thu Nov 28 18:53:12 2002 ++++ linux-2.4.20/drivers/char/Makefile Wed Feb 26 14:26:36 2003 +@@ -249,6 +249,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 +@@ -300,6 +303,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.20-orig/drivers/char/Config.in Thu Nov 28 18:53:12 2002 ++++ linux-2.4.20/drivers/char/Config.in Wed Feb 26 14:26:36 2003 +@@ -141,6 +141,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.20/drivers/char/bmc_ipmi.c Thu Feb 27 11:54:39 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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.20/drivers/char/bmc_selmsg.c Thu Feb 27 11:58:31 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 ++#include ++#include /* 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.20-orig/arch/i386/kernel/process.c Fri Aug 2 20:39:42 2002 ++++ linux-2.4.20/arch/i386/kernel/process.c Wed Feb 26 14:26:36 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.20-orig/arch/i386/kernel/traps.c Thu Nov 28 18:53:09 2002 ++++ linux-2.4.20/arch/i386/kernel/traps.c Wed Feb 26 15:49:54 2003 +@@ -281,6 +281,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) + { +@@ -289,6 +291,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.20-orig/arch/i386/kernel/i386_ksyms.c Fri Aug 2 20:39:42 2002 ++++ linux-2.4.20/arch/i386/kernel/i386_ksyms.c Wed Feb 26 16:01:01 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); diff --git a/kern/bmcpanic-2.5.44.patch b/kern/bmcpanic-2.5.44.patch new file mode 100644 index 0000000..eb0e1d0 --- /dev/null +++ b/kern/bmcpanic-2.5.44.patch @@ -0,0 +1,795 @@ +--- linux-2.5.44-orig/kernel/panic.c Sat Oct 19 00:02:32 2002 ++++ linux-2.5.44/kernel/panic.c Fri Jan 31 13:03:55 2003 +@@ -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__ + { +--- linux-2.5.44-orig/kernel/ksyms.c Sat Oct 19 00:01:08 2002 ++++ linux-2.5.44/kernel/ksyms.c Wed Jan 29 10:09:26 2003 +@@ -63,6 +63,8 @@ + #endif + + extern void set_device_ro(kdev_t dev,int flag); ++extern int panic_timeout; ++extern char *panic_string; + + extern struct timezone sys_tz; + +@@ -601,3 +603,6 @@ + + /* debug */ + EXPORT_SYMBOL(dump_stack); ++EXPORT_SYMBOL(panic_notifier_list); ++EXPORT_SYMBOL(panic_timeout); ++EXPORT_SYMBOL(panic_string); +--- linux-2.5.44-orig/drivers/char/Config.in Thu Jan 30 12:59:15 2003 ++++ linux-2.5.44/drivers/char/Config.in Thu Jan 30 12:55:53 2003 +@@ -110,6 +110,9 @@ + dep_tristate ' Device interface for IPMI' CONFIG_IPMI_DEVICE_INTERFACE $CONFIG_IPMI_HANDLER + dep_tristate ' IPMI KCS handler' CONFIG_IPMI_KCS $CONFIG_IPMI_HANDLER + dep_tristate ' IPMI Watchdog Timer' CONFIG_IPMI_WATCHDOG $CONFIG_IPMI_HANDLER ++if [ "$CONFIG_IPMI_HANDLER" == "y" ]; then ++ int ' Action after Panic (0=reset,1=power down,2=power cycle)' CONFIG_IPMI_PANIC_ACTION 0 ++fi + + mainmenu_option next_comment + comment 'Watchdog Cards' +--- linux-2.5.44-orig/drivers/char/ipmi/Makefile Thu Jan 30 12:59:15 2003 ++++ linux-2.5.44/drivers/char/ipmi/Makefile Fri Jan 31 11:40:33 2003 +@@ -6,7 +6,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 +--- linux-2.5.44-orig/drivers/char/ipmi/ipmi_msghandler.c Thu Jan 30 12:59:15 2003 ++++ linux-2.5.44/drivers/char/ipmi/ipmi_msghandler.c Tue Feb 4 10:46:51 2003 +@@ -44,6 +44,14 @@ + #include + #include + ++#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); + +@@ -369,6 +377,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, +@@ -413,6 +422,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]; +@@ -1618,8 +1628,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) + { + } +@@ -1652,12 +1667,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; +@@ -1681,6 +1693,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; +@@ -1699,14 +1735,55 @@ + /* For every registered interface, set it to run to completion. */ + for (i=0; ihandlers->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; +@@ -1738,6 +1815,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"); +@@ -1795,3 +1875,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 +--- /dev/null Fri Mar 23 23:37:44 2001 ++++ linux-2.5.44/drivers/char/ipmi/ipmi_panic.c Tue Feb 4 10:50:45 2003 +@@ -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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 */ +--- linux-2.5.44-orig/arch/i386/kernel/reboot.c Sat Oct 19 00:01:20 2002 ++++ linux-2.5.44/arch/i386/kernel/reboot.c Thu Jan 30 15:45:32 2003 +@@ -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 --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 + #include + ++#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; ihandlers->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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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__ + { diff --git a/kern/bmcpanic-2.6.0.patch b/kern/bmcpanic-2.6.0.patch new file mode 100644 index 0000000..da24915 --- /dev/null +++ b/kern/bmcpanic-2.6.0.patch @@ -0,0 +1,162 @@ +--- linux-2.6.0-test3orig/drivers/char/ipmi/ipmi_msghandler.c 2003-08-09 00:37:25.000000000 -0400 ++++ linux-2.6.0-test3/drivers/char/ipmi/ipmi_msghandler.c 2003-08-14 09:40:49.000000000 -0400 +@@ -1813,7 +1813,9 @@ + { + } + +-static void send_panic_events(void) ++#define EVENT_VALID 0x80 ++ ++static void send_panic_events(int evt, char *str) + { + struct ipmi_msg msg; + ipmi_smi_t intf; +@@ -1837,12 +1839,17 @@ + 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; ++ /* ++ * Add whatever breadcrumbs we can to indicate the type of panic. ++ * In kernel 2.6.0 an event code and panic string are passed in. ++ */ ++ if (evt != 0) ++ data[7] = EVENT_VALID | (evt & 0x7f); ++ if (str != NULL) { ++ data[3] = str[0]; ++ data[6] = str[1]; ++ if (evt == 0) data[7] = str[2]; ++ } + + smi_msg.done = dummy_smi_done_handler; + recv_msg.done = dummy_recv_done_handler; +@@ -1891,7 +1898,7 @@ + } + + #ifdef CONFIG_IPMI_PANIC_EVENT +- send_panic_events(); ++ send_panic_events(event,(char *)ptr); + #endif + + return NOTIFY_DONE; +--- linux-2.6.0-test3orig/arch/i386/kernel/traps.c 2003-08-09 00:33:15.000000000 -0400 ++++ linux-2.6.0-test3/arch/i386/kernel/traps.c 2003-08-13 17:37:50.789828552 -0400 +@@ -288,6 +288,8 @@ + return address; + } + ++extern int last_trapnr; ++ + static inline void do_trap(int trapnr, int signr, char *str, int vm86, + struct pt_regs * regs, long error_code, siginfo_t *info) + { +@@ -312,8 +314,10 @@ + } + + kernel_trap: { +- if (!fixup_exception(regs)) ++ if (!fixup_exception(regs)) { ++ last_trapnr = trapnr; + die(str, regs, error_code); ++ } + return; + } + +@@ -387,8 +391,10 @@ + return; + + gp_in_kernel: +- if (!fixup_exception(regs)) ++ if (!fixup_exception(regs)) { ++ last_trapnr = 13; + die("general protection fault", regs, error_code); ++ } + } + + static void mem_parity_error(unsigned char reason, struct pt_regs * regs) +@@ -721,6 +727,7 @@ + error_code); + return; + } ++ last_trapnr = 19; + die_if_kernel("cache flush denied", regs, error_code); + current->thread.trap_no = 19; + current->thread.error_code = error_code; +--- linux-2.6.0-test3orig/kernel/ksyms.c 2003-08-09 00:31:15.000000000 -0400 ++++ linux-2.6.0-test3/kernel/ksyms.c 2003-08-13 17:39:00.000000000 -0400 +@@ -71,6 +71,7 @@ + extern struct timezone sys_tz; + + extern int panic_timeout; ++extern int last_trapnr; + + /* process memory management */ + EXPORT_SYMBOL(do_mmap_pgoff); +@@ -504,6 +505,7 @@ + EXPORT_SYMBOL(panic); + EXPORT_SYMBOL(panic_notifier_list); + EXPORT_SYMBOL(panic_timeout); ++EXPORT_SYMBOL(last_trapnr); + EXPORT_SYMBOL(sprintf); + EXPORT_SYMBOL(snprintf); + EXPORT_SYMBOL(sscanf); +--- linux-2.6.0-test3orig/kernel/panic.c 2003-08-09 00:41:41.000000000 -0400 ++++ linux-2.6.0-test3/kernel/panic.c 2003-08-13 17:38:49.000000000 -0400 +@@ -20,9 +20,15 @@ + + asmlinkage void sys_sync(void); /* it's really int */ + ++/* values for panic event */ ++#define EVENT_VALID 0x80 ++#define PANIC_IN_INTERRUPT 0x40 ++#define TRAP_INIT 0x80 ++ + int panic_timeout; + int panic_on_oops; + int tainted; ++int last_trapnr = TRAP_INIT; + + struct notifier_block *panic_notifier_list; + +@@ -47,6 +53,7 @@ + { + static char buf[1024]; + va_list args; ++ int event = 0; + #if defined(CONFIG_ARCH_S390) + unsigned long caller = (unsigned long) __builtin_return_address(0); + #endif +@@ -56,11 +63,16 @@ + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + printk(KERN_EMERG "Kernel panic: %s\n",buf); +- if (in_interrupt()) ++ if (last_trapnr != TRAP_INIT) { ++ /* should be <= 30 trapnr values, so packing in 0x3f is ok */ ++ event = EVENT_VALID + (last_trapnr & 0x3f); ++ } ++ if (in_interrupt()) { ++ event |= PANIC_IN_INTERRUPT; /*0x40*/ + printk(KERN_EMERG "In interrupt handler - not syncing\n"); +- else if (!current->pid) ++ } else if (!current->pid) + printk(KERN_EMERG "In idle task - not syncing\n"); +- else ++ else + sys_sync(); + bust_spinlocks(0); + +@@ -68,7 +80,7 @@ + smp_send_stop(); + #endif + +- notifier_call_chain(&panic_notifier_list, 0, buf); ++ notifier_call_chain(&panic_notifier_list, event, buf); + + if (panic_timeout > 0) + { diff --git a/kern/ipmi_kcs.patch b/kern/ipmi_kcs.patch new file mode 100644 index 0000000..15f9a10 --- /dev/null +++ b/kern/ipmi_kcs.patch @@ -0,0 +1,1592 @@ +--- linux-2.4.2-virgin/drivers/char/misc.c Sun Apr 8 18:22:17 2001 ++++ linux-bmc/drivers/char/misc.c Tue Oct 30 17:18:07 2001 +@@ -273,6 +273,9 @@ + #ifdef CONFIG_TOSHIBA + tosh_init(); + #endif ++#ifdef CONFIG_IPMI_KCS ++ ipmi_kcs_init(); ++#endif + if (devfs_register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { + printk("unable to get major %d for misc devices\n", + MISC_MAJOR); +--- linux-2.4.2-virgin/drivers/char/Config.in Sun Apr 8 18:22:22 2001 ++++ linux-bmc/drivers/char/Config.in Tue Oct 30 17:18:07 2001 +@@ -81,6 +81,11 @@ + dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT + fi + ++tristate 'IPMI KCS Interface' CONFIG_IPMI_KCS ++if [ "$CONFIG_IPMI_KCS" != "n" ]; then ++ bool ' BMC watchdog timer support' CONFIG_BMC_WDT ++fi ++ + source drivers/i2c/Config.in + + source drivers/sensors/Config.in +--- linux-2.4.2-virgin/drivers/char/Makefile Sun Apr 8 18:22:17 2001 ++++ linux-bmc/drivers/char/Makefile Tue Oct 30 17:18:08 2001 +@@ -191,6 +191,8 @@ + obj-$(CONFIG_NWBUTTON) += nwbutton.o + obj-$(CONFIG_NWFLASH) += nwflash.o + ++obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o ++ + # Only one watchdog can succeed. We probe the hardware watchdog + # drivers first, then the softdog driver. This means if your hardware + # watchdog dies or is 'borrowed' for some reason the software watchdog +--- linux-2.4.2-virgin/include/linux/miscdevice.h Sun Apr 8 18:47:26 2001 ++++ linux-bmc/include/linux/miscdevice.h Tue Oct 30 17:32:42 2001 +@@ -31,6 +31,9 @@ + #define SGI_STREAMS_KEYBOARD 150 + /* drivers/sgi/char/usema.c */ + #define SGI_USEMACLONE 151 ++#define IPMI_KCS_MINOR 173 ++#define IPMI_BT_MINOR 210 ++#define IPMI_SMIC_MINOR 211 + + #define TUN_MINOR 200 + +--- /dev/null Fri Mar 23 23:37:44 2001 ++++ linux-bmc/drivers/char/ipmi_kcs.c Tue Oct 30 17:18:07 2001 +@@ -0,0 +1,1211 @@ ++/* ++ * Intelligent Platform Management Interface driver for Linux 2.2.x ++ * ++ * (c) Copyright 2000 San Mehat , All Rights Reserved. ++ * http://www.valinux.com ++ * ++ * 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. ++ * ++ * Neither San Mehat nor VA Linux Systems admit liability nor provide ++ * warranty for any of this software. This material is provided ++ * "AS-IS" and at no charge. ++ * ++ * (c) Copyright 1999 San Mehat ++ * ++ * Release 0.04. - Initial Release ++ * ++ * Release 0.05. - Fixed ring buffer bugs... better buffer handling ++ * ++ * Release 0.06. - Changed polling freq to 1/10 sec ++ * ++ * Release 0.07. - Integrated watchdog commands into IOCTL's and added ++ * support for blinking front panel LED ++ * ++ * Release 0.08. - Sensor read commands added as ioctl ++ * ++ * Release 0.09. - Changed polling freq back to 1 second ++ * - Fixed possible bug where a chip status variable was ++ * not declared volatile. ++ * - Fixed buffer memory leak ++ * - Fixed ioctl return value problem ++ * - Changed architecture so that applications calling ++ * driver ioctl()'s are put to sleep after request ++ * is sent. The reply is handled by the normal ++ * driver polling timer queue and ring buffer ++ * ++ * Release 0.10. - Modified kcs_write routine so once a write is complete ++ * if the interface isn't in a 'READ STATE' it's okay. ++ * ++ * Release 0.12. - Added Intel Nightshade MB fixups since NS boards don't ++ * support pre-timeout NMI support ++ * - FRU download support added ++ * - /proc/ipmi created with fru data and driver status ++ * Release 0.13. - Added ioctl for setting asset tag ++ * - Fixed bug in /proc ++ * - Added asset tag max length field ++ * Release 1.00 - Added intelligent proc reading so that asset tag is ++ * refreshed whenever /proc/ipmi is read ++ * - Code cleanup ++ * - When asset tag is set with data whoes size is < maximum, ++ * pad the rest out with NULLs ++ * Release 1.10 - Fixed SMP bug which was causing command failures when ++ * /proc/ipmi was being read while a command was being ++ * executed (added semaphore) ++ * - Fixed /proc/ipmi so commands only get issued once ++ * Release 1.20 - Removed a bunch of useless warning msgs ++ * Release 1.30 - Added more stringent error checking when ISA state ++ * enters ERROR_STATE ++ * - Added better unexpected OBF handling on transactions ++ * - Explicitly set power sensor state to NO BLINKY on ++ * startup ++ * Release 2.0 - Re-wrote kcs state machine ++ * - Removed high level functions from driver ++ * - removed driver read() and write() capabilities ++ * - /proc/ipmi renamed to /proc/ipmi_kcs ++ * - /proc/ipmi_kcs now contains only BMC info ++ * ++ * Release 2.1 - Added support of watchdog timer through /dev/watchdog ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ipmi_kcs.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PROC_FS ++#include ++#endif ++ ++#ifdef CONFIG_BMC_WDT ++#include ++#endif ++ ++/* function prototypes */ ++ ++int ipmi_kcs_init(void); ++ ++static int kcs_open(struct inode *inode, struct file *file); ++static int kcs_release(struct inode *inode, struct file *file); ++static ssize_t kcs_read(struct file *file, char *buf, size_t count, ++ loff_t * ptr); ++static ssize_t kcs_write(struct file *file, const char *buf, size_t count, ++ loff_t * ppos); ++static long long kcs_llseek(struct file *file, long long offset, ++ int origin); ++static int kcs_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++ ++#ifdef CONFIG_BMC_WDT ++static int wdt_is_open; ++static int watchdog_set(int cmd); ++static int watchdog_reset(void); ++static int wdt_open(struct inode *inode, struct file *file); ++static int wdt_release(struct inode *inode, struct file *file); ++static ssize_t wdt_write(struct file *file, const char *buf, size_t count, ++ loff_t * ppos); ++static int wdt_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++ ++static struct file_operations wdt_fops = { ++ owner:THIS_MODULE, ++ write:wdt_write, ++ ioctl:wdt_ioctl, ++ open:wdt_open, ++ release:wdt_release, ++}; ++ ++static struct miscdevice wdt_miscdev = { ++ WATCHDOG_MINOR, ++ "bmc_wdt", ++ &wdt_fops ++}; ++ ++/* module parameters */ ++static int wdt_margin = 60; /* watchdog timer interval in seconds */ ++static int wdt_pre = 1; /* watchdog pre-timeout interval in seconds */ ++static int wdt_action = 0x03; /* Default is no pre-timeout interrupt and power cycle upon timeout */ ++ ++MODULE_PARM(wdt_margin, "i"); ++MODULE_PARM(wdt_pre, "i"); ++MODULE_PARM(wdt_action, "i"); ++ ++#define WATCHDOG_ENABLE 0x01 ++#define WATCHDOG_DISABLE 0x02 ++#endif ++ ++static unsigned char get_kcs_state(void); ++static unsigned char read_kcs_data(void); ++static void write_kcs_data(unsigned char data); ++static void write_kcs_cmd(unsigned char cmd); ++static int is_obf_set(void); ++static int clear_obf(void); ++static int wait_while_ibf(void); ++static int get_deviceid(void); ++static int kcs_do_xfer(BMC_REQUEST * request, int request_len, ++ BMC_RESPONSE * response, int *response_len); ++static int old_kcs_do_xfer(BMC_REQUEST * request, int request_len, ++ BMC_RESPONSE * response, int *response_len); ++static int new_kcs_do_xfer(BMC_REQUEST * request, int request_len, ++ BMC_RESPONSE * response, int *response_len); ++ ++#ifdef CONFIG_PROC_FS ++int ipmi_get_info(char *buf, char **start, off_t fpos, int length, ++ int *eof, void *unused); ++#endif ++ ++/* static globals */ ++static int kcs_refcnt = 0; ++static int driver_major = 2; ++static int driver_minor = 1; ++static int kcs_machine = 0; ++ ++static struct { ++ unsigned int tx_good; ++ unsigned int tx_bad; ++} kcs_stat; ++ ++static DEVICE_ID_RESPONSE dev_id; ++DECLARE_MUTEX(kcs_sem); ++ ++#ifdef CONFIG_PROC_FS ++static struct proc_dir_entry *ipmi_proc_entry; ++#endif ++ ++static struct file_operations kcs_fops = { ++ owner:THIS_MODULE, ++ write:kcs_write, ++ ioctl:kcs_ioctl, ++ open:kcs_open, ++ release:kcs_release, ++ read:kcs_read, ++}; ++ ++static struct miscdevice kcs_miscdev = { ++ IPMI_KCS_MINOR, ++ "ipmi_kcs", ++ &kcs_fops ++}; ++ ++static struct ipmi_driver_info drv_inf; ++ ++/***************/ ++ ++static long long kcs_llseek(struct file *file, long long offset, ++ int origin) ++{ ++ return -ESPIPE; ++} ++ ++static ssize_t kcs_write(struct file *file, const char *buf, size_t count, ++ loff_t * ppos) ++{ ++ return (-ENOSYS); ++} ++ ++static ssize_t kcs_read(struct file *file, char *buf, size_t count, ++ loff_t * ptr) ++{ ++ return (-ENOSYS); ++} ++ ++static int kcs_ioctl(struct inode *inode, ++ struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case IOCTL_IPMI_XFER: ++ { ++ IPMI_XFER user_buffer; ++ int rc; ++ int response_len = sizeof(user_buffer.response); ++ ++ if (!arg) ++ return (-EFAULT); ++ if (copy_from_user ++ ((void *) &user_buffer, (void *) arg, ++ sizeof(IPMI_XFER))) ++ return (-EFAULT); ++ if ((user_buffer.request_len < 2) ++ || (user_buffer.response_len < 3)) ++ return (-EINVAL); ++ ++ rc = kcs_do_xfer((BMC_REQUEST *) & user_buffer. ++ request, user_buffer.request_len, ++ (BMC_RESPONSE *) & user_buffer. ++ response, &response_len); ++ user_buffer.response_len = response_len; ++ copy_to_user((void *) arg, (void *) &user_buffer, ++ sizeof(IPMI_XFER)); ++ return (rc); ++ } ++ case IOCTL_DRIVER_INFO: ++ { ++ struct ipmi_driver_info user_buffer; ++ ++ if (!arg) ++ return (-EFAULT); ++ if (copy_from_user ++ ((void *) &user_buffer, (void *) arg, ++ sizeof(user_buffer))) ++ return (-EFAULT); ++ copy_to_user((void *) arg, (void *) &drv_inf, ++ sizeof(drv_inf)); ++ return (0); ++ } ++ default: ++ return -EINVAL; ++ } ++ return (0); ++} ++ ++static int get_deviceid() ++{ ++ unsigned char request_buffer[MAX_BUFFER_SIZE]; ++ unsigned char response_buffer[MAX_BUFFER_SIZE]; ++ BMC_REQUEST *bmc_req; ++ BMC_RESPONSE *bmc_resp; ++ int rc, response_len; ++ ++ memset(&dev_id, 0, sizeof(DEVICE_ID_RESPONSE)); ++ memset(request_buffer, 0, sizeof(request_buffer)); ++ memset(response_buffer, 0, sizeof(response_buffer)); ++ bmc_req = (BMC_REQUEST *) request_buffer; ++ bmc_resp = (BMC_RESPONSE *) response_buffer; ++ response_len = sizeof(response_buffer); ++ ++ bmc_req->lun = 0; ++ bmc_req->netfn = APP_REQUEST; ++ bmc_req->cmd = 0x01; /* GET_DEVICE_ID */ ++ ++#if defined(__ia64__) ++ rc = new_kcs_do_xfer(bmc_req, 2, bmc_resp, &response_len); ++#else ++ rc = old_kcs_do_xfer(bmc_req, 2, bmc_resp, &response_len); ++#endif ++ if (bmc_resp->cc != 0x00) { ++ printk("[IPMI_KCS] get_deviceid() failed (0x%.2x)\n", ++ bmc_resp->cc); ++ return (-EIO); ++ } ++ memcpy(&dev_id, bmc_resp->data, sizeof(DEVICE_ID_RESPONSE)); ++ return (0); ++} ++ ++static int kcs_open(struct inode *inode, struct file *file) ++{ ++ switch (MINOR(inode->i_rdev)) { ++ case IPMI_KCS_MINOR: ++ { ++ MOD_INC_USE_COUNT; ++ kcs_refcnt++; ++ return 0; ++ } ++ default: ++ return -ENODEV; ++ } ++} ++ ++static int kcs_release(struct inode *inode, struct file *file) ++{ ++ if (MINOR(inode->i_rdev) == IPMI_KCS_MINOR) { ++ kcs_refcnt--; ++ } ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++#ifdef CONFIG_BMC_WDT ++static ssize_t wdt_write(struct file *file, const char *buf, size_t count, ++ loff_t * ppos) ++{ ++ /* Can't seek (pwrite) on this device */ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ ++ /* ++ * Stop and then restart the watchdog timer. ++ */ ++ if (count) { ++ if (watchdog_set(WATCHDOG_DISABLE)) ++ return -EIO; ++ if (watchdog_set(WATCHDOG_ENABLE)) ++ return -EIO; ++ if (watchdog_reset()) ++ return -EIO; ++ return 1; ++ } ++ return 0; ++} ++ ++static int wdt_ioctl(struct inode *inode, ++ struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ static struct watchdog_info ident = { ++ WDIOF_KEEPALIVEPING, 1, "BMC WDT" ++ }; ++ ++ switch (cmd) { ++ case WDIOC_GETSUPPORT: ++ if (copy_to_user ++ ((struct watchdog_info *) arg, &ident, sizeof(ident))) ++ return -EFAULT; ++ break; ++ ++ case WDIOC_GETSTATUS: ++ if (copy_to_user((int *) arg, &wdt_is_open, sizeof(int))) ++ return -EFAULT; ++ break; ++ ++ case WDIOC_KEEPALIVE: ++ if (watchdog_set(WATCHDOG_DISABLE)) ++ return -EIO; ++ if (watchdog_set(WATCHDOG_ENABLE)) ++ return -EIO; ++ if (watchdog_reset()) ++ return -EIO; ++ break; ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++ return 0; ++} ++ ++static int watchdog_set(int cmd) ++{ ++ unsigned char request_buffer[MAX_BUFFER_SIZE]; ++ unsigned char response_buffer[MAX_BUFFER_SIZE]; ++ BMC_REQUEST *bmc_req; ++ BMC_RESPONSE *bmc_resp; ++ int rc, response_len; ++ SET_WATCHDOG *set; ++ int fixup = 0; ++ ++ memset(request_buffer, 0, sizeof(request_buffer)); ++ memset(response_buffer, 0, sizeof(response_buffer)); ++ bmc_req = (BMC_REQUEST *) request_buffer; ++ bmc_resp = (BMC_RESPONSE *) response_buffer; ++ response_len = sizeof(response_buffer); ++ ++ bmc_req->lun = 0; ++ bmc_req->netfn = APP_REQUEST; ++ bmc_req->cmd = 0x24; /* Set Watchdog Timer */ ++ ++ set = (SET_WATCHDOG *) bmc_req->data; ++ ++ while (1) { ++ set->timer_use = 0x04; ++ ++ set->timeout_action = wdt_action & 0x0F; ++ set->pre_irq = (wdt_action >> 4) & 0x0F; ++ ++ if (cmd == WATCHDOG_DISABLE) { ++ set->timeout_action = 0x00; ++ set->pre_irq = 0x00; ++ } ++ set->pretimeout_interval = wdt_pre; ++ ++ if (fixup) { ++ set->pre_irq = 0x00; ++ set->pretimeout_interval = 0; ++ } ++ ++ set->tuefc_biosfrb2 = 0x00; ++ set->tuefc_biospost = 0x0; ++ set->tuefc_osload = 0x00; ++ set->tuefc_smsos = 0x01; ++ set->initial_count = wdt_margin * 10; ++ ++ rc = kcs_do_xfer(bmc_req, 2 + sizeof(SET_WATCHDOG), ++ bmc_resp, &response_len); ++ ++ if (bmc_resp->cc == 0xcc) { ++ fixup++; ++ if (fixup == 2) { ++ printk ++ ("[IPMI_KCS] Flakey NMI fixup failed\n"); ++ return (-EIO); ++ } ++ printk("[IPMI_KCS] Flakey NMI fixup enabled\n"); ++ continue; ++ } else if (bmc_resp->cc != 0x00) { ++ printk ++ ("[IPMI_KCS] Set watchdog timer failed (rc = 0x%.2x)\n", ++ bmc_resp->cc); ++ return (-EIO); ++ } ++ break; ++ } ++ return (0); ++} ++ ++static int watchdog_reset() ++{ ++ unsigned char request_buffer[MAX_BUFFER_SIZE]; ++ unsigned char response_buffer[MAX_BUFFER_SIZE]; ++ BMC_REQUEST *bmc_req; ++ BMC_RESPONSE *bmc_resp; ++ int rc, response_len; ++ ++ memset(request_buffer, 0, sizeof(request_buffer)); ++ memset(response_buffer, 0, sizeof(response_buffer)); ++ bmc_req = (BMC_REQUEST *) request_buffer; ++ bmc_resp = (BMC_RESPONSE *) response_buffer; ++ response_len = sizeof(response_buffer); ++ ++ bmc_req->lun = 0; ++ bmc_req->netfn = APP_REQUEST; ++ bmc_req->cmd = 0x22; /* Reset Watchdog Timer */ ++ ++ rc = kcs_do_xfer(bmc_req, 2, bmc_resp, &response_len); ++ if (bmc_resp->cc != 0x00) { ++ printk("[IPMI_KCS] Reset Watchdog Timer failed (0x%.2x)\n", ++ bmc_resp->cc); ++ return (-EIO); ++ } ++ return (0); ++} ++ ++static int wdt_open(struct inode *inode, struct file *file) ++{ ++ switch (MINOR(inode->i_rdev)) { ++ case WATCHDOG_MINOR: ++ if (wdt_is_open) ++ return -EBUSY; ++ ++ MOD_INC_USE_COUNT; ++ /* ++ * Activate ++ */ ++ wdt_is_open = 1; ++ if (watchdog_reset()) ++ return -EIO; ++ return 0; ++ default: ++ return -ENODEV; ++ } ++} ++ ++static int wdt_release(struct inode *inode, struct file *file) ++{ ++ lock_kernel(); ++ if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { ++#ifndef CONFIG_WATCHDOG_NOWAYOUT ++ if (watchdog_set(WATCHDOG_DISABLE)) ++ return -EIO; ++#endif ++ wdt_is_open = 0; ++ MOD_DEC_USE_COUNT; ++ } ++ unlock_kernel(); ++ return 0; ++} ++ ++/* ++ * Notifier for system down ++ */ ++ ++static int wdt_notify_sys(struct notifier_block *this, unsigned long code, ++ void *unused) ++{ ++ if (code == SYS_DOWN || code == SYS_HALT) { ++ /* Turn the timer off */ ++ watchdog_set(WATCHDOG_DISABLE); ++ } ++ return NOTIFY_DONE; ++} ++ ++/* ++ * The BMC needs to learn about soft shutdowns in order to ++ * turn the watchdog timer off. ++ */ ++ ++static struct notifier_block wdt_notifier = { ++ wdt_notify_sys, ++ NULL, ++ 0 ++}; ++#endif ++ ++#ifdef MODULE ++int init_module(void) ++{ ++ return (ipmi_kcs_init()); ++} ++ ++void cleanup_module(void) ++{ ++ printk("[IPMI_KCS] Driver shutting down.\n"); ++#ifdef CONFIG_PROC_FS ++ remove_proc_entry("ipmi_kcs", 0); ++#endif ++ misc_deregister(&kcs_miscdev); ++#ifdef CONFIG_BMC_WDT ++ misc_deregister(&wdt_miscdev); ++ unregister_reboot_notifier(&wdt_notifier); ++#endif ++ release_region(KCS_BASE, 16); ++} ++#endif ++ ++int ipmi_kcs_init() ++{ ++ printk ++ ("IPMI KCS driver (San Mehat nettwerk@valinux.com) v%d.%d at io 0x%x\n", ++ driver_major, driver_minor, KCS_BASE); ++ request_region(KCS_BASE, 16, "ipmi_kcs"); ++ if ((inb_p(KCS_STATUS_REG) == 0xFF) && ++ (inb_p(KCS_DATAIN_REG) == 0xFF)) { ++ printk("--KCS ISA window not present. Driver exiting\n"); ++ release_region(KCS_BASE, 16); ++ return (-ENXIO); ++ } ++ ++ kcs_stat.tx_good = 0; ++ kcs_stat.tx_bad = 0; ++ memset(&drv_inf, 0, sizeof(drv_inf)); ++ strcpy(drv_inf.driver_name, "ipmi_kcs"); ++ drv_inf.major_ver = driver_major; ++ drv_inf.minor_ver = driver_minor; ++ ++ misc_register(&kcs_miscdev); ++#ifdef CONFIG_BMC_WDT ++ misc_register(&wdt_miscdev); ++ register_reboot_notifier(&wdt_notifier); ++#endif ++ get_deviceid(); ++ 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_machine = KCS_LEGACY; ++ drv_inf.flags |= KCS_FLAG_LEGACY; ++ } else { ++ printk("--Using new KCS state machine\n"); ++ kcs_machine = KCS_NEW; ++ } ++ if (!pci_present()) { ++ printk ++ ("--PCIBIOS not present. Unable to determine chipset vendor\n"); ++ drv_inf.flags |= KCS_FLAG_BLINKY; ++ } else { ++ int pci_index = 0; ++ unsigned char pci_bus, pci_device_fn; ++ unsigned short vendor, device; ++ ++ for (; pci_index < 0xff; pci_index++) { ++ if (pcibios_find_class(PCI_CLASS_BRIDGE_HOST << 8, ++ pci_index, ++ &pci_bus, ++ &pci_device_fn) != ++ PCIBIOS_SUCCESSFUL) { ++ break; ++ } ++ pcibios_read_config_word(pci_bus, pci_device_fn, ++ PCI_VENDOR_ID, &vendor); ++ pcibios_read_config_word(pci_bus, pci_device_fn, ++ PCI_DEVICE_ID, &device); ++ drv_inf.mb_chipset_vendor = vendor; ++ drv_inf.mb_chipset_device = device; ++ printk ++ ("--Motherboard Chipset vendor 0x%.4x, device 0x%.4x\n", ++ vendor, device); ++ if ((vendor == 0x8086) && (device == 0x71a0)) { ++ drv_inf.flags |= KCS_FLAG_BLINKY; ++ } ++ } ++ } ++ if (drv_inf.flags & KCS_FLAG_BLINKY) { ++ printk("--Intel Lancewood features enabled\n"); ++ } else ++ printk("--No vendor specific features enabled\n"); ++#ifdef CONFIG_PROC_FS ++ if (!(ipmi_proc_entry = create_proc_entry("ipmi_kcs", 0, 0))) ++ printk(KERN_ERR, ++ "--ERROR: Unable to register /proc/ipmi_kcs\n"); ++ else ++ ipmi_proc_entry->read_proc = ipmi_get_info; ++ ++#endif ++ return (0); ++} ++ ++static int kcs_do_xfer(BMC_REQUEST * request, ++ int request_len, ++ BMC_RESPONSE * response, int *response_len) ++{ ++ int rc = 0; ++ ++ down(&kcs_sem); ++ switch (kcs_machine) { ++ case KCS_LEGACY: ++ { ++ rc = old_kcs_do_xfer(request, request_len, ++ response, response_len); ++ break; ++ } ++ case KCS_NEW: ++ { ++ rc = new_kcs_do_xfer(request, request_len, ++ response, response_len); ++ break; ++ } ++ default: ++ { ++ printk ++ ("[IPMI_KCS] Undefined or bad KCS state machine selected (%d)\n", ++ kcs_machine); ++ get_deviceid(); ++ if ((dev_id.ipmi_version_major == 0) && ++ (dev_id.ipmi_version_minor == 9)) { ++ printk ++ ("Recalibrated to use legacy KCS state machine\n"); ++ kcs_machine = KCS_LEGACY; ++ } else { ++ printk ++ ("Recalibrated to use new KCS state machine\n"); ++ kcs_machine = KCS_NEW; ++ } ++ rc = -EAGAIN; ++ break; ++ } ++ } ++ if (rc == 0) ++ kcs_stat.tx_good++; ++ else ++ kcs_stat.tx_bad++; ++ up(&kcs_sem); ++ return (rc); ++} ++ ++static int new_kcs_do_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_KCS] Maximum retries exceeded. Aborting transfer\n"); ++ rc = -EIO; ++ break; ++ } ++ switch (state) { ++ case SEND_INIT: ++ { ++ i = 0; ++ state = SEND_START; ++ wait_while_ibf(); ++ if (clear_obf() != 0) { ++ state = ERROR; ++ break; ++ } ++ } ++ case SEND_START: ++ { ++ state = SEND_NEXT; ++ write_kcs_cmd(WRITE_START); ++ wait_while_ibf(); ++ if (get_kcs_state() != KCS_WRITE_STATE) { ++ state = ERROR; ++ break; ++ } ++ } ++ 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 (get_kcs_state() != KCS_WRITE_STATE) { ++ state = ERROR; ++ break; ++ } ++ if (clear_obf() != 0) { ++ state = ERROR; ++ break; ++ } ++ break; ++ } ++ case SEND_END: ++ { ++ write_kcs_cmd(WRITE_END); ++ wait_while_ibf(); ++ if (get_kcs_state() != KCS_WRITE_STATE) { ++ state = ERROR; ++ break; ++ } ++ if (clear_obf() != 0) { ++ state = ERROR; ++ break; ++ } ++ 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(); ++ write_kcs_data(KCS_READ); ++ 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 (wait_until_obf() == 0) { ++ clear_obf(); ++ state = RECV_END; ++ break; ++ } else { ++ state = ERROR; ++ break; ++ } ++ } ++ case KCS_READ_STATE: ++ { ++ if (wait_until_obf() == 0) ++ state = RECV_NEXT; ++ else ++ state = ERROR; ++ break; ++ } ++ } ++ break; ++ } ++ case RECV_END: ++ { ++ if ((i < MIN_BMC_RESPONSE_SIZE) || ++ (response->netfn != ++ (request->netfn | 0x01)) ++ || (response->cmd != request->cmd)) { ++ mdelay(BMC_RETRY_DELAY); ++ bad++; ++ state = SEND_INIT; ++ printk ++ ("[IPMI_KCS] Request/Response CMD/NETFN mismatch error\n"); ++ ++ printk ++ (" RQcmd/RQnetfn=0x%x/0x%x,RScmd/RSnetfn=0x%x/0x%x\n", ++ request->cmd, request->netfn, ++ response->cmd, ++ response->netfn); ++ break; ++ } ++ ++ *response_len = i; ++ rc = 0; ++ state = END; ++ break; ++ } ++ case ERROR: ++ default: ++ { ++ printk ++ ("[IPMI_KCS] BMC in bad state. Retrying transfer\n"); ++ mdelay(BMC_RETRY_DELAY); ++ bad++; ++ state = SEND_INIT; ++ break; ++ } ++ } ++ } ++ return (rc); ++} ++ ++static int old_kcs_do_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_KCS] Maximum retries exceeded. Aborting transfer\n"); ++ rc = -EIO; ++ break; ++ } ++ switch (state) { ++ case SEND_INIT: ++ { ++ i = 0; ++ state = SEND_START; ++ wait_while_ibf(); ++ } ++ 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(); ++ break; ++ } ++ case SEND_END: ++ { ++ wait_while_ibf(); ++ write_kcs_cmd(WRITE_END); ++ wait_while_ibf(); ++ if (get_kcs_state() != KCS_WRITE_STATE) { ++ state = ERROR; ++ break; ++ } ++ 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(); ++ wait_while_ibf(); ++ write_kcs_data(KCS_READ); ++ 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: ++ { ++ state = RECV_END; ++ break; ++ } ++ case KCS_READ_STATE: ++ { ++ 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)) { ++ mdelay(BMC_RETRY_DELAY); ++ bad++; ++ state = SEND_INIT; ++ printk ++ ("[IPMI_KCS] Request/Response CMD/NETFN mismatch error\n"); ++ ++ printk ++ (" RQcmd/RQnetfn=0x%x/0x%x,RScmd/RSnetfn=0x%x/0x%x\n", ++ request->cmd, request->netfn, ++ response->cmd, ++ response->netfn); ++ break; ++ } ++ ++ *response_len = i; ++ rc = 0; ++ state = END; ++ break; ++ } ++ case ERROR: ++ default: ++ { ++ printk ++ ("[IPMI_KCS] BMC in bad state. Retrying transfer\n"); ++ mdelay(BMC_RETRY_DELAY); ++ bad++; ++ state = SEND_INIT; ++ break; ++ } ++ } ++ } ++ return (rc); ++} ++ ++#ifdef CONFIG_PROC_FS ++int ipmi_get_info(char *buf, char **start, off_t fpos, int length, ++ int *eof, void *unused) ++{ ++ char *p; ++ ++ if (get_deviceid() != 0) { ++ printk("[IPMI_KCS] Unable to get device ID\n"); ++ memset(&dev_id, 0, sizeof(dev_id)); ++ } ++ ++ p = buf; ++ p += sprintf(p, "Driver Version\t: %d.%d\n", ++ driver_major, driver_minor); ++ p += sprintf(p, "BMC Version\t: %x.%x\n", ++ dev_id.major_firmware_revision, ++ dev_id.minor_firmware_revision); ++ p += sprintf(p, "IPMI Version\t: %d.%d\n", ++ dev_id.ipmi_version_major, dev_id.ipmi_version_minor); ++ p += sprintf(p, "\nTotal Good Transactions\t: %d\n", ++ kcs_stat.tx_good); ++ p += sprintf(p, "Total Bad Transactions\t: %d\n", kcs_stat.tx_bad); ++ ++ return p - buf; ++} ++#endif ++ ++/* ++ * kcs chip mashing stuff ++ */ ++static int wait_while_ibf() ++{ ++ 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 wait_until_obf() ++{ ++ 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() ++{ ++ unsigned char cs; ++ ++ cs = inb_p(KCS_STATUS_REG); ++ return (cs & KCS_STATE_MASK); ++} ++ ++static unsigned char read_kcs_data() ++{ ++ 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 is_obf_set() ++{ ++ unsigned char cs; ++ ++ cs = inb_p(KCS_STATUS_REG); ++ return ((cs & KCS_OBF) == KCS_OBF); ++} ++ ++static int clear_obf() ++{ ++ read_kcs_data(); ++ return (0); ++} +--- /dev/null Fri Mar 23 23:37:44 2001 ++++ linux-bmc/drivers/char/ipmi_kcs.h Tue Oct 30 17:18:07 2001 +@@ -0,0 +1,171 @@ ++/* ++ * Intelligent Platform Management Interface driver for Linux 2.x ++ * ++ * (c) Copyright 1999 San Mehat & VA Linux Systems ++ * 1382 Bordeaux Dr. ++ * Sunnyvale, California ++ * 94089 ++ * ++ * http://www.valinux.com ++ * ++ * This driver is provided under the GNU public license, incorporated ++ * herein by reference. The driver is provided without warranty or ++ * support. ++ * ++ * ++ */ ++ ++#include ++ ++#define KCS_LEGACY 1 ++#define KCS_NEW 2 ++ ++#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 */ ++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; ++ ++typedef struct set_watchdog ++ { ++ unsigned char timer_use :3; ++ unsigned char res1 :4; ++ unsigned char dontlog :1; ++ ++ unsigned char timeout_action :3; ++ unsigned char res2 :1; ++ unsigned char pre_irq :3; ++ unsigned char res3 :1; ++ ++ unsigned char pretimeout_interval; ++ ++ unsigned char tuefc_res1 :1; ++ unsigned char tuefc_biosfrb2 :1; ++ unsigned char tuefc_biospost :1; ++ unsigned char tuefc_osload :1; ++ unsigned char tuefc_smsos :1; ++ unsigned char tuefc_oem :1; ++ unsigned char tuefc_res2 :1; ++ unsigned char tuefc_res3 :1; ++ ++ unsigned short initial_count; ++ } SET_WATCHDOG; ++ ++typedef struct get_watchdog_response ++ { ++ unsigned char timer_use :3; ++ unsigned char res1 :3; ++ unsigned char timer_status :1; ++ unsigned char sel_log :1; ++ ++ unsigned char timeout_act :3; ++ unsigned char res2 :1; ++ unsigned char pre_irq_act :3; ++ unsigned char res3 :1; ++ ++ unsigned char pre_timeout __attribute__ ((packed)); ++ ++ unsigned char timer_use_xp __attribute__ ((packed)); ++ ++ unsigned short init_count __attribute__ ((packed)); ++ unsigned short current_count __attribute__ ((packed)); ++ } GET_WATCHDOG_RESPONSE; +--- /dev/null Fri Mar 23 23:37:44 2001 ++++ linux-bmc/include/linux/ipmi_ioctls.h Tue Oct 30 17:18:07 2001 +@@ -0,0 +1,152 @@ ++/* ++ * Intelligent Platform Management Interface driver for Linux 2.x ++ * ++ * (c) Copyright 1999 San Mehat & VA Linux Systems ++ * 1382 Bordeaux Dr. ++ * Sunnyvale, California ++ * 94089 ++ * ++ * http://www.valinux.com ++ * ++ * This driver is provided under the GNU public license, incorporated ++ * herein by reference. The driver is provided without warranty or ++ * support. ++ * ++ * IOCTL definitions for IPMI drivers ++ */ ++ ++/* ++ * Note: The following macros should be used on the IPMI_XFER structure. ++ * DO NOT try to muck with this structure directly.. use the macros ++ * to ensure future compatibility: ++ * ++ * INIT_XFER(IPMI_XFER *); ++ * -- Zero out a IPMI_XFER structure and initialize it for use ++ * ++ * SET_REQUEST_LUN(IPMI_XFER *, unsigned char lun); ++ * -- Set the request packet logical unit ++ * ++ * SET_REQUEST_NETFN(IPMI_XFER *, unsigned char netfn); ++ * -- Set the request packet network function code ++ * ++ * SET_REQUEST_CMD(IPMI_XFER *, unsigned char cmd); ++ * -- Set the request packet IPMI command code ++ * ++ * SET_REQUEST_DATA(IPMI_XFER *, unsigned char *data, int length); ++ * -- Set the request packet optional argument data field ++ * ++ * GET_RESPONSE_LUN(IPMI_XFER *, unsigned char lun); ++ * -- Get the response packet logical unit ++ * ++ * GET_RESPONSE_NETFN(IPMI_XFER *, unsigned char netfn); ++ * -- Get the response packet network function code ++ * ++ * GET_RESPONSE_CMD(IPMI_XFER *, unsigned char cmd); ++ * -- Get the response packet command ++ * ++ * GET_RESPONSE_CC(IPMI_XFER *, unsigned char cc); ++ * -- Get the response packet completion code ++ * ++ * GET_RESPONSE_DATA_LENGTH(IPMI_XFER *, int len); ++ * -- Get the response packet data length ++ * ++ * GET_RESPONSE_DATA(IPMI_XFER *, unsigned char *buffer); ++ * -- Copy the response packet data into local buffer ++ */ ++ ++#ifndef _IPMI_IOCTLS_H ++#define _IPMI_IOCTLS_H ++ ++#define IOCTL_IPMI_XFER 0x01 ++#define IOCTL_DRIVER_INFO 0x02 ++ ++typedef struct ipmi_xfer ++ { ++ unsigned char request[64]; ++ unsigned char response[64]; ++ int request_len; ++ int response_len; ++ } IPMI_XFER; ++ ++struct ipmi_driver_info ++ { ++ char driver_name[64]; /* Name of the driver */ ++ int major_ver; ++ int minor_ver; ++ unsigned short mb_chipset_vendor; /* PCI host bridge vendor tag */ ++ unsigned short mb_chipset_device; /* PCI host bridge vendor device id */ ++ unsigned int flags; /* driver specific flags */ ++ unsigned int reserved; ++ }; ++ ++/* flags definitions for the 'ipmi_kcs' driver */ ++#define KCS_FLAG_BLINKY 0x01 /* Set if blinky works (only on Intel L440GX) */ ++#define KCS_FLAG_LEGACY 0x02 /* Set if using legacy KCS interface ( < IPMI 1.0) */ ++ ++#define INIT_XFER(_xferp) \ ++ memset(_xferp, 0, sizeof(IPMI_XFER)); \ ++ _xferp->request_len = 2; \ ++ _xferp->response_len = sizeof(_xferp->response); ++ ++#define SET_REQUEST_LUN(_xferp, _lun) \ ++ { \ ++ unsigned char _netfn_copy; \ ++ \ ++ _netfn_copy = (_xferp->request[0] & 0xFC); \ ++ _xferp->request[0] = _lun; \ ++ _xferp->request[0]|= _netfn_copy; \ ++ } ++ ++#define SET_REQUEST_NETFN(_xferp, netfn) \ ++ { \ ++ unsigned char __lun_copy; \ ++ \ ++ __lun_copy = (_xferp->request[0] & 0x3); \ ++ _xferp->request[0] = (netfn << 2); \ ++ _xferp->request[0]|= __lun_copy; \ ++ } ++ ++#define SET_REQUEST_CMD(_xferp, _cmd) \ ++ _xferp->request[1] = _cmd; ++ ++#define SET_REQUEST_DATA(_xferp, datap, _len) \ ++ { \ ++ memcpy(&_xferp->request[2], datap, _len); \ ++ _xferp->request_len = (_len + 2); \ ++ } ++ ++#define GET_RESPONSE_LUN(_xferp, _lun) \ ++ _lun = (_xferp->response[0] & 0x3); ++ ++#define GET_RESPONSE_NETFN(_xferp, netfn) \ ++ netfn = ((_xferp->response[0] & 0xFC) >> 2); ++ ++#define GET_RESPONSE_CMD(_xferp, _cmd) \ ++ _cmd = _xferp->response[1]; ++ ++#define GET_RESPONSE_CC(_xferp, cc) \ ++ cc = _xferp->response[2]; ++ ++#define GET_RESPONSE_DATA_LENGTH(_xferp, _len) \ ++ _len = (_xferp->response_len - 3); ++ ++#define GET_RESPONSE_DATA(_xferp, datap) \ ++ memcpy(datap, &_xferp->response[3], (_xferp->response_len -3)); ++ ++/* ++ * The Netfn codes ++ */ ++#define CHASSIS_REQUEST 0x00 ++#define CHASSIS_RESPONSE 0x01 ++#define BRIDGE_REQUEST 0x02 ++#define BRIDGE_RESPONSE 0x03 ++#define SENSOR_REQUEST 0x04 ++#define SENSOR_RESPONSE 0x05 ++#define APP_REQUEST 0x06 ++#define APP_RESPONSE 0x07 ++#define FIRMWARE_REQUEST 0x08 ++#define FIRMWARE_RESPONSE 0x09 ++#define STORAGE_REQUEST 0x0A ++#define STORAGE_RESPONSE 0x0B ++ ++#endif diff --git a/kern/linux-ipmi-2.4.20-v17.diff b/kern/linux-ipmi-2.4.20-v17.diff new file mode 100644 index 0000000..a919310 --- /dev/null +++ b/kern/linux-ipmi-2.4.20-v17.diff @@ -0,0 +1,6647 @@ +diff -urN linux.orig/Documentation/Configure.help linux/Documentation/Configure.help +--- linux.orig/Documentation/Configure.help Fri Dec 6 09:11:24 2002 ++++ linux/Documentation/Configure.help Fri Dec 6 09:12:27 2002 +@@ -26242,6 +26242,31 @@ + + If unsure, say N. + ++IPMI top-level message handler ++CONFIG_IPMI_HANDLER ++ 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. ++ ++Generate a panic event to all BMCs on a panic ++CONFIG_IPMI_PANIC_EVENT ++ When a panic occurs, this will cause the IPMI message handler to ++ generate an IPMI event describing the panic to each interface ++ registered with the message handler. ++ ++Device interface for IPMI ++CONFIG_IPMI_DEVICE_INTERFACE ++ This provides an IOCTL interface to the IPMI message handler so ++ userland processes may use IPMI. It supports poll() and select(). ++ ++IPMI KCS handler ++CONFIG_IPMI_KCS ++ Provides a driver for a KCS-style interface to a BMC. ++ ++IPMI Watchdog Timer ++CONFIG_IPMI_WATCHDOG ++ This enables the IPMI watchdog timer. ++ + # + # A couple of things I keep forgetting: + # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, +diff -urN linux.orig/Documentation/IPMI.txt linux/Documentation/IPMI.txt +--- linux.orig/Documentation/IPMI.txt Wed Dec 31 18:00:00 1969 ++++ linux/Documentation/IPMI.txt Tue Dec 31 13:00:01 2002 +@@ -0,0 +1,352 @@ ++ ++ The Linux IPMI Driver ++ --------------------- ++ Corey Minyard ++ ++ ++ ++This document describes how to use the IPMI driver for Linux. If you ++are not familiar with IPMI itself, see the web site at ++http://www.intel.com/design/servers/ipmi/index.htm. IPMI is a big ++subject and I can't cover it all here! ++ ++Basic Design ++------------ ++ ++The Linux IPMI driver is designed to be very modular and flexible, you ++only need to take the pieces you need and you can use it in many ++different ways. Because of that, it's broken into many chunks of ++code. These chunks are: ++ ++ipmi_msghandler - This is the central piece of software for the IPMI ++system. It handles all messages, message timing, and responses. The ++IPMI users tie into this, and the IPMI physical interfaces (called ++System Management Interfaces, or SMIs) also tie in here. This ++provides the kernelland interface for IPMI, but does not provide an ++interface for use by application processes. ++ ++ipmi_devintf - This provides a userland IOCTL interface for the IPMI ++driver, each open file for this device ties in to the message handler ++as an IPMI user. ++ ++ipmi_kcs_drv - A driver for the KCS SMI. Most system have a KCS ++interface for IPMI. ++ ++ ++Much documentation for the interface is in the include files. The ++IPMI include files are: ++ ++ipmi.h - Contains the user interface and IOCTL interface for IPMI. ++ ++ipmi_smi.h - Contains the interface for SMI drivers to use. ++ ++ipmi_msgdefs.h - General definitions for base IPMI messaging. ++ ++ ++Addressing ++---------- ++ ++The IPMI addressing works much like IP addresses, you have an overlay ++to handle the different address types. The overlay is: ++ ++ struct ipmi_addr ++ { ++ int addr_type; ++ short channel; ++ char data[IPMI_MAX_ADDR_SIZE]; ++ }; ++ ++The addr_type determines what the address really is. The driver ++currently understands two different types of addresses. ++ ++"System Interface" addresses are defined as: ++ ++ struct ipmi_system_interface_addr ++ { ++ int addr_type; ++ short channel; ++ }; ++ ++and the type is IPMI_SYSTEM_INTERFACE_ADDR_TYPE. This is used for talking ++straight to the BMC on the current card. The channel must be ++IPMI_BMC_CHANNEL. ++ ++Messages that are destined to go out on the IPMB bus use the ++IPMI_IPMB_ADDR_TYPE address type. The format is ++ ++ struct ipmi_ipmb_addr ++ { ++ int addr_type; ++ short channel; ++ unsigned char slave_addr; ++ unsigned char lun; ++ }; ++ ++The "channel" here is generally zero, but some devices support more ++than one channel, it corresponds to the channel as defined in the IPMI ++spec. ++ ++ ++Messages ++-------- ++ ++Messages are defined as: ++ ++struct ipmi_msg ++{ ++ unsigned char netfn; ++ unsigned char lun; ++ unsigned char cmd; ++ unsigned char *data; ++ int data_len; ++}; ++ ++The driver takes care of adding/stripping the header information. The ++data portion is just the data to be send (do NOT put addressing info ++here) or the response. Note that the completion code of a response is ++the first item in "data", it is not stripped out because that is how ++all the messages are defined in the spec (and thus makes counting the ++offsets a little easier :-). ++ ++When using the IOCTL interface from userland, you must provide a block ++of data for "data", fill it, and set data_len to the length of the ++block of data, even when receiving messages. Otherwise the driver ++will have no place to put the message. ++ ++Messages coming up from the message handler in kernelland will come in ++as: ++ ++ struct ipmi_recv_msg ++ { ++ struct list_head link; ++ ++ /* The type of message as defined in the "Receive Types" ++ defines above. */ ++ int recv_type; ++ ++ ipmi_user_t *user; ++ struct ipmi_addr addr; ++ long msgid; ++ struct ipmi_msg msg; ++ ++ /* Call this when done with the message. It will presumably free ++ the message and do any other necessary cleanup. */ ++ void (*done)(struct ipmi_recv_msg *msg); ++ ++ /* Place-holder for the data, don't make any assumptions about ++ the size or existence of this, since it may change. */ ++ unsigned char msg_data[IPMI_MAX_MSG_LENGTH]; ++ }; ++ ++You should look at the receive type and handle the message ++appropriately. ++ ++ ++The Upper Layer Interface (Message Handler) ++------------------------------------------- ++ ++The upper layer of the interface provides the users with a consistent ++view of the IPMI interfaces. It allows multiple SMI interfaces to be ++addressed (because some boards actually have multiple BMCs on them) ++and the user should not have to care what type of SMI is below them. ++ ++ ++Creating the User ++ ++To user the message handler, you must first create a user using ++ipmi_create_user. The interface number specifies which SMI you want ++to connect to, and you must supply callback functions to be called ++when data comes in. The callback function can run at interrupt level, ++so be careful using the callbacks. This also allows to you pass in a ++piece of data, the handler_data, that will be passed back to you on ++all calls. ++ ++Once you are done, call ipmi_destroy_user() to get rid of the user. ++ ++From userland, opening the device automatically creates a user, and ++closing the device automatically destroys the user. ++ ++ ++Messaging ++ ++To send a message from kernel-land, the ipmi_request() call does ++pretty much all message handling. Most of the parameter are ++self-explanatory. However, it takes a "msgid" parameter. This is NOT ++the sequence number of messages. It is simply a long value that is ++passed back when the response for the message is returned. You may ++use it for anything you like. ++ ++Responses come back in the function pointed to by the ipmi_recv_hndl ++field of the "handler" that you passed in to ipmi_create_user(). ++Remember again, these may be running at interrupt level. Remember to ++look at the receive type, too. ++ ++From userland, you fill out an ipmi_req_t structure and use the ++IPMICTL_SEND_COMMAND ioctl. For incoming stuff, you can use select() ++or poll() to wait for messages to come in. However, you cannot use ++read() to get them, you must call the IPMICTL_RECEIVE_MSG with the ++ipmi_recv_t structure to actually get the message. Remember that you ++must supply a pointer to a block of data in the msg.data field, and ++you must fill in the msg.data_len field with the size of the data. ++This gives the receiver a place to actually put the message. ++ ++If the message cannot fit into the data you provide, you will get an ++EMSGSIZE error and the driver will leave the data in the receive ++queue. If you want to get it and have it truncate the message, us ++the IPMICTL_RECEIVE_MSG_TRUNC ioctl. ++ ++When you send a command (which is defined by the lowest-order bit of ++the netfn per the IPMI spec) on the IPMB bus, the driver will ++automatically assign the sequence number to the command and save the ++command. If the response is not receive in the IPMI-specified 5 ++seconds, it will generate a response automatically saying the command ++timed out. If an unsolicited response comes in (if it was after 5 ++seconds, for instance), that response will be ignored. ++ ++In kernelland, after you receive a message and are done with it, you ++MUST call ipmi_free_recv_msg() on it, or you will leak messages. Note ++that you should NEVER mess with the "done" field of a message, that is ++required to properly clean up the message. ++ ++Note that when sending, there is an ipmi_request_supply_msgs() call ++that lets you supply the smi and receive message. This is useful for ++pieces of code that need to work even if the system is out of buffers ++(the watchdog timer uses this, for instance). You supply your own ++buffer and own free routines. This is not recommended for normal use, ++though, since it is tricky to manage your own buffers. ++ ++ ++Events and Incoming Commands ++ ++The driver takes care of polling for IPMI events and receiving ++commands (commands are messages that are not responses, they are ++commands that other things on the IPMB bus have sent you). To receive ++these, you must register for them, they will not automatically be sent ++to you. ++ ++To receive events, you must call ipmi_set_gets_events() and set the ++"val" to non-zero. Any events that have been received by the driver ++since startup will immediately be delivered to the first user that ++registers for events. After that, if multiple users are registered ++for events, they will all receive all events that come in. ++ ++For receiving commands, you have to individually register commands you ++want to receive. Call ipmi_register_for_cmd() and supply the netfn ++and command name for each command you want to receive. Only one user ++may be registered for each netfn/cmd, but different users may register ++for different commands. ++ ++From userland, equivalent IOCTLs are provided to do these functions. ++ ++ ++The Lower Layer (SMI) Interface ++------------------------------- ++ ++As mentioned before, multiple SMI interfaces may be registered to the ++message handler, each of these is assigned an interface number when ++they register with the message handler. They are generally assigned ++in the order they register, although if an SMI unregisters and then ++another one registers, all bets are off. ++ ++The ipmi_smi.h defines the interface for SMIs, see that for more ++details. ++ ++ ++The KCS Driver ++-------------- ++ ++The KCS driver allows up to 4 KCS interfaces to be configured in the ++system. By default, the driver will register one KCS interface at the ++spec-specified I/O port 0xca2 without interrupts. You can change this ++at module load time (for a module) with: ++ ++ insmod ipmi_kcs_drv.o kcs_ports=,... kcs_addrs=, ++ kcs_irqs=,... kcs_trydefaults=[0|1] ++ ++The KCS driver supports two types of interfaces, ports (for I/O port ++based KCS interfaces) and memory addresses (for KCS interfaces in ++memory). The driver will support both of them simultaneously, setting ++the port to zero (or just not specifying it) will allow the memory ++address to be used. The port will override the memory address if it ++is specified and non-zero. kcs_trydefaults sets whether the standard ++IPMI interface at 0xca2 and any interfaces specified by ACPE are ++tried. By default, the driver tries it, set this value to zero to ++turn this off. ++ ++When compiled into the kernel, the addresses can be specified on the ++kernel command line as: ++ ++ ipmi_kcs=:,:....,[nodefault] ++ ++The values is either "p" or "m" for port or memory ++addresses. So for instance, a KCS interface at port 0xca2 using ++interrupt 9 and a memory interface at address 0xf9827341 with no ++interrupt would be specified "ipmi_kcs=p0xca2:9,m0xf9827341". ++If you specify zero for in irq or don't specify it, the driver will ++run polled unless the software can detect the interrupt to use in the ++ACPI tables. ++ ++By default, the driver will attempt to detect a KCS device at the ++spec-specified 0xca2 address and any address specified by ACPI. If ++you want to turn this off, use the "nodefault" option. ++ ++If you have high-res timers compiled into the kernel, the driver will ++use them to provide much better performance. Note that if you do not ++have high-res timers enabled in the kernel and you don't have ++interrupts enabled, the driver will run VERY slowly. Don't blame me, ++the KCS interface sucks. ++ ++ ++Other Pieces ++------------ ++ ++Watchdog ++ ++A watchdog timer is provided that implements the Linux-standard ++watchdog timer interface. It has three module parameters that can be ++used to control it: ++ ++ insmod ipmi_watchdog timeout= pretimeout= action= ++ preaction= preop= ++ ++The timeout is the number of seconds to the action, and the pretimeout ++is the amount of seconds before the reset that the pre-timeout panic will ++occur (if pretimeout is zero, then pretimeout will not be enabled). ++ ++The action may be "reset", "power_cycle", or "power_off", and ++specifies what to do when the timer times out, and defaults to ++"reset". ++ ++The preaction may be "pre_smi" for an indication through the SMI ++interface, "pre_int" for an indication through the SMI with an ++interrupts, and "pre_nmi" for a NMI on a preaction. This is how ++the driver is informed of the pretimeout. ++ ++The preop may be set to "preop_none" for no operation on a pretimeout, ++"preop_panic" to set the preoperation to panic, or "preop_give_data" ++to provide data to read from the watchdog device when the pretimeout ++occurs. A "pre_nmi" setting CANNOT be used with "preop_give_data" ++because you can't do data operations from an NMI. ++ ++When preop is set to "preop_give_data", one byte comes ready to read ++on the device when the pretimeout occurs. Select and fasync work on ++the device, as well. ++ ++When compiled into the kernel, the kernel command line is available ++for configuring the watchdog: ++ ++ ipmi_wdog=[,[,