1989f2807SJerry Jelinek /* 2989f2807SJerry Jelinek * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3989f2807SJerry Jelinek * All rights reserved. 4989f2807SJerry Jelinek * 5989f2807SJerry Jelinek * Redistribution and use in source and binary forms, with or without 6989f2807SJerry Jelinek * modification, are permitted provided that the following conditions 7989f2807SJerry Jelinek * are met: 8989f2807SJerry Jelinek * 1. Redistributions of source code must retain the above copyright 9989f2807SJerry Jelinek * notice, this list of conditions and the following disclaimer. 10989f2807SJerry Jelinek * 2. Redistributions in binary form must reproduce the above copyright 11989f2807SJerry Jelinek * notice, this list of conditions and the following disclaimer in the 12989f2807SJerry Jelinek * documentation and/or other materials provided with the distribution. 13989f2807SJerry Jelinek * 14989f2807SJerry Jelinek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15989f2807SJerry Jelinek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16989f2807SJerry Jelinek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17989f2807SJerry Jelinek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18989f2807SJerry Jelinek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19989f2807SJerry Jelinek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20989f2807SJerry Jelinek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21989f2807SJerry Jelinek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22989f2807SJerry Jelinek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23989f2807SJerry Jelinek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24989f2807SJerry Jelinek * SUCH DAMAGE. 25989f2807SJerry Jelinek */ 26989f2807SJerry Jelinek 27989f2807SJerry Jelinek /* $FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.16 2011/11/07 15:43:11 ed Exp $ */ 28989f2807SJerry Jelinek 29989f2807SJerry Jelinek /* 30989f2807SJerry Jelinek * Copyright 2012, Joyent, Inc. All rights reserved. 311e393477SMarcel Telka * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 32989f2807SJerry Jelinek */ 33989f2807SJerry Jelinek 34989f2807SJerry Jelinek #include <sys/devops.h> 35989f2807SJerry Jelinek #include <sys/conf.h> 36989f2807SJerry Jelinek #include <sys/modctl.h> 37989f2807SJerry Jelinek #include <sys/types.h> 38989f2807SJerry Jelinek #include <sys/file.h> 39989f2807SJerry Jelinek #include <sys/errno.h> 40989f2807SJerry Jelinek #include <sys/open.h> 41989f2807SJerry Jelinek #include <sys/cred.h> 42989f2807SJerry Jelinek #include <sys/uio.h> 43989f2807SJerry Jelinek #include <sys/stat.h> 44989f2807SJerry Jelinek #include <sys/cmn_err.h> 45989f2807SJerry Jelinek #include <sys/ddi.h> 46989f2807SJerry Jelinek #include <sys/sunddi.h> 47989f2807SJerry Jelinek #include <sys/smbios.h> 48989f2807SJerry Jelinek #include <sys/smbios_impl.h> 49989f2807SJerry Jelinek #include <sys/ipmi.h> 50989f2807SJerry Jelinek #include "ipmivars.h" 51989f2807SJerry Jelinek 52989f2807SJerry Jelinek /* 53989f2807SJerry Jelinek * Request management. 54989f2807SJerry Jelinek */ 55989f2807SJerry Jelinek 56989f2807SJerry Jelinek /* Allocate a new request with request and reply buffers. */ 57989f2807SJerry Jelinek struct ipmi_request * 58989f2807SJerry Jelinek ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 59989f2807SJerry Jelinek uint8_t command, size_t requestlen, size_t replylen) 60989f2807SJerry Jelinek { 61989f2807SJerry Jelinek struct ipmi_request *req; 62989f2807SJerry Jelinek 63989f2807SJerry Jelinek req = kmem_zalloc(sizeof (struct ipmi_request) + requestlen + replylen, 64989f2807SJerry Jelinek KM_SLEEP); 65989f2807SJerry Jelinek req->ir_sz = sizeof (struct ipmi_request) + requestlen + replylen; 66989f2807SJerry Jelinek req->ir_owner = dev; 67989f2807SJerry Jelinek req->ir_msgid = msgid; 68989f2807SJerry Jelinek req->ir_addr = addr; 69989f2807SJerry Jelinek req->ir_command = command; 70989f2807SJerry Jelinek if (requestlen) { 71989f2807SJerry Jelinek req->ir_request = (uchar_t *)&req[1]; 72989f2807SJerry Jelinek req->ir_requestlen = requestlen; 73989f2807SJerry Jelinek } 74989f2807SJerry Jelinek if (replylen) { 75989f2807SJerry Jelinek req->ir_reply = (uchar_t *)&req[1] + requestlen; 76989f2807SJerry Jelinek req->ir_replybuflen = replylen; 77989f2807SJerry Jelinek } 781e393477SMarcel Telka 791e393477SMarcel Telka cv_init(&req->ir_cv, NULL, CV_DEFAULT, NULL); 801e393477SMarcel Telka req->ir_status = IRS_ALLOCATED; 811e393477SMarcel Telka 82989f2807SJerry Jelinek return (req); 83989f2807SJerry Jelinek } 84989f2807SJerry Jelinek 85989f2807SJerry Jelinek /* Free a request no longer in use. */ 86989f2807SJerry Jelinek void 87989f2807SJerry Jelinek ipmi_free_request(struct ipmi_request *req) 88989f2807SJerry Jelinek { 891e393477SMarcel Telka if (req == NULL) 901e393477SMarcel Telka return; 911e393477SMarcel Telka 921e393477SMarcel Telka cv_destroy(&req->ir_cv); 931e393477SMarcel Telka 94989f2807SJerry Jelinek kmem_free(req, req->ir_sz); 95989f2807SJerry Jelinek } 96989f2807SJerry Jelinek 97989f2807SJerry Jelinek /* Store a processed request on the appropriate completion queue. */ 98989f2807SJerry Jelinek /*ARGSUSED*/ 99989f2807SJerry Jelinek void 100989f2807SJerry Jelinek ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 101989f2807SJerry Jelinek { 102989f2807SJerry Jelinek struct ipmi_device *dev; 103989f2807SJerry Jelinek 104989f2807SJerry Jelinek IPMI_LOCK_ASSERT(sc); 105989f2807SJerry Jelinek 1061e393477SMarcel Telka if (req->ir_status == IRS_CANCELED) { 1071e393477SMarcel Telka ASSERT(req->ir_owner == NULL); 1081e393477SMarcel Telka ipmi_free_request(req); 1091e393477SMarcel Telka return; 1101e393477SMarcel Telka } 1111e393477SMarcel Telka 112*cc944374SMarcel Telka req->ir_status = IRS_COMPLETED; 113*cc944374SMarcel Telka 114*cc944374SMarcel Telka /* 115*cc944374SMarcel Telka * Anonymous requests (from inside the driver) always have a 116*cc944374SMarcel Telka * waiter that we awaken. 117*cc944374SMarcel Telka */ 118*cc944374SMarcel Telka if (req->ir_owner == NULL) { 119*cc944374SMarcel Telka cv_signal(&req->ir_cv); 120*cc944374SMarcel Telka } else { 121989f2807SJerry Jelinek dev = req->ir_owner; 122989f2807SJerry Jelinek TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 123989f2807SJerry Jelinek pollwakeup(dev->ipmi_pollhead, POLLIN | POLLRDNORM); 124*cc944374SMarcel Telka 125*cc944374SMarcel Telka dev->ipmi_status &= ~IPMI_BUSY; 126*cc944374SMarcel Telka if (dev->ipmi_status & IPMI_CLOSING) 127*cc944374SMarcel Telka cv_signal(&dev->ipmi_cv); 128989f2807SJerry Jelinek } 129989f2807SJerry Jelinek } 130989f2807SJerry Jelinek 131989f2807SJerry Jelinek /* 132989f2807SJerry Jelinek * Enqueue an internal driver request and wait until it is completed. 133989f2807SJerry Jelinek */ 134989f2807SJerry Jelinek static int 1351e393477SMarcel Telka ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request **preq, 136989f2807SJerry Jelinek int timo) 137989f2807SJerry Jelinek { 138989f2807SJerry Jelinek int error; 1391e393477SMarcel Telka struct ipmi_request *req = *preq; 1401e393477SMarcel Telka 1411e393477SMarcel Telka ASSERT(req->ir_owner == NULL); 142989f2807SJerry Jelinek 143989f2807SJerry Jelinek IPMI_LOCK(sc); 144989f2807SJerry Jelinek error = sc->ipmi_enqueue_request(sc, req); 1451e393477SMarcel Telka 1461e393477SMarcel Telka if (error != 0) { 147989f2807SJerry Jelinek IPMI_UNLOCK(sc); 1481e393477SMarcel Telka return (error); 1491e393477SMarcel Telka } 1501e393477SMarcel Telka 1511e393477SMarcel Telka while (req->ir_status != IRS_COMPLETED && error >= 0) 152989f2807SJerry Jelinek if (timo == 0) 1531e393477SMarcel Telka cv_wait(&req->ir_cv, &sc->ipmi_lock); 154989f2807SJerry Jelinek else 1551e393477SMarcel Telka error = cv_timedwait(&req->ir_cv, &sc->ipmi_lock, 156989f2807SJerry Jelinek ddi_get_lbolt() + timo); 1571e393477SMarcel Telka 1581e393477SMarcel Telka switch (req->ir_status) { 1591e393477SMarcel Telka case IRS_QUEUED: 1601e393477SMarcel Telka TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 1611e393477SMarcel Telka req->ir_status = IRS_CANCELED; 162989f2807SJerry Jelinek error = EWOULDBLOCK; 1631e393477SMarcel Telka break; 1641e393477SMarcel Telka case IRS_PROCESSED: 1651e393477SMarcel Telka req->ir_status = IRS_CANCELED; 1661e393477SMarcel Telka error = EWOULDBLOCK; 1671e393477SMarcel Telka *preq = NULL; 1681e393477SMarcel Telka break; 1691e393477SMarcel Telka case IRS_COMPLETED: 170989f2807SJerry Jelinek error = req->ir_error; 1711e393477SMarcel Telka break; 1721e393477SMarcel Telka default: 1731e393477SMarcel Telka panic("IPMI: Invalid request status"); 1741e393477SMarcel Telka break; 175989f2807SJerry Jelinek } 176989f2807SJerry Jelinek IPMI_UNLOCK(sc); 177989f2807SJerry Jelinek 178989f2807SJerry Jelinek return (error); 179989f2807SJerry Jelinek } 180989f2807SJerry Jelinek 181989f2807SJerry Jelinek /* 182989f2807SJerry Jelinek * Helper routine for polled system interfaces that use 183989f2807SJerry Jelinek * ipmi_polled_enqueue_request() to queue requests. This request 184989f2807SJerry Jelinek * waits until there is a pending request and then returns the first 185989f2807SJerry Jelinek * request. If the driver is shutting down, it returns NULL. 186989f2807SJerry Jelinek */ 187989f2807SJerry Jelinek struct ipmi_request * 188989f2807SJerry Jelinek ipmi_dequeue_request(struct ipmi_softc *sc) 189989f2807SJerry Jelinek { 190989f2807SJerry Jelinek struct ipmi_request *req; 191989f2807SJerry Jelinek 192989f2807SJerry Jelinek IPMI_LOCK_ASSERT(sc); 193989f2807SJerry Jelinek 194989f2807SJerry Jelinek while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 195989f2807SJerry Jelinek cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock); 196989f2807SJerry Jelinek if (sc->ipmi_detaching) 197989f2807SJerry Jelinek return (NULL); 198989f2807SJerry Jelinek 199989f2807SJerry Jelinek req = TAILQ_FIRST(&sc->ipmi_pending_requests); 200989f2807SJerry Jelinek TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 2011e393477SMarcel Telka req->ir_status = IRS_PROCESSED; 202*cc944374SMarcel Telka 203*cc944374SMarcel Telka if (req->ir_owner != NULL) 204*cc944374SMarcel Telka req->ir_owner->ipmi_status |= IPMI_BUSY; 205*cc944374SMarcel Telka 206989f2807SJerry Jelinek return (req); 207989f2807SJerry Jelinek } 208989f2807SJerry Jelinek 209989f2807SJerry Jelinek int 210989f2807SJerry Jelinek ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 211989f2807SJerry Jelinek { 212989f2807SJerry Jelinek 213989f2807SJerry Jelinek IPMI_LOCK_ASSERT(sc); 214989f2807SJerry Jelinek 215989f2807SJerry Jelinek TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 2161e393477SMarcel Telka req->ir_status = IRS_QUEUED; 217989f2807SJerry Jelinek cv_signal(&sc->ipmi_request_added); 218989f2807SJerry Jelinek return (0); 219989f2807SJerry Jelinek } 220989f2807SJerry Jelinek 221989f2807SJerry Jelinek void 222e1c99a74SAlek Pinchuk ipmi_shutdown(struct ipmi_softc *sc) 223e1c99a74SAlek Pinchuk { 224e1c99a74SAlek Pinchuk taskq_destroy(sc->ipmi_kthread); 225e1c99a74SAlek Pinchuk 226e1c99a74SAlek Pinchuk cv_destroy(&sc->ipmi_request_added); 227e1c99a74SAlek Pinchuk mutex_destroy(&sc->ipmi_lock); 228e1c99a74SAlek Pinchuk } 229e1c99a74SAlek Pinchuk 230e1c99a74SAlek Pinchuk boolean_t 231989f2807SJerry Jelinek ipmi_startup(struct ipmi_softc *sc) 232989f2807SJerry Jelinek { 233989f2807SJerry Jelinek struct ipmi_request *req; 234989f2807SJerry Jelinek int error, i; 235989f2807SJerry Jelinek 236989f2807SJerry Jelinek /* Initialize interface-independent state. */ 237989f2807SJerry Jelinek mutex_init(&sc->ipmi_lock, NULL, MUTEX_DEFAULT, NULL); 238989f2807SJerry Jelinek cv_init(&sc->ipmi_request_added, NULL, CV_DEFAULT, NULL); 239989f2807SJerry Jelinek TAILQ_INIT(&sc->ipmi_pending_requests); 240989f2807SJerry Jelinek 241989f2807SJerry Jelinek /* Initialize interface-dependent state. */ 242989f2807SJerry Jelinek error = sc->ipmi_startup(sc); 243989f2807SJerry Jelinek if (error) { 244989f2807SJerry Jelinek cmn_err(CE_WARN, "Failed to initialize interface: %d", error); 245e1c99a74SAlek Pinchuk return (B_FALSE); 246989f2807SJerry Jelinek } 247989f2807SJerry Jelinek 248989f2807SJerry Jelinek /* Send a GET_DEVICE_ID request. */ 249989f2807SJerry Jelinek req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 250989f2807SJerry Jelinek IPMI_GET_DEVICE_ID, 0, 15); 251989f2807SJerry Jelinek 2521e393477SMarcel Telka error = ipmi_submit_driver_request(sc, &req, MAX_TIMEOUT); 253989f2807SJerry Jelinek if (error == EWOULDBLOCK) { 254989f2807SJerry Jelinek cmn_err(CE_WARN, "Timed out waiting for GET_DEVICE_ID"); 255989f2807SJerry Jelinek ipmi_free_request(req); 256e1c99a74SAlek Pinchuk return (B_FALSE); 257989f2807SJerry Jelinek } else if (error) { 258989f2807SJerry Jelinek cmn_err(CE_WARN, "Failed GET_DEVICE_ID: %d", error); 259989f2807SJerry Jelinek ipmi_free_request(req); 260e1c99a74SAlek Pinchuk return (B_FALSE); 261989f2807SJerry Jelinek } else if (req->ir_compcode != 0) { 262989f2807SJerry Jelinek cmn_err(CE_WARN, 263989f2807SJerry Jelinek "Bad completion code for GET_DEVICE_ID: %d", 264989f2807SJerry Jelinek req->ir_compcode); 265989f2807SJerry Jelinek ipmi_free_request(req); 266e1c99a74SAlek Pinchuk return (B_FALSE); 267989f2807SJerry Jelinek } else if (req->ir_replylen < 5) { 268989f2807SJerry Jelinek cmn_err(CE_WARN, "Short reply for GET_DEVICE_ID: %d", 269989f2807SJerry Jelinek req->ir_replylen); 270989f2807SJerry Jelinek ipmi_free_request(req); 271e1c99a74SAlek Pinchuk return (B_FALSE); 272989f2807SJerry Jelinek } 273989f2807SJerry Jelinek 274989f2807SJerry Jelinek cmn_err(CE_CONT, "!device rev. %d, firmware rev. %d.%d%d, " 275989f2807SJerry Jelinek "version %d.%d", 276989f2807SJerry Jelinek req->ir_reply[1] & 0x0f, req->ir_reply[2] & 0x7f, 277989f2807SJerry Jelinek req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 278989f2807SJerry Jelinek req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 279989f2807SJerry Jelinek 280989f2807SJerry Jelinek ipmi_free_request(req); 281989f2807SJerry Jelinek 282989f2807SJerry Jelinek req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 283989f2807SJerry Jelinek IPMI_CLEAR_FLAGS, 1, 0); 284989f2807SJerry Jelinek 2851e393477SMarcel Telka if ((error = ipmi_submit_driver_request(sc, &req, 0)) != 0) { 286989f2807SJerry Jelinek cmn_err(CE_WARN, "Failed to clear IPMI flags: %d\n", error); 287e1c99a74SAlek Pinchuk ipmi_free_request(req); 288e1c99a74SAlek Pinchuk return (B_FALSE); 289e1c99a74SAlek Pinchuk } 290989f2807SJerry Jelinek 291989f2807SJerry Jelinek /* Magic numbers */ 292989f2807SJerry Jelinek if (req->ir_compcode == 0xc0) { 293989f2807SJerry Jelinek cmn_err(CE_NOTE, "!Clear flags is busy"); 294989f2807SJerry Jelinek } 295989f2807SJerry Jelinek if (req->ir_compcode == 0xc1) { 296989f2807SJerry Jelinek cmn_err(CE_NOTE, "!Clear flags illegal"); 297989f2807SJerry Jelinek } 298989f2807SJerry Jelinek ipmi_free_request(req); 299989f2807SJerry Jelinek 300989f2807SJerry Jelinek for (i = 0; i < 8; i++) { 301989f2807SJerry Jelinek req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 302989f2807SJerry Jelinek IPMI_GET_CHANNEL_INFO, 1, 0); 303989f2807SJerry Jelinek req->ir_request[0] = (uchar_t)i; 304989f2807SJerry Jelinek 3051e393477SMarcel Telka if (ipmi_submit_driver_request(sc, &req, 0) != 0) { 306989f2807SJerry Jelinek ipmi_free_request(req); 307989f2807SJerry Jelinek break; 308989f2807SJerry Jelinek } 309989f2807SJerry Jelinek 310989f2807SJerry Jelinek if (req->ir_compcode != 0) { 311989f2807SJerry Jelinek ipmi_free_request(req); 312989f2807SJerry Jelinek break; 313989f2807SJerry Jelinek } 314989f2807SJerry Jelinek ipmi_free_request(req); 315989f2807SJerry Jelinek } 316989f2807SJerry Jelinek cmn_err(CE_CONT, "!number of channels %d", i); 317989f2807SJerry Jelinek 318989f2807SJerry Jelinek /* probe for watchdog */ 319989f2807SJerry Jelinek req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 320989f2807SJerry Jelinek IPMI_GET_WDOG, 0, 0); 321989f2807SJerry Jelinek 3221e393477SMarcel Telka if ((error = ipmi_submit_driver_request(sc, &req, 0)) != 0) { 323989f2807SJerry Jelinek cmn_err(CE_WARN, "Failed to check IPMI watchdog: %d\n", error); 324989f2807SJerry Jelinek ipmi_free_request(req); 325e1c99a74SAlek Pinchuk return (B_FALSE); 326989f2807SJerry Jelinek } 327989f2807SJerry Jelinek 328989f2807SJerry Jelinek if (req->ir_compcode == 0x00) { 329989f2807SJerry Jelinek cmn_err(CE_CONT, "!watchdog supported"); 330989f2807SJerry Jelinek 331989f2807SJerry Jelinek /* 332989f2807SJerry Jelinek * Here is where we could register a watchdog event handler. 333989f2807SJerry Jelinek * See ipmi_wd_event() in the FreeBSD code. 334989f2807SJerry Jelinek */ 335989f2807SJerry Jelinek } 336989f2807SJerry Jelinek ipmi_free_request(req); 337e1c99a74SAlek Pinchuk 338e1c99a74SAlek Pinchuk return (B_TRUE); 339989f2807SJerry Jelinek } 340