summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/ipmi.4187
-rw-r--r--sys/dev/ipmi/ipmi.c1096
-rw-r--r--sys/dev/ipmi/ipmi_pci.c309
-rw-r--r--sys/dev/ipmi/ipmi_smbios.c538
-rw-r--r--sys/dev/ipmi/ipmivars.h138
-rw-r--r--sys/modules/ipmi/Makefile9
-rw-r--r--sys/sys/ipmi.h119
7 files changed, 2396 insertions, 0 deletions
diff --git a/share/man/man4/ipmi.4 b/share/man/man4/ipmi.4
new file mode 100644
index 0000000..2dcea85
--- /dev/null
+++ b/share/man/man4/ipmi.4
@@ -0,0 +1,187 @@
+.\"
+.\" Copyright (c) 2006 Tom Rhodes
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 10, 2006
+.Dt IPMI 4
+.Os
+.Sh NAME
+.Nm ipmi
+.Nd "OpenIPMI compatible IPMI interface driver"
+.Sh SYNOPSIS
+.Cd "options IPMI"
+.Pp
+.In sys/dev/ipmi.h
+.Ft int
+.Fn ioctl "int d" "IPMICTL_RECEIVE_MSG_TRUNC" "struct ipmi_recv *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_RECEIVE_MSG" "struct ipmi_recv *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_SEND_COMMAND" "struct ipmi_req *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_REGISTER_FOR_CMD" "struct ipmi_cmdspec *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_UNREGISTER_FOR_CMD" "struct ipmi_cmdspec *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_SET_GETS_EVENTS_CMD" "int *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_SET_MY_ADDRESS_CMD" "unsigned int *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_GET_MY_ADDRESS_CMD" "unsigned int *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_SET_MY_LUN_CMD" "unsigned int *"
+.Ft int
+.Fn ioctl "int d" "IPMICTL_GET_MY_LUN_CMD" "unsigned int *"
+.Sh DESCRIPTION
+The
+.Tn IPMI
+(Intelligent Platform Management Interface) is a standard for
+monitoring system hardware by permitting generic code to detect
+and monitor the sensors in a system.
+The
+.Tn IPMI
+standard offers watchdog support, an FRU database, and other
+support extensions.
+It is currently being adopted by the makers of many
+single board and embedded system manufacturers.
+.Pp
+The
+.Nm
+driver in
+.Fx
+is heavily adopted from the standard and
+.Lx
+driver; however, not all features described in the
+standard are supported.
+.Sh IOCTL
+Sending and receiving messages through the
+.Nm
+driver requires the use of IOCTLs.
+The IOCTLs are used due to the complexity of
+data sent to and from the device.
+Currently the following IOCTLs are defined:
+.Pp
+.Dv IPMI_IOC_MAGIC
+The magic IOCTL value for this interface.
+.Pp
+.Dv IPMICTL_RECEIVE_MSG_TRUNC
+Like
+.Dv RECEIVE_MSG
+but if the message can not fit into the buffer, it
+will truncate the contents instead of leaving the data
+in the buffer.
+.Pp
+.Dv IPMICTL_RECEIVE_MSG
+Receive a message.
+Possible error values:
+.Bl -tag -width Er
+.It Bq Er EAGAIN
+No messages are in the process queue.
+.It Bq Er EFAULT
+An address supplied was invalid.
+.It Bq Er EMSGSIZE
+The address could not fit in the message buffer and
+will remain in the buffer.
+.El
+.Pp
+.Dv IPMICTL_SEND_COMMAND
+Send a message to the interface.
+Possible error values:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+An address supplied was invalid
+.It Bq Er ENOMEM
+Buffers could not be allowed for the command, out of memory.
+.El
+.Pp
+.Dv IPMICTL_SET_MY_ADDRESS_CMD
+Set the slave address for source messages.
+.Pp
+.Dv IPMICTL_GET_MY_ADDRESS_CMD
+Get the slave address for source messages.
+.Pp
+.Dv IPMICTL_SET_MY_LUN_CMD
+Set the slave LUN for source messages.
+.Pp
+.Dv IPMICTL_GET_MY_LUN_CMD
+Get the slave LUN for source messages.
+.Pp
+.Ss Unimplemented IOCTLS:
+.Pp
+.Dv IPMICTL_REGISTER_FOR_CMD
+Register to receive a specfic command
+Possible error values:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+An supplied address was invalid.
+.It Bq Er EBUSY
+The network function/command is already in use.
+.It Bq Er ENOMEM
+Could not allocate memory.
+.El
+.Pp
+.Dv IPMICTL_UNREGISTER_FOR_CMD
+Unregister to receive a specfic command
+Possible error values:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+An address supplied was invalid.
+.It Bq Er ENOENT
+The network function/command was not found.
+.El
+.Pp
+.Ss Stub only IOCTL:
+.Pp
+.Dv IPMICTL_SET_GETS_EVENTS_CMD
+Set whether this interface receives events.
+Possible error values:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+An address supplied was invalid.
+.El
+.Pp
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr watchdog 4 ,
+.Xr watchdog 8 ,
+.Xr watchdogd 8 ,
+.Xr watchdog 9
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.Ar Doug Ambrisko An ambrisko@FreeBSD.org .
+This manual page was written by
+.Ar Tom Rhodes An trhodes@FreeBSD.org .
+.Sh BUGS
+Not all features of the MontaVista driver are supported.
+.Pp
+Currently, IPMB and BT modes are not implemented.
diff --git a/sys/dev/ipmi/ipmi.c b/sys/dev/ipmi/ipmi.c
new file mode 100644
index 0000000..3b47f21
--- /dev/null
+++ b/sys/dev/ipmi/ipmi.c
@@ -0,0 +1,1096 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+
+#include <sys/disk.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+#include <sys/watchdog.h>
+#include <sys/sysctl.h>
+
+#ifdef LOCAL_MODULE
+#include <ipmi.h>
+#include <ipmivars.h>
+#else
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+struct ipmi_done_list {
+ u_char *data;
+ int channel;
+ int msgid;
+ int len;
+ TAILQ_ENTRY(ipmi_done_list) list;
+};
+
+#define MAX_TIMEOUT 3 * hz
+
+static int ipmi_wait_for_ibf(device_t, int);
+static int ipmi_wait_for_obf(device_t, int);
+static void ipmi_clear_obf(device_t, int);
+static void ipmi_error(device_t);
+static void ipmi_check_read(device_t);
+static int ipmi_write(device_t, u_char *, int);
+static void ipmi_wait_for_tx_okay(device_t);
+static void ipmi_wait_for_rx_okay(device_t);
+static void ipmi_wait_for_not_busy(device_t);
+static void ipmi_set_busy(device_t);
+static int ipmi_ready_to_read(device_t);
+#ifdef IPMB
+static int ipmi_handle_attn(device_t dev);
+static int ipmi_ipmb_checksum(u_char, int);
+static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char,
+ u_char, u_char, int)
+#endif
+
+static d_ioctl_t ipmi_ioctl;
+static d_poll_t ipmi_poll;
+static d_open_t ipmi_open;
+static d_close_t ipmi_close;
+
+int ipmi_attached = 0;
+
+#define IPMI_MINOR 0
+
+static int on = 1;
+SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters");
+SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW,
+ &on, 0, "");
+
+static struct cdevsw ipmi_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ipmi_open,
+ .d_close = ipmi_close,
+ .d_ioctl = ipmi_ioctl,
+ .d_poll = ipmi_poll,
+ .d_name = "ipmi",
+};
+
+MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi");
+
+static int
+ipmi_open(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+ struct ipmi_softc *sc;
+
+ if (!on)
+ return ENOENT;
+
+ sc = dev->si_drv1;
+ if (sc->ipmi_refcnt) {
+ return EBUSY;
+ }
+ sc->ipmi_refcnt = 1;
+
+ return 0;
+}
+
+static int
+ipmi_poll(struct cdev *dev, int poll_events, struct thread *td)
+{
+ struct ipmi_softc *sc;
+ int revents = 0;
+
+ sc = dev->si_drv1;
+
+ ipmi_check_read(sc->ipmi_dev);
+
+ if (poll_events & (POLLIN | POLLRDNORM)) {
+ if (!TAILQ_EMPTY(&sc->ipmi_done))
+ revents |= poll_events & (POLLIN | POLLRDNORM);
+ if (TAILQ_EMPTY(&sc->ipmi_done) && sc->ipmi_requests == 0) {
+ revents |= POLLERR;
+ }
+ }
+
+ if (revents == 0) {
+ if (poll_events & (POLLIN | POLLRDNORM))
+ selrecord(td, &sc->ipmi_select);
+ }
+
+ return revents;
+}
+
+static int
+ipmi_close(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+ struct ipmi_softc *sc;
+ int error = 0;
+
+ sc = dev->si_drv1;
+
+ sc->ipmi_refcnt = 0;
+
+ return error;
+}
+
+#ifdef IPMB
+static int
+ipmi_ipmb_checksum(u_char *data, int len)
+{
+ u_char sum = 0;
+
+ for (; len; len--) {
+ sum += *data++;
+ }
+ return -sum;
+}
+
+static int
+ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn,
+ u_char command, u_char seq, u_char *data, int data_len)
+{
+ u_char *temp;
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int error;
+ u_char slave_addr = 0x52;
+
+ temp = malloc(data_len + 10, M_IPMI, M_WAITOK);
+ bzero(temp, data_len + 10);
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_SEND_MSG;
+ temp[2] = channel;
+ temp[3] = slave_addr;
+ temp[4] = netfn << 2;
+ temp[5] = ipmi_ipmb_check_sum(&temp[3], 2);
+ temp[6] = sc->ipmi_address;
+ temp[7] = seq << 2 | sc->ipmi_lun;
+ temp[8] = command;
+
+ bcopy(data, &temp[9], data_len);
+ temp[data_len + 9] = ipmi_ipmb_check(&temp[6], data_len + 3);
+ ipmi_write(sc->ipmi_dev, temp, data_len + 9);
+ free(temp, M_IPMI);
+
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK);
+ bzero(temp, IPMI_MAX_RX);
+ error = ipmi_read(dev, temp, IPMI_MAX_RX);
+ free(temp, M_IPMI);
+
+ return error;
+}
+
+static int
+ipmi_handle_attn(device_t dev)
+{
+ u_char temp[IPMI_MAX_RX];
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_printf(sc->ipmi_dev, "BMC has a message\n");
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_GET_MSG_FLAGS;
+ ipmi_write(sc->ipmi_dev, temp, 2);
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, IPMI_MAX_RX);
+ error = ipmi_read(dev, temp, IPMI_MAX_RX);
+
+ if (temp[2] == 0) {
+ if (temp[3] & IPMI_MSG_BUFFER_FULL) {
+ device_printf(sc->ipmi_dev, "message buffer full");
+ }
+ if (temp[3] & IPMI_WDT_PRE_TIMEOUT) {
+ device_printf(sc->ipmi_dev,
+ "watchdog about to go off");
+ }
+ if (temp[3] & IPMI_MSG_AVAILABLE) {
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_GET_MSG;
+ ipmi_write(sc->ipmi_dev, temp, 2);
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, IPMI_MAX_RX);
+ error = ipmi_read(dev, temp, IPMI_MAX_RX);
+
+ device_printf(sc->ipmi_dev, "throw out message ");
+ dump_buf(temp, 16);
+ }
+ } else
+ return -1;
+ return error;
+}
+#endif
+
+static int
+ipmi_ready_to_read(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int status, flags;
+
+ if (sc->ipmi_bios_info.smic_mode) {
+ flags = INB(sc, sc->ipmi_smic_flags);
+#ifdef IPMB
+ if (flags & SMIC_STATUS_SMS_ATN) {
+ ipmi_handle_attn(dev);
+ return 0;
+ }
+#endif
+ if (flags & SMIC_STATUS_RX_RDY)
+ return 1;
+ } else if (sc->ipmi_bios_info.kcs_mode) {
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+#ifdef IPMB
+ if (status & KCS_STATUS_SMS_ATN) {
+ ipmi_handle_attn(dev);
+ return 0;
+ }
+#endif
+ if (status & KCS_STATUS_OBF)
+ return 1;
+ } else {
+ device_printf(dev,"Unsupported mode\n");
+ }
+
+ return 0;
+}
+
+void
+ipmi_intr(void *arg) {
+ device_t dev = arg;
+
+ ipmi_check_read(dev);
+}
+
+static void
+ipmi_check_read(device_t dev){
+ struct ipmi_softc *sc = device_get_softc(dev);
+ struct ipmi_done_list *item;
+ int status;
+ u_char *temp;
+
+ if (!sc->ipmi_requests)
+ return;
+
+ untimeout((timeout_t *)ipmi_check_read, dev, sc->ipmi_timeout_handle);
+
+ if(ipmi_ready_to_read(dev)) {
+ sc->ipmi_requests--;
+ temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK);
+ bzero(temp, IPMI_MAX_RX);
+ status = ipmi_read(dev, temp, IPMI_MAX_RX);
+ item = malloc(sizeof(struct ipmi_done_list), M_IPMI, M_WAITOK);
+ bzero(item, sizeof(struct ipmi_done_list));
+ item->data = temp;
+ item->len = status;
+ if (ticks - sc->ipmi_timestamp > MAX_TIMEOUT) {
+ device_printf(dev, "read timeout when ready\n");
+ TAILQ_INSERT_TAIL(&sc->ipmi_done, item, list);
+ selwakeup(&sc->ipmi_select);
+ } else if (status) {
+ TAILQ_INSERT_TAIL(&sc->ipmi_done, item, list);
+ selwakeup(&sc->ipmi_select);
+ }
+ } else {
+ if (ticks - sc->ipmi_timestamp > MAX_TIMEOUT) {
+ sc->ipmi_requests--;
+ device_printf(dev, "read timeout when not ready\n");
+ temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK);
+ bzero(temp, IPMI_MAX_RX);
+ sc->ipmi_busy = 0;
+ wakeup(&sc->ipmi_busy);
+ status = -1;
+ item = malloc(sizeof(struct ipmi_done_list),
+ M_IPMI, M_WAITOK);
+ bzero(item, sizeof(struct ipmi_done_list));
+ item->data = temp;
+ item->len = status;
+ TAILQ_INSERT_TAIL(&sc->ipmi_done, item, list);
+ selwakeup(&sc->ipmi_select);
+ }
+ }
+ if (sc->ipmi_requests)
+ sc->ipmi_timeout_handle
+ = timeout((timeout_t *)ipmi_check_read, dev, hz/30);
+}
+
+static int
+ipmi_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
+ int flags, struct thread *td)
+{
+ struct ipmi_softc *sc;
+ struct ipmi_req *req = (struct ipmi_req *)data;
+ struct ipmi_recv *recv = (struct ipmi_recv *)data;
+ struct ipmi_addr addr;
+ struct ipmi_done_list *item;
+ u_char *temp;
+ int error, len;
+
+ sc = dev->si_drv1;
+
+ switch (cmd) {
+ case IPMICTL_SEND_COMMAND:
+ /* clear out old stuff in queue of stuff done */
+ while((item = TAILQ_FIRST(&sc->ipmi_done))) {
+ TAILQ_REMOVE(&sc->ipmi_done, item, list);
+ free(item->data, M_IPMI);
+ free(item, M_IPMI);
+ }
+
+ error = copyin(req->addr, &addr, sizeof(addr));
+ temp = malloc(req->msg.data_len + 2, M_IPMI, M_WAITOK);
+ if (temp == NULL) {
+ return ENOMEM;
+ }
+ temp[0] = req->msg.netfn << 2;
+ temp[1] = req->msg.cmd;
+ error = copyin(req->msg.data, &temp[2],
+ req->msg.data_len);
+ if (error != 0) {
+ free(temp, M_IPMI);
+ return error;
+ }
+ error = ipmi_write(sc->ipmi_dev,
+ temp, req->msg.data_len + 2);
+ free(temp, M_IPMI);
+
+ if (error != 1)
+ return EIO;
+ sc->ipmi_requests++;
+ sc->ipmi_timestamp = ticks;
+ ipmi_check_read(sc->ipmi_dev);
+
+ return 0;
+ case IPMICTL_RECEIVE_MSG_TRUNC:
+ case IPMICTL_RECEIVE_MSG:
+ item = TAILQ_FIRST(&sc->ipmi_done);
+ if (!item) {
+ return EAGAIN;
+ }
+
+ error = copyin(recv->addr, &addr, sizeof(addr));
+ if (error != 0)
+ return error;
+ TAILQ_REMOVE(&sc->ipmi_done, item, list);
+ addr.channel = IPMI_BMC_CHANNEL;
+ recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
+ recv->msgid = item->msgid;
+ recv->msg.netfn = item->data[0] >> 2;
+ recv->msg.cmd = item->data[1];
+ error = len = item->len;
+ len -= 2;
+ if (len < 0)
+ len = 1;
+ if (recv->msg.data_len < len && cmd == IPMICTL_RECEIVE_MSG) {
+ TAILQ_INSERT_HEAD(&sc->ipmi_done, item, list);
+ return EMSGSIZE;
+ }
+ len = min(recv->msg.data_len, len);
+ recv->msg.data_len = len;
+ error = copyout(&addr, recv->addr,sizeof(addr));
+ if (error == 0)
+ error = copyout(&item->data[2], recv->msg.data, len);
+ free(item->data, M_IPMI);
+ free(item, M_IPMI);
+
+ if (error != 0)
+ return error;
+ return 0;
+ case IPMICTL_SET_MY_ADDRESS_CMD:
+ sc->ipmi_address = *(int*)data;
+ return 0;
+ case IPMICTL_GET_MY_ADDRESS_CMD:
+ *(int*)data = sc->ipmi_address;
+ return 0;
+ case IPMICTL_SET_MY_LUN_CMD:
+ sc->ipmi_lun = *(int*)data & 0x3;
+ return 0;
+ case IPMICTL_GET_MY_LUN_CMD:
+ *(int*)data = sc->ipmi_lun;
+ return 0;
+ case IPMICTL_SET_GETS_EVENTS_CMD:
+ /*
+ device_printf(sc->ipmi_dev,
+ "IPMICTL_SET_GETS_EVENTS_CMD NA\n");
+ */
+ return 0;
+ case IPMICTL_REGISTER_FOR_CMD:
+ case IPMICTL_UNREGISTER_FOR_CMD:
+ return EOPNOTSUPP;
+ }
+
+ device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd);
+
+ return ENOIOCTL;
+}
+
+static int
+ipmi_wait_for_ibf(device_t dev, int state) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int status, start = ticks;
+ int first = 1;
+
+ if (state == 0) {
+ /* WAIT FOR IBF = 0 */
+ do {
+ if (first)
+ first =0;
+ else
+ DELAY(100);
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+ } while (ticks - start < MAX_TIMEOUT
+ && status & KCS_STATUS_IBF);
+ } else {
+ /* WAIT FOR IBF = 1 */
+ do {
+ if (first)
+ first =0;
+ else
+ DELAY(100);
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+ } while (ticks - start < MAX_TIMEOUT
+ && !(status & KCS_STATUS_IBF));
+ }
+ return status;
+}
+
+static int
+ipmi_wait_for_obf(device_t dev, int state) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int status, start = ticks;
+ int first = 1;
+
+ if (state == 0) {
+ /* WAIT FOR OBF = 0 */
+ do {
+ if (first)
+ first = 0;
+ else
+ DELAY(100);
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+ } while (ticks - start < MAX_TIMEOUT
+ && status & KCS_STATUS_OBF);
+ } else {
+ /* WAIT FOR OBF = 1 */
+ do {
+ if (first)
+ first =0;
+ else
+ DELAY(100);
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+ } while (ticks - start < MAX_TIMEOUT
+ && !(status & KCS_STATUS_OBF));
+ }
+ return status;
+}
+
+static void
+ipmi_clear_obf(device_t dev, int status) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int data;
+
+ /* Clear OBF */
+ if (status & KCS_STATUS_OBF) {
+ data = INB(sc, sc->ipmi_kcs_data_out_reg);
+ }
+}
+
+static void
+ipmi_error(device_t dev) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int status, data = 0;
+ int retry = 0;
+
+ for(;;){
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ /* ABORT */
+ OUTB(sc, sc->ipmi_kcs_command_reg,
+ KCS_CONTROL_GET_STATUS_ABORT);
+
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ /* Clear OBF */
+ ipmi_clear_obf(dev, status);
+
+ if (status & KCS_STATUS_OBF) {
+ data = INB(sc, sc->ipmi_kcs_data_out_reg);
+ device_printf(dev, "Data %x\n", data);
+ }
+
+ /* 0x00 to DATA_IN */
+ OUTB(sc, sc->ipmi_kcs_data_in_reg, 0x00);
+
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
+
+ /* Wait for OBF = 1 */
+ status = ipmi_wait_for_obf(dev, 1);
+
+ /* Read error status */
+ data = INB(sc, sc->ipmi_kcs_data_out_reg);
+
+ /* Write READ into Data_in */
+ OUTB(sc, sc->ipmi_kcs_data_in_reg, KCS_DATA_IN_READ);
+
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+ }
+
+ /* IDLE STATE */
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
+ /* Wait for OBF = 1 */
+ status = ipmi_wait_for_obf(dev, 1);
+
+ /* Clear OBF */
+ ipmi_clear_obf(dev, status);
+ break;
+ }
+
+ retry++;
+ if (retry > 2) {
+ device_printf(dev, "Retry exhausted %x\n", retry);
+ break;
+ }
+ }
+}
+
+static void
+ipmi_wait_for_tx_okay(device_t dev) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int flags;
+
+ do {
+ flags = INB(sc, sc->ipmi_smic_flags);
+ } while(!flags & SMIC_STATUS_TX_RDY);
+}
+
+static void
+ipmi_wait_for_rx_okay(device_t dev) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int flags;
+
+ do {
+ flags = INB(sc, sc->ipmi_smic_flags);
+ } while(!flags & SMIC_STATUS_RX_RDY);
+}
+
+static void
+ipmi_wait_for_not_busy(device_t dev) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int flags;
+
+ do {
+ flags = INB(sc, sc->ipmi_smic_flags);
+ } while(flags & SMIC_STATUS_BUSY);
+}
+
+static void
+ipmi_set_busy(device_t dev) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int flags;
+
+ flags = INB(sc, sc->ipmi_smic_flags);
+ flags |= SMIC_STATUS_BUSY;
+ OUTB(sc, sc->ipmi_smic_flags, flags);
+}
+
+int
+ipmi_read(device_t dev, u_char *bytes, int len){
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int status, flags, data, i = -1, error;
+
+ bzero(bytes, len);
+ if (sc->ipmi_bios_info.smic_mode) {
+ ipmi_wait_for_not_busy(dev);
+ do {
+ flags = INB(sc, sc->ipmi_smic_flags);
+ } while(!flags & SMIC_STATUS_RX_RDY);
+
+ OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_RD_START);
+ ipmi_wait_for_rx_okay(dev);
+ ipmi_set_busy(dev);
+ ipmi_wait_for_not_busy(dev);
+ status = INB(sc, sc->ipmi_smic_ctl_sts);
+ if (status != SMIC_SC_SMS_RD_START) {
+ error = INB(sc, sc->ipmi_smic_data);
+ device_printf(dev, "Read did not start %x %x\n",
+ status, error);
+ sc->ipmi_busy = 0;
+ return -1;
+ }
+ for (i = -1; ; len--) {
+ i++;
+ data = INB(sc, sc->ipmi_smic_data);
+ if (len > 0)
+ *bytes++ = data;
+ else {
+ device_printf(dev, "Read short %x\n", data);
+ break;
+ }
+ do {
+ flags = INB(sc, sc->ipmi_smic_flags);
+ } while(!flags & SMIC_STATUS_RX_RDY);
+
+ OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_RD_NEXT);
+ ipmi_wait_for_rx_okay(dev);
+ ipmi_set_busy(dev);
+ ipmi_wait_for_not_busy(dev);
+ status = INB(sc, sc->ipmi_smic_ctl_sts);
+ if (status == SMIC_SC_SMS_RD_NEXT) {
+ continue;
+ } else if (status == SMIC_SC_SMS_RD_END) {
+ break;
+ } else {
+ device_printf(dev, "Read did not next %x\n",
+ status);
+ }
+ }
+ i++;
+ data = INB(sc, sc->ipmi_smic_data);
+ if (len > 0)
+ *bytes++ = data;
+ else
+ device_printf(dev, "Read short %x\n", data);
+
+ OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_RD_END);
+ i++;
+
+ } else if (sc->ipmi_bios_info.kcs_mode) {
+ for (i = -1; ; len--) {
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ /* Read State */
+ if (KCS_STATUS_STATE(status)
+ == KCS_STATUS_STATE_READ) {
+ i++;
+
+ /* Wait for OBF = 1 */
+ status = ipmi_wait_for_obf(dev, 1);
+
+ /* Read Data_out */
+ data = INB(sc, sc->ipmi_kcs_data_out_reg);
+ if (len > 0)
+ *bytes++ = data;
+ else {
+ device_printf(dev, "Read short %x byte %d\n", data, i);
+ break;
+ }
+
+ /* Write READ into Data_in */
+ OUTB(sc, sc->ipmi_kcs_data_in_reg,
+ KCS_DATA_IN_READ);
+
+ /* Idle State */
+ } else if (KCS_STATUS_STATE(status)
+ == KCS_STATUS_STATE_IDLE) {
+ i++;
+
+ /* Wait for OBF = 1*/
+ status = ipmi_wait_for_obf(dev, 1);
+
+ /* Read Dummy */
+ data = INB(sc, sc->ipmi_kcs_data_out_reg);
+ break;
+
+ /* error state */
+ } else {
+ device_printf(dev,
+ "read status error %x byte %d\n",
+ status, i);
+ sc->ipmi_busy = 0;
+ ipmi_error(dev);
+ return -1;
+ }
+ }
+ } else {
+ device_printf(dev, "Unsupported mode\n");
+ }
+ sc->ipmi_busy = 0;
+ wakeup(&sc->ipmi_busy);
+
+ return i;
+}
+
+
+static int
+ipmi_write(device_t dev, u_char *bytes, int len){
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int status, flags, retry;
+
+ while(sc->ipmi_busy){
+ status = tsleep(&sc->ipmi_busy, PCATCH, "ipmi", 0);
+ if (status)
+ return status;
+ }
+ sc->ipmi_busy = 1;
+ if (sc->ipmi_bios_info.smic_mode) {
+ ipmi_wait_for_not_busy(dev);
+
+ OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_WR_START);
+ ipmi_wait_for_tx_okay(dev);
+ OUTB(sc, sc->ipmi_smic_data, *bytes++);
+ len--;
+ ipmi_set_busy(dev);
+ ipmi_wait_for_not_busy(dev);
+ status = INB(sc, sc->ipmi_smic_ctl_sts);
+ if (status != SMIC_SC_SMS_WR_START) {
+ device_printf(dev, "Write did not start %x\n",status);
+ sc->ipmi_busy = 0;
+ return -1;
+ }
+ for(len--; len; len--) {
+ OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_WR_NEXT);
+ ipmi_wait_for_tx_okay(dev);
+ OUTB(sc, sc->ipmi_smic_data, *bytes++);
+ ipmi_set_busy(dev);
+ ipmi_wait_for_not_busy(dev);
+ status = INB(sc, sc->ipmi_smic_ctl_sts);
+ if (status != SMIC_SC_SMS_WR_NEXT) {
+ device_printf(dev, "Write did not next %x\n",
+ status);
+ sc->ipmi_busy = 0;
+ return -1;
+ }
+ }
+ do {
+ flags = INB(sc, sc->ipmi_smic_flags);
+ } while(!flags & SMIC_STATUS_TX_RDY);
+ OUTB(sc, sc->ipmi_smic_ctl_sts, SMIC_CC_SMS_WR_END);
+ ipmi_wait_for_tx_okay(dev);
+ OUTB(sc, sc->ipmi_smic_data, *bytes);
+ ipmi_set_busy(dev);
+ ipmi_wait_for_not_busy(dev);
+ status = INB(sc, sc->ipmi_smic_ctl_sts);
+ if (status != SMIC_SC_SMS_WR_END) {
+ device_printf(dev, "Write did not end %x\n",status);
+ return -1;
+ }
+ } else if (sc->ipmi_bios_info.kcs_mode) {
+ for (retry = 0; retry < 10; retry++) {
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ /* Clear OBF */
+ ipmi_clear_obf(dev, status);
+
+ /* Write start to command */
+ OUTB(sc, sc->ipmi_kcs_command_reg,
+ KCS_CONTROL_WRITE_START);
+
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE)
+ break;
+ DELAY(1000000);
+ }
+
+ for(len--; len; len--) {
+ if (KCS_STATUS_STATE(status)
+ != KCS_STATUS_STATE_WRITE) {
+ /* error state */
+ device_printf(dev, "status error %x\n",status);
+ ipmi_error(dev);
+ sc->ipmi_busy = 0;
+ return -1;
+ break;
+ } else {
+ /* Clear OBF */
+ ipmi_clear_obf(dev, status);
+
+ /* Data to Data */
+ OUTB(sc, sc->ipmi_kcs_data_out_reg, *bytes++);
+
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ if (KCS_STATUS_STATE(status)
+ != KCS_STATUS_STATE_WRITE) {
+ device_printf(dev, "status error %x\n"
+ ,status);
+ ipmi_error(dev);
+ return -1;
+ } else {
+ /* Clear OBF */
+ ipmi_clear_obf(dev, status);
+ }
+ }
+ }
+ /* Write end to command */
+ OUTB(sc, sc->ipmi_kcs_command_reg, KCS_CONTROL_WRITE_END);
+
+ /* Wait for IBF = 0 */
+ status = ipmi_wait_for_ibf(dev, 0);
+
+ if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE) {
+ /* error state */
+ device_printf(dev, "status error %x\n",status);
+ ipmi_error(dev);
+ sc->ipmi_busy = 0;
+ return -1;
+ } else {
+ /* Clear OBF */
+ ipmi_clear_obf(dev, status);
+ OUTB(sc, sc->ipmi_kcs_data_out_reg, *bytes++);
+ }
+ } else {
+ device_printf(dev, "Unsupported mode\n");
+ }
+ sc->ipmi_busy = 2;
+ return 1;
+}
+
+/*
+ * Watchdog event handler.
+ */
+
+static void
+ipmi_set_watchdog(device_t dev, int sec) {
+ u_char *temp;
+ int s;
+
+ temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK);
+
+ temp[0] = IPMI_APP_REQUEST << 2;
+ if (sec) {
+ temp[1] = IPMI_SET_WDOG;
+ temp[2] = IPMI_SET_WD_TIMER_DONT_STOP
+ | IPMI_SET_WD_TIMER_SMS_OS;
+ temp[3] = IPMI_SET_WD_ACTION_RESET;
+ temp[4] = 0;
+ temp[5] = 0; /* Timer use */
+ temp[6] = (sec * 10) & 0xff;
+ temp[7] = (sec * 10) / 2550;
+ } else {
+ temp[1] = IPMI_SET_WDOG;
+ temp[2] = IPMI_SET_WD_TIMER_SMS_OS;
+ temp[3] = 0;
+ temp[4] = 0;
+ temp[5] = 0; /* Timer use */
+ temp[6] = 0;
+ temp[7] = 0;
+ }
+
+ s = splhigh();
+ ipmi_write(dev, temp, 8);
+
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, IPMI_MAX_RX);
+ ipmi_read(dev, temp, IPMI_MAX_RX);
+
+ if (sec) {
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_RESET_WDOG;
+
+ ipmi_write(dev, temp, 2);
+
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, IPMI_MAX_RX);
+ ipmi_read(dev, temp, IPMI_MAX_RX);
+ }
+ splx(s);
+
+ free(temp, M_IPMI);
+ /*
+ dump_watchdog(dev);
+ */
+}
+
+static void
+ipmi_wd_event(void *arg, unsigned int cmd, int *error)
+{
+ struct ipmi_softc *sc = arg;
+ unsigned int timeout;
+
+ /* disable / enable */
+ if (!(cmd & WD_ACTIVE)) {
+ ipmi_set_watchdog(sc->ipmi_dev, 0);
+ *error = 0;
+ return;
+ }
+
+ cmd &= WD_INTERVAL;
+ /* convert from power-of-to-ns to WDT ticks */
+ if (cmd >= 64) {
+ *error = EINVAL;
+ return;
+ }
+ timeout = ((uint64_t)1 << cmd) / 1800000000;
+
+ /* reload */
+ ipmi_set_watchdog(sc->ipmi_dev, timeout);
+
+ *error = 0;
+}
+
+int
+ipmi_attach(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ u_char temp[1024];
+ int i;
+ int status;
+ int unit;
+
+ TAILQ_INIT(&sc->ipmi_done);
+ sc->ipmi_address = IPMI_BMC_SLAVE_ADDR;
+ sc->ipmi_lun = IPMI_BMC_SMS_LUN;
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_GET_DEVICE_ID;
+ ipmi_write(dev, temp, 2);
+
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, sizeof(temp));
+ ipmi_read(dev, temp, sizeof(temp));
+ device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d, "
+ "version %d.%d\n",
+ temp[4] & 0x0f,
+ temp[5] & 0x0f, temp[7],
+ temp[7] & 0x0f, temp[7] >> 4);
+
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_CLEAR_FLAGS;
+ temp[2] = 8;
+ ipmi_write(dev, temp, 3);
+
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, sizeof(temp));
+ ipmi_read(dev, temp, sizeof(temp));
+ if (temp[2] == 0xc0) {
+ device_printf(dev, "Clear flags is busy\n");
+ }
+ if (temp[2] == 0xc1) {
+ device_printf(dev, "Clear flags illegal\n");
+ }
+
+ for(i = 0; i < 8; i++){
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_GET_CHANNEL_INFO;
+ temp[2] = i;
+ ipmi_write(dev, temp, 3);
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, sizeof(temp));
+ ipmi_read(dev, temp, sizeof(temp));
+ if (temp[2]) {
+ break;
+ }
+ }
+ device_printf(dev, "Number of channels %d\n", i);
+
+ /* probe for watchdog */
+ bzero(temp, sizeof(temp));
+ temp[0] = IPMI_APP_REQUEST << 2;
+ temp[1] = IPMI_GET_WDOG;
+ status = ipmi_write(dev, temp, 2);
+ while (!ipmi_ready_to_read(dev))
+ DELAY(1000);
+ bzero(temp, sizeof(temp));
+ ipmi_read(dev, temp, sizeof(temp));
+ if (temp[0] == 0x1c && temp[2] == 0x00) {
+ device_printf(dev, "Attached watchdog\n");
+ /* register the watchdog event handler */
+ sc->ipmi_ev_tag = EVENTHANDLER_REGISTER(watchdog_list,
+ ipmi_wd_event, sc, 0);
+ }
+ unit = device_get_unit(sc->ipmi_dev);
+ /* force device to be ipmi0 since that is what ipmitool expects */
+ sc->ipmi_dev_t = make_dev(&ipmi_cdevsw, unit, UID_ROOT, GID_OPERATOR,
+ 0660, "ipmi%d", 0);
+ sc->ipmi_dev_t->si_drv1 = sc;
+
+ ipmi_attached = 1;
+
+ return 0;
+}
+
+int
+ipmi_detach(device_t dev)
+{
+ struct ipmi_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->ipmi_requests)
+ untimeout((timeout_t *)ipmi_check_read, dev,
+ sc->ipmi_timeout_handle);
+ destroy_dev(sc->ipmi_dev_t);
+ return 0;
+}
+
+#ifdef DEBUG
+static void
+dump_buf(u_char *data, int len){
+ char buf[20];
+ char line[1024];
+ char temp[30];
+ int count = 0;
+ int i=0;
+
+ printf("Address %p len %d\n", data, len);
+ if (len > 256)
+ len = 256;
+ line[0] = '\000';
+ for (; len > 0; len--, data++) {
+ sprintf(temp, "%02x ", *data);
+ strcat(line, temp);
+ if (*data >= ' ' && *data <= '~')
+ buf[count] = *data;
+ else if (*data >= 'A' && *data <= 'Z')
+ buf[count] = *data;
+ else
+ buf[count] = '.';
+ if (++count == 16) {
+ buf[count] = '\000';
+ count = 0;
+ printf(" %3x %s %s\n", i, line, buf);
+ i+=16;
+ line[0] = '\000';
+ }
+ }
+ buf[count] = '\000';
+
+ for (; count != 16; count++) {
+ strcat(line, " ");
+ }
+ printf(" %3x %s %s\n", i, line, buf);
+}
+#endif
diff --git a/sys/dev/ipmi/ipmi_pci.c b/sys/dev/ipmi/ipmi_pci.c
new file mode 100644
index 0000000..b91891d
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_pci.c
@@ -0,0 +1,309 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/selinfo.h>
+
+#include <sys/bus.h>
+#include <sys/conf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#ifdef LOCAL_MODULE
+#include <ipmivars.h>
+#else
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+static int ipmi_pci_probe(device_t dev);
+static int ipmi_pci_attach(device_t dev);
+static int ipmi_pci_detach(device_t dev);
+
+static device_method_t ipmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipmi_pci_probe),
+ DEVMETHOD(device_attach, ipmi_pci_attach),
+ DEVMETHOD(device_detach, ipmi_pci_detach),
+ { 0, 0 }
+};
+
+struct ipmi_ident
+{
+ u_int16_t vendor;
+ u_int16_t device;
+ char *desc;
+} ipmi_identifiers[] = {
+ {0x1028, 0x000d, "Dell PE2650 SMIC interface"},
+ {0, 0, 0}
+};
+
+static int
+ipmi_pci_probe(device_t dev) {
+ struct ipmi_ident *m;
+
+ if (ipmi_attached)
+ return ENXIO;
+
+ for (m = ipmi_identifiers; m->vendor != 0; m++) {
+ if ((m->vendor == pci_get_vendor(dev)) &&
+ (m->device == pci_get_device(dev))) {
+ device_set_desc(dev, m->desc);
+ return (BUS_PROBE_DEFAULT);
+ }
+ }
+
+ return ENXIO;
+}
+
+static int
+ipmi_pci_attach(device_t dev) {
+ struct ipmi_softc *sc = device_get_softc(dev);
+ device_t parent, smbios_attach_dev = NULL;
+ devclass_t dc;
+ int status, flags;
+ int error;
+
+
+ /*
+ * We need to attach to something that can address the BIOS/
+ * SMBIOS memory range. This is usually the isa bus however
+ * during a static kernel boot the isa bus is not available
+ * so we run up the tree to the nexus bus. A module load
+ * will use the isa bus attachment. If neither work bail
+ * since the SMBIOS defines stuff we need to know to attach to
+ * this device.
+ */
+ dc = devclass_find("isa");
+ if (dc != NULL) {
+ smbios_attach_dev = devclass_get_device(dc, 0);
+ }
+
+ if (smbios_attach_dev == NULL) {
+ smbios_attach_dev = dev;
+ for (;;) {
+ parent = device_get_parent(smbios_attach_dev);
+ if (parent == NULL)
+ break;
+ if (strcmp(device_get_name(smbios_attach_dev),
+ "nexus") == 0)
+ break;
+ smbios_attach_dev = parent;
+ }
+ }
+
+ if (smbios_attach_dev == NULL) {
+ device_printf(dev, "Couldn't find isa/nexus device\n");
+ goto bad;
+ }
+ sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev);
+ if (sc->ipmi_smbios_dev == NULL) {
+ device_printf(dev, "Couldn't find isa device\n");
+ goto bad;
+ }
+ error = ipmi_smbios_probe(sc->ipmi_smbios_dev);
+ if (error != 0) {
+ goto bad;
+ }
+ sc->ipmi_dev = dev;
+ error = ipmi_smbios_query(dev);
+ device_delete_child(dev, sc->ipmi_smbios_dev);
+ if (error != 0)
+ goto bad;
+
+ /* Now we know about the IPMI attachment info. */
+ if (sc->ipmi_bios_info.kcs_mode) {
+ if (sc->ipmi_bios_info.io_mode)
+ device_printf(dev, "KCS mode found at io 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+ else
+ device_printf(dev, "KCS mode found at mem 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+
+ sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset;
+ sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset;
+ sc->ipmi_kcs_data_out_reg = 0;
+ sc->ipmi_kcs_data_in_reg = 0;
+
+ if (sc->ipmi_bios_info.io_mode) {
+ sc->ipmi_io_rid = PCIR_BAR(0);
+ sc->ipmi_io_res = bus_alloc_resource(dev,
+ SYS_RES_IOPORT, &sc->ipmi_io_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 2),
+ sc->ipmi_bios_info.offset * 2,
+ RF_ACTIVE);
+ } else {
+ sc->ipmi_mem_rid = PCIR_BAR(0);
+ sc->ipmi_mem_res = bus_alloc_resource(dev,
+ SYS_RES_MEMORY, &sc->ipmi_mem_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 2),
+ sc->ipmi_bios_info.offset * 2,
+ RF_ACTIVE);
+ }
+
+ if (!sc->ipmi_io_res){
+ device_printf(dev, "couldn't configure pci io res\n");
+ goto bad;
+ }
+
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+ if (status == 0xff) {
+ device_printf(dev, "couldn't find it\n");
+ goto bad;
+ }
+ if(status & KCS_STATUS_OBF){
+ ipmi_read(dev, NULL, 0);
+ }
+ } else if (sc->ipmi_bios_info.smic_mode) {
+ if (sc->ipmi_bios_info.io_mode)
+ device_printf(dev, "SMIC mode found at io 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+ else
+ device_printf(dev, "SMIC mode found at mem 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+
+ sc->ipmi_smic_data = 0;
+ sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
+ sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2;
+
+ if (sc->ipmi_bios_info.io_mode) {
+ sc->ipmi_io_rid = PCIR_BAR(0);
+ sc->ipmi_io_res = bus_alloc_resource(dev,
+ SYS_RES_IOPORT, &sc->ipmi_io_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 3),
+ sc->ipmi_bios_info.offset * 3,
+ RF_ACTIVE);
+ } else {
+ sc->ipmi_mem_rid = PCIR_BAR(0);
+ sc->ipmi_mem_res = bus_alloc_resource(dev,
+ SYS_RES_MEMORY, &sc->ipmi_mem_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 2),
+ sc->ipmi_bios_info.offset * 2,
+ RF_ACTIVE);
+ }
+
+ if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
+ device_printf(dev, "couldn't configure pci res\n");
+ goto bad;
+ }
+
+ flags = INB(sc, sc->ipmi_smic_flags);
+ if (flags == 0xff) {
+ device_printf(dev, "couldn't find it\n");
+ goto bad;
+ }
+ if ((flags & SMIC_STATUS_SMS_ATN)
+ && (flags & SMIC_STATUS_RX_RDY)){
+ ipmi_read(dev, NULL, 0);
+ }
+ } else {
+ device_printf(dev, "No IPMI interface found\n");
+ goto bad;
+ }
+ ipmi_attach(dev);
+
+ sc->ipmi_irq_rid = 0;
+ sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ,
+ &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
+ if (sc->ipmi_irq_res == NULL) {
+ device_printf(sc->ipmi_dev, "can't allocate interrupt\n");
+ } else {
+ if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res,
+ INTR_TYPE_MISC, ipmi_intr,
+ sc->ipmi_dev, &sc->ipmi_irq)) {
+ device_printf(sc->ipmi_dev,
+ "can't set up interrupt\n");
+ return (EINVAL);
+ }
+ }
+
+ return 0;
+bad:
+ return ENXIO;
+}
+
+static int ipmi_pci_detach(device_t dev) {
+ struct ipmi_softc *sc;
+
+ sc = device_get_softc(dev);
+ ipmi_detach(dev);
+ if (sc->ipmi_ev_tag)
+ EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
+
+ if (sc->ipmi_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
+ sc->ipmi_mem_res);
+ if (sc->ipmi_io_res)
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
+ sc->ipmi_io_res);
+ if (sc->ipmi_irq)
+ bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res,
+ sc->ipmi_irq);
+ if (sc->ipmi_irq_res)
+ bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ,
+ sc->ipmi_irq_rid, sc->ipmi_irq_res);
+
+ return 0;
+}
+
+static driver_t ipmi_pci_driver = {
+ "ipmi",
+ ipmi_methods,
+ sizeof(struct ipmi_softc)
+};
+
+DRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
diff --git a/sys/dev/ipmi/ipmi_smbios.c b/sys/dev/ipmi/ipmi_smbios.c
new file mode 100644
index 0000000..de14dfb
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_smbios.c
@@ -0,0 +1,538 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#include <sys/watchdog.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <machine/pc/bios.h>
+
+#ifdef LOCAL_MODULE
+#include <ipmi.h>
+#include <ipmivars.h>
+#else
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+struct smbios_table_entry {
+ uint8_t anchor_string[4];
+ uint8_t checksum;
+ uint8_t length;
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint16_t maximum_structure_size;
+ uint8_t entry_point_revision;
+ uint8_t formatted_area[5];
+ uint8_t DMI_anchor_string[5];
+ uint8_t intermediate_checksum;
+ uint16_t structure_table_length;
+ uint32_t structure_table_address;
+ uint16_t number_structures;
+ uint8_t BCD_revision;
+};
+
+struct structure_header {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+};
+
+struct ipmi_device {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+ uint8_t interface_type;
+ uint8_t spec_revision;
+ uint8_t i2c_slave_address;
+ uint8_t NV_storage_device_address;
+ uint64_t base_address;
+ uint8_t base_address_modifier;
+ uint8_t interrupt_number;
+};
+
+#define SMBIOS_START 0xf0000
+#define SMBIOS_STEP 0x10
+#define SMBIOS_OFF 0
+#define SMBIOS_LEN 4
+#define SMBIOS_SIG "_SM_"
+
+devclass_t ipmi_devclass;
+typedef void (*dispatchproc_t)(uint8_t *p, char **table,
+ struct ipmi_get_info *info);
+
+static void smbios_run_table(uint8_t *, int, dispatchproc_t *,
+ struct ipmi_get_info *);
+static char *get_strings(char *, char **);
+static int smbios_cksum (struct smbios_table_entry *);
+static void smbios_t38_proc_info(uint8_t *, char **, struct ipmi_get_info *);
+static int ipmi_smbios_attach (device_t);
+static int ipmi_smbios_modevent(module_t, int, void *);
+
+static void
+smbios_t38_proc_info(uint8_t *p, char **table, struct ipmi_get_info *info)
+{
+ struct ipmi_device *s = (struct ipmi_device *) p;
+
+ bzero(info, sizeof(struct ipmi_get_info));
+ if (s->interface_type == 0x01)
+ info->kcs_mode = 1;
+ if (s->interface_type == 0x02)
+ info->smic_mode = 1;
+ info->address = s->base_address & ~1;
+ switch (s->base_address_modifier >> 6) {
+ case 0x00:
+ info->offset = 1;
+ break;
+ case 0x01:
+ info->offset = 4;
+ break;
+ case 0x10:
+ info->offset = 2;
+ break;
+ case 0x11:
+ info->offset = 0;
+ break;
+ }
+ info->io_mode = s->base_address & 1;
+}
+
+static char *
+get_strings(char *p, char **table)
+{
+ /* Scan for strings, stoping at a single null byte */
+ while (*p != 0) {
+ *table++ = p;
+ p += strlen(p) + 1;
+ }
+ *table = 0;
+
+ /* Skip past terminating null byte */
+ return p + 1;
+}
+
+
+static void
+smbios_run_table(uint8_t *p, int entries, dispatchproc_t *dispatchstatus,
+ struct ipmi_get_info *info)
+{
+ struct structure_header *s;
+ char *table[20];
+ uint8_t *nextp;
+
+ while(entries--) {
+ s = (struct structure_header *) p;
+ nextp = get_strings(p + s->length, table);
+
+ /*
+ * No strings still has a double-null at the end,
+ * skip over it
+ */
+ if (table[0] == 0)
+ nextp++;
+
+ if (dispatchstatus[*p]) {
+ (dispatchstatus[*p])(p, table, info);
+ }
+ p = nextp;
+ }
+}
+
+device_t
+ipmi_smbios_identify (driver_t *driver, device_t parent)
+{
+ device_t child = NULL;
+ u_int32_t addr;
+ int length;
+ int rid1, rid2;
+
+ addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN,
+ SMBIOS_STEP, SMBIOS_OFF);
+ if (addr != 0) {
+ rid1 = 0;
+ length = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr))
+ ->length;
+
+ child = BUS_ADD_CHILD(parent, 0, "ipmi", -1);
+ if (driver != NULL)
+ device_set_driver(child, driver);
+ bus_set_resource(child, SYS_RES_MEMORY, rid1, addr, length);
+ rid2 = 1;
+ length = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr))
+ ->structure_table_length;
+ addr = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr))
+ ->structure_table_address;
+ bus_set_resource(child, SYS_RES_MEMORY, rid2, addr, length);
+ device_set_desc(child, "System Management BIOS");
+ } else {
+ device_printf(parent, "Failed to find SMBIOS signature\n");
+ }
+
+ return child;
+}
+
+int
+ipmi_smbios_probe(device_t dev)
+{
+ struct resource *res1 = NULL, *res2 = NULL;
+ int rid1, rid2;
+ int error;
+
+ if (ipmi_attached)
+ return(EBUSY);
+
+ error = 0;
+ rid1 = 0;
+ rid2 = 1;
+ res1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid1,
+ 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE);
+
+ if (res1 == NULL) {
+ device_printf(dev, "Unable to allocate memory resource.\n");
+ error = ENOMEM;
+ goto bad;
+ }
+ res2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid2,
+ 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (res2 == NULL) {
+ device_printf(dev, "Unable to allocate memory resource.\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ if (smbios_cksum(
+ (struct smbios_table_entry *)rman_get_virtual(res1))) {
+ device_printf(dev, "SMBIOS checksum failed.\n");
+ error = ENXIO;
+ goto bad;
+ }
+
+bad:
+ if (res1)
+ bus_release_resource(dev, SYS_RES_MEMORY, rid1, res1);
+ if (res2)
+ bus_release_resource(dev, SYS_RES_MEMORY, rid2, res2);
+ return error;
+}
+
+
+int
+ipmi_smbios_query(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ dispatchproc_t dispatch_smbios_ipmi[256];
+ struct resource *res = NULL , *res2 = NULL;
+ int rid, rid2;
+ int error;
+
+ error = 0;
+
+ rid = 0;
+ res = bus_alloc_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, &rid,
+ 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE );
+ if (res == NULL) {
+ device_printf(dev, "Unable to allocate memory resource.\n");
+ error = ENOMEM;
+ goto bad;
+ }
+ rid2 = 1;
+ res2 = bus_alloc_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, &rid2,
+ 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (res2 == NULL) {
+ device_printf(dev, "Unable to allocate memory resource.\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ sc->ipmi_smbios =
+ (struct smbios_table_entry *)rman_get_virtual(res);
+
+ sc->ipmi_busy = 0;
+
+ device_printf(sc->ipmi_smbios_dev, "SMBIOS Version: %d.%02d",
+ sc->ipmi_smbios->major_version,
+ sc->ipmi_smbios->minor_version);
+ if (bcd2bin(sc->ipmi_smbios->BCD_revision))
+ printf(", revision: %d.%02d",
+ bcd2bin(sc->ipmi_smbios->BCD_revision >> 4),
+ bcd2bin(sc->ipmi_smbios->BCD_revision & 0x0f));
+ printf("\n");
+
+ bzero(&sc->ipmi_bios_info, sizeof(sc->ipmi_bios_info));
+
+ bzero((void *)dispatch_smbios_ipmi, sizeof(dispatch_smbios_ipmi));
+ dispatch_smbios_ipmi[38] = (void *)smbios_t38_proc_info;
+ smbios_run_table(
+ (void *)rman_get_virtual(res2),
+ sc->ipmi_smbios->number_structures,
+ dispatch_smbios_ipmi,
+ (void*)&sc->ipmi_bios_info);
+
+bad:
+ if (res)
+ bus_release_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY,
+ rid, res);
+ res = NULL;
+ if (res2)
+ bus_release_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY,
+ rid2, res2);
+ res2 = NULL;
+ return 0;
+}
+
+static int
+ipmi_smbios_attach(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int error;
+ int status, flags;
+
+ error = 0;
+ sc->ipmi_smbios_dev = dev;
+ sc->ipmi_dev = dev;
+ ipmi_smbios_query(dev);
+
+ if (sc->ipmi_bios_info.kcs_mode) {
+ if (sc->ipmi_bios_info.io_mode)
+ device_printf(dev, "KCS mode found at io 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+ else
+ device_printf(dev, "KCS mode found at mem 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+
+ sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset;
+ sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset;
+ sc->ipmi_kcs_data_out_reg = 0;
+ sc->ipmi_kcs_data_in_reg = 0;
+
+ if (sc->ipmi_bios_info.io_mode) {
+ sc->ipmi_io_rid = 2;
+ sc->ipmi_io_res = bus_alloc_resource(dev,
+ SYS_RES_IOPORT, &sc->ipmi_io_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 2),
+ sc->ipmi_bios_info.offset * 2,
+ RF_ACTIVE);
+ } else {
+ sc->ipmi_mem_rid = 2;
+ sc->ipmi_mem_res = bus_alloc_resource(dev,
+ SYS_RES_MEMORY, &sc->ipmi_mem_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 2),
+ sc->ipmi_bios_info.offset * 2,
+ RF_ACTIVE);
+ }
+
+ if (!sc->ipmi_io_res){
+ device_printf(dev,
+ "couldn't configure smbios io res\n");
+ goto bad;
+ }
+
+ status = INB(sc, sc->ipmi_kcs_status_reg);
+ if (status == 0xff) {
+ device_printf(dev, "couldn't find it\n");
+ error = ENXIO;
+ goto bad;
+ }
+ if(status & KCS_STATUS_OBF){
+ ipmi_read(dev, NULL, 0);
+ }
+ } else if (sc->ipmi_bios_info.smic_mode) {
+ if (sc->ipmi_bios_info.io_mode)
+ device_printf(dev, "SMIC mode found at io 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+ else
+ device_printf(dev, "SMIC mode found at mem 0x%llx "
+ "alignment 0x%x on %s\n",
+ (long long)sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.offset,
+ device_get_name(device_get_parent(sc->ipmi_dev)));
+
+ sc->ipmi_smic_data = 0;
+ sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
+ sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2;
+
+ if (sc->ipmi_bios_info.io_mode) {
+ sc->ipmi_io_rid = 2;
+ sc->ipmi_io_res = bus_alloc_resource(dev,
+ SYS_RES_IOPORT, &sc->ipmi_io_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 3),
+ sc->ipmi_bios_info.offset * 3,
+ RF_ACTIVE);
+ } else {
+ sc->ipmi_mem_res = bus_alloc_resource(dev,
+ SYS_RES_MEMORY, &sc->ipmi_mem_rid,
+ sc->ipmi_bios_info.address,
+ sc->ipmi_bios_info.address +
+ (sc->ipmi_bios_info.offset * 2),
+ sc->ipmi_bios_info.offset * 2,
+ RF_ACTIVE);
+ }
+
+ if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
+ device_printf(dev, "couldn't configure smbios res\n");
+ error = ENXIO;
+ goto bad;
+ }
+
+ flags = INB(sc, sc->ipmi_smic_flags);
+ if (flags == 0xff) {
+ device_printf(dev, "couldn't find it\n");
+ error = ENXIO;
+ goto bad;
+ }
+ if ((flags & SMIC_STATUS_SMS_ATN)
+ && (flags & SMIC_STATUS_RX_RDY)){
+ /* skip in smbio mode
+ ipmi_read(dev, NULL, 0);
+ */
+ }
+ } else {
+ device_printf(dev, "No IPMI interface found\n");
+ error = ENXIO;
+ goto bad;
+ }
+ ipmi_attach(dev);
+
+ return 0;
+bad:
+ /*
+ device_delete_child(device_get_parent(dev), dev);
+ */
+ return error;
+}
+
+static int
+smbios_cksum (struct smbios_table_entry *e)
+{
+ u_int8_t *ptr;
+ u_int8_t cksum;
+ int i;
+
+ ptr = (u_int8_t *)e;
+ cksum = 0;
+ for (i = 0; i < e->length; i++) {
+ cksum += ptr[i];
+ }
+
+ return cksum;
+}
+
+
+static int
+ipmi_smbios_detach (device_t dev)
+{
+ struct ipmi_softc *sc;
+
+ sc = device_get_softc(dev);
+ ipmi_detach(dev);
+ if (sc->ipmi_ev_tag)
+ EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
+
+ if (sc->ipmi_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
+ sc->ipmi_mem_res);
+ if (sc->ipmi_io_res)
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
+ sc->ipmi_io_res);
+ return 0;
+}
+
+
+static device_method_t ipmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, ipmi_smbios_identify),
+ DEVMETHOD(device_probe, ipmi_smbios_probe),
+ DEVMETHOD(device_attach, ipmi_smbios_attach),
+ DEVMETHOD(device_detach, ipmi_smbios_detach),
+ { 0, 0 }
+};
+
+static driver_t ipmi_smbios_driver = {
+ "ipmi",
+ ipmi_methods,
+ sizeof(struct ipmi_softc),
+};
+
+static int
+ipmi_smbios_modevent (mod, what, arg)
+ module_t mod;
+ int what;
+ void * arg;
+{
+ device_t * devs;
+ int count;
+ int i;
+
+ switch (what) {
+ case MOD_LOAD:
+ return 0;
+ case MOD_UNLOAD:
+ devclass_get_devices(ipmi_devclass, &devs, &count);
+ for (i = 0; i < count; i++) {
+ device_delete_child(device_get_parent(devs[i]),
+ devs[i]);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+DRIVER_MODULE(ipmi, isa, ipmi_smbios_driver, ipmi_devclass,
+ ipmi_smbios_modevent, 0);
diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h
new file mode 100644
index 0000000..c793eab
--- /dev/null
+++ b/sys/dev/ipmi/ipmivars.h
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+struct ipmi_get_info {
+ int kcs_mode;
+ int smic_mode;
+ uint64_t address;
+ int offset;
+ int io_mode;
+};
+
+struct ipmi_softc {
+ device_t ipmi_dev;
+ device_t ipmi_smbios_dev;
+ struct cdev *ipmi_dev_t;
+ int ipmi_refcnt;
+ struct smbios_table_entry *ipmi_smbios;
+ struct ipmi_get_info ipmi_bios_info;
+ int ipmi_kcs_status_reg;
+ int ipmi_kcs_command_reg;
+ int ipmi_kcs_data_out_reg;
+ int ipmi_kcs_data_in_reg;
+ int ipmi_smic_data;
+ int ipmi_smic_ctl_sts;
+ int ipmi_smic_flags;
+ int ipmi_io_rid;
+ struct resource * ipmi_io_res;
+ int ipmi_mem_rid;
+ struct resource * ipmi_mem_res;
+ int ipmi_irq_rid;
+ struct resource * ipmi_irq_res;
+ void *ipmi_irq;
+ u_char ipmi_address;
+ u_char ipmi_lun;
+ int ipmi_busy;
+ struct selinfo ipmi_select;
+ int ipmi_timestamp;
+ int ipmi_requests;
+ struct callout_handle ipmi_timeout_handle;
+ TAILQ_HEAD(,ipmi_done_list) ipmi_done;
+ eventhandler_tag ipmi_ev_tag;
+};
+
+struct ipmi_ipmb {
+ u_char foo;
+};
+
+/* KCS status flags */
+#define KCS_STATUS_OBF 0x01 /* Data Out ready from BMC */
+#define KCS_STATUS_IBF 0x02 /* Data In from System */
+#define KCS_STATUS_SMS_ATN 0x04 /* Ready in RX queue */
+#define KCS_STATUS_C_D 0x08 /* Command/Data register write*/
+#define KCS_STATUS_OEM1 0x10
+#define KCS_STATUS_OEM2 0x20
+#define KCS_STATUS_S0 0x40
+#define KCS_STATUS_S1 0x80
+ #define KCS_STATUS_STATE(x) ((x)>>6)
+ #define KCS_STATUS_STATE_IDLE 0x0
+ #define KCS_STATUS_STATE_READ 0x1
+ #define KCS_STATUS_STATE_WRITE 0x2
+ #define KCS_STATUS_STATE_ERROR 0x3
+#define KCS_IFACE_STATUS_ABORT 0x01
+#define KCS_IFACE_STATUS_ILLEGAL 0x02
+#define KCS_IFACE_STATUS_LENGTH_ERR 0x06
+
+/* KCD control codes */
+#define KCS_CONTROL_GET_STATUS_ABORT 0x60
+#define KCS_CONTROL_WRITE_START 0x61
+#define KCS_CONTROL_WRITE_END 0x62
+#define KCS_DATA_IN_READ 0x68
+
+/* SMIC status flags */
+#define SMIC_STATUS_BUSY 0x01 /* System set and BMC clears it */
+#define SMIC_STATUS_SMS_ATN 0x04 /* BMC has a message */
+#define SMIC_STATUS_EVT_ATN 0x08 /* Event has been RX */
+#define SMIC_STATUS_SMI 0x08 /* asserted SMI */
+#define SMIC_STATUS_TX_RDY 0x40 /* Ready to accept WRITE */
+#define SMIC_STATUS_RX_RDY 0x80 /* Ready to read */
+
+/* SMIC control codes */
+#define SMIC_CC_SMS_GET_STATUS 0x40
+#define SMIC_CC_SMS_WR_START 0x41
+#define SMIC_CC_SMS_WR_NEXT 0x42
+#define SMIC_CC_SMS_WR_END 0x43
+#define SMIC_CC_SMS_RD_START 0x44
+#define SMIC_CC_SMS_RD_NEXT 0x45
+#define SMIC_CC_SMS_RD_END 0x46
+
+/* SMIC status codes */
+#define SMIC_SC_SMS_RDY 0xc0
+#define SMIC_SC_SMS_WR_START 0xc1
+#define SMIC_SC_SMS_WR_NEXT 0xc2
+#define SMIC_SC_SMS_WR_END 0xc3
+#define SMIC_SC_SMS_RD_START 0xc4
+#define SMIC_SC_SMS_RD_NEXT 0xc5
+#define SMIC_SC_SMS_RD_END 0xc6
+
+#define RES(x) (x)->ipmi_io_res ? (x)->ipmi_io_res : (x)->ipmi_mem_res
+#define INB(sc, x) bus_space_read_1(rman_get_bustag(RES(sc)), \
+ rman_get_bushandle(RES(sc)), (x))
+#define OUTB(sc, x, value) bus_space_write_1(rman_get_bustag(RES(sc)), \
+ rman_get_bushandle(RES(sc)), (x), value)
+
+int ipmi_attach(device_t);
+int ipmi_detach(device_t);
+int ipmi_smbios_query(device_t);
+int ipmi_smbios_probe(device_t);
+int ipmi_read(device_t, u_char *, int);
+void ipmi_intr(void *);
+
+device_t ipmi_smbios_identify (driver_t *driver, device_t parent);
+extern devclass_t ipmi_devclass;
+extern int ipmi_attached;
diff --git a/sys/modules/ipmi/Makefile b/sys/modules/ipmi/Makefile
new file mode 100644
index 0000000..966ccd8
--- /dev/null
+++ b/sys/modules/ipmi/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/ipmi
+
+KMOD= ipmi
+SRCS= ipmi.c ipmi_pci.c ipmi_smbios.c
+SRCS+= bus_if.h device_if.h pci_if.h vnode_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/sys/ipmi.h b/sys/sys/ipmi.h
new file mode 100644
index 0000000..8832030
--- /dev/null
+++ b/sys/sys/ipmi.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#define IPMI_MAX_ADDR_SIZE 0x20
+#define IPMI_MAX_RX 1024
+#define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */
+#define IPMI_BMC_CHANNEL 0x0f /* Linux BMC channel */
+
+#define IPMI_BMC_SMS_LUN 0x02
+
+#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE 0x0c
+#define IPMI_IPMB_ADDR_TYPE 0x01
+#define IPMI_IPMB_BROADCAST_ADDR_TYPE 0x41
+
+#define IPMI_IOC_MAGIC 'i'
+#define IPMICTL_RECEIVE_MSG_TRUNC _IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv)
+#define IPMICTL_RECEIVE_MSG _IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv)
+#define IPMICTL_SEND_COMMAND _IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req)
+#define IPMICTL_REGISTER_FOR_CMD _IOW(IPMI_IOC_MAGIC, 14, struct ipmi_cmdspec)
+#define IPMICTL_UNREGISTER_FOR_CMD _IOW(IPMI_IOC_MAGIC, 15, struct ipmi_cmdspec)
+#define IPMICTL_SET_GETS_EVENTS_CMD _IOW(IPMI_IOC_MAGIC, 16, int)
+#define IPMICTL_SET_MY_ADDRESS_CMD _IOW(IPMI_IOC_MAGIC, 17, unsigned int)
+#define IPMICTL_GET_MY_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 18, unsigned int)
+#define IPMICTL_SET_MY_LUN_CMD _IOW(IPMI_IOC_MAGIC, 19, unsigned int)
+#define IPMICTL_GET_MY_LUN_CMD _IOR(IPMI_IOC_MAGIC, 20, unsigned int)
+
+#define IPMI_RESPONSE_RECV_TYPE 1
+#define IPMI_ASYNC_EVENT_RECV_TYPE 2
+#define IPMI_CMD_RECV_TYPE 3
+
+#define IPMI_APP_REQUEST 0x06
+#define IPMI_GET_DEVICE_ID 0x01
+#define IPMI_CLEAR_FLAGS 0x30
+#define IPMI_GET_MSG_FLAGS 0x31
+# define IPMI_MSG_AVAILABLE 0x01
+# define IPMI_MSG_BUFFER_FULL 0x02
+# define IPMI_WDT_PRE_TIMEOUT 0x08
+#define IPMI_GET_MSG 0x33
+#define IPMI_SEND_MSG 0x34
+#define IPMI_GET_CHANNEL_INFO 0x42
+#define IPMI_RESET_WDOG 0x22
+#define IPMI_SET_WDOG 0x24
+#define IPMI_GET_WDOG 0x25
+
+#define IPMI_SET_WD_TIMER_SMS_OS 0x04
+#define IPMI_SET_WD_TIMER_DONT_STOP 0x40
+#define IPMI_SET_WD_ACTION_RESET 0x01
+
+struct ipmi_msg {
+ unsigned char netfn;
+ unsigned char cmd;
+ unsigned short data_len;
+ unsigned char *data;
+};
+
+struct ipmi_req {
+ unsigned char *addr;
+ unsigned int addr_len;
+ long msgid;
+ struct ipmi_msg msg;
+};
+
+struct ipmi_recv {
+ int recv_type;
+ unsigned char *addr;
+ unsigned int addr_len;
+ long msgid;
+ struct ipmi_msg msg;
+};
+
+struct ipmi_cmdspec {
+ unsigned char netfn;
+ unsigned char cmd;
+};
+
+
+struct ipmi_addr {
+ int addr_type;
+ short channel;
+ unsigned char data[IPMI_MAX_ADDR_SIZE];
+};
+
+struct ipmi_system_interface_addr {
+ int addr_type;
+ short channel;
+ unsigned char lun;
+};
+
+struct ipmi_ipmb_addr {
+ int addr_type;
+ short channel;
+ unsigned char slave_addr;
+ unsigned char lun;
+};
OpenPOWER on IntegriCloud