diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2012-04-26 15:49:44 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2012-04-26 15:49:44 +0000 |
commit | 8c07126d4a0608e93b1cc5be587164e6fb9c8339 (patch) | |
tree | ffbe0d6945ac5c3d35d788b6ad656953d16d99ad | |
parent | d10518cf890461b05fb2f867395ae61cd960474a (diff) | |
download | illumos-joyent-8c07126d4a0608e93b1cc5be587164e6fb9c8339.tar.gz |
OS-101 bmc driver is missing which makes ipmi not work locally; port freebsd openipmi driver
-rw-r--r-- | manifest | 4 | ||||
-rw-r--r-- | usr/src/cmd/devfsadm/devlink.tab.sh | 6 | ||||
-rw-r--r-- | usr/src/man/man7d/Makefile | 1 | ||||
-rw-r--r-- | usr/src/man/man7d/ipmi.7d | 163 | ||||
-rw-r--r-- | usr/src/uts/common/sys/Makefile | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ipmi.h | 176 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.files | 5 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.intel.shared | 1 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.rules | 8 | ||||
-rw-r--r-- | usr/src/uts/intel/io/ipmi/ipmi.c | 279 | ||||
-rw-r--r-- | usr/src/uts/intel/io/ipmi/ipmi.conf | 25 | ||||
-rw-r--r-- | usr/src/uts/intel/io/ipmi/ipmi_kcs.c | 507 | ||||
-rw-r--r-- | usr/src/uts/intel/io/ipmi/ipmi_main.c | 578 | ||||
-rw-r--r-- | usr/src/uts/intel/io/ipmi/ipmivars.h | 191 | ||||
-rw-r--r-- | usr/src/uts/intel/ipmi/Makefile | 83 |
15 files changed, 2024 insertions, 4 deletions
@@ -3781,6 +3781,7 @@ f usr/include/sys/iommulib.h 0644 root bin f usr/include/sys/ipc.h 0644 root bin f usr/include/sys/ipc_impl.h 0644 root bin f usr/include/sys/ipc_rctl.h 0644 root bin +f usr/include/sys/ipmi.h 0644 root bin f usr/include/sys/isa_defs.h 0644 root bin f usr/include/sys/iscsi_authclient.h 0644 root bin f usr/include/sys/iscsi_authclientglue.h 0644 root bin @@ -4515,6 +4516,7 @@ f usr/kernel/drv/amd64/bpf 0755 root sys f usr/kernel/drv/amd64/dump 0755 root sys f usr/kernel/drv/amd64/fssnap 0755 root sys f usr/kernel/drv/amd64/ipf 0755 root sys +f usr/kernel/drv/amd64/ipmi 0755 root sys f usr/kernel/drv/amd64/kstat 0755 root sys f usr/kernel/drv/amd64/ksyms 0755 root sys f usr/kernel/drv/amd64/logindmux 0755 root sys @@ -4531,6 +4533,7 @@ f usr/kernel/drv/bpf.conf 0644 root sys f usr/kernel/drv/dump.conf 0644 root sys f usr/kernel/drv/fssnap.conf 0644 root sys f usr/kernel/drv/ipf.conf 0644 root sys +f usr/kernel/drv/ipmi.conf 0644 root sys f usr/kernel/drv/kstat.conf 0644 root sys f usr/kernel/drv/ksyms.conf 0644 root sys f usr/kernel/drv/logindmux.conf 0644 root sys @@ -15117,6 +15120,7 @@ f usr/share/man/man7d/hwahc.7d 0444 root bin f usr/share/man/man7d/hwarc.7d 0444 root bin f usr/share/man/man7d/hxge.7d 0444 root bin f usr/share/man/man7d/igb.7d 0444 root bin +f usr/share/man/man7d/ipmi.7d 0444 root bin f usr/share/man/man7d/ipnet.7d 0444 root bin f usr/share/man/man7d/iscsi.7d 0444 root bin f usr/share/man/man7d/ixgbe.7d 0444 root bin diff --git a/usr/src/cmd/devfsadm/devlink.tab.sh b/usr/src/cmd/devfsadm/devlink.tab.sh index 6724fcb573..5498b43fd4 100644 --- a/usr/src/cmd/devfsadm/devlink.tab.sh +++ b/usr/src/cmd/devfsadm/devlink.tab.sh @@ -22,8 +22,7 @@ # # Copyright (c) 1998, 2000 by Sun Microsystems, Inc. # All rights reserved. -# -#ident "%Z%%M% %I% %E% SMI" +# Copyright (c) 2012 Joyent, Inc. All rights reserved. # # This is the script that generates the devlink.tab file. It is # architecture-aware, and dumps different stuff for x86 and sparc. @@ -34,8 +33,6 @@ # cat <<EOM -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1998 by Sun Microsystems, Inc. # # @@ -105,6 +102,7 @@ case "$MACH" in # It depends on the build machine being an x86 # cat <<-EOM + type=ddi_pseudo;name=ipmi;minor=ipmi \D EOM ;; "sparc" ) 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..710b8f968c --- /dev/null +++ b/usr/src/man/man7d/ipmi.7d @@ -0,0 +1,163 @@ +'\" te +.\" Copyright (c) 2012, Joyent, Inc. All Rights Reserved +.\" 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] +.TH IMPI 7D "Apr 21, 2012" +.SH NAME +ipmi \- OpenIPMI compatible IPMI interface driver +.SH SYNOPSIS +.LP +.nf +\fB/dev/ipmi\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/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index f888de67ac..87c3d50692 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -279,6 +279,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/ipmi.h b/usr/src/uts/common/sys/ipmi.h new file mode 100644 index 0000000000..9dafac407d --- /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 /* Linux Default slave address */ +#define IPMI_BMC_CHANNEL 0x0f /* Linux BMC channel */ + +#define IPMI_BMC_SMS_LUN 0x02 + +#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE 0x0c +#define IPMI_IPMB_ADDR_TYPE 0x01 +#define IPMI_IPMB_BROADCAST_ADDR_TYPE 0x41 + +#define IPMI_IOC_MAGIC 'i' +#define IPMICTL_RECEIVE_MSG_TRUNC \ + _IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv) +#define IPMICTL_RECEIVE_MSG \ + _IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv) +#define IPMICTL_SEND_COMMAND \ + _IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req) +#define IPMICTL_REGISTER_FOR_CMD \ + _IOW(IPMI_IOC_MAGIC, 14, struct ipmi_cmdspec) +#define IPMICTL_UNREGISTER_FOR_CMD \ + _IOW(IPMI_IOC_MAGIC, 15, struct ipmi_cmdspec) +#define IPMICTL_SET_GETS_EVENTS_CMD _IOW(IPMI_IOC_MAGIC, 16, int) +#define IPMICTL_SET_MY_ADDRESS_CMD _IOW(IPMI_IOC_MAGIC, 17, unsigned int) +#define IPMICTL_GET_MY_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 18, unsigned int) +#define IPMICTL_SET_MY_LUN_CMD _IOW(IPMI_IOC_MAGIC, 19, unsigned int) +#define IPMICTL_GET_MY_LUN_CMD _IOR(IPMI_IOC_MAGIC, 20, unsigned int) + +#define IPMI_RESPONSE_RECV_TYPE 1 +#define IPMI_ASYNC_EVENT_RECV_TYPE 2 +#define IPMI_CMD_RECV_TYPE 3 + +#define IPMI_APP_REQUEST 0x06 +#define IPMI_GET_DEVICE_ID 0x01 +#define IPMI_CLEAR_FLAGS 0x30 +#define IPMI_GET_MSG_FLAGS 0x31 +#define IPMI_MSG_AVAILABLE 0x01 +#define IPMI_MSG_BUFFER_FULL 0x02 +#define IPMI_WDT_PRE_TIMEOUT 0x08 +#define IPMI_GET_MSG 0x33 +#define IPMI_SEND_MSG 0x34 +#define IPMI_GET_CHANNEL_INFO 0x42 +#define IPMI_RESET_WDOG 0x22 +#define IPMI_SET_WDOG 0x24 +#define IPMI_GET_WDOG 0x25 + +#define IPMI_SET_WD_TIMER_SMS_OS 0x04 +#define IPMI_SET_WD_TIMER_DONT_STOP 0x40 +#define IPMI_SET_WD_ACTION_RESET 0x01 + +struct ipmi_msg { + unsigned char netfn; + unsigned char cmd; + unsigned short data_len; + unsigned char *data; +}; + +struct ipmi_req { + unsigned char *addr; + unsigned int addr_len; + long msgid; + struct ipmi_msg msg; +}; + +struct ipmi_recv { + int recv_type; + unsigned char *addr; + unsigned int addr_len; + long msgid; + struct ipmi_msg msg; +}; + +struct ipmi_cmdspec { + unsigned char netfn; + unsigned char cmd; +}; + +struct ipmi_addr { + int addr_type; + short channel; + unsigned char data[IPMI_MAX_ADDR_SIZE]; +}; + +struct ipmi_system_interface_addr { + int addr_type; + short channel; + unsigned char lun; +}; + +struct ipmi_ipmb_addr { + int addr_type; + short channel; + unsigned char slave_addr; + unsigned char lun; +}; + +#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 4def75f7ee..e415767f12 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -403,6 +403,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 3e33ab1712..247fe0dffc 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) @@ -417,6 +422,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/ipmi.c b/usr/src/uts/intel/io/ipmi/ipmi.c new file mode 100644 index 0000000000..56829dfcad --- /dev/null +++ b/usr/src/uts/intel/io/ipmi/ipmi.c @@ -0,0 +1,279 @@ +/* + * 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); + + (void) ipmi_submit_driver_request(sc, req, 0); + + /* 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; + + (void) ipmi_submit_driver_request(sc, req, 0); + + 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); + + (void) ipmi_submit_driver_request(sc, req, 0); + + 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..480db14459 --- /dev/null +++ b/usr/src/uts/intel/io/ipmi/ipmi_kcs.c @@ -0,0 +1,507 @@ +/* + * 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/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..73fad7c995 --- /dev/null +++ b/usr/src/uts/intel/io/ipmi/ipmi_main.c @@ -0,0 +1,578 @@ +/* + * 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 void +get_smbios_ipmi_info() +{ + smbios_ipmi_t ipmi; + + if (ksmbios == NULL || smbios_info_ipmi(ksmbios, &ipmi) == SMB_ERR) + return; + + cmn_err(CE_CONT, "!SMBIOS type 0x%x, addr 0x%llx", ipmi.smbip_type, + (long long unsigned int)(ipmi.smbip_addr)); + + 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; + } + + if (ipmi.smbip_intr > 15) + cmn_err(CE_WARN, "!SMBIOS: Non-ISA IRQ %d for IPMI", + ipmi.smbip_intr); + else + sc->ipmi_io_irq = ipmi.smbip_intr; +} + +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; + + if ((dp = lookup_ipmidev_by_dev(dev)) == NULL) + return (ENODEV); + + IPMI_LOCK(sc); + /* remove any pending requests */ +restart: + for (req = TAILQ_FIRST(&sc->ipmi_pending_requests); req != NULL; + req = TAILQ_NEXT(req, ir_link)) { + if (req->ir_owner == dp) { + TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); + ipmi_free_request(req); + goto restart; + } + } + 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; + + 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 (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); + dev->ipmi_address = *(int *)data; + IPMI_UNLOCK(sc); + break; + + case IPMICTL_GET_MY_ADDRESS_CMD: + IPMI_LOCK(sc); + *(int *)data = dev->ipmi_address; + IPMI_UNLOCK(sc); + break; + + case IPMICTL_SET_MY_LUN_CMD: + IPMI_LOCK(sc); + dev->ipmi_lun = *(int *)data & 0x3; + IPMI_UNLOCK(sc); + break; + + case IPMICTL_GET_MY_LUN_CMD: + IPMI_LOCK(sc); + *(int *)data = dev->ipmi_lun; + 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); + + get_smbios_ipmi_info(); + + /* + * 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..c63d500e97 --- /dev/null +++ b/usr/src/uts/intel/io/ipmi/ipmivars.h @@ -0,0 +1,191 @@ +/* + * 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 + +struct ipmi_softc; + +/* 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 |