summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2012-04-26 15:49:44 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2012-04-26 15:49:44 +0000
commit989f28072d20c73ae0955d6a1e3e2fc74831cb39 (patch)
tree58240a19d06cfc768da50e9e02bd1813900e2807 /usr/src
parent951bafea4a4c40f620a735a1b4827188d065e18c (diff)
downloadillumos-joyent-989f28072d20c73ae0955d6a1e3e2fc74831cb39.tar.gz
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>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/devfsadm/i386/misc_link_i386.c15
-rw-r--r--usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c2
-rw-r--r--usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c7
-rw-r--r--usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c2
-rw-r--r--usr/src/lib/libipmi/common/ipmi_bmc.c135
-rw-r--r--usr/src/lib/libipmi/common/libipmi.c3
-rw-r--r--usr/src/lib/libipmi/common/libipmi.h24
-rw-r--r--usr/src/man/man7d/Makefile1
-rw-r--r--usr/src/man/man7d/ipmi.7d180
-rw-r--r--usr/src/pkg/manifests/driver-ipmi.mf45
-rw-r--r--usr/src/pkg/manifests/system-header.mf1
-rw-r--r--usr/src/uts/common/sys/Makefile2
-rw-r--r--usr/src/uts/common/sys/bmc_intf.h203
-rw-r--r--usr/src/uts/common/sys/ipmi.h176
-rw-r--r--usr/src/uts/intel/Makefile.files5
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/intel/Makefile.rules8
-rw-r--r--usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE23
-rw-r--r--usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmi.c287
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmi.conf25
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmi_kcs.c508
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmi_main.c611
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmivars.h189
-rw-r--r--usr/src/uts/intel/ipmi/Makefile83
25 files changed, 2239 insertions, 298 deletions
diff --git a/usr/src/cmd/devfsadm/i386/misc_link_i386.c b/usr/src/cmd/devfsadm/i386/misc_link_i386.c
index e9d831ed8e..5d2e18ad8a 100644
--- a/usr/src/cmd/devfsadm/i386/misc_link_i386.c
+++ b/usr/src/cmd/devfsadm/i386/misc_link_i386.c
@@ -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 serial(di_minor_t minor, di_node_t node);
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 @@ static devfsadm_create_t misc_cbt[] = {
{ "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 @@ kdmouse(di_minor_t minor, di_node_t node)
}
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);
}
diff --git a/usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c b/usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c
index 7137297c0b..b618de53e0 100644
--- a/usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c
+++ b/usr/src/cmd/fm/modules/common/fdd-msg/fdd_msg.c
@@ -188,7 +188,7 @@ send_fma_cap_to_ilom(fmd_hdl_t *hdl, uint32_t fma_cap)
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)
diff --git a/usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c b/usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c
index 789e16e08d..431c0f10ce 100644
--- a/usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c
+++ b/usr/src/cmd/fm/modules/common/sp-monitor/sp_monitor.c
@@ -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 @@ _fmd_init(fmd_hdl_t *hdl)
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)
diff --git a/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c b/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c
index 715c7311da..559020efe5 100644
--- a/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c
+++ b/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c
@@ -271,7 +271,7 @@ ipmi_platform_message(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
/*
* 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;
diff --git a/usr/src/lib/libipmi/common/ipmi_bmc.c b/usr/src/lib/libipmi/common/ipmi_bmc.c
index 92ac6dc333..b6c4076a56 100644
--- a/usr/src/lib/libipmi/common/ipmi_bmc.c
+++ b/usr/src/lib/libipmi/common/ipmi_bmc.c
@@ -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 @@ ipmi_bmc_open(ipmi_handle_t *ihp, nvlist_t *params)
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 @@ ipmi_bmc_open(ipmi_handle_t *ihp, nvlist_t *params)
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,86 +96,82 @@ ipmi_bmc_send(void *data, ipmi_cmd_t *cmd, ipmi_cmd_t *response,
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;
-
- /*
- * 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);
-
- /* construct and send the message */
- if ((msg = ipmi_zalloc(ibp->ib_ihp, msgsz)) == NULL)
- return (-1);
- bmcreq = (bmc_req_t *)&msg->msg[0];
-
- 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;
-
- if (putmsg(ibp->ib_fd, NULL, &sb, 0) < 0) {
- ipmi_free(ibp->ib_ihp, msg);
+ struct ipmi_req req;
+ struct ipmi_recv recv;
+ struct ipmi_addr addr;
+ fd_set rset;
+ struct ipmi_system_interface_addr bmc_addr;
+
+ bmc_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ bmc_addr.channel = IPMI_BMC_CHANNEL;
+ bmc_addr.lun = cmd->ic_lun;
+
+ (void) memset(&req, 0, sizeof (struct ipmi_req));
+
+ req.addr = (unsigned char *) &bmc_addr;
+ req.addr_len = sizeof (bmc_addr);
+
+ 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 (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 */
- sb.buf = (char *)ibp->ib_msg;
- sb.maxlen = ibp->ib_msglen;
- if (getmsg(ibp->ib_fd, NULL, &sb, &flags) < 0) {
+ FD_ZERO(&rset);
+ FD_SET(ibp->ib_fd, &rset);
+
+ 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);
+ }
- switch (ibp->ib_msg->m_type) {
- case BMC_MSG_RESPONSE:
- bmcrsp = (bmc_rsp_t *)&ibp->ib_msg->msg[0];
-
- 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]));
+ 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);
+ }
- default:
+ if (recv.recv_type != IPMI_RESPONSE_RECV_TYPE) {
(void) ipmi_set_error(ibp->ib_ihp, EIPMI_BMC_RESPONSE,
- "unknown BMC message type %d", ibp->ib_msg->m_type);
+ "unknown BMC message type %d", recv.recv_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);
}
diff --git a/usr/src/lib/libipmi/common/libipmi.c b/usr/src/lib/libipmi/common/libipmi.c
index d678dc8257..63e985755c 100644
--- a/usr/src/lib/libipmi/common/libipmi.c
+++ b/usr/src/lib/libipmi/common/libipmi.c
@@ -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 @@ static struct ipmi_err_conv {
{ 0xD5, EIPMI_UNAVAILABLE },
{ 0xD6, EIPMI_UNAVAILABLE },
{ 0xFF, EIPMI_UNSPECIFIED },
- { BMC_IPMI_OEM_FAILURE_SENDBMC, EIPMI_SEND_FAILED },
};
#define IPMI_ERROR_COUNT \
diff --git a/usr/src/lib/libipmi/common/libipmi.h b/usr/src/lib/libipmi/common/libipmi.h
index 46e15e1b43..af8a1f6351 100644
--- a/usr/src/lib/libipmi/common/libipmi.h
+++ b/usr/src/lib/libipmi/common/libipmi.h
@@ -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 @@ typedef struct ipmi_handle ipmi_handle_t;
/*
* 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 struct ipmi_handle ipmi_handle_t;
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 */
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index 81fd702ed1..7ba0cf10af 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -209,6 +209,7 @@ i386_MANFILES = ahci.7d \
ecpp.7d \
heci.7d \
i915.7d \
+ ipmi.7d \
ipw.7d \
iwh.7d \
iwi.7d \
diff --git a/usr/src/man/man7d/ipmi.7d b/usr/src/man/man7d/ipmi.7d
new file mode 100644
index 0000000000..50e80253de
--- /dev/null
+++ b/usr/src/man/man7d/ipmi.7d
@@ -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.
diff --git a/usr/src/pkg/manifests/driver-ipmi.mf b/usr/src/pkg/manifests/driver-ipmi.mf
new file mode 100644
index 0000000000..e6eea4cb99
--- /dev/null
+++ b/usr/src/pkg/manifests/driver-ipmi.mf
@@ -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
diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf
index baffee6182..3fb3c1351c 100644
--- a/usr/src/pkg/manifests/system-header.mf
+++ b/usr/src/pkg/manifests/system-header.mf
@@ -813,7 +813,6 @@ file path=usr/include/sys/bitmap.h
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
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index ea6c93ff59..271682bc67 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -86,7 +86,6 @@ CHKHDRS= \
bitset.h \
bl.h \
blkdev.h \
- bmc_intf.h \
bofi.h \
bofi_impl.h \
bpp_io.h \
@@ -278,6 +277,7 @@ CHKHDRS= \
ipc.h \
ipc_impl.h \
ipc_rctl.h \
+ ipmi.h \
isa_defs.h \
iscsi_authclient.h \
iscsi_authclientglue.h \
diff --git a/usr/src/uts/common/sys/bmc_intf.h b/usr/src/uts/common/sys/bmc_intf.h
deleted file mode 100644
index 83211e0174..0000000000
--- a/usr/src/uts/common/sys/bmc_intf.h
+++ /dev/null
@@ -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 */
diff --git a/usr/src/uts/common/sys/ipmi.h b/usr/src/uts/common/sys/ipmi.h
new file mode 100644
index 0000000000..94a53392de
--- /dev/null
+++ b/usr/src/uts/common/sys/ipmi.h
@@ -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_ */
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index 2fe0900d3e..082c4eefb3 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -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 @@ CORE_PCBE_OBJS = core_pcbe.o
AMR_OBJS = amr.o
#
+# IPMI module
+IPMI_OBJS += ipmi_main.o ipmi.o ipmi_kcs.o
+
+#
# IOMMULIB module
#
IOMMULIB_OBJS = iommulib.o
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 874cc84b83..f953af7233 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -400,6 +400,7 @@ DRV_KMODS += rtls
DRV_KMODS += sfe
DRV_KMODS += amd8111s
DRV_KMODS += igb
+DRV_KMODS += ipmi
DRV_KMODS += iprb
DRV_KMODS += ixgbe
DRV_KMODS += vr
diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules
index c4978092ce..b246e8fcca 100644
--- a/usr/src/uts/intel/Makefile.rules
+++ b/usr/src/uts/intel/Makefile.rules
@@ -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 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/hotplug/pcicfg/%.c
$(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_nb5000/%.c
$(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))
diff --git a/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE b/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..78c9a1985c
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE
@@ -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.
diff --git a/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE.descrip b/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..eb678fe823
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+INTELLIGENT PLATFORM MANAGEMENT INTERFACE SUPPORT
diff --git a/usr/src/uts/intel/io/ipmi/ipmi.c b/usr/src/uts/intel/io/ipmi/ipmi.c
new file mode 100644
index 0000000000..7c59bc2fda
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/ipmi.c
@@ -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);
+}
diff --git a/usr/src/uts/intel/io/ipmi/ipmi.conf b/usr/src/uts/intel/io/ipmi/ipmi.conf
new file mode 100644
index 0000000000..43cad12991
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/ipmi.conf
@@ -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;
diff --git a/usr/src/uts/intel/io/ipmi/ipmi_kcs.c b/usr/src/uts/intel/io/ipmi/ipmi_kcs.c
new file mode 100644
index 0000000000..2cdeb5c0f7
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/ipmi_kcs.c
@@ -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);
+}
diff --git a/usr/src/uts/intel/io/ipmi/ipmi_main.c b/usr/src/uts/intel/io/ipmi/ipmi_main.c
new file mode 100644
index 0000000000..9d1e2d0031
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/ipmi_main.c
@@ -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));
+}
diff --git a/usr/src/uts/intel/io/ipmi/ipmivars.h b/usr/src/uts/intel/io/ipmi/ipmivars.h
new file mode 100644
index 0000000000..2943fd1574
--- /dev/null
+++ b/usr/src/uts/intel/io/ipmi/ipmivars.h
@@ -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_ */
diff --git a/usr/src/uts/intel/ipmi/Makefile b/usr/src/uts/intel/ipmi/Makefile
new file mode 100644
index 0000000000..38a63f6d23
--- /dev/null
+++ b/usr/src/uts/intel/ipmi/Makefile
@@ -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