Index: oldkernel/linux/Documentation/Configure.help diff -u linux/Documentation/Configure.help:1.7 linux/Documentation/Configure.help:1.8 --- linux/Documentation/Configure.help:1.7 Thu Jun 1 16:52:14 2000 +++ linux/Documentation/Configure.help Thu Jun 1 17:01:59 2000 @@ -9240,6 +9240,17 @@ many of the newer IBM Thinkpads. If you experience hangs when you suspend, try setting this to Y. Otherwise, say N. +Intelligent Platform Management Interface Support +CONFIG_IPMI_KCS + Enables support for the Keyboard Controller Style (KCS) interface to + the Baseboard Management Controller (BMC) which allows a user program + to send and receive IPMI commands. The BMC is a next generation style + server management co-processor which exists on many Intel based + server boards (Qlogic also has a chip which is compatible which is + available on a few other vendors boards). For documentation and + utilities to access the watchdog, temperature sensor, and other + data, see http://valinux.com/projects. + Watchdog Timer Support CONFIG_WATCHDOG If you say Y here (and to one of the following options) and create a Index: oldkernel/linux/arch/i386/kernel/traps.c diff -u linux/arch/i386/kernel/traps.c:1.2 linux/arch/i386/kernel/traps.c:1.3 --- linux/arch/i386/kernel/traps.c:1.2 Thu Jun 1 15:05:19 2000 +++ linux/arch/i386/kernel/traps.c Thu Jun 1 17:01:59 2000 @@ -333,8 +333,122 @@ mem_parity_error(reason, regs); if (reason & 0x40) io_check_error(reason, regs); + if (reason & 0x20) + { + bmc_nmi_error(reason, regs); + return; + } if (!(reason & 0xc0)) unknown_nmi_error(reason, regs); +} + +/* + * The IPMI KCS NMI watchdog handler stuff + */ +#include + +#define IO 0xca2 +#define ISA_BMC_STATUS (IO + 1) +#define ISA_BMC_COMMAND (IO + 1) +#define ISA_BMC_DATA_IN (IO + 0) +#define ISA_BMC_DATA_OUT (IO + 0) +#define ISA_STATE_MASK 0xC0 +#define ISA_IDLE_STATE 0x00 +#define ISA_READ_STATE 0x40 +#define ISA_WRITE_STATE 0x80 +#define ISA_ERROR_STATE 0xC0 +#define ISA_S1_FLAG 0x80 +#define ISA_S0_FLAG 0x40 +#define ISA_CD_FLAG 0x08 +#define ISA_SMS_MSG_FLAG 0x04 +#define ISA_IBF_FLAG 0x02 +#define ISA_OBF_FLAG 0x01 +#define ISA_GET_STATUS_ABORT 0x60 +#define ISA_WRITE_START 0x61 +#define ISA_WRITE_END 0X62 +#define ISA_READ 0X68 +#define MAX_ISA_LENGTH 35 +#define ISA_TIMEOUT 1000 + +static void bmc_nmi_error(unsigned char reason, struct pt_regs * regs) +{ + struct cmd + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + } pkt; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.netfn=0x06; + pkt.lun=0x00; + pkt.cmd=0x55; + kcs_send_message(&pkt, sizeof(pkt)); +} + +static int wait_while_ibf(int timeout) +{ + unsigned int master_timeout=5; + unsigned char status_byte; + + status_byte = (inb_p(ISA_BMC_STATUS) & 0xFF); + while(status_byte & ISA_IBF_FLAG) + { + master_timeout--; + if (!master_timeout) + return(-1); + udelay(100); + status_byte = (inb_p(ISA_BMC_STATUS) & 0xFF); + } + return(0); +} + +static int kcs_send_message(unsigned char *buf, int length) +{ + int status; + int i; + unsigned char chipstatus; + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-EIO); + + outb_p(ISA_WRITE_START, ISA_BMC_COMMAND); + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-EIO); + + for (i=0; i ,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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipmi_kcs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* function prototypes */ +static int kcs_send_message(unsigned char *buf, int length); +static int kcs_read_message(int *msglen, unsigned char *buf); +static int wait_while_ibf(int timeout); +static int ipmi_kcs_dispatch_internal(void *data, int size, void *reply); + +static int add_data_to_ringbuffer(unsigned char *data, int length); +static int remove_data_from_ringbuffer(unsigned char *data, int length); +static int watchdog_set(unsigned char args); +static int watchdog_ping(void); +static int panel_set(unsigned char state); +static int read_sensor(struct sensor_request *sensor); + +void ipmi_kcs_poll(unsigned long nothing); +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); + +/* static globals */ +static struct timer_list poll_timer; +static int kcs_is_open=0; +static struct wait_queue *wq = NULL; +static int head; +static int tail; +static unsigned char *buffer; + +static struct file_operations kcs_fops = { + kcs_llseek, + kcs_read, + kcs_write, + NULL, /* No Readdir */ + NULL, /* No Select */ + kcs_ioctl, + NULL, /* No mmap */ + kcs_open, + NULL, /* flush */ + kcs_release +}; + +static struct miscdevice kcs_miscdev= +{ + IPMI_KCS_MINOR, + "ipmi_kcs", + &kcs_fops +}; + +#define POLL_FREQ ((HZ/10)) + +/***************/ + +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) +{ + unsigned char tmp_buffer[MAX_ISA_LENGTH]; + int rc; + + if (!count) + return(0); + if (count > MAX_ISA_LENGTH) + return -EFBIG; + copy_from_user(&tmp_buffer[0], buf, count); + if ((rc = kcs_send_message(&tmp_buffer[0],count))<0) + { + printk("kcs_write(): Unable to send message\n"); + return(rc); + } + return(0); +} + +static ssize_t kcs_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + unsigned char tmp_buffer[MAX_ISA_LENGTH]; + int rc; + + /* Can't seek (pread) on this device */ + if (ptr != &file->f_pos) + return -ESPIPE; + + if (count > MAX_ISA_LENGTH) + count = MAX_ISA_LENGTH; + + switch(MINOR(file->f_dentry->d_inode->i_rdev)) + { + case IPMI_KCS_MINOR: + /* Check to see if theres any data to be read */ + if (head == tail) + { + if (file->f_flags & O_NONBLOCK) + { + return -EAGAIN; + } + else + { + interruptible_sleep_on(&wq); + } + } + /* If we're here theres data to be read */ + if ((rc = remove_data_from_ringbuffer(&tmp_buffer[0], count))<0) + { + printk("kcs_read(): ring buffer remove failure\n"); + return -EIO; + } + copy_to_user(buf, &tmp_buffer[0], rc); + return(rc); + break; + default: + return -EINVAL; + } +} + +static int kcs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned char argument; + + switch(cmd) + { + case IOCTL_WATCHDOG_SET: + if (!arg) + return(-EINVAL); + if (copy_from_user(&argument, (void *) arg, sizeof(unsigned char))) + return(-EFAULT); + return(watchdog_set(argument)); + break; + case IOCTL_WATCHDOG_PING: + if (arg) + return(-EINVAL); + return(watchdog_ping()); + break; + case IOCTL_PANEL_LED_SET_BLINK: + if (!arg) + return(-EINVAL); + if (copy_from_user(&argument, (void *) arg, sizeof(unsigned char))) + return(-EFAULT); + if ((argument != 0x00) && (argument != 0x01)) + return(-EINVAL); + return(panel_set(argument)); + break; + case IOCTL_READ_SENSOR: + { + struct sensor_request req; + int rc; + + if (!arg) + return(-EINVAL); + if (copy_from_user(&req, (void *) arg, sizeof(req))) + return(-EFAULT); + rc = read_sensor(&req); + if (copy_to_user((struct sensor_request *)arg, &req, sizeof(req))) + return(-EFAULT); + return(rc); + } + default: + return -EINVAL; + } + return (0); +} + +static int read_sensor(struct sensor_request *sensor) +{ + SENSOR_CMD pkt; + + memset(&pkt, 0, sizeof(pkt)); + pkt.netfn=0x04; + pkt.lun=0x00; + pkt.cmd=0x2d; + pkt.sensor_number=sensor->sensor_number; + + if ((sensor->result_length =ipmi_kcs_dispatch_internal((void *) &pkt, + sizeof(pkt), + (void *) &sensor->result_buffer[0]))<0) + { + printk("read_sensor(): IPMI command timed out for reply\n"); + return -EIO; + } + if (sensor->result_buffer[2] != 0x00) + { + printk("read_sensor(): IPMI command failed (rc = 0x%.2x)\n", + sensor->result_buffer[2]); + return -EIO; + } + return(0); +} + +static int watchdog_set(unsigned char args) +{ + IPMI_KCS_SET_WATCHDOG pkt; + unsigned char reply[MAX_ISA_LENGTH]; + + memset(&pkt, 0, sizeof(pkt)); + pkt.netfn=0x06; + pkt.lun=0x0; + pkt.cmd=0x24; + pkt.timer_use = 0x04; + + if (args & WATCHDOG_ACTION_REBOOT) + pkt.timeout_action = 0x03; + if (args & WATCHDOG_ACTION_NMI) + pkt.pre_irq = 0x02; + + pkt.pretimeout_interval = 10; + pkt.tuefc_biosfrb2=0x00; + pkt.tuefc_biospost=0x0; + pkt.tuefc_osload=0x00; + pkt.tuefc_smsos=0x01; + pkt.initial_count = (30 * 10); + if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0) + { + printk("watchdog_set(): IPMI command timed out for reply\n"); + return(-EIO); + } + if (reply[2] != 0x00) + { + printk("watchdog_set(): IPMI command failed (rc = 0x%.2x)\n",reply[2]); + return(-EIO); + } + return(0); +} + +static int watchdog_ping(void) +{ + IPMI_KCS_RESET_WATCHDOG pkt; + unsigned char reply[MAX_ISA_LENGTH]; + + memset(&pkt, 0, sizeof(pkt)); + pkt.netfn = 0x06; + pkt.lun = 0x00; + pkt.cmd = 0x22; + + if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0) + { + printk("watchdog_ping(): IPMI command timed out for reply\n"); + return(-EIO); + } + if (reply[2] != 0x00) + { + printk("watchdog_ping(): IPMI command failed (rc = 0x%.2x)\n",reply[2]); + return(-EIO); + } + return(0); +} + +static int panel_set(unsigned char state) +{ + unsigned char reply[MAX_ISA_LENGTH]; + struct blinky_cmd + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + } pkt; + + memset(&pkt, 0, sizeof(pkt)); + pkt.netfn= 0x06; + pkt.lun= 0x00; + if (state == PANEL_LED_BLINK) + pkt.cmd= 0x55; + else + pkt.cmd= 0x56; + + if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0) + { + printk("panel_set(): IPMI command timed out for reply\n"); + return(-EIO); + } + if (reply[2] != 0x00) + { + printk("panel_set(): IPMI command failed (rc = 0x%.2x)\n",reply[2]); + return(-EIO); + } + return(0); +} + +static int ipmi_kcs_dispatch_internal(void *data, int size, void *reply) +{ + volatile unsigned char status; + int rc; + int msglen; + unsigned int jiffystart; + int i; + + if ((rc =kcs_send_message((unsigned char *) data, size))<0) + { + printk("[IPMI_KCS] Message dispatch failed. (rc %d)\n",rc); + return(-EIO); + } + + interruptible_sleep_on(&wq); + if ((rc = remove_data_from_ringbuffer((unsigned char *) reply, + MAX_ISA_LENGTH))<0) + { + printk("[IPMI_KCS] Ring buffer remove failure\n"); + return -EIO; + } + return(rc); +} + +static int kcs_open(struct inode *inode, struct file *file) +{ + switch(MINOR(inode->i_rdev)) + { + case IPMI_KCS_MINOR: + { + if(kcs_is_open) + return -EBUSY; + MOD_INC_USE_COUNT; + + kcs_is_open=1; + return 0; + } + default: + return -ENODEV; + } +} + +static int kcs_release(struct inode *inode, struct file *file) +{ + if(MINOR(inode->i_rdev)==IPMI_KCS_MINOR) + kcs_is_open=0; + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef MODULE +#define kcs_init init_module + +__initfunc(int kcs_init(void)) +{ + return(ipmi_kcs_init()); +} + +void cleanup_module(void) +{ + printk("[IPMI_KCS] Driver shutting down.\n"); + kfree(buffer); + del_timer(&poll_timer); + misc_deregister(&kcs_miscdev); + release_region(IO,16); +} +#endif + +static int add_data_to_ringbuffer(unsigned char *data, int length) +{ + int i; + int diff; + + /* Make sure we have room in the ringbuffer */ + diff = tail - head; + if (diff <0) + diff = PAGE_SIZE - head + tail; + diff = PAGE_SIZE - diff; + if (length > diff) + { + printk("[IPMI_KCS] Not enough room in ringbuffer\n"); + printk("[IPMI_KCS] len = %d, head = %d, tail = %d\n",length, head, tail); + return(-1); /* Not enough room */ + } + + for (i = 0; i < length; i++) + { + buffer[tail] = data[i]; + tail++; + tail &= (PAGE_SIZE -1); + } + return(0); +} + +static int remove_data_from_ringbuffer(unsigned char *data, int length) +{ + int i; + + if (head == tail) + return(0); /* No data to be read */ + + for (i = 0; i < length; i++) + { + *data++ = buffer[head++]; + head &= (PAGE_SIZE -1); + if (head == tail) + { + i++; + return(i); + } + } + i++; + return(i); +} + +void ipmi_kcs_poll(unsigned long nothing) +{ + volatile unsigned char status; + unsigned char data[MAX_ISA_LENGTH]; + int rc; + int msglen; + + poll_timer.expires = jiffies + POLL_FREQ; + add_timer(&poll_timer); + + status = inb_p(ISA_BMC_STATUS); + if ((!(status & ISA_SMS_MSG_FLAG)) && ((status & ISA_STATE_MASK) !=ISA_READ_STATE)) + return; + /* Something in the queue */ + if ((rc = kcs_read_message(&msglen, &data[0]))<0) + { + printk("ipmi_kcs_poll(): Read Message failed\n"); + return; + } + if (!kcs_is_open) + return; /* Dont store it if nobody is listening.. */ + if (add_data_to_ringbuffer(&data[0],msglen)<0) + { + printk("ipmi_kcs_poll(): ring buffer overflow. Dropping newest frame\n"); + return; + } + wake_up_interruptible(&wq); +} + + +static int kcs_send_message(unsigned char *buf, int length) +{ + volatile unsigned char chipstatus; + int status; + int i; + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-1); + + outb_p(ISA_WRITE_START, ISA_BMC_COMMAND); + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-2); + + for (i=0; i + +#define IO 0xca2 +#define ISA_BMC_STATUS (IO + 1) +#define ISA_BMC_COMMAND (IO + 1) +#define ISA_BMC_DATA_IN (IO + 0) +#define ISA_BMC_DATA_OUT (IO + 0) + +/* State bits based on S1 & S0 below */ +#define ISA_STATE_MASK 0xC0 +#define ISA_IDLE_STATE 0x00 +#define ISA_READ_STATE 0x40 +#define ISA_WRITE_STATE 0x80 +#define ISA_ERROR_STATE 0xC0 + +/* Status Register Bits */ +#define ISA_S1_FLAG 0x80 +#define ISA_S0_FLAG 0x40 +#define ISA_CD_FLAG 0x08 +#define ISA_SMS_MSG_FLAG 0x04 +#define ISA_IBF_FLAG 0x02 +#define ISA_OBF_FLAG 0x01 + +/* SMS Transfer Stream Control Codes */ +#define ISA_GET_STATUS_ABORT 0x60 +#define ISA_WRITE_START 0x61 +#define ISA_WRITE_END 0X62 +#define ISA_READ 0X68 + +#define MAX_ISA_LENGTH 35 + +#define ISA_TIMEOUT 1000 + +typedef struct ipmi_ksc_set_watchdog + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + + 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; + } IPMI_KCS_SET_WATCHDOG; + +typedef struct ipmi_ksc_reset_watchdog + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + } IPMI_KCS_RESET_WATCHDOG; + +typedef struct sensor_cmd + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + unsigned char sensor_number; + } SENSOR_CMD; Index: oldkernel/linux/include/linux/ipmi_kcs_ioctls.h diff -u /dev/null linux/include/linux/ipmi_kcs_ioctls.h:1.1 --- /dev/null Mon Jul 31 21:14:42 2000 +++ linux/include/linux/ipmi_kcs_ioctls.h Thu Jun 1 17:01:59 2000 @@ -0,0 +1,39 @@ +/* + * 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 KCS driver + */ + +#ifndef _IPMI_KCS_IOCTLS_H +#define _IPMI_KCS_IOCTLS_H + + +#define IOCTL_WATCHDOG_SET 0x01 + #define WATCHDOG_ACTION_REBOOT 0x01 + #define WATCHDOG_ACTION_NMI 0x02 + +#define IOCTL_WATCHDOG_PING 0x02 + +#define IOCTL_PANEL_LED_SET_BLINK 0x03 + #define PANEL_LED_NORMAL 0x00 + #define PANEL_LED_BLINK 0x01 + +#define IOCTL_READ_SENSOR 0x04 +struct sensor_request + { + unsigned char sensor_number; + unsigned char result_buffer[64]; + int result_length; + }; +#endif Index: oldkernel/linux/include/linux/miscdevice.h diff -u linux/include/linux/miscdevice.h:1.1.1.1 linux/include/linux/miscdevice.h:1.2 --- linux/include/linux/miscdevice.h:1.1.1.1 Wed May 31 12:33:49 2000 +++ linux/include/linux/miscdevice.h Thu Jun 1 17:01:59 2000 @@ -18,6 +18,7 @@ #define NVRAM_MINOR 144 #define I2O_MINOR 166 #define MISC_DYNAMIC_MINOR 255 +#define IPMI_KCS_MINOR 173 #define SGI_GRAPHICS_MINOR 146 #define SGI_OPENGL_MINOR 147