changeset 13848:440c93ffaf29

370 would like an open ipmi baseboard driver Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com> Reviewed by: Albert Lee <trisk@nexenta.com> Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com> Reviewed by: Alek Pinchuk <pinchuk.alek@gmail.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Dan McDonald <danmcd@nexenta.com>
author Jerry Jelinek <jerry.jelinek@joyent.com>
date Thu, 26 Apr 2012 15:49:44 +0000
parents d79e5216dd27
children 3468a95b27cd
files usr/src/cmd/devfsadm/i386/misc_link_i386.c usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c usr/src/lib/libipmi/common/ipmi_bmc.c usr/src/lib/libipmi/common/libipmi.c usr/src/lib/libipmi/common/libipmi.h usr/src/man/man7d/Makefile usr/src/man/man7d/ipmi.7d usr/src/pkg/manifests/driver-ipmi.mf usr/src/pkg/manifests/system-header.mf usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/bmc_intf.h usr/src/uts/common/sys/ipmi.h usr/src/uts/intel/Makefile.files usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/Makefile.rules usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE.descrip usr/src/uts/intel/io/ipmi/ipmi.c usr/src/uts/intel/io/ipmi/ipmi.conf usr/src/uts/intel/io/ipmi/ipmi_kcs.c usr/src/uts/intel/io/ipmi/ipmi_main.c usr/src/uts/intel/io/ipmi/ipmivars.h usr/src/uts/intel/ipmi/Makefile
diffstat 25 files changed, 2238 insertions(+), 297 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/devfsadm/i386/misc_link_i386.c	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/cmd/devfsadm/i386/misc_link_i386.c	Thu Apr 26 15:49:44 2012 +0000
@@ -21,6 +21,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc.  All rights reserved.
  */
 
 #include <regex.h>
@@ -41,7 +42,7 @@
 static int diskette(di_minor_t minor, di_node_t node);
 static int vt00(di_minor_t minor, di_node_t node);
 static int kdmouse(di_minor_t minor, di_node_t node);
-static int bmc(di_minor_t minor, di_node_t node);
+static int ipmi(di_minor_t minor, di_node_t node);
 static int smbios(di_minor_t minor, di_node_t node);
 static int agp_process(di_minor_t minor, di_node_t node);
 static int drm_node(di_minor_t minor, di_node_t node);
@@ -62,8 +63,8 @@
 	{ "mouse", "ddi_mouse", "mouse8042",
 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse
 	},
-	{ "pseudo", "ddi_pseudo", "bmc",
-	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, bmc,
+	{ "pseudo", "ddi_pseudo", "ipmi",
+	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi,
 	},
 	{ "pseudo", "ddi_pseudo", "smbios",
 	    TYPE_EXACT | DRV_EXACT, ILEVEL_1, smbios,
@@ -350,9 +351,13 @@
 }
 
 static int
-bmc(di_minor_t minor, di_node_t node)
+ipmi(di_minor_t minor, di_node_t node)
 {
-	(void) devfsadm_mklink("bmc", node, minor, 0);
+	/*
+	 * Follow convention from other systems, and include an instance#,
+	 * even though there will only be one.
+	 */
+	(void) devfsadm_mklink("ipmi0", node, minor, 0);
 	return (DEVFSADM_CONTINUE);
 }
 
--- a/usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c	Thu Apr 26 15:49:44 2012 +0000
@@ -188,7 +188,7 @@
 	if ((ipmi_hdl = ipmi_open(&error, &msg, IPMI_TRANSPORT_BMC, NULL))
 	    == NULL) {
 		/*
-		 * If /dev/bmc doesn't exist on the system, then return
+		 * If /dev/ipmi0 doesn't exist on the system, then return
 		 * without doing anything.
 		 */
 		if (error != EIPMI_BMC_OPEN_FAILED)
--- a/usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c	Thu Apr 26 15:49:44 2012 +0000
@@ -22,13 +22,14 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc.  All rights reserved.
  */
 
 /*
- * /dev/bmc IPMI monitor
+ * /dev/ipmi IPMI monitor
  *
  * The purpose of this module is to monitor the connection between the system
- * and the service processor attached via /dev/bmc.  The module assumes the SP
+ * and the service processor attached via /dev/ipmi0.  The module assumes the SP
  * supports the Sun OEM uptime IPMI command.  If the BMC connection does not
  * exist, or the uptime function is not implemented, then the module unloads
  * without doing anything.
@@ -148,7 +149,7 @@
 	if ((smp->sm_hdl = ipmi_open(&error, &msg, IPMI_TRANSPORT_BMC, NULL))
 	    == NULL) {
 		/*
-		 * If /dev/bmc doesn't exist on the system, then unload the
+		 * If /dev/ipmi0 doesn't exist on the system, then unload the
 		 * module without doing anything.
 		 */
 		if (error != EIPMI_BMC_OPEN_FAILED)
--- a/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c	Thu Apr 26 15:49:44 2012 +0000
@@ -271,7 +271,7 @@
 		/*
 		 * The spec states that any values between 0x20 and 0x29 are
 		 * legitimate for "system software".  However, some versions of
-		 * Sun's ILOM rejects messages over /dev/bmc with a generator
+		 * Sun's ILOM rejects messages over /dev/ipmi0 with a generator
 		 * of 0x20, so we use 0x21 instead.
 		 */
 		pem.ipem_generator = 0x21;
--- a/usr/src/lib/libipmi/common/ipmi_bmc.c	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/lib/libipmi/common/ipmi_bmc.c	Thu Apr 26 15:49:44 2012 +0000
@@ -21,6 +21,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc.  All rights reserved.
  */
 
 #include <errno.h>
@@ -33,23 +34,23 @@
 #include <stropts.h>
 #include <unistd.h>
 
-#include <sys/bmc_intf.h>
+#include <sys/ipmi.h>
 
 #include "ipmi_impl.h"
 
 /*
- * IPMI transport for /dev/bmc
+ * IPMI transport for the local BMC at /dev/ipmi0.
  */
 
 typedef struct ipmi_bmc {
 	ipmi_handle_t	*ib_ihp;	/* ipmi handle */
-	int		ib_fd;		/* /dev/bmc filedescriptor */
+	int		ib_fd;		/* /dev/ipmi0 filedescriptor */
 	uint32_t	ib_msgseq;	/* message sequence number */
-	bmc_msg_t	*ib_msg;	/* message buffer */
+	uint8_t		*ib_msg;	/* message buffer */
 	size_t		ib_msglen;	/* size of message buffer */
 } ipmi_bmc_t;
 
-#define	BMC_DEV	"/dev/bmc"
+#define	BMC_DEV	"/dev/ipmi0"
 
 static void
 ipmi_bmc_close(void *data)
@@ -73,7 +74,7 @@
 		return (NULL);
 	ibp->ib_ihp = ihp;
 
-	/* open /dev/bmc */
+	/* open /dev/ipmi0 */
 	if ((ibp->ib_fd = open(BMC_DEV, O_RDWR)) < 0) {
 		ipmi_free(ihp, ibp);
 		(void) ipmi_set_error(ihp, EIPMI_BMC_OPEN_FAILED, "%s",
@@ -81,7 +82,7 @@
 		return (NULL);
 	}
 
-	if ((ibp->ib_msg = (bmc_msg_t *)ipmi_zalloc(ihp, BUFSIZ)) == NULL) {
+	if ((ibp->ib_msg = (uint8_t *)ipmi_zalloc(ihp, BUFSIZ)) == NULL) {
 		ipmi_bmc_close(ibp);
 		return (NULL);
 	}
@@ -95,84 +96,80 @@
     int *completion)
 {
 	ipmi_bmc_t *ibp = data;
-	struct strbuf sb;
-	int flags = 0;
-	size_t msgsz;
-	bmc_msg_t *msg;
-	bmc_req_t *bmcreq;
-	bmc_rsp_t *bmcrsp;
+	struct ipmi_req req;
+	struct ipmi_recv recv;
+	struct ipmi_addr addr;
+	fd_set rset;
+	struct ipmi_system_interface_addr bmc_addr;
 
-	/*
-	 * The length of the message structure is equal to the size of the
-	 * bmc_req_t structure, PLUS any additional data space in excess of
-	 * the data space already reserved in the data member + <n> for
-	 * the rest of the members in the bmc_msg_t structure.
-	 */
-	msgsz = offsetof(bmc_msg_t, msg) + sizeof (bmc_req_t) +
-	    ((cmd->ic_dlen > SEND_MAX_PAYLOAD_SIZE) ?
-	    (cmd->ic_dlen - SEND_MAX_PAYLOAD_SIZE) : 0);
+	bmc_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	bmc_addr.channel = IPMI_BMC_CHANNEL;
+	bmc_addr.lun = cmd->ic_lun;
 
-	/* construct and send the message */
-	if ((msg = ipmi_zalloc(ibp->ib_ihp, msgsz)) == NULL)
-		return (-1);
-	bmcreq = (bmc_req_t *)&msg->msg[0];
+	(void) memset(&req, 0, sizeof (struct ipmi_req));
+
+	req.addr = (unsigned char *) &bmc_addr;
+	req.addr_len = sizeof (bmc_addr);
 
-	msg->m_type = BMC_MSG_REQUEST;
-	msg->m_id = ibp->ib_msgseq++;
-	bmcreq->fn = cmd->ic_netfn;
-	bmcreq->lun = cmd->ic_lun;
-	bmcreq->cmd = cmd->ic_cmd;
-	bmcreq->datalength = cmd->ic_dlen;
-	(void) memcpy(bmcreq->data, cmd->ic_data, cmd->ic_dlen);
-	sb.len = msgsz;
-	sb.buf = (char *)msg;
+	req.msgid = ibp->ib_msgseq++;
+	req.msg.netfn = cmd->ic_netfn;
+	req.msg.cmd = cmd->ic_cmd;
+	req.msg.data = cmd->ic_data;
+	req.msg.data_len = cmd->ic_dlen;
 
-	if (putmsg(ibp->ib_fd, NULL, &sb, 0) < 0) {
-		ipmi_free(ibp->ib_ihp, msg);
+	if (ioctl(ibp->ib_fd, IPMICTL_SEND_COMMAND, &req) < 0) {
 		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_PUTMSG, "%s",
 		    strerror(errno));
 		return (-1);
 	}
 
-	ipmi_free(ibp->ib_ihp, msg);
+	/* get the response from the BMC */
+
+	FD_ZERO(&rset);
+	FD_SET(ibp->ib_fd, &rset);
 
-	/* get the response from the BMC */
-	sb.buf = (char *)ibp->ib_msg;
-	sb.maxlen = ibp->ib_msglen;
+	if (select(ibp->ib_fd + 1, &rset, NULL, NULL, NULL) < 0) {
+		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_GETMSG, "%s",
+		    strerror(errno));
+		return (-1);
+	}
+	if (FD_ISSET(ibp->ib_fd, &rset) == 0) {
+		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_GETMSG, "%s",
+		    "No data available");
+		return (-1);
+	}
 
-	if (getmsg(ibp->ib_fd, NULL, &sb, &flags) < 0) {
+	recv.addr = (unsigned char *) &addr;
+	recv.addr_len = sizeof (addr);
+	recv.msg.data = (unsigned char *)ibp->ib_msg;
+	recv.msg.data_len = ibp->ib_msglen;
+
+	/* get data */
+	if (ioctl(ibp->ib_fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv) < 0) {
 		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_GETMSG, "%s",
 		    strerror(errno));
 		return (-1);
 	}
 
-	switch (ibp->ib_msg->m_type) {
-	case BMC_MSG_RESPONSE:
-		bmcrsp = (bmc_rsp_t *)&ibp->ib_msg->msg[0];
+	if (recv.recv_type != IPMI_RESPONSE_RECV_TYPE) {
+		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_RESPONSE,
+		    "unknown BMC message type %d", recv.recv_type);
+		return (-1);
+	}
 
-		response->ic_netfn = bmcrsp->fn;
-		response->ic_lun = bmcrsp->lun;
-		response->ic_cmd = bmcrsp->cmd;
-		if (bmcrsp->ccode != 0) {
-			*completion = bmcrsp->ccode;
-			response->ic_dlen = 0;
-			response->ic_data = NULL;
-		} else {
-			*completion = 0;
-			response->ic_dlen = bmcrsp->datalength;
-			response->ic_data = bmcrsp->data;
-		}
-		break;
-
-	case BMC_MSG_ERROR:
-		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_RESPONSE, "%s",
-		    strerror(ibp->ib_msg->msg[0]));
-		return (-1);
-
-	default:
-		(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_RESPONSE,
-		    "unknown BMC message type %d", ibp->ib_msg->m_type);
-		return (-1);
+	response->ic_netfn = recv.msg.netfn;
+	/* The lun is not returned in addr, return the lun passed in */
+	response->ic_lun = cmd->ic_lun;
+	response->ic_cmd = recv.msg.cmd;
+	if (recv.msg.data[0] != 0) {
+		*completion = recv.msg.data[0];
+		response->ic_dlen = 0;
+		response->ic_data = NULL;
+	} else {
+		*completion = 0;
+		response->ic_dlen = (recv.msg.data_len > 0) ?
+		    recv.msg.data_len - 1 : 0;
+		response->ic_data = &(recv.msg.data[1]);
 	}
 
 	return (0);
--- a/usr/src/lib/libipmi/common/libipmi.c	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/lib/libipmi/common/libipmi.c	Thu Apr 26 15:49:44 2012 +0000
@@ -26,8 +26,6 @@
 #include <libipmi.h>
 #include <string.h>
 
-#include <sys/bmc_intf.h>
-
 #include "ipmi_impl.h"
 
 ipmi_handle_t *
@@ -119,7 +117,6 @@
 	{ 0xD5,			EIPMI_UNAVAILABLE },
 	{ 0xD6,			EIPMI_UNAVAILABLE },
 	{ 0xFF,			EIPMI_UNSPECIFIED },
-	{ BMC_IPMI_OEM_FAILURE_SENDBMC,	EIPMI_SEND_FAILED },
 };
 
 #define	IPMI_ERROR_COUNT \
