ipmi.c revision 182322
190075Sobrien/*- 290075Sobrien * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3169699Skan * All rights reserved. 490075Sobrien * 590075Sobrien * Redistribution and use in source and binary forms, with or without 690075Sobrien * modification, are permitted provided that the following conditions 790075Sobrien * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 990075Sobrien * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1190075Sobrien * notice, this list of conditions and the following disclaimer in the 1290075Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1790075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169699Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169699Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2190075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2290075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2390075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2490075Sobrien * SUCH DAMAGE. 2590075Sobrien */ 2690075Sobrien 27132729Skan#include <sys/cdefs.h> 2890075Sobrien__FBSDID("$FreeBSD: head/sys/dev/ipmi/ipmi.c 182322 2008-08-28 02:13:53Z jhb $"); 2990075Sobrien 30169699Skan#include <sys/param.h> 3190075Sobrien#include <sys/systm.h> 3290075Sobrien#include <sys/bus.h> 3390075Sobrien#include <sys/condvar.h> 34169699Skan#include <sys/conf.h> 35169699Skan#include <sys/kernel.h> 36169699Skan#include <sys/malloc.h> 37169699Skan#include <sys/module.h> 3890075Sobrien#include <sys/poll.h> 3990075Sobrien#include <sys/rman.h> 4090075Sobrien#include <sys/selinfo.h> 41117406Skan#include <sys/sysctl.h> 4290075Sobrien#include <sys/watchdog.h> 4390075Sobrien 4490075Sobrien#ifdef LOCAL_MODULE 4590075Sobrien#include <ipmi.h> 4690075Sobrien#include <ipmivars.h> 4790075Sobrien#else 4890075Sobrien#include <sys/ipmi.h> 4990075Sobrien#include <dev/ipmi/ipmivars.h> 5090075Sobrien#endif 5190075Sobrien 5290075Sobrien#ifdef IPMB 5390075Sobrienstatic int ipmi_ipmb_checksum(u_char, int); 5490075Sobrienstatic int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, 5590075Sobrien u_char, u_char, int) 56132729Skan#endif 5790075Sobrien 5890075Sobrienstatic d_ioctl_t ipmi_ioctl; 5990075Sobrienstatic d_poll_t ipmi_poll; 6090075Sobrienstatic d_open_t ipmi_open; 6190075Sobrienstatic void ipmi_dtor(void *arg); 6290075Sobrien 6390075Sobrienint ipmi_attached = 0; 6490075Sobrien 6590075Sobrienstatic int on = 1; 6690075SobrienSYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters"); 6790075SobrienSYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW, 6890075Sobrien &on, 0, ""); 6990075Sobrien 7090075Sobrienstatic struct cdevsw ipmi_cdevsw = { 7190075Sobrien .d_version = D_VERSION, 7290075Sobrien .d_open = ipmi_open, 73169699Skan .d_ioctl = ipmi_ioctl, 74169699Skan .d_poll = ipmi_poll, 75169699Skan .d_name = "ipmi", 7690075Sobrien}; 7790075Sobrien 7890075SobrienMALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 7990075Sobrien 8090075Sobrienstatic int 8190075Sobrienipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 8290075Sobrien{ 8390075Sobrien struct ipmi_device *dev; 8490075Sobrien struct ipmi_softc *sc; 85169699Skan int error; 86169699Skan 87169699Skan if (!on) 88132729Skan return (ENOENT); 8990075Sobrien 9090075Sobrien /* Initialize the per file descriptor data. */ 9190075Sobrien dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 9290075Sobrien error = devfs_set_cdevpriv(dev, ipmi_dtor); 93117406Skan if (error) { 94169699Skan free(dev, M_IPMI); 9590075Sobrien return (error); 9690075Sobrien } 97169699Skan 98132729Skan sc = cdev->si_drv1; 99132729Skan TAILQ_INIT(&dev->ipmi_completed_requests); 100132729Skan dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 10190075Sobrien dev->ipmi_lun = IPMI_BMC_SMS_LUN; 10290075Sobrien dev->ipmi_softc = sc; 10390075Sobrien IPMI_LOCK(sc); 10490075Sobrien sc->ipmi_opened++; 10590075Sobrien IPMI_UNLOCK(sc); 10690075Sobrien 10790075Sobrien return (0); 10890075Sobrien} 10990075Sobrien 11090075Sobrienstatic int 11190075Sobrienipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 11290075Sobrien{ 11390075Sobrien struct ipmi_device *dev; 11490075Sobrien struct ipmi_softc *sc; 115169699Skan int revents = 0; 116169699Skan 11790075Sobrien if (devfs_get_cdevpriv((void **)&dev)) 11890075Sobrien return (0); 119169699Skan 120169699Skan sc = cdev->si_drv1; 12190075Sobrien IPMI_LOCK(sc); 12290075Sobrien if (poll_events & (POLLIN | POLLRDNORM)) { 12390075Sobrien if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 124169699Skan revents |= poll_events & (POLLIN | POLLRDNORM); 12590075Sobrien if (dev->ipmi_requests == 0) 12690075Sobrien revents |= POLLERR; 12790075Sobrien } 12890075Sobrien 12990075Sobrien if (revents == 0) { 13090075Sobrien if (poll_events & (POLLIN | POLLRDNORM)) 13190075Sobrien selrecord(td, &dev->ipmi_select); 13290075Sobrien } 13390075Sobrien IPMI_UNLOCK(sc); 13490075Sobrien 13590075Sobrien return (revents); 13690075Sobrien} 13790075Sobrien 13890075Sobrienstatic void 13990075Sobrienipmi_purge_completed_requests(struct ipmi_device *dev) 14090075Sobrien{ 14190075Sobrien struct ipmi_request *req; 14290075Sobrien 14390075Sobrien while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 14490075Sobrien req = TAILQ_FIRST(&dev->ipmi_completed_requests); 14590075Sobrien TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 14690075Sobrien dev->ipmi_requests--; 14790075Sobrien ipmi_free_request(req); 148132729Skan } 149132729Skan} 150132729Skan 151132729Skanstatic void 15290075Sobrienipmi_dtor(void *arg) 15390075Sobrien{ 15490075Sobrien struct ipmi_request *req, *nreq; 15590075Sobrien struct ipmi_device *dev; 15690075Sobrien struct ipmi_softc *sc; 15790075Sobrien 15890075Sobrien dev = arg; 159169699Skan sc = dev->ipmi_softc; 16090075Sobrien 16190075Sobrien IPMI_LOCK(sc); 162169699Skan if (dev->ipmi_requests) { 163169699Skan /* Throw away any pending requests for this device. */ 16490075Sobrien TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 16590075Sobrien nreq) { 16690075Sobrien if (req->ir_owner == dev) { 16790075Sobrien TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 16890075Sobrien ir_link); 16990075Sobrien dev->ipmi_requests--; 17090075Sobrien ipmi_free_request(req); 171117406Skan } 17290075Sobrien } 17390075Sobrien 174132729Skan /* Throw away any pending completed requests for this device. */ 17590075Sobrien ipmi_purge_completed_requests(dev); 17690075Sobrien 17790075Sobrien /* 17890075Sobrien * If we still have outstanding requests, they must be stuck 17990075Sobrien * in an interface driver, so wait for those to drain. 18090075Sobrien */ 18190075Sobrien dev->ipmi_closing = 1; 18290075Sobrien while (dev->ipmi_requests > 0) { 18390075Sobrien msleep(&dev->ipmi_requests, &sc->ipmi_lock, PWAIT, 18490075Sobrien "ipmidrain", 0); 18590075Sobrien ipmi_purge_completed_requests(dev); 18690075Sobrien } 18790075Sobrien } 188132729Skan sc->ipmi_opened--; 189132729Skan IPMI_UNLOCK(sc); 190132729Skan 19190075Sobrien /* Cleanup. */ 19290075Sobrien free(dev, M_IPMI); 19390075Sobrien} 19490075Sobrien 19590075Sobrien#ifdef IPMB 19690075Sobrienstatic int 19790075Sobrienipmi_ipmb_checksum(u_char *data, int len) 19890075Sobrien{ 19990075Sobrien u_char sum = 0; 20090075Sobrien 20190075Sobrien for (; len; len--) { 20290075Sobrien sum += *data++; 20390075Sobrien } 20490075Sobrien return (-sum); 20590075Sobrien} 20690075Sobrien 20790075Sobrien/* XXX: Needs work */ 208169699Skanstatic int 209169699Skanipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 210169699Skan u_char command, u_char seq, u_char *data, int data_len) 211117406Skan{ 21290075Sobrien struct ipmi_softc *sc = device_get_softc(dev); 213132729Skan struct ipmi_request *req; 214132729Skan u_char slave_addr = 0x52; 215132729Skan int error; 216132729Skan 217132729Skan req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 218132729Skan IPMI_SEND_MSG, data_len + 8, 0); 219132729Skan req->ir_request[0] = channel; 220132729Skan req->ir_request[1] = slave_addr; 22190075Sobrien req->ir_request[2] = IPMI_ADDR(netfn, 0); 22290075Sobrien req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 22390075Sobrien req->ir_request[4] = sc->ipmi_address; 22490075Sobrien req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 22590075Sobrien req->ir_request[6] = command; 22690075Sobrien 227132729Skan bcopy(data, &req->ir_request[7], data_len); 228132729Skan temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 229132729Skan data_len + 3); 230132729Skan 23190075Sobrien ipmi_submit_driver_request(sc, req); 23290075Sobrien error = req->ir_error; 23390075Sobrien ipmi_free_request(req); 234132729Skan 235132729Skan return (error); 236132729Skan} 237132729Skan 238132729Skanstatic int 239132729Skanipmi_handle_attn(struct ipmi_softc *sc) 240132729Skan{ 24190075Sobrien struct ipmi_request *req; 24290075Sobrien int error; 243117406Skan 244169699Skan device_printf(sc->ipmi_dev, "BMC has a message\n"); 245169699Skan req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 246169699Skan IPMI_GET_MSG_FLAGS, 0, 1); 247117406Skan 24890075Sobrien ipmi_submit_driver_request(sc, req); 24990075Sobrien 25090075Sobrien if (req->ir_error == 0 && req->ir_compcode == 0) { 25190075Sobrien if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 252117406Skan device_printf(sc->ipmi_dev, "message buffer full"); 25390075Sobrien } 25490075Sobrien if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 25590075Sobrien device_printf(sc->ipmi_dev, 25690075Sobrien "watchdog about to go off"); 25790075Sobrien } 25890075Sobrien if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 25990075Sobrien ipmi_free_request(req); 26090075Sobrien 26190075Sobrien req = ipmi_alloc_driver_request( 26290075Sobrien IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 26390075Sobrien 16); 26490075Sobrien 26590075Sobrien device_printf(sc->ipmi_dev, "throw out message "); 26690075Sobrien dump_buf(temp, 16); 26790075Sobrien } 26890075Sobrien } 269117406Skan error = req->ir_error; 27090075Sobrien ipmi_free_request(req); 27190075Sobrien 27290075Sobrien return (error); 27390075Sobrien} 27490075Sobrien#endif 275169699Skan 276169699Skan#ifdef IPMICTL_SEND_COMMAND_32 277169699Skan#define PTRIN(p) ((void *)(uintptr_t)(p)) 27890075Sobrien#define PTROUT(p) ((uintptr_t)(p)) 279169699Skan#endif 28090075Sobrien 28190075Sobrienstatic int 28290075Sobrienipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 28390075Sobrien int flags, struct thread *td) 28490075Sobrien{ 285132729Skan struct ipmi_softc *sc; 28690075Sobrien struct ipmi_device *dev; 287169699Skan struct ipmi_request *kreq; 288169699Skan struct ipmi_req *req = (struct ipmi_req *)data; 289169699Skan struct ipmi_recv *recv = (struct ipmi_recv *)data; 290169699Skan struct ipmi_addr addr; 291169699Skan#ifdef IPMICTL_SEND_COMMAND_32 292169699Skan struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 293132729Skan struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 294169699Skan union { 295169699Skan struct ipmi_req req; 296132729Skan struct ipmi_recv recv; 297169699Skan } thunk32; 298169699Skan#endif 299169699Skan int error, len; 300169699Skan 301169699Skan error = devfs_get_cdevpriv((void **)&dev); 30290075Sobrien if (error) 303169699Skan return (error); 30490075Sobrien 305132729Skan sc = cdev->si_drv1; 306169699Skan 307169699Skan#ifdef IPMICTL_SEND_COMMAND_32 308169699Skan /* Convert 32-bit structures to native. */ 309132729Skan switch (cmd) { 310132729Skan case IPMICTL_SEND_COMMAND_32: 31190075Sobrien req = &thunk32.req; 312132729Skan req->addr = PTRIN(req32->addr); 31390075Sobrien req->addr_len = req32->addr_len; 314132729Skan req->msgid = req32->msgid; 31590075Sobrien req->msg.netfn = req32->msg.netfn; 316132729Skan req->msg.cmd = req32->msg.cmd; 317132729Skan req->msg.data_len = req32->msg.data_len; 318132729Skan req->msg.data = PTRIN(req32->msg.data); 319132729Skan break; 32090075Sobrien case IPMICTL_RECEIVE_MSG_TRUNC_32: 321132729Skan case IPMICTL_RECEIVE_MSG_32: 322132729Skan recv = &thunk32.recv; 323132729Skan recv->addr = PTRIN(recv32->addr); 32490075Sobrien recv->addr_len = recv32->addr_len; 325169699Skan recv->msg.data_len = recv32->msg.data_len; 326169699Skan recv->msg.data = PTRIN(recv32->msg.data); 327169699Skan break; 328132729Skan } 329132729Skan#endif 330132729Skan 331132729Skan switch (cmd) { 33290075Sobrien#ifdef IPMICTL_SEND_COMMAND_32 333132729Skan case IPMICTL_SEND_COMMAND_32: 334132729Skan#endif 335132729Skan case IPMICTL_SEND_COMMAND: 336132729Skan /* 33790075Sobrien * XXX: Need to add proper handling of this. 338132729Skan */ 33990075Sobrien error = copyin(req->addr, &addr, sizeof(addr)); 340132729Skan if (error) 34190075Sobrien return (error); 342132729Skan 343132729Skan IPMI_LOCK(sc); 344132729Skan /* clear out old stuff in queue of stuff done */ 345132729Skan /* XXX: This seems odd. */ 346132729Skan while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 347132729Skan TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 348132729Skan ir_link); 349132729Skan dev->ipmi_requests--; 350132729Skan ipmi_free_request(kreq); 351132729Skan } 352132729Skan IPMI_UNLOCK(sc); 353132729Skan 354132729Skan kreq = ipmi_alloc_request(dev, req->msgid, 355132729Skan IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 356169699Skan req->msg.data_len, IPMI_MAX_RX); 357169699Skan error = copyin(req->msg.data, kreq->ir_request, 358169699Skan req->msg.data_len); 359169699Skan if (error) { 360132729Skan ipmi_free_request(kreq); 361132729Skan return (error); 362132729Skan } 363132729Skan IPMI_LOCK(sc); 364132729Skan dev->ipmi_requests++; 365117406Skan error = sc->ipmi_enqueue_request(sc, kreq); 366117406Skan IPMI_UNLOCK(sc); 367132729Skan if (error) 368117406Skan return (error); 369117406Skan break; 370117406Skan#ifdef IPMICTL_SEND_COMMAND_32 371117406Skan case IPMICTL_RECEIVE_MSG_TRUNC_32: 372132729Skan case IPMICTL_RECEIVE_MSG_32: 373117406Skan#endif 374117406Skan case IPMICTL_RECEIVE_MSG_TRUNC: 375117406Skan case IPMICTL_RECEIVE_MSG: 376117406Skan error = copyin(recv->addr, &addr, sizeof(addr)); 377117406Skan if (error) 378117406Skan return (error); 379117406Skan 380117406Skan IPMI_LOCK(sc); 381117406Skan kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 382117406Skan if (kreq == NULL) { 383117406Skan IPMI_UNLOCK(sc); 38490075Sobrien return (EAGAIN); 38590075Sobrien } 38690075Sobrien addr.channel = IPMI_BMC_CHANNEL; 38790075Sobrien /* XXX */ 38890075Sobrien recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 38990075Sobrien recv->msgid = kreq->ir_msgid; 39090075Sobrien recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 39190075Sobrien recv->msg.cmd = kreq->ir_command; 392117406Skan error = kreq->ir_error; 393117406Skan if (error) { 39490075Sobrien TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 395117406Skan ir_link); 396117406Skan dev->ipmi_requests--; 397117406Skan IPMI_UNLOCK(sc); 398117406Skan ipmi_free_request(kreq); 399117406Skan return (error); 400117406Skan } 401117406Skan len = kreq->ir_replylen + 1; 402117406Skan if (recv->msg.data_len < len && 403117406Skan (cmd == IPMICTL_RECEIVE_MSG 404117406Skan#ifdef IPMICTL_RECEIVE_MSG_32 405117406Skan || cmd == IPMICTL_RECEIVE_MSG 406117406Skan#endif 407117406Skan )) { 408169699Skan IPMI_UNLOCK(sc); 409117406Skan return (EMSGSIZE); 410169699Skan } 411117406Skan TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 412117406Skan dev->ipmi_requests--; 413117406Skan IPMI_UNLOCK(sc); 414117406Skan len = min(recv->msg.data_len, len); 41590075Sobrien recv->msg.data_len = len; 41690075Sobrien error = copyout(&addr, recv->addr,sizeof(addr)); 41790075Sobrien if (error == 0) 418117406Skan error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 41990075Sobrien if (error == 0) 42090075Sobrien error = copyout(kreq->ir_reply, recv->msg.data + 1, 421117406Skan len - 1); 422117406Skan ipmi_free_request(kreq); 42396557Sobrien if (error) 424117406Skan return (error); 42590075Sobrien break; 42690075Sobrien case IPMICTL_SET_MY_ADDRESS_CMD: 42790075Sobrien IPMI_LOCK(sc); 42890075Sobrien dev->ipmi_address = *(int*)data; 42990075Sobrien IPMI_UNLOCK(sc); 43090075Sobrien break; 43190075Sobrien case IPMICTL_GET_MY_ADDRESS_CMD: 43290075Sobrien IPMI_LOCK(sc); 43390075Sobrien *(int*)data = dev->ipmi_address; 434132729Skan IPMI_UNLOCK(sc); 43590075Sobrien break; 43690075Sobrien case IPMICTL_SET_MY_LUN_CMD: 43790075Sobrien IPMI_LOCK(sc); 438117406Skan dev->ipmi_lun = *(int*)data & 0x3; 439117406Skan IPMI_UNLOCK(sc); 440117406Skan break; 441117406Skan case IPMICTL_GET_MY_LUN_CMD: 442117406Skan IPMI_LOCK(sc); 443117406Skan *(int*)data = dev->ipmi_lun; 444117406Skan IPMI_UNLOCK(sc); 445117406Skan break; 446117406Skan case IPMICTL_SET_GETS_EVENTS_CMD: 447117406Skan /* 448117406Skan device_printf(sc->ipmi_dev, 449117406Skan "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 450117406Skan */ 451117406Skan break; 452117406Skan case IPMICTL_REGISTER_FOR_CMD: 453117406Skan case IPMICTL_UNREGISTER_FOR_CMD: 454117406Skan return (EOPNOTSUPP); 455117406Skan default: 456117406Skan device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 457117406Skan return (ENOIOCTL); 458117406Skan } 459117406Skan 460117406Skan#ifdef IPMICTL_SEND_COMMAND_32 461117406Skan /* Update changed fields in 32-bit structures. */ 462117406Skan switch (cmd) { 463117406Skan case IPMICTL_RECEIVE_MSG_TRUNC_32: 464117406Skan case IPMICTL_RECEIVE_MSG_32: 465117406Skan recv32->recv_type = recv->recv_type; 466117406Skan recv32->msgid = recv->msgid; 467117406Skan recv32->msg.netfn = recv->msg.netfn; 468117406Skan recv32->msg.cmd = recv->msg.cmd; 469117406Skan recv32->msg.data_len = recv->msg.data_len; 470117406Skan break; 471117406Skan } 472117406Skan#endif 473117406Skan return (0); 474117406Skan} 475117406Skan 476117406Skan/* 477117406Skan * Request management. 478117406Skan */ 479117406Skan 480117406Skan/* Allocate a new request with request and reply buffers. */ 481117406Skanstruct ipmi_request * 482117406Skanipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 483117406Skan uint8_t command, size_t requestlen, size_t replylen) 484117406Skan{ 485117406Skan struct ipmi_request *req; 486117406Skan 487117406Skan req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 488117406Skan M_IPMI, M_WAITOK | M_ZERO); 489117406Skan req->ir_owner = dev; 490117406Skan req->ir_msgid = msgid; 491117406Skan req->ir_addr = addr; 492117406Skan req->ir_command = command; 493117406Skan if (requestlen) { 494117406Skan req->ir_request = (char *)&req[1]; 495117406Skan req->ir_requestlen = requestlen; 496117406Skan } 497117406Skan if (replylen) { 498117406Skan req->ir_reply = (char *)&req[1] + requestlen; 499117406Skan req->ir_replybuflen = replylen; 500117406Skan } 501117406Skan return (req); 502117406Skan} 503117406Skan 504117406Skan/* Free a request no longer in use. */ 505117406Skanvoid 506117406Skanipmi_free_request(struct ipmi_request *req) 507117406Skan{ 508117406Skan 509117406Skan free(req, M_IPMI); 510117406Skan} 511117406Skan 512117406Skan/* Store a processed request on the appropriate completion queue. */ 513117406Skanvoid 514117406Skanipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 515117406Skan{ 516117406Skan struct ipmi_device *dev; 517117406Skan 518117406Skan IPMI_LOCK_ASSERT(sc); 519117406Skan 520117406Skan /* 521117406Skan * Anonymous requests (from inside the driver) always have a 522117406Skan * waiter that we awaken. 523117406Skan */ 524117406Skan if (req->ir_owner == NULL) 525117406Skan wakeup(req); 526117406Skan else { 527117406Skan dev = req->ir_owner; 528117406Skan TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 529117406Skan selwakeup(&dev->ipmi_select); 530117406Skan if (dev->ipmi_closing) 531117406Skan wakeup(&dev->ipmi_requests); 532117406Skan } 533117406Skan} 534117406Skan 535117406Skan/* Enqueue an internal driver request and wait until it is completed. */ 536117406Skanint 537117406Skanipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 538117406Skan int timo) 539132729Skan{ 540132729Skan int error; 541132729Skan 542132729Skan IPMI_LOCK(sc); 543132729Skan error = sc->ipmi_enqueue_request(sc, req); 544132729Skan if (error == 0) 545132729Skan error = msleep(req, &sc->ipmi_lock, 0, "ipmireq", timo); 546117406Skan if (error == 0) 547117406Skan error = req->ir_error; 548117406Skan IPMI_UNLOCK(sc); 549117406Skan return (error); 550117406Skan} 551169699Skan 552169699Skan/* 553169699Skan * Helper routine for polled system interfaces that use 554169699Skan * ipmi_polled_enqueue_request() to queue requests. This request 555169699Skan * waits until there is a pending request and then returns the first 556117406Skan * request. If the driver is shutting down, it returns NULL. 557117406Skan */ 558117406Skanstruct ipmi_request * 559117406Skanipmi_dequeue_request(struct ipmi_softc *sc) 560117406Skan{ 561117406Skan struct ipmi_request *req; 562117406Skan 563117406Skan IPMI_LOCK_ASSERT(sc); 564117406Skan 565117406Skan while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 566117406Skan cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock); 567117406Skan if (sc->ipmi_detaching) 568169699Skan return (NULL); 569169699Skan 570117406Skan req = TAILQ_FIRST(&sc->ipmi_pending_requests); 571169699Skan TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 572117406Skan return (req); 573117406Skan} 574117406Skan 575117406Skan/* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 576117406Skanint 577169699Skanipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 578169699Skan{ 579169699Skan 580117406Skan IPMI_LOCK_ASSERT(sc); 581169699Skan 582117406Skan TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 583117406Skan cv_signal(&sc->ipmi_request_added); 584117406Skan return (0); 585117406Skan} 586117406Skan 587117406Skan/* 588117406Skan * Watchdog event handler. 589103452Skan */ 590103452Skan 591103452Skanstatic void 592103452Skanipmi_set_watchdog(struct ipmi_softc *sc, int sec) 593103452Skan{ 59490075Sobrien struct ipmi_request *req; 59590075Sobrien int error; 59690075Sobrien 59790075Sobrien req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 59890075Sobrien IPMI_SET_WDOG, 6, 0); 59990075Sobrien 60090075Sobrien if (sec) { 60190075Sobrien req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 60290075Sobrien | IPMI_SET_WD_TIMER_SMS_OS; 60390075Sobrien req->ir_request[1] = IPMI_SET_WD_ACTION_RESET; 60490075Sobrien req->ir_request[2] = 0; 60590075Sobrien req->ir_request[3] = 0; /* Timer use */ 60690075Sobrien req->ir_request[4] = (sec * 10) & 0xff; 60790075Sobrien req->ir_request[5] = (sec * 10) / 2550; 60890075Sobrien } else { 60990075Sobrien req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 610117406Skan req->ir_request[1] = 0; 611117406Skan req->ir_request[2] = 0; 612117406Skan req->ir_request[3] = 0; /* Timer use */ 613117406Skan req->ir_request[4] = 0; 61490075Sobrien req->ir_request[5] = 0; 61590075Sobrien } 61690075Sobrien 61790075Sobrien error = ipmi_submit_driver_request(sc, req, 0); 61890075Sobrien if (error) 619132729Skan device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 62090075Sobrien 621132729Skan if (error == 0 && sec) { 622132729Skan ipmi_free_request(req); 623132729Skan 624132729Skan req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 625132729Skan IPMI_RESET_WDOG, 0, 0); 626132729Skan 627132729Skan error = ipmi_submit_driver_request(sc, req, 0); 62890075Sobrien if (error) 629169699Skan device_printf(sc->ipmi_dev, 630132729Skan "Failed to reset watchdog\n"); 631132729Skan } 632132729Skan 633132729Skan ipmi_free_request(req); 634132729Skan /* 635169699Skan dump_watchdog(sc); 636132729Skan */ 637132729Skan} 638132729Skan 639132729Skanstatic void 640132729Skanipmi_wd_event(void *arg, unsigned int cmd, int *error) 641132729Skan{ 642132729Skan struct ipmi_softc *sc = arg; 643132729Skan unsigned int timeout; 644132729Skan 645132729Skan cmd &= WD_INTERVAL; 646169699Skan if (cmd > 0 && cmd <= 63) { 647132729Skan timeout = ((uint64_t)1 << cmd) / 1800000000; 648132729Skan ipmi_set_watchdog(sc, timeout); 649169699Skan *error = 0; 650132729Skan } else { 65190075Sobrien ipmi_set_watchdog(sc, 0); 65290075Sobrien } 653132729Skan} 654132729Skan 655117406Skanstatic void 656132729Skanipmi_startup(void *arg) 657169699Skan{ 658169699Skan struct ipmi_softc *sc = arg; 659132729Skan struct ipmi_request *req; 660132729Skan device_t dev; 661169699Skan int error, i; 662169699Skan 66390075Sobrien config_intrhook_disestablish(&sc->ipmi_ich); 664169699Skan dev = sc->ipmi_dev; 665169699Skan 66690075Sobrien /* Initialize interface-independent state. */ 66790075Sobrien mtx_init(&sc->ipmi_lock, device_get_nameunit(dev), "ipmi", MTX_DEF); 66890075Sobrien cv_init(&sc->ipmi_request_added, "ipmireq"); 66990075Sobrien TAILQ_INIT(&sc->ipmi_pending_requests); 670132729Skan 67190075Sobrien /* Initialize interface-dependent state. */ 672132729Skan error = sc->ipmi_startup(sc); 673132729Skan if (error) { 67490075Sobrien device_printf(dev, "Failed to initialize interface: %d\n", 67590075Sobrien error); 676132729Skan return; 67790075Sobrien } 67890075Sobrien 67990075Sobrien /* Send a GET_DEVICE_ID request. */ 680132729Skan req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 68190075Sobrien IPMI_GET_DEVICE_ID, 0, 15); 682169699Skan 683169699Skan error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 684132729Skan if (error == EWOULDBLOCK) { 68590075Sobrien device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 686169699Skan ipmi_free_request(req); 687169699Skan return; 688132729Skan } else if (error) { 68990075Sobrien device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 690132729Skan ipmi_free_request(req); 691132729Skan return; 692132729Skan } else if (req->ir_compcode != 0) { 693132729Skan device_printf(dev, 694132729Skan "Bad completion code for GET_DEVICE_ID: %d\n", 695132729Skan req->ir_compcode); 696132729Skan ipmi_free_request(req); 697132729Skan return; 698132729Skan } else if (req->ir_replylen < 5) { 699132729Skan device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 700132729Skan req->ir_replylen); 701169699Skan ipmi_free_request(req); 70290075Sobrien return; 703132729Skan } 704132729Skan 705132729Skan device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d, " 706132729Skan "version %d.%d\n", 707132729Skan req->ir_reply[1] & 0x0f, 708169699Skan req->ir_reply[2] & 0x0f, req->ir_reply[4], 70990075Sobrien req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 71090075Sobrien 711169699Skan ipmi_free_request(req); 712169699Skan 713169699Skan req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 714169699Skan IPMI_CLEAR_FLAGS, 1, 0); 71590075Sobrien 716169699Skan ipmi_submit_driver_request(sc, req, 0); 717169699Skan 718169699Skan /* XXX: Magic numbers */ 71990075Sobrien if (req->ir_compcode == 0xc0) { 72090075Sobrien device_printf(dev, "Clear flags is busy\n"); 72190075Sobrien } 72290075Sobrien if (req->ir_compcode == 0xc1) { 72390075Sobrien device_printf(dev, "Clear flags illegal\n"); 724169699Skan } 72590075Sobrien ipmi_free_request(req); 72690075Sobrien 72790075Sobrien for (i = 0; i < 8; i++) { 72890075Sobrien req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 72990075Sobrien IPMI_GET_CHANNEL_INFO, 1, 0); 73090075Sobrien req->ir_request[0] = i; 73190075Sobrien 73290075Sobrien ipmi_submit_driver_request(sc, req, 0); 73390075Sobrien 73490075Sobrien if (req->ir_compcode != 0) { 73590075Sobrien ipmi_free_request(req); 736132729Skan break; 737132729Skan } 73890075Sobrien ipmi_free_request(req); 73990075Sobrien } 74090075Sobrien device_printf(dev, "Number of channels %d\n", i); 74190075Sobrien 74290075Sobrien /* probe for watchdog */ 74390075Sobrien req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 74490075Sobrien IPMI_GET_WDOG, 0, 0); 74590075Sobrien 74690075Sobrien ipmi_submit_driver_request(sc, req, 0); 74790075Sobrien 74890075Sobrien if (req->ir_compcode == 0x00) { 74990075Sobrien device_printf(dev, "Attached watchdog\n"); 750132729Skan /* register the watchdog event handler */ 751132729Skan sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER(watchdog_list, 752132729Skan ipmi_wd_event, sc, 0); 753132729Skan } 75490075Sobrien ipmi_free_request(req); 75590075Sobrien 75690075Sobrien sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 75790075Sobrien UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 758132729Skan if (sc->ipmi_cdev == NULL) { 759132729Skan device_printf(dev, "Failed to create cdev\n"); 760169699Skan return; 761169699Skan } 762169699Skan sc->ipmi_cdev->si_drv1 = sc; 763169699Skan} 764169699Skan 765132729Skanint 76690075Sobrienipmi_attach(device_t dev) 76790075Sobrien{ 76890075Sobrien struct ipmi_softc *sc = device_get_softc(dev); 76990075Sobrien int error; 770132729Skan 77190075Sobrien if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 772132729Skan error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 77390075Sobrien NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 77490075Sobrien if (error) { 775132729Skan device_printf(dev, "can't set up interrupt\n"); 77690075Sobrien return (error); 777132729Skan } 77890075Sobrien } 779169699Skan 78090075Sobrien bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 781169699Skan sc->ipmi_ich.ich_func = ipmi_startup; 782169699Skan sc->ipmi_ich.ich_arg = sc; 783132729Skan if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 78490075Sobrien device_printf(dev, "can't establish configuration hook\n"); 785169699Skan return (ENOMEM); 786169699Skan } 787132729Skan 78890075Sobrien ipmi_attached = 1; 78990075Sobrien return (0); 79090075Sobrien} 791132729Skan 792169699Skanint 79390075Sobrienipmi_detach(device_t dev) 794161660Skan{ 795161660Skan struct ipmi_softc *sc; 796132729Skan 79790075Sobrien sc = device_get_softc(dev); 798169699Skan 79990075Sobrien /* Fail if there are any open handles. */ 800132729Skan IPMI_LOCK(sc); 801117406Skan if (sc->ipmi_opened) { 802132729Skan IPMI_UNLOCK(sc); 803117406Skan return (EBUSY); 804132729Skan } 805132729Skan IPMI_UNLOCK(sc); 806169699Skan if (sc->ipmi_cdev) 807132729Skan destroy_dev(sc->ipmi_cdev); 80890075Sobrien 80990075Sobrien /* Detach from watchdog handling and turn off watchdog. */ 81090075Sobrien if (sc->ipmi_watchdog_tag) { 81190075Sobrien EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 81290075Sobrien ipmi_set_watchdog(sc, 0); 813169699Skan } 814169699Skan 815169699Skan /* XXX: should use shutdown callout I think. */ 816169699Skan /* If the backend uses a kthread, shut it down. */ 817169699Skan IPMI_LOCK(sc); 818169699Skan sc->ipmi_detaching = 1; 819169699Skan if (sc->ipmi_kthread) { 820169699Skan cv_broadcast(&sc->ipmi_request_added); 82190075Sobrien msleep(sc->ipmi_kthread, &sc->ipmi_lock, 0, "ipmi_wait", 0); 82290075Sobrien } 82390075Sobrien IPMI_UNLOCK(sc); 824132729Skan if (sc->ipmi_irq) 825132729Skan bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 82690075Sobrien 827132729Skan ipmi_release_resources(dev); 828117406Skan mtx_destroy(&sc->ipmi_lock); 829169699Skan return (0); 830169699Skan} 831169699Skan 832169699Skanvoid 833169699Skanipmi_release_resources(device_t dev) 834169699Skan{ 835169699Skan struct ipmi_softc *sc; 836169699Skan int i; 837169699Skan 838169699Skan sc = device_get_softc(dev); 839169699Skan if (sc->ipmi_irq) 840169699Skan bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 841169699Skan if (sc->ipmi_irq_res) 842169699Skan bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 843169699Skan sc->ipmi_irq_res); 844169699Skan for (i = 0; i < MAX_RES; i++) 845169699Skan if (sc->ipmi_io_res[i]) 846169699Skan bus_release_resource(dev, sc->ipmi_io_type, 847169699Skan sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 848169699Skan} 849169699Skan 850169699Skandevclass_t ipmi_devclass; 851169699Skan 852169699Skan/* XXX: Why? */ 853259268Spfgstatic void 854259268Spfgipmi_unload(void *arg) 855169699Skan{ 856259268Spfg device_t * devs; 857169699Skan int count; 858169699Skan int i; 859169699Skan 860169699Skan if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) 861169699Skan return; 862169699Skan for (i = 0; i < count; i++) 863132729Skan device_delete_child(device_get_parent(devs[i]), devs[i]); 864132729Skan free(devs, M_TEMP); 865132729Skan} 866132729SkanSYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 867132729Skan 868132729Skan#ifdef IMPI_DEBUG 869169699Skanstatic void 870169699Skandump_buf(u_char *data, int len) 871169699Skan{ 872169699Skan char buf[20]; 873169699Skan char line[1024]; 874169699Skan char temp[30]; 875132729Skan int count = 0; 876132729Skan int i=0; 877132729Skan 878132729Skan printf("Address %p len %d\n", data, len); 879132729Skan if (len > 256) 880169699Skan len = 256; 881169699Skan line[0] = '\000'; 882169699Skan for (; len > 0; len--, data++) { 883169699Skan sprintf(temp, "%02x ", *data); 884169699Skan strcat(line, temp); 885132729Skan if (*data >= ' ' && *data <= '~') 886132729Skan buf[count] = *data; 887169699Skan else if (*data >= 'A' && *data <= 'Z') 888169699Skan buf[count] = *data; 889169699Skan else 890169699Skan buf[count] = '.'; 891169699Skan if (++count == 16) { 892132729Skan buf[count] = '\000'; 893169699Skan count = 0; 894169699Skan printf(" %3x %s %s\n", i, line, buf); 895169699Skan i+=16; 896169699Skan line[0] = '\000'; 897169699Skan } 898169699Skan } 899169699Skan buf[count] = '\000'; 900169699Skan 901169699Skan for (; count != 16; count++) { 902169699Skan strcat(line, " "); 903169699Skan } 904169699Skan printf(" %3x %s %s\n", i, line, buf); 905169699Skan} 906169699Skan#endif 907169699Skan