summaryrefslogtreecommitdiffstats
path: root/sys/dev/ipmi
diff options
context:
space:
mode:
authorambrisko <ambrisko@FreeBSD.org>2006-02-10 20:51:35 +0000
committerambrisko <ambrisko@FreeBSD.org>2006-02-10 20:51:35 +0000
commitb7dae28d4aa65025d8b050fdb6b27c4513ea04c5 (patch)
tree29eb76da22030da0fe8abe3fb9217106ac073034 /sys/dev/ipmi
parent0b9aa32c53586867cabc1d7b02456ca0dfc44f9c (diff)
downloadFreeBSD-src-b7dae28d4aa65025d8b050fdb6b27c4513ea04c5.zip
FreeBSD-src-b7dae28d4aa65025d8b050fdb6b27c4513ea04c5.tar.gz
Add an OpenIPMI mostly compatible driver. This driver was developed
to work with ipmitools. It works with other tools that have an OpenIPMI driver interface. The port will need to get updated to used this. I have not implemented the IPMB mode yet so ioctl's for that don't really do much otherwise it should work like the OpenIPMI version. The ipmi.h definitions was derived from the ipmitool header file. The bus attachments are done for smbios and pci/smbios. Differences in bus probe order for modules/static are delt with. ACPI attachment should be done. This drivers registers with the watchdod(4) interface Work to do: - BT interface - IPMB mode This has been tested on Dell PE2850, PE2650 & PE850 with i386 & amd64 kernel. I will link this into the build on next week. Tom Rhodes, helped me with the man page. Sponsored by: IronPort Systems Inc. Inspired from: ipmitool & Linux
Diffstat (limited to 'sys/dev/ipmi')
-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
4 files changed, 2081 insertions, 0 deletions
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;
OpenPOWER on IntegriCloud