--- a/usr/src/lib/libipmi/common/libipmi.h	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/lib/libipmi/common/libipmi.h	Thu Apr 26 15:49:44 2012 +0000
@@ -20,20 +20,20 @@
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 #ifndef	_LIBIPMI_H
 #define	_LIBIPMI_H
 
-#include <sys/bmc_intf.h>
 #include <sys/byteorder.h>
 #include <sys/nvpair.h>
 #include <sys/sysmacros.h>
 
 /*
  * Private interfaces for communicating with attached services over IPMI.  This
- * library is designed for system software communicating with Sun-supported
- * service processors over /dev/bmc.  It is not a generic IPMI library.
+ * library is designed for system software communicating with Illumos-supported
+ * service processors over /dev/ipmi0.  It is not a generic IPMI library.
  *
  * Documentation references refer to "Intelligent Platform Management Interface
  * Specification Second Generation v2.0", document revision 1.0 with Februrary
@@ -52,10 +52,12 @@
 /*
  * Basic netfn definitions.  See section 5.1.
  */
-#define	IPMI_NETFN_CHASSIS		BMC_NETFN_CHASSIS
-#define	IPMI_NETFN_APP			BMC_NETFN_APP
-#define	IPMI_NETFN_STORAGE		BMC_NETFN_STORAGE
-#define	IPMI_NETFN_SE			BMC_NETFN_SE
+#define	IPMI_NETFN_CHASSIS		0x0
+#define	IPMI_NETFN_BRIDGE		0x2
+#define	IPMI_NETFN_SE			0x4
+#define	IPMI_NETFN_APP			0x6
+#define	IPMI_NETFN_FIRMWARE		0x8
+#define	IPMI_NETFN_STORAGE		0xa
 #define	IPMI_NETFN_TRANSPORT		0x0C
 #define	IPMI_NETFN_OEM			0x2e
 
@@ -66,10 +68,10 @@
 
 typedef enum {
 	EIPMI_NOMEM = EIPMI_BASE,	/* memory allocation failure */
-	EIPMI_BMC_OPEN_FAILED,		/* failed to open /dev/bmc */
-	EIPMI_BMC_PUTMSG,		/* failed to send message to /dev/bmc */
-	EIPMI_BMC_GETMSG,	/* failed to read response from /dev/bmc */
-	EIPMI_BMC_RESPONSE,		/* response from /dev/bmc failed */
+	EIPMI_BMC_OPEN_FAILED,		/* failed to open /dev/ipmi0 */
+	EIPMI_BMC_PUTMSG,	/* failed to send message to /dev/ipmi0 */
+	EIPMI_BMC_GETMSG,	/* failed to read response from /dev/ipmi0 */
+	EIPMI_BMC_RESPONSE,		/* response from /dev/ipmi0 failed */
 	EIPMI_INVALID_COMMAND,		/* invalid command */
 	EIPMI_COMMAND_TIMEOUT,		/* command timeout */
 	EIPMI_DATA_LENGTH_EXCEEDED,	/* maximum data length exceeded */
--- a/usr/src/man/man7d/Makefile	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/man/man7d/Makefile	Thu Apr 26 15:49:44 2012 +0000
@@ -209,6 +209,7 @@
 		ecpp.7d		\
 		heci.7d		\
 		i915.7d		\
+		ipmi.7d		\
 		ipw.7d		\
 		iwh.7d		\
 		iwi.7d		\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/man/man7d/ipmi.7d	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,180 @@
+'\"
+.\" CDDL HEADER START
+.\"
+.\" The contents of this file are subject to the terms of the
+.\" Common Development and Distribution License (the "License").
+.\" You may not use this file except in compliance with the License.
+.\"
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+.\" or http://www.opensolaris.org/os/licensing.
+.\" See the License for the specific language governing permissions
+.\" and limitations under the License.
+.\"
+.\" When distributing Covered Code, include this CDDL HEADER in each
+.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+.\" If applicable, add the following below this CDDL HEADER, with the
+.\" fields enclosed by brackets "[]" replaced with your own identifying
+.\" information: Portions Copyright [yyyy] [name of copyright owner]
+.\"
+.\" CDDL HEADER END
+.\"
+.\" Copyright (c) 2012, Joyent, Inc. All Rights Reserved
+.\"
+.TH IMPI 7D "Apr 21, 2012"
+.SH NAME
+ipmi \- OpenIPMI compatible IPMI interface driver
+.SH SYNOPSIS
+.LP
+.nf
+\fB/dev/ipmi0\fR
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBipmi\fR device is a character special file that provides access to the
+Intelligent Platform Management Interface for the system.  For more
+information on \fBIPMI\fR and to obtain a copy of the \fBIPMI\fR
+specification and implementation guidelines, refer to
+http://\fIhttp://www.intel.com/design/servers/ipmi/\fR.
+The driver is adapted from the FreeBSD driver which is in turn adapted from
+the Linux driver, however, not all features described in the standard are
+supported. The current implementation depends on the \fBsmbios\fR(7d) to
+discover the existence of an IPMI device.
+.sp
+.LP
+
+.SH IOCTLS
+.sp
+.LP
+Sending and receiving messages through the IPMI drivers requires the use of
+\fBioctl\fR(2).
+
+The ioctl command codes below are defined in \fBsys/ipmi.h\fR.
+The third argument to ioctl should be a pointer to the type indicated.
+Currently the following ioctls are supported:
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_RECEIVE_MSG "struct ipmi_recv"
+.br
+Receive a message.
+.br
+Possible error values:
+.RS +8
+EAGAIN No messages are in the process queue.
+.br
+EFAULT An address supplied was invalid.
+.br
+EMSGSIZE The address could not fit in the message buffer and
+will remain in the buffer.
+.RE
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_RECEIVE_MSG_TRUNC "struct ipmi_recv"
+.br
+Like IPMICTL_RECEIVE_MSG but if the message cannot fit into the buffer, it
+will truncate the contents instead of leaving the data in the buffer.
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_SEND_COMMAND "struct ipmi_req"
+.br
+Send a message to the interface.
+.br
+Possible error values:
+.RS +8
+EFAULT An address supplied was invalid
+.br
+ENOMEM Buffers could not be allowed for the command, out of memory.
+.RE
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_SET_MY_ADDRESS_CMD "unsigned int"
+.br
+Set the slave address for source messages.
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_GET_MY_ADDRESS_CMD "unsigned int"
+.br
+Get the slave address for source messages.
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_SET_MY_LUN_CMD "unsigned int"
+.br
+Set the slave LUN for source messages.
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_GET_MY_LUN_CMD "unsigned int"
+.br
+Get the slave LUN for source messages.
+.RE
+
+Stub Only Ioctl
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_SET_GETS_EVENTS_CMD int
+.br
+Set whether this interface receives events.
+.RE
+
+Unimplemented Ioctls
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_REGISTER_FOR_CMD
+.br
+Register to receive a specific command
+.RE
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+IPMICTL_UNREGISTER_FOR_CMD
+.br
+Unregister to receive a specific command
+.RE
+
+.SH SEE ALSO
+.sp
+.LP
+\fBipmitool\fR(1M), \fBioctl\fR(2), \fBsmbios\fR(7d)
+.sp
+.LP
+\fIIntelligent Platform Management Interface Specification Second
+Generation\fR, v2.0 \(em
+June 12, 2009 Markup
+.SH NOTES
+.sp
+.LP
+Not all systems include an \fBIPMI\fR.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/driver-ipmi.mf	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+<include global_zone_only_component>
+set name=pkg.fmri value=pkg:/driver/ipmi@$(PKGVERS)
+set name=pkg.summary \
+    value="Intelligent Platform Management Interface baseboard driver"
+set name=info.classification value=org.opensolaris.category.2008:Drivers/Ports
+set name=variant.arch value=i386
+dir path=usr/include
+dir path=usr/include/sys
+dir path=usr/include/sys
+dir path=usr/kernel group=sys
+dir path=usr/kernel/drv group=sys
+dir path=usr/kernel/drv/$(ARCH64) group=sys
+dir path=usr/share
+dir path=usr/share/man
+dir path=usr/share/man/man7d
+driver name=ipmi perms="* 0600 root sys"
+file path=usr/include/sys/ipmi.h
+file path=usr/kernel/drv/amd64/ipmi
+file path=usr/kernel/drv/ipmi
+file path=usr/kernel/drv/ipmi.conf
+file path=usr/share/man/man7d/ipmi.7d
+license lic_CDDL license=lic_CDDL
+license usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE \
+    license=usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE
--- a/usr/src/pkg/manifests/system-header.mf	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/pkg/manifests/system-header.mf	Thu Apr 26 15:49:44 2012 +0000
@@ -813,7 +813,6 @@
 file path=usr/include/sys/bitset.h
 file path=usr/include/sys/bl.h
 file path=usr/include/sys/blkdev.h
-file path=usr/include/sys/bmc_intf.h
 file path=usr/include/sys/bofi.h
 file path=usr/include/sys/bofi_impl.h
 file path=usr/include/sys/bootconf.h
--- a/usr/src/uts/common/sys/Makefile	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/uts/common/sys/Makefile	Thu Apr 26 15:49:44 2012 +0000
@@ -86,7 +86,6 @@
 	bitset.h		\
 	bl.h			\
 	blkdev.h		\
-	bmc_intf.h		\
 	bofi.h			\
 	bofi_impl.h		\
 	bpp_io.h		\
@@ -278,6 +277,7 @@
 	ipc.h			\
 	ipc_impl.h		\
 	ipc_rctl.h		\
+	ipmi.h			\
 	isa_defs.h		\
 	iscsi_authclient.h	\
 	iscsi_authclientglue.h	\
--- a/usr/src/uts/common/sys/bmc_intf.h	Thu Sep 27 16:04:13 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,203 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _BMC_INTF_H
-#define	_BMC_INTF_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <sys/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define	BMC_SUCCESS		0x0
-#define	BMC_FAILURE		0x1
-
-#define	BMC_NETFN_CHASSIS		0x0
-#define	BMC_NETFN_BRIDGE		0x2
-#define	BMC_NETFN_SE			0x4
-#define	BMC_NETFN_APP			0x6
-#define	BMC_NETFN_FIRMWARE		0x8
-#define	BMC_NETFN_STORAGE		0xa
-#define	BMC_NETFN_TRANSPORT		0xc
-
-#define	SEND_MAX_PAYLOAD_SIZE		34	/* MAX payload */
-#define	RECV_MAX_PAYLOAD_SIZE		33	/* MAX payload */
-#define	BMC_MIN_RESPONSE_SIZE		3
-#define	BMC_MIN_REQUEST_SIZE		2
-#define	BMC_MAX_RESPONSE_SIZE   (BMC_MIN_RESPONSE_SIZE + RECV_MAX_PAYLOAD_SIZE)
-#define	BMC_MAX_REQUEST_SIZE	(BMC_MIN_REQUEST_SIZE + BMC_MAX_RESPONSE_SIZE)
-
-#define	BUF_SIZE 256
-#define	MAX_BUF_SIZE			256
-
-/*
- * Useful macros
- */
-#define	FORM_NETFNLUN(net, lun)	((((net) << 2) | ((lun) & 0x3)))
-#define	GET_NETFN(netfn)	(((netfn) >> 2) & 0x3f)
-#define	GET_LUN(netfn)		(netfn & 0x3)
-#define	RESP_NETFN(nflun)	((nflun) | 1)
-#define	ISREQUEST(nl)		(((nl) & 1) == 0)	/* test for request */
-#define	ISRESPONSE(nl)		(((nl) & 1) == 1)	/* test for response */
-
-
-/* for checking BMC specific stuff */
-#define	BMC_GET_DEVICE_ID		0x1	/* GET DEVICE ID COMMAND */
-#define	BMC_IPMI_15_VER		0x51	/* IPMI 1.5 definion */
-
-/* BMC Completion Code and OEM Completion Code */
-#define	BMC_IPMI_UNSPECIFIC_ERROR	0xFF	/* Unspecific Error */
-#define	BMC_IPMI_INVALID_COMMAND	0xC1	/* Invalid Command */
-#define	BMC_IPMI_COMMAND_TIMEOUT	0xC3	/* Command Timeout */
-#define	BMC_IPMI_DATA_LENGTH_EXCEED	0xC8	/* DataLength exceeded limit */
-#define	BMC_IPMI_OEM_FAILURE_SENDBMC	0x7E	/* Cannot send BMC req */
-
-
-#define	IOCTL_IPMI_KCS_ACTION		0x01
-#define	IOCTL_IPMI_INTERFACE_METHOD	0x02
-
-/* Interface methods returned from IOCTL_IPMI_INTERFACE_METHOD ioctl: */
-
-#define	BMC_IOCTL_METHOD		0	/* Not returned from ioctl, */
-						/* but can be used by	*/
-						/* applications that want to */
-						/* compare against an	*/
-						/* alternative method.	*/
-#define	BMC_PUTMSG_METHOD		1
-
-/*
- * bmc_req_t is the data structure to send
- * request packet from applications to the driver
- * module.
- *
- * the request pkt is mainly for KCS-interface-BMC
- * messages. Since the system interface is session-less
- * connections, the packet won't have any session
- * information.
- *
- * the data payload will be 2 bytes less than max
- * BMC supported packet size.
- * the address of the responder is always BMC and so
- * rsSa field is not required.
- */
-typedef struct bmc_req {
-	uint8_t fn;			/* netFn for command */
-	uint8_t lun;			/* logical unit on responder */
-	uint8_t cmd;			/* command */
-	uint8_t datalength;		/* length of following data */
-	uint8_t data[SEND_MAX_PAYLOAD_SIZE]; /* request data */
-} bmc_req_t;
-
-/*
- * bmc_rsp_t is the data structure to send
- * respond packet from applications to the driver
- * module.
- *
- * the respond pkt is mainly for KCS-interface-BMC
- * messages. Since the system interface is session-less
- * connections, the packet won't have any session
- * information.
- *
- * the data payload will be 2 bytes less than max
- * BMC supported packet size.
- */
-typedef struct bmc_rsp {
-	uint8_t	fn;			/* netFn for command */
-	uint8_t	lun;			/* logical unit on responder */
-	uint8_t	cmd;			/* command */
-	uint8_t	ccode;			/* completion code */
-	uint8_t	datalength;		/* Length */
-	uint8_t	data[RECV_MAX_PAYLOAD_SIZE]; /* response */
-} bmc_rsp_t;
-
-/*
- * the data structure for synchronous operation via ioctl (DEPRECATED)
- */
-typedef struct bmc_reqrsp {
-	bmc_req_t	req;			/* request half */
-	bmc_rsp_t	rsp;			/* response half */
-} bmc_reqrsp_t;
-
-
-/*
- * The new way of communicating with the bmc driver is to use putmsg() to
- * send a message of a particular type.  Replies from the driver also have this
- * form, and will require the user to process the type field before examining
- * the rest of the reply.
- *
- * The only change that must be observed when using the request and response
- * structures defined above is as follows:
- * when sending messages to the bmc driver, the data portion is now variable
- * (the caller must allocate enough space to store the all structure members,
- * plus enough space to cover the amount of data in the request), e.g.:
- *
- * bmc_msg_t *msg = malloc(offsetof(bmc_msg_t, msg) + sizeof(bmc_req_t) + 10);
- *
- * The amount allocated for the message is (# of bytes before the msg field) +
- * the size of a bmc_req_t (which includes SEND_MAX_PAYLOAD_SIZE
- * bytes in the data field), plus an additional 10 bytes for the data
- * field (so the data field would occupy (SEND_MAX_PAYLOAD_SIZE + 10)
- * bytes).  The datalength member must reflect the amount of data in the
- * request's data field (as was required when using the ioctl interface).
- */
-typedef struct bmc_msg {
-	uint8_t		m_type;		/* Message type (see below) */
-	uint32_t	m_id;		/* Message ID */
-	uint8_t		reserved[32];
-	uint8_t		msg[1];		/* Variable length message data */
-} bmc_msg_t;
-
-
-/*
- * An error response passed back from the bmc driver will have its m_id
- * field set to BMC_UNKNOWN_MSG_ID if a message is sent to it that is not
- * at least as large as a bmc_msg_t.
- */
-#define	BMC_UNKNOWN_MSG_ID	~((uint32_t)0)
-
-
-/*
- * Possible values for the m_type field in bmc_msg_t:
- */
-#define	BMC_MSG_REQUEST		1	/* BMC request (as above, sent to the */
-					/* driver by the user), bmc_msg.msg */
-					/* begins with the bmc_req_t	*/
-					/* structure.			*/
-#define	BMC_MSG_RESPONSE	2	/* BMC response (sent by the driver) */
-					/* bmc_msg.msg begins with the	*/
-					/* bmc_rsp_t structure.		*/
-#define	BMC_MSG_ERROR		3	/* Error while processing a user msg */
-					/* msg[0] is the error code	*/
-					/* (interpret as an errno value) */
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif /* _BMC_INTF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/ipmi.h	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/sys/ipmi.h,v 1.2 2006/09/22 22:11:29 jhb Exp $
+ */
+
+/*
+ * Copyright 2012 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_IPMI_H_
+#define	_SYS_IPMI_H_
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	IPMI_MAX_ADDR_SIZE		0x20
+#define	IPMI_MAX_RX			1024
+#define	IPMI_BMC_SLAVE_ADDR		0x20 /* Default slave address */
+#define	IPMI_BMC_CHANNEL		0x0f /* BMC channel */
+
+#define	IPMI_BMC_SMS_LUN		0x02
+
+#define	IPMI_SYSTEM_INTERFACE_ADDR_TYPE	0x0c
+#define	IPMI_IPMB_ADDR_TYPE		0x01
+#define	IPMI_IPMB_BROADCAST_ADDR_TYPE	0x41
+
+#define	IPMI_IOC_MAGIC			'i'
+#define	IPMICTL_RECEIVE_MSG_TRUNC	\
+				_IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv)
+#define	IPMICTL_RECEIVE_MSG		\
+				_IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv)
+#define	IPMICTL_SEND_COMMAND		\
+				_IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req)
+#define	IPMICTL_REGISTER_FOR_CMD	\
+				_IOW(IPMI_IOC_MAGIC, 14, struct ipmi_cmdspec)
+#define	IPMICTL_UNREGISTER_FOR_CMD	\
+				_IOW(IPMI_IOC_MAGIC, 15, struct ipmi_cmdspec)
+#define	IPMICTL_SET_GETS_EVENTS_CMD	_IOW(IPMI_IOC_MAGIC, 16, int)
+#define	IPMICTL_SET_MY_ADDRESS_CMD	_IOW(IPMI_IOC_MAGIC, 17, unsigned int)
+#define	IPMICTL_GET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 18, unsigned int)
+#define	IPMICTL_SET_MY_LUN_CMD		_IOW(IPMI_IOC_MAGIC, 19, unsigned int)
+#define	IPMICTL_GET_MY_LUN_CMD		_IOR(IPMI_IOC_MAGIC, 20, unsigned int)
+
+#define	IPMI_RESPONSE_RECV_TYPE		1
+#define	IPMI_ASYNC_EVENT_RECV_TYPE	2
+#define	IPMI_CMD_RECV_TYPE		3
+
+#define	IPMI_APP_REQUEST		0x06
+#define	IPMI_GET_DEVICE_ID		0x01
+#define	IPMI_CLEAR_FLAGS		0x30
+#define	IPMI_GET_MSG_FLAGS		0x31
+#define	IPMI_MSG_AVAILABLE		0x01
+#define	IPMI_MSG_BUFFER_FULL		0x02
+#define	IPMI_WDT_PRE_TIMEOUT		0x08
+#define	IPMI_GET_MSG			0x33
+#define	IPMI_SEND_MSG			0x34
+#define	IPMI_GET_CHANNEL_INFO		0x42
+#define	IPMI_RESET_WDOG			0x22
+#define	IPMI_SET_WDOG			0x24
+#define	IPMI_GET_WDOG			0x25
+
+#define	IPMI_SET_WD_TIMER_SMS_OS	0x04
+#define	IPMI_SET_WD_TIMER_DONT_STOP	0x40
+#define	IPMI_SET_WD_ACTION_RESET	0x01
+
+struct ipmi_msg {
+	unsigned char	netfn;
+	unsigned char	cmd;
+	unsigned short	data_len;
+	unsigned char	*data;
+};
+
+struct ipmi_req {
+	unsigned char	*addr;
+	unsigned int	addr_len;
+	long		msgid;
+	struct ipmi_msg	msg;
+};
+
+struct ipmi_recv {
+	int		recv_type;
+	unsigned char	*addr;
+	unsigned int	addr_len;
+	long		msgid;
+	struct ipmi_msg	msg;
+};
+
+struct ipmi_cmdspec {
+	unsigned char	netfn;
+	unsigned char	cmd;
+};
+
+struct ipmi_addr {
+	int		addr_type;
+	short		channel;
+	unsigned char	data[IPMI_MAX_ADDR_SIZE];
+};
+
+struct ipmi_system_interface_addr {
+	int		addr_type;
+	short		channel;
+	unsigned char	lun;
+};
+
+struct ipmi_ipmb_addr {
+	int		addr_type;
+	short		channel;
+	unsigned char	slave_addr;
+	unsigned char	lun;
+};
+
+#ifdef _KERNEL
+
+#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 /* _KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_IPMI_H_ */
--- a/usr/src/uts/intel/Makefile.files	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/uts/intel/Makefile.files	Thu Apr 26 15:49:44 2012 +0000
@@ -21,6 +21,7 @@
 
 #
 # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, Joyent, Inc. All rights reserved.
 #
 
 #
@@ -248,6 +249,10 @@
 AMR_OBJS = amr.o
 
 #
+#	IPMI module
+IPMI_OBJS +=	ipmi_main.o ipmi.o ipmi_kcs.o
+
+#
 #	IOMMULIB module
 #
 IOMMULIB_OBJS = iommulib.o
--- a/usr/src/uts/intel/Makefile.intel.shared	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/uts/intel/Makefile.intel.shared	Thu Apr 26 15:49:44 2012 +0000
@@ -400,6 +400,7 @@
 DRV_KMODS	+= sfe
 DRV_KMODS	+= amd8111s
 DRV_KMODS	+= igb
+DRV_KMODS	+= ipmi
 DRV_KMODS	+= iprb
 DRV_KMODS	+= ixgbe
 DRV_KMODS	+= vr
--- a/usr/src/uts/intel/Makefile.rules	Thu Sep 27 16:04:13 2012 -0400
+++ b/usr/src/uts/intel/Makefile.rules	Thu Apr 26 15:49:44 2012 +0000
@@ -21,6 +21,7 @@
 #
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2012 Joyent, Inc.  All rights reserved.
 #
 
 #
@@ -168,6 +169,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/intel/io/ipmi/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/intel/io/intel_nb5000/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -413,6 +418,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/intel/io/intel_nhm/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/intel/io/ipmi/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(SRC)/common/mc/mc-amd/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,23 @@
+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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE.descrip	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,1 @@
+INTELLIGENT PLATFORM MANAGEMENT INTERFACE SUPPORT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/ipmi.c	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.16 2011/11/07 15:43:11 ed Exp $ */
+
+/*
+ * Copyright 2012, Joyent, Inc.  All rights reserved.
+ */
+
+#include <sys/devops.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/smbios.h>
+#include <sys/smbios_impl.h>
+#include <sys/ipmi.h>
+#include "ipmivars.h"
+
+static kmutex_t		slpmutex;
+static kcondvar_t	slplock;
+
+/*
+ * Request management.
+ */
+
+/* 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 = kmem_zalloc(sizeof (struct ipmi_request) + requestlen + replylen,
+	    KM_SLEEP);
+	req->ir_sz = sizeof (struct ipmi_request) + requestlen + replylen;
+	req->ir_owner = dev;
+	req->ir_msgid = msgid;
+	req->ir_addr = addr;
+	req->ir_command = command;
+	if (requestlen) {
+		req->ir_request = (uchar_t *)&req[1];
+		req->ir_requestlen = requestlen;
+	}
+	if (replylen) {
+		req->ir_reply = (uchar_t *)&req[1] + requestlen;
+		req->ir_replybuflen = replylen;
+	}
+	return (req);
+}
+
+/* Free a request no longer in use. */
+void
+ipmi_free_request(struct ipmi_request *req)
+{
+	kmem_free(req, req->ir_sz);
+}
+
+/* Store a processed request on the appropriate completion queue. */
+/*ARGSUSED*/
+void
+ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+	struct ipmi_device *dev;
+
+	IPMI_LOCK_ASSERT(sc);
+
+	/*
+	 * Anonymous requests (from inside the driver) always have a
+	 * waiter that we awaken.
+	 */
+	if (req->ir_owner == NULL) {
+		mutex_enter(&slpmutex);
+		cv_signal(&slplock);
+		mutex_exit(&slpmutex);
+	} else {
+		dev = req->ir_owner;
+		TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link);
+		pollwakeup(dev->ipmi_pollhead, POLLIN | POLLRDNORM);
+	}
+}
+
+/*
+ * Enqueue an internal driver request and wait until it is completed.
+ */
+static int
+ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req,
+    int timo)
+{
+	int error;
+
+	IPMI_LOCK(sc);
+	error = sc->ipmi_enqueue_request(sc, req);
+	if (error == 0) {
+		/* Wait for result - see ipmi_complete_request */
+		IPMI_UNLOCK(sc);
+		mutex_enter(&slpmutex);
+		if (timo == 0)
+			cv_wait(&slplock, &slpmutex);
+		else
+			error = cv_timedwait(&slplock, &slpmutex,
+			    ddi_get_lbolt() + timo);
+		mutex_exit(&slpmutex);
+		IPMI_LOCK(sc);
+		if (error == -1)
+			error = EWOULDBLOCK;
+		else
+			error = req->ir_error;
+	}
+	IPMI_UNLOCK(sc);
+
+	return (error);
+}
+
+/*
+ * 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;
+
+	IPMI_LOCK_ASSERT(sc);
+
+	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);
+
+	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
+	TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
+	return (req);
+}
+
+int
+ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+
+	IPMI_LOCK_ASSERT(sc);
+
+	TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link);
+	cv_signal(&sc->ipmi_request_added);
+	return (0);
+}
+
+void
+ipmi_startup(struct ipmi_softc *sc)
+{
+	struct ipmi_request *req;
+	int error, i;
+
+	mutex_init(&slpmutex, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&slplock, NULL, CV_DEFAULT, NULL);
+
+	/* Initialize interface-independent state. */
+	mutex_init(&sc->ipmi_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&sc->ipmi_request_added, NULL, CV_DEFAULT, NULL);
+	TAILQ_INIT(&sc->ipmi_pending_requests);
+
+	/* Initialize interface-dependent state. */
+	error = sc->ipmi_startup(sc);
+	if (error) {
+		cmn_err(CE_WARN, "Failed to initialize interface: %d", 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) {
+		cmn_err(CE_WARN, "Timed out waiting for GET_DEVICE_ID");
+		ipmi_free_request(req);
+		return;
+	} else if (error) {
+		cmn_err(CE_WARN, "Failed GET_DEVICE_ID: %d", error);
+		ipmi_free_request(req);
+		return;
+	} else if (req->ir_compcode != 0) {
+		cmn_err(CE_WARN,
+		    "Bad completion code for GET_DEVICE_ID: %d",
+		    req->ir_compcode);
+		ipmi_free_request(req);
+		return;
+	} else if (req->ir_replylen < 5) {
+		cmn_err(CE_WARN, "Short reply for GET_DEVICE_ID: %d",
+		    req->ir_replylen);
+		ipmi_free_request(req);
+		return;
+	}
+
+	cmn_err(CE_CONT, "!device rev. %d, firmware rev. %d.%d%d, "
+	    "version %d.%d",
+	    req->ir_reply[1] & 0x0f, req->ir_reply[2] & 0x7f,
+	    req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f,
+	    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);
+
+	if ((error = ipmi_submit_driver_request(sc, req, 0)) != 0)
+		cmn_err(CE_WARN, "Failed to clear IPMI flags: %d\n", error);
+
+	/* Magic numbers */
+	if (req->ir_compcode == 0xc0) {
+		cmn_err(CE_NOTE, "!Clear flags is busy");
+	}
+	if (req->ir_compcode == 0xc1) {
+		cmn_err(CE_NOTE, "!Clear flags illegal");
+	}
+	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] = (uchar_t)i;
+
+		if (ipmi_submit_driver_request(sc, req, 0) != 0) {
+			ipmi_free_request(req);
+			break;
+		}
+
+		if (req->ir_compcode != 0) {
+			ipmi_free_request(req);
+			break;
+		}
+		ipmi_free_request(req);
+	}
+	cmn_err(CE_CONT, "!number of channels %d", i);
+
+	/* probe for watchdog */
+	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
+	    IPMI_GET_WDOG, 0, 0);
+
+	if ((error = ipmi_submit_driver_request(sc, req, 0)) != 0) {
+		cmn_err(CE_WARN, "Failed to check IPMI watchdog: %d\n", error);
+		ipmi_free_request(req);
+		return;
+	}
+
+	if (req->ir_compcode == 0x00) {
+		cmn_err(CE_CONT, "!watchdog supported");
+
+		/*
+		 * Here is where we could register a watchdog event handler.
+		 * See ipmi_wd_event() in the FreeBSD code.
+		 */
+	}
+	ipmi_free_request(req);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/ipmi.conf	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,25 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012, Joyent, Inc.  All rights reserved.
+#
+name="ipmi" parent="pseudo" instance=0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/ipmi_kcs.c	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $FreeBSD: src/sys/dev/ipmi/ipmi_kcs.c,v 1.3 2008/08/28 02:11:04 jhb */
+
+/*
+ * Copyright 2012, Joyent, Inc.  All rights reserved.
+ */
+
+#include <sys/param.h>
+#include <sys/disp.h>
+#include <sys/systm.h>
+#include <sys/condvar.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <sys/ipmi.h>
+#include "ipmivars.h"
+
+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);
+
+#define	RETRY_USECS	100
+static clock_t timeout_usecs;
+
+static int
+kcs_wait_for_ibf(struct ipmi_softc *sc, int state)
+{
+	int status;
+	clock_t i;
+
+	status = INB(sc, KCS_CTL_STS);
+	if (state == 0) {
+		/* WAIT FOR IBF = 0 */
+		for (i = 0; i < timeout_usecs && status & KCS_STATUS_IBF;
+		    i += RETRY_USECS) {
+			drv_usecwait(RETRY_USECS);
+			status = INB(sc, KCS_CTL_STS);
+		}
+	} else {
+		/* WAIT FOR IBF = 1 */
+		for (i = 0; i < timeout_usecs && !(status & KCS_STATUS_IBF);
+		    i += RETRY_USECS) {
+			drv_usecwait(RETRY_USECS);
+			status = INB(sc, KCS_CTL_STS);
+		}
+	}
+	return (status);
+}
+
+static int
+kcs_wait_for_obf(struct ipmi_softc *sc, int state)
+{
+	int status;
+	clock_t i;
+
+	status = INB(sc, KCS_CTL_STS);
+	if (state == 0) {
+		/* WAIT FOR OBF = 0 */
+		for (i = 0; i < timeout_usecs && status & KCS_STATUS_OBF;
+		    i += RETRY_USECS) {
+			drv_usecwait(RETRY_USECS);
+			status = INB(sc, KCS_CTL_STS);
+		}
+	} else {
+		/* WAIT FOR OBF = 1 */
+		for (i = 0; i < timeout_usecs && !(status & KCS_STATUS_OBF);
+		    i += RETRY_USECS) {
+			drv_usecwait(RETRY_USECS);
+			status = INB(sc, KCS_CTL_STS);
+		}
+	}
+	return (status);
+}
+
+static void
+kcs_clear_obf(struct ipmi_softc *sc, int status)
+{
+	/* Clear OBF */
+	if (status & KCS_STATUS_OBF) {
+		(void) INB(sc, KCS_DATA);
+	}
+}
+
+static void
+kcs_error(struct ipmi_softc *sc)
+{
+	int retry, status;
+	uchar_t 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)
+				cmn_err(CE_WARN,
+				    "KCS Error Data %02x", 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)
+				cmn_err(CE_WARN, "KCS error: %02x", 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;
+		}
+	}
+	cmn_err(CE_WARN, "KCS: Error retry exhausted");
+}
+
+/*
+ * 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, uchar_t 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, uchar_t 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, uchar_t *data)
+{
+	int status;
+
+	/* 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 */
+		(void) 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)
+{
+	uchar_t *cp, data;
+	int i, state;
+
+	/* Send the request. */
+	if (!kcs_start_write(sc)) {
+		cmn_err(CE_WARN, "KCS: Failed to start write");
+		goto fail;
+	}
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: WRITE_START... ok");
+#endif
+
+	if (!kcs_write_byte(sc, req->ir_addr)) {
+		cmn_err(CE_WARN, "KCS: Failed to write address");
+		goto fail;
+	}
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: Wrote address: %02x", req->ir_addr);
+#endif
+
+	if (req->ir_requestlen == 0) {
+		if (!kcs_write_last_byte(sc, req->ir_command)) {
+			cmn_err(CE_WARN,
+			    "KCS: Failed to write command");
+			goto fail;
+		}
+#ifdef KCS_DEBUG
+		cmn_err(CE_NOTE, "KCS: Wrote command: %02x",
+		    req->ir_command);
+#endif
+	} else {
+		if (!kcs_write_byte(sc, req->ir_command)) {
+			cmn_err(CE_WARN,
+			    "KCS: Failed to write command");
+			goto fail;
+		}
+#ifdef KCS_DEBUG
+		cmn_err(CE_NOTE, "KCS: Wrote command: %02x",
+		    req->ir_command);
+#endif
+
+		cp = req->ir_request;
+		for (i = 0; i < req->ir_requestlen - 1; i++) {
+			if (!kcs_write_byte(sc, *cp++)) {
+				cmn_err(CE_WARN,
+				    "KCS: Failed to write data byte %d",
+				    i + 1);
+				goto fail;
+			}
+#ifdef KCS_DEBUG
+			cmn_err(CE_NOTE, "KCS: Wrote data: %02x",
+			    cp[-1]);
+#endif
+		}
+
+		if (!kcs_write_last_byte(sc, *cp)) {
+			cmn_err(CE_WARN,
+			    "KCS: Failed to write last dta byte");
+			goto fail;
+		}
+#ifdef KCS_DEBUG
+		cmn_err(CE_NOTE, "KCS: Wrote last data: %02x",
+		    *cp);
+#endif
+	}
+
+	/* Read the reply.  First, read the NetFn/LUN. */
+	if (kcs_read_byte(sc, &data) != 1) {
+		cmn_err(CE_WARN, "KCS: Failed to read address");
+		goto fail;
+	}
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: Read address: %02x", data);
+#endif
+	if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
+		cmn_err(CE_WARN, "KCS: Reply address mismatch");
+		goto fail;
+	}
+
+	/* Next we read the command. */
+	if (kcs_read_byte(sc, &data) != 1) {
+		cmn_err(CE_WARN, "KCS: Failed to read command");
+		goto fail;
+	}
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: Read command: %02x", data);
+#endif
+	if (data != req->ir_command) {
+		cmn_err(CE_WARN, "KCS: Command mismatch");
+		goto fail;
+	}
+
+	/* Next we read the completion code. */
+	if (kcs_read_byte(sc, &req->ir_compcode) != 1) {
+		cmn_err(CE_WARN, "KCS: Failed to read completion code");
+		goto fail;
+	}
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: Read completion code: %02x",
+	    req->ir_compcode);
+#endif
+
+	/* Finally, read the reply from the BMC. */
+	i = 0;
+	for (;;) {
+		state = kcs_read_byte(sc, &data);
+		if (state == 0) {
+			cmn_err(CE_WARN,
+			    "KCS: Read failed on byte %d", i + 1);
+			goto fail;
+		}
+		if (state == 2)
+			break;
+		if (i < req->ir_replybuflen) {
+			req->ir_reply[i] = data;
+#ifdef KCS_DEBUG
+			cmn_err(CE_NOTE, "KCS: Read data %02x",
+			    data);
+		} else {
+			cmn_err(CE_WARN,
+			    "KCS: Read short %02x byte %d", data, i + 1);
+#endif
+		}
+		i++;
+	}
+	req->ir_replylen = i;
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: READ finished (%d bytes)", i);
+	if (req->ir_replybuflen < i)
+#else
+	if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
+#endif
+		cmn_err(CE_WARN, "KCS: Read short: %d buffer, %d actual",
+		    (int)(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);
+}
+
+static int
+kcs_startup(struct ipmi_softc *sc)
+{
+	sc->ipmi_kthread = taskq_create_proc("ipmi_kcs", 1, minclsyspri, 1, 1,
+	    curzone->zone_zsched, TASKQ_PREPOPULATE);
+
+	if (taskq_dispatch(sc->ipmi_kthread, kcs_loop, (void *) sc,
+	    TQ_SLEEP) == NULL) {
+		taskq_destroy(sc->ipmi_kthread);
+		return (1);
+	}
+
+	return (0);
+}
+
+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) {
+		cmn_err(CE_CONT, "!KCS couldn't find it");
+		return (ENXIO);
+	}
+
+	timeout_usecs = drv_hztousec(MAX_TIMEOUT);
+
+#ifdef KCS_DEBUG
+	cmn_err(CE_NOTE, "KCS: initial state: %02x", status);
+#endif
+	if (status & KCS_STATUS_OBF ||
+	    KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE)
+		kcs_error(sc);
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/ipmi_main.c	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,611 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2012, Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * The ipmi driver is an openipmi compatible IPMI driver based on the FreeBSD
+ * driver.
+ *
+ * The current implementation has several limitations:
+ * 1) It only does discovery through the SMBIOS.  The FreeBSD driver has
+ *    several additional ways to discover the IPMI device (acpi, bus checking,
+ *    etc.).  This support could be ported if necessary.
+ * 2) The driver currently only supports the IPMI KCS_MODE mode (reported
+ *    through the SMBIOS as SMBIOS SMB_IPMI_T_KCS). Support for the other modes
+ *    (BT_MODE, SMIC_MODE, SSIF_MODE) could be ported if necessary.
+ * 3) The driver does not currently set up an IPMI watchdog.  This also could
+ *    be ported if necessary.
+ */
+
+#include <sys/devops.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/smbios.h>
+#include <sys/smbios_impl.h>
+#include <sys/policy.h>
+#include <sys/ipmi.h>
+#include "ipmivars.h"
+
+static dev_info_t		*ipmi_dip;
+static boolean_t		ipmi_attached = B_FALSE;
+static boolean_t		ipmi_found = B_FALSE;
+static struct ipmi_softc	softc;
+static struct ipmi_softc	*sc = &softc;
+static list_t			dev_list;
+static id_space_t		*minor_ids;
+
+#define	PTRIN(p)	((void *)(uintptr_t)(p))
+#define	PTROUT(p)	((uintptr_t)(p))
+
+/*
+ * Use the SMBIOS info to determine if the system has an IPMI.
+ */
+static int
+get_smbios_ipmi_info(void)
+{
+	smbios_ipmi_t ipmi;
+
+	if (ksmbios == NULL || smbios_info_ipmi(ksmbios, &ipmi) == SMB_ERR)
+		return (DDI_FAILURE);
+
+	cmn_err(CE_CONT, "!SMBIOS type 0x%x, addr 0x%llx", ipmi.smbip_type,
+	    (long long unsigned int)(ipmi.smbip_addr));
+
+	/*
+	 * Some systems have a bios that will report an IPMI device even when
+	 * it is not installed. In this case we see 0x0 as the base address.
+	 * If we see this address, assume the device is not really present.
+	 */
+	if (ipmi.smbip_addr == NULL) {
+		cmn_err(CE_WARN, "!SMBIOS: Invalid base address");
+		return (DDI_FAILURE);
+	}
+
+	sc->ipmi_io_type = ipmi.smbip_type;
+	switch (ipmi.smbip_type) {
+	case SMB_IPMI_T_KCS:
+	case SMB_IPMI_T_SMIC:
+		sc->ipmi_io_address = ipmi.smbip_addr;
+		sc->ipmi_io_mode = (ipmi.smbip_flags & SMB_IPMI_F_IOADDR) ?
+		    1 : 0;
+		sc->ipmi_io_spacing = ipmi.smbip_regspacing;
+		break;
+	case SMB_IPMI_T_SSIF:
+		if ((ipmi.smbip_addr & 0xffffffffffffff00) != 0) {
+			cmn_err(CE_WARN, "!SMBIOS: Invalid SSIF SMBus address, "
+			    "using BMC I2C slave address instead");
+			sc->ipmi_io_address = ipmi.smbip_i2c;
+		} else {
+			sc->ipmi_io_address = ipmi.smbip_addr;
+		}
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+
+	if (ipmi.smbip_intr > 15) {
+		cmn_err(CE_WARN, "!SMBIOS: Non-ISA IRQ %d for IPMI",
+		    ipmi.smbip_intr);
+		return (DDI_FAILURE);
+	}
+
+	sc->ipmi_io_irq = ipmi.smbip_intr;
+	return (DDI_SUCCESS);
+}
+
+static ipmi_device_t *
+lookup_ipmidev_by_dev(dev_t dev)
+{
+	ipmi_device_t	*p;
+
+	for (p = list_head(&dev_list); p; p = list_next(&dev_list, p)) {
+		if (dev == p->ipmi_dev)
+			return (p);
+	}
+	return (NULL);
+}
+
+/*
+ * Each open returns a new pseudo device.
+ */
+/*ARGSUSED*/
+static int
+ipmi_open(dev_t *devp, int flag, int otyp, cred_t *cred)
+{
+	minor_t minor;
+	ipmi_device_t *dev;
+
+	if (ipmi_attached == B_FALSE)
+		return (ENXIO);
+
+	if (ipmi_found == B_FALSE)
+		return (ENODEV);
+
+	/* exclusive opens are not supported */
+	if (flag & FEXCL)
+		return (ENOTSUP);
+
+	if ((minor = (minor_t)id_alloc_nosleep(minor_ids)) == 0)
+		return (ENODEV);
+
+	/* Initialize the per file descriptor data. */
+	dev = kmem_zalloc(sizeof (ipmi_device_t), KM_SLEEP);
+
+	dev->ipmi_pollhead = kmem_zalloc(sizeof (pollhead_t), KM_SLEEP);
+
+	TAILQ_INIT(&dev->ipmi_completed_requests);
+	dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
+	dev->ipmi_lun = IPMI_BMC_SMS_LUN;
+	*devp = makedevice(getmajor(*devp), minor);
+	dev->ipmi_dev = *devp;
+
+	list_insert_head(&dev_list, dev);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+ipmi_close(dev_t dev, int flag, int otyp, cred_t *cred)
+{
+	ipmi_device_t *dp;
+	struct ipmi_request *req, *next;
+
+	if ((dp = lookup_ipmidev_by_dev(dev)) == NULL)
+		return (ENODEV);
+
+	IPMI_LOCK(sc);
+	/* remove any pending requests */
+	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
+	while (req != NULL) {
+		next = TAILQ_NEXT(req, ir_link);
+
+		if (req->ir_owner == dp) {
+			TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
+			ipmi_free_request(req);
+		}
+		req = next;
+	}
+	IPMI_UNLOCK(sc);
+
+	/* remove any requests in queue of stuff completed */
+	while ((req = TAILQ_FIRST(&dp->ipmi_completed_requests)) != NULL) {
+		TAILQ_REMOVE(&dp->ipmi_completed_requests, req, ir_link);
+		ipmi_free_request(req);
+	}
+
+	list_remove(&dev_list, dp);
+	id_free(minor_ids, getminor(dev));
+	kmem_free(dp->ipmi_pollhead, sizeof (pollhead_t));
+	kmem_free(dp, sizeof (ipmi_device_t));
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+ipmi_ioctl(dev_t dv, int cmd, intptr_t data, int flags, cred_t *cr, int *rvalp)
+{
+	struct ipmi_device *dev;
+	struct ipmi_request *kreq;
+	struct ipmi_req req;
+	struct ipmi_recv recv;
+	struct ipmi_recv32 recv32;
+	struct ipmi_addr addr;
+	int error, len;
+	model_t model;
+	int orig_cmd = 0;
+	uchar_t	t_lun;
+
+	if (secpolicy_sys_config(cr, B_FALSE) != 0)
+		return (EPERM);
+
+	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
+		return (ENODEV);
+
+	model = get_udatamodel();
+	if (model == DATAMODEL_NATIVE) {
+		switch (cmd) {
+		case IPMICTL_SEND_COMMAND:
+			if (copyin((void *)data, &req, sizeof (req)))
+				return (EFAULT);
+			break;
+		case IPMICTL_RECEIVE_MSG_TRUNC:
+		case IPMICTL_RECEIVE_MSG:
+			if (copyin((void *)data, &recv, sizeof (recv)))
+				return (EFAULT);
+			break;
+		}
+	} else {
+		/* Convert 32-bit structures to native. */
+		struct ipmi_req32 req32;
+
+		switch (cmd) {
+		case IPMICTL_SEND_COMMAND_32:
+			if (copyin((void *)data, &req32, sizeof (req32)))
+				return (EFAULT);
+
+			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);
+
+			cmd = IPMICTL_SEND_COMMAND;
+			break;
+
+		case IPMICTL_RECEIVE_MSG_TRUNC_32:
+		case IPMICTL_RECEIVE_MSG_32:
+			if (copyin((void *)data, &recv32, sizeof (recv32)))
+				return (EFAULT);
+
+			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);
+
+			orig_cmd = cmd;
+			cmd = (cmd == IPMICTL_RECEIVE_MSG_TRUNC_32) ?
+			    IPMICTL_RECEIVE_MSG_TRUNC : IPMICTL_RECEIVE_MSG;
+			break;
+		}
+	}
+
+	switch (cmd) {
+	case IPMICTL_SEND_COMMAND:
+		IPMI_LOCK(sc);
+		/* clear out old stuff in queue of stuff done */
+		while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))
+		    != NULL) {
+			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
+			    ir_link);
+			dev->ipmi_requests--;
+			ipmi_free_request(kreq);
+		}
+		IPMI_UNLOCK(sc);
+
+		/* Check that we didn't get a ridiculous length */
+		if (req.msg.data_len > IPMI_MAX_RX)
+			return (EINVAL);
+
+		kreq = ipmi_alloc_request(dev, req.msgid,
+		    IPMI_ADDR(req.msg.netfn, 0), req.msg.cmd,
+		    req.msg.data_len, IPMI_MAX_RX);
+		/* This struct is the same for 32/64 */
+		if (req.msg.data_len > 0 &&
+		    copyin(req.msg.data, kreq->ir_request, req.msg.data_len)) {
+			ipmi_free_request(kreq);
+			return (EFAULT);
+		}
+		IPMI_LOCK(sc);
+		dev->ipmi_requests++;
+		error = sc->ipmi_enqueue_request(sc, kreq);
+		IPMI_UNLOCK(sc);
+		if (error)
+			return (error);
+		break;
+
+	case IPMICTL_RECEIVE_MSG_TRUNC:
+	case IPMICTL_RECEIVE_MSG:
+		/* This struct is the same for 32/64 */
+		if (copyin(recv.addr, &addr, sizeof (addr)))
+			return (EFAULT);
+
+		IPMI_LOCK(sc);
+		kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
+		if (kreq == NULL) {
+			IPMI_UNLOCK(sc);
+			return (EAGAIN);
+		}
+		addr.channel = IPMI_BMC_CHANNEL;
+		recv.recv_type = IPMI_RESPONSE_RECV_TYPE;
+		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) {
+			IPMI_UNLOCK(sc);
+			ipmi_free_request(kreq);
+			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 = (unsigned short)len;
+
+		if (orig_cmd == IPMICTL_RECEIVE_MSG_TRUNC_32 ||
+		    orig_cmd == IPMICTL_RECEIVE_MSG_32) {
+			/* Update changed fields in 32-bit structure. */
+			recv32.recv_type = recv.recv_type;
+			recv32.msgid = (int32_t)recv.msgid;
+			recv32.msg.netfn = recv.msg.netfn;
+			recv32.msg.cmd = recv.msg.cmd;
+			recv32.msg.data_len = recv.msg.data_len;
+
+			error = copyout(&recv32, (void *)data, sizeof (recv32));
+		} else {
+			error = copyout(&recv, (void *)data, sizeof (recv));
+		}
+
+		/* This struct is the same for 32/64 */
+		if (error == 0)
+			error = copyout(&addr, recv.addr, sizeof (addr));
+		if (error == 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 (EFAULT);
+
+		break;
+
+	case IPMICTL_SET_MY_ADDRESS_CMD:
+		IPMI_LOCK(sc);
+		if (copyin((void *)data, &dev->ipmi_address,
+		    sizeof (dev->ipmi_address))) {
+			IPMI_UNLOCK(sc);
+			return (EFAULT);
+		}
+		IPMI_UNLOCK(sc);
+		break;
+
+	case IPMICTL_GET_MY_ADDRESS_CMD:
+		IPMI_LOCK(sc);
+		if (copyout(&dev->ipmi_address, (void *)data,
+		    sizeof (dev->ipmi_address))) {
+			IPMI_UNLOCK(sc);
+			return (EFAULT);
+		}
+		IPMI_UNLOCK(sc);
+		break;
+
+	case IPMICTL_SET_MY_LUN_CMD:
+		IPMI_LOCK(sc);
+		if (copyin((void *)data, &t_lun, sizeof (t_lun))) {
+			IPMI_UNLOCK(sc);
+			return (EFAULT);
+		}
+		dev->ipmi_lun = t_lun & 0x3;
+		IPMI_UNLOCK(sc);
+		break;
+
+	case IPMICTL_GET_MY_LUN_CMD:
+		IPMI_LOCK(sc);
+		if (copyout(&dev->ipmi_lun, (void *)data,
+		    sizeof (dev->ipmi_lun))) {
+			IPMI_UNLOCK(sc);
+			return (EFAULT);
+		}
+		IPMI_UNLOCK(sc);
+		break;
+
+	case IPMICTL_SET_GETS_EVENTS_CMD:
+		break;
+
+	case IPMICTL_REGISTER_FOR_CMD:
+	case IPMICTL_UNREGISTER_FOR_CMD:
+		return (EINVAL);
+
+	default:
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+static int
+ipmi_poll(dev_t dv, short events, int anyyet, short *reventsp,
+    pollhead_t **phpp)
+{
+	struct ipmi_device *dev;
+	short revent = 0;
+
+	if ((dev = lookup_ipmidev_by_dev(dv)) == NULL)
+		return (ENODEV);
+
+	if (events & (POLLIN | POLLRDNORM)) {
+		if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
+			revent |= events & (POLLIN | POLLRDNORM);
+		if (dev->ipmi_requests == 0)
+			revent |= POLLERR;
+	}
+
+	if (revent == 0) {
+		/* nothing has occurred */
+		if (!anyyet)
+			*phpp = dev->ipmi_pollhead;
+	}
+
+	*reventsp = revent;
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+ipmi_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
+{
+	switch (cmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		*resultp = ipmi_dip;
+		return (DDI_SUCCESS);
+	case DDI_INFO_DEVT2INSTANCE:
+		*resultp = NULL;
+		return (DDI_SUCCESS);
+	}
+	return (DDI_FAILURE);
+}
+
+static int
+ipmi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	if (cmd != DDI_ATTACH)
+		return (DDI_FAILURE);
+
+	if (get_smbios_ipmi_info() == DDI_FAILURE)
+		return (DDI_FAILURE);
+
+	/*
+	 * Support for the other types (SMIC, SSIF) should be added here.
+	 */
+	switch (sc->ipmi_io_type) {
+	case SMB_IPMI_T_KCS:
+		if (ipmi_kcs_attach(sc) != 0)
+			return (DDI_FAILURE);
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+	ipmi_found = B_TRUE;
+
+	if (ddi_create_minor_node(dip, "ipmi", S_IFCHR, 0, DDI_PSEUDO,
+	    0) == DDI_FAILURE) {
+		cmn_err(CE_WARN, "!attach could not create minor node");
+		ddi_remove_minor_node(dip, NULL);
+		return (DDI_FAILURE);
+	}
+
+	ipmi_dip = dip;
+
+	list_create(&dev_list, sizeof (ipmi_device_t),
+	    offsetof(ipmi_device_t, ipmi_node));
+
+	/* Create ID space for open devs.  ID 0 is reserved. */
+	minor_ids = id_space_create("ipmi_id_space", 1, 128);
+
+	ipmi_startup(sc);
+	ipmi_attached = B_TRUE;
+
+	return (DDI_SUCCESS);
+}
+
+static int
+ipmi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	if (ipmi_found == B_FALSE)
+		return (DDI_SUCCESS);
+
+	if (!list_is_empty(&dev_list))
+		return (DDI_FAILURE);
+
+	/* poke the taskq so that it can terminate */
+	sc->ipmi_detaching = 1;
+	cv_signal(&sc->ipmi_request_added);
+
+	ddi_remove_minor_node(dip, NULL);
+	ipmi_dip = NULL;
+
+	taskq_destroy(sc->ipmi_kthread);
+	list_destroy(&dev_list);
+	id_space_destroy(minor_ids);
+
+	ipmi_attached = B_FALSE;
+	return (DDI_SUCCESS);
+}
+
+static struct cb_ops ipmi_cb_ops = {
+	ipmi_open,
+	ipmi_close,
+	nodev,			/* strategy */
+	nodev,			/* print */
+	nodev,			/* dump */
+	nodev,			/* read */
+	nodev,			/* write */
+	ipmi_ioctl,
+	nodev,			/* devmap */
+	nodev,			/* mmap */
+	nodev,			/* segmap */
+	ipmi_poll,
+	ddi_prop_op,
+	NULL,			/* streamtab */
+	D_NEW | D_MP		/* flags */
+};
+
+static struct dev_ops ipmi_ops = {
+	DEVO_REV,
+	0,			/* reference count */
+	ipmi_info,
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	ipmi_attach,
+	ipmi_detach,
+	nodev,			/* reset */
+	&ipmi_cb_ops,
+	NULL,			/* bus ops */
+	NULL,			/* power */
+	ddi_quiesce_not_needed,
+};
+
+static struct modldrv md = {
+	&mod_driverops, "ipmi driver", &ipmi_ops
+};
+
+static struct modlinkage ml = {
+	MODREV_1, &md, NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&ml));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&ml));
+}
+
+int
+_info(struct modinfo *mip)
+{
+	return (mod_info(&ml, mip));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/io/ipmi/ipmivars.h	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/ipmi/ipmivars.h,v 1.3 2008/08/28 02:13:53 jhb Exp $
+ */
+
+/*
+ * Copyright 2012, Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _IPMIVARS_H_
+#define	_IPMIVARS_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct ipmi_device;
+struct ipmi_request;
+
+struct ipmi_request {
+	TAILQ_ENTRY(ipmi_request) ir_link;
+	struct ipmi_device *ir_owner;	/* Driver uses NULL. */
+	uchar_t		*ir_request;	/* Request is data to send to BMC. */
+	size_t		ir_requestlen;
+	uchar_t		*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;
+	int		ir_sz;		/* size of request */
+};
+
+#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
+
+/* Per file descriptor data. */
+typedef struct ipmi_device {
+	TAILQ_HEAD(, ipmi_request) ipmi_completed_requests;
+	pollhead_t		*ipmi_pollhead;
+	int			ipmi_requests;
+	uchar_t			ipmi_address;	/* IPMB address. */
+	uchar_t			ipmi_lun;
+	dev_t			ipmi_dev;
+	list_node_t		ipmi_node;	/* list link for open devs */
+} ipmi_device_t;
+
+struct ipmi_softc {
+	int			ipmi_io_rid;
+	int			ipmi_io_type;
+	uint64_t		ipmi_io_address;
+	int			ipmi_io_mode;
+	int			ipmi_io_spacing;
+	int			ipmi_io_irq;
+	void			*ipmi_irq;
+	int			ipmi_detaching;
+	TAILQ_HEAD(, ipmi_request) ipmi_pending_requests;
+	kmutex_t		ipmi_lock;
+	kcondvar_t		ipmi_request_added;
+	taskq_t			*ipmi_kthread;
+	int			(*ipmi_startup)(struct ipmi_softc *);
+	int			(*ipmi_enqueue_request)(struct ipmi_softc *,
+				    struct ipmi_request *);
+};
+
+#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 */
+#define	KCS_STATUS_SMS_ATN		0x04 /* Ready in RX queue */
+#define	KCS_STATUS_C_D			0x08 /* Command/Data register write */
+#define	KCS_STATUS_OEM1			0x10
+#define	KCS_STATUS_OEM2			0x20
+#define	KCS_STATUS_S0			0x40
+#define	KCS_STATUS_S1			0x80
+#define	KCS_STATUS_STATE(x)		((x)>>6)
+#define	KCS_STATUS_STATE_IDLE		0x0
+#define	KCS_STATUS_STATE_READ		0x1
+#define	KCS_STATUS_STATE_WRITE		0x2
+#define	KCS_STATUS_STATE_ERROR		0x3
+#define	KCS_IFACE_STATUS_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
+
+/* KCS control codes */
+#define	KCS_CONTROL_GET_STATUS_ABORT	0x60
+#define	KCS_CONTROL_WRITE_START		0x61
+#define	KCS_CONTROL_WRITE_END		0x62
+#define	KCS_DATA_IN_READ		0x68
+
+/* SMIC status flags */
+#define	SMIC_STATUS_BUSY		0x01 /* System set and BMC clears it */
+#define	SMIC_STATUS_SMS_ATN		0x04 /* BMC has a message */
+#define	SMIC_STATUS_EVT_ATN		0x08 /* Event has been RX */
+#define	SMIC_STATUS_SMI			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
+#define	SMIC_CC_SMS_WR_START		0x41
+#define	SMIC_CC_SMS_WR_NEXT		0x42
+#define	SMIC_CC_SMS_WR_END		0x43
+#define	SMIC_CC_SMS_RD_START		0x44
+#define	SMIC_CC_SMS_RD_NEXT		0x45
+#define	SMIC_CC_SMS_RD_END		0x46
+
+/* SMIC status codes */
+#define	SMIC_SC_SMS_RDY			0xc0
+#define	SMIC_SC_SMS_WR_START		0xc1
+#define	SMIC_SC_SMS_WR_NEXT		0xc2
+#define	SMIC_SC_SMS_WR_END		0xc3
+#define	SMIC_SC_SMS_RD_START		0xc4
+#define	SMIC_SC_SMS_RD_NEXT		0xc5
+#define	SMIC_SC_SMS_RD_END		0xc6
+
+#define	IPMI_ADDR(netfn, lun)		((netfn) << 2 | (lun))
+#define	IPMI_REPLY_ADDR(addr)		((addr) + 0x4)
+
+#define	IPMI_LOCK(sc)			mutex_enter(&(sc)->ipmi_lock)
+#define	IPMI_UNLOCK(sc)			mutex_exit(&(sc)->ipmi_lock)
+#define	IPMI_LOCK_ASSERT(sc)		ASSERT(MUTEX_HELD(&(sc)->ipmi_lock))
+
+#define	ipmi_alloc_driver_request(addr, cmd, reqlen, replylen)		\
+	ipmi_alloc_request(NULL, 0, (addr), (cmd), (reqlen), (replylen))
+
+#define	INB(sc, x)							\
+	inb((sc)->ipmi_io_address + ((sc)->ipmi_io_spacing * (x)))
+#define	OUTB(sc, x, value)						\
+	outb((sc)->ipmi_io_address + ((sc)->ipmi_io_spacing * (x)), value)
+
+#define	MAX_TIMEOUT (3 * hz)
+
+/* Manage requests. */
+void	ipmi_complete_request(struct ipmi_softc *, struct ipmi_request *);
+struct ipmi_request *ipmi_dequeue_request(struct ipmi_softc *);
+int	ipmi_polled_enqueue_request(struct ipmi_softc *, struct ipmi_request *);
+struct ipmi_request *ipmi_alloc_request(struct ipmi_device *, long msgid,
+	    uint8_t, uint8_t, size_t, size_t);
+void	ipmi_free_request(struct ipmi_request *);
+
+/* Interface attach routines. */
+void	ipmi_startup(struct ipmi_softc *sc);
+int	ipmi_kcs_attach(struct ipmi_softc *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _IPMIVARS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/ipmi/Makefile	Thu Apr 26 15:49:44 2012 +0000
@@ -0,0 +1,83 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2012, Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# IPMI interface
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+#       Define the module and object file sets.
+#
+MODULE		= ipmi
+OBJECTS		= $(IPMI_OBJS:%=$(OBJS_DIR)/%)
+LINTS           = $(IPMI_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE      = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/intel/io/ipmi
+
+#
+#       Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#       Define targets
+#
+ALL_TARGET      = $(BINARY) $(SRC_CONFFILE)
+LINT_TARGET     = $(LINT_MODULE).lint
+INSTALL_TARGET  = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Kernel Module Dependencies
+#
+LDFLAGS += -dy
+
+#
+#       Default build targets.
+#
+.KEEP_STATE:
+
+def:            $(DEF_DEPS)
+
+all:            $(ALL_DEPS)
+
+clean:          $(CLEAN_DEPS)
+
+clobber:        $(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:        $(INSTALL_DEPS)
+
+#
+#       Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