Index: head/share/man/man4/ipmi.4 =================================================================== --- head/share/man/man4/ipmi.4 (revision 324989) +++ head/share/man/man4/ipmi.4 (revision 324990) @@ -1,201 +1,214 @@ .\" .\" Copyright (c) 2006 Tom Rhodes .\" 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$ .\" .Dd July 10, 2007 .Dt IPMI 4 .Os .Sh NAME .Nm ipmi .Nd "OpenIPMI compatible IPMI interface driver" .Sh SYNOPSIS .Cd "device ipmi" .Pp To manually specify I/O attachment in .Pa /boot/device.hints : .Cd hint.ipmi.0.at="isa" .Cd hint.ipmi.0.port="0xCA2" .Cd hint.ipmi.0.spacing="8" .Cd hint.ipmi.0.mode="KCS" .Pp To manually specify memory attachment in .Pa /boot/device.hints : .Cd hint.ipmi.0.at="isa" .Cd hint.ipmi.0.maddr="0xf0000000" .Cd hint.ipmi.0.spacing="8" .Cd hint.ipmi.0.mode="SMIC" .Pp Meaning of .Ar spacing : .Bl -tag -offset indent -compact -width 0x0 .It 8 8 bit alignment .It 16 16 bit alignment .It 32 32 bit alignment .El .Pp If the .Ar port and .Ar spacing are not specified the interface type default will be used. Only specify either the .Ar port for I/O access or .Ar maddr for memory access. .Sh DESCRIPTION The .Tn IPMI (Intelligent Platform Management Interface) is a standard for monitoring system hardware by permitting generic code to detect and monitor the sensors in a system. The .Tn IPMI standard offers watchdog support, an FRU database, and other support extensions. It is currently being adopted by the makers of many single board and embedded system manufacturers. .Pp The .Nm driver in .Fx is heavily adopted from the standard and .Tn Linux driver; however, not all features described in the standard are supported. +.Pp +The +.Nm +driver implements the power cycling option to +.Xr shutdown 8 +to implement power cycling of the system. +The motherboard's BMC must support the chassis device and the optional +power cycle subcomand of the chassis control command as described in section 28.3 +if the IPMI standard. +The length of time the system is off will be at least one second, but +may be longer if the power cycle interval has been set (see section 28.9). .Sh IOCTLS Sending and receiving messages through the .Nm driver requires the use of .Xr ioctl 2 . The ioctls are used due to the complexity of data sent to and from the device. The .Xr ioctl 2 command codes below are defined in .In sys/ipmi.h . The third argument to .Xr ioctl 2 should be a pointer to the type indicated. .Pp Currently the following ioctls are supported: .Bl -tag -width indent .It Dv IPMICTL_RECEIVE_MSG Pq Vt "struct ipmi_recv" Receive a message. Possible error values: .Bl -tag -width Er .It Bq Er EAGAIN No messages are in the process queue. .It Bq Er EFAULT An address supplied was invalid. .It Bq Er EMSGSIZE The address could not fit in the message buffer and will remain in the buffer. .El .It Dv IPMICTL_RECEIVE_MSG_TRUNC Pq Vt "struct ipmi_recv" Like .Dv 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. .It Dv IPMICTL_SEND_COMMAND Pq Vt "struct ipmi_req" Send a message to the interface. Possible error values: .Bl -tag -width Er .It Bq Er EFAULT An address supplied was invalid. .It Bq Er ENOMEM Buffers could not be allowed for the command, out of memory. .El .It Dv IPMICTL_SET_MY_ADDRESS_CMD Pq Vt "unsigned int" Set the slave address for source messages. .It Dv IPMICTL_GET_MY_ADDRESS_CMD Pq Vt "unsigned int" Get the slave address for source messages. .It Dv IPMICTL_SET_MY_LUN_CMD Pq Vt "unsigned int" Set the slave LUN for source messages. .It Dv IPMICTL_GET_MY_LUN_CMD Pq Vt "unsigned int" Get the slave LUN for source messages. .El .Ss Unimplemented Ioctls .Bl -tag -width indent .It Dv IPMICTL_REGISTER_FOR_CMD Pq Vt "struct ipmi_cmdspec" Register to receive a specific command. Possible error values: .Bl -tag -width Er .It Bq Er EFAULT An supplied address was invalid. .It Bq Er EBUSY The network function/command is already in use. .It Bq Er ENOMEM Could not allocate memory. .El .It Dv IPMICTL_UNREGISTER_FOR_CMD Pq Vt "struct ipmi_cmdspec" Unregister to receive a specific command. Possible error values: .Bl -tag -width Er .It Bq Er EFAULT An address supplied was invalid. .It Bq Er ENOENT The network function/command was not found. .El .El .Ss Stub Only Ioctl .Bl -tag -width indent .It Dv IPMICTL_SET_GETS_EVENTS_CMD Pq Vt int Set whether this interface receives events. Possible error values: .Bl -tag -width Er .It Bq Er EFAULT An address supplied was invalid. .El .El .Sh SEE ALSO .Xr ioctl 2 , .Xr watchdog 4 , +.Xr reboot 8 , +.Xr shutdown 8 , .Xr watchdog 8 , .Xr watchdogd 8 , .Xr watchdog 9 .Sh HISTORY The .Nm driver first appeared in .Fx 6.2 . .Sh AUTHORS .An -nosplit The .Nm driver was written by .An Doug Ambrisko Aq Mt ambrisko@FreeBSD.org . This manual page was written by .An Tom Rhodes Aq Mt trhodes@FreeBSD.org . .Sh BUGS Not all features of the MontaVista driver are supported. .Pp Currently, IPMB and BT modes are not implemented. Index: head/sys/dev/ipmi/ipmi.c =================================================================== --- head/sys/dev/ipmi/ipmi.c (revision 324989) +++ head/sys/dev/ipmi/ipmi.c (revision 324990) @@ -1,940 +1,997 @@ /*- * Copyright (c) 2006 IronPort Systems Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #ifdef LOCAL_MODULE #include #include #else #include #include #endif /* * Driver request structures are allocated on the stack via alloca() to * avoid calling malloc(), especially for the watchdog handler. * To avoid too much stack growth, a previously allocated structure can * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure * that there is adequate reply/request space in the original allocation. */ #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ bzero((req), sizeof(struct ipmi_request)); \ ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ (reqlen) + (replylen)); \ IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ (replylen)) #ifdef IPMB static int ipmi_ipmb_checksum(u_char, int); static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, u_char, u_char, int) #endif static d_ioctl_t ipmi_ioctl; static d_poll_t ipmi_poll; static d_open_t ipmi_open; static void ipmi_dtor(void *arg); int ipmi_attached = 0; static int on = 1; static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters"); SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW, &on, 0, ""); static struct cdevsw ipmi_cdevsw = { .d_version = D_VERSION, .d_open = ipmi_open, .d_ioctl = ipmi_ioctl, .d_poll = ipmi_poll, .d_name = "ipmi", }; static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); static int ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) { struct ipmi_device *dev; struct ipmi_softc *sc; int error; if (!on) return (ENOENT); /* Initialize the per file descriptor data. */ dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); error = devfs_set_cdevpriv(dev, ipmi_dtor); if (error) { free(dev, M_IPMI); return (error); } sc = cdev->si_drv1; TAILQ_INIT(&dev->ipmi_completed_requests); dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; dev->ipmi_lun = IPMI_BMC_SMS_LUN; dev->ipmi_softc = sc; IPMI_LOCK(sc); sc->ipmi_opened++; IPMI_UNLOCK(sc); return (0); } static int ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) { struct ipmi_device *dev; struct ipmi_softc *sc; int revents = 0; if (devfs_get_cdevpriv((void **)&dev)) return (0); sc = cdev->si_drv1; IPMI_LOCK(sc); if (poll_events & (POLLIN | POLLRDNORM)) { if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) revents |= poll_events & (POLLIN | POLLRDNORM); if (dev->ipmi_requests == 0) revents |= POLLERR; } if (revents == 0) { if (poll_events & (POLLIN | POLLRDNORM)) selrecord(td, &dev->ipmi_select); } IPMI_UNLOCK(sc); return (revents); } static void ipmi_purge_completed_requests(struct ipmi_device *dev) { struct ipmi_request *req; while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { req = TAILQ_FIRST(&dev->ipmi_completed_requests); TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); dev->ipmi_requests--; ipmi_free_request(req); } } static void ipmi_dtor(void *arg) { struct ipmi_request *req, *nreq; struct ipmi_device *dev; struct ipmi_softc *sc; dev = arg; sc = dev->ipmi_softc; IPMI_LOCK(sc); if (dev->ipmi_requests) { /* Throw away any pending requests for this device. */ TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, nreq) { if (req->ir_owner == dev) { TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); dev->ipmi_requests--; ipmi_free_request(req); } } /* Throw away any pending completed requests for this device. */ ipmi_purge_completed_requests(dev); /* * If we still have outstanding requests, they must be stuck * in an interface driver, so wait for those to drain. */ dev->ipmi_closing = 1; while (dev->ipmi_requests > 0) { msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, PWAIT, "ipmidrain", 0); ipmi_purge_completed_requests(dev); } } sc->ipmi_opened--; IPMI_UNLOCK(sc); /* Cleanup. */ free(dev, M_IPMI); } #ifdef IPMB static int ipmi_ipmb_checksum(u_char *data, int len) { u_char sum = 0; for (; len; len--) { sum += *data++; } return (-sum); } /* XXX: Needs work */ static int ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, u_char command, u_char seq, u_char *data, int data_len) { struct ipmi_softc *sc = device_get_softc(dev); struct ipmi_request *req; u_char slave_addr = 0x52; int error; IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_SEND_MSG, data_len + 8, 0); req->ir_request[0] = channel; req->ir_request[1] = slave_addr; req->ir_request[2] = IPMI_ADDR(netfn, 0); req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); req->ir_request[4] = sc->ipmi_address; req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); req->ir_request[6] = command; bcopy(data, &req->ir_request[7], data_len); temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], data_len + 3); ipmi_submit_driver_request(sc, req); error = req->ir_error; return (error); } static int ipmi_handle_attn(struct ipmi_softc *sc) { struct ipmi_request *req; int error; device_printf(sc->ipmi_dev, "BMC has a message\n"); IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG_FLAGS, 0, 1); ipmi_submit_driver_request(sc, req); if (req->ir_error == 0 && req->ir_compcode == 0) { if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { device_printf(sc->ipmi_dev, "message buffer full"); } if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { device_printf(sc->ipmi_dev, "watchdog about to go off"); } if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 16); device_printf(sc->ipmi_dev, "throw out message "); dump_buf(temp, 16); } } error = req->ir_error; return (error); } #endif #ifdef IPMICTL_SEND_COMMAND_32 #define PTRIN(p) ((void *)(uintptr_t)(p)) #define PTROUT(p) ((uintptr_t)(p)) #endif static int ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct ipmi_softc *sc; struct ipmi_device *dev; struct ipmi_request *kreq; struct ipmi_req *req = (struct ipmi_req *)data; struct ipmi_recv *recv = (struct ipmi_recv *)data; struct ipmi_addr addr; #ifdef IPMICTL_SEND_COMMAND_32 struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; union { struct ipmi_req req; struct ipmi_recv recv; } thunk32; #endif int error, len; error = devfs_get_cdevpriv((void **)&dev); if (error) return (error); sc = cdev->si_drv1; #ifdef IPMICTL_SEND_COMMAND_32 /* Convert 32-bit structures to native. */ switch (cmd) { case IPMICTL_SEND_COMMAND_32: req = &thunk32.req; req->addr = PTRIN(req32->addr); req->addr_len = req32->addr_len; req->msgid = req32->msgid; req->msg.netfn = req32->msg.netfn; req->msg.cmd = req32->msg.cmd; req->msg.data_len = req32->msg.data_len; req->msg.data = PTRIN(req32->msg.data); break; case IPMICTL_RECEIVE_MSG_TRUNC_32: case IPMICTL_RECEIVE_MSG_32: recv = &thunk32.recv; recv->addr = PTRIN(recv32->addr); recv->addr_len = recv32->addr_len; recv->msg.data_len = recv32->msg.data_len; recv->msg.data = PTRIN(recv32->msg.data); break; } #endif switch (cmd) { #ifdef IPMICTL_SEND_COMMAND_32 case IPMICTL_SEND_COMMAND_32: #endif case IPMICTL_SEND_COMMAND: /* * XXX: Need to add proper handling of this. */ error = copyin(req->addr, &addr, sizeof(addr)); if (error) return (error); IPMI_LOCK(sc); /* clear out old stuff in queue of stuff done */ /* XXX: This seems odd. */ while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); dev->ipmi_requests--; ipmi_free_request(kreq); } IPMI_UNLOCK(sc); kreq = ipmi_alloc_request(dev, req->msgid, IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, req->msg.data_len, IPMI_MAX_RX); error = copyin(req->msg.data, kreq->ir_request, req->msg.data_len); if (error) { ipmi_free_request(kreq); return (error); } IPMI_LOCK(sc); dev->ipmi_requests++; error = sc->ipmi_enqueue_request(sc, kreq); IPMI_UNLOCK(sc); if (error) return (error); break; #ifdef IPMICTL_SEND_COMMAND_32 case IPMICTL_RECEIVE_MSG_TRUNC_32: case IPMICTL_RECEIVE_MSG_32: #endif case IPMICTL_RECEIVE_MSG_TRUNC: case IPMICTL_RECEIVE_MSG: error = copyin(recv->addr, &addr, sizeof(addr)); if (error) return (error); IPMI_LOCK(sc); kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); if (kreq == NULL) { IPMI_UNLOCK(sc); return (EAGAIN); } addr.channel = IPMI_BMC_CHANNEL; /* XXX */ recv->recv_type = IPMI_RESPONSE_RECV_TYPE; recv->msgid = kreq->ir_msgid; recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; recv->msg.cmd = kreq->ir_command; error = kreq->ir_error; if (error) { TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); dev->ipmi_requests--; IPMI_UNLOCK(sc); ipmi_free_request(kreq); return (error); } len = kreq->ir_replylen + 1; if (recv->msg.data_len < len && (cmd == IPMICTL_RECEIVE_MSG #ifdef IPMICTL_RECEIVE_MSG_32 || cmd == IPMICTL_RECEIVE_MSG_32 #endif )) { IPMI_UNLOCK(sc); return (EMSGSIZE); } TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); dev->ipmi_requests--; IPMI_UNLOCK(sc); len = min(recv->msg.data_len, len); recv->msg.data_len = len; error = copyout(&addr, recv->addr,sizeof(addr)); if (error == 0) error = copyout(&kreq->ir_compcode, recv->msg.data, 1); if (error == 0) error = copyout(kreq->ir_reply, recv->msg.data + 1, len - 1); ipmi_free_request(kreq); if (error) return (error); break; case IPMICTL_SET_MY_ADDRESS_CMD: 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: /* device_printf(sc->ipmi_dev, "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); */ break; case IPMICTL_REGISTER_FOR_CMD: case IPMICTL_UNREGISTER_FOR_CMD: return (EOPNOTSUPP); default: device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); return (ENOIOCTL); } #ifdef IPMICTL_SEND_COMMAND_32 /* Update changed fields in 32-bit structures. */ switch (cmd) { case IPMICTL_RECEIVE_MSG_TRUNC_32: case IPMICTL_RECEIVE_MSG_32: recv32->recv_type = recv->recv_type; recv32->msgid = recv->msgid; recv32->msg.netfn = recv->msg.netfn; recv32->msg.cmd = recv->msg.cmd; recv32->msg.data_len = recv->msg.data_len; break; } #endif return (0); } /* * Request management. */ static __inline void ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) { req->ir_owner = dev; req->ir_msgid = msgid; req->ir_addr = addr; req->ir_command = command; if (requestlen) { req->ir_request = (char *)&req[1]; req->ir_requestlen = requestlen; } if (replylen) { req->ir_reply = (char *)&req[1] + requestlen; req->ir_replybuflen = replylen; } } /* Allocate a new request with request and reply buffers. */ struct ipmi_request * ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) { struct ipmi_request *req; req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, M_IPMI, M_WAITOK | M_ZERO); ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); return (req); } /* Free a request no longer in use. */ void ipmi_free_request(struct ipmi_request *req) { free(req, M_IPMI); } /* Store a processed request on the appropriate completion queue. */ void ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) { struct ipmi_device *dev; IPMI_LOCK_ASSERT(sc); /* * Anonymous requests (from inside the driver) always have a * waiter that we awaken. */ if (req->ir_owner == NULL) wakeup(req); else { dev = req->ir_owner; TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); selwakeup(&dev->ipmi_select); if (dev->ipmi_closing) wakeup(&dev->ipmi_requests); } } /* Perform an internal driver request. */ int ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo) { return (sc->ipmi_driver_request(sc, req, timo)); } /* * 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_requests_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); } /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 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); } /* * Watchdog event handler. */ static int ipmi_reset_watchdog(struct ipmi_softc *sc) { struct ipmi_request *req; int error; IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_RESET_WDOG, 0, 0); error = ipmi_submit_driver_request(sc, req, 0); if (error) device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); return (error); } static int ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) { struct ipmi_request *req; int error; if (sec > 0xffff / 10) return (EINVAL); IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_SET_WDOG, 6, 0); if (sec) { req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP | IPMI_SET_WD_TIMER_SMS_OS; req->ir_request[1] = IPMI_SET_WD_ACTION_RESET; req->ir_request[2] = 0; req->ir_request[3] = 0; /* Timer use */ req->ir_request[4] = (sec * 10) & 0xff; req->ir_request[5] = (sec * 10) >> 8; } else { req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; req->ir_request[1] = 0; req->ir_request[2] = 0; req->ir_request[3] = 0; /* Timer use */ req->ir_request[4] = 0; req->ir_request[5] = 0; } error = ipmi_submit_driver_request(sc, req, 0); if (error) device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); return (error); } static void ipmi_wd_event(void *arg, unsigned int cmd, int *error) { struct ipmi_softc *sc = arg; unsigned int timeout; int e; if (dumping) return; cmd &= WD_INTERVAL; if (cmd > 0 && cmd <= 63) { timeout = ((uint64_t)1 << cmd) / 1000000000; if (timeout == 0) timeout = 1; if (timeout != sc->ipmi_watchdog_active) { e = ipmi_set_watchdog(sc, timeout); if (e == 0) { sc->ipmi_watchdog_active = timeout; } else { (void)ipmi_set_watchdog(sc, 0); sc->ipmi_watchdog_active = 0; } } if (sc->ipmi_watchdog_active != 0) { e = ipmi_reset_watchdog(sc); if (e == 0) { *error = 0; } else { (void)ipmi_set_watchdog(sc, 0); sc->ipmi_watchdog_active = 0; } } } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { e = ipmi_set_watchdog(sc, 0); if (e != 0 && cmd == 0) *error = EOPNOTSUPP; } } static void +ipmi_power_cycle(void *arg, int howto) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + + /* + * Ignore everything except power cycling requests + */ + if ((howto & RB_POWERCYCLE) == 0) + return; + + device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); + + /* + * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 + * as described in IPMI v2.0 spec section 28.3. + */ + IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), + IPMI_CHASSIS_CONTROL, 1, 0); + req->ir_request[0] = IPMI_CC_POWER_CYCLE; + + ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); + + if (req->ir_error != 0 || req->ir_compcode != 0) { + device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", + req->ir_error, req->ir_compcode); + return; + } + + /* + * BMCs are notoriously slow, give it up to 10s to effect the power + * down leg of the power cycle. If that fails, fallback to the next + * hanlder in the shutdown_final chain and/or the platform failsafe. + */ + DELAY(10 * 1000 * 1000); + device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); +} + +static void ipmi_startup(void *arg) { struct ipmi_softc *sc = arg; struct ipmi_request *req; device_t dev; int error, i; config_intrhook_disestablish(&sc->ipmi_ich); dev = sc->ipmi_dev; /* Initialize interface-independent state. */ mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); cv_init(&sc->ipmi_request_added, "ipmireq"); TAILQ_INIT(&sc->ipmi_pending_requests); /* Initialize interface-dependent state. */ error = sc->ipmi_startup(sc); if (error) { device_printf(dev, "Failed to initialize interface: %d\n", error); return; } /* Send a GET_DEVICE_ID request. */ IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_DEVICE_ID, 0, 15); error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); if (error == EWOULDBLOCK) { device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); return; } else if (error) { device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); return; } else if (req->ir_compcode != 0) { device_printf(dev, "Bad completion code for GET_DEVICE_ID: %d\n", req->ir_compcode); return; } else if (req->ir_replylen < 5) { device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", req->ir_replylen); return; } device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " - "version %d.%d\n", - 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); + "version %d.%d, device support mask %#x\n", + 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, req->ir_reply[5]); + sc->ipmi_dev_support = req->ir_reply[5]; + IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_CLEAR_FLAGS, 1, 0); ipmi_submit_driver_request(sc, req, 0); /* XXX: Magic numbers */ if (req->ir_compcode == 0xc0) { device_printf(dev, "Clear flags is busy\n"); } if (req->ir_compcode == 0xc1) { device_printf(dev, "Clear flags illegal\n"); } for (i = 0; i < 8; i++) { IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_CHANNEL_INFO, 1, 0); req->ir_request[0] = i; ipmi_submit_driver_request(sc, req, 0); if (req->ir_compcode != 0) break; } device_printf(dev, "Number of channels %d\n", i); /* * Probe for watchdog, but only for backends which support * polled driver requests. */ if (sc->ipmi_driver_requests_polled) { IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_WDOG, 0, 0); ipmi_submit_driver_request(sc, req, 0); if (req->ir_compcode == 0x00) { device_printf(dev, "Attached watchdog\n"); /* register the watchdog event handler */ sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( watchdog_list, ipmi_wd_event, sc, 0); } } sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); if (sc->ipmi_cdev == NULL) { device_printf(dev, "Failed to create cdev\n"); return; } sc->ipmi_cdev->si_drv1 = sc; + + /* + * Power cycle the system off using IPMI. We use last - 1 since we don't + * handle all the other kinds of reboots. We'll let others handle them. + * We only try to do this if the BMC supports the Chassis device. + */ + if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { + device_printf(dev, "Establishing power cycle handler\n"); + sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, + ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 1); + } } int ipmi_attach(device_t dev) { struct ipmi_softc *sc = device_get_softc(dev); int error; if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); if (error) { device_printf(dev, "can't set up interrupt\n"); return (error); } } bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); sc->ipmi_ich.ich_func = ipmi_startup; sc->ipmi_ich.ich_arg = sc; if (config_intrhook_establish(&sc->ipmi_ich) != 0) { device_printf(dev, "can't establish configuration hook\n"); return (ENOMEM); } ipmi_attached = 1; return (0); } int ipmi_detach(device_t dev) { struct ipmi_softc *sc; sc = device_get_softc(dev); /* Fail if there are any open handles. */ IPMI_LOCK(sc); if (sc->ipmi_opened) { IPMI_UNLOCK(sc); return (EBUSY); } IPMI_UNLOCK(sc); if (sc->ipmi_cdev) destroy_dev(sc->ipmi_cdev); /* Detach from watchdog handling and turn off watchdog. */ if (sc->ipmi_watchdog_tag) { EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); ipmi_set_watchdog(sc, 0); } + + /* Detach from shutdown handling for power cycle reboot */ + if (sc->ipmi_power_cycle_tag) + EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); /* XXX: should use shutdown callout I think. */ /* If the backend uses a kthread, shut it down. */ IPMI_LOCK(sc); sc->ipmi_detaching = 1; if (sc->ipmi_kthread) { cv_broadcast(&sc->ipmi_request_added); msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, "ipmi_wait", 0); } IPMI_UNLOCK(sc); if (sc->ipmi_irq) bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); ipmi_release_resources(dev); mtx_destroy(&sc->ipmi_io_lock); mtx_destroy(&sc->ipmi_requests_lock); return (0); } void ipmi_release_resources(device_t dev) { struct ipmi_softc *sc; int i; sc = device_get_softc(dev); if (sc->ipmi_irq) bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); if (sc->ipmi_irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, sc->ipmi_irq_res); for (i = 0; i < MAX_RES; i++) if (sc->ipmi_io_res[i]) bus_release_resource(dev, sc->ipmi_io_type, sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); } devclass_t ipmi_devclass; /* XXX: Why? */ static void ipmi_unload(void *arg) { device_t * devs; int count; int i; if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) return; for (i = 0; i < count; i++) device_delete_child(device_get_parent(devs[i]), devs[i]); free(devs, M_TEMP); } SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); #ifdef IMPI_DEBUG static void dump_buf(u_char *data, int len) { char buf[20]; char line[1024]; char temp[30]; int count = 0; int i=0; printf("Address %p len %d\n", data, len); if (len > 256) len = 256; line[0] = '\000'; for (; len > 0; len--, data++) { sprintf(temp, "%02x ", *data); strcat(line, temp); if (*data >= ' ' && *data <= '~') buf[count] = *data; else if (*data >= 'A' && *data <= 'Z') buf[count] = *data; else buf[count] = '.'; if (++count == 16) { buf[count] = '\000'; count = 0; printf(" %3x %s %s\n", i, line, buf); i+=16; line[0] = '\000'; } } buf[count] = '\000'; for (; count != 16; count++) { strcat(line, " "); } printf(" %3x %s %s\n", i, line, buf); } #endif Index: head/sys/dev/ipmi/ipmivars.h =================================================================== --- head/sys/dev/ipmi/ipmivars.h (revision 324989) +++ head/sys/dev/ipmi/ipmivars.h (revision 324990) @@ -1,254 +1,256 @@ /*- * Copyright (c) 2006 IronPort Systems Inc. * 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$ */ #ifndef __IPMIVARS_H__ #define __IPMIVARS_H__ struct ipmi_get_info { int iface_type; uint64_t address; int offset; int io_mode; int irq; }; struct ipmi_device; struct ipmi_request { TAILQ_ENTRY(ipmi_request) ir_link; struct ipmi_device *ir_owner; /* Driver uses NULL. */ u_char *ir_request; /* Request is data to send to BMC. */ size_t ir_requestlen; u_char *ir_reply; /* Reply is data read from BMC. */ size_t ir_replybuflen; /* Length of ir_reply[] buffer. */ int ir_replylen; /* Length of reply from BMC. */ int ir_error; long ir_msgid; uint8_t ir_addr; uint8_t ir_command; uint8_t ir_compcode; }; #define MAX_RES 3 #define KCS_DATA 0 #define KCS_CTL_STS 1 #define SMIC_DATA 0 #define SMIC_CTL_STS 1 #define SMIC_FLAGS 2 struct ipmi_softc; /* Per file descriptor data. */ struct ipmi_device { TAILQ_ENTRY(ipmi_device) ipmi_link; TAILQ_HEAD(,ipmi_request) ipmi_completed_requests; struct selinfo ipmi_select; struct ipmi_softc *ipmi_softc; int ipmi_closing; int ipmi_requests; u_char ipmi_address; /* IPMB address. */ u_char ipmi_lun; }; struct ipmi_kcs { }; struct ipmi_smic { }; struct ipmi_ssif { device_t smbus; int smbus_address; }; struct ipmi_softc { device_t ipmi_dev; union { struct ipmi_kcs kcs; struct ipmi_smic smic; struct ipmi_ssif ssif; } _iface; int ipmi_io_rid; int ipmi_io_type; struct mtx ipmi_io_lock; struct resource *ipmi_io_res[MAX_RES]; int ipmi_io_spacing; int ipmi_irq_rid; struct resource *ipmi_irq_res; void *ipmi_irq; int ipmi_detaching; int ipmi_opened; + uint8_t ipmi_dev_support; /* IPMI_ADS_* */ struct cdev *ipmi_cdev; TAILQ_HEAD(,ipmi_request) ipmi_pending_requests; int ipmi_driver_requests_polled; + eventhandler_tag ipmi_power_cycle_tag; eventhandler_tag ipmi_watchdog_tag; int ipmi_watchdog_active; struct intr_config_hook ipmi_ich; struct mtx ipmi_requests_lock; struct cv ipmi_request_added; struct proc *ipmi_kthread; driver_intr_t *ipmi_intr; int (*ipmi_startup)(struct ipmi_softc *); int (*ipmi_enqueue_request)(struct ipmi_softc *, struct ipmi_request *); int (*ipmi_driver_request)(struct ipmi_softc *, struct ipmi_request *, int); }; #define ipmi_ssif_smbus_address _iface.ssif.smbus_address #define ipmi_ssif_smbus _iface.ssif.smbus struct ipmi_ipmb { u_char foo; }; #define KCS_MODE 0x01 #define SMIC_MODE 0x02 #define BT_MODE 0x03 #define SSIF_MODE 0x04 /* KCS status flags */ #define KCS_STATUS_OBF 0x01 /* Data Out ready from BMC */ #define KCS_STATUS_IBF 0x02 /* Data In from System */ #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) mtx_lock(&(sc)->ipmi_requests_lock) #define IPMI_UNLOCK(sc) mtx_unlock(&(sc)->ipmi_requests_lock) #define IPMI_LOCK_ASSERT(sc) mtx_assert(&(sc)->ipmi_requests_lock, MA_OWNED) #define IPMI_IO_LOCK(sc) mtx_lock(&(sc)->ipmi_io_lock) #define IPMI_IO_UNLOCK(sc) mtx_unlock(&(sc)->ipmi_io_lock) #define IPMI_IO_LOCK_ASSERT(sc) mtx_assert(&(sc)->ipmi_io_lock, MA_OWNED) /* I/O to a single I/O resource. */ #define INB_SINGLE(sc, x) \ bus_read_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x)) #define OUTB_SINGLE(sc, x, value) \ bus_write_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x), value) /* I/O with each register in its in I/O resource. */ #define INB_MULTIPLE(sc, x) \ bus_read_1((sc)->ipmi_io_res[(x)], 0) #define OUTB_MULTIPLE(sc, x, value) \ bus_write_1((sc)->ipmi_io_res[(x)], 0, value) /* * Determine I/O method based on whether or not we have more than one I/O * resource. */ #define INB(sc, x) \ ((sc)->ipmi_io_res[1] != NULL ? INB_MULTIPLE(sc, x) : INB_SINGLE(sc, x)) #define OUTB(sc, x, value) \ ((sc)->ipmi_io_res[1] != NULL ? OUTB_MULTIPLE(sc, x, value) : \ OUTB_SINGLE(sc, x, value)) #define MAX_TIMEOUT 6 * hz int ipmi_attach(device_t); int ipmi_detach(device_t); void ipmi_release_resources(device_t); /* Manage requests. */ struct ipmi_request *ipmi_alloc_request(struct ipmi_device *, long, uint8_t, uint8_t, size_t, size_t); void ipmi_complete_request(struct ipmi_softc *, struct ipmi_request *); struct ipmi_request *ipmi_dequeue_request(struct ipmi_softc *); void ipmi_free_request(struct ipmi_request *); int ipmi_polled_enqueue_request(struct ipmi_softc *, struct ipmi_request *); int ipmi_submit_driver_request(struct ipmi_softc *, struct ipmi_request *, int); /* Identify BMC interface via SMBIOS. */ int ipmi_smbios_identify(struct ipmi_get_info *); /* Match BMC PCI device listed in SMBIOS. */ const char *ipmi_pci_match(uint16_t, uint16_t); /* Interface attach routines. */ int ipmi_kcs_attach(struct ipmi_softc *); int ipmi_kcs_probe_align(struct ipmi_softc *); int ipmi_smic_attach(struct ipmi_softc *); int ipmi_ssif_attach(struct ipmi_softc *, device_t, int); #ifdef IPMB int ipmi_handle_attn(struct ipmi_softc *); #endif extern devclass_t ipmi_devclass; extern int ipmi_attached; #endif /* !__IPMIVARS_H__ */ Index: head/sys/sys/ipmi.h =================================================================== --- head/sys/sys/ipmi.h (revision 324989) +++ head/sys/sys/ipmi.h (revision 324990) @@ -1,155 +1,172 @@ /*- * Copyright (c) 2006 IronPort Systems Inc. * 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$ */ #ifndef __SYS_IPMI_H__ #define __SYS_IPMI_H__ #define IPMI_MAX_ADDR_SIZE 0x20 #define IPMI_MAX_RX 1024 #define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */ #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_CHASSIS_REQUEST 0x00 +# define IPMI_CHASSIS_CONTROL 0x02 +# define IPMI_CC_POWER_DOWN 0x0 +# define IPMI_CC_POWER_UP 0x1 +# define IPMI_CC_POWER_CYCLE 0x2 +# define IPMI_CC_HARD_RESET 0x3 +# define IPMI_CC_PULSE_DI 0x4 +# define IPMI_CC_SOFT_OVERTEMP 0x5 + #define IPMI_APP_REQUEST 0x06 #define IPMI_GET_DEVICE_ID 0x01 +# define IPMI_ADS_CHASSIS 0x80 +# define IPMI_ADS_BRIDGE 0x40 +# define IPMI_ADS_EVENT_GEN 0x20 +# define IPMI_ADS_EVENT_RCV 0x10 +# define IPMI_ADS_FRU_INV 0x08 +# define IPMI_ADS_SEL 0x04 +# define IPMI_ADS_SDR 0x02 +# define IPMI_ADS_SENSOR 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; }; #if defined(__amd64__) /* Compatibility with 32-bit binaries. */ #define IPMICTL_RECEIVE_MSG_TRUNC_32 _IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv32) #define IPMICTL_RECEIVE_MSG_32 _IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv32) #define IPMICTL_SEND_COMMAND_32 _IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req32) struct ipmi_msg32 { unsigned char netfn; unsigned char cmd; unsigned short data_len; uint32_t data; }; struct ipmi_req32 { uint32_t addr; unsigned int addr_len; int32_t msgid; struct ipmi_msg32 msg; }; struct ipmi_recv32 { int recv_type; uint32_t addr; unsigned int addr_len; int32_t msgid; struct ipmi_msg32 msg; }; #endif #endif /* !__SYS_IPMI_H__ */