summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2006-09-22 22:11:29 +0000
committerjhb <jhb@FreeBSD.org>2006-09-22 22:11:29 +0000
commitcdd54d372bfbeaef61a9ae75207eb95fcea95a5a (patch)
treeb4530401f4d668bcc04bd21e8a352cd811260fba
parent22a36e67f2dc507b9d579ccc4b1a81de4741c860 (diff)
downloadFreeBSD-src-cdd54d372bfbeaef61a9ae75207eb95fcea95a5a.zip
FreeBSD-src-cdd54d372bfbeaef61a9ae75207eb95fcea95a5a.tar.gz
Update the ipmi(4) driver:
- Split out the communication protocols into their own files and use a couple of function pointers in the softc that the commuication protocols setup in their own attach routine. - Add support for the SSIF interface (talking to IPMI over SMBus). - Add an ACPI attachment. - Add a PCI attachment that attaches to devices with the IPMI interface subclass. - Split the ISA attachment out into its own file: ipmi_isa.c. - Change the code to probe the SMBIOS table for an IPMI entry to just use pmap_mapbios() to map the table in rather than trying to setup a fake resource on an isa device and then activating the resource to map in the table. - Make bus attachments leaner by adding attach functions for each communication interface (ipmi_kcs_attach(), ipmi_smic_attach(), etc.) that setup per-interface data. - Formalize the model used by the driver to handle requests by adding an explicit struct ipmi_request object that holds the state of a given request and reply for the entire lifetime of the request. By bundling the request into an object, it is easier to add retry logic to the various communication backends (as well as eventually support BT mode which uses a slightly different message format than KCS, SMIC, and SSIF). - Add a per-softc lock and remove D_NEEDGIANT as the driver is now MPSAFE. - Add 32-bit compatibility ioctl shims so you can use a 32-bit ipmitool on FreeBSD/amd64. - Add ipmi(4) to i386 and amd64 NOTES. Submitted by: ambrisko (large portions of 2 and 3) Sponsored by: IronPort Systems, Yahoo! MFC after: 6 days
-rw-r--r--sys/amd64/conf/NOTES2
-rw-r--r--sys/conf/files.amd648
-rw-r--r--sys/conf/files.i3868
-rw-r--r--sys/dev/ipmi/ipmi.c1445
-rw-r--r--sys/dev/ipmi/ipmi_acpi.c210
-rw-r--r--sys/dev/ipmi/ipmi_isa.c209
-rw-r--r--sys/dev/ipmi/ipmi_kcs.c607
-rw-r--r--sys/dev/ipmi/ipmi_pci.c420
-rw-r--r--sys/dev/ipmi/ipmi_smbios.c501
-rw-r--r--sys/dev/ipmi/ipmi_smbus.c131
-rw-r--r--sys/dev/ipmi/ipmi_smic.c361
-rw-r--r--sys/dev/ipmi/ipmi_ssif.c375
-rw-r--r--sys/dev/ipmi/ipmivars.h210
-rw-r--r--sys/i386/conf/NOTES2
-rw-r--r--sys/modules/ipmi/Makefile8
-rw-r--r--sys/sys/ipmi.h36
16 files changed, 3134 insertions, 1399 deletions
diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES
index d2d57f0..11870cb 100644
--- a/sys/amd64/conf/NOTES
+++ b/sys/amd64/conf/NOTES
@@ -346,6 +346,7 @@ options SAFE_RNDTEST # enable rndtest support
#
# Miscellaneous hardware:
#
+# ipmi: Intelligent Platform Management Interface
# smbios: DMI/SMBIOS entry point
# vpd: Vital Product Data kernel interface
# cy: Cyclades serial driver
@@ -368,6 +369,7 @@ device digi_EPCX_PCI
device digi_Xe
device digi_Xem
device digi_Xr
+device ipmi
# Parallel (8255 PPI) basic I/O (mode 0) port (e.g. Advantech PCL-724)
device pbio
hint.pbio.0.at="isa"
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index 5ddcb4b..15c71b4 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -161,7 +161,13 @@ dev/if_ndis/if_ndis_pci.c optional ndis cardbus | ndis pci
dev/if_ndis/if_ndis_usb.c optional ndis usb
dev/io/iodev.c optional io
dev/ipmi/ipmi.c optional ipmi
-dev/ipmi/ipmi_smbios.c optional ipmi isa
+dev/ipmi/ipmi_acpi.c optional ipmi acpi
+dev/ipmi/ipmi_isa.c optional ipmi isa
+dev/ipmi/ipmi_kcs.c optional ipmi
+dev/ipmi/ipmi_smic.c optional ipmi
+dev/ipmi/ipmi_smbus.c optional ipmi smbus
+dev/ipmi/ipmi_smbios.c optional ipmi
+dev/ipmi/ipmi_ssif.c optional ipmi smbus
dev/ipmi/ipmi_pci.c optional ipmi pci
dev/fdc/fdc.c optional fdc
dev/fdc/fdc_acpi.c optional fdc
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index bd569bd..11419d0 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -196,7 +196,13 @@ dev/if_ndis/if_ndis_pci.c optional ndis cardbus | ndis pci
dev/if_ndis/if_ndis_usb.c optional ndis usb
dev/io/iodev.c optional io
dev/ipmi/ipmi.c optional ipmi
-dev/ipmi/ipmi_smbios.c optional ipmi isa
+dev/ipmi/ipmi_acpi.c optional ipmi acpi
+dev/ipmi/ipmi_isa.c optional ipmi isa
+dev/ipmi/ipmi_kcs.c optional ipmi
+dev/ipmi/ipmi_smic.c optional ipmi
+dev/ipmi/ipmi_smbus.c optional ipmi smbus
+dev/ipmi/ipmi_smbios.c optional ipmi
+dev/ipmi/ipmi_ssif.c optional ipmi smbus
dev/ipmi/ipmi_pci.c optional ipmi pci
dev/kbd/kbd.c optional atkbd | sc | ukbd | vt
dev/le/if_le_isa.c optional le isa
diff --git a/sys/dev/ipmi/ipmi.c b/sys/dev/ipmi/ipmi.c
index 38e7a21..a3ba52e 100644
--- a/sys/dev/ipmi/ipmi.c
+++ b/sys/dev/ipmi/ipmi.c
@@ -29,20 +29,17 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/conf.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 <sys/poll.h>
#include <sys/rman.h>
-#include <sys/watchdog.h>
+#include <sys/selinfo.h>
#include <sys/sysctl.h>
+#include <sys/watchdog.h>
#ifdef LOCAL_MODULE
#include <ipmi.h>
@@ -52,29 +49,7 @@ __FBSDID("$FreeBSD$");
#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)
@@ -92,11 +67,10 @@ int ipmi_attached = 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, "");
+ &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,
@@ -106,60 +80,126 @@ static struct cdevsw ipmi_cdevsw = {
MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi");
-static int
-ipmi_open(struct cdev *dev, int flags, int fmt, struct thread *td)
+static int
+ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
{
+ struct ipmi_device *dev;
struct ipmi_softc *sc;
if (!on)
- return ENOENT;
-
- sc = dev->si_drv1;
- if (sc->ipmi_refcnt) {
- return EBUSY;
+ return (ENOENT);
+
+ dev = cdev->si_drv1;
+ sc = dev->ipmi_softc;
+ IPMI_LOCK(sc);
+ if (dev->ipmi_open) {
+ IPMI_UNLOCK(sc);
+ return (EBUSY);
}
- sc->ipmi_refcnt = 1;
+ dev->ipmi_open = 1;
+ IPMI_UNLOCK(sc);
- return 0;
+ return (0);
}
-static int
-ipmi_poll(struct cdev *dev, int poll_events, struct thread *td)
+static int
+ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td)
{
+ struct ipmi_device *dev;
struct ipmi_softc *sc;
int revents = 0;
- sc = dev->si_drv1;
-
- ipmi_check_read(sc->ipmi_dev);
+ dev = cdev->si_drv1;
+ sc = dev->ipmi_softc;
+ IPMI_LOCK(sc);
if (poll_events & (POLLIN | POLLRDNORM)) {
- if (!TAILQ_EMPTY(&sc->ipmi_done))
+ if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
revents |= poll_events & (POLLIN | POLLRDNORM);
- if (TAILQ_EMPTY(&sc->ipmi_done) && sc->ipmi_requests == 0) {
+ if (dev->ipmi_requests == 0)
revents |= POLLERR;
- }
}
if (revents == 0) {
if (poll_events & (POLLIN | POLLRDNORM))
- selrecord(td, &sc->ipmi_select);
+ selrecord(td, &dev->ipmi_select);
}
+ IPMI_UNLOCK(sc);
- return revents;
+ return (revents);
}
-static int
-ipmi_close(struct cdev *dev, int flags, int fmt, struct thread *td)
+static void
+ipmi_purge_completed_requests(struct ipmi_device *dev)
{
+ struct ipmi_request *req;
+
+ while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) {
+ req = TAILQ_FIRST(&dev->ipmi_completed_requests);
+ TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link);
+ dev->ipmi_requests--;
+ ipmi_free_request(req);
+ }
+}
+
+static int
+ipmi_close(struct cdev *cdev, int flags, int fmt, struct thread *td)
+{
+ struct ipmi_request *req, *nreq;
+ struct ipmi_device *dev;
struct ipmi_softc *sc;
- int error = 0;
+#ifdef CLONING
+ int bit;
+#endif
- sc = dev->si_drv1;
+ dev = cdev->si_drv1;
+ sc = dev->ipmi_softc;
+
+ IPMI_LOCK(sc);
+ if (dev->ipmi_requests) {
+ /* Throw away any pending requests for this device. */
+ TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link,
+ nreq) {
+ if (req->ir_owner == dev) {
+ TAILQ_REMOVE(&sc->ipmi_pending_requests, req,
+ ir_link);
+ dev->ipmi_requests--;
+ ipmi_free_request(req);
+ }
+ }
- sc->ipmi_refcnt = 0;
+ /* Throw away any pending completed requests for this device. */
+ ipmi_purge_completed_requests(dev);
- return error;
+ /*
+ * If we still have outstanding requests, they must be stuck
+ * in an interface driver, so wait for those to drain.
+ */
+ dev->ipmi_closing = 1;
+ while (dev->ipmi_requests > 0) {
+ msleep(&dev->ipmi_requests, &sc->ipmi_lock, PWAIT,
+ "ipmidrain", 0);
+ ipmi_purge_completed_requests(dev);
+ }
+ }
+
+#ifdef CLONING
+ /* Detach this sub-device from the main driver. */
+ bit = minor(cdev) % 32;
+ sc->ipmi_cdev_mask &= ~(1 << bit);
+ TAILQ_REMOVE(&sc->ipmi_cdevs, dev, ipmi_link);
+ IPMI_UNLOCK(sc);
+
+ /* Cleanup. */
+ cdev->si_drv1 = NULL;
+ free(dev, M_IPMI);
+ destroy_dev(cdev);
+#else
+ dev->ipmi_open = 0;
+ IPMI_UNLOCK(sc);
+#endif
+
+ return (0);
}
#ifdef IPMB
@@ -171,707 +211,384 @@ ipmi_ipmb_checksum(u_char *data, int len)
for (; len; len--) {
sum += *data++;
}
- return -sum;
+ return (-sum);
}
+/* XXX: Needs work */
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;
+ struct ipmi_request *req;
u_char slave_addr = 0x52;
+ int error;
- 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;
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_SEND_MSG, data_len + 8, 0);
+ req->ir_request[0] = channel;
+ req->ir_request[1] = slave_addr;
+ req->ir_request[2] = IPMI_ADDR(netfn, 0);
+ req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2);
+ req->ir_request[4] = sc->ipmi_address;
+ req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun);
+ req->ir_request[6] = command;
+
+ bcopy(data, &req->ir_request[7], data_len);
+ temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4],
+ data_len + 3);
+
+ ipmi_submit_driver_request(sc, req);
+ error = req->ir_error;
+ ipmi_free_request(req);
+
+ return (error);
}
static int
-ipmi_handle_attn(device_t dev)
+ipmi_handle_attn(struct ipmi_softc *sc)
{
- u_char temp[IPMI_MAX_RX];
- struct ipmi_softc *sc = device_get_softc(dev);
+ struct ipmi_request *req;
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) {
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_GET_MSG_FLAGS, 0, 1);
+
+ ipmi_submit_driver_request(sc, req);
+
+ if (req->ir_error == 0 && req->ir_compcode == 0) {
+ if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) {
device_printf(sc->ipmi_dev, "message buffer full");
}
- if (temp[3] & IPMI_WDT_PRE_TIMEOUT) {
+ if (req->ir_reply[0] & 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);
+ if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) {
+ ipmi_free_request(req);
+
+ req = ipmi_alloc_driver_request(
+ IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0,
+ 16);
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");
}
+ error = req->ir_error;
+ ipmi_free_request(req);
- return 0;
-}
-
-void
-ipmi_intr(void *arg) {
- device_t dev = arg;
-
- ipmi_check_read(dev);
+ return (error);
}
+#endif
-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);
-}
+#ifdef IPMICTL_SEND_COMMAND_32
+#define PTRIN(p) ((void *)(uintptr_t)(p))
+#define PTROUT(p) ((uintptr_t)(p))
+#endif
static int
-ipmi_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
+ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
int flags, struct thread *td)
{
struct ipmi_softc *sc;
+ struct ipmi_device *dev;
+ struct ipmi_request *kreq;
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;
+#ifdef IPMICTL_SEND_COMMAND_32
+ struct ipmi_req32 *req32 = (struct ipmi_req32 *)data;
+ struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data;
+ union {
+ struct ipmi_req req;
+ struct ipmi_recv recv;
+ } thunk32;
+#endif
int error, len;
- sc = dev->si_drv1;
+ dev = cdev->si_drv1;
+ sc = dev->ipmi_softc;
+
+#ifdef IPMICTL_SEND_COMMAND_32
+ /* Convert 32-bit structures to native. */
+ switch (cmd) {
+ case IPMICTL_SEND_COMMAND_32:
+ req = &thunk32.req;
+ req->addr = PTRIN(req32->addr);
+ req->addr_len = req32->addr_len;
+ req->msgid = req32->msgid;
+ req->msg.netfn = req32->msg.netfn;
+ req->msg.cmd = req32->msg.cmd;
+ req->msg.data_len = req32->msg.data_len;
+ req->msg.data = PTRIN(req32->msg.data);
+ break;
+ case IPMICTL_RECEIVE_MSG_TRUNC_32:
+ case IPMICTL_RECEIVE_MSG_32:
+ recv = &thunk32.recv;
+ recv->addr = PTRIN(recv32->addr);
+ recv->addr_len = recv32->addr_len;
+ recv->msg.data_len = recv32->msg.data_len;
+ recv->msg.data = PTRIN(recv32->msg.data);
+ break;
+ }
+#endif
switch (cmd) {
+#ifdef IPMICTL_SEND_COMMAND_32
+ case IPMICTL_SEND_COMMAND_32:
+#endif
case IPMICTL_SEND_COMMAND:
+ /*
+ * XXX: Need to add proper handling of this.
+ */
+ error = copyin(req->addr, &addr, sizeof(addr));
+ if (error)
+ return (error);
+
+ IPMI_LOCK(sc);
/* 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);
+ /* XXX: This seems odd. */
+ while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) {
+ TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
+ ir_link);
+ dev->ipmi_requests--;
+ ipmi_free_request(kreq);
}
+ IPMI_UNLOCK(sc);
- 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],
+ kreq = ipmi_alloc_request(dev, req->msgid,
+ IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd,
+ req->msg.data_len, IPMI_MAX_RX);
+ error = copyin(req->msg.data, kreq->ir_request,
req->msg.data_len);
- if (error != 0) {
- free(temp, M_IPMI);
- return error;
+ if (error) {
+ ipmi_free_request(kreq);
+ 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;
+ IPMI_LOCK(sc);
+ dev->ipmi_requests++;
+ error = sc->ipmi_enqueue_request(sc, kreq);
+ IPMI_UNLOCK(sc);
+ if (error)
+ return (error);
+ break;
+#ifdef IPMICTL_SEND_COMMAND_32
+ case IPMICTL_RECEIVE_MSG_TRUNC_32:
+ case IPMICTL_RECEIVE_MSG_32:
+#endif
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);
+ if (error)
+ return (error);
+
+ IPMI_LOCK(sc);
+ kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
+ if (kreq == NULL) {
+ IPMI_UNLOCK(sc);
+ return (EAGAIN);
+ }
addr.channel = IPMI_BMC_CHANNEL;
+ /* XXX */
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;
+ recv->msgid = kreq->ir_msgid;
+ recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
+ recv->msg.cmd = kreq->ir_command;
+ error = kreq->ir_error;
+ if (error) {
+ TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
+ ir_link);
+ dev->ipmi_requests--;
+ IPMI_UNLOCK(sc);
+ ipmi_free_request(kreq);
+ return (error);
}
+ len = kreq->ir_replylen + 1;
+ if (recv->msg.data_len < len &&
+ (cmd == IPMICTL_RECEIVE_MSG
+#ifdef IPMICTL_RECEIVE_MSG_32
+ || cmd == IPMICTL_RECEIVE_MSG
+#endif
+ )) {
+ IPMI_UNLOCK(sc);
+ return (EMSGSIZE);
+ }
+ TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link);
+ dev->ipmi_requests--;
+ IPMI_UNLOCK(sc);
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;
+ error = copyout(&kreq->ir_compcode, recv->msg.data, 1);
+ if (error == 0)
+ error = copyout(kreq->ir_reply, recv->msg.data + 1,
+ len - 1);
+ ipmi_free_request(kreq);
+ if (error)
+ return (error);
+ break;
case IPMICTL_SET_MY_ADDRESS_CMD:
- sc->ipmi_address = *(int*)data;
- return 0;
+ IPMI_LOCK(sc);
+ dev->ipmi_address = *(int*)data;
+ IPMI_UNLOCK(sc);
+ break;
case IPMICTL_GET_MY_ADDRESS_CMD:
- *(int*)data = sc->ipmi_address;
- return 0;
+ IPMI_LOCK(sc);
+ *(int*)data = dev->ipmi_address;
+ IPMI_UNLOCK(sc);
+ break;
case IPMICTL_SET_MY_LUN_CMD:
- sc->ipmi_lun = *(int*)data & 0x3;
- return 0;
+ IPMI_LOCK(sc);
+ dev->ipmi_lun = *(int*)data & 0x3;
+ IPMI_UNLOCK(sc);
+ break;
case IPMICTL_GET_MY_LUN_CMD:
- *(int*)data = sc->ipmi_lun;
- return 0;
+ IPMI_LOCK(sc);
+ *(int*)data = dev->ipmi_lun;
+ IPMI_UNLOCK(sc);
+ break;
case IPMICTL_SET_GETS_EVENTS_CMD:
/*
device_printf(sc->ipmi_dev,
"IPMICTL_SET_GETS_EVENTS_CMD NA\n");
*/
- return 0;
+ break;
case IPMICTL_REGISTER_FOR_CMD:
case IPMICTL_UNREGISTER_FOR_CMD:
- return EOPNOTSUPP;
+ return (EOPNOTSUPP);
+ default:
+ device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd);
+ return (ENOIOCTL);
}
- 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));
+#ifdef IPMICTL_SEND_COMMAND_32
+ /* Update changed fields in 32-bit structures. */
+ switch (cmd) {
+ case IPMICTL_RECEIVE_MSG_TRUNC_32:
+ case IPMICTL_RECEIVE_MSG_32:
+ recv32->recv_type = recv->recv_type;
+ recv32->msgid = recv->msgid;
+ recv32->msg.netfn = recv->msg.netfn;
+ recv32->msg.cmd = recv->msg.cmd;
+ recv32->msg.data_len = recv->msg.data_len;
+ break;
}
- return status;
+#endif
+ return (0);
}
-static void
-ipmi_clear_obf(device_t dev, int status) {
- struct ipmi_softc *sc = device_get_softc(dev);
- int data;
+/*
+ * Request management.
+ */
- /* Clear OBF */
- if (status & KCS_STATUS_OBF) {
- data = INB(sc, sc->ipmi_kcs_data_out_reg);
+/* Allocate a new request with request and reply buffers. */
+struct ipmi_request *
+ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr,
+ uint8_t command, size_t requestlen, size_t replylen)
+{
+ struct ipmi_request *req;
+
+ req = malloc(sizeof(struct ipmi_request) + requestlen + replylen,
+ M_IPMI, M_WAITOK | M_ZERO);
+ req->ir_owner = dev;
+ req->ir_msgid = msgid;
+ req->ir_addr = addr;
+ req->ir_command = command;
+ if (requestlen) {
+ req->ir_request = (char *)&req[1];
+ req->ir_requestlen = requestlen;
}
+ if (replylen) {
+ req->ir_reply = (char *)&req[1] + requestlen;
+ req->ir_replybuflen = replylen;
+ }
+ return (req);
}
-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);
+/* Free a request no longer in use. */
+void
+ipmi_free_request(struct ipmi_request *req)
+{
- /* Write READ into Data_in */
- OUTB(sc, sc->ipmi_kcs_data_in_reg, KCS_DATA_IN_READ);
+ free(req, M_IPMI);
+}
- /* Wait for IBF = 0 */
- status = ipmi_wait_for_ibf(dev, 0);
- }
+/* Store a processed request on the appropriate completion queue. */
+void
+ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+ struct ipmi_device *dev;
- /* IDLE STATE */
- if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
- /* Wait for OBF = 1 */
- status = ipmi_wait_for_obf(dev, 1);
+ IPMI_LOCK_ASSERT(sc);
- /* Clear OBF */
- ipmi_clear_obf(dev, status);
- break;
- }
-
- retry++;
- if (retry > 2) {
- device_printf(dev, "Retry exhausted %x\n", retry);
- break;
- }
+ /*
+ * Anonymous requests (from inside the driver) always have a
+ * waiter that we awaken.
+ */
+ if (req->ir_owner == NULL)
+ wakeup(req);
+ else {
+ dev = req->ir_owner;
+ TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link);
+ selwakeup(&dev->ipmi_select);
+ if (dev->ipmi_closing)
+ wakeup(&dev->ipmi_requests);
}
}
-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;
+/* Enqueue an internal driver request and wait until it is completed. */
+int
+ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req,
+ int timo)
+{
+ int error;
- do {
- flags = INB(sc, sc->ipmi_smic_flags);
- } while(!flags & SMIC_STATUS_RX_RDY);
+ IPMI_LOCK(sc);
+ error = sc->ipmi_enqueue_request(sc, req);
+ if (error == 0)
+ error = msleep(req, &sc->ipmi_lock, 0, "ipmireq", timo);
+ if (error == 0)
+ error = req->ir_error;
+ IPMI_UNLOCK(sc);
+ return (error);
}
-static void
-ipmi_wait_for_not_busy(device_t dev) {
- struct ipmi_softc *sc = device_get_softc(dev);
- int flags;
+/*
+ * Helper routine for polled system interfaces that use
+ * ipmi_polled_enqueue_request() to queue requests. This request
+ * waits until there is a pending request and then returns the first
+ * request. If the driver is shutting down, it returns NULL.
+ */
+struct ipmi_request *
+ipmi_dequeue_request(struct ipmi_softc *sc)
+{
+ struct ipmi_request *req;
- do {
- flags = INB(sc, sc->ipmi_smic_flags);
- } while(flags & SMIC_STATUS_BUSY);
-}
+ IPMI_LOCK_ASSERT(sc);
-static void
-ipmi_set_busy(device_t dev) {
- struct ipmi_softc *sc = device_get_softc(dev);
- int flags;
+ while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests))
+ cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock);
+ if (sc->ipmi_detaching)
+ return (NULL);
- flags = INB(sc, sc->ipmi_smic_flags);
- flags |= SMIC_STATUS_BUSY;
- OUTB(sc, sc->ipmi_smic_flags, flags);
+ req = TAILQ_FIRST(&sc->ipmi_pending_requests);
+ TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
+ return (req);
}
+/* Default implementation of ipmi_enqueue_request() for polled interfaces. */
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);
- }
+ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
- 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;
+ IPMI_LOCK(sc);
+ TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link);
+ IPMI_UNLOCK(sc);
+ cv_signal(&sc->ipmi_request_added);
+ return (0);
}
/*
@@ -879,56 +596,50 @@ ipmi_write(device_t dev, u_char *bytes, int len){
*/
static void
-ipmi_set_watchdog(device_t dev, int sec) {
- u_char *temp;
- int s;
+ipmi_set_watchdog(struct ipmi_softc *sc, int sec)
+{
+ struct ipmi_request *req;
+ int error;
- temp = malloc(IPMI_MAX_RX, M_IPMI, M_WAITOK);
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_SET_WDOG, 6, 0);
- temp[0] = IPMI_APP_REQUEST << 2;
if (sec) {
- temp[1] = IPMI_SET_WDOG;
- temp[2] = IPMI_SET_WD_TIMER_DONT_STOP
+ req->ir_request[0] = 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;
+ req->ir_request[1] = IPMI_SET_WD_ACTION_RESET;
+ req->ir_request[2] = 0;
+ req->ir_request[3] = 0; /* Timer use */
+ req->ir_request[4] = (sec * 10) & 0xff;
+ req->ir_request[5] = (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;
+ req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS;
+ req->ir_request[1] = 0;
+ req->ir_request[2] = 0;
+ req->ir_request[3] = 0; /* Timer use */
+ req->ir_request[4] = 0;
+ req->ir_request[5] = 0;
}
- s = splhigh();
- ipmi_write(dev, temp, 8);
+ error = ipmi_submit_driver_request(sc, req, 0);
+ if (error)
+ device_printf(sc->ipmi_dev, "Failed to set watchdog\n");
- 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;
+ if (error == 0 && sec) {
+ ipmi_free_request(req);
- ipmi_write(dev, temp, 2);
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_RESET_WDOG, 0, 0);
- while (!ipmi_ready_to_read(dev))
- DELAY(1000);
- bzero(temp, IPMI_MAX_RX);
- ipmi_read(dev, temp, IPMI_MAX_RX);
+ error = ipmi_submit_driver_request(sc, req, 0);
+ if (error)
+ device_printf(sc->ipmi_dev,
+ "Failed to reset watchdog\n");
}
- splx(s);
- free(temp, M_IPMI);
+ ipmi_free_request(req);
/*
- dump_watchdog(dev);
+ dump_watchdog(sc);
*/
}
@@ -940,7 +651,7 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error)
/* disable / enable */
if (!(cmd & WD_ACTIVE)) {
- ipmi_set_watchdog(sc->ipmi_dev, 0);
+ ipmi_set_watchdog(sc, 0);
*error = 0;
return;
}
@@ -954,92 +665,208 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error)
timeout = ((uint64_t)1 << cmd) / 1800000000;
/* reload */
- ipmi_set_watchdog(sc->ipmi_dev, timeout);
+ ipmi_set_watchdog(sc, timeout);
*error = 0;
}
-int
-ipmi_attach(device_t dev)
+#ifdef CLONING
+static void
+ipmi_clone(void *arg, struct ucred *cred, char *name, int namelen,
+ struct cdev **cdev)
{
- 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));
+ struct ipmi_softc *sc = arg;
+ struct ipmi_device *dev;
+ int minor, unit;
+
+ if (*cdev != NULL)
+ return;
+
+ if (strcmp(name, device_get_nameunit(sc->ipmi_dev)) != 0)
+ return;
+
+ dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO);
+
+ /* Reserve a sub-device. */
+ IPMI_LOCK(sc);
+ minor = ffs(~(sc->ipmi_cdev_mask & 0xffff));
+ if (minor == 0 || !sc->ipmi_cloning) {
+ IPMI_UNLOCK(sc);
+ free(dev, M_IPMI);
+ return;
+ }
+ minor--;
+ sc->ipmi_cdev_mask |= (1 << minor);
+ TAILQ_INSERT_TAIL(&sc->ipmi_cdevs, dev, ipmi_link);
+ IPMI_UNLOCK(sc);
+
+ /* Initialize the device. */
+ TAILQ_INIT(&dev->ipmi_completed_requests);
+ dev->ipmi_softc = sc;
+ dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
+ dev->ipmi_lun = IPMI_BMC_SMS_LUN;
+ unit = device_get_unit(sc->ipmi_dev);
+ dev->ipmi_cdev = make_dev_cred(&ipmi_cdevsw, unit * 32 + minor, cred,
+ UID_ROOT, GID_OPERATOR, 0660, "ipmi%d.%d", unit, minor);
+ if (dev->ipmi_cdev == NULL) {
+ IPMI_LOCK(sc);
+ sc->ipmi_cdev_mask &= ~(1 << minor);
+ TAILQ_REMOVE(&sc->ipmi_cdevs, dev, ipmi_link);
+ IPMI_UNLOCK(sc);
+ free(dev, M_IPMI);
+ return;
+ }
+ dev->ipmi_cdev->si_drv1 = dev;
+ *cdev = dev->ipmi_cdev;
+ dev_ref(*cdev);
+}
+#endif
+
+static void
+ipmi_startup(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+ struct ipmi_request *req;
+ device_t dev;
+ int error, i;
+
+ config_intrhook_disestablish(&sc->ipmi_ich);
+ dev = sc->ipmi_dev;
+
+ /* Initialize interface-independent state. */
+ mtx_init(&sc->ipmi_lock, device_get_nameunit(dev), "ipmi", MTX_DEF);
+ cv_init(&sc->ipmi_request_added, "ipmireq");
+ TAILQ_INIT(&sc->ipmi_pending_requests);
+#ifdef CLONING
+ TAILQ_INIT(&sc->ipmi_cdevs);
+#endif
+
+ /* Initialize interface-dependent state. */
+ error = sc->ipmi_startup(sc);
+ if (error) {
+ device_printf(dev, "Failed to initialize interface: %d\n",
+ error);
+ return;
+ }
+
+ /* Send a GET_DEVICE_ID request. */
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_GET_DEVICE_ID, 0, 15);
+
+ error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT);
+ if (error == EWOULDBLOCK) {
+ device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n");
+ return;
+ } else if (error) {
+ device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error);
+ return;
+ } else if (req->ir_compcode != 0) {
+ device_printf(dev,
+ "Bad completion code for GET_DEVICE_ID: %d\n",
+ req->ir_compcode);
+ return;
+ } else if (req->ir_replylen < 5) {
+ device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n",
+ req->ir_replylen);
+ return;
+ }
+
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) {
+ req->ir_reply[1] & 0x0f,
+ req->ir_reply[2] & 0x0f, req->ir_reply[4],
+ req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4);
+
+ ipmi_free_request(req);
+
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_CLEAR_FLAGS, 1, 0);
+
+ ipmi_submit_driver_request(sc, req, 0);
+
+ /* XXX: Magic numbers */
+ if (req->ir_compcode == 0xc0) {
device_printf(dev, "Clear flags is busy\n");
}
- if (temp[2] == 0xc1) {
+ if (req->ir_compcode == 0xc1) {
device_printf(dev, "Clear flags illegal\n");
}
+ ipmi_free_request(req);
+
+ for (i = 0; i < 8; i++) {
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_GET_CHANNEL_INFO, 1, 0);
+ req->ir_request[0] = i;
+
+ ipmi_submit_driver_request(sc, req, 0);
- 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]) {
+ if (req->ir_compcode != 0) {
+ ipmi_free_request(req);
break;
}
+ ipmi_free_request(req);
}
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) {
+ req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+ IPMI_GET_WDOG, 0, 0);
+
+ ipmi_submit_driver_request(sc, req, 0);
+
+ if (req->ir_compcode == 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);
+ sc->ipmi_watchdog_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_free_request(req);
- ipmi_attached = 1;
+#ifdef CLONING
+ sc->ipmi_cloning = 1;
+ sc->ipmi_clone_tag = EVENTHANDLER_REGISTER(dev_clone, ipmi_clone, sc,
+ 1000);
+#else
+ /* Initialize the device. */
+ TAILQ_INIT(&sc->ipmi_idev.ipmi_completed_requests);
+ sc->ipmi_idev.ipmi_softc = sc;
+ sc->ipmi_idev.ipmi_address = IPMI_BMC_SLAVE_ADDR;
+ sc->ipmi_idev.ipmi_lun = IPMI_BMC_SMS_LUN;
+ sc->ipmi_idev.ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev));
+ if (sc->ipmi_idev.ipmi_cdev == NULL) {
+ device_printf(dev, "Failed to create cdev\n");
+ return;
+ }
+ sc->ipmi_idev.ipmi_cdev->si_drv1 = &sc->ipmi_idev;
+#endif
+}
- return 0;
+int
+ipmi_attach(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int error;
+
+ if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) {
+ error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC,
+ sc->ipmi_intr, sc, &sc->ipmi_irq);
+ if (error) {
+ device_printf(dev, "can't set up interrupt\n");
+ return (error);
+ }
+ }
+
+ bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook));
+ sc->ipmi_ich.ich_func = ipmi_startup;
+ sc->ipmi_ich.ich_arg = sc;
+ if (config_intrhook_establish(&sc->ipmi_ich) != 0) {
+ device_printf(dev, "can't establish configuration hook\n");
+ return (ENOMEM);
+ }
+
+ ipmi_attached = 1;
+ return (0);
}
int
@@ -1048,16 +875,90 @@ 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;
+
+ /* Fail if there are any open handles. */
+ IPMI_LOCK(sc);
+#ifdef CLONING
+ if (!TAILQ_EMPTY(&sc->ipmi_cdevs)) {
+ IPMI_UNLOCK(sc);
+ return (EBUSY);
+ }
+
+ /* Turn off cloning. */
+ sc->ipmi_cloning = 0;
+ IPMI_UNLOCK(sc);
+
+ EVENTHANDLER_DEREGISTER(dev_clone, sc->ipmi_clone_tag);
+#else
+ if (sc->ipmi_idev.ipmi_open) {
+ IPMI_UNLOCK(sc);
+ return (EBUSY);
+ }
+ IPMI_UNLOCK(sc);
+ destroy_dev(sc->ipmi_idev.ipmi_cdev);
+#endif
+
+ /* Detach from watchdog handling and turn off watchdog. */
+ if (sc->ipmi_watchdog_tag) {
+ EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag);
+ ipmi_set_watchdog(sc, 0);
+ }
+
+ /* XXX: should use shutdown callout I think. */
+ /* If the backend uses a kthread, shut it down. */
+ IPMI_LOCK(sc);
+ sc->ipmi_detaching = 1;
+ if (sc->ipmi_kthread) {
+ cv_broadcast(&sc->ipmi_request_added);
+ msleep(sc->ipmi_kthread, &sc->ipmi_lock, 0, "ipmi_wait", 0);
+ }
+ IPMI_UNLOCK(sc);
+ if (sc->ipmi_irq)
+ bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
+
+ ipmi_release_resources(dev);
+ mtx_destroy(&sc->ipmi_lock);
+ return (0);
+}
+
+void
+ipmi_release_resources(device_t dev)
+{
+ struct ipmi_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ if (sc->ipmi_irq)
+ bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
+ if (sc->ipmi_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid,
+ sc->ipmi_irq_res);
+ for (i = 0; i < MAX_RES; i++)
+ if (sc->ipmi_io_res[i])
+ bus_release_resource(dev, sc->ipmi_io_type,
+ sc->ipmi_io_rid + i, sc->ipmi_io_res[i]);
+}
+
+devclass_t ipmi_devclass;
+
+/* XXX: Why? */
+static void
+ipmi_unload(void *arg)
+{
+ device_t * devs;
+ int count;
+ int i;
+
+ devclass_get_devices(ipmi_devclass, &devs, &count);
+ for (i = 0; i < count; i++)
+ device_delete_child(device_get_parent(devs[i]), devs[i]);
}
+SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL);
#ifdef IMPI_DEBUG
static void
-dump_buf(u_char *data, int len){
+dump_buf(u_char *data, int len)
+{
char buf[20];
char line[1024];
char temp[30];
diff --git a/sys/dev/ipmi/ipmi_acpi.c b/sys/dev/ipmi/ipmi_acpi.c
new file mode 100644
index 0000000..e2518eb
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_acpi.c
@@ -0,0 +1,210 @@
+/*-
+ * 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/bus.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+/* Hooks for the ACPI CA debugging infrastructure */
+#define _COMPONENT ACPI_BUTTON
+ACPI_MODULE_NAME("IPMI")
+
+#ifdef LOCAL_MODULE
+#include <ipmi.h>
+#include <ipmivars.h>
+#else
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+static int ipmi_acpi_probe(device_t);
+static int ipmi_acpi_attach(device_t);
+
+int
+ipmi_acpi_probe(device_t dev)
+{
+ static char *ipmi_ids[] = {"IPI0001", NULL};
+
+ if (ipmi_attached)
+ return (EBUSY);
+
+ if (acpi_disabled("ipmi") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, ipmi_ids) == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, "IPMI System Interface");
+
+ return (0);
+}
+
+static int
+ipmi_acpi_attach(device_t dev)
+{
+ ACPI_HANDLE devh;
+ const char *mode;
+ struct ipmi_get_info info;
+ struct ipmi_softc *sc = device_get_softc(dev);
+ int count, error, flags, i, type;
+ int interface_type = 0, interface_version = 0;
+
+ error = 0;
+ devh = acpi_get_handle(dev);
+ if (ACPI_FAILURE(acpi_GetInteger(devh, "_IFT", &interface_type)))
+ return (ENXIO);
+
+ if (ACPI_FAILURE(acpi_GetInteger(devh, "_SRV", &interface_version)))
+ return (ENXIO);
+
+ switch (interface_type) {
+ case KCS_MODE:
+ count = 2;
+ mode = "KCS";
+ break;
+ case SMIC_MODE:
+ count = 3;
+ mode = "SMIC";
+ break;
+ case BT_MODE:
+ device_printf(dev, "BT interface not supported\n");
+ return (ENXIO);
+ case SSIF_MODE:
+ if (ACPI_FAILURE(acpi_GetInteger(devh, "_ADR", &flags)))
+ return (ENXIO);
+ info.address = flags >> 1;
+ device_printf(dev, "SSIF interface not supported on ACPI\n");
+ return (0);
+ default:
+ return (ENXIO);
+ }
+
+ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, NULL, NULL) == 0)
+ type = SYS_RES_IOPORT;
+ else if (bus_get_resource(dev, SYS_RES_MEMORY, 0, NULL, NULL) == 0)
+ type = SYS_RES_MEMORY;
+ else {
+ device_printf(dev, "unknown resource type\n");
+ return (ENXIO);
+ }
+
+ sc->ipmi_io_rid = 0;
+ sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
+ &sc->ipmi_io_rid, RF_ACTIVE);
+ sc->ipmi_io_type = type;
+ sc->ipmi_io_spacing = 1;
+ if (sc->ipmi_io_res[0] == NULL) {
+ device_printf(dev, "couldn't configure I/O resource\n");
+ return (ENXIO);
+ }
+
+ /* If we have multiple resources, allocate up to MAX_RES. */
+ for (i = 1; i < MAX_RES; i++) {
+ sc->ipmi_io_rid = i;
+ sc->ipmi_io_res[i] = bus_alloc_resource_any(dev, type,
+ &sc->ipmi_io_rid, RF_ACTIVE);
+ if (sc->ipmi_io_res[i] == NULL)
+ break;
+ }
+ sc->ipmi_io_rid = 0;
+
+ /* If we have multiple resources, make sure we have enough of them. */
+ if (sc->ipmi_io_res[1] != NULL && sc->ipmi_io_res[count - 1] == NULL) {
+ device_printf(dev, "too few I/O resources\n");
+ error = ENXIO;
+ goto bad;
+ }
+
+ device_printf(dev, "%s mode found at %s 0x%jx on %s\n",
+ mode, type == SYS_RES_IOPORT ? "io" : "mem",
+ (uintmax_t)rman_get_start(sc->ipmi_io_res[0]),
+ device_get_name(device_get_parent(dev)));
+
+ sc->ipmi_dev = dev;
+
+ /*
+ * Setup an interrupt if we have an interrupt resource. We
+ * don't support GPE interrupts via _GPE yet.
+ */
+ sc->ipmi_irq_rid = 0;
+ sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
+
+ /* Warn if _GPE exists. */
+ if (ACPI_SUCCESS(AcpiEvaluateObject(devh, "_GPE", NULL, NULL)))
+ device_printf(dev, "_GPE support not implemented\n");
+
+ /*
+ * We assume an alignment of 1 byte as currently the IPMI spec
+ * doesn't provide any way to determine the alignment via ACPI.
+ */
+ switch (interface_type) {
+ case KCS_MODE:
+ error = ipmi_kcs_attach(sc);
+ if (error)
+ goto bad;
+ break;
+ case SMIC_MODE:
+ error = ipmi_smic_attach(sc);
+ if (error)
+ goto bad;
+ break;
+ }
+ error = ipmi_attach(dev);
+ if (error)
+ goto bad;
+
+ return (0);
+bad:
+ ipmi_release_resources(dev);
+ return (error);
+}
+
+static device_method_t ipmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipmi_acpi_probe),
+ DEVMETHOD(device_attach, ipmi_acpi_attach),
+ DEVMETHOD(device_detach, ipmi_detach),
+ { 0, 0 }
+};
+
+static driver_t ipmi_acpi_driver = {
+ "ipmi",
+ ipmi_methods,
+ sizeof(struct ipmi_softc),
+};
+
+DRIVER_MODULE(ipmi_acpi, acpi, ipmi_acpi_driver, ipmi_devclass, 0, 0);
+MODULE_DEPEND(ipmi_acpi, acpi, 1, 1, 1);
diff --git a/sys/dev/ipmi/ipmi_isa.c b/sys/dev/ipmi/ipmi_isa.c
new file mode 100644
index 0000000..a77081a
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_isa.c
@@ -0,0 +1,209 @@
+/*-
+ * 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/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+
+#include <machine/pci_cfgreg.h>
+#include <dev/pci/pcireg.h>
+
+#include <isa/isavar.h>
+
+#ifdef LOCAL_MODULE
+#include <ipmi.h>
+#include <ipmivars.h>
+#else
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+static void
+ipmi_isa_identify(driver_t *driver, device_t parent)
+{
+ struct ipmi_get_info info;
+ uint32_t devid;
+
+ if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE &&
+ device_find_child(parent, "ipmi", -1) == NULL) {
+ /*
+ * XXX: Hack alert. On some broken systems, the IPMI
+ * interface is described via SMBIOS, but the actual
+ * IO resource is in a PCI device BAR, so we have to let
+ * the PCI device attach ipmi instead. In that case don't
+ * create an isa ipmi device. For now we hardcode the list
+ * of bus, device, function tuples.
+ */
+ devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4);
+ if (devid != 0xffffffff &&
+ ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL)
+ return;
+ BUS_ADD_CHILD(parent, 0, "ipmi", -1);
+ }
+}
+
+static int
+ipmi_isa_probe(device_t dev)
+{
+
+ /* Skip any PNP devices. */
+ if (isa_get_logicalid(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "IPMI System Interface");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ipmi_isa_attach(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ struct ipmi_get_info info;
+ const char *mode;
+ int count, error, i, type;
+
+ /* This should never fail. */
+ if (!ipmi_smbios_identify(&info))
+ return (ENXIO);
+
+ /*
+ * Give other drivers precedence. Unfortunately, this doesn't
+ * work if we have an SMBIOS table that duplicates a PCI device
+ * that's later on the bus than the PCI-ISA bridge.
+ */
+ if (ipmi_attached)
+ return (EBUSY);
+
+ switch (info.iface_type) {
+ case KCS_MODE:
+ count = 2;
+ mode = "KCS";
+ break;
+ case SMIC_MODE:
+ count = 3;
+ mode = "SMIC";
+ break;
+ case BT_MODE:
+ device_printf(dev, "BT mode is unsupported\n");
+ return (ENXIO);
+ default:
+ return (ENXIO);
+ }
+ error = 0;
+ sc->ipmi_dev = dev;
+
+ device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
+ mode, info.io_mode ? "io" : "mem",
+ (uintmax_t)info.address, info.offset,
+ device_get_name(device_get_parent(dev)));
+ if (info.io_mode)
+ type = SYS_RES_IOPORT;
+ else
+ type = SYS_RES_MEMORY;
+
+ sc->ipmi_io_type = type;
+ sc->ipmi_io_spacing = info.offset;
+ if (info.offset == 1) {
+ sc->ipmi_io_rid = 0;
+ sc->ipmi_io_res[0] = bus_alloc_resource(dev, type,
+ &sc->ipmi_io_rid, info.address, info.address + count - 1,
+ count, RF_ACTIVE);
+ if (sc->ipmi_io_res[0] == NULL) {
+ device_printf(dev, "couldn't configure I/O resource\n");
+ return (ENXIO);
+ }
+ } else {
+ for (i = 0; i < count; i++) {
+ sc->ipmi_io_rid = i;
+ sc->ipmi_io_res[i] = bus_alloc_resource(dev, type,
+ &sc->ipmi_io_rid, info.address + i * info.offset,
+ info.address + i * info.offset, 1, RF_ACTIVE);
+ if (sc->ipmi_io_res[i] == NULL) {
+ device_printf(dev,
+ "couldn't configure I/O resource\n");
+ error = ENXIO;
+ sc->ipmi_io_rid = 0;
+ goto bad;
+ }
+ }
+ sc->ipmi_io_rid = 0;
+ }
+
+ if (info.irq != 0) {
+ sc->ipmi_irq_rid = 0;
+ sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &sc->ipmi_irq_rid, info.irq, info.irq, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ }
+
+ switch (info.iface_type) {
+ case KCS_MODE:
+ error = ipmi_kcs_attach(sc);
+ if (error)
+ goto bad;
+ break;
+ case SMIC_MODE:
+ error = ipmi_smic_attach(sc);
+ if (error)
+ goto bad;
+ break;
+ }
+
+ error = ipmi_attach(dev);
+ if (error)
+ goto bad;
+
+ return (0);
+bad:
+ ipmi_release_resources(dev);
+ return (error);
+}
+
+static device_method_t ipmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, ipmi_isa_identify),
+ DEVMETHOD(device_probe, ipmi_isa_probe),
+ DEVMETHOD(device_attach, ipmi_isa_attach),
+ DEVMETHOD(device_detach, ipmi_detach),
+ { 0, 0 }
+};
+
+static driver_t ipmi_isa_driver = {
+ "ipmi",
+ ipmi_methods,
+ sizeof(struct ipmi_softc),
+};
+
+DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0);
diff --git a/sys/dev/ipmi/ipmi_kcs.c b/sys/dev/ipmi/ipmi_kcs.c
new file mode 100644
index 0000000..8ad606e
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_kcs.c
@@ -0,0 +1,607 @@
+/*-
+ * 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/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+#include <machine/bus.h>
+
+#ifdef LOCAL_MODULE
+#include <ipmi.h>
+#include <ipmivars.h>
+#else
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+static void kcs_clear_obf(struct ipmi_softc *, int);
+static void kcs_error(struct ipmi_softc *);
+static int kcs_wait_for_ibf(struct ipmi_softc *, int);
+static int kcs_wait_for_obf(struct ipmi_softc *, int);
+
+static int
+kcs_wait_for_ibf(struct ipmi_softc *sc, int state)
+{
+ int status, start = ticks;
+
+ status = INB(sc, KCS_CTL_STS);
+ if (state == 0) {
+ /* WAIT FOR IBF = 0 */
+ while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_IBF) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+ } else {
+ /* WAIT FOR IBF = 1 */
+ while (ticks - start < MAX_TIMEOUT &&
+ !(status & KCS_STATUS_IBF)) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+ }
+ return (status);
+}
+
+static int
+kcs_wait_for_obf(struct ipmi_softc *sc, int state)
+{
+ int status, start = ticks;
+
+ status = INB(sc, KCS_CTL_STS);
+ if (state == 0) {
+ /* WAIT FOR OBF = 0 */
+ while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_OBF) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+ } else {
+ /* WAIT FOR OBF = 1 */
+ while (ticks - start < MAX_TIMEOUT &&
+ !(status & KCS_STATUS_OBF)) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+ }
+ return (status);
+}
+
+static void
+kcs_clear_obf(struct ipmi_softc *sc, int status)
+{
+ int data;
+
+ /* Clear OBF */
+ if (status & KCS_STATUS_OBF) {
+ data = INB(sc, KCS_DATA);
+ }
+}
+
+static void
+kcs_error(struct ipmi_softc *sc)
+{
+ int retry, status;
+ u_char data;
+
+ for (retry = 0; retry < 2; retry++) {
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ /* ABORT */
+ OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ /* Clear OBF */
+ kcs_clear_obf(sc, status);
+
+ if (status & KCS_STATUS_OBF) {
+ data = INB(sc, KCS_DATA);
+ if (data != 0)
+ device_printf(sc->ipmi_dev,
+ "KCS Error Data %02x\n", data);
+ }
+
+ /* 0x00 to DATA_IN */
+ OUTB(sc, KCS_DATA, 0x00);
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
+
+ /* Wait for OBF = 1 */
+ status = kcs_wait_for_obf(sc, 1);
+
+ /* Read error status */
+ data = INB(sc, KCS_DATA);
+ if (data != 0)
+ device_printf(sc->ipmi_dev, "KCS error: %02x\n",
+ data);
+
+ /* Write READ into Data_in */
+ OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+ }
+
+ /* IDLE STATE */
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
+ /* Wait for OBF = 1 */
+ status = kcs_wait_for_obf(sc, 1);
+
+ /* Clear OBF */
+ kcs_clear_obf(sc, status);
+ return;
+ }
+ }
+ device_printf(sc->ipmi_dev, "KCS Error retry exhausted\n");
+}
+
+/*
+ * Start to write a request. Waits for IBF to clear and then sends the
+ * WR_START command.
+ */
+static int
+kcs_start_write(struct ipmi_softc *sc)
+{
+ int retry, status;
+
+ for (retry = 0; retry < 10; retry++) {
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ /* Clear OBF */
+ kcs_clear_obf(sc, status);
+
+ /* Write start to command */
+ OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_START);
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE)
+ break;
+ DELAY(1000000);
+ }
+
+ if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
+ /* error state */
+ return (0);
+
+ /* Clear OBF */
+ kcs_clear_obf(sc, status);
+
+ return (1);
+}
+
+/*
+ * Write a byte of the request message, excluding the last byte of the
+ * message which requires special handling.
+ */
+static int
+kcs_write_byte(struct ipmi_softc *sc, u_char data)
+{
+ int status;
+
+ /* Data to Data */
+ OUTB(sc, KCS_DATA, data);
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
+ return (0);
+
+ /* Clear OBF */
+ kcs_clear_obf(sc, status);
+ return (1);
+}
+
+/*
+ * Write the last byte of a request message.
+ */
+static int
+kcs_write_last_byte(struct ipmi_softc *sc, u_char data)
+{
+ int status;
+
+ /* Write end to command */
+ OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_END);
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
+ /* error state */
+ return (0);
+
+ /* Clear OBF */
+ kcs_clear_obf(sc, status);
+
+ /* Send data byte to DATA. */
+ OUTB(sc, KCS_DATA, data);
+ return (1);
+}
+
+/*
+ * Read one byte of the reply message.
+ */
+static int
+kcs_read_byte(struct ipmi_softc *sc, u_char *data)
+{
+ int status;
+ u_char dummy;
+
+ /* Wait for IBF = 0 */
+ status = kcs_wait_for_ibf(sc, 0);
+
+ /* Read State */
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
+
+ /* Wait for OBF = 1 */
+ status = kcs_wait_for_obf(sc, 1);
+
+ /* Read Data_out */
+ *data = INB(sc, KCS_DATA);
+
+ /* Write READ into Data_in */
+ OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
+ return (1);
+ }
+
+ /* Idle State */
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
+
+ /* Wait for OBF = 1*/
+ status = kcs_wait_for_obf(sc, 1);
+
+ /* Read Dummy */
+ dummy = INB(sc, KCS_DATA);
+ return (2);
+ }
+
+ /* Error State */
+ return (0);
+}
+
+/*
+ * Send a request message and collect the reply. Returns true if we
+ * succeed.
+ */
+static int
+kcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+ u_char *cp, data;
+ int i, state;
+
+ /* Send the request. */
+ if (!kcs_start_write(sc)) {
+ device_printf(sc->ipmi_dev, "KCS: Failed to start write\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: WRITE_START... ok\n");
+#endif
+
+ if (!kcs_write_byte(sc, req->ir_addr)) {
+ device_printf(sc->ipmi_dev, "KCS: Failed to write address\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Wrote address: %02x\n", req->ir_addr);
+#endif
+
+ if (req->ir_requestlen == 0) {
+ if (!kcs_write_last_byte(sc, req->ir_command)) {
+ device_printf(sc->ipmi_dev,
+ "KCS: Failed to write command\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
+ req->ir_command);
+#endif
+ } else {
+ if (!kcs_write_byte(sc, req->ir_command)) {
+ device_printf(sc->ipmi_dev,
+ "KCS: Failed to write command\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
+ req->ir_command);
+#endif
+
+ cp = req->ir_request;
+ for (i = 0; i < req->ir_requestlen - 1; i++) {
+ if (!kcs_write_byte(sc, *cp++)) {
+ device_printf(sc->ipmi_dev,
+ "KCS: Failed to write data byte %d\n",
+ i + 1);
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Wrote data: %02x\n",
+ cp[-1]);
+#endif
+ }
+
+ if (!kcs_write_last_byte(sc, *cp)) {
+ device_printf(sc->ipmi_dev,
+ "KCS: Failed to write last dta byte\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Wrote last data: %02x\n",
+ *cp);
+#endif
+ }
+
+ /* Read the reply. First, read the NetFn/LUN. */
+ if (kcs_read_byte(sc, &data) != 1) {
+ device_printf(sc->ipmi_dev, "KCS: Failed to read address\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Read address: %02x\n", data);
+#endif
+ if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
+ device_printf(sc->ipmi_dev, "KCS: Reply address mismatch\n");
+ goto fail;
+ }
+
+ /* Next we read the command. */
+ if (kcs_read_byte(sc, &data) != 1) {
+ device_printf(sc->ipmi_dev, "KCS: Failed to read command\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Read command: %02x\n", data);
+#endif
+ if (data != req->ir_command) {
+ device_printf(sc->ipmi_dev, "KCS: Command mismatch\n");
+ goto fail;
+ }
+
+ /* Next we read the completion code. */
+ if (kcs_read_byte(sc, &req->ir_compcode) != 1) {
+ device_printf(sc->ipmi_dev,
+ "KCS: Failed to read completion code\n");
+ goto fail;
+ }
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Read completion code: %02x\n",
+ req->ir_compcode);
+#endif
+
+ /* Finally, read the reply from the BMC. */
+ i = 0;
+ for (;;) {
+ state = kcs_read_byte(sc, &data);
+ if (state == 0) {
+ device_printf(sc->ipmi_dev,
+ "KCS: Read failed on byte %d\n", i + 1);
+ goto fail;
+ }
+ if (state == 2)
+ break;
+ if (i < req->ir_replybuflen) {
+ req->ir_reply[i] = data;
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: Read data %02x\n",
+ data);
+ } else {
+ device_printf(sc->ipmi_dev,
+ "KCS: Read short %02x byte %d\n", data, i + 1);
+#endif
+ }
+ i++;
+ }
+ req->ir_replylen = i;
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: READ finished (%d bytes)\n", i);
+ if (req->ir_replybuflen < i)
+#else
+ if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
+#endif
+ device_printf(sc->ipmi_dev,
+ "KCS: Read short: %zd buffer, %d actual\n",
+ req->ir_replybuflen, i);
+ return (1);
+fail:
+ kcs_error(sc);
+ return (0);
+}
+
+static void
+kcs_loop(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+ struct ipmi_request *req;
+ int i, ok;
+
+ IPMI_LOCK(sc);
+ while ((req = ipmi_dequeue_request(sc)) != NULL) {
+ ok = 0;
+ for (i = 0; i < 3 && !ok; i++)
+ ok = kcs_polled_request(sc, req);
+ if (ok)
+ req->ir_error = 0;
+ else
+ req->ir_error = EIO;
+ ipmi_complete_request(sc, req);
+ }
+ IPMI_UNLOCK(sc);
+ kthread_exit(0);
+}
+
+static int
+kcs_startup(struct ipmi_softc *sc)
+{
+
+ return (kthread_create(kcs_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: kcs",
+ device_get_nameunit(sc->ipmi_dev)));
+}
+
+int
+ipmi_kcs_attach(struct ipmi_softc *sc)
+{
+ int status;
+
+ /* Setup function pointers. */
+ sc->ipmi_startup = kcs_startup;
+ sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
+
+ /* See if we can talk to the controller. */
+ status = INB(sc, KCS_CTL_STS);
+ if (status == 0xff) {
+ device_printf(sc->ipmi_dev, "couldn't find it\n");
+ return (ENXIO);
+ }
+
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "KCS: initial state: %02x\n", status);
+#endif
+ if (status & KCS_STATUS_OBF ||
+ KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE)
+ kcs_error(sc);
+
+ return (0);
+}
+
+/*
+ * Determine the alignment automatically for a PCI attachment. In this case,
+ * any unused bytes will return 0x00 when read. We make use of the C/D bit
+ * in the CTL_STS register to try to start a GET_STATUS transaction. When
+ * we write the command, that bit should be set, so we should get a non-zero
+ * value back when we read CTL_STS if the offset we are testing is the CTL_STS
+ * register.
+ */
+int
+ipmi_kcs_probe_align(struct ipmi_softc *sc)
+{
+ int data, status;
+
+ sc->ipmi_io_spacing = 1;
+retry:
+#ifdef KCS_DEBUG
+ device_printf(sc->ipmi_dev, "Trying KCS align %d... ", sc->ipmi_io_spacing);
+#endif
+
+ /* Wait for IBF = 0 */
+ status = INB(sc, KCS_CTL_STS);
+ while (status & KCS_STATUS_IBF) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+
+ OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
+
+ /* Wait for IBF = 0 */
+ status = INB(sc, KCS_CTL_STS);
+ while (status & KCS_STATUS_IBF) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+
+ /* If we got 0x00 back, then this must not be the CTL_STS register. */
+ if (status == 0) {
+#ifdef KCS_DEBUG
+ printf("failed\n");
+#endif
+ sc->ipmi_io_spacing <<= 1;
+ if (sc->ipmi_io_spacing > 4)
+ return (0);
+ goto retry;
+ }
+#ifdef KCS_DEBUG
+ printf("ok\n");
+#endif
+
+ /* Finish out the transaction. */
+
+ /* Clear OBF */
+ if (status && KCS_STATUS_OBF)
+ data = INB(sc, KCS_DATA);
+
+ /* 0x00 to DATA_IN */
+ OUTB(sc, KCS_DATA, 0);
+
+ /* Wait for IBF = 0 */
+ status = INB(sc, KCS_CTL_STS);
+ while (status & KCS_STATUS_IBF) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
+ /* Wait for IBF = 1 */
+ while (!(status & KCS_STATUS_OBF)) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+
+ /* Read error status. */
+ data = INB(sc, KCS_DATA);
+
+ /* Write dummy READ to DATA_IN. */
+ OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
+
+ /* Wait for IBF = 0 */
+ status = INB(sc, KCS_CTL_STS);
+ while (status & KCS_STATUS_IBF) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+ }
+
+ if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
+ /* Wait for IBF = 1 */
+ while (!(status & KCS_STATUS_OBF)) {
+ DELAY(100);
+ status = INB(sc, KCS_CTL_STS);
+ }
+
+ /* Clear OBF */
+ if (status && KCS_STATUS_OBF)
+ data = INB(sc, KCS_DATA);
+ } else
+ device_printf(sc->ipmi_dev, "KCS probe: end state %x\n",
+ KCS_STATUS_STATE(status));
+
+ return (1);
+}
diff --git a/sys/dev/ipmi/ipmi_pci.c b/sys/dev/ipmi/ipmi_pci.c
index b91891d..def00c9 100644
--- a/sys/dev/ipmi/ipmi_pci.c
+++ b/sys/dev/ipmi/ipmi_pci.c
@@ -28,18 +28,14 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
-#include <sys/malloc.h>
#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.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 <sys/selinfo.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@@ -52,17 +48,8 @@ __FBSDID("$FreeBSD$");
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
+static struct ipmi_ident
{
u_int16_t vendor;
u_int16_t device;
@@ -72,238 +59,237 @@ struct ipmi_ident
{0, 0, 0}
};
-static int
-ipmi_pci_probe(device_t dev) {
+const char *
+ipmi_pci_match(uint16_t vendor, uint16_t device)
+{
struct ipmi_ident *m;
+ for (m = ipmi_identifiers; m->vendor != 0; m++)
+ if (m->vendor == vendor && m->device == device)
+ return (m->desc);
+ return (NULL);
+}
+
+static int
+ipmi_pci_probe(device_t dev)
+{
+ const char *desc;
+
if (ipmi_attached)
- return ENXIO;
+ 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);
- }
+ desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
+ if (desc != NULL) {
+ device_set_desc(dev, desc);
+ return (BUS_PROBE_DEFAULT);
}
- return ENXIO;
+ return (ENXIO);
}
static int
-ipmi_pci_attach(device_t dev) {
+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;
+ struct ipmi_get_info info;
+ const char *mode;
+ int error, type;
+ /* Look for an IPMI entry in the SMBIOS table. */
+ if (!ipmi_smbios_identify(&info))
+ return (ENXIO);
- /*
- * 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);
- }
+ sc->ipmi_dev = dev;
- 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;
- }
+ switch (info.iface_type) {
+ case KCS_MODE:
+ mode = "KCS";
+ break;
+ case SMIC_MODE:
+ mode = "SMIC";
+ break;
+ case BT_MODE:
+ device_printf(dev, "BT mode is unsupported\n");
+ return (ENXIO);
+ default:
+ device_printf(dev, "No IPMI interface found\n");
+ return (ENXIO);
}
-
- if (smbios_attach_dev == NULL) {
- device_printf(dev, "Couldn't find isa/nexus device\n");
- goto bad;
+
+ device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
+ mode, info.io_mode ? "io" : "mem",
+ (uintmax_t)info.address, info.offset,
+ device_get_name(device_get_parent(dev)));
+ if (info.io_mode)
+ type = SYS_RES_IOPORT;
+ else
+ type = SYS_RES_MEMORY;
+
+ sc->ipmi_io_rid = PCIR_BAR(0);
+ sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
+ &sc->ipmi_io_rid, RF_ACTIVE);
+ sc->ipmi_io_type = type;
+ sc->ipmi_io_spacing = info.offset;
+
+ if (sc->ipmi_io_res[0] == NULL) {
+ device_printf(dev, "couldn't configure pci io res\n");
+ return (ENXIO);
}
- 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;
+
+ sc->ipmi_irq_rid = 0;
+ sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
+
+ switch (info.iface_type) {
+ case KCS_MODE:
+ error = ipmi_kcs_attach(sc);
+ if (error)
+ goto bad;
+ break;
+ case SMIC_MODE:
+ error = ipmi_smic_attach(sc);
+ if (error)
+ goto bad;
+ break;
}
- error = ipmi_smbios_probe(sc->ipmi_smbios_dev);
- if (error != 0) {
+ error = ipmi_attach(dev);
+ if (error)
goto bad;
+
+ return (0);
+bad:
+ ipmi_release_resources(dev);
+ return (error);
+}
+
+static device_method_t ipmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipmi_pci_probe),
+ DEVMETHOD(device_attach, ipmi_pci_attach),
+ DEVMETHOD(device_detach, ipmi_detach),
+ { 0, 0 }
+};
+
+static driver_t ipmi_pci_driver = {
+ "ipmi",
+ ipmi_methods,
+ sizeof(struct ipmi_softc)
+};
+
+DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
+
+/* Native IPMI on PCI driver. */
+
+static int
+ipmi2_pci_probe(device_t dev)
+{
+
+ if (pci_get_class(dev) == PCIC_SERIALBUS &&
+ pci_get_subclass(dev) == 0x07) {
+ device_set_desc(dev, "IPMI System Interface");
+ return (BUS_PROBE_GENERIC);
}
- 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);
- }
+ return (ENXIO);
+}
- if (!sc->ipmi_io_res){
- device_printf(dev, "couldn't configure pci io res\n");
- goto bad;
- }
+static int
+ipmi2_pci_attach(device_t dev)
+{
+ struct ipmi_softc *sc;
+ int error, iface, type;
- 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);
- }
+ sc = device_get_softc(dev);
+ sc->ipmi_dev = dev;
- if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
- device_printf(dev, "couldn't configure pci res\n");
- goto bad;
- }
+ /* Interface is determined by progif. */
+ switch (pci_get_progif(dev)) {
+ case 0:
+ iface = SMIC_MODE;
+ break;
+ case 1:
+ iface = KCS_MODE;
+ break;
+ case 2:
+ iface = BT_MODE;
+ device_printf(dev, "BT interface unsupported\n");
+ return (ENXIO);
+ default:
+ device_printf(dev, "Unsupported interface: %d\n",
+ pci_get_progif(dev));
+ return (ENXIO);
+ }
- 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;
+ /*
+ * Bottom bit of bar indicates resouce type. There should be
+ * constants in pcireg.h for fields in a BAR.
+ */
+ sc->ipmi_io_rid = PCIR_BAR(0);
+ if (pci_read_config(dev, PCIR_BAR(0), 4) & 0x1)
+ type = SYS_RES_IOPORT;
+ else
+ type = SYS_RES_MEMORY;
+ sc->ipmi_io_type = type;
+ sc->ipmi_io_spacing = 1;
+ sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
+ &sc->ipmi_io_rid, RF_ACTIVE);
+ if (sc->ipmi_io_res[0] == NULL) {
+ device_printf(dev, "couldn't map ports/memory\n");
+ return (ENXIO);
}
- 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_res = bus_alloc_resource_any(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);
+
+ switch (iface) {
+ case KCS_MODE:
+ device_printf(dev, "using KSC interface\n");
+
+ /*
+ * We have to examine the resource directly to determine the
+ * alignment.
+ */
+ if (!ipmi_kcs_probe_align(sc)) {
+ device_printf(dev, "Unable to determine alignment\n");
+ error = ENXIO;
+ goto bad;
}
+
+ error = ipmi_kcs_attach(sc);
+ if (error)
+ goto bad;
+ break;
+ case SMIC_MODE:
+ device_printf(dev, "using SMIC interface\n");
+
+ error = ipmi_smic_attach(sc);
+ if (error)
+ goto bad;
+ break;
}
+ error = ipmi_attach(dev);
+ if (error)
+ goto bad;
- return 0;
+ return (0);
bad:
- return ENXIO;
+ ipmi_release_resources(dev);
+ return (error);
}
-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 device_method_t ipmi2_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipmi2_pci_probe),
+ DEVMETHOD(device_attach, ipmi2_pci_attach),
+ DEVMETHOD(device_detach, ipmi_detach),
+ { 0, 0 }
+};
-static driver_t ipmi_pci_driver = {
- "ipmi",
- ipmi_methods,
- sizeof(struct ipmi_softc)
+static driver_t ipmi2_pci_driver = {
+ "ipmi",
+ ipmi2_methods,
+ sizeof(struct ipmi_softc)
};
-DRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
+DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0);
diff --git a/sys/dev/ipmi/ipmi_smbios.c b/sys/dev/ipmi/ipmi_smbios.c
index de14dfb..8d2e4dc 100644
--- a/sys/dev/ipmi/ipmi_smbios.c
+++ b/sys/dev/ipmi/ipmi_smbios.c
@@ -29,20 +29,13 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.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>
@@ -77,7 +70,7 @@ struct structure_header {
uint16_t handle;
};
-struct ipmi_device {
+struct ipmi_entry {
uint8_t type;
uint8_t length;
uint16_t handle;
@@ -90,50 +83,88 @@ struct ipmi_device {
uint8_t interrupt_number;
};
+/* Fields in the base_address field of an IPMI entry. */
+#define IPMI_BAR_MODE(ba) ((ba) & 0x0000000000000001)
+#define IPMI_BAR_ADDR(ba) ((ba) & 0xfffffffffffffffe)
+
+/* Fields in the base_address_modifier field of an IPMI entry. */
+#define IPMI_BAM_IRQ_TRIGGER 0x01
+#define IPMI_BAM_IRQ_POLARITY 0x02
+#define IPMI_BAM_IRQ_VALID 0x08
+#define IPMI_BAM_ADDR_LSB(bam) (((bam) & 0x10) >> 4)
+#define IPMI_BAM_REG_SPACING(bam) (((bam) & 0xc0) >> 6)
+#define SPACING_8 0x0
+#define SPACING_32 0x1
+#define SPACING_16 0x2
+
#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 struct ipmi_get_info ipmi_info;
+static int ipmi_probed;
+static struct mtx ipmi_info_mtx;
+MTX_SYSINIT(ipmi_info, &ipmi_info_mtx, "ipmi info", MTX_DEF);
+
+static char *get_strings(char *, char **);
+static void ipmi_smbios_probe(struct ipmi_get_info *);
+static int smbios_cksum (struct smbios_table_entry *);
+static void smbios_run_table(uint8_t *, int, dispatchproc_t *,
+ struct ipmi_get_info *);
+static void smbios_t38_proc_info(uint8_t *, char **,
+ struct ipmi_get_info *);
static void
smbios_t38_proc_info(uint8_t *p, char **table, struct ipmi_get_info *info)
{
- struct ipmi_device *s = (struct ipmi_device *) p;
+ struct ipmi_entry *s = (struct ipmi_entry *)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;
+ switch (s->interface_type) {
+ case KCS_MODE:
+ case SMIC_MODE:
+ info->address = IPMI_BAR_ADDR(s->base_address) |
+ IPMI_BAM_ADDR_LSB(s->base_address_modifier);
+ info->io_mode = IPMI_BAR_MODE(s->base_address);
+ switch (IPMI_BAM_REG_SPACING(s->base_address_modifier)) {
+ case SPACING_8:
+ info->offset = 1;
+ break;
+ case SPACING_32:
+ info->offset = 4;
+ break;
+ case SPACING_16:
+ info->offset = 2;
+ break;
+ default:
+ printf("SMBIOS: Invalid register spacing\n");
+ return;
+ }
break;
- case 0x11:
- info->offset = 0;
+ case SSIF_MODE:
+ if ((s->base_address & 0xffffffffffffff00) != 0) {
+ printf("SMBIOS: Invalid SSIF SMBus address, using BMC I2C slave address instead\n");
+ info->address = s->i2c_slave_address >> 1;
+ break;
+ }
+ info->address = IPMI_BAR_ADDR(s->base_address) >> 1;
break;
+ default:
+ return;
+ }
+ if (s->length > offsetof(struct ipmi_entry, interrupt_number)) {
+ if (s->interrupt_number > 15)
+ printf("SMBIOS: Non-ISA IRQ %d for IPMI\n",
+ s->interrupt_number);
+ else
+ info->irq = s->interrupt_number;
}
- info->io_mode = s->base_address & 1;
+ info->iface_type = s->interface_type;
}
static char *
@@ -147,7 +178,7 @@ get_strings(char *p, char **table)
*table = 0;
/* Skip past terminating null byte */
- return p + 1;
+ return (p + 1);
}
@@ -177,282 +208,88 @@ smbios_run_table(uint8_t *p, int entries, dispatchproc_t *dispatchstatus,
}
}
-device_t
-ipmi_smbios_identify (driver_t *driver, device_t parent)
+/*
+ * Walk the SMBIOS table looking for an IPMI (type 38) entry. If we find
+ * one, return the parsed data in the passed in ipmi_get_info structure and
+ * return true. If we don't find one, return false.
+ */
+static void
+ipmi_smbios_probe(struct ipmi_get_info *info)
{
- device_t child = NULL;
+ dispatchproc_t dispatch_smbios_ipmi[256];
+ struct smbios_table_entry *header;
+ void *table;
u_int32_t addr;
- int length;
- int rid1, rid2;
+ bzero(info, sizeof(struct ipmi_get_info));
+
+ /* Find the SMBIOS table header. */
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;
-}
+ if (addr == 0)
+ return;
-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;
+ /*
+ * Map the header. We first map a fixed size to get the actual
+ * length and then map it a second time with the actual length so
+ * we can verify the checksum.
+ */
+ header = pmap_mapbios(addr, sizeof(struct smbios_table_entry));
+ table = pmap_mapbios(addr, header->length);
+ pmap_unmapbios((vm_offset_t)header, sizeof(struct smbios_table_entry));
+ header = table;
+ if (smbios_cksum(header) != 0) {
+ pmap_unmapbios((vm_offset_t)header, header->length);
+ return;
}
- 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));
-
+ /* Now map the actual table and walk it looking for an IPMI entry. */
+ table = pmap_mapbios(header->structure_table_address,
+ header->structure_table_length);
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;
+ smbios_run_table(table, header->number_structures, dispatch_smbios_ipmi,
+ info);
+
+ /* Unmap everything. */
+ pmap_unmapbios((vm_offset_t)table, header->structure_table_length);
+ pmap_unmapbios((vm_offset_t)header, header->length);
}
-static int
-ipmi_smbios_attach(device_t dev)
+/*
+ * Return the SMBIOS IPMI table entry info to the caller. If we haven't
+ * searched the IPMI table yet, search it. Otherwise, return a cached
+ * copy of the data.
+ */
+int
+ipmi_smbios_identify(struct ipmi_get_info *info)
{
- 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;
+ mtx_lock(&ipmi_info_mtx);
+ switch (ipmi_probed) {
+ case 0:
+ /* Need to probe the SMBIOS table. */
+ ipmi_probed++;
+ mtx_unlock(&ipmi_info_mtx);
+ ipmi_smbios_probe(&ipmi_info);
+ mtx_lock(&ipmi_info_mtx);
+ ipmi_probed++;
+ wakeup(&ipmi_info);
+ break;
+ case 1:
+ /* Another thread is currently probing the table, so wait. */
+ while (ipmi_probed == 1)
+ msleep(&ipmi_info, &ipmi_info_mtx, 0, "ipmi info", 0);
+ break;
+ default:
+ /* The cached data is available. */
+ break;
}
- ipmi_attach(dev);
- return 0;
-bad:
- /*
- device_delete_child(device_get_parent(dev), dev);
- */
- return error;
+ bcopy(&ipmi_info, info, sizeof(ipmi_info));
+ mtx_unlock(&ipmi_info_mtx);
+
+ return (info->iface_type != 0);
}
static int
@@ -468,71 +305,5 @@ smbios_cksum (struct smbios_table_entry *e)
cksum += ptr[i];
}
- return cksum;
+ 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/ipmi_smbus.c b/sys/dev/ipmi/ipmi_smbus.c
new file mode 100644
index 0000000..3798a62
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_smbus.c
@@ -0,0 +1,131 @@
+/*-
+ * 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/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+
+#include <dev/smbus/smbconf.h>
+#include <dev/smbus/smbus.h>
+#include <dev/smbus/smb.h>
+
+#include "smbus_if.h"
+
+#ifdef LOCAL_MODULE
+#include <ipmivars.h>
+#else
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+static void ipmi_smbus_identify(driver_t *driver, device_t parent);
+static int ipmi_smbus_probe(device_t dev);
+static int ipmi_smbus_attach(device_t dev);
+
+static void
+ipmi_smbus_identify(driver_t *driver, device_t parent)
+{
+ struct ipmi_get_info info;
+
+ if (ipmi_smbios_identify(&info) && info.iface_type == SSIF_MODE &&
+ device_find_child(parent, "ipmi", -1) == NULL)
+ BUS_ADD_CHILD(parent, 0, "ipmi", -1);
+}
+
+static int
+ipmi_smbus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "IPMI System Interface");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ipmi_smbus_attach(device_t dev)
+{
+ struct ipmi_softc *sc = device_get_softc(dev);
+ struct ipmi_get_info info;
+ int error;
+
+ /* This should never fail. */
+ if (!ipmi_smbios_identify(&info))
+ return (ENXIO);
+
+ if (info.iface_type != SSIF_MODE) {
+ device_printf(dev, "No SSIF IPMI interface found\n");
+ return (ENXIO);
+ }
+
+ sc->ipmi_dev = dev;
+
+ if (info.irq != 0) {
+ sc->ipmi_irq_rid = 0;
+ sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &sc->ipmi_irq_rid, info.irq, info.irq, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ }
+
+ device_printf(dev, "SSIF mode found at address 0x%llx on %s\n",
+ (long long)info.address, device_get_name(device_get_parent(dev)));
+ error = ipmi_ssif_attach(sc, device_get_parent(dev), info.address);
+ if (error)
+ goto bad;
+
+ error = ipmi_attach(dev);
+ if (error)
+ goto bad;
+
+ return (0);
+bad:
+ ipmi_release_resources(dev);
+ return (error);
+}
+
+static device_method_t ipmi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, ipmi_smbus_identify),
+ DEVMETHOD(device_probe, ipmi_smbus_probe),
+ DEVMETHOD(device_attach, ipmi_smbus_attach),
+ DEVMETHOD(device_detach, ipmi_detach),
+ { 0, 0 }
+};
+
+static driver_t ipmi_smbus_driver = {
+ "ipmi",
+ ipmi_methods,
+ sizeof(struct ipmi_softc)
+};
+
+DRIVER_MODULE(ipmi_smbus, smbus, ipmi_smbus_driver, ipmi_devclass, 0, 0);
+MODULE_DEPEND(ipmi_smbus, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
diff --git a/sys/dev/ipmi/ipmi_smic.c b/sys/dev/ipmi/ipmi_smic.c
new file mode 100644
index 0000000..bdffd1c
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_smic.c
@@ -0,0 +1,361 @@
+/*-
+ * 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/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+#include <machine/bus.h>
+
+#ifdef LOCAL_MODULE
+#include <ipmi.h>
+#include <ipmivars.h>
+#else
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+static void smic_wait_for_tx_okay(struct ipmi_softc *);
+static void smic_wait_for_rx_okay(struct ipmi_softc *);
+static void smic_wait_for_not_busy(struct ipmi_softc *);
+static void smic_set_busy(struct ipmi_softc *);
+
+static void
+smic_wait_for_tx_okay(struct ipmi_softc *sc)
+{
+ int flags;
+
+ do {
+ flags = INB(sc, SMIC_FLAGS);
+ } while (!(flags & SMIC_STATUS_TX_RDY));
+}
+
+static void
+smic_wait_for_rx_okay(struct ipmi_softc *sc)
+{
+ int flags;
+
+ do {
+ flags = INB(sc, SMIC_FLAGS);
+ } while (!(flags & SMIC_STATUS_RX_RDY));
+}
+
+static void
+smic_wait_for_not_busy(struct ipmi_softc *sc)
+{
+ int flags;
+
+ do {
+ flags = INB(sc, SMIC_FLAGS);
+ } while (flags & SMIC_STATUS_BUSY);
+}
+
+static void
+smic_set_busy(struct ipmi_softc *sc)
+{
+ int flags;
+
+ flags = INB(sc, SMIC_FLAGS);
+ flags |= SMIC_STATUS_BUSY;
+ flags &= ~SMIC_STATUS_RESERVED;
+ OUTB(sc, SMIC_FLAGS, flags);
+}
+
+/*
+ * Start a transfer with a WR_START transaction that sends the NetFn/LUN
+ * address.
+ */
+static int
+smic_start_write(struct ipmi_softc *sc, u_char data)
+{
+ u_char error, status;
+
+ smic_wait_for_not_busy(sc);
+
+ OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
+ OUTB(sc, SMIC_DATA, data);
+ smic_set_busy(sc);
+ smic_wait_for_not_busy(sc);
+ status = INB(sc, SMIC_CTL_STS);
+ if (status != SMIC_SC_SMS_WR_START) {
+ error = INB(sc, SMIC_DATA);
+ device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
+ error);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Write a byte in the middle of the message (either the command or one of
+ * the data bytes) using a WR_NEXT transaction.
+ */
+static int
+smic_write_next(struct ipmi_softc *sc, u_char data)
+{
+ u_char error, status;
+
+ OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
+ smic_wait_for_tx_okay(sc);
+ OUTB(sc, SMIC_DATA, data);
+ smic_set_busy(sc);
+ smic_wait_for_not_busy(sc);
+ status = INB(sc, SMIC_CTL_STS);
+ if (status != SMIC_SC_SMS_WR_NEXT) {
+ error = INB(sc, SMIC_DATA);
+ device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
+ error);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Write the last byte of a transfer to end the write phase via a WR_END
+ * transaction.
+ */
+static int
+smic_write_last(struct ipmi_softc *sc, u_char data)
+{
+ u_char error, status;
+
+ OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
+ smic_wait_for_tx_okay(sc);
+ OUTB(sc, SMIC_DATA, data);
+ smic_set_busy(sc);
+ smic_wait_for_not_busy(sc);
+ status = INB(sc, SMIC_CTL_STS);
+ if (status != SMIC_SC_SMS_WR_END) {
+ error = INB(sc, SMIC_DATA);
+ device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
+ error);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Start the read phase of a transfer with a RD_START transaction.
+ */
+static int
+smic_start_read(struct ipmi_softc *sc, u_char *data)
+{
+ u_char error, status;
+
+ smic_wait_for_not_busy(sc);
+
+ OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
+ smic_wait_for_rx_okay(sc);
+ smic_set_busy(sc);
+ smic_wait_for_not_busy(sc);
+ status = INB(sc, SMIC_CTL_STS);
+ if (status != SMIC_SC_SMS_RD_START) {
+ error = INB(sc, SMIC_DATA);
+ device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
+ error);
+ return (0);
+ }
+ *data = INB(sc, SMIC_DATA);
+ return (1);
+}
+
+/*
+ * Read a byte via a RD_NEXT transaction. If this was the last byte, return
+ * 2 rather than 1.
+ */
+static int
+smic_read_byte(struct ipmi_softc *sc, u_char *data)
+{
+ u_char error, status;
+
+ OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
+ smic_wait_for_rx_okay(sc);
+ smic_set_busy(sc);
+ smic_wait_for_not_busy(sc);
+ status = INB(sc, SMIC_CTL_STS);
+ if (status != SMIC_SC_SMS_RD_NEXT &&
+ status != SMIC_SC_SMS_RD_END) {
+ error = INB(sc, SMIC_DATA);
+ device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
+ error);
+ return (0);
+ }
+ *data = INB(sc, SMIC_DATA);
+ if (status == SMIC_SC_SMS_RD_NEXT)
+ return (1);
+ else
+ return (2);
+}
+
+/* Complete a transfer via a RD_END transaction after reading the last byte. */
+static int
+smic_read_end(struct ipmi_softc *sc)
+{
+ u_char error, status;
+
+ OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
+ smic_set_busy(sc);
+ smic_wait_for_not_busy(sc);
+ status = INB(sc, SMIC_CTL_STS);
+ if (status != SMIC_SC_SMS_RDY) {
+ error = INB(sc, SMIC_DATA);
+ device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
+ error);
+ return (0);
+ }
+ return (1);
+}
+
+static int
+smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+ u_char *cp, data;
+ int i, state;
+
+ /* First, start the message with the address. */
+ if (!smic_start_write(sc, req->ir_addr))
+ return (0);
+
+ if (req->ir_requestlen == 0) {
+ /* Send the command as the last byte. */
+ if (!smic_write_last(sc, req->ir_command))
+ return (0);
+ } else {
+ /* Send the command. */
+ if (!smic_write_next(sc, req->ir_command))
+ return (0);
+
+ /* Send the payload. */
+ cp = req->ir_request;
+ for (i = 0; i < req->ir_requestlen - 1; i++)
+ if (!smic_write_next(sc, *cp++))
+ return (0);
+ if (!smic_write_last(sc, *cp))
+ return (0);
+ }
+
+ /* Start the read phase by reading the NetFn/LUN. */
+ if (smic_start_read(sc, &data) != 1)
+ return (0);
+ if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
+ device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
+ return (0);
+ }
+
+ /* Read the command. */
+ if (smic_read_byte(sc, &data) != 1)
+ return (0);
+ if (data != req->ir_command) {
+ device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
+ return (0);
+ }
+
+ /* Read the completion code. */
+ state = smic_read_byte(sc, &req->ir_compcode);
+ if (state == 0)
+ return (0);
+
+ /* Finally, read the reply from the BMC. */
+ i = 0;
+ while (state == 1) {
+ state = smic_read_byte(sc, &data);
+ if (state == 0)
+ return (0);
+ if (state == 2)
+ break;
+ if (i < req->ir_replybuflen)
+ req->ir_reply[i] = data;
+ i++;
+ }
+
+ /* Terminate the transfer. */
+ if (!smic_read_end(sc))
+ return (0);
+ req->ir_replylen = i;
+ if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
+ device_printf(sc->ipmi_dev,
+ "SMIC: Read short: %zd buffer, %d actual\n",
+ req->ir_replybuflen, i);
+ return (1);
+}
+
+static void
+smic_loop(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+ struct ipmi_request *req;
+ int i, ok;
+
+ IPMI_LOCK(sc);
+ while ((req = ipmi_dequeue_request(sc)) != NULL) {
+ ok = 0;
+ for (i = 0; i < 3 && !ok; i++)
+ ok = smic_polled_request(sc, req);
+ if (ok)
+ req->ir_error = 0;
+ else
+ req->ir_error = EIO;
+ ipmi_complete_request(sc, req);
+ }
+ IPMI_UNLOCK(sc);
+ kthread_exit(0);
+}
+
+static int
+smic_startup(struct ipmi_softc *sc)
+{
+
+ return (kthread_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
+ "%s: smic", device_get_nameunit(sc->ipmi_dev)));
+}
+
+int
+ipmi_smic_attach(struct ipmi_softc *sc)
+{
+ int flags;
+
+ /* Setup function pointers. */
+ sc->ipmi_startup = smic_startup;
+ sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
+
+ /* See if we can talk to the controller. */
+ flags = INB(sc, SMIC_FLAGS);
+ if (flags == 0xff) {
+ device_printf(sc->ipmi_dev, "couldn't find it\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
diff --git a/sys/dev/ipmi/ipmi_ssif.c b/sys/dev/ipmi/ipmi_ssif.c
new file mode 100644
index 0000000..2f90b6e
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_ssif.c
@@ -0,0 +1,375 @@
+/*-
+ * 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/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/module.h>
+#include <sys/selinfo.h>
+
+#include <dev/smbus/smbconf.h>
+#include <dev/smbus/smb.h>
+
+#include "smbus_if.h"
+
+#ifdef LOCAL_MODULE
+#include <ipmivars.h>
+#else
+#include <dev/ipmi/ipmivars.h>
+#endif
+
+#define SMBUS_WRITE_SINGLE 0x02
+#define SMBUS_WRITE_START 0x06
+#define SMBUS_WRITE_CONT 0x07
+#define SMBUS_READ_START 0x03
+#define SMBUS_READ_CONT 0x09
+#define SMBUS_DATA_SIZE 32
+
+#ifdef SSIF_DEBUG
+static void
+dump_buffer(device_t dev, const char *msg, u_char *bytes, int len)
+{
+ int i;
+
+ device_printf(dev, "%s:", msg);
+ for (i = 0; i < len; i++)
+ printf(" %02x", bytes[i]);
+ printf("\n");
+}
+#endif
+
+static int
+ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+ u_char ssif_buf[SMBUS_DATA_SIZE];
+ device_t dev = sc->ipmi_dev;
+ device_t smbus = sc->ipmi_ssif_smbus;
+ u_char *cp, block, count, offset;
+ size_t len;
+ int error;
+
+ /* Acquire the bus while we send the request. */
+ if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
+ return (0);
+
+ /*
+ * First, send out the request. Begin by filling out the first
+ * packet which includes the NetFn/LUN and command.
+ */
+ ssif_buf[0] = req->ir_addr;
+ ssif_buf[1] = req->ir_command;
+ if (req->ir_requestlen > 0)
+ bcopy(req->ir_request, &ssif_buf[2],
+ min(req->ir_requestlen, SMBUS_DATA_SIZE - 2));
+
+ /* Small requests are sent with a single command. */
+ if (req->ir_requestlen <= 30) {
+#ifdef SSIF_DEBUG
+ dump_buffer(dev, "WRITE_SINGLE", ssif_buf,
+ req->ir_requestlen + 2);
+#endif
+ error = smbus_error(smbus_bwrite(smbus,
+ sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE,
+ req->ir_requestlen + 2, ssif_buf));
+ if (error) {
+#ifdef SSIF_ERROR_DEBUG
+ device_printf(dev, "SSIF: WRITE_SINGLE error %d\n",
+ error);
+#endif
+ goto fail;
+ }
+ } else {
+ /* Longer requests are sent out in 32-byte messages. */
+#ifdef SSIF_DEBUG
+ dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE);
+#endif
+ error = smbus_error(smbus_bwrite(smbus,
+ sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START,
+ SMBUS_DATA_SIZE, ssif_buf));
+ if (error) {
+#ifdef SSIF_ERROR_DEBUG
+ device_printf(dev, "SSIF: WRITE_START error %d\n",
+ error);
+#endif
+ goto fail;
+ }
+
+ len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2);
+ cp = req->ir_request + (SMBUS_DATA_SIZE - 2);
+ while (len > 0) {
+#ifdef SSIF_DEBUG
+ dump_buffer(dev, "WRITE_CONT", cp,
+ min(len, SMBUS_DATA_SIZE));
+#endif
+ error = smbus_error(smbus_bwrite(smbus,
+ sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
+ min(len, SMBUS_DATA_SIZE), cp));
+ if (error) {
+#ifdef SSIF_ERROR_DEBUG
+ device_printf(dev, "SSIF: WRITE_CONT error %d\n",
+ error);
+#endif
+ goto fail;
+ }
+ cp += SMBUS_DATA_SIZE;
+ len -= SMBUS_DATA_SIZE;
+ }
+
+ /*
+ * The final WRITE_CONT transaction has to have a non-zero
+ * length that is also not SMBUS_DATA_SIZE. If our last
+ * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE
+ * bytes, then len will be 0, and we send an extra 0x00 byte
+ * to terminate the transaction.
+ */
+ if (len == 0) {
+ char c = 0;
+
+#ifdef SSIF_DEBUG
+ dump_buffer(dev, "WRITE_CONT", &c, 1);
+#endif
+ error = smbus_error(smbus_bwrite(smbus,
+ sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
+ 1, &c));
+ if (error) {
+#ifdef SSIF_ERROR_DEBUG
+ device_printf(dev, "SSIF: WRITE_CONT error %d\n",
+ error);
+#endif
+ goto fail;
+ }
+ }
+ }
+
+ /* Release the bus. */
+ smbus_release_bus(smbus, dev);
+
+ /* Give the BMC 100ms to chew on the request. */
+ tsleep(&error, 0, "ssifwt", hz / 10);
+
+ /* Try to read the first packet. */
+read_start:
+ if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
+ return (0);
+ count = SMBUS_DATA_SIZE;
+ error = smbus_error(smbus_bread(smbus,
+ sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf));
+ if (error == ENXIO || error == EBUSY) {
+ smbus_release_bus(smbus, dev);
+#ifdef SSIF_DEBUG
+ device_printf(dev, "SSIF: READ_START retry\n");
+#endif
+ /* Give the BMC another 10ms. */
+ tsleep(&error, 0, "ssifwt", hz / 100);
+ goto read_start;
+ }
+ if (error) {
+#ifdef SSIF_ERROR_DEBUG
+ device_printf(dev, "SSIF: READ_START failed: %d\n", error);
+#endif
+ goto fail;
+ }
+#ifdef SSIF_DEBUG
+ device_printf("SSIF: READ_START: ok\n");
+#endif
+
+ /*
+ * If this is the first part of a multi-part read, then we need to
+ * skip the first two bytes.
+ */
+ if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1)
+ offset = 2;
+ else
+ offset = 0;
+
+ /* We had better get the reply header. */
+ if (count < 3) {
+ device_printf(dev, "SSIF: Short reply packet\n");
+ goto fail;
+ }
+
+ /* Verify the NetFn/LUN. */
+ if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) {
+ device_printf(dev, "SSIF: Reply address mismatch\n");
+ goto fail;
+ }
+
+ /* Verify the command. */
+ if (ssif_buf[offset + 1] != req->ir_command) {
+ device_printf(dev, "SMIC: Command mismatch\n");
+ goto fail;
+ }
+
+ /* Read the completion code. */
+ req->ir_compcode = ssif_buf[offset + 2];
+
+ /* If this is a single read, just copy the data and return. */
+ if (offset == 0) {
+#ifdef SSIF_DEBUG
+ dump_buffer(dev, "READ_SINGLE", ssif_buf, count);
+#endif
+ len = count - 3;
+ bcopy(&ssif_buf[3], req->ir_reply,
+ min(req->ir_replybuflen, len));
+ goto done;
+ }
+
+ /*
+ * This is the first part of a multi-read transaction, so copy
+ * out the payload and start looping.
+ */
+#ifdef SSIF_DEBUG
+ dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2);
+#endif
+ bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5));
+ len = count - 5;
+ block = 1;
+
+ for (;;) {
+ /* Read another packet via READ_CONT. */
+ count = SMBUS_DATA_SIZE;
+ error = smbus_error(smbus_bread(smbus,
+ sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count,
+ ssif_buf));
+ if (error) {
+#ifdef SSIF_ERROR_DEBUG
+ printf("SSIF: READ_CONT failed: %d\n", error);
+#endif
+ goto fail;
+ }
+#ifdef SSIF_DEBUG
+ device_printf(dev, "SSIF: READ_CONT... ok\n");
+#endif
+
+ /* Verify the block number. 0xff marks the last block. */
+ if (ssif_buf[0] != 0xff && ssif_buf[0] != block) {
+ device_printf(dev, "SSIF: Read wrong block %d %d\n",
+ ssif_buf[0], block);
+ goto fail;
+ }
+ if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) {
+ device_printf(dev,
+ "SSIF: Read short middle block, length %d\n",
+ count);
+ goto fail;
+ }
+#ifdef SSIF_DEBUG
+ if (ssif_buf[0] == 0xff)
+ dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1);
+ else
+ dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1);
+#endif
+ if (len < req->ir_replybuflen)
+ bcopy(&ssif_buf[1], &req->ir_reply[len],
+ min(req->ir_replybuflen - len, count - 1));
+ len += count - 1;
+
+ /* If this was the last block we are done. */
+ if (ssif_buf[0] != 0xff)
+ break;
+ block++;
+ }
+
+done:
+ /* Save the total length and return success. */
+ req->ir_replylen = len;
+ smbus_release_bus(smbus, dev);
+ return (1);
+
+fail:
+ smbus_release_bus(smbus, dev);
+ return (0);
+}
+
+static void
+ssif_loop(void *arg)
+{
+ struct ipmi_softc *sc = arg;
+ struct ipmi_request *req;
+ int i, ok;
+
+ IPMI_LOCK(sc);
+ while ((req = ipmi_dequeue_request(sc)) != NULL) {
+ IPMI_UNLOCK(sc);
+ ok = 0;
+ for (i = 0; i < 5; i++) {
+ ok = ssif_polled_request(sc, req);
+ if (ok)
+ break;
+
+ /* Wait 60 ms between retries. */
+ tsleep(&ok, 0, "retry", 60 * hz / 1000);
+#ifdef SSIF_RETRY_DEBUG
+ device_printf(sc->ipmi_dev,
+ "SSIF: Retrying request (%d)\n", i + 1);
+#endif
+ }
+ if (ok)
+ req->ir_error = 0;
+ else
+ req->ir_error = EIO;
+ IPMI_LOCK(sc);
+ ipmi_complete_request(sc, req);
+ IPMI_UNLOCK(sc);
+
+ /* Enforce 10ms between requests. */
+ tsleep(&ok, 0, "delay", hz / 100);
+
+ IPMI_LOCK(sc);
+ }
+ IPMI_UNLOCK(sc);
+ kthread_exit(0);
+}
+
+static int
+ssif_startup(struct ipmi_softc *sc)
+{
+
+ return (kthread_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0,
+ "%s: ssif", device_get_nameunit(sc->ipmi_dev)));
+}
+
+int
+ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address)
+{
+
+ /* Setup smbus address. */
+ sc->ipmi_ssif_smbus = smbus;
+ sc->ipmi_ssif_smbus_address = smbus_address;
+
+ /* Setup function pointers. */
+ sc->ipmi_startup = ssif_startup;
+ sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
+
+ return (0);
+}
diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h
index c793eab..c56fb88 100644
--- a/sys/dev/ipmi/ipmivars.h
+++ b/sys/dev/ipmi/ipmivars.h
@@ -26,50 +26,116 @@
* $FreeBSD$
*/
+#ifndef __IPMIVARS_H__
+#define __IPMIVARS_H__
+
struct ipmi_get_info {
- int kcs_mode;
- int smic_mode;
+ int iface_type;
uint64_t address;
int offset;
int io_mode;
+ int irq;
+};
+
+struct ipmi_device;
+
+struct ipmi_request {
+ TAILQ_ENTRY(ipmi_request) ir_link;
+ struct ipmi_device *ir_owner; /* Driver uses NULL. */
+ u_char *ir_request; /* Request is data to send to BMC. */
+ size_t ir_requestlen;
+ u_char *ir_reply; /* Reply is data read from BMC. */
+ size_t ir_replybuflen; /* Length of ir_reply[] buffer. */
+ int ir_replylen; /* Length of reply from BMC. */
+ int ir_error;
+ long ir_msgid;
+ uint8_t ir_addr;
+ uint8_t ir_command;
+ uint8_t ir_compcode;
+};
+
+#define MAX_RES 3
+#define KCS_DATA 0
+#define KCS_CTL_STS 1
+#define SMIC_DATA 0
+#define SMIC_CTL_STS 1
+#define SMIC_FLAGS 2
+
+struct ipmi_softc;
+
+/* Per struct cdev data. */
+struct ipmi_device {
+ TAILQ_ENTRY(ipmi_device) ipmi_link;
+ TAILQ_HEAD(,ipmi_request) ipmi_completed_requests;
+ struct selinfo ipmi_select;
+ struct ipmi_softc *ipmi_softc;
+ struct cdev *ipmi_cdev;
+ int ipmi_open;
+ int ipmi_closing;
+ int ipmi_requests;
+ u_char ipmi_address; /* IPMB address. */
+ u_char ipmi_lun;
+};
+
+struct ipmi_kcs {
+};
+
+struct ipmi_smic {
+};
+
+struct ipmi_ssif {
+ device_t smbus;
+ int smbus_address;
};
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;
+ union {
+ struct ipmi_kcs kcs;
+ struct ipmi_smic smic;
+ struct ipmi_ssif ssif;
+ } _iface;
int ipmi_io_rid;
- struct resource * ipmi_io_res;
- int ipmi_mem_rid;
- struct resource * ipmi_mem_res;
+ int ipmi_io_type;
+ struct resource *ipmi_io_res[MAX_RES];
+ int ipmi_io_spacing;
int ipmi_irq_rid;
- struct resource * ipmi_irq_res;
+ 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;
+ int ipmi_detaching;
+#ifdef CLONING
+ int ipmi_cloning;
+ u_int ipmi_cdev_mask;
+ TAILQ_HEAD(,ipmi_device) ipmi_cdevs;
+#else
+ struct ipmi_device ipmi_idev;
+#endif
+ TAILQ_HEAD(,ipmi_request) ipmi_pending_requests;
+#ifdef CLONING
+ eventhandler_tag ipmi_clone_tag;
+#endif
+ eventhandler_tag ipmi_watchdog_tag;
+ struct intr_config_hook ipmi_ich;
+ struct mtx ipmi_lock;
+ struct cv ipmi_request_added;
+ struct proc *ipmi_kthread;
+ driver_intr_t *ipmi_intr;
+ int (*ipmi_startup)(struct ipmi_softc *);
+ int (*ipmi_enqueue_request)(struct ipmi_softc *, struct ipmi_request *);
};
+#define ipmi_ssif_smbus_address _iface.ssif.smbus_address
+#define ipmi_ssif_smbus _iface.ssif.smbus
+
struct ipmi_ipmb {
u_char foo;
};
+#define KCS_MODE 0x01
+#define SMIC_MODE 0x02
+#define BT_MODE 0x03
+#define SSIF_MODE 0x04
+
/* KCS status flags */
#define KCS_STATUS_OBF 0x01 /* Data Out ready from BMC */
#define KCS_STATUS_IBF 0x02 /* Data In from System */
@@ -84,11 +150,13 @@ struct ipmi_ipmb {
#define KCS_STATUS_STATE_READ 0x1
#define KCS_STATUS_STATE_WRITE 0x2
#define KCS_STATUS_STATE_ERROR 0x3
+#define KCS_IFACE_STATUS_OK 0x00
#define KCS_IFACE_STATUS_ABORT 0x01
#define KCS_IFACE_STATUS_ILLEGAL 0x02
#define KCS_IFACE_STATUS_LENGTH_ERR 0x06
+#define KCS_IFACE_STATUS_UNKNOWN_ERR 0xff
-/* KCD control codes */
+/* KCS control codes */
#define KCS_CONTROL_GET_STATUS_ABORT 0x60
#define KCS_CONTROL_WRITE_START 0x61
#define KCS_CONTROL_WRITE_END 0x62
@@ -98,9 +166,10 @@ struct ipmi_ipmb {
#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_SMI 0x10 /* asserted SMI */
#define SMIC_STATUS_TX_RDY 0x40 /* Ready to accept WRITE */
#define SMIC_STATUS_RX_RDY 0x80 /* Ready to read */
+#define SMIC_STATUS_RESERVED 0x22
/* SMIC control codes */
#define SMIC_CC_SMS_GET_STATUS 0x40
@@ -120,19 +189,78 @@ struct ipmi_ipmb {
#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)
+#define IPMI_ADDR(netfn, lun) ((netfn) << 2 | (lun))
+#define IPMI_REPLY_ADDR(addr) ((addr) + 0x4)
+
+#define IPMI_LOCK(sc) mtx_lock(&(sc)->ipmi_lock)
+#define IPMI_UNLOCK(sc) mtx_unlock(&(sc)->ipmi_lock)
+#define IPMI_LOCK_ASSERT(sc) mtx_assert(&(sc)->ipmi_lock, MA_OWNED)
+
+#define ipmi_alloc_driver_request(addr, cmd, reqlen, replylen) \
+ ipmi_alloc_request(NULL, 0, (addr), (cmd), (reqlen), (replylen))
-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 *);
+#if __FreeBSD_version < 601105
+#define bus_read_1(r, o) \
+ bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), (o))
+#define bus_write_1(r, o, v) \
+ bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), (o), (v))
+#endif
+
+/* I/O to a single I/O resource. */
+#define INB_SINGLE(sc, x) \
+ bus_read_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x))
+#define OUTB_SINGLE(sc, x, value) \
+ bus_write_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x), value)
+
+/* I/O with each register in its in I/O resource. */
+#define INB_MULTIPLE(sc, x) \
+ bus_read_1((sc)->ipmi_io_res[(x)], 0)
+#define OUTB_MULTIPLE(sc, x, value) \
+ bus_write_1((sc)->ipmi_io_res[(x)], 0, value)
+
+/*
+ * Determine I/O method based on whether or not we have more than one I/O
+ * resource.
+ */
+#define INB(sc, x) \
+ ((sc)->ipmi_io_res[1] != NULL ? INB_MULTIPLE(sc, x) : INB_SINGLE(sc, x))
+#define OUTB(sc, x, value) \
+ ((sc)->ipmi_io_res[1] != NULL ? OUTB_MULTIPLE(sc, x, value) : \
+ OUTB_SINGLE(sc, x, value))
+
+#define MAX_TIMEOUT 3 * hz
+
+int ipmi_attach(device_t);
+int ipmi_detach(device_t);
+void ipmi_release_resources(device_t);
+
+/* Manage requests. */
+struct ipmi_request *ipmi_alloc_request(struct ipmi_device *, long, uint8_t,
+ uint8_t, size_t, size_t);
+void ipmi_complete_request(struct ipmi_softc *, struct ipmi_request *);
+struct ipmi_request *ipmi_dequeue_request(struct ipmi_softc *);
+void ipmi_free_request(struct ipmi_request *);
+int ipmi_polled_enqueue_request(struct ipmi_softc *, struct ipmi_request *);
+int ipmi_submit_driver_request(struct ipmi_softc *, struct ipmi_request *,
+ int);
+
+/* Identify BMC interface via SMBIOS. */
+int ipmi_smbios_identify(struct ipmi_get_info *);
+
+/* Match BMC PCI device listed in SMBIOS. */
+const char *ipmi_pci_match(uint16_t, uint16_t);
+
+/* Interface attach routines. */
+int ipmi_kcs_attach(struct ipmi_softc *);
+int ipmi_kcs_probe_align(struct ipmi_softc *);
+int ipmi_smic_attach(struct ipmi_softc *);
+int ipmi_ssif_attach(struct ipmi_softc *, device_t, int);
+
+#ifdef IPMB
+int ipmi_handle_attn(struct ipmi_softc *);
+#endif
-device_t ipmi_smbios_identify (driver_t *driver, device_t parent);
extern devclass_t ipmi_devclass;
extern int ipmi_attached;
+
+#endif /* !__IPMIVARS_H__ */
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 1a28d4a..8d86fcf 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -698,6 +698,7 @@ options SAFE_RNDTEST # enable rndtest support
# Miscellaneous hardware:
#
# apm: Laptop Advanced Power Management (experimental)
+# ipmi: Intelligent Platform Management Interface
# pmtimer: Timer device driver for power management events (APM or ACPI)
# smapi: System Management Application Program Interface driver
# smbios: DMI/SMBIOS entry point
@@ -730,6 +731,7 @@ options SAFE_RNDTEST # enable rndtest support
device apm
hint.apm.0.flags="0x20"
+device ipmi
device smapi
device smbios
device vpd
diff --git a/sys/modules/ipmi/Makefile b/sys/modules/ipmi/Makefile
index 966ccd8..3f45c73 100644
--- a/sys/modules/ipmi/Makefile
+++ b/sys/modules/ipmi/Makefile
@@ -2,8 +2,12 @@
.PATH: ${.CURDIR}/../../dev/ipmi
+# XXX - ipmi_smbus and ipmi_ssif depend on smbus
+# XXX - ipmi_acpi depends on acpi
KMOD= ipmi
-SRCS= ipmi.c ipmi_pci.c ipmi_smbios.c
-SRCS+= bus_if.h device_if.h pci_if.h vnode_if.h
+SRCS= ipmi.c ipmi_kcs.c ipmi_smic.c ipmi_smbios.c ipmi_ssif.c
+SRCS+= ipmi_acpi.c ipmi_isa.c ipmi_pci.c ipmi_smbus.c
+SRCS+= opt_acpi.h
+SRCS+= acpi_if.h bus_if.h device_if.h isa_if.h pci_if.h smbus_if.h
.include <bsd.kmod.mk>
diff --git a/sys/sys/ipmi.h b/sys/sys/ipmi.h
index 8832030..578d78c 100644
--- a/sys/sys/ipmi.h
+++ b/sys/sys/ipmi.h
@@ -26,6 +26,9 @@
* $FreeBSD$
*/
+#ifndef __SYS_IPMI_H__
+#define __SYS_IPMI_H__
+
#define IPMI_MAX_ADDR_SIZE 0x20
#define IPMI_MAX_RX 1024
#define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */
@@ -117,3 +120,36 @@ struct ipmi_ipmb_addr {
unsigned char slave_addr;
unsigned char lun;
};
+
+#if defined(__amd64__)
+/* Compatiblity with 32-bit binaries. */
+
+#define IPMICTL_RECEIVE_MSG_TRUNC_32 _IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv32)
+#define IPMICTL_RECEIVE_MSG_32 _IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv32)
+#define IPMICTL_SEND_COMMAND_32 _IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req32)
+
+struct ipmi_msg32 {
+ unsigned char netfn;
+ unsigned char cmd;
+ unsigned short data_len;
+ uint32_t data;
+};
+
+struct ipmi_req32 {
+ uint32_t addr;
+ unsigned int addr_len;
+ int32_t msgid;
+ struct ipmi_msg32 msg;
+};
+
+struct ipmi_recv32 {
+ int recv_type;
+ uint32_t addr;
+ unsigned int addr_len;
+ int32_t msgid;
+ struct ipmi_msg32 msg;
+};
+
+#endif
+
+#endif /* !__SYS_IPMI_H__ */
OpenPOWER on IntegriCloud